How to Integrate Razorpay Payments into Your MERN Project
Hi there! I'm Aditya, a passionate Full-Stack Developer driven by a love for turning concepts into captivating digital experiences. With a blend of creativity and technical expertise, I specialize in crafting user-friendly websites and applications that leave a lasting impression. Let's connect and bring your digital vision to life!
If you are building a web application that requires online payments—such as course selling, appointment booking, SaaS billing, or e-commerce—Razorpay is one of the most reliable payment gateways available in India.
This article explains how to integrate Razorpay in a MERN application by covering:
Backend setup (Node.js + Express)
Razorpay order creation
Razorpay checkout integration in React
Payment verification
Webhook setup (optional but recommended)
Recommended production setup
Clean reusable code
You can use this as a reference guide for any future MERN project requiring payments.
1. Understanding Razorpay Payment Flow
Before writing any code, here is the exact working flow:
React Frontend → Express Backend → Razorpay API
1. User clicks “Pay Now”
2. React calls the backend to create a Razorpay order
3. Backend returns order_id
4. Razorpay pop-up opens → user completes payment
5. Razorpay returns payment_id + order_id + signature to frontend
6. Frontend sends these values back to the backend
7. Backend verifies the payment signature using HMAC SHA256
8 If valid → mark payment as SUCCESS
This is the official and recommended approach.
2. Project Structure
backend/
├── server.js
├── routes/
│ └── paymentRoutes.js
├── controllers/
│ └── paymentController.js
├── utils/
│ └── razorpay.js
├── .env
frontend/
├── src/
│ └── components/PayButton.jsx
├── public/
└── index.html
3. Install Backend Dependencies
Run the following command:
npm install express razorpay cors dotenv crypto
4. Configure Environment Variables (.env)
Create a .env file in your backend folder:
RAZORPAY_KEY_ID=YOUR_TEST_OR_LIVE_KEY_ID
RAZORPAY_KEY_SECRET=YOUR_TEST_OR_LIVE_KEY_SECRET
CLIENT_URL=http://localhost:3000
SERVER_URL=http://localhost:5000
5. Create Razorpay Instance (Backend)
backend/utils/razorpay.js
import Razorpay from 'razorpay';
import dotenv from 'dotenv';
dotenv.config();
export const razorpay = new Razorpay({
key_id: process.env.RAZORPAY_KEY_ID,
key_secret: process.env.RAZORPAY_KEY_SECRET,
});
6. Backend Controller: Create Razorpay Order
backend/controllers/paymentController.js
import crypto from "crypto";
import { razorpay } from "../utils/razorpay.js";
export const createOrder = async (req, res) => {
try {
const { amount } = req.body;
const options = {
amount: Number(amount) * 100,
currency: "INR",
receipt: "receipt_" + Date.now(),
};
const order = await razorpay.orders.create(options);
res.json({ success: true, order });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
7. Backend Controller: Verify Razorpay Payment Signature
export const verifyPayment = async (req, res) => {
try {
const { razorpay_order_id, razorpay_payment_id, razorpay_signature } =
req.body;
const body = razorpay_order_id + "|" + razorpay_payment_id;
const expectedSignature = crypto
.createHmac("sha256", process.env.RAZORPAY_KEY_SECRET)
.update(body)
.digest("hex");
if (expectedSignature === razorpay_signature) {
return res.json({ success: true, message: "Payment Verified" });
} else {
return res.status(400).json({ success: false, message: "Invalid Signature" });
}
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
8. Create Express Routes
backend/routes/paymentRoutes.js
import express from 'express';
import { createOrder, verifyPayment } from '../controllers/paymentController.js';
const router = express.Router();
router.post("/create-order", createOrder);
router.post("/verify-payment", verifyPayment);
export default router;
9. Final Express Setup
backend/server.js
import express from "express";
import cors from "cors";
import dotenv from "dotenv";
import paymentRoutes from "./routes/paymentRoutes.js";
dotenv.config();
const app = express();
app.use(express.json());
app.use(cors({ origin: process.env.CLIENT_URL }));
app.use("/api/payment", paymentRoutes);
app.get("/", (req, res) => {
res.send("Razorpay MERN Backend Running");
});
app.listen(5000, () => console.log("Server running on port 5000"));
Backend is now ready.
10. Frontend Setup
Install Axios:
npm install axios
Add Razorpay script loader inside:
frontend/public/index.html
<script src="https://checkout.razorpay.com/v1/checkout.js"></script>
11. Create Payment Button Component
frontend/src/components/PayButton.jsx
import axios from "axios";
function PayButton() {
const handlePayment = async () => {
const { data } = await axios.post(
"http://localhost:5000/api/payment/create-order",
{ amount: 499 }
);
const order = data.order;
const options = {
key: "YOUR_TEST_OR_LIVE_KEY_ID",
amount: order.amount,
currency: "INR",
name: "My Application",
description: "Payment for Service",
order_id: order.id,
handler: async function (response) {
const verify = await axios.post(
"http://localhost:5000/api/payment/verify-payment",
response
);
if (verify.data.success) {
alert("Payment Successful");
} else {
alert("Payment Verification Failed");
}
},
theme: {
color: "#121212",
},
};
const razorpayObj = new window.Razorpay(options);
razorpayObj.open();
};
return <button onClick={handlePayment}>Pay Now</button>;
}
export default PayButton;
12. Testing Razorpay Payments
Use Razorpay’s test mode.
Test card:
4111 1111 1111 1111
Expiry: Any future month/year
CVV: 123
Payment will always succeed in test mode.
13. Optional: Razorpay Webhook (For 100% Accurate Payments)
If the user closes the tab immediately after paying, verification might fail.
Webhooks ensure payment confirmation is always delivered.
backend/routes/webhookRoutes.js
import express from "express";
import crypto from "crypto";
const router = express.Router();
router.post("/razorpay", (req, res) => {
const secret = process.env.RAZORPAY_KEY_SECRET;
const shasum = crypto.createHmac("sha256", secret);
shasum.update(JSON.stringify(req.body));
const digest = shasum.digest("hex");
if (digest === req.headers["x-razorpay-signature"]) {
console.log("Webhook Verified");
// database update logic
console.log("Event Data:", req.body);
} else {
return res.status(400).json({ message: "Invalid Signature" });
}
res.status(200).json({ status: "ok" });
});
export default router;
Add this in server.js:
app.use("/api/webhook", webhookRoutes);
Set webhook URL in Razorpay Dashboard:
https://your-domain.com/api/webhook/razorpay
Conclusion
Integrating Razorpay into a MERN stack application can significantly enhance your project's payment capabilities, providing a seamless and secure transaction experience for users.
By following this guide, you have learned how to set up the backend and frontend, create and verify Razorpay orders, and implement optional webhooks for reliable payment confirmations.
With this knowledge, you can confidently incorporate Razorpay into any future MERN projects, ensuring a robust and efficient payment system.
