State reference

Payment states

Payment states mirror the underlying provider (card processor, Net30 for invoice). Throttle normalises them so your handler is provider- agnostic.

State machine

stateDiagram-v2
  [*] --> pending: POST /payments or buyer click
  pending --> authorized: Provider/Net30 authorizes
  pending --> failed: provider declines
  authorized --> captured: capture call
  authorized --> voided: void before capture
  captured --> refunded: full refund
  captured --> partially_refunded: partial refund
  partially_refunded --> refunded: subsequent refunds total to full
  refunded --> [*]
  voided --> [*]
  failed --> [*]
Payment lifecycle from creation to terminal.

States

  • pending — payment row created, no provider response yet. UI shows a spinner; back-end has the row id but no auth/capture proof.
  • authorized — provider authorised the amount (funds reserved). Most card flows auto-capture so this is brief; manual-capture flows stay here until the merchant explicitly captures.
  • captured — funds moved. Triggers payment.captured outbound event. Order can now transition to paid. Capture may be partial: pass an amount below the authorized total to POST /v1/payments/{id}/capture. The captured value is tracked on capturedAmount (the event carries both capturedAmount and authorizedAmount); the uncaptured remainder is released by the processor, and refunds are capped at the captured amount.
  • failed — provider declined OR a network/timeout error during processing. Carries error.code + error.message. Terminal.
  • voided — authorisation released before capture. Funds never moved. Terminal.
  • partially_refunded — at least one refund processed but total refunded is less than captured amount. Still accepts further refunds.
  • refunded — total refunded equals captured amount. Terminal.

Net30 special cases

  • Net30 payments go through pendingauthorized on invoice issue, then → captured when the buyer pays the invoice (manual mark-paid OR ACH webhook).
  • invoice.overdue fires when an authorized Net30 payment passes its dueDate; the payment row stays authorized until paid or voided.

Disputes & chargebacks

Disputes are tracked out-of-band from the status machine, on the disputed boolean plus disputeReason / disputeOpenedAt (all exposed on the payment object). A payment keeps its underlying status (usually captured) while disputed.

  • Card chargebacks are ingested automatically from the payment provider: an inbound dispute.* / chargeback.* webhook sets disputed = true and fires payment.disputed. A merchant-favourable resolution (won / reversed) clears the flag and fires payment.dispute_cleared.
  • Net30 disputes are opened/cleared manually from the dashboard and fire the same two events.