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.ts

Provider factory. Exports the active payment provider based on environment variable.

/lib/payments/stripe.ts

Stripe implementation of PaymentProvider interface.

/lib/payments/razorpay.ts

Razorpay implementation of PaymentProvider interface.

/lib/webhooks/stripe.ts

Stripe webhook event handlers. Processes subscription events.

/lib/webhooks/razorpay.ts

Razorpay webhook event handlers. Processes subscription events.

/app/api/payments/checkout/route.ts

Unified checkout endpoint. Works with both providers.

/app/api/payments/webhook/stripe/route.ts

Stripe webhook endpoint. Receives and processes Stripe events.

/app/api/payments/webhook/razorpay/route.ts

Razorpay 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:

  1. User clicks "Upgrade" button
  2. Frontend calls POST /api/payments/checkout with plan name
  3. Server reads PAYMENT_PROVIDER environment variable
  4. Creates checkout session with appropriate provider
  5. Returns checkout URL
  6. User completes payment on provider-hosted page
  7. Provider sends webhook
  8. Webhook handler verifies signature and creates subscription
  9. Entitlements sync automatically via plan relationship

Frontend Usage

typescript
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 subscription
  • invoice.paid - Updates status to active
  • customer.subscription.updated - Syncs subscription data
  • customer.subscription.deleted - Cancels subscription
  • invoice.payment_failed - Marks subscription past_due

Razorpay Events

  • subscription.activated - Creates subscription
  • subscription.charged - Updates payment status
  • subscription.cancelled - Cancels subscription
  • subscription.paused - Pauses subscription
  • subscription.resumed - Resumes subscription
  • payment.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