Billing setup

Wire Stripe so subscriptions sync to Convex without losing webhook events.

1. Pricing source of truth

Platform fees come from lib/hybrid-pricing.ts (and convex/lib/hybridPricing.ts). Checkout uses Stripe price_data to create the subscription product and recurring price in code — you do not need pre-created Products or Price IDs in the Stripe Dashboard.

  • Monthly: tier basePriceUsd
  • Yearly: monthly × STRIPE_YEARLY_MONTHS_PAID (default 10 for two months free)
  • Change tiers on the site by editing hybrid pricing and redeploying

2. Stripe Dashboard

  1. Connect your account and copy API keys into .env.
  2. Enable the Customer Portal (invoices, payment methods, cancel).
  3. No manual products required for Checkout — optional: configure tax, branding, and business details in Stripe settings.

3. Webhook endpoint

Add endpoint https://your-domain.com/api/webhook/stripe and subscribe to:

  • checkout.session.completed
  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • invoice.paid
  • invoice.payment_failed

Set STRIPE_WEBHOOK_SECRET on Next.js. Tier and interval are read from Checkout/session metadata and subscription metadata — not from fixed Price IDs.

4. Convex enforcement

Deploy schema, then enable billing when ready:

npx convex deploy
npx convex env set BILLING_ENFORCED true

With enforcement on, new users must complete Checkout before ingest or project creation. Leave BILLING_ENFORCED unset in dev for Scale defaults.

5. Customer Portal

Enable the Stripe Customer Portal for payment method updates, invoices, and cancellation. Dashboard users open it from Billing.

6. Verify

  • Complete test Checkout → tier on Billing matches metadata
  • Replay a webhook → duplicate, no double tier update
  • Change basePriceUsd in hybrid pricing → new checkouts use the new amount