# Object model (/en/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 [#the-objects]

```
Checkout Session                           cs_*
├── customer                               cus_*
└── paymentIntent                          pi_*
    ├── charges[]                          ch_*
    │   └── balanceTransaction             txn_*  (the receivable)
    └── refunds[]                          re_*
        └── balanceTransaction             txn_*  (the reversal)
```

| Object                                                                                   | What it represents                                                                                                                                             |
| ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <ApiLink href="/api-reference/objects/checkout-session">Checkout Session</ApiLink>       | **A configured checkout.** Line items, customer fields, and the integration surface (hosted page, drop-in, elements). One per customer attempt.                |
| <ApiLink href="/api-reference/objects/customer">Customer</ApiLink>                       | **The shopper's record.** Contact, billing, and shipping details. Captured during checkout, or linked by `customerId` if you already have one.                 |
| <ApiLink href="/api-reference/objects/payment-intent">Payment Intent</ApiLink>           | **The transaction.** What money is meant to move, the method used, and the state it's in. The financial counterpart to the Checkout Session.                   |
| <ApiLink href="/api-reference/objects/charge">Charge</ApiLink>                           | **A 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. |
| <ApiLink href="/api-reference/objects/refund">Refund</ApiLink>                           | **A money reversal.** Sends money back to the customer, full or partial, against a captured Charge.                                                            |
| <ApiLink href="/api-reference/objects/balance-transaction">Balance Transaction</ApiLink> | **A ledger row.** Every Charge and every Refund produces one. The signed value is what hits your balance.                                                      |

## What you create vs what you read [#what-you-create-vs-what-you-read]

<Callout type="info">
  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.
</Callout>

The two creation surfaces:

* `POST /checkout/sessions` produces a <ApiLink href="/api-reference/objects/checkout-session">Checkout Session</ApiLink>. 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 <ApiLink href="/api-reference/objects/refund">Refund</ApiLink> 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 [#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 [#how-the-objects-connect]

<Cards>
  <Card icon="<ShoppingCart />" title="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.
  </Card>

  <Card icon="<Layers />" title="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.
  </Card>

  <Card icon="<ArrowDownToLine />" title="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.
  </Card>

  <Card icon="<Anchor />" title="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.
  </Card>
</Cards>

## When you reach into the deeper objects [#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 payment             | Read `cs_*` on `checkout.session.completed`. That's the whole job for most integrations.                                          |
| Issue a refund from code                   | `POST /refunds` with the `pi_*`. See the [Refunds](/integrate/refunds) integration page.                                          |
| Look at the exact charge that ran          | Read `paymentIntent.charges[]` on the session response.                                                                           |
| Reconcile against your balance and payouts | Read `paymentIntent.charges[].balanceTransaction`. Each carries the settled amount, fees, and which payout (if any) it landed in. |
| Look up a returning customer               | Read `cs.customer` (or fetch <ApiLink href="/api-reference/objects/customer">Customer</ApiLink> by `cus_*`).                      |

## The events you receive [#the-events-you-receive]

The one event every integration listens for is `checkout.session.completed`. The `data.object` is a full <ApiLink href="/api-reference/objects/checkout-session">Checkout Session</ApiLink>, 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](/integrate/webhooks/event-reference).

## Where to next [#where-to-next]

<Cards>
  <Card icon="<Boxes />" title="Checkout Session reference" href="/integrate/checkout-session/overview">
    The fields on the session you create, the lifecycle it moves through, and what you can
    configure.
  </Card>

  <Card icon="<RotateCcw />" title="Refunds" href="/integrate/refunds">
    How to issue a refund against a `pi_*` (or a specific `ch_*`). Single API call.
  </Card>

  <Card icon="<Webhook />" title="Setting up a webhook" href="/integrate/webhooks/setting-up-an-endpoint">
    Receive `checkout.session.completed` and verify the signature.
  </Card>

  <Card icon="<User />" title="Choose your integration" href="/get-started/choose-your-integration">
    Pick the surface that builds the Checkout Session: Payment Links, Hosted, Drop-in, or Elements.
  </Card>
</Cards>