Faridabad, India
WhatsApp Us
Home Blog Razorpay + UPI Guide
E-Commerce

The Razorpay + UPI Integration Guide Indian Developers Actually Need

RV
Rohit Verma — Backend Architect, The Code Art
Mar 7, 2026
8 min read
Razorpay · UPI · Indian Payments · Integration

The Razorpay Docs Are Good. They Don't Tell You Everything.

Razorpay's documentation is genuinely well-written. The basic integration — create order, open checkout, verify signature — works as described and you can have it running in an afternoon. What the docs don't cover are the production realities: the webhook edge cases that create ghost orders, the UPI behaviours that vary by app, the reconciliation problems that surface at scale, and the GST compliance details that your CA will flag six months after launch.

This guide covers what we've learned across multiple production Indian e-commerce and SaaS builds. It's for developers who've read the Razorpay docs and want the layer of knowledge that only comes from seeing things break in production.

Webhook Architecture — The Part Most Integrations Get Wrong

Webhooks are how Razorpay tells your backend that a payment succeeded, failed, or was refunded. Getting webhook handling wrong is the most common source of payment bugs in Indian e-commerce — specifically, "ghost orders" where the customer's bank shows a debit but your system shows no order.

The signature verification problem

Always verify the webhook signature. Every webhook call includes an X-Razorpay-Signature header — an HMAC-SHA256 digest of the raw request body using your webhook secret. Compute it yourself and compare. If they don't match, discard the webhook — it's either a test call or a spoofed request.

The critical mistake: reading the body as parsed JSON before signature verification. The digest is computed over the raw body bytes. If your framework has already parsed the JSON and you're re-serialising it for verification, character encoding and key ordering differences will cause legitimate webhooks to fail signature checks. Read the raw body bytes, verify, then parse.

// Node.js — correct webhook verification app.post('/webhook/razorpay', express.raw({type: 'application/json'}), (req, res) => { const sig = req.headers['x-razorpay-signature']; const digest = crypto .createHmac('sha256', process.env.RAZORPAY_WEBHOOK_SECRET) .update(req.body) // raw Buffer, NOT parsed JSON .digest('hex'); if (sig !== digest) return res.status(400).send('Invalid signature'); // Safe to process now const event = JSON.parse(req.body); });

Idempotency — handle duplicate webhooks

Razorpay will retry failed webhooks. If your endpoint returns a non-200 status (including 500s from your own bugs), Razorpay retries the same webhook multiple times over the next 24 hours. Your order fulfilment logic must be idempotent — processing the same payment.captured event twice must not create two orders or charge the customer twice.

The implementation: store the Razorpay payment ID in a processed_webhooks table with a unique constraint. Before processing any webhook, check if the payment ID is already there. If it is, return 200 immediately. If it isn't, insert it and process. The unique constraint prevents a race condition if two webhook calls arrive simultaneously.

UPI-Specific Edge Cases

UPI accounts for 40–60% of online transactions in India and has behaviours that differ meaningfully from card payments:

Payment pending — the most mishandled state

UPI payments have a "pending" state that card payments don't. This happens when the user initiates payment in their UPI app but the bank confirmation hasn't come back yet. Your backend receives a payment.pending webhook, not a payment.captured.

Teams that haven't handled this correctly show the user a "payment failed" page while the UPI transaction is still processing. The bank completes the payment 2–4 minutes later, Razorpay captures it, and now you have a payment with no order on the other end. The customer calls support. This is entirely preventable — show a "payment processing" screen for UPI, poll the Razorpay payment status API every 5 seconds for 3 minutes, and resolve to success or failure based on the result.

UPI Autopay (recurring) mandates

If your product has subscriptions, UPI Autopay mandates behave differently from credit card recurring charges. The customer must approve the mandate in their UPI app for each new recurring series. The first debit works like a normal UPI payment. Subsequent debits are pre-approved up to the mandate limit but still require a notification to the customer's UPI app 24 hours before execution. Build the notification flow — customers who receive unexpected UPI debits dispute them at a high rate.

COD Order Management — What the Gateway Docs Skip

Razorpay doesn't handle COD — COD orders have no payment gateway component at all. But in most Indian e-commerce backends, COD orders and prepaid orders live in the same order management system. Getting this model right saves enormous operational pain:

  • Order status model: COD orders need their own status progression — placed → verified → dispatched → delivered → payment_collected. The "verified" step is important: many COD orders are fraudulent, duplicate, or from bad-address customers. A verification call before dispatch, tracked in the system, dramatically reduces RTO (Return to Origin) rates.
  • COD remittance reconciliation: When your courier settles COD collections, they send you a remittance file (CSV from Delhivery, XLSX from Shiprocket). Your backend needs to match each remittance line to an order, mark it as collected, and flag discrepancies. Don't do this in Excel — build it into the backend from day one.
  • NDR (Non-Delivery Report) workflow: When a COD delivery attempt fails, couriers generate an NDR. You have a decision window — re-attempt, contact customer, or RTO. This window is typically 24–48 hours. Build an NDR queue into your operations panel. Teams that handle NDRs systematically reduce RTO by 20–35% compared to teams that manage them ad hoc.

Refund Reconciliation at Scale

Refunds are straightforward for the first 50 orders. At 500 daily orders, refund reconciliation becomes a finance function with real complexity:

  • Razorpay refunds take 5–7 business days to reach the customer's account. Your customer service team will receive "I haven't received my refund" queries for every refund, peaking around day 5. Build refund status tracking into your customer portal — a single screen showing "refund initiated, estimated arrival 22 April" reduces these tickets by ~70%.
  • Partial refunds (for partial returns, or when an item in a multi-item order is returned) need careful accounting. The original payment ID should be kept intact; the partial refund is a child record. Don't mutate the original order value.
  • For B2B orders, refunds require a credit note in GST accounting. Your backend should auto-generate a credit note document when a refund is processed for a GST invoice. Your CA will thank you.

GST Invoice Generation — The Compliance Layer

Every B2B transaction above ₹200 (and practically all B2C transactions for branded goods) needs a GST-compliant invoice. What "GST-compliant" means in practice:

  • Your company's GSTIN, trade name, and registered address
  • Invoice number in a sequential series (you can't skip or reuse invoice numbers)
  • HSN/SAC code for every line item
  • Taxable value, CGST amount, SGST amount (or IGST for inter-state) — separately stated, not lumped
  • For B2B: buyer's GSTIN, buyer's address
  • Digital signature or at minimum a "This is a computer-generated document" disclaimer

Generate invoices server-side (not client-side PDF from JavaScript) and store them immutably against the order. Never regenerate an invoice if any order detail has changed — instead, generate a credit note for the difference. This is what your auditor will check.

The integration checklist

Signature verification on raw bytes · Idempotent webhook processing · UPI pending state handling · COD verification workflow · NDR queue · Refund status tracking · Sequential GST invoice series · Credit note generation for B2B refunds. If your current integration is missing any of these, it's only a matter of volume before you hit the problem.

If you're building a payment integration from scratch or auditing an existing one, we're happy to review your architecture — no obligation, just a technical conversation.

RV
Rohit Verma
Backend Architect, The Code Art
Rohit has built payment integrations for multiple Indian e-commerce and SaaS products, handling thousands of transactions daily. He has debugged more ghost orders and webhook failures than he'd like to count, and now builds systems where they don't happen.