Skip to main content
Available in🇮🇳 India
This page covers one-time card payments on S2S JSON (POST /v1/payments/create/json) and S2S Redirect (POST /v1/payments/create/redirect). Standard Checkout and Custom Checkout are out of scope for this page. Start with the overview for test keys and webhook setup.
Before you startEvery scenario on this page needs an order. Order behaviour has scenarios of its own, all on Orders. The ones that matter for this method: ORD-01 create, ORD-02 lifecycle, ORD-03 retry after a failure, ORD-04 signature, ORD-05 idempotent create, ORD-06 fetch, ORD-07 already paid, ORD-16 partial payments. Request errors (ORD-08 to ORD-15) are the same for every method; run them once.

1. Test Mode Coverage

2. Test Kit

  • Use any future expiry date and any 3-digit CVV (4 digits for Amex).
  • On the mock bank page, enter any OTP of 4–10 digits, or choose Success, to approve.
  • For a specific error, pay with a card from Simulate Failures and choose Failure. Any other card + Failure returns gateway_technical_error.
Success cards Cards for specific flows

3. Scenarios to Test Your Integration

Must-have (6) — the paths where money moves or is lost: a successful payment, a decline, a cancel, a fetch-and-reconcile, and the 3DS redirect. Other common scenarios (8) — refunds, Native OTP, saved cards and tokens, network tokens, Token HQ, one-time payments on a recurring token, and an IIN lookup. Error scenarios (6) — failures you trigger deliberately with a test card, so you can check your error handling without waiting for a real one. Not available in test mode yet (5) — real scenarios your integration still has to handle, but can’t be reproduced here today. 25 scenarios in all: 20 you can run in test mode today, 5 you cannot.

3.1 Must-have

3.2 Other common scenarios

3.3 Error scenarios

3.4 Not available in test mode yet

4. Scenario Details

Each block follows the same format: before you start, how to test, expected result, webhooks, and errors to test.

P01 — Successful card payment (covers P09, CARD-N1)

Before you start: an order created with POST /v1/orders (every payment needs an order_id). Test order creation once on Orders. How to test: create the payment with the order’s order_id and a success card from the test kit (for example Visa 4100 2800 0000 1007) using S2S JSON (POST /v1/payments/create/json) or S2S Redirect (POST /v1/payments/create/redirect). Redirect the customer to the URL in next[] (JSON) or render the returned page (Redirect). On the mock bank page, enter any 4–10 digit OTP. Expected result: payment captured (or authorized if you capture manually). The customer returns to your callback_url with razorpay_payment_id, razorpay_order_id and razorpay_signature; verify the signature server-side. Webhooks: payment.authorized, payment.captured, order.paid Your integration should: verify the signature server-side and fulfil on payment.captured.

P04 — Customer cancels, times out or session expires (covers P05)

How to test: start a payment, then close the mock bank page or choose cancel. Use the cancel and timeout cards for the exact errors: payment_cancelled (4100 2800 0007 0002), payment_timed_out (4100 2800 0009 0000), payment_session_expired (4100 2800 0003 0048). Expected result: the payment ends failed. The order stays unpaid so the customer can retry. Webhooks: payment.failed

P06 — Authentication failures

How to test: pay with each authentication-failure card and choose Failure: 4100 2800 0000 0009 (authentication_failed), 4100 2800 0004 0039 (declined_by_bank), 4100 2800 0004 0047 (acs_maximum_attempts_reached), 4100 2800 0002 0031 (incorrect_pin), 4100 2800 0005 0046 (pin_attempts_exceeded), 4100 2800 0004 0013 (card_not_enrolled). Expected result: payment fails at error.step: "payment_authentication". Ask the customer to retry or use another card. Webhooks: payment.failed

P10 — Fetch payment and reconcile

How to test: call GET /v1/payments/:id after each scenario above. Also call it with a wrong ID, and with a test-mode payment ID using live keys. Expected result: the status matches the webhook you received. A wrong ID returns an error.

CARD-B1 — 3DS: redirect to the bank page

How to test: pay with any success card. You’re redirected to the mock bank page; any 4–10 digit OTP succeeds, an OTP under 4 digits fails the payment. Webhooks: success payment.authorized, payment.captured, order.paid; failure payment.failed

CARD-B2 — Native OTP: generate, submit and resend

Before you start: S2S JSON V2 enabled on your account (on-demand). How to test: optionally check Native OTP support with POST /v1/payment/flows. Create the payment with auth_type: "otp" using a Native OTP card. Call POST /v1/payments/:payment_id/otp_generate (URL is in next[]). Submit the OTP with POST /v1/payments/:payment_id/otp/submit. Any 4–10 digit OTP succeeds in test mode. Test resend once with otp/resend, then submit again. Expected result: otp_generate and otp/resend return next[] with otp_submit and otp_resend. A successful submit returns the signature fields. Webhooks: success payment.authorized, payment.captured, order.paid. A wrong OTP that returns action: "RETRY" sends no webhook; payment.failed comes only when the payment finally fails. Your integration should: treat action: "RETRY" as “ask again”, not as a failure.

CARD-C1 — Save a card during payment

Before you start: a customer created with POST /v1/customers. How to test: pay with a success card, save: 1 and customer_id on POST /v1/payments/create/json. Expected result: payment succeeds and a token is created for the customer, visible in GET /v1/customers/:customer_id/tokens. Webhooks: payment.authorized, payment.captured, order.paid. If you use Token HQ, also token.initiated, token.activated.

CARD-C2 — Pay with a saved Razorpay token

Before you start: a token saved in CARD-C1. How to test: pay again with customer_id and the token, plus the CVV. Expected result: payment succeeds without the card number. Webhooks: payment.authorized, payment.captured, order.paid

CARD-C3 — Pay with your own network token

Before you start: you’re a token requestor with a network token and cryptogram. How to test: create the payment with card.tokenised: true, cryptogram_value, token_provider and provider_type: "network". Webhooks: payment.authorized, payment.captured, order.paid

CARD-C4 — Create, fetch and delete tokens

Before you start: Token HQ enabled on your account for the /v1/tokens APIs. How to test: Token HQ — call POST /v1/tokens, POST /v1/tokens/fetch, POST /v1/tokens/delete. Saved cards — call GET /v1/customers/:customer_id/tokens, GET .../tokens/:token_id and DELETE .../tokens/:token_id. Webhooks: create token.initiated, token.activated. Delete token.deactivated. Sent by the network later: token.expiry_updated, token.suspended.

CARD-C5 — One-time payment with a recurring token

Before you start: recurring payments enabled on your account, and a customer. How to test: use this if you save cards through a recurring (mandate) registration and also charge the same token for one-time purchases. Register the mandate with recurring: 1 and customer_id, using the recurring test card Visa 4718 6091 0820 4366. Wait until the token is confirmed. For the one-time purchase, create a new order, then call POST /v1/payments/create/json with customer_id and token, without recurring (or recurring: 0). CVV is optional for tokenised cards. Complete authentication from next[] (3DS on the mock bank page, or Native OTP as in CARD-B2). Expected result: the one-time payment succeeds like any saved-card payment: the customer authenticates, and no pre-debit notification is sent. The mandate and token stay active. Webhooks: mandate registration — payment.authorized (carries token_id), then token.confirmed or token.rejected. One-time payment — payment.authorized, payment.captured, order.paid, and order.notification.delivered is not sent (note the order. prefix on the live-mode event name).

CARD-D1 — Look up card details by IIN

Before you start: IIN API enabled on your account (on-demand). How to test: call GET /v1/iins/:iin before creating the payment — for example to check card type, international, EMI or recurring eligibility, or Native OTP support. A 6-digit card IIN (for example 410028) works for all networks. A 9-digit token IIN works for Visa, Mastercard and RuPay; a 6-digit token IIN works for Amex; Diners Club isn’t supported. Then send invalid IINs: 000000 (doesn’t exist), 41002 (wrong length).

P12 — Full and partial refunds

Before you start: a captured payment from P01. How to test: refund one captured payment in full and another in part, with POST /v1/payments/:payment_id/refund. Expected result: you receive refund.created and refund.processed. The payment shows refund_status as full or partial. Webhooks: refund.created, refund.processed, or refund.failed if the refund fails. Refund errors are covered on Refunds.

5. Webhooks: What Fires, and What It Lets You Decide

Every scenario above delivers a specific set of events, in a specific order. This table is the one place that says which, and — the part that actually matters — what each one entitles you to do. Most integration bugs that reach production are not missing webhooks; they are acting on the wrong one. 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.
  • 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.

6. Common Integration Gaps to Avoid

Webhook and token updates

Fetch API when webhooks are late

7. Best Integration Practices

These apply to every Razorpay integration. “On this method” says what each one means for cards; “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

8. Simulate Failures

The full list of Cards error reasons, including the ones no trigger reproduces in test mode, is on the Error index. Branch your handling on error.reason and error.source, never on the description text. These are payment errors; errors from creating the order are on Orders.

Errors available in test mode (33 triggers → 31 distinct error.reason values)

Grouped by source: Customer (19), Issuing bank (8), Bank (1), Gateway (2), Your request (business) (1), Razorpay internal (2), Source not mapped (1). Pay with the card and choose Failure on the mock bank page.

Errors not available in test mode (38 rows → 38 distinct error.reason values)

These happen on live card payments but can’t be triggered here. Handle them by error.reason the same way. Grouped by source: Customer (1), Your request (business) (1), Razorpay internal (36), of which source not mapped (33). 17 mandate errors are left out of this count — simulate those on Card recurring.
Handy TipsFor a full picture of what fires when, see Webhooks and Fetch APIs. Verify the signature on every event, key your handler on the event ID, and never assume event order.
Last modified on November 12, 2026