D
Docs
Documentation Stripe Integration

Stripe Integration

The subscription system integrates deeply with Stripe for secure payment processing, subscription management, and billing automation. This guide covers the complete Stripe integration setup and usage.

Overview

The Stripe integration handles:

  • Payment processing via Stripe Checkout
  • Subscription lifecycle management through webhooks
  • Customer portal for self-service billing
  • Invoice and payment tracking
  • Plan synchronization between your app and Stripe

Stripe Configuration

Environment Variables

Configure your Stripe integration with these environment variables:

# Stripe API Keys
STRIPE_SECRET_KEY=sk_test_...  # Your Stripe secret key
STRIPE_PUBLISHABLE_KEY=pk_test_...  # Your Stripe publishable key

# Webhook Configuration
STRIPE_WEBHOOK_SECRET=whsec_...  # Webhook endpoint secret

# Plan Price IDs (created in Stripe Dashboard)
STRIPE_STARTER_PRICE_ID=price_starter_monthly
STRIPE_STARTER_ANNUAL_PRICE_ID=price_starter_annual
STRIPE_PRO_PRICE_ID=price_pro_monthly
STRIPE_PRO_ANNUAL_PRICE_ID=price_pro_annual
STRIPE_ENTERPRISE_PRICE_ID=price_enterprise_monthly
STRIPE_ENTERPRISE_ANNUAL_PRICE_ID=price_enterprise_annual

Stripe Dashboard Setup

  1. Create Products in your Stripe Dashboard for each subscription plan
  2. Create Prices for monthly and annual billing cycles
  3. Configure Webhooks to point to your application endpoint
  4. Set up Customer Portal for self-service billing

Webhook Handling

Webhook Endpoint

The system processes Stripe webhooks at:

POST /api/webhooks/stripe

Supported Events

The webhook handler processes these Stripe events:

Checkout Completed

// Event: checkout.session.completed
// Triggered when a customer completes checkout

Subscription Created

// Event: customer.subscription.created
// Creates new subscription in database

Subscription Updated

// Event: customer.subscription.updated
// Updates subscription (plan changes, seat changes, etc.)

Subscription Deleted

// Event: customer.subscription.deleted
// Marks subscription as canceled

Payment Events

// Event: invoice.payment_succeeded
// Updates subscription status to active

// Event: invoice.payment_failed
// Updates subscription status to past_due

Webhook Processing Flow

  1. Verify webhook signature using Stripe webhook secret
  2. Parse event data and determine event type
  3. Process event based on type (create, update, delete subscription)
  4. Update database with new subscription state
  5. Return success response to Stripe

Webhook Security

The webhook handler includes security measures:

// Signature verification
const signature = getHeader(event, 'stripe-signature')
const stripeEvent = stripe.webhooks.constructEvent(
  body,
  signature,
  webhookSecret
)

Error Handling

Webhook processing includes comprehensive error handling:

  • Signature verification failures return 400 status
  • Missing webhook secret returns 500 status
  • Processing errors are logged and return 500 status
  • Idempotent processing prevents duplicate operations

Checkout Integration

Creating Checkout Sessions

Create Stripe checkout sessions for subscription purchases:

// API: POST /api/subscriptions/checkout
const checkoutSession = await createCheckoutSession({
  planId: 'plan_123',
  billingInterval: 'monthly',
  organizationId: 'org_456',
  successUrl: 'https://yourapp.com/success',
  cancelUrl: 'https://yourapp.com/cancel',
  seats: 10
}, userInfo)

Checkout Session Configuration

The checkout session includes:

{
  mode: 'subscription',
  customer: stripeCustomerId,
  line_items: [{
    price: stripePriceId,
    quantity: seats || 1
  }],
  metadata: {
    organizationId,
    planId,
    userId,
    seats: seats?.toString()
  },
  subscription_data: {
    metadata: {
      organizationId,
      planId,
      userId,
      seats: seats?.toString()
    },
    trial_period_days: plan.trialDays || undefined
  },
  success_url: successUrl,
  cancel_url: cancelUrl
}

Frontend Integration

// Create checkout session
const response = await fetch('/api/subscriptions/checkout', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    planId: 'plan_pro_123',
    billingInterval: 'monthly',
    organizationId: currentOrganization.id
  })
})

const { data } = await response.json()

// Redirect to Stripe Checkout
window.location.href = data.url

Customer Portal

Billing Portal Access

Provide customers with self-service billing management:

// API: POST /api/subscriptions/billing-portal
const portalSession = await stripe.billingPortal.sessions.create({
  customer: stripeCustomerId,
  return_url: returnUrl
})

Portal Features

The Stripe Customer Portal allows customers to:

  • Update payment methods
  • Download invoices
  • View billing history
  • Update billing information
  • Cancel subscriptions
  • Reactivate canceled subscriptions

Portal Configuration

Configure the portal in your Stripe Dashboard:

  1. Enable features you want customers to access
  2. Set business information (name, logo, support details)
  3. Configure cancellation flow
  4. Set up email notifications

Subscription Management

Plan Changes

Handle subscription plan changes through Stripe:

// Upgrade/downgrade subscription
await stripe.subscriptions.update(stripeSubscriptionId, {
  items: [{
    id: subscriptionItemId,
    price: newStripePriceId
  }],
  proration_behavior: 'create_prorations'
})

Seat-Based Billing

For seat-based plans, update quantity:

// Update seat count
await stripe.subscriptions.update(stripeSubscriptionId, {
  items: [{
    id: subscriptionItemId,
    quantity: newSeatCount
  }],
  proration_behavior: 'create_prorations'
})

Cancellation

Cancel subscriptions with different strategies:

// Cancel at period end
await stripe.subscriptions.update(stripeSubscriptionId, {
  cancel_at_period_end: true
})

// Cancel immediately
await stripe.subscriptions.cancel(stripeSubscriptionId)

Data Synchronization

Subscription Status Mapping

Stripe subscription statuses map to application statuses:

Stripe StatusApp StatusDescription
incompleteincompletePayment not processed
incomplete_expiredincomplete_expiredPayment failed and expired
trialingtrialingIn trial period
activeactiveActive and paid
past_duepast_duePayment failed but active
canceledcanceledSubscription canceled
unpaidunpaidPayment failed, suspended

Metadata Synchronization

Important metadata is stored in Stripe subscriptions:

metadata: {
  organizationId: 'org_123',
  planId: 'plan_456', 
  userId: 'user_789',
  seats: '10'
}

Billing Interval Detection

The system determines billing intervals from Stripe data:

async function determineBillingInterval(subscription: any): Promise<'monthly' | 'annual' | null> {
  const priceId = subscription.items.data[0].price.id
  const plans = await getAllSubscriptionPlans()
  
  for (const plan of plans) {
    if (plan.stripePriceIdMonthly === priceId) return 'monthly'
    if (plan.stripePriceIdAnnual === priceId) return 'annual'
  }
  
  // Fallback to Stripe price interval
  const interval = subscription.items.data[0].price.recurring?.interval
  return interval === 'month' ? 'monthly' : interval === 'year' ? 'annual' : null
}

Error Handling

Common Stripe Errors

Handle common Stripe API errors:

try {
  await stripe.subscriptions.create(subscriptionData)
} catch (error) {
  if (error.type === 'StripeCardError') {
    // Card was declined
  } else if (error.type === 'StripeInvalidRequestError') {
    // Invalid parameters
  } else if (error.type === 'StripeAPIError') {
    // Stripe API error
  } else if (error.type === 'StripeConnectionError') {
    // Network error
  }
}

Webhook Error Recovery

Implement webhook error recovery:

  1. Log all webhook events for debugging
  2. Implement idempotent processing to handle retries
  3. Use Stripe event IDs to prevent duplicate processing
  4. Monitor webhook delivery in Stripe Dashboard

Testing

Test Mode Configuration

Use Stripe test mode for development:

# Test API Keys
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PUBLISHABLE_KEY=pk_test_...

Test Cards

Use Stripe test cards for testing:

// Successful payment
'4242424242424242'

// Declined payment
'4000000000000002'

// Requires authentication
'4000002500003155'

Webhook Testing

Test webhooks locally using Stripe CLI:

# Install Stripe CLI
stripe listen --forward-to localhost:3000/api/webhooks/stripe

# Trigger test events
stripe trigger customer.subscription.created

Monitoring and Analytics

Stripe Dashboard

Monitor your subscription business through Stripe Dashboard:

  • Revenue metrics and MRR tracking
  • Customer lifecycle analytics
  • Churn and retention reports
  • Failed payment monitoring

Application Metrics

Track subscription metrics in your application:

// Example metrics to track
const metrics = {
  activeSubscriptions: await getActiveSubscriptionCount(),
  trialConversions: await getTrialConversionRate(),
  churnRate: await getChurnRate(),
  averageRevenuePerUser: await getARPU()
}

Security Best Practices

API Key Management

  • Never expose secret keys in client-side code
  • Use environment variables for API keys
  • Rotate keys regularly for security
  • Use restricted API keys when possible

Webhook Security

  • Always verify webhook signatures
  • Use HTTPS endpoints for webhooks
  • Implement rate limiting on webhook endpoints
  • Log and monitor webhook activity

Customer Data

  • Follow PCI compliance guidelines
  • Never store card data in your database
  • Use Stripe's secure vaults for payment methods
  • Implement proper access controls

Troubleshooting

Common Issues

  1. Webhook signature verification fails
    • Check webhook secret configuration
    • Verify endpoint URL in Stripe Dashboard
  2. Subscription not created after payment
    • Check webhook event processing
    • Verify metadata is correctly set
  3. Plan upgrades not working
    • Ensure Stripe price IDs are correct
    • Check proration settings
  4. Customer portal not accessible
    • Verify customer has valid Stripe customer ID
    • Check portal configuration in Stripe

Debug Tools

  • Stripe Dashboard event logs
  • Application logs for webhook processing
  • Stripe CLI for local testing
  • Browser developer tools for checkout issues

This comprehensive Stripe integration provides a robust foundation for subscription billing in your SaaS application.