Checkout
Moving a workspace onto a paid plan, through Stripe Checkout.
Generated from
packages/api/src/billing/checkout.controller.ts by packages/docs/tools/generate-reference.mjs. Every example is a real exchange recorded against the control plane by packages/api/src/testing/reference-examples.spec.ts, with ids from that run. Credentials in the formats this product issues (kr_, krsk_, krses_, JWTs) and values under credential-named fields (password, *_secret, *_token, *_key, lookup_hash) are elided at record time; the recorded file and every page of this site are swept for credential shapes (kr_, krsk_, krses_, JWTs, URLs carrying a password, password fields) before either is accepted. Do not edit by hand: src/reference.spec.ts regenerates it and fails on a difference.POST /v1/billing/checkout_session -- the one write on the billing plane,
and the only thing in this repository that can move a workspace off the entry
plan.
Three guards, each for a different reason:
assertLiveCredential, because a subscription carries no environment at all. Akrsk_test_key is the credential a vendor hands to CI, and "CI started a paid subscription" is the same class of mistake as "CI deleted the project holding every live key".env-boundary.spec.tssweeps this route for it, from Fastify's own route table.assertOwner, because buying is an owner's decision. It returns early for a machine principal by design, which is exactly why the line above it is not redundant.- the gateway being configured, because with no Stripe secret key there is nothing to check out against. Unset closes the endpoint rather than half-opening it.
What it deliberately does not do: change the entitlement. The frozen
columns on keyring.workspace_subscription move when Stripe says the
subscription exists, through POST /v1/webhooks/stripe, and never on the
strength of somebody having opened a payment page.
POST /v1/billing/checkout_session
Answers 201 on success.
Authentication.
- A
krsk_secret key or akrses_dashboard session, asAuthorization: Bearer.
Body. Validated by this schema, from the control plane's own source:
const CreateCheckoutSession = z.object({
/**
* A plan **code**, never a Stripe price id. A caller-supplied price id would
* let anyone subscribe to a retired price, a price belonging to a plan that
* is not self-serve, or one created by hand in the Stripe dashboard for a
* negotiated contract -- none of which the catalog offers. The code is
* resolved here against the same `where retired_at is null` the published
* ladder is read through.
*/
plan_code: z.string().min(1).max(64),
});Example.
curl https://api.keyringdev.com/v1/billing/checkout_session \
-X POST \
-H "Authorization: Bearer $KEYRING_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"plan_code": "team"
}'HTTP/1.1 201 Created
Content-Type: application/json
{
"object": "checkout_session",
"id": "cs_reference_muqfmpe2",
"url": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3",
"plan_code": "team"
}