web/src/ee/features/billing/README.md
Stripe Billing powers subscriptions and usage-based pricing for organizations. We primarily use the new flexible billing model (plan + usage) and still handle legacy, single-item metered subscriptions during migration.
See web/src/ee/features/billing/server/cloudBillingRouter.ts.
getSubscriptionInfo — live cancellation/scheduled-change info from Stripe.createStripeCheckoutSession — starts checkout for a product from the catalogue.changeStripeSubscriptionProduct — switches plan (upgrade now, downgrade at period end).cancelStripeSubscription / reactivateStripeSubscription — manage cancellation flags.clearPlanSwitchSchedule — releases any active/not-started schedule.getStripeCustomerPortalUrl — portal for payment methods, tax IDs, invoices (not for plan switches).getInvoices — paginated invoice list with subscription/usage/tax breakdown and preview row.Implemented in web/src/ee/features/billing/server/stripeBillingService.ts.
Checkout
subscription_data.billing_mode is set to flexible; metadata carries orgId and cloudRegion.Plan Changes
Cancellation / Reactivation
cancel_at_period_end (or clears cancel_at); reactivate clears cancellation flags.Cloud Config
cloudConfig.stripe: customerId, activeSubscriptionId, activeProductId, activeUsageProductId.activeProductId and catalogue mapping.plan is set (manual override), creating/changing subscriptions is blocked until removed.Usage Meter in Stripe
Hourly Job
cloudUsageMeteringQueue).cron_jobs table to ensure singleton execution/backfill.See web/src/ee/features/billing/server/stripeWebhookHandler.ts.
cloudRegion.orgId, cloudRegion) is set (falls back to checkout session lookup if needed).customerId, activeSubscriptionId, activeProductId, activeUsageProductId to org cloudConfig; on deleted: clears active IDs.invoice.created: recreates usage alert to make alerts fire once per billing period.billing.alert.triggered: emails admins/owners and configured recipients.Lightweight guidance for local/staging testing. Stripe docs are solid references.
cloudConfig to:{ "stripe": { "customerId": "cus_T2dbT3t6hyp9RE" } }
NODE_OPTIONS="--max-old-space-size=8192" pnpm build && pnpm start
stripe listen --forward-to localhost:3000/api/billing/stripe-webhook
STRIPE_WEBHOOK_SIGNING_SECRET and STRIPE_SECRET_KEY point to sandbox and have correct permissions.