Payments
Multi-provider payment system supporting Stripe and Razorpay with unified API and automatic entitlement synchronization.
What This System Does
The payment system handles:
- Subscription checkout with Stripe or Razorpay
- Webhook processing for subscription events
- Automatic entitlement synchronization with subscriptions
- Provider-agnostic API (switch providers without code changes)
- Subscription management (cancel, reactivate)
- Failed payment handling and retries
Why It Exists
Payments enable monetization and feature gating. The multi-provider abstraction allows you to choose the best payment provider for your market (Stripe for global, Razorpay for India) without rewriting code.
When You Need to Care About It
You'll interact with payments when:
- Setting up your first subscription plan
- Switching between Stripe and Razorpay
- Adding custom checkout flows or metadata
- Handling subscription lifecycle events
- Customizing payment failure handling
Key Concepts
Provider Abstraction
Both Stripe and Razorpay implement a common PaymentProvider interface. This allows you to switch providers by changing the PAYMENT_PROVIDER environment variable.
Automatic Entitlement Sync
When a subscription is activated, the system automatically grants entitlements based on the plan. When cancelled, entitlements are revoked. This happens via webhooks.
Webhook Verification
All webhooks are cryptographically verified using provider-specific secrets. Invalid signatures are rejected.
Important Files
/lib/payments/index.tsProvider factory. Exports the active payment provider based on environment variable.
/lib/payments/stripe.tsStripe implementation of PaymentProvider interface.
/lib/payments/razorpay.tsRazorpay implementation of PaymentProvider interface.
/lib/webhooks/stripe.tsStripe webhook event handlers. Processes subscription events.
/lib/webhooks/razorpay.tsRazorpay webhook event handlers. Processes subscription events.
/app/api/payments/checkout/route.tsUnified checkout endpoint. Works with both providers.
/app/api/payments/webhook/stripe/route.tsStripe webhook endpoint. Receives and processes Stripe events.
/app/api/payments/webhook/razorpay/route.tsRazorpay webhook endpoint. Receives and processes Razorpay events.
Providers
Stripe
Recommended for global products. Supports credit cards, debit cards, and various payment methods worldwide.
Fees: 2.9% + $0.30 per transaction (US). Additional fees for international cards.
Best for: Global products, international customers, comprehensive payment methods.
Razorpay
Recommended for India-focused products. Supports UPI, netbanking, wallets, and cards in India.
Fees: 2% per transaction (India). Lower fees for UPI/netbanking.
Best for: India-focused products, UPI payments, lower transaction fees in India.
Checkout Flow
The checkout process:
- User clicks "Upgrade" button
- Frontend calls
POST /api/payments/checkoutwith plan name - Server reads
PAYMENT_PROVIDERenvironment variable - Creates checkout session with appropriate provider
- Returns checkout URL
- User completes payment on provider-hosted page
- Provider sends webhook
- Webhook handler verifies signature and creates subscription
- Entitlements sync automatically via plan relationship
Frontend Usage
const response = await fetch("/api/payments/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ planName: "pro" }),
});
const { url } = await response.json();
window.location.href = url;Webhooks
Webhooks automatically sync subscription state and entitlements:
Stripe Events
checkout.session.completed- Creates subscriptioninvoice.paid- Updates status to activecustomer.subscription.updated- Syncs subscription datacustomer.subscription.deleted- Cancels subscriptioninvoice.payment_failed- Marks subscription past_due
Razorpay Events
subscription.activated- Creates subscriptionsubscription.charged- Updates payment statussubscription.cancelled- Cancels subscriptionsubscription.paused- Pauses subscriptionsubscription.resumed- Resumes subscriptionpayment.failed- Marks subscription past_due
Important: Webhooks must be configured in your payment provider dashboard. See the deployment guide for setup instructions.
What You Can Customize
Adding Custom Plans
1. Create plan in payment provider dashboard
2. Add to database via seed script
3. Link provider IDs using update scripts
Custom Checkout Flow
Extend /app/api/payments/checkout/route.ts to add custom metadata or modify checkout parameters.
Custom Webhook Events
Add handlers for new webhook events in /lib/webhooks/ files.
What NOT to Touch
/lib/payments/
Provider abstraction layer. Marked with 🔒 CORE SYSTEM. Don't modify the interface or core logic.
/lib/webhooks/
Webhook handlers ensure subscriptions stay in sync with entitlements. Don't modify the core sync logic.
Subscription ↔ Entitlement Sync
The automatic entitlement sync is critical. Modifying it can break feature gating.
Related Sections
- Entitlements - Feature gating system that works with payments
- Database - Subscription and plan data models
- Deployment - Webhook configuration for production