---
title: Orders and payments
description: Integrate payment collection with orders using your own payment provider or managed payment sessions
---

An order and a payment work together on every purchase. The order records what the customer
buys. The payment satisfies the payment requirement of that order, and the order cannot be
submitted before it does. You collect the payment through your own provider, or through a
managed payment session.

## Core concept

Every order has **requirements** that must be met before submission:

- `requiresPayment`: Whether the order needs payment before it can be fulfilled
- `requiresPaymentProfile`: Whether a stored payment method is needed for future billing
- `requiresSigning`: Whether the order requires a digital signature

Each requirement is `NOT_REQUIRED`, `OPTIONAL`, or `REQUIRED`. Read them after you calculate
the price. They tell you the correct submission flow. An order with a total of zero can need
no payment at all. Read the requirements, and do not assume that a payment is due.

> **Note**
>
> The requirements are per order. One order can need a payment where the order before it did not.

## Quick path

**1. Create order**

Create an order and add line items for the products the customer wants to buy.

**2. Read price**

Read the taxes and totals off the order to determine the amount due.

**3. Check requirements**

Inspect the order requirements to determine if payment, a payment profile, or signing is needed.

**4. Collect payment**

Collect payment through your own provider or use a managed payment session.

**5. Submit order**

Submit the order with the external payment reference to begin fulfillment. Orders paying through
a managed payment session or payment link are submitted automatically once the payment succeeds.

## Choosing a payment approach

You have three ways to collect a payment. Use your own payment infrastructure, or use a
managed payment session.

- **Your own provider (Recommended)** — Collect the payment on your own payment stack, such as Stripe, Adyen, or Braintree. Pass the reference when you submit the order. You keep full control of the checkout, the payment methods, and the provider relationship.
- **Hosted payment page** — Use a managed payment session with `hosted: true`. The response carries a checkout URL that the provider hosts. Send the customer there. You build no payment interface.
- **Embedded payment widget** — Use a managed payment session with `hosted: false`. The response carries the provider credentials. Render the payment form in your own interface with the SDK of the provider.

### When to use each approach

| Approach                | Best for                                                                                             |
| ----------------------- | ---------------------------------------------------------------------------------------------------- |
| **Your own provider**   | A team that already runs a payment infrastructure and wants control of the provider and the checkout |
| **Hosted payment page** | A fast integration that needs no payment components of its own                                       |
| **Embedded widget**     | A team that wants a managed payment backend, but its own payment interface                           |

> **Info**
>
> Most integrations collect the payment on their own provider. That way one payment relationship
> covers everything, and the checkout stays under their control.

## 1. Create an order and add line items

Create an order for a customer and include the products they want to purchase.

```bash
curl -X POST "{BASE_URL}/orders" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer": {
      "customerId": "123e4567-e89b-12d3-a456-426614174000"
    },
    "lineItems": [
      {
        "type": "SUBSCRIPTION",
        "lineItemId": "sub-1",
        "productOfferingId": "offering-id",
        "subscriber": {
          "name": "Jane Doe",
          "email": "jane@example.com"
        }
      }
    ]
  }'
```

You can also add line items to an existing order separately:

```bash
curl -X POST "{BASE_URL}/orders/{orderId}/line-items" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "ADDON",
    "lineItemId": "addon-1",
    "productOfferingId": "addon-offering-id",
    "parentLineItemId": "sub-1"
  }'
```

See [Create Order](/api-reference/orders.md#tag/orders/POST/orders) and [Add Line Items](/api-reference/orders.md#tag/orders/POST/orders/{orderId}/line-items)

## 2. Read the price

Taxes and totals are recalculated whenever the order changes, so read them off the order before collecting payment.

```bash
curl "{BASE_URL}/orders/{orderId}" \
  -H "X-API-Key: $API_KEY"
```

See [Get Order](/api-reference/orders.md#tag/orders/GET/orders/{orderId})

## 3. Check order requirements

After calculating the price, inspect the order to determine what is needed before submission.

```bash
# Fetch the order and inspect the requirements object
curl "{BASE_URL}/orders/{orderId}" \
  -H "X-API-Key: $API_KEY"
```

See [Get Order](/api-reference/orders.md#tag/orders/GET/orders/{orderId})

## 4. Collect payment

Once you know the order requires payment, choose one of the following approaches.

### Option A: Your own payment provider (recommended)

Collect the payment through your own payment provider: Stripe, Adyen, Braintree, or another
one. You keep full control of the checkout, and you keep the payment infrastructure that you
already run. This approach adds no dependency.

After collecting payment on your side, submit the order with an `externalPayment` reference:

```bash
# Step 1: Collect payment through your own provider
# (This happens in your existing payment flow)

# Step 2: Submit the order with the payment reference
curl -X POST "{BASE_URL}/orders/{orderId}/submit" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "externalPayment": {
      "reference": "pi_3ABC123def456",
      "receiptDescription": "Subscription activation payment",
      "receiptUrl": "https://yourapp.com/receipts/abc123"
    }
  }'
```

The `externalPayment` object accepts:

| Field                | Required | Description                                                |
| -------------------- | -------- | ---------------------------------------------------------- |
| `reference`          | Yes      | The payment reference or transaction ID from your provider |
| `receiptDescription` | No       | A human-readable description of the payment                |
| `receiptUrl`         | No       | A URL to the payment receipt or confirmation page          |

> **Note**
>
> When using external payments, you are responsible for collecting the correct amount and handling
> refunds through your payment provider.

See [Submit Order](/api-reference/orders.md#tag/orders/POST/orders/{orderId}/submit)

### Option B: Hosted payment page

If you prefer a managed payment flow, create a payment session with `hosted: true` to get a checkout URL. Redirect the customer to the provider-hosted payment page.

```bash
curl -X POST "{BASE_URL}/payment-sessions" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "{orderId}",
    "paymentProvider": "STRIPE",
    "hosted": true,
    "returnUrl": "https://yourapp.com/payment/success",
    "cancelUrl": "https://yourapp.com/payment/cancel"
  }'

# Redirect the customer to provider.checkoutUrl from the response
```

After the customer completes payment, they are redirected to your `returnUrl`. There is no submit call to make — once the payment succeeds, the order is submitted automatically. Poll the order until it leaves `PENDING_PAYMENT`, or subscribe to the `order.statusChanged` webhook event.

```bash
# Poll the order state
curl "{BASE_URL}/orders/{orderId}" \
  -H "X-API-Key: $API_KEY"

# When the payment succeeds, the state moves from PENDING_PAYMENT to SUBMITTED
```

See [Create Payment Session](/api-reference/payment-sessions.md#tag/payment-sessions/POST/payment-sessions) and [Get Payment Session](/api-reference/payment-sessions.md#tag/payment-sessions/GET/payment-sessions/{paymentSessionId})

### Option C: Embedded payment widget

Create a payment session with `hosted: false` (or omit the field) to get provider credentials for rendering a payment form directly in your UI.

```bash
curl -X POST "{BASE_URL}/payment-sessions" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "{orderId}",
    "paymentProvider": "STRIPE"
  }'

# Use provider.clientSecret and provider.publishableKey from the response
# to render a payment widget
```

Use the returned credentials with the provider's client SDK. For example, with Stripe Elements:
```javascript
const stripe = Stripe(publishableKey);
const elements = stripe.elements({ clientSecret });

const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');

// When the customer submits the form:
const { error } = await stripe.confirmPayment({
  elements,
  confirmParams: {
    return_url: 'https://yourapp.com/payment/success',
  },
});
```

After the payment completes, the order is submitted automatically — no submit call is needed. Poll the order until it leaves `PENDING_PAYMENT`, or subscribe to the `order.statusChanged` webhook event:

```bash
curl "{BASE_URL}/orders/{orderId}" \
  -H "X-API-Key: $API_KEY"

# When the payment succeeds, the state moves from PENDING_PAYMENT to SUBMITTED
```

See [Create Payment Session](/api-reference/payment-sessions.md#tag/payment-sessions/POST/payment-sessions)

## Zero-total orders with payment profile

An order with a total of zero can still need a stored payment method, as a trial subscription does. Submit that order with a payment profile session ID.

```bash
curl -X POST "{BASE_URL}/orders/{orderId}/submit" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentProfileSessionId": "profile-session-id"
  }'
```

## Order states

| State              | Description                                    |
| ------------------ | ---------------------------------------------- |
| `PENDING`          | The order is a cart. You can still change it   |
| `PENDING_PAYMENT`  | The order is locked and waits for the payment  |
| `SUBMITTED`        | You submitted the order for processing         |
| `PENDING_APPROVAL` | The order waits for an approval                |
| `PROCESSING`       | The order is in fulfillment                    |
| `COMPLETED`        | The platform fulfilled the order               |
| `CANCELLED`        | The order was canceled before it completed     |
| `EXPIRED`          | The order expired after a period of inactivity |
| `FAILED`           | The fulfillment of the order failed            |

## Payment session statuses

| Status            | Description                                       |
| ----------------- | ------------------------------------------------- |
| `PENDING`         | Session created, awaiting customer payment        |
| `REQUIRES_ACTION` | The customer has one more step, such as 3D Secure |
| `COMPLETED`       | Payment successfully collected                    |
| `FAILED`          | Payment failed                                    |

## Requirements reference

When you retrieve an order after calculating the price, the `requirements` object tells you what is needed before submission.

| Requirement              | Values                                 | Description                               |
| ------------------------ | -------------------------------------- | ----------------------------------------- |
| `requiresPayment`        | `NOT_REQUIRED`, `OPTIONAL`, `REQUIRED` | Whether payment must be collected         |
| `requiresPaymentProfile` | `NOT_REQUIRED`, `OPTIONAL`, `REQUIRED` | Whether a stored payment method is needed |
| `requiresSigning`        | `NOT_REQUIRED`, `OPTIONAL`, `REQUIRED` | Whether a digital signature is needed     |

## Next steps

- [Orders](/api-reference/orders.md) — Full order management API reference
- [Payment sessions](/api-reference/payment-sessions.md) — Managed payment session creation and management
- [Payment profiles](/api-reference/payment-profiles.md) — Stored payment methods for recurring billing
- [Webhooks](/api-reference/webhooks.md) — Set up notifications for order and payment events
