Files
Kordant/tasks/core-services-implementation/01-stripe-checkout-webhooks.md
2026-05-31 22:03:18 -04:00

58 lines
3.3 KiB
Markdown

# 01. Stripe Checkout, Webhooks, and Subscription State Management
meta:
id: core-services-01
feature: core-services-implementation
priority: P0
depends_on: []
tags: [billing, stripe, payments, foundation]
objective:
- Enable paid customer acquisition by implementing complete Stripe payment lifecycle — checkout, webhook handling, subscription state machine, and customer portal.
deliverables:
- Stripe Checkout session creation for each plan tier (Shield, Guard, Fortress, Family Fortress)
- Webhook endpoint handling all critical Stripe events
- Subscription state machine in Drizzle ORM
- Customer portal (billing settings, plan change, cancellation)
- Trial period support (14-day free trial)
steps:
1. Add `STRIPE_WEBHOOK_SECRET` to `.env.example` and validate in `env.ts`
2. Implement `createCheckoutSession(planId, customerId?, trial?)` in `billing.service.ts`
3. Implement `POST /api/webhooks/stripe` route handler with signature verification
4. Handle events: `checkout.session.completed`, `invoice.payment_succeeded`, `invoice.payment_failed`, `customer.subscription.updated`, `customer.subscription.deleted`
5. Update subscription record in database on each event (status, tier, period end, payment method)
6. Implement `createCustomerPortalSession(customerId)` for subscription management
7. Add trial logic: create subscription with `trial_end`, handle trial-to-paid transition
8. Add proration logic for tier upgrades/downgrades using `proration_behavior: 'create_prorations'`
9. Update billing router tRPC procedures: `getCheckoutUrl`, `getPortalUrl`, `getSubscription`, `cancelSubscription`
10. Add rate limiting on checkout creation (prevent abuse)
tests:
- Unit: Mock Stripe API responses, verify database state transitions for each webhook event
- Integration: Create real Stripe test-mode checkout session, complete payment, verify subscription activation
- E2E: End-to-end checkout flow from dashboard → Stripe Checkout → webhook → active subscription
acceptance_criteria:
- [ ] Customer can click "Subscribe" on Shield plan and be redirected to Stripe Checkout
- [ ] After successful payment, webhook creates active subscription record in database
- [ ] Customer can access billing portal to view invoices, change plan, or cancel
- [ ] Trial subscription auto-converts to paid or suspends after trial ends
- [ ] Tier upgrade creates prorated invoice and updates subscription immediately
- [ ] `invoice.payment_failed` sets grace period status and sends retry email
- [ ] All webhook events are idempotent (duplicate events don't create duplicate records)
- [ ] Webhook handler returns 200 for handled events, 400 for invalid signatures
validation:
- Run `stripe trigger checkout.session.completed` in Stripe CLI, verify database record
- Run `stripe trigger invoice.payment_failed`, verify grace period status
- Create test checkout, pay with `4242 4242 4242 4242`, verify active subscription in dashboard
- Run test suite: `vitest run billing.test.ts`
notes:
- Stripe API version: `2026-04-22.dahlia` (already configured in `stripe.ts`)
- Webhook endpoint must be publicly accessible for Stripe to deliver — use ngrok for local dev
- Store `stripeCustomerId` and `stripeSubscriptionId` on user/subscription records
- Use `stripe-webhook` event type in database for audit trail