# Billing and checkout

URL: https://docs.spacerun.co/docs/platform/guides/billing-checkout

> Read balances, launch checkout and the billing portal, and reconcile ledger and invoices.

Prerequisites: a user JWT, a configured product catalog and Stripe integration. The examples use [Quickstart's apiRequest helper](https://docs.spacerun.co/docs/platform/guides/quickstart). Replace every example price/product ID with one returned by your deployment.

## Choose and purchase a product

1. Read [listPublicProducts](https://docs.spacerun.co/docs/platform/reference/public-site/listPublicProducts) or the purchasable packs from [getMyBilling](https://docs.spacerun.co/docs/platform/reference/billing/getMyBilling). The public products endpoint returns a bare array.
2. Select the product's configured price. A credit pack uses one-off payment; a plan uses a single-seat subscription.
3. Create [createCheckoutSession](https://docs.spacerun.co/docs/platform/reference/billing/createCheckoutSession) and redirect to its non-null URL.

```ts
const checkout = await apiRequest("/v1/me/billing/checkout-session", {
  method: "POST",
  body: JSON.stringify({
    priceId: "price_example",
    successUrl: "https://portal.example.com/billing?checkout=success",
    cancelUrl: "https://portal.example.com/billing?checkout=cancelled",
    quantity: 1,
  }),
});
if (!checkout.url) throw new Error("Checkout did not return a redirect URL");
window.location.assign(checkout.url);
```

```json
{"url":"https://checkout.stripe.com/example","id":"cs_example"}
```

Supply `productId` or `priceId`. When supplying both, the price must match the catalog product's default. `quantity` applies to credit packs; subscriptions remain single-seat. The server selects the payment mode and creates the customer's Stripe record when needed.

Returning to `successUrl` is not evidence that credits are available. Re-read billing until the processed payment is reflected; do not update balances from a redirect query parameter. Checkout has no client-facing `Idempotency-Key` contract, so avoid automatically resending a POST after an ambiguous result.

## Manage and inspect billing

[createBillingPortalSession](https://docs.spacerun.co/docs/platform/reference/billing/createBillingPortalSession) takes `{ "returnUrl": "https://portal.example.com/billing" }` and returns `{ "url": "..." }`. Redirect there for provider-managed subscription/payment settings.

[getMyBilling](https://docs.spacerun.co/docs/platform/reference/billing/getMyBilling) includes credit balance, tier/limits, subscription state and live credit lots. Numeric values can be strings or numbers; normalize before arithmetic. [listMyCreditLedger](https://docs.spacerun.co/docs/platform/reference/billing/listMyCreditLedger) has `{ data, meta: { page, per_page } }` without `total`. [listMyInvoices](https://docs.spacerun.co/docs/platform/reference/billing/listMyInvoices) wraps data and returns up to 50 recent invoices; it is empty before a Stripe customer exists. Stripe invoice amount fields use the currency's minor units; credit values are separate units.

Recover from `404 product` by refreshing catalog choices. `422 price_mismatch` or `product_has_no_price` needs a corrected selection/catalog configuration. Maintenance can return 503 with `Retry-After`; see [Errors](https://docs.spacerun.co/docs/platform/guides/errors-pagination-limits). Staff adjustments, gifts, refunds and plan changes have separate permissions and operations in the reference; they are not a user checkout shortcut.
