fix(billing): refuse Stripe checkout for AWS Marketplace-billed customers
Merged
WA
3d ago+331+331+012345678901234567890123456789-11-11-01234567890123456789
Summary
- AWS Marketplace customers are billed by AWS, and a listed product must be billed entirely through AWS Marketplace. But nothing on the server stopped a Marketplace-billed org or account from reaching Stripe checkout, and such a customer looks Free to Stripe. The only gate was a client hook that, when its license lookup failed, reported "not Marketplace-billed" and showed the plans. Found in review of the v0.9.45 release (#9744).
- Server: checkout refuses a reference with a pending or active Marketplace license.
- Orgs are checked in
authorizeSubscriptionReference. - Personal accounts are checked in
assertPersonalCheckoutAllowed, because the Stripe plugin skipsauthorizeReferencefor personal checkouts. - Only
upgrade-subscriptionis affected: billing portal, cancel and list still work, and staff Enterprise provisioning uses a separate path. - It does nothing when
AWS_MARKETPLACE_PRODUCT_CODEis unset, so self-hosted and non-prod are unchanged.
- Orgs are checked in
- Client: an unresolved lookup now keeps plans hidden, as Marketplace billing does. If it fails, the upgrade page says it couldn't confirm how the workspace is billed and offers "Try again", so a Stripe customer can't get stuck there.
Type of Change
- Bug fix
Testing
checkout-guard.integration.ts(real Postgres), 9 pass:- refuses an org with a pending or active license, and an account-registered license;
- allows an inactive license, no license, and an unset product code (org and account);
- other billing actions still pass.
hooks/queries/aws-marketplace.test.tsx, 2 pass: a rejected lookup leaves billing unresolved, and[]resolves it as not Marketplace-billed. The existingauthorization.test.tsstill passes.- Red-proofed each guard separately: the org check, the personal check, the product-code switch, the inactive filter, the upgrade-only scope, and the hook's resolved state. Each fails only its own tests.
tsc --noEmit -p apps/sim,bun run lint, plus source-conventions, api-validation:strict, boundaries, react-query, client-boundary, comment-hygiene, test-patterns, explicit-any and utils.
Checklist
- Code follows project style guidelines
- Self-reviewed my changes
- Tests added/updated and passing (new tests pass the
test-auditauthoring gate) - No new warnings introduced
- I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)