What changed, and why
Buyers had no way to redeem a coupon when buying a run pack. The Checkout session this route builds never turned on Stripe's promotion-code field, so a valid code had nowhere to go.
The fix is one line: allow_promotion_codes: true on the session. Stripe then shows its "Add promotion code" box and does all the work server-side — matching the code to an active promotion, checking its rules, and re-pricing the session. Nothing about which runs get credited changes. The blast radius is the paid checkout path only; free play, grounding, and the game loop are untouched.
The one line, and why the money still adds up
The flag sits at the top of the sessions.create options, right beside mode: "payment". Everything below it — the line item, the metadata, the redirect URLs — is unchanged.
The new flag and its rationale; the rest of the session is as before.
server/api/checkout.post.ts · 93 lines
⋯ 52 lines hidden (lines 1–52)
⋯ 16 lines hidden (lines 78–93)
The test that pins it
The spec drives the real route handler with Stripe mocked at the boundary, then reads back the exact options passed to sessions.create. The new case asserts the flag is present and true. It discriminates: on the pre-fix code the field is undefined, so toBe(true) fails — the test only passes because the flag is actually sent.
The new case sits beside the metadata test and reuses its arg-capture pattern.