If you’re a developer student looking to implement seamless subscription billing in your web app, you’ve come to the right place. In this guide, we’ll walk you through creating a complete subscription system using Node.js, Stripe, and React—from backend APIs to the frontend UI. 💡
By the end of this blog, you’ll be able to:
✅ Create and manage subscription plans on Stripe
✅ Set up a secure backend with webhooks
✅ Build a beautiful subscription UI in React
✅ Handle real-time Stripe checkout flows
✅ Manage subscription success and failure states
Let’s dive into it! 🌊
🧠 What You’ll Learn
- Why Stripe for subscriptions?
- Setting up Stripe account and plans
- Building the Node.js backend (with Express)
- Configuring Stripe webhooks securely
- Designing a modern React frontend
- Connecting frontend to backend
- Handling success and cancellation flows
- Deployment & security best practices
🔧 Prerequisites
Before we jump in, make sure you have:
- Basic knowledge of JavaScript and Node.js
- Node.js and npm installed on your system
- A Stripe account (https://stripe.com)
- React app scaffolded (using Vite or Create React App)
- A code editor like VSCode
🌟 Why Use Stripe?
Stripe is one of the most popular payment gateways for developers. Here’s why:
- 🛠️ Developer-friendly SDKs
- 💼 Built-in support for subscriptions & invoicing
- 🌎 Global payments support
- 🔐 PCI-compliant and secure
- ⚙️ Powerful dashboard for analytics
🚀 Step 1: Set Up Your Stripe Dashboard
- Create a Stripe account at stripe.com
- Go to Products > Prices, and create recurring prices for your subscription plans:
- Basic Plan – ₹1,000/month
- Premium Plan – ₹2,000/month
- Copy the Price ID for each plan—this will be used in your backend.
🔐 Don’t forget to grab your Secret Key from the Developers > API Keys section.
🛠️ Step 2: Building the Backend with Node.js + Stripe
We’ll now build a backend server using Express.js that will:
- Create a Stripe Checkout session
- Handle webhooks securely
- Accept subscription details
📁 Backend Project Structure
stripe-backend/
├── .env
├── index.js
├── package.json
📦 Install Dependencies
npm init -y
npm install express cors dotenv stripe body-parser
📄 .env (Store secrets securely)
PORT=5000
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
🧩 Backend Code (index.js
)
const express = require("express");
const cors = require("cors");
const dotenv = require("dotenv");
const bodyParser = require("body-parser");
dotenv.config();
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
const app = express();
const PORT = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
app.use(bodyParser.raw({ type: "application/json" }));
🔗 Create Checkout Session API
app.post("/stripe-checkout-session", async (req, res) => {
try {
const { priceId } = req.body;
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
mode: "subscription",
line_items: [{ price: priceId, quantity: 1 }],
success_url: "http://localhost:5173/success",
cancel_url: "http://localhost:5173/cancel",
customer_email: "customer@example.com",
billing_address_collection: "required",
shipping_address_collection: { allowed_countries: ["IN"] },
custom_fields: [
{
key: "customer_name",
label: { type: "custom", custom: "Full Name" },
type: "text",
optional: false,
},
],
});
res.status(200).json({ url: session.url });
} catch (error) {
console.error("Error creating session", error);
res.status(500).json({ error: "Internal server error" });
}
});
📡 Handling Stripe Webhooks
Webhooks allow your server to react to events such as successful payments or subscription cancellations.
app.post("/webhook", (req, res) => {
const sig = req.headers["stripe-signature"];
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
} catch (err) {
console.error("Webhook Error:", err.message);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
switch (event.type) {
case "checkout.session.completed":
const session = event.data.object;
console.log("Subscription successful:", session);
break;
case "invoice.payment_failed":
const failedInvoice = event.data.object;
console.warn("Payment failed:", failedInvoice);
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
res.json({ received: true });
});
▶️ Start the Server
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Run it with:
node index.js
✅ Your backend is ready!
🧑🎨 Step 3: Build the React Frontend
Now let’s build a stunning frontend in React with Stripe integration.
📁 Frontend Folder Structure
react-app/
├── src/
│ ├── pages/
│ │ ├── Plans.jsx
│ │ ├── Success.jsx
│ │ └── Cancel.jsx
│ ├── App.jsx
│ └── index.css
📦 Install React Router
npm install react-router-dom
🎨 Plans.jsx
import React from 'react';
const Plans = () => {
const plans = [
{
name: 'Basic',
price: '₹1,000',
features: ['1 project', 'Basic support', '24/7 access', 'Basic analytics'],
priceId: 'price_1RUP0USF61vTyQk0InchPf2z'
},
{
name: 'Premium',
price: '₹2,000',
features: ['Unlimited projects', 'Premium support', '24/7 priority access', 'Advanced analytics', 'Custom integrations'],
priceId: 'price_1RSAIeSF61vTyQk0Ny0Czz08'
}
];
const handleSubscribe = async (priceId) => {
try {
const res = await fetch("http://localhost:5000/stripe-checkout-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ priceId })
});
const data = await res.json();
if (data?.url) window.location.href = data.url;
else alert("Failed to redirect. Please try again.");
} catch (error) {
console.error("Subscription error", error);
alert("An error occurred during checkout.");
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 py-12">
<div className="max-w-7xl mx-auto text-center mb-12">
<h2 className="text-4xl font-extrabold text-gray-900">Choose Your Plan</h2>
<p className="mt-4 text-xl text-gray-500">Select the perfect plan for your needs</p>
</div>
<div className="grid sm:grid-cols-2 gap-6 lg:max-w-4xl mx-auto">
{plans.map((plan) => (
<div key={plan.name} className="bg-white rounded-2xl shadow-xl p-8 hover:scale-105 transition-transform">
<h3 className="text-2xl font-semibold">{plan.name}</h3>
<p className="text-4xl mt-4">{plan.price}<span className="text-base text-gray-500">/month</span></p>
<ul className="mt-6 text-left">
{plan.features.map((feature) => (
<li key={feature} className="flex items-center gap-2 text-gray-700">
✅ {feature}
</li>
))}
</ul>
<button onClick={() => handleSubscribe(plan.priceId)} className="mt-6 w-full bg-indigo-600 text-white py-2 rounded-lg hover:bg-indigo-700 transition">
Subscribe Now
</button>
</div>
))}
</div>
</div>
);
};
export default Plans;
🛬 Success.jsx & Cancel.jsx
// Success.jsx
export default function Success() {
return <div className="p-10 text-green-600 text-center text-2xl">🎉 Subscription successful!</div>;
}
// Cancel.jsx
export default function Cancel() {
return <div className="p-10 text-red-600 text-center text-2xl">❌ Payment cancelled. Try again.</div>;
}
🔀 App.jsx
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Plans from './pages/Plans';
import Success from './pages/Success';
import Cancel from './pages/Cancel';
import './index.css';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Plans />} />
<Route path="/success" element={<Success />} />
<Route path="/cancel" element={<Cancel />} />
</Routes>
</Router>
);
}
export default App;
🔒 Security Tips
- Use HTTPS in production
- Never expose your
STRIPE_SECRET_KEY
on the frontend - Use env variables on both frontend and backend
- Validate webhook events with Stripe’s
constructEvent
🌍 Ready to Deploy?
Use services like:
- Render, Railway, or Heroku for backend
- Vercel or Netlify for React frontend
Make sure to:
- Point the
success_url
andcancel_url
to your deployed frontend - Secure webhook endpoint URLs
- Monitor usage and invoices in the Stripe dashboard
🧪 Testing Subscriptions
- Use Stripe test cards (e.g.,
4242 4242 4242 4242
) - Enable test mode in dashboard
- Simulate different scenarios: success, failure, cancellation
💼 Real-World Use Cases
This setup is ideal for:
- SaaS platforms
- Developer tools
- Membership-based portals
- E-learning platforms
- API monetization
DOWNLOAD SOURCE CODE
🏁 Wrapping Up
Building a full-stack subscription system doesn’t have to be a headache. With the power of Stripe and the flexibility of React and Node.js, you can implement a secure, scalable solution in no time.
Whether you’re building the next indie SaaS or a global product, this guide gives you the roadmap to succeed. 🚀
Happy coding, developers! 🧑💻💖