fix(billing): refuse Stripe checkout for AWS Marketplace-billed customers

Merged
WA
3d ago
+331-11

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 skips authorizeReference for personal checkouts.
    • Only upgrade-subscription is affected: billing portal, cancel and list still work, and staff Enterprise provisioning uses a separate path.
    • It does nothing when AWS_MARKETPLACE_PRODUCT_CODE is unset, so self-hosted and non-prod are unchanged.
  • 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 existing authorization.test.ts still 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-audit authoring gate)
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)