docs
Integrate

Object model

How XPay's resources fit together. What you create, what you read, and the two IDs to store on your order record.

A successful payment on XPay is a small graph of related objects. You create the top one (a Checkout Session) and read the rest as nested fields. This page is the one-screen mental model of how those objects fit together, which IDs you'll see, and what to keep on your order record.

The objects

Checkout Session                           cs_*
├── customer                               cus_*
└── paymentIntent                          pi_*
    ├── charges[]                          ch_*
    │   └── balanceTransaction             txn_*  (the receivable)
    └── refunds[]                          re_*
        └── balanceTransaction             txn_*  (the reversal)
ObjectWhat it represents
Checkout SessionAPIA configured checkout. Line items, customer fields, and the integration surface (hosted page, drop-in, elements). One per customer attempt.
CustomerAPIThe shopper's record. Contact, billing, and shipping details. Captured during checkout, or linked by customerId if you already have one.
Payment IntentAPIThe transaction. What money is meant to move, the method used, and the state it's in. The financial counterpart to the Checkout Session.
ChargeAPIA money-move attempt. One try to authorize and capture money on the customer's payment method. A Payment Intent has more than one if the customer retried.
RefundAPIA money reversal. Sends money back to the customer, full or partial, against a captured Charge.
Balance TransactionAPIA ledger row. Every Charge and every Refund produces one. The signed value is what hits your balance.

What you create vs what you read

You create Checkout Sessions and Refunds. Payment Intents, Charges, and Balance Transactions are read-only resources. You encounter them as nested fields in responses and as IDs in webhook payloads, but the API does not expose direct creation. Every payment flow starts at a Checkout Session.

The two creation surfaces:

  • POST /checkout/sessions produces a Checkout SessionAPI. Behind the scenes the customer's payment runs on a Payment Intent, which produces Charges, which produce Balance Transactions. You see all of them as nested fields on the session, but you don't POST any of them yourself.
  • POST /refunds produces a RefundAPI against a pi_* (the Payment Intent for the payment) or a ch_* (a specific Charge). Provide exactly one. The Refund produces its own Balance Transaction (the reversal).

Everything else is read-only. You retrieve a Checkout Session, you read its paymentIntent, you read its charges, you read their balance transactions. You never create them on your own.

Payment Links are a no-code feature created in the dashboard. When a customer opens a /p/plink_... URL, XPay creates the Checkout Session for that customer automatically. You still receive checkout.session.completed and the same nested object graph as any other integration.

Which IDs to store

Two IDs do different jobs. Keep both on your order record.

  • cs_* is your checkout reference. It identifies the customer's checkout context: which line items they bought, what fields they filled in, which integration surface they used. Use it for support, debugging, and re-rendering the customer's order in your own UI.
  • pi_* is your transaction handle. It identifies the money: what was charged, what was refunded, what settled into your balance. Use it to issue refunds and to reconcile against payouts. One per successful payment.

Both arrive on the checkout.session.completed payload, so capture them at the same time and store both. Each one is the right tool for its own job, and neither replaces the other.

cus_* (if present) is the third id worth keeping when you want to re-find a returning customer across orders. Everything else (ch_*, txn_*) is reachable on demand from GET /checkout/sessions/:id or GET /refunds/:id and rarely needs to live on your order record.

How the objects connect

Checkout Session is the parent

The session has at most one Payment Intent (created when the customer submits the form) and at most one Customer (existing or created during checkout). Read the session, you have the rest.

Payment Intent holds the lifecycle

Every payment attempt on the session goes through one Payment Intent. Retries reuse it; they don't spawn a new one.

Charges and Refunds are the money events

A Charge is "money came in." A Refund is "money went back." Each produces a Balance Transaction, which is the row that settles into your balance and your payouts.

Balance Transactions are the ledger

The signed value of a Balance Transaction (positive for charges, negative for refunds) is the truth about your money. Charges and Refunds are how you describe the event; Balance Transactions are how the books balance.

When you reach into the deeper objects

For most integrations the answer is "rarely." The deeper objects exist so the data is consistent and traceable, not because you need to interact with them directly.

You need to...What to do
Fulfill an order after paymentRead cs_* on checkout.session.completed. That's the whole job for most integrations.
Issue a refund from codePOST /refunds with the pi_*. See the Refunds integration page.
Look at the exact charge that ranRead paymentIntent.charges[] on the session response.
Reconcile against your balance and payoutsRead paymentIntent.charges[].balanceTransaction. Each carries the settled amount, fees, and which payout (if any) it landed in.
Look up a returning customerRead cs.customer (or fetch CustomerAPI by cus_*).

The events you receive

The one event every integration listens for is checkout.session.completed. The data.object is a full Checkout SessionAPI, identical to GET /checkout/sessions/:id, with the full nested graph attached. That single event is enough to fulfill the order and capture every ID you might want to store.

Other events (checkout.session.expired, refund.created, charge.refunded) are useful when you want to react to specific lifecycle changes outside the success path. The full list is on the Event reference.

Where to next

On this page