Skip to content

How billing works

Read this once and the rest of the API will feel inevitable.

The object graph

Product ──┐
├── Plan ──┐
Component ┘ ├── Subscription ── Invoice ── Payment
│ │ │ │
│ └─ Usage event │ └─ Refund
│ │
Customer ───── Payment ──────┘
method

Six objects do the work:

  • Productwhat you sell. A SKU, basically.
  • Planhow you price it. Carries currency, interval, and one or more components (each with its own pricing model).
  • Customer — the buyer. Owns payment methods and subscriptions.
  • Subscription — a customer on a plan, recurring on the plan’s interval.
  • Invoice — the bill produced when a billing period closes (or on demand).
  • Payment — the attempt to collect that invoice via a payment method.

Two satellites:

  • Usage event — for metered components, you push counted events; we aggregate at period close.
  • Refund / Credit note — the reverse motion when something needs to be unwound.

The lifecycle in one paragraph

A customer subscribes to a plan. Paylera advances the subscription through its FSM (pending_activationtrialingactive → …). At each billing period boundary, the invoice engine prices line items, applies discounts, computes tax, and finalises an invoice. A payment is then initiated through your configured provider; it advances through its own FSM (pendingprocessingrequires_actionsucceeded / failed / charged_back). If the payment fails, the dunning engine schedules retries per your policy. If it succeeds, the revenue recognition engine schedules deferred revenue across the service period and posts it to the ledger as it earns. Every state change emits a webhook event so your system can react.

Idempotency, by default

Every money-moving and resource-creating route requires an Idempotency-Key header. If you retry the same key with the same body, you get the original response back — no duplicate customer, no duplicate charge. Keys are scoped per-tenant per-route and live for 24 hours. See Idempotency.

Events, not polling

You don’t poll /v1/subscriptions/{id} waiting for active. You register a webhook endpoint and react to subscription.activated. Events are typed, signed, ordered per aggregate, and delivered at-least-once.

The full event catalog is at Webhooks → Event catalog.

Money is typed

Every monetary value carries an explicit currency:

{ "amount": "29.00", "currency": "USD" }

Strings, not floats — financial code uses decimal arithmetic. Rounding is per-line at the currency’s minor units. We never compare amounts as strings. See Money & currency.

Multi-tenancy

Your API key resolves to exactly one tenant. There is no cross-tenant API in the public surface. Every read is filtered by your tenant; every write is recorded against your tenant. If you operate multiple brands or legal entities, you’ll get one tenant per brand and a separate API key for each.

What you’ll never have to think about

  • Race conditions between webhook delivery and your business logic — the envelope carries an event_id you can dedupe on.
  • Out-of-order events for the same aggregate — events for one subscription / invoice / payment arrive in commit order.
  • FX precision drift — invoices carry the locked rate; the ledger has an explicit FX-gain/loss leg.
  • Whether a refund needs a credit note — refunds emit one automatically.
  • Whether a finalised invoice can be edited — it can’t. Issue a credit note.

Where to next