> ## Documentation Index
> Fetch the complete documentation index at: https://docs.clinkbill.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How Payments Work

> Checkout Session, Order, and your own order — what each one actually means.

## What actually happens during a payment

<Steps>
  <Step title="The customer clicks Buy on your site">
    Your frontend sends the product and quantity to your own backend. It does not call Clink.
  </Step>

  <Step title="Your backend creates its own order first">
    Record what was bought, for how much, and by whom. This record is the source of truth in your system, and every status below hangs off it.
  </Step>

  <Step title="Your backend creates a Checkout Session">
    Clink returns a `sessionId` and a `url`. That `url` is the checkout page.
  </Step>

  <Step title="The customer pays">
    Choosing a payment method, entering card details, and passing 3DS all happen on Clink's page. You never touch card numbers.
  </Step>

  <Step title="Clink sends the result to your webhook">
    `order.succeeded` on success, `order.failed` on failure.
  </Step>

  <Step title="Your backend confirms payment, then fulfills">
    Only after the event is received, verified, and matched to your order do you ship, top up, or grant access.
  </Step>
</Steps>

Step 2 is the one people skip. If you use Clink's Session as your order record, reconciliation breaks down later: Clink knows someone paid 19.99 USD, but not which user of yours bought which thing.

## Three things called "order"

Three things in those six steps can all be called an order: the **Checkout Session** Clink creates, the **Order** the customer's payment produces, and the merchant backend's own record. Similar names, completely different jobs.

| Object           | What it is                                              | Created by                    | Used for                                           |
| ---------------- | ------------------------------------------------------- | ----------------------------- | -------------------------------------------------- |
| Checkout Session | One checkout visit — a time-limited payment entry point | Clink, when you call the API  | Getting the customer to a checkout page            |
| Order            | The result of one payment attempt                       | Clink, when the customer pays | Deciding whether the money actually arrived        |
| Your own order   | The business record in your database                    | Your backend                  | Deciding whether to fulfill, and reconciling later |

One Session can produce several Orders. A customer whose first card is declined and who then pays with a second card produces two Orders under the same Session. So read the Order to determine the payment result, not the Session.

## Reading the statuses

### Checkout Session

`status` tells you whether the payment entry point is still usable:

| Session `status` | Meaning                                         | What to do                                           |
| ---------------- | ----------------------------------------------- | ---------------------------------------------------- |
| `open`           | Still valid, payment is still possible          | Wait for the customer, or send them back to checkout |
| `completed`      | Payment succeeded; no further attempts accepted | Read the Order via `orderId` to confirm the result   |
| `expired`        | The entry point expired; no new attempts        | Create a new Session to keep collecting              |

`paymentStatus` separately reports `unpaid`, `processing`, or `paid`, which is convenient for showing progress in your UI. For reconciliation, still read the Order.

<Warning>
  `expired` does not mean the payment failed. A customer may have started a payment moments before expiry, and that Order can still succeed afterwards. Do not mark your order as failed just because the Session expired.
</Warning>

### Order

`status` is the payment result, and the field you map onto your own business status:

| Order `status`     | Meaning                                         | What to do                                                                                           |
| ------------------ | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `success`          | Payment succeeded                               | Fulfill, idempotently                                                                                |
| `pending`          | In progress, no result yet                      | Wait for the webhook, or poll `GET /order/{id}`. **Do not charge again**                             |
| `failed`           | Payment failed                                  | Record `failureCode` and `failureMessage`; let the customer retry once you know nothing is in flight |
| `requires_action`  | The customer must do one more step, such as 3DS | Use the returned `action` to guide them through it                                                   |
| `partial_refunded` | Partially refunded                              | Update the refunded total                                                                            |
| `refunded`         | Fully refunded                                  | Revoke access or run your returns flow                                                               |

`pending` causes the most damage. It means "not known yet", not "failed". Starting a second charge here is how customers get billed twice.

## What counts as grounds for fulfillment

Exactly one thing: your backend received a signature-verified `order.succeeded` event, or polled `GET /order/{id}` and got `success`, and that Order matches your own order record.

<Warning>
  These four look like the money arrived. On their own, none of them count:

  * The customer landed on your `successUrl` — anyone can type that address
  * The Session `status` became `completed` — that only says the entry point closed
  * Your webhook returned HTTP 200 — that only tells Clink you received something
  * A frontend SDK fired `complete` or `session-success` — browser events can be forged
</Warning>

## Two environments

Clink has a sandbox and a production environment. The sandbox is what people usually call the test environment; no real money moves.

|              | Sandbox                               | Production                        |
| ------------ | ------------------------------------- | --------------------------------- |
| Dashboard    | `https://uat-dashboard.clinkbill.com` | `https://dashboard.clinkbill.com` |
| API          | `https://uat-api.clinkbill.com`       | `https://api.clinkbill.com`       |
| Key prefixes | `sk_uat_` / `pk_uat_`                 | `sk_prod_` / `pk_prod_`           |
| Money        | None. Charge freely                   | Real charges                      |

The two environments are separate dashboards. You use the same account and password for both, but data and keys are fully isolated — products, customers, and orders created in the sandbox do not exist in production.

<Warning>
  During development, log in to the sandbox dashboard at `uat-dashboard.clinkbill.com`. Keys initialized in the production dashboard start with `sk_prod_`, and using one against the sandbox API fails authentication with an error that does not mention environments at all.
</Warning>

Test card `4242 4242 4242 4242`, any 3-digit CVC, any future expiry. It works in the sandbox only.

## Next

<CardGroup cols={2}>
  <Card title="Quickstart" icon="trending-up" href="/quickstart">
    Follow along and see a successful order in about fifteen minutes.
  </Card>

  <Card title="Choose an integration" icon="git-branch" href="/choose-integration">
    Redirect to checkout, or embed it in your own page.
  </Card>
</CardGroup>
