Stripe Payment Integration - Phase 1 Backend Implementation Summary
Overview
This document summarizes the complete backend implementation of Stripe payment integration for the CivStart GovFit platform, including GovFit subscriptions and credit pack purchases with monthly credit rollover.
Implementation Date: December 2, 2024 Status: ✅ Phase 1 Backend Complete
Implementation Checklist
✅ 1. Database Schema Updates
Location: packages/database/prisma/schema.prisma
StartupContact Model - New Fields:
stripeCustomerId(String?, unique) - Stripe customer IDgovfitSubscriptionId(String?, unique) - GovFit subscription IDgovfitSubscriptionStatus(String?) - Subscription status (active/past_due/canceled/unpaid)govfitCurrentPeriodEnd(DateTime?) - Current billing period end datesubscriptionGracePeriodEnd(DateTime?) - Grace period end date (1 day after expiry)rolloverCreditsUsed(Int, default 0) - Track rolled over credits
New Models Created:
CreditPackSubscription:
- Tracks monthly credit pack subscriptions
- Fields: id, startupId, stripeSubscriptionId, stripePriceId, credits, status, currentPeriodEnd, lastCreditAllocation, rolloverLimit, metadata
- Indexes on: startupId, stripeSubscriptionId, status, currentPeriodEnd
StripeEvent:
- Idempotent webhook event tracking
- Fields: id, stripeEventId, type, customerId, subscriptionId, processed, processingError, rawEvent, createdAt, processedAt
- Prevents duplicate event processing
Migration: 20251201184508_add_stripe_payment_integration
✅ 2. Stripe SDK Integration
Package Installed: stripe@latest
Installation Method: pnpm add stripe --filter backend
✅ 3. Environment Configuration
Location: .env.template
Variables Added (All Environments):
STRIPE_SECRET_KEY=sk_test_... # sk_live_... for production
STRIPE_PUBLISHABLE_KEY=pk_test_... # pk_live_... for production
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
✅ 4. Module Structure
Location: apps/backend/src/stripe/
Files Created:
stripe/
├── dto/
│ ├── index.ts
│ ├── create-checkout-session.dto.ts
│ ├── cancel-subscription.dto.ts
│ └── subscription.dto.ts
├── interfaces/
│ └── stripe-metadata.interface.ts
├── stripe.service.ts
├── stripe.controller.ts
├── stripe-webhook.controller.ts
└── stripe.module.ts
✅ 5. Core Stripe Service
Location: apps/backend/src/stripe/stripe.service.ts
Key Methods Implemented:
Customer Management:
createOrGetCustomer(email, startupId, startupName)- Create/retrieve Stripe customer
Checkout:
createCheckoutSession(startupId, priceId, successUrl, cancelUrl)- Generate checkout URLparsePriceId(priceId, startupId, startupEmail)- Extract product metadata
Webhook Handlers:
handleCheckoutComplete(session)- Process successful checkout- GovFit: Creates subscription, adds 1 credit/month
- One-time pack: Adds credits immediately
- Monthly pack: Creates CreditPackSubscription, sets rollover limits
handleInvoicePaid(invoice)- Monthly renewal with rollover logichandleSubscriptionDeleted(subscription)- Cleanup on cancellationhandlePaymentFailed(invoice)- Update status to past_due
Subscription Management:
getActiveSubscriptions(startupId)- List all active subscriptionscancelSubscription(subscriptionId, startupId)- Cancel subscriptiongetBillingHistory(startupId, limit)- Retrieve past invoicesgetUpcomingInvoice(startupId)- Get next billing cycle info
Security:
verifyWebhookSignature(payload, signature)- Webhook signature verification
✅ 6. Webhook Controller
Location: apps/backend/src/stripe/stripe-webhook.controller.ts
Endpoint:
POST /stripe/webhook - Receives all Stripe webhook events
Events Handled:
- checkout.session.completed - Process new purchases
- invoice.paid - Handle monthly renewals with rollover
- invoice.payment_failed - Mark subscriptions as past_due
- customer.subscription.deleted - Handle cancellations
- customer.subscription.updated - Update subscription status
Features:
- Idempotent event processing (prevents duplicates)
- Event logging in
StripeEventtable - Error tracking with
processingErrorfield - Raw event storage for debugging
✅ 7. REST API Controller
Location: apps/backend/src/stripe/stripe.controller.ts
Endpoints (All Protected by ClerkAuthGuard):
POST /stripe/create-checkout-session
- Body:
{ priceId, successUrl, cancelUrl } - Returns:
{ checkoutUrl, sessionId }
GET /stripe/subscriptions
- Returns:
{ govfit, creditPacks[], totalMonthlyCredits }
POST /stripe/cancel-subscription
- Body:
{ subscriptionId } - Returns:
{ success, message, canceledAt }
GET /stripe/billing-history?limit=10
- Returns:
{ invoices[], hasMore }
GET /stripe/upcoming-invoice
- Returns:
{ amount, currency, periodStart, periodEnd, items[] }
GET /stripe/health
- Returns:
{ status, timestamp }
✅ 8. Subscription Service
Location: apps/backend/src/services/subscription.service.ts
Cron Jobs:
Daily Grace Period Check (@Cron(EVERY_DAY_AT_MIDNIGHT))
- Checks all expired subscriptions
- Locks access if grace period (1 day) expired
- Sends 7-day expiry warnings
Key Methods:
checkExpiredSubscriptions()- Daily cron joblockExpiredSubscription(startupId)- Lock access for expired subssendExpiryWarnings()- Send 7-day warningsisSubscriptionActive(startupId)- Check if subscription is activegetSubscriptionStatus(startupId)- Get detailed subscription statustriggerSubscriptionCheck()- Manual trigger for admin
✅ 9. Subscription Access Guard
Location: apps/backend/src/auth/subscription.guard.ts
Usage:
@UseGuards(ClerkAuthGuard, ActiveSubscriptionGuard)
Protection Logic:
- Check for active GovFit subscription
- Allow access during 1-day grace period
- Allow unlimited tier (legacy/admin-granted)
- Block access if expired past grace period
Error Response:
{
"statusCode": 403,
"message": "Your GovFit subscription has been canceled...",
"error": "Subscription Required",
"requiresSubscription": true,
"billingUrl": "/settings/billing"
}
✅ 10. Credit Rollover Logic
Rollover Limits per Pack:
1-credit pack → max 4 unused credits
4-credit pack → max 10 unused credits
12-credit pack → max 30 unused credits
28-credit pack → max 50 unused credits
Monthly Reset Algorithm:
unused = creditsRemaining;
rolloverCredits = min(unused, rolloverLimit);
newCreditsTotal = monthlyAllocation + rolloverCredits;
Implementation:
Located in StripeService.handleCreditPackRenewal():
- Calculate unused credits
- Apply rollover limit
- Reset credits:
creditsTotal = allocation + rollover - Log transaction with rollover details
- Emit WebSocket update
✅ 11. WebSocket Events
Location: Integrated via WebSocketService
Subscription Events:
subscription:updated- Status changesubscription:expiring- 7-day warningsubscription:grace_period- Entered grace periodsubscription:locked- Access lockedcredits:reset- Monthly reset with rollover
Credit Update Payload:
{
startupId: string,
creditsRemaining: number,
creditsUsed: number,
creditsTotal: number,
monthlyAllocation: number,
creditTier: string,
lastUpdate: string,
reason: string
}
✅ 12. Module Registration
Location: apps/backend/src/app.module.ts
imports: [
// ... other modules
StripeModule,
];
Product Configuration
GovFit Platform Access
- Price: $12,000/year (billed monthly: $1,000/month)
- Lookup Key:
govfit_platform_access - Credits: 1 credit/month (resets monthly)
- Metadata:
{credits: 1, frequency: 'monthly', productType: 'govfit'}
Credit Packs
1-Credit Pack
- One-time: $650 | Lookup:
creditpack_1_onetime - Monthly: $618/month | Lookup:
creditpack_1_monthly - Rollover: Max 4 unused credits
4-Credit Pack
- One-time: $2,000 | Lookup:
creditpack_4_onetime - Monthly: $1,800/month | Lookup:
creditpack_4_monthly - Rollover: Max 10 unused credits
12-Credit Pack
- One-time: $5,000 | Lookup:
creditpack_12_onetime - Monthly: $4,250/month | Lookup:
creditpack_12_monthly - Rollover: Max 30 unused credits
28-Credit Pack
- One-time: $10,000 | Lookup:
creditpack_28_onetime - Monthly: $8,000/month | Lookup:
creditpack_28_monthly - Rollover: Max 50 unused credits
Webhook Setup Instructions
1. Create Webhook in Stripe Dashboard
Navigate to: Developers → Webhooks → Add endpoint
2. Set Endpoint URL
- Development:
https://api-dev.civstart.ventures/stripe/webhook - Staging:
https://api-staging.civstart.ventures/stripe/webhook - Production:
https://api.civstart.ventures/stripe/webhook
3. Select Events
checkout.session.completedinvoice.paidinvoice.payment_failedcustomer.subscription.deletedcustomer.subscription.updated
4. Copy Webhook Secret
Save the whsec_... key to your environment variables.
Testing Checklist
Unit Tests (TODO - Phase 1.5)
- StripeService customer creation
- Checkout session generation
- Webhook event processing
- Rollover credit calculations
- Subscription status checks
Integration Tests (TODO - Phase 1.5)
- End-to-end checkout flow
- Webhook signature verification
- Idempotent event processing
- Grace period expiry
- Credit rollover on renewal
Manual Testing
- Test GovFit subscription purchase
- Test one-time credit pack purchase
- Test monthly credit pack subscription
- Test subscription cancellation
- Test grace period access
- Test credit rollover on renewal
- Verify webhook events in Stripe dashboard
- Test billing history retrieval
Next Steps (Phase 2 - Frontend)
Frontend Implementation Tasks:
-
Billing Page UI (
/settings/billing)- Subscription status display
- Credit pack purchase interface
- Active subscriptions list
- Billing history table
- Cancel subscription modal
-
Stripe Checkout Integration
- Checkout button component
- Success/cancel redirect handling
- Loading states
-
Subscription Widgets
- Credit balance display
- Expiry warning banners
- Grace period notifications
- Upgrade prompts
-
WebSocket Integration
- Real-time credit updates
- Subscription status changes
- Expiry warnings
Security Considerations
Implemented:
- ✅ Webhook signature verification
- ✅ Clerk authentication on all API endpoints
- ✅ Startup ownership verification
- ✅ Idempotent event processing
- ✅ Raw event logging for audit trails
Production Checklist:
- Use LIVE Stripe API keys (sklive, pklive)
- Enable Stripe Radar for fraud detection
- Set up payment method verification
- Configure webhook retry logic
- Enable 3D Secure authentication
- Set up invoice email notifications
- Configure tax collection (if applicable)
Monitoring & Observability
Logs to Monitor:
- Webhook event processing (
StripeWebhookController) - Failed payment attempts
- Subscription cancellations
- Grace period expirations
- Credit rollover operations
Metrics to Track:
- Webhook processing latency
- Failed webhook events (check
StripeEvent.processingError) - Subscription churn rate
- Average credit usage per startup
- Revenue by product type
API Documentation
Base URL:
- Development:
https://api-dev.civstart.ventures - Staging:
https://api-staging.civstart.ventures - Production:
https://api.civstart.ventures
Authentication:
All endpoints require Clerk JWT token in Authorization header:
Authorization: Bearer <clerk_jwt_token>
Endpoints:
See section 7 above for full endpoint documentation.
Troubleshooting
Common Issues:
1. Webhook signature verification failed
- Verify
STRIPE_WEBHOOK_SECRETis correctly set - Check that raw body is being passed to verification
- Ensure webhook endpoint URL matches Stripe dashboard
2. Customer not found
- Check
stripeCustomerIdis stored in database - Verify customer exists in Stripe dashboard
- Customer may have been deleted manually
3. Credits not rolling over
- Check
lastCreditAllocationtimestamp - Verify
rolloverLimitis set correctly - Check
invoice.paidwebhook is being received
4. Subscription not canceling
- Verify
subscriptionIdbelongs to the startup - Check Stripe subscription status in dashboard
- Look for errors in
StripeEventtable
Database Queries
Check Active Subscriptions:
SELECT
id,
startup_name,
govfit_subscription_status,
govfit_current_period_end
FROM startup_contacts
WHERE govfit_subscription_id IS NOT NULL;
Check Credit Pack Subscriptions:
SELECT
sc.startup_name,
cps.credits,
cps.status,
cps.rollover_limit,
cps.current_period_end
FROM credit_pack_subscriptions cps
JOIN startup_contacts sc ON cps.startup_id = sc.id
WHERE cps.status = 'active';
Check Failed Webhook Events:
SELECT
type,
processing_error,
created_at
FROM stripe_events
WHERE processed = false
OR processing_error IS NOT NULL;
Contact & Support
Implementation Team:
- Backend Lead: [Your Name]
- Database: Prisma + PostgreSQL
- Framework: NestJS 11.x
- Payment Provider: Stripe
Documentation:
- Stripe API Docs: https://stripe.com/docs/api
- NestJS Docs: https://docs.nestjs.com
- Prisma Docs: https://www.prisma.io/docs
Changelog
December 2, 2024 - Phase 1 Complete
- ✅ Database schema with Stripe integration
- ✅ Stripe service with full CRUD operations
- ✅ Webhook handlers for all payment events
- ✅ REST API endpoints for subscription management
- ✅ Subscription access guard
- ✅ Grace period cron jobs
- ✅ Credit rollover logic
- ✅ WebSocket real-time updates
- ✅ Module registration and integration
End of Implementation Summary