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

# Checkout

> Creating a session on your server, and what your subscriber does with it in your page.

A Checkout session is one subscriber's permission to start one meter. You create it on your server with your secret key, hand its **id** to your own page, and `@elapse/react` does the rest in place. Nobody is sent to a page Elapse hosts, and you never hold a card, a wallet or a balance.

## Creating a session

<CodeGroup>
  ```ts TypeScript theme={"system"}
  const session = await elapse.checkout.sessions.create({
    product: "prod_…",
    successUrl: "https://yourapp.example/ok",
    cancelUrl: "https://yourapp.example/cancel",
    maxDurationSeconds: 3600, // optional cap; default one hour
  });
  // Hand session.id to your page. There is no URL to redirect to.
  return { sessionId: session.id };
  ```

  ```bash cURL theme={"system"}
  curl "$ELAPSE_API_URL/v1/checkout/sessions" \
    -H "Authorization: Bearer $ELAPSE_SECRET_KEY" \
    -H "Content-Type: application/json" \
    -d '{"product":"prod_…","success_url":"https://yourapp.example/ok","cancel_url":"https://yourapp.example/cancel","max_duration_seconds":3600}'
  ```
</CodeGroup>

`max_duration_seconds` caps how long one subscription can run and therefore how much the subscriber is asked to fund: `rate × cap`, shown to them as a dollar amount before they authorise. When the cap is reached the meter stops on its own.

**`success_url` is a security boundary, not a redirect.** Its origin is the only one Elapse will post a signature result back to, so it must be your app's origin. `cancel_url` is where a subscriber who backs out is sent from their own account page.

## What the subscriber does

In your page, not ours:

```tsx theme={"system"}
<ElapseProvider publishableKey="pk_test_…">
  <Authorize session={sessionId} onAuthorised={(e) => setSub(e.subscription)} />
  <Meter session={sessionId} />
</ElapseProvider>
```

1. **`<Authorize>`** shows how long the meter may run and the most it can cost — unless you pass `cap`, in which case you have chosen it and the step is skipped.
2. **Face ID**, in a window Elapse opens on its own origin. Your code never touches the subscriber's wallet.
3. **`<Meter>`** ticks the seconds and the dollars, offers the controls the Product allows, and turns into the receipt when the meter stops.

The money is AUSD in the subscriber's own wallet, in test mode and live mode alike. If it is short of the cap, an **Add funds** step shows the amount needed and continues by itself when it lands. Unused funds come back the moment the meter stops.

## Granting access

Grant access when `subscription.created` arrives at your webhook endpoint — and for a merchant-started Product, when `subscription.updated` says `active`. Revoke it on `subscription.canceled`. Never grant from something the browser told you; a callback in a page can be forged, a signed webhook cannot. The [Quickstart](/quickstart) handler does exactly that with an in-memory map keyed by subscription id.

## Session states

| `status`   | Meaning                                                                                         |
| ---------- | ----------------------------------------------------------------------------------------------- |
| `open`     | Created, not yet authorised. Expires after one hour.                                            |
| `complete` | The subscriber authorised it. A Subscription exists and `checkout.session.completed` has fired. |
| `expired`  | Nobody authorised it within the hour. Create a new one.                                         |

A session is single-use. Create one per visit, not one per product.

## The promise

Whatever happens on the chain underneath, the subscriber only ever sees seconds and dollars. The one exception is opt-in: `<Meter proof>` shows the transaction that started the meter and the one that ended it, for merchants whose audience wants to verify. Your integration never has to mention it.
