Orders
If CheckoutSession is what the buyer sees, Order is what you care about. It’s the permanent record of:
- What was being bought (line items)
- How much was owed and how much has actually been paid
- Refunds outstanding and applied
- Your external reference (
external_ref) — typically your own order ID, stored on the Order and returned onGET /b2b/v1/orders/{id}(not echoed in webhook payloads — see below)
A CheckoutSession dies after one of its PaymentIntents settles or the TTL expires. The Order lives forever.
When an Order is created
When you call POST /b2b/v1/checkout-sessions/quick, the
payment-service creates both a new Order and a new CheckoutSession
in one transaction. If you already have an Order and want to retry
checkout (e.g., after the buyer abandoned), use
POST /b2b/v1/checkout-sessions instead to attach a fresh session
to the existing order — preserving the audit trail.
Lifecycle
| State | Means |
|---|---|
DRAFT | Reserved for a future drafts flow. No code path creates DRAFT orders today — every order is created PENDING, so you will not observe this state. |
PENDING | A CheckoutSession is live and unresolved. |
PAID | Full amount settled. The webhook payment.settled fires here. Safe to fulfill. |
PARTIAL_PAID | Money arrived but less than total. See “Underpayment” below. |
CANCELED | Session expired or merchant cancelled. metadata.canceled_reason explains why (payment_timeout, merchant_canceled, …). |
REFUNDED | All paid amount has been refunded. |
PARTIALLY_REFUNDED | Some refund executed but balance remains paid. |
The state to switch your fulfillment logic on is PAID — not
the CheckoutSession’s COMPLETED. The payment.settled webhook
is the canonical signal.
The external_ref field
When creating a session you can include external_ref (any string
up to 255 chars — typically your own order ID). It threads through
the whole pipeline:
- Stored on the Order
- Visible in the merchant dashboard for support lookups
- Returned on
GET /b2b/v1/orders/{id}so a webhook handler can fetch it after receivingpayment.settled
Webhook payloads do not echo external_ref directly today —
earlier drafts of these docs claimed data.external_ref flowed
through to every event, and that was wrong. To map a payment.*
webhook back to your DB row, take order_id from the payload and
fetch the order. The native external_ref field on webhook
payloads is on the roadmap.
Underpayment
If the buyer’s on-chain transfer clears for less than the order
total, the Order goes to PARTIAL_PAID. You have three options:
- Accept and resolve. Flips the order to
PAIDand firesorder.resolved. This is not a public REST endpoint — resolution is an internal/operational action today, so for a self-serve flow prefer option 2 (collect the remainder). - Wait for the remainder. Create a new CheckoutSession against
the same Order with
amount_due= residual. The buyer pays the difference; when that settles, Order moves toPAID. - Cancel and refund. Refund the partial amount and cancel the Order. The buyer is responsible for any chain fees.
The product hasn’t made a recommendation here — different merchants want different policies. Pick one and bake it into your admin.
Refunds
Refunds are a separate API surface and a separate concept page. See Concepts → Refunds.
API endpoints
| Method | Path | Notes |
|---|---|---|
POST | /b2b/v1/orders | Create an Order without a session (rare) |
GET | /b2b/v1/orders/:order_id | Read full order with line items + payment history |
PATCH | /b2b/v1/orders/:order_id/cancel | Cancel an unpaid order |
PATCH | /b2b/v1/orders/:order_id/reopen | Reopen an auto-canceled (payment_timeout) order |
What’s next
- Concepts → Sessions — the buyer-facing shell that wraps an Order.
- Concepts → Refunds — refund states and the manual on-chain submission step.
- Webhooks → Overview — every event fired during an Order’s lifecycle.