Docs

Search documentation

Jump to a documentation page.

PricingMarketingApp

Search documentation

Jump to a documentation page.

Stripe

Stripe & billing

Org-level subscriptions use Stripe (test keys by default) with server code in packages/server/src/billing/ and UI on the product app (/settings/billing, pricing/checkout flows).

Configuration

Env varPurpose
STRIPE_SECRET_KEYAPI secret; must be sk_test_... unless STRIPE_ALLOW_LIVE=1.
STRIPE_WEBHOOK_SECRETVerifies POST /api/stripe/webhook.
STRIPE_PRICE_STARTER / STRIPE_PRICE_PROPrice ids (price_...), not product ids.

Client singleton and guards: packages/server/src/billing/stripe-config.ts (getStripe(), getStripeWebhookSecret()).

Database

  • Table subscriptions (one per organization): stripe_customer_id, stripe_subscription_id, stripe_price_id, status, billing period, cancel_at_period_end.
  • Checkout and customers attach organizationId in metadata so webhooks and reconciliation can resolve the workspace.

Server functions (high level)

  • getPricingCatalog — Plans from PRICING_CATALOG in shared config, filtered to env-configured price ids.
  • getOrgBillingSummary — Org subscription snapshot for settings UI (membership check).
  • ensureStripeCustomerForOrg — Creates or recovers Stripe customer; clears stale IDs if the customer does not exist for the current API key.
  • createStripeCheckoutSession / Customer Portal — Restricted to owner/admin; price ids must match configured catalog env vars.
  • reconcileOrgSubscriptionFromStripe — Pulls latest subscription from Stripe (retries with delay) and upserts DB — useful when webhooks are not wired locally.

Implementation: packages/server/src/billing/billing-actions.ts, subscription-sync.ts.

Webhook endpoint

  • Route: apps/web/src/routes/api/stripe/webhook.tsxpackages/server/src/billing/stripe-webhook.ts.
  • Verifies signature with STRIPE_WEBHOOK_SECRET.
  • Handled events include:
    • checkout.session.completed — sync from session.
    • customer.subscription.created / updated — sync by subscription id.
    • customer.subscription.deleted — mark canceled.
    • invoice.payment_failed — notify org billing recipients (see billing-notification-hooks).
    • customer.subscription.trial_will_end — trial ending notifications where implemented.

Local development

  • Without stripe listen, use “Pull from Stripe” / reconcileOrgSubscriptionFromStripe after checkout so the DB updates.
  • With webhooks: stripe listen --forward-to http://localhost:3001/api/stripe/webhook (use your app port) and set STRIPE_WEBHOOK_SECRET to the CLI signing secret.

Billing access

  • Only owner and admin org roles may manage billing (assertBillingManager in billing-actions.ts).

Related