<!-- Source: https://docs.infraio.xyz/en/concepts/orders -->
<!-- Last updated: 2026-10-04 -->

# Orders

If [CheckoutSession](https://docs.infraio.xyz/en/concepts/sessions) 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 on `GET /b2b/v1/orders/{id}`
  (not echoed in webhook payloads — see below)

An order does not need line items: send `amount` instead of `items` to bill a plain amount (an invoice, a deposit, a custom payment link). Send one or the other, never both.

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`, InfraIO Pay
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

```mermaid
stateDiagram-v2
    [*] --> PENDING: order created
    PENDING --> PAID:              full payment settled
    PENDING --> PARTIAL_PAID:      underpayment settled
    PENDING --> CANCELED:          session expired or canceled
    PARTIAL_PAID --> PAID:         merchant resolves OR remainder paid
    PAID --> PARTIALLY_REFUNDED:   partial refund executed
    PAID --> REFUNDED:             full refund executed
    PARTIALLY_REFUNDED --> REFUNDED: subsequent refund covers the rest
    PAID --> [*]
    REFUNDED --> [*]
    CANCELED --> [*]
```

| State | Means |
| --- | --- |
| `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. |

> **Note:**
>
> **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 receiving `payment.settled`

> **Warning:**
>
> Webhook payloads do **not** include `external_ref`. To map a `payment.*`
> webhook back to your own record, take `order_id` from the payload and
> fetch the order.

## 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:

1. **Accept and resolve.** Flips the order to `PAID` and fires
   `order.resolved`. This isn't available through the REST API, so for a
   self-serve flow use option 2 (collect the remainder).
2. **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 to `PAID`.
3. **Cancel and refund.** Refund the partial amount and cancel the
   Order. The buyer is responsible for any chain fees.

## Refunds

Refunds are a separate API surface and a separate concept page.
See [Concepts → Refunds](https://docs.infraio.xyz/en/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](https://docs.infraio.xyz/en/concepts/sessions) — the buyer-facing
  shell that wraps an Order.
- [Concepts → Refunds](https://docs.infraio.xyz/en/concepts/refunds) — refund states and
  the manual on-chain submission step.
- [Webhooks → Overview](https://docs.infraio.xyz/en/webhooks/overview) — every event fired
  during an Order's lifecycle.
