cmux-billing
Installation
SKILL.md
cmux Billing
Read before changing billing, pricing, Stripe, Pro entitlement, checkout, webhook, or subscription code.
Architecture map
/api/billing/checkoutcreates Stripe Checkout Sessions for Pro whenSTRIPE_SECRET_KEYis set. It setsclient_reference_idto the Stack user id, auto-creates an anonymous Stack user for signed-out buyers, and falls back to the legacy Stack purchase path when Stripe is unset orplan=team. The "already active" short-circuit lives here./api/billing/portalresolves the current Stack user, looks up theirstripe_customersrow, and creates a Stripe customer portal session returning to/pricing./api/billing/subscriptioncancels or resumes the active Stripe Pro subscription;/dashboard/billingrenders localized in-dashboard plan state and self-serve actions.web/services/billing/purchase.tsis the shared idempotent recorder used by/api/billing/completeand/api/stripe/webhook. It attaches email to the purchaser, recordsbilling_email_claimson conflict, and never cross-grants based on an unverified email.- VM code reads the plan from Stack
clientReadOnlyMetadata: a non-emptycmuxVmPlan(operator override) takes precedence, otherwisecmuxPlan(the Stripe mirror) supplies the entitlement.resolveProPlanStatusreports Pro for an activestripe_subscriptionsrow or a paidcmuxVmPlanoverride (pro,team,founders);billingManagementstays Stripe-only, so a granted account shows Pro without a portal link. - The private cmux-admin app (https://cmux-admin.vercel.app/pro, source
admin/in cmuxterm-hq) lets verifiedcmux.com,manaflow.ai, andmanaflow.comaccounts search users, teams, and emails and, after a confirmation dialog: grant or remove the usercmuxVmPlanoverride (/api/admin/users), grant or remove a team override ofteam(/api/admin/teams), grant Pro to an email with no account yet (/api/admin/email-grants, stored inadmin_plan_grantsand applied by the after-sign-in callback once the mailbox is verified), and downgrade a paying customer by cancelling the Stripe subscription at period end (/api/admin/subscriptions, same service as the self-serve form).services/admin/access.tsis the gate,services/admin/proGrants.tswrites under the account-mutation lease and recordsserverMetadata.cmuxAdminPlanGrant(who, when, which plan). Non-admins get 403 on the API; there is no admin page on cmux.com. Theadmin_plan_grantsmigration is an operator step; until it runs, pending email grants report 503 and search omits them. /api/stripe/webhookis signature-verified, insert-first idempotent throughstripe_webhook_events, safe for foreign events in the shared Stripe account, and gates cmux handling onmetadata.app === "cmux". Return 2xx only after durable writes; return 500 to make Stripe retry.