ISO 9001 · ISO 27001 · ISO 20000 certified CERT-In aligned security audits

ETHICSCOMPUTER / JOURNAL · PAYMENTS & FINTECH · 02 NOV 2026 · 6 MIN READ

Razorpay Webhooks: Signature Verification and Idempotency Explained

How Razorpay's payment and webhook signatures work, the mistakes that break verification, how to process webhooks exactly once, and a test checklist to run before go-live.

IN THIS ARTICLE (8 Sections)

Two bugs cause most of the Razorpay problems we are asked to fix in existing integrations: an order marked paid because the browser said so, and a payment credited twice because the same webhook arrived twice. Both are avoidable with two checks Razorpay already provides and some discipline in how your server records events.

This guide explains the payment signature and the webhook signature, the mistakes that make verification fail or quietly pass, how to make webhook processing idempotent, which events to subscribe to, and a test checklist to run before you switch to live keys.

Quick answer: Razorpay gives you two checks. After checkout, your server recomputes an HMAC-SHA256 of the order ID and payment ID, joined by a pipe, using your API key secret, and compares it with the returned razorpay_signature. For webhooks, it computes HMAC-SHA256 over the raw request body with your webhook secret and compares it with the X-Razorpay-Signature header. Then each event is processed exactly once.

The two signatures and what each one proves

The two checks are often confused, so it helps to see them side by side:

CheckWhat you hashSecret usedWhat it proves
Payment signaturerazorpay_order_id + "|" + razorpay_payment_idYour API key secretThe payment ID sent by the browser really belongs to the order your server created
Webhook signatureThe raw, unmodified request bodyThe webhook secret you set when creating the webhook in the DashboardThe event came from Razorpay and was not altered in transit

The payment signature protects the moment the customer returns from checkout. The webhook covers everything after that: captures, failures, refunds and subscription charges, including cases where the customer closed the tab before your success page loaded. You need both. Razorpay's official SDKs include helpers for each check (in PHP, verifyPaymentSignature and verifyWebhookSignature on the utility class), and using them avoids subtle mistakes.

A valid signature proves authenticity, not correctness. After verifying, compare the payment's amount and currency with the order in your database, and fetch the payment from the API if anything looks unusual.

Five mistakes that break verification

  1. Hashing parsed JSON instead of the raw body. Frameworks such as Express and Laravel parse the body before your code runs. If you re-encode the parsed object, spacing or key order can change and the signature will not match. Read the raw body instead: $request->getContent() in Laravel, or express.raw() on the webhook route in Node.js.
  2. Mixing up the secrets. The payment signature uses the API key secret; the webhook signature uses the separate webhook secret. Teams that use one where the other belongs often "fix" the failure by switching the check off.
  3. Trusting the browser. The checkout handler runs in the customer's browser. If your server marks the order paid on that callback without checking the signature, anyone with developer tools can fake a success.
  4. Leaking secrets. The key secret belongs on the server only, never in JavaScript, a mobile app or a public Git repository. Keep secrets in environment variables or a secrets manager, and rotate them if they are exposed.
  5. Plain comparisons and slow handlers. Use a constant-time comparison such as hash_equals in PHP or crypto.timingSafeEqual in Node.js. And do not send emails or generate invoices before responding to the webhook: if Razorpay does not get a timely success response, it retries, and you receive the same event again.

Designing webhook processing to be idempotent

Idempotent means that processing the same event twice has the same effect as processing it once. Razorpay can deliver an event more than once, for example when your server times out, and events can arrive out of order, such as payment.captured before payment.authorized. Design for both:

  • Record every event first: Store the event ID, which Razorpay sends in the x-razorpay-event-id header, in a table with a unique constraint. If the insert fails because the ID already exists, return success and stop.
  • Make the business action unique too: Put a unique constraint on the payment ID in your payments or ledger table, so even two different events for the same payment cannot credit a wallet twice.
  • Use a state machine: An order moves from created to paid to refunded, never backwards. If a late payment.authorized arrives after the order is already paid, ignore it.
  • Lock the order row: Update the order inside a database transaction with a row lock, so the browser callback and the webhook racing each other cannot both fulfil it.
  • Acknowledge fast, process later: Verify, store and queue the event, return a 2xx response, and let a background worker do the fulfilment.
  • Keep a safety net: A scheduled job that fetches the status of orders still pending after a set time catches any webhook that never arrived.

Which events to subscribe to

Subscribe only to what you act on. A typical e-commerce or SaaS setup uses:

  • order.paid or payment.captured: the trigger for fulfilment. Pick one as the single source of truth to avoid handling the same payment twice.
  • payment.failed: to offer the customer a retry and close abandoned attempts.
  • refund.processed and refund.failed: to update the order and inform the customer.
  • Subscription events: such as subscription.charged and subscription.halted, if you use Razorpay Subscriptions.

Also check your capture setting. If automatic capture is off, an authorised payment must be captured through the API. Payments left uncaptured are refunded to the customer after a period, which can look like a lost sale.

Test checklist before going live

TestExpected result
Send a webhook with one character of the body changedRejected with a 4xx response and logged
Replay the same event twiceOne fulfilment and one ledger entry
Pay, then close the browser before the success page loadsOrder still marked paid through the webhook
Send a tampered payment ID from the browser callbackSignature check fails; order stays unpaid
Deliver payment.captured before payment.authorizedOrder ends in the paid state
Stop the webhook worker for a while, then restart itQueued events are processed; pending orders are resolved by the status job
Rotate the webhook secretThe new secret works and the old one is rejected

Run these in test mode with a tunnelling tool or a staging server that Razorpay can reach, then repeat a few with small live payments after switching keys. If you already have a live integration with duplicate orders or unexplained pending payments, an audit against this list usually finds the cause. Our Razorpay integration service covers both new builds and fixes to existing ones.

How EthicsComputer can help

We build and repair Razorpay integrations on your own Razorpay account, in PHP, Laravel, Node.js, WordPress or mobile apps, with both signatures verified, idempotent webhook processing, refunds and a reconciliation report. For finance teams that need every settlement matched to orders and bank credits, we also build payment reconciliation software, and our API security testing can check your payment endpoints independently.

Seeing duplicate orders or payments stuck as pending?

Tell us your stack and what is going wrong, and we will review your Razorpay verification and webhook flow.

About the author

Vinay Grover founded EthicsComputer in Rohtak, Haryana in 2018 and leads its cybersecurity, software and AI work for clients across India and abroad. EthicsComputer is ISO 9001, ISO/IEC 27001 and ISO 20000-1 certified.

Vinay Grover
AUTHOR

Founder & CEO of EthicsComputer (Rohtak, since 2018). Leads the company's VAPT, software architecture and AI automation work. Full profile

END OF ARTICLE

Continue exploring EthicsComputer's technical journal.

EthicsComputer assistant
EthicsComputer Assistant
Online • Fast Response
⚡ Fast Response
Instant Quote & Demo
Get a Quote
Verified by SMS
Call Direct
+91 90532 10052
Direct Architect Scoping // Step 1 of 2

Request Fast Quote & Architecture SLA

Receive a preliminary project architecture, pricing tiers, and timeline estimate within 24 hours under strict NDA.

100% Mutual NDA Protected Step 1 of 2 (3 fields)