Stripe Billing
Subscription billing with monthly/yearly prices, unique lookup keys, and webhook-driven credit sync.
Setup
1. Configure Stripe credentials
Configure secret key, webhook secret, and environment-specific callback endpoints.
2. Create products and prices (monthly/yearly)
Use one Stripe product per plan, and create two recurring prices under that product:
- Monthly price:
recurring.interval = month - Yearly price:
recurring.interval = year
Use a globally unique lookup_key for each price.
| Plan | Monthly lookup key | Yearly lookup key |
|---|---|---|
| Creator | creator_monthly | creator_yearly |
| Pro | pro_monthly | pro_yearly |
| Scale | scale_monthly | scale_yearly |
For top-up credit packs (one-time prices), use:
credits_startercredits_growthcredits_scale
All lookup keys in your Stripe account must be unique.
3. Configure yearly discount
Recommended: create a dedicated yearly price with discount already applied (for example monthly * 12 * 0.9).
Optional: use coupon/promotion code, but keep webhook and entitlement logic idempotent.
4. Map price references in env
This project supports both:
- Direct
price_id(price_xxx) - Lookup reference (
lookup:creator_monthlyor plaincreator_monthly)
You can set price_id in env and override lookup fallback:
STRIPE_PRICE_SUB_CREATOR_MONTHLYSTRIPE_PRICE_SUB_CREATOR_YEARLYSTRIPE_PRICE_SUB_PRO_MONTHLYSTRIPE_PRICE_SUB_PRO_YEARLYSTRIPE_PRICE_SUB_SCALE_MONTHLYSTRIPE_PRICE_SUB_SCALE_YEARLYSTRIPE_PRICE_CREDITS_STARTER(compatible withSTRIPE_PRICE_CREDITS_500)STRIPE_PRICE_CREDITS_GROWTH(compatible withSTRIPE_PRICE_CREDITS_1200)STRIPE_PRICE_CREDITS_SCALE(compatible withSTRIPE_PRICE_CREDITS_3000)
5. Verify webhook event handling
Validate checkout completion, subscription renewal, payment failure, and cancellation events.
6. Seed product/payment config
bun run seed:prod -- --env-file ../../apps/ship-api/.env.productionWhat Bunship Already Implements
- Subscription checkout and one-time checkout
- Customer portal session generation for self-service plan management
- Webhook pipeline in
apps/ship-api/src/services/webhook/stripe.ts - Idempotent credit refresh for renewals (
invoice.paid/invoice.payment_succeeded)
Code Map
- API routes:
apps/ship-api/src/module/subscription.ts,apps/ship-api/src/module/orders.ts - Webhook endpoint:
apps/ship-api/src/module/webhook/stripe.ts - Webhook business logic:
apps/ship-api/src/services/webhook/stripe.ts - Product/price seed:
packages/db/src/drizzle/seed/product.ts
Buyer Checklist
- Stripe products/prices in dashboard match your seed config.
- Webhook endpoint is reachable from Stripe in production.
- Failed payment, cancellation, and renewal flows are tested.
- Dashboard and admin pages show consistent order/subscription status.