Verifying Razorpay Order Payment Webhook Signatures in Node.js and PHP
Verify razorpay webhook signature nodejs and PHP implementations accurately in production systems to prevent fraudulent order events and unauthorized payload injection. Razorpay appends an X-Razorpay-Signature header to every webhook push event, derived from an HMAC-SHA256 hash using your custom webhook secret. Verification failures—resulting in 400 Bad Request or 401 Unauthorized errors—typically occur because developers confuse account API secrets with webhook secrets, or pass a JSON-stringified object rather than the untouched, raw HTTP payload buffer into the cryptographic utility. If Node.js parses the body via standard middleware or PHP receives $_POST form data instead of reading file_get_contents('php://input'), structural changes in whitespace or field encoding cause signature checks to fail instantly.
To ensure your system parses incoming event streams accurately, you can use our HookDoc Tool to inspect raw payloads, review headers, and debug JSON objects in real time.
Step-by-Step Guide: How to Verify Razorpay Webhook Signature Nodejs & PHP Handlers
Another common point of confusion is mixing up checkout payment redirect validation with async webhook signature validation. Standard payment redirection uses razorpay_order_id + "|" + razorpay_payment_id signed by your API Key Secret. Webhooks, however, sign the raw request body using your Webhook Secret configured in the Razorpay Dashboard. Using your primary API Secret to validate an order.paid or payment.captured webhook header will guarantee a validation hash mismatch every single time.
To fix this issue when you set up routes to verify razorpay webhook signature nodejs applications, ensure your server pulls the raw unparsed request payload. In Node.js, use validateWebhookSignature(rawBody, signature, secret) provided by the official razorpay SDK, or calculate crypto.createHmac('sha256', secret).update(rawBody).digest('hex'). In PHP, pass the raw input stream directly to hash_hmac('sha256', $rawBody, $secret). Always utilize constant-time string comparison algorithms to guard against timing side-channel attacks. For complete specification standards, consult the Official Razorpay Webhook Security Documentation.
The Code Fix
JavaScript
const { validateWebhookSignature } = require('razorpay/dist/utils/razorpay-utils');
/**
* Helper function to verify razorpay webhook signature nodejs applications safely.
*/
function verifyRazorpayWebhook(req) {
const razorpaySignature = req.headers['x-razorpay-signature'];
const webhookSecret = process.env.RAZORPAY_WEBHOOK_SECRET;
if (!razorpaySignature || !req.rawBody) return false;
// req.rawBody must be the untouched Buffer or raw string of the incoming request
return validateWebhookSignature(req.rawBody, razorpaySignature, webhookSecret);
}
Hardening Express Backend Infrastructure Against Cryptographic and Timing Attacks
When configuring your backend middleware to verify razorpay webhook signature nodejs endpoints, implementing proper buffer retention and caching patterns ensures complete application protection:
-
Capture Raw Payload Buffer: Ensure Express routes capturing Razorpay events utilize
express.raw({ type: 'application/json' })to retain raw payload streams prior to JSON transformation. -
Implement Idempotency with Redis: Store processed
X-Razorpay-Event-Idheaders in Redis In-Memory Data Store using a 24-hour Time-To-Live (TTL) to block duplicate webhook executions. -
Use Safe Comparison Utilities: Avoid using standard
===operators when comparing calculated hashes against headers. Reference Node.js Crypto Module Documentation for constant-time comparison methods. -
Enforce Strict HTTPS Enforcement: Always verify that your webhook listener endpoint is served over TLS/SSL to prevent man-in-the-middle payload tampering during transit.
-
Log Verification Failures: Maintain clear audit logs for failed signature verification attempts without logging sensitive secret keys to simplify security monitoring.
Common Pitfalls When You Verify Razorpay Webhook Signature Nodejs Scripts
Even experienced developers encounter integration bugs when handling Razorpay payment webhooks:
-
Secret Confusion: Attempting to verify webhook signatures using the
Key Secretinstead of the specificWebhook Secretdefined under Dashboard settings. -
JSON Re-stringification: Running
JSON.stringify(req.body)alters property ordering or escaped slashes, breaking the HMAC hash match. -
PHP
$_POSTGlobal Usage: In PHP integrations, using$_POSTloses raw payload bytes; always read directly fromfile_get_contents('php://input'). -
Ignoring Missing Headers: Failing to check if the
X-Razorpay-Signatureheader exists before processing can cause unhandled server errors.
Debugging Webhook Workflows with WebhookIQ Utilities
If your Razorpay payment webhooks are failing signature checks during checkout flows, you can copy your raw payload bytes and signature header into HookDoc to verify HMAC hex outputs against your secret in real time.
Check out our Webhook Testing Services page or explore more technical articles on our Webhook Testing Blog to master webhooks security, schema validation, and real-time event debugging. When you properly test and verify razorpay webhook signature nodejs endpoints in a sandboxed testing environment, you protect your system against malicious payloads while maintaining smooth payment event processing.
