If you are building payment features with Razorpay in a MERN stack application, you will eventually face a problem that every freelancer encounters:
How do I reliably confirm a payment when the user closes the browser, refreshes the page, or the network fails?
This is where Razorpay Webhooks become essential.
In this guide, I’ll walk you through how webhooks work in Razorpay and how to implement them in a MERN stack (MongoDB, Express, React, Node.js) — in a way that is reliable, secure, and production-ready.
What is a Razorpay Webhook?
A webhook is an automated message sent from Razorpay to your server whenever a specific event occurs.
Instead of your server repeatedly asking Razorpay:
“Did the payment succeed yet?”
Razorpay sends your server a notification automatically.
Examples of webhook events include:
- payment.captured
- payment.failed
- order.paid
- refund.processed
- subscription.charged
So whenever something happens, Razorpay pushes the event to your backend API.
Why Webhooks are Critical in MERN Applications
Many developers rely only on the frontend payment success callback.
That works during testing, but in production it causes problems like:
- User closes the payment window
- Internet disconnects
- Frontend crashes
- Payment succeeds but frontend never updates
With webhooks:
- Payment confirmation happens server-to-server
- It works even if the user leaves the page
- Your database stays accurate
This is why serious payment systems always use webhooks.
MERN Stack Architecture with Razorpay Webhooks
The typical flow looks like this:
User -> React Frontend
-> Node/Express Backend
-> Razorpay Order Created
User completes payment
Razorpay -> Webhook -> Node Server
Node Server -> Verify Signature -> Update MongoDB
So the webhook endpoint becomes the source of truth for payment success.
Step 1: Create a Webhook in Razorpay Dashboard
Go to:
Razorpay Dashboard → Settings → Webhooks
Add a new webhook:
URL: https://yourdomain.com/api/webhook/razorpay
Secret: your_webhook_secret
Select events like:
- payment.captured
- payment.failed
- order.paid
Save it.
Now Razorpay will send POST requests to your backend whenever these events occur.
Step 2: Create Webhook Endpoint in Express
In your Node.js backend, create a webhook route.
Example:
const express = require("express");
const crypto = require("crypto");
const router = express.Router();
router.post("/webhook/razorpay", express.raw({ type: "application/json" }), (req, res) => {
const webhookSecret = process.env.RAZORPAY_WEBHOOK_SECRET;
const signature = req.headers["x-razorpay-signature"];
const generatedSignature = crypto
.createHmac("sha256", webhookSecret)
.update(req.body)
.digest("hex");
if (generatedSignature === signature) {
const event = JSON.parse(req.body);
console.log("Webhook verified:", event.event);
// Handle events here
if (event.event === "payment.captured") {
const payment = event.payload.payment.entity;
// Update MongoDB
console.log("Payment Successful:", payment.id);
}
res.status(200).send("Webhook received");
} else {
res.status(400).send("Invalid signature");
}
});
module.exports = router;
Step 3: Update MongoDB Payment Status
When a webhook confirms the payment, update your database.
Example:
await Order.findOneAndUpdate(
{ razorpayOrderId: payment.order_id },
{
paymentStatus: "paid",
razorpayPaymentId: payment.id
}
);
Now your system knows the real payment status.
Step 4: Connect This with Your React Frontend
Your frontend should not blindly trust the payment success response.
Instead:
- Payment happens
- Webhook confirms payment
- Backend updates MongoDB
- Frontend fetches updated order status
This ensures data consistency.
Step 5: Testing Razorpay Webhooks
Testing webhooks can be confusing at first.
Here are the easiest methods:
Option 1: Use ngrok
Expose your local server:
ngrok http 5000
Use the generated URL as the webhook URL.
Example:
https://abcd1234.ngrok.io/api/webhook/razorpay
Option 2: Razorpay Webhook Test Tool
Razorpay dashboard lets you send test webhook events.
Use this to simulate:
- payment success
- payment failure
Security Best Practices
Never skip these steps in production.
Always Verify Webhook Signature
Without verification, anyone could send fake payment events to your API.
Always check:
x-razorpay-signature
Use Raw Body Parsing
Do NOT use normal express.json() for webhook routes.
Use:
express.raw()
Otherwise the signature verification fails.
Store Webhook Logs
Save webhook responses for debugging.
Example Mongo schema:
WebhookLogs
- event
- payload
- receivedAt
This helps track payment issues.
Common Mistakes Developers Make
Updating payment status from frontend
This leads to fake confirmations.
Always update from webhook events.
Ignoring retry events
If your server fails, Razorpay retries webhooks automatically.
Make your API idempotent.
Not handling failed payments
Track both:
- payment.captured
- payment.failed
Final Thoughts
If you are a freelancer building payment systems with Razorpay and MERN, webhooks are not optional — they are the backbone of reliable payment processing.
Once implemented correctly, webhooks allow you to:
- confirm payments securely
- automate order processing
- prevent payment inconsistencies
- build production-grade payment systems
And most importantly, they ensure your database always reflects the real payment status.
✅ If you're building Razorpay integrations with MERN, mastering webhooks is one of the most valuable backend skills you can have.
Top comments (0)