Skip to main content
Most apps need one page where a customer sees what they’re on and changes it. Load the data from your backend, render it, and send each button to one Ledger call. Resolve the signed-in user’s customerId on your server, never from a value the browser sends, so one customer can’t read or change another’s billing.

Load the page

Balances explains each field. Listing plans and packs needs a key with Catalog read access.

Wire up the buttons

Sell through Checkout

1

Set where Checkout returns

In Settings › Checkout, set a success URL and a cancel URL: the pages in your app that Stripe sends the customer back to. They must be https; plain http is accepted only for localhost in Sandbox. Until you set them, Sandbox returns the customer to the Ledger dashboard. Live requires them.
2

Attach and redirect

A customer with a saved card is charged straight away and gets status: "active", unless the card needs 3-D Secure, which returns a checkoutUrl instead. Calling attach again while a Checkout is open returns the same URL, so a double click is safe. A customer on a free plan keeps it until the paid plan’s payment lands.
3

Confirm from your backend

When the customer lands on your success URL, load the page again from your backend. Ledger grants credits only when Stripe’s signed webhook confirms the payment, usually within seconds; the redirect proves nothing. While currentPlan.status is pending_checkout, show “Confirming your payment” and reload in a moment.

Test it

In Sandbox, pay with Stripe test cards, any future expiry and any CVC: To see renewals without waiting a month, simulate time.

Errors from attach

Billing lifecycle shows what happens from attach to credits, and at each renewal.
Other ways. Without an SDK, use POST /v1/billing/attach.