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(default10for two months free) - Change tiers on the site by editing hybrid pricing and redeploying
2. Stripe Dashboard
- Connect your account and copy API keys into
.env. - Enable the Customer Portal (invoices, payment methods, cancel).
- 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.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.paidinvoice.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
basePriceUsdin hybrid pricing → new checkouts use the new amount