Skip to main content
Available in🇮🇳 India
Create QR payments, simulate failures and reconcile against the webhooks in test mode, then check the scan and approve journey on real devices before you go live. This page covers one-time UPI QR payments on S2S: you take the Intent upi:// link and render it as a QR code, typically on desktop. The customer scans it with any UPI app. QR uses the same API calls and test triggers as Intent. UPI flows: Intent · QR · Collect Start with the overview for test keys and webhook setup.

1. Test Mode Coverage

How live mode and test mode differ

What you can test

Endpoint coverage in test mode

Webhooks: what fires, and what it lets you decide

Every webhook this flow fires in live mode is listed below, along with whether it also fires in test mode. Test mode webhooks have the same shape as live mode ones and are delivered to the URL set in your test mode Dashboard. Which webhooks each scenario fires Before you trust any of them
  • Verify the signature on every event. Replay one event to your endpoint and send one with a changed body. The replay must be ignored and the tampered event rejected. An endpoint that skips this is an open write to your order state.
  • Expect duplicates. The same event can arrive more than once. Key your handler on the event ID and make it idempotent.
  • Order is not guaranteed across events. Do not assume payment.captured lands before order.paid; handle whichever arrives first.
  • Webhook first, callback second. The browser callback can be lost, blocked or forged. Fulfil on the webhook.

2. Before you begin

  • Generate your test mode API keys from the Dashboard. Keys that start with rzp_test_ send all requests to test mode. You do not need to enable any separate flag.
  • Use the same APIs and request formats you use in live mode. Only the API keys change.
  • Pass every amount in paise, as you do in live mode. For example, send 201 for ₹2.01 and 100 for ₹1.
  • Configure a webhook URL in your test mode Dashboard to receive test events. Test mode webhooks are separate from live mode ones and do not carry over.
In test mode the amount you send decides the outcome, because there is no real bank on the other end. Send 100 (₹1) and the payment succeeds. Send one of the reserved amounts and the payment fails with the error mapped to that amount. For example, create the order for 201 (₹2.01) and the payment always fails with an insufficient balance error, so you can check that your code handles that case without waiting for a real account to run out of money.
  • Send 100 (₹1) on both the order and the payment to succeed.
  • Send a reserved amount from 3.3 UPI error scenarios on both calls to fail with the error mapped to that amount.
  • You do not need to scan the QR you render.
  • Any amount that is not in the reserved list succeeds.
Amounts are in paise and follow one pattern: 1xx bank and network, 2xx account and limits, 3xx customer action, 4xx validation and risk.

3. Scenarios to Test Your Integration

This section lists every scenario we think is worth running against your UPI QR integration before you go live. They are grouped by what each one proves, rather than by how urgent they are, so that you can work down the page in order and know that you have covered the integration end to end. Must-have. We highly recommend running these scenarios first. They cover the successful path and the handful of outcomes you are almost certain to meet once you are live, so getting through them is the shortest route to an integration you can trust. Other common scenarios. These come up in normal day-to-day running and are shared across one-time UPI and UPI Autopay, so the same handling has to work on both. They are not errors as such, but your integration still needs to recognise them and respond correctly, so we recommend covering them before launch. Error scenarios. These are failures you can bring on deliberately, using a reserved trigger, so that you can check your error handling without waiting for a real failure to happen. Most rows use a single trigger; where a row lists two, each one is shown separately in the error object column. Not available in test mode yet. These scenarios are real and your integration still has to handle them, but they cannot be reproduced in test mode today. We recommend reading through them and checking your handling on live traffic or in a controlled live test before you ship.

3.1 Must-build UPI scenarios

#ScenarioTrigger credsSteps to follow
1Successful paymentamount: 100Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Verify the signature on your server. Confirm payment.captured. Webhooks received: payment.authorized, payment.captured, order.paid. Status changes: the payment moves to captured and the order to paid.
2Customer cancels or declines in the app302, 304Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm payment.failed with payment_cancelled / payment_declined. The order stays open.
3Request times out105Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm payment_timed_out. Stop polling and let the customer retry.
4Fetch payment and reconcile (all methods)A payment ID from 1 (success) and one from any row in 3.3 UPI error scenariosPoll GET /v1/payments/:id while the customer is in the UPI app. Confirm the status matches the webhook. Repeat scenario 1 with each reserved amount to cover the error scenarios in 3.3, and replace the QR with the result once the payment is final (scenario 10). Scanning the QR is client-side, so check that on real devices.

3.2 Common scenarios in UPI

#ScenarioTrigger credsSteps to follow
5Full and partial refund (all methods)A captured payment from 1Refund in full and in part. Confirm refund.processed.

3.3 UPI error scenarios

Bank and network (1xx) The payment reached the rails and something outside the customer’s control went wrong. These are temporary, so offer the customer the same method again.
#ScenarioTrigger credsError objectSteps to follow
6Bank technical error102, 104on 102
code GATEWAY_ERROR
source issuer_bank
step payment_debit_request
reason bank_technical_error

on 104
code GATEWAY_ERROR
source issuer_bank
step payment_debit_response
reason bank_technical_error
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm bank_technical_error and a retry option.
7Gateway technical error101, 103on 101
code GATEWAY_ERROR
source gateway
step payment_response
reason gateway_technical_error

on 103
code GATEWAY_ERROR
source gateway
step payment_response
reason gateway_technical_error
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm gateway_technical_error.
8UPI app / PSP not available107code GATEWAY_ERROR
source customer_psp
step payment_request
reason upi_app_not_available
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm upi_app_not_available and a retry option.
9Stop showing the QR at a final stateamount: 100, then 302on 302
code BAD_REQUEST_ERROR
source customer
step payment_authentication
reason payment_cancelled
Create an order with the trigger amount. Render the QR. Confirm the QR is replaced by the result on payment.captured / payment.failed.
Account and limits (2xx) The customer’s account, their PIN or their bank’s limits stopped the debit. A few are recoverable on the same account, a wrong PIN above all; most are not. Read the reason before you offer a retry.
#ScenarioTrigger credsError objectSteps to follow
10Insufficient funds201code GATEWAY_ERROR
source customer
step payment_debit_request
reason insufficient_funds
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm insufficient_funds and a “use another account” message.
11Wrong UPI PIN204code BAD_REQUEST_ERROR
source customer
step payment_authentication
reason incorrect_pin
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm incorrect_pin and a retry option.
12Per-transaction limit exceeded208code BAD_REQUEST_ERROR
source customer
step payment_debit_response
reason transaction_limit_exceeded
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm transaction_limit_exceeded and an “amount too high” message.
13Daily / net debit limit exceeded209code BAD_REQUEST_ERROR
source customer
step payment_debit_response
reason transaction_limit_exceeded
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm transaction_limit_exceeded and a “daily limit reached, try again tomorrow” message.
14UPI PIN not set205code BAD_REQUEST_ERROR
source customer
step payment_authentication
reason pin_not_set
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm pin_not_set and a “set your UPI PIN” message, not a retry-PIN prompt.
15UPI PIN attempts exceeded (locked)206code BAD_REQUEST_ERROR
source customer
step payment_authentication
reason pin_attempts_exceeded
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm pin_attempts_exceeded and a “try later or use another method” message, not a retry-PIN prompt.
16Remitter account blocked212code BAD_REQUEST_ERROR
source issuer_bank
step payment_debit_request
reason debit_instrument_blocked
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm debit_instrument_blocked and an “account blocked, use another” message.
17Beneficiary account does not exist213code GATEWAY_ERROR
source gateway
step payment_credit_request
reason beneficiary_account_does_not_exist
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm beneficiary_account_does_not_exist (receiver account inactive).
18Transaction frequency limit exceeded210code BAD_REQUEST_ERROR
source business
step payment_initiation
reason transaction_frequency_limit_exceeded
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm transaction_frequency_limit_exceeded and a “try again later” message.
19Registered mobile number not found202code BAD_REQUEST_ERROR
source customer
step payment_request
reason mobile_number_invalid
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm mobile_number_invalid and a “use another method” message.
20Invalid bank account203code BAD_REQUEST_ERROR
source customer
step payment_debit_request
reason bank_account_invalid
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm bank_account_invalid and a “use another account” message.
Validation and risk (4xx) The request or the customer failed a check before or during the debit. Read the reason before you decide whether a retry is worth anything.
#ScenarioTrigger credsError objectSteps to follow
21Declined by bank or risk check402, 404on 402
code GATEWAY_ERROR
source issuer_bank
step payment_debit_request
reason payment_declined

on 404
code GATEWAY_ERROR
source issuer_bank
step payment_debit_response
reason payment_risk_check_failed
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm payment_risk_check_failed / payment_declined.
22API validation errors (all methods)Missing amount, amount: 0, wrong key secret—Send each bad request. Confirm HTTP 4xx, no payment created, no webhook.
23Invalid UPI ID (VPA resolution failed)401code GATEWAY_ERROR
source network
step payment_request
reason vpa_resolution_failed
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm vpa_resolution_failed and an “incorrect UPI ID” message.
24Invalid device407code BAD_REQUEST_ERROR
source customer
step payment_authentication
reason invalid_device
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm invalid_device and a “use another method” message.
25Duplicate request406code BAD_REQUEST_ERROR
source gateway
step payment_request
reason duplicate_request
Create an order with the trigger amount. Create the payment: POST /v1/payments/create/upi with upi.flow: "intent". Render the upi:// link as a QR code. Confirm duplicate_request (temporary) and allow retry.
The full list of UPI error reasons, including the ones no trigger reproduces in test mode, is on the Error index.

3.4 Not available in test mode yet

4. Common Integration Gaps to Avoid

These are the mistakes that most often cause failed payments, missed debits or double charges after go-live. “Test with” points to the scenario that proves you’ve handled it. Webhook and token updates Error handling and messaging to the payer Fetch API when webhooks are late Capture & refunds

5. Best Integration Practices

These apply to every Razorpay integration. “On this method” says what each one means here; “Test it” links to where it is tested once for every method. All best practices: Overview · Webhooks and Fetch APIs · Payment states and late authorisation · Error handling · Downtime handling · Security
Last modified on November 12, 2026