Implementing subscription payments is essential for SaaS applications. In this guide, we'll learn how to integrate Stripe subscriptions with Node.js and React, including checkout sessions, webhooks, customer portal, and subscription management.
Building a SaaS application means you need to handle recurring payments, and Stripe makes this process straightforward. After implementing Stripe subscriptions in multiple production applications, I've learned the patterns that work reliably and scale well.
In this guide, I'll show you how to implement a complete Stripe subscription system with minimal code. We'll cover creating checkout sessions, handling webhooks, managing subscriptions, and integrating the customer portal. The examples are production-ready and follow Stripe's best practices.
What is Stripe Subscriptions?
Stripe Subscriptions is a payment solution that handles:
- Recurring billing - Automatic monthly/yearly charges
- Subscription management - Upgrade, downgrade, cancel subscriptions
- Customer portal - Self-service billing management
- Webhooks - Real-time subscription lifecycle events
- Proration - Automatic billing adjustments for plan changes
- Multiple payment methods - Credit cards, ACH, and more
Installation and Setup
First, let's install Stripe:
npm install stripe
Configure Stripe with your API keys:
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2025-02-24.acacia',
});
Stripe Service: Core Functions
Create a service class to handle Stripe operations:
import Stripe from 'stripe';
export class StripeService {
// Get or create Stripe customer
static async getOrCreateCustomer(userId: string, email: string) {
// Check if user already has a customer ID
const user = await getUser(userId);
if (user?.stripeCustomerId) {
return user.stripeCustomerId;
}
// Create new customer
const customer = await stripe.customers.create({
email,
metadata: { userId },
});
// Save customer ID to database
await updateUser(userId, { stripeCustomerId: customer.id });
return customer.id;
}
// Create checkout session for new subscription
static async createCheckoutSession(
userId: string,
userEmail: string,
planId: string,
billingInterval: 'monthly' | 'yearly'
) {
const plan = await getPlan(planId);
const priceId = billingInterval === 'monthly'
? plan.stripePriceIdMonthly
: plan.stripePriceIdYearly;
const customerId = await this.getOrCreateCustomer(userId, userEmail);
const session = await stripe.checkout.sessions.create({
customer: customerId,
line_items: [{ price: priceId, quantity: 1 }],
mode: 'subscription',
success_url: `${FRONTEND_URL}/dashboard?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${FRONTEND_URL}/pricing`,
metadata: { userId, planId, billingInterval },
});
return session.url!;
}
}
Best Practices
- Use Stripe webhooks for subscription events
- Store customer IDs in your database
- Handle subscription lifecycle events
- Implement customer portal for self-service
- Handle errors gracefully
- Test with Stripe test mode
π Read the Complete Guide
This is just a brief overview! The complete guide on my blog includes:
- β Webhook Handling - Processing subscription events
- β Customer Portal - Self-service billing management
- β Subscription Management - Upgrade, downgrade, cancel
- β Payment Methods - Handling multiple payment methods
- β Error Handling - Comprehensive error management
- β Real-world examples from production applications
π Read the full article with all code examples here
What's your experience with Stripe subscriptions? Share your tips in the comments! π
For more backend guides, check out my blog covering Express.js, Prisma ORM, JWT Authentication, and more.
Top comments (0)