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
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.