Back to Blog

How to Integrate Razorpay Payments into Your MERN Project

How to Integrate Razorpay Payments into Your MERN Project

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

bash
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:

javascript
npm install express razorpay cors dotenv crypto

4. Configure Environment Variables (.env)

Create a .env file in your backend folder:

javascript
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

javascript
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

javascript
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

javascript
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

java
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

javascript
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:

bash
npm install axios

Add Razorpay script loader inside:

frontend/public/index.html

javascript
<script src="https://checkout.razorpay.com/v1/checkout.js"></script>

11. Create Payment Button Component

frontend/src/components/PayButton.jsx

javascript
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:

bash
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

javascript
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:

javascript
app.use("/api/webhook", webhookRoutes);

Set webhook URL in Razorpay Dashboard:

bash
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.

Based in Greater Noida, IndiaCurrently