Razorpay Subscriptions and Recurring Payments in Node.js Setup and Management
Learn how to implement recurring subscription payments with Razorpay in Node.js creating plans, subscriptions, handling auto-renewals, and managing cancellations.
Razorpay Subscriptions and Recurring Payments
Subscriptions allow you to charge customers automatically on a recurring basis (monthly, yearly). This guide covers Razorpay subscription integration in Node.js.
How Subscriptions Work
1. Create a Plan (e.g., ₹500/month)
2. Create a Subscription (links customer to plan)
3. Customer authenticates once (first payment)
4. Razorpay auto-charges on each billing cycle
5. Webhook notifies you of each payment
6. Handle subscription cancellation/expiry
Step 1: Create a Plan
// One-time setup for each pricing tier const createPlan = async () => { const plan = await razorpay.plans.create({ period: "monthly", // daily, weekly, monthly, yearly interval: 1, // every 1 month item: { name: "DevTinder Premium", amount: 50000, // ₹500 in paise currency: "INR", description: "Premium plan with unlimited connections" } }); console.log("Plan created:", plan.id); // Save plan.id in your database }; // Create a yearly plan const createYearlyPlan = async () => { const plan = await razorpay.plans.create({ period: "yearly", interval: 1, item: { name: "DevTinder Premium Yearly", amount: 500000, // ₹5000 in paise currency: "INR", description: "Yearly premium plan" } }); console.log("Yearly plan:", plan.id); };
Step 2: Create a Subscription
router.post("/subscription/create", userAuth, asyncHandler(async (req, res) => { const { planId } = req.body; // Razorpay plan ID const subscription = await razorpay.subscriptions.create({ plan_id: planId, customer_notify: 1, // Notify customer total_count: 12, // 12 billing cycles (12 months) quantity: 1, notes: { userId: req.user._id.toString() } }); // Save in database await Subscription.create({ userId: req.user._id, subscriptionId: subscription.id, planId: planId, status: subscription.status, // "created" totalCount: 12, currentCount: 0 }); res.json({ subscriptionId: subscription.id, shortUrl: subscription.short_url // Share this with the customer }); }));
Step 3: Handle Subscription Webhooks
router.post("/subscription/webhook", express.raw({ type: "application/json" }), asyncHandler(async (req, res) => { const signature = req.headers["x-razorpay-signature"]; const expectedSignature = crypto .createHmac("sha256", process.env.RAZORPAY_WEBHOOK_SECRET) .update(req.body) .digest("hex"); if (expectedSignature !== signature) { return res.status(400).json({ message: "Invalid signature" }); } const event = JSON.parse(req.body.toString()); switch (event.event) { case "subscription.activated": await handleSubscriptionActivated(event.payload.subscription.entity); break; case "subscription.charged": await handleSubscriptionCharged(event.payload.subscription.entity, event.payload.payment.entity); break; case "subscription.cancelled": await handleSubscriptionCancelled(event.payload.subscription.entity); break; case "subscription.expired": await handleSubscriptionExpired(event.payload.subscription.entity); break; case "subscription.pending": await handleSubscriptionPending(event.payload.subscription.entity); break; } res.status(200).json({ status: "ok" }); }) ); const handleSubscriptionCharged = async (subscription, payment) => { const dbSub = await Subscription.findOne({ subscriptionId: subscription.id }); if (dbSub) { dbSub.currentCount = subscription.current_count; dbSub.status = subscription.status; // "active" dbSub.currentPeriodStart = new Date(subscription.current_start); dbSub.currentPeriodEnd = new Date(subscription.current_end); await dbSub.save(); // Update user's premium status await User.findByIdAndUpdate(dbSub.userId, { isPremium: true, premiumExpiresAt: new Date(subscription.current_end) }); // Send confirmation email await sendEmail(dbSub.userId.email, "subscriptionCharged", { amount: payment.amount / 100, nextBillingDate: subscription.current_end }); } };
Step 4: Cancel a Subscription
router.post("/subscription/cancel", userAuth, asyncHandler(async (req, res) => { const subscription = await Subscription.findOne({ userId: req.user._id, status: "active" }); if (!subscription) { throw new AppError("No active subscription found", 404); } // Cancel on Razorpay await razorpay.subscriptions.cancel(subscription.subscriptionId); // Update database subscription.status = "cancelled"; subscription.cancelledAt = new Date(); await subscription.save(); // User keeps premium until the current period ends res.json({ message: "Subscription cancelled. Premium active until the end of the current billing period." }); }));
Subscription Model
const subscriptionSchema = new mongoose.Schema({ userId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true }, subscriptionId: { type: String, required: true, unique: true }, planId: String, status: { type: String, enum: ["created", "authenticated", "active", "pending", "halted", "cancelled", "completed", "expired"], default: "created" }, totalCount: Number, currentCount: { type: Number, default: 0 }, currentPeriodStart: Date, currentPeriodEnd: Date, cancelledAt: Date, method: String // Payment method for auto-renewal }, { timestamps: true });
The Takeaway
Razorpay subscriptions involve: creating a plan (period, interval, amount), creating a subscription (links customer to plan), handling webhooks (activated, charged, cancelled, expired), and allowing cancellations. The webhook subscription.charged fires on each billing cycle update the user's premium status and current period. Save subscription ID, status, and period dates in a Subscription model.
Create a plan (e.g., ₹500/month), create a subscription linking the customer to the plan, the customer authenticates once (first payment), Razorpay auto-charges on each billing cycle, and webhooks notify you of each charge. Handle cancellation via the API or dashboard.
Use razorpay.plans.create({ period: 'monthly', interval: 1, item: { name, amount: 50000, currency: 'INR', description } }). Period can be daily, weekly, monthly, or yearly. Amount is in paise. Save the plan ID for creating subscriptions.
subscription.activated (first payment successful), subscription.charged (recurring payment successful), subscription.cancelled (user or admin cancelled), subscription.expired (all billing cycles completed), and subscription.pending (payment pending for the current cycle).
Call razorpay.subscriptions.cancel(subscriptionId). Update the database status to 'cancelled'. The user keeps premium access until the current billing period ends (currentPeriodEnd). After that, downgrade the user to free plan.
userId, subscriptionId (unique), planId, status (created/active/cancelled/expired), totalCount (total billing cycles), currentCount (cycles charged so far), currentPeriodStart, currentPeriodEnd, cancelledAt, and payment method.
Ready to master Node.js completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master Node.js
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

