> ## Documentation Index
> Fetch the complete documentation index at: https://razorpay.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Card Recurring: How to Test Your Integration

> Test card mandate registration and subsequent debits in Test Mode, including token states, AFA simulation and failure scenarios.

Available in
🇮🇳 India

Use Test Mode to check your card recurring integration before you go live. Test Mode simulates the card network, mandate hub and bank responses, so you can register mandates, run subsequent debits and handle failures without moving real money. This page covers S2S integrations. See [UPI Autopay Intent](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/recurring/upi-autopay-intent.md) for the UPI equivalent.

Start with the [overview](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s.md) for test keys and webhook setup.

> [!NOTE]
>
> **Before you start**
>
> Every scenario on this page needs an order. Order behaviour has scenarios of its own, all on [Orders](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/orders.md). The ones that matter for this method: ORD-20 mandate order, ORD-21 mandate order errors, ORD-22 a new order per subsequent debit, ORD-04 signature, ORD-05 idempotent create, ORD-06 fetch. Request errors (ORD-08 to ORD-15) are the same for every method; run them once.

## 1. Test Mode Coverage

### How live mode and test mode differ

| Behaviour | Live mode | Test mode |
| - | - | - |
| Subsequent payment timing | A pre-debit notification (PDN) is sent first. RBI requires the pre-debit notification at least 24 hours before the subsequent debit, and Razorpay debits 36 hours after the notification. | A subsequent payment is simulated to reach its terminal state (Success or Failure) about 5 minutes after it is created, with the relevant Razorpay webhook triggered. This lets you validate the flow quickly without waiting. |
| Pre-debit notification and AFA | The mandate hub sends the pre-debit notification, and the AFA (Additional Factor of Authentication) request for subsequent debits above the AFA limit. | Test Mode does not make calls to the mandate hubs, so no PDN or AFA messages are sent to the customer for any of the simulated scenarios. |
| Customer cancels, pauses or resumes the mandate | The customer does this from the mandate hub portal or issuer app. Razorpay sends the `token.cancelled` or `token.paused` webhook, and `token.confirmed` when a paused mandate is resumed. | Coming soon. Mandates created in Test Mode are not registered on the mandate hubs (MandateHQ and SiHub), so these actions can't be performed through the mandate hub portals. The test cards that simulate a customer cancelling or pausing the mandate are coming soon. Deleting a token with the Delete Token API works today. |

### What you can test

| Area | What you can test | Available on test mode |
| - | - | - |
| Mandate registration | Successful registration (including cards that do and don't support Native OTP) and registration failures, using the cards in section 3. | Available |
| Subsequent debits | Successful subsequent debits and failures, using the cards in section 3. | Available |
| Token management | Fetching tokens and deleting a token. | Available |
| Customer actions | A customer cancelling or pausing the mandate. | Coming soon |
| Webhooks | Payment, order and token webhooks for each of the above, delivered to the URL configured in your Test Mode Dashboard. | Available |

### Endpoint coverage in test mode

| API | Endpoint | Available on test mode |
| - | - | - |
| Create a customer | `POST /v1/customers` | Available |
| Create an order | `POST /v1/orders` | Available |
| Create an authorisation payment | `POST /v1/payments/create/json` | Available |
| Create a subsequent (recurring) payment | `POST /v1/payments/create/recurring` | Available |
| Fetch a payment (contains the token ID) | `GET /v1/payments/:id` | Available |
| Fetch all tokens for a customer | `GET /v1/customers/:id/tokens` | Available |
| Delete a token | `DELETE /v1/customers/:customer_id/tokens/:token_id` | Available |
| Check card eligibility (IIN API, on-demand) | `GET /v1/iins/:iin` | Available |

### Webhooks: what fires, and what it lets you decide

Test Mode webhooks have the same shape as live mode ones and are delivered to the URL set in your Test Mode Dashboard.

| Webhook | Fires when | What it lets you decide | In test mode |
| - | - | - | - |
| `payment.authorized` | The registration payment is authorised. | Store the `token_id` against the customer. | Available |
| `payment.captured` | The registration payment or a subsequent debit is captured. | The payment succeeded. | Available |
| `order.paid` | The order is fully paid. | The order is settled. Use a new order for the next subsequent debit. | Available |
| `payment.failed` | The registration payment or a subsequent debit fails. | Branch your handling on `error.reason` and `error.source`, not on the description text. | Available |
| `token.confirmed` | The mandate is registered, or a paused mandate is resumed. | You can create subsequent debits on this token. | Available |
| `token.rejected` | The token is rejected at the mandate hub, for example when the card BIN isn't supported. | Don't schedule subsequent debits. The customer has to register again. | Available |
| `token.cancelled` | You delete the token with the Delete Token API. | Stop charging the token. | Available |
| `token.cancelled` | The customer cancels the mandate. | Stop charging the token. | Coming soon |
| `token.paused` | The customer pauses the mandate. | Stop charging the token until `token.confirmed` arrives. | Coming soon |

**Which webhooks each scenario fires**

| Scenario | Webhooks, in order | In test mode |
| - | - | - |
| Registration succeeds (scenario 1) | `payment.authorized`, `payment.captured`, `order.paid`, `token.confirmed` | Available |
| Registration payment succeeds, token rejected later (scenario 10) | `payment.captured`, then `token.rejected` | Available |
| Registration fails | `payment.failed` | Available |
| Subsequent debit succeeds (scenario 3) | `payment.captured`, `order.paid` | Available |
| Subsequent debit fails | `payment.failed` | Available |
| Subsequent debit on a token that isn't confirmed (scenario 14) | None. No payment is created; read the error from the API response. | Available |
| You delete the token (scenario 16) | `token.cancelled` | Available |
| Customer cancels the mandate (scenario 24) | `token.cancelled` | Coming soon |
| Customer pauses the mandate (scenario 25) | `token.paused` | Coming soon |

**Before you rely on webhooks**

* **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.

### Token states

Charge a token only while it is `confirmed`.

| State | Means | Can you create a subsequent debit? | Webhook | Available on test mode |
| - | - | - | - | - |
| `initiated` | Registration started | No | — | Available |
| `confirmed` | Mandate is active (or was resumed) | Yes | `token.confirmed` | Available |
| `rejected` | Registration failed, or the mandate hub rejected the card (scenario 10) | No | `token.rejected` | Available |
| `paused` | The customer paused the mandate | No | `token.paused` | Coming soon |
| `cancelled` (you deleted the token) | — | No | `token.cancelled` | Available |
| `cancelled` (the customer cancelled the mandate) | — | No | `token.cancelled` | Coming soon |

## 2. Before You Begin

* Use your test keys (`rzp_test_...`). The APIs and requests are the same as in live mode; only the keys change.
* Configure a webhook URL in your Test Mode Dashboard to receive test events.
* Test Mode has a mock bank page with Success and Failure buttons. No real money is deducted.
* In Test Mode, the card number decides the scenario. To use a test card:
  * Create the authorisation payment and pass the card for your scenario in `card.number`. Use any CVV and any future expiry date.
  * Redirect the customer to the authentication URL returned in the response. A mock bank page is displayed.
  * Choose Success to register the mandate, or Failure for a registration failure card.
* For subsequent debits, register with the same card number first, then charge the token. The subsequent debit reaches its final state about 5 minutes later.

**Registration and subsequent-debit cards**

| Network | Card number | CVV and expiry date |
| - | - | - |
| Visa | `4100 2800 0000 1007` | Any CVV and any future date |
| Mastercard | `5305 6200 0006 0000` | Any CVV and any future date |
| RuPay | `6527 6589 0000 1005` | Any CVV and any future date |
| Visa (legacy) | `4718 6091 0820 4366` | Any CVV and any future date. Previously documented for Subscriptions, and still supported for the happy mandate-registration flow. |

## 3. Scenarios to Test Your Integration

This section lists the scenarios worth running against your card recurring integration before you go live. They are grouped by what each one proves, so that you can work down the page in order and cover the integration end to end.

**Must-have.** Run these first. They cover registration, the subsequent debit and the outcomes you are almost certain to meet once you are live, including a failed registration and a failed subsequent debit.

**Other common scenarios.** These come up in normal running: Native OTP and bank redirect, eligibility checks, a token rejected after registration, cards that can't be used for recurring, AFA, token checks and token management.

**Error scenarios.** Failures you bring on deliberately with a test card, so that you can check your error handling without waiting for a real failure. Each row uses one test card.

**Not available in test mode yet.** These are real scenarios your integration has to handle, but they can't be reproduced in Test Mode today.

### 3.1 Must-have

<table>
  <colgroup>
    <col width="5%" />

    <col width="22%" />

    <col width="18%" />

    <col width="55%" />
  </colgroup>

  <thead><tr><th>#</th><th>Scenario</th><th>Trigger creds</th><th>Steps to follow</th></tr></thead>

  <tbody>
    <tr><td>1</td><td>Register a mandate</td><td>`4100 2800 0000 1007` (Visa), `5305 6200 0006 0000` (Mastercard) or `6527 6589 0000 1005` (RuPay) + Success</td><td>Create a customer (`POST /v1/customers`) and store the returned `customer_id`. Create an order (`POST /v1/orders`) with `method: "card"`, the `customer_id` and a `token` object (`max_amount`, `expire_at`, `frequency`). Create the authorisation payment (`POST /v1/payments/create/json`, `recurring: true`) with the trigger card in `card.number`, any CVV and any future expiry date. Redirect to the authentication URL in the response, and choose Success on the mock bank page. Webhooks received: `payment.authorized`, `payment.captured`, `order.paid`, `token.confirmed`. Status changes: the payment moves to `captured` and the token to `confirmed`. Store the `token_id`; you need it for every subsequent debit.</td></tr>
    <tr><td>2</td><td>Registration fails: insufficient funds</td><td>`4100 2800 0008 0001` + Failure</td><td>Create a customer and an order as in #1. Create the authorisation payment with the trigger card, any CVV and any future expiry date. Redirect to the authentication URL, and choose Failure on the mock bank page. Confirm `payment.failed` with `insufficient_funds`. The mandate is not registered. Let the customer retry the registration, for example with another card.</td></tr>
    <tr><td>3</td><td>Subsequent debit succeeds</td><td>Same card as #1</td><td>Register a mandate (#1). Create a new order (`POST /v1/orders`) for the subsequent debit, then charge the token (`POST /v1/payments/create/recurring`), or charge it from the Dashboard. Wait about 5 minutes (poll with a timeout). Confirm `payment.captured` and `order.paid`.</td></tr>
    <tr><td>4</td><td>Subsequent debit fails: insufficient funds</td><td>`4718 6000 0007 0021`</td><td>Register a mandate with the trigger card, choosing Success (#1). Create a new order for the subsequent debit, then charge the token, or charge it from the Dashboard. Confirm `payment.failed` with `insufficient_funds`. Token stays active.</td></tr>
    <tr><td>5</td><td>Mandate cancelled before the subsequent debit</td><td>`4718 6091 2000 0016`</td><td>Register a mandate with the trigger card, choosing Success (#1). Create a new order for the subsequent debit, then charge the token. Confirm `card_mandate_not_active`. Confirm you stop scheduling subsequent debits on this token.</td></tr>
    <tr><td>6</td><td>Mandate paused before the subsequent debit</td><td>`4718 6091 2007 0019`</td><td>Register a mandate with the trigger card, choosing Success (#1). Create a new order for the subsequent debit, then charge the token. Confirm `card_mandate_paused`. Confirm you stop scheduling subsequent debits on this token.</td></tr>
  </tbody>
</table>

### 3.2 Other common scenarios

<table>
  <colgroup>
    <col width="5%" />

    <col width="22%" />

    <col width="18%" />

    <col width="55%" />
  </colgroup>

  <thead><tr><th>#</th><th>Scenario</th><th>Trigger creds</th><th>Steps to follow</th></tr></thead>

  <tbody>
    <tr><td>7</td><td>Register with a card that supports Native OTP</td><td>`5305 6200 0006 0000`</td><td>Create a customer and an order as in #1. Create the authorisation payment with the trigger card, any CVV and any future expiry date. The customer enters the OTP on the Razorpay page. Confirm the mandate registration succeeds and `token.confirmed` is sent.</td></tr>
    <tr><td>8</td><td>Register with a card that doesn't support Native OTP</td><td>`4100 2800 0000 1007`</td><td>Create a customer and an order as in #1. Create the authorisation payment with the trigger card, any CVV and any future expiry date. The customer is redirected to the mock bank page; choose Success. Confirm the mandate registration succeeds and `token.confirmed` is sent.</td></tr>
    <tr><td>9</td><td>Check recurring eligibility with the IIN API</td><td>`GET /v1/iins/:iin` (on-demand)</td><td>Call the IIN API with the card's IIN before you create the registration payment. The IIN API is an on-demand feature; ask Support to enable it on your account. Confirm you block registration when `recurring.available` is `false`.</td></tr>
    <tr><td>10</td><td>Registration payment succeeds, token rejected later</td><td>`4100 2800 0005 0012` + Success</td><td>Create a customer and an order as in #1. Create the authorisation payment with the trigger card, any CVV and any future expiry date. Redirect to the authentication URL, and choose Success on the mock bank page. Confirm `payment.captured`, then `token.rejected`. The token is rejected at the mandate hub because the card BIN isn't supported. Confirm you don't schedule subsequent debits. Treat the mandate as live only after `token.confirmed`.</td></tr>
    <tr><td>11</td><td>Card not eligible for recurring</td><td>`4100 2800 0006 0011` + Failure</td><td>Create a customer and an order as in #1. Create the authorisation payment with the trigger card, any CVV and any future expiry date. Redirect to the authentication URL, and choose Failure on the mock bank page. Confirm `payment.failed` with `card_mandate_card_not_supported`. Ask the customer to use a different card.</td></tr>
    <tr><td>12</td><td>Card BIN not supported for recurring</td><td>`6527 6500 0007 0022` + Failure</td><td>Create a customer and an order as in #1. Create the authorisation payment with the trigger card, any CVV and any future expiry date. Redirect to the authentication URL, and choose Failure on the mock bank page. Confirm `payment.failed` with `card_mandate_bin_not_supported`. Ask the customer to use a different card.</td></tr>
    <tr><td>13</td><td>Customer doesn't approve AFA</td><td>`4718 6091 2009 0017`</td><td>Register a mandate with the trigger card, choosing Success (#1). Create a new order for the subsequent debit, then charge the token. Confirm `customer_action_required` and your customer prompt.</td></tr>
    <tr><td>14</td><td>Subsequent debit on a token that isn't confirmed</td><td>`4718 6091 2002 0014`</td><td>Register a mandate with the trigger card, choosing Success (#1). Create a new order for the subsequent debit, then charge the token. Confirm the create payment request is rejected with `token_not_eligible` and no payment is created. Read the error from the API response; no payment webhook is sent.</td></tr>
    <tr><td>15</td><td>Subsequent debit on an expired token</td><td>`4718 6091 2008 0018`</td><td>Register a mandate with the trigger card, choosing Success (#1). Create a new order for the subsequent debit, then charge the token. Confirm the payment is created and then fails with `token_expired`.</td></tr>
    <tr><td>16</td><td>Fetch and delete tokens</td><td>A token from scenario 1</td><td>Fetch the token created by the registration payment: `GET /v1/payments/:id` (the response contains `token_id`). Fetch all tokens for the customer: `GET /v1/customers/:id/tokens`. Delete the token: `DELETE /v1/customers/:customer_id/tokens/:token_id`. Confirm the status changes to `cancelled` and `token.cancelled` is sent. The token is not removed from Razorpay.</td></tr>
  </tbody>
</table>

### 3.3 Error scenarios

**Registration**

These happen while the customer is registering the mandate. The registration payment fails, so the mandate is not registered and you can't create subsequent debits. The customer has to start the registration again.

<table>
  <colgroup>
    <col width="5%" />

    <col width="18%" />

    <col width="14%" />

    <col width="22%" />

    <col width="41%" />
  </colgroup>

  <thead><tr><th>#</th><th>Scenario</th><th>Trigger creds</th><th>Error object</th><th>Steps to follow</th></tr></thead>

  <tbody>
    <tr><td>17</td><td>Customer cancels the payment</td><td>`4100 2800 0007 0002` + Failure</td><td>`code` BAD\_REQUEST\_ERROR\
    `source` customer\
    `step` payment\_authentication\
    `reason` payment\_cancelled</td><td>Create a customer and an order as in #1. Create the authorisation payment with the trigger card, any CVV and any future expiry date. Redirect to the authentication URL, and choose Failure on the mock bank page. Confirm `payment.failed` and the error object. Let the customer retry the registration.</td></tr>
    <tr><td>18</td><td>Payment times out</td><td>`4100 2800 0009 0000` + Failure</td><td>`code` BAD\_REQUEST\_ERROR\
    `source` customer\
    `step` payment\_authentication\
    `reason` payment\_timed\_out</td><td>Create a customer and an order as in #1. Create the authorisation payment with the trigger card, any CVV and any future expiry date. Redirect to the authentication URL, and choose Failure on the mock bank page. Confirm `payment.failed` and the error object. Let the customer retry the registration.</td></tr>
    <tr><td>19</td><td>Incorrect OTP or verification details</td><td>`4100 2800 0000 0009` + Failure</td><td>`code` GATEWAY\_ERROR\
    `source` customer\
    `step` payment\_authentication\
    `reason` authentication\_failed</td><td>Create a customer and an order as in #1. Create the authorisation payment with the trigger card, any CVV and any future expiry date. Redirect to the authentication URL, and choose Failure on the mock bank page. Confirm `payment.failed` and the error object. Let the customer retry the registration.</td></tr>
    <tr><td>20</td><td>Card not enabled for online payments</td><td>`4100 2800 0004 0013` + Failure</td><td>`code` GATEWAY\_ERROR\
    `source` customer\
    `step` card\_enrollment\_check\
    `reason` card\_not\_enrolled</td><td>Create a customer and an order as in #1. Create the authorisation payment with the trigger card, any CVV and any future expiry date. Redirect to the authentication URL, and choose Failure on the mock bank page. Confirm `payment.failed` and the error object. Let the customer retry the registration.</td></tr>
    <tr><td>21</td><td>Declined by the issuing bank</td><td>`4100 2800 0005 0004` + Failure</td><td>`code` BAD\_REQUEST\_ERROR\
    `source` issuer\_bank\
    `step` payment\_authorization\
    `reason` card\_declined</td><td>Create a customer and an order as in #1. Create the authorisation payment with the trigger card, any CVV and any future expiry date. Redirect to the authentication URL, and choose Failure on the mock bank page. Confirm `payment.failed` and the error object. Let the customer retry the registration.</td></tr>
    <tr><td>22</td><td>Card disabled for online payments</td><td>`4100 2800 0003 0006` + Failure</td><td>`code` BAD\_REQUEST\_ERROR\
    `source` issuer\_bank\
    `step` payment\_authorization\
    `reason` card\_disabled\_for\_online\_payments</td><td>Create a customer and an order as in #1. Create the authorisation payment with the trigger card, any CVV and any future expiry date. Redirect to the authentication URL, and choose Failure on the mock bank page. Confirm `payment.failed` and the error object. Let the customer retry the registration.</td></tr>
  </tbody>
</table>

**Subsequent debit**

These happen on a subsequent debit, after the mandate is registered.

<table>
  <colgroup>
    <col width="5%" />

    <col width="18%" />

    <col width="14%" />

    <col width="22%" />

    <col width="41%" />
  </colgroup>

  <thead><tr><th>#</th><th>Scenario</th><th>Trigger creds</th><th>Error object</th><th>Steps to follow</th></tr></thead>

  <tbody>
    <tr><td>23</td><td>Pre-debit notification fails</td><td>`4718 6091 2001 0015`</td><td>`code` BAD\_REQUEST\_ERROR\
    `source` issuer\_bank\
    `step` pre\_debit\_notification\_verify\
    `reason` pre\_debit\_notification\_failed</td><td>Register a mandate with the trigger card, choosing Success (#1). Create a new order for the subsequent debit, then charge the token. Confirm `payment.failed` with the error object about 5 minutes after you create the payment.</td></tr>
  </tbody>
</table>

The full list of card recurring error reasons, including the ones no test card reproduces, is on the [Error index](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices/error-handling.md). These are payment errors; errors from creating the order (bad `amount`, `currency`, `receipt` and so on) are on [Orders → Simulate failures](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/orders.md).

### 3.4 Not available in test mode yet

These scenarios can't be reproduced in Test Mode today, but your integration still has to handle them. Each row gives the behaviour your code has to implement.

| # | Scenario | What it is | What your integration has to do | Test mode |
| - | - | - | - | - |
| 24 | Customer cancels the mandate | In live mode, the customer cancels from the mandate hub portal or the issuer app, and Razorpay sends `token.cancelled`. Test card `4718 6091 2009 0025` simulates this and is coming soon in test mode. | Update your stored token status on `token.cancelled` and stop charging the token. | Coming soon |
| 25 | Customer pauses the mandate | In live mode, the customer pauses from the mandate hub portal or the issuer app, and Razorpay sends `token.paused`; `token.confirmed` is sent when the customer resumes it. Test card `4718 6091 2008 0026` simulates the pause and is coming soon in test mode. | Update your stored token status on `token.paused`, and on `token.confirmed` when the mandate resumes. Don't charge the token until it is `confirmed` again. | Coming soon |
| 26 | 36-hour subsequent debit window and AFA messages | In live mode, the mandate hub sends the pre-debit notification and the card is debited 36 hours later; debits above the AFA limit need the customer's approval through a link valid for 72 hours. Test mode simulates the window: a subsequent debit reaches its final state in about 5 minutes, and no pre-debit notification or AFA message reaches the customer. | Wait for `payment.captured` or `payment.failed`; don't treat `created` as final. Check this in a controlled live test. | Live only |

## 4. Common Integration Gaps to Avoid

These are the mistakes that most often cause failed payments, missed subsequent debits or double charges after go-live. "Test with" gives the scenario numbers in section 3 that prove you've handled it.

### Webhook and token updates

| Gap | What goes wrong in production | What to do | Test with |
| - | - | - | - |
| Scheduling subsequent debits on a captured authorisation payment | The registration payment captures but the mandate hub rejects the token. Every subsequent debit then fails. | Treat the mandate as live only after `token.confirmed`. Handle `token.rejected` after `payment.captured`. | 1, 10 |
| Token status not kept in sync | The customer cancels from their bank app; you keep charging and every subsequent debit fails. | Update your stored token status on `token.cancelled` and `token.paused`, and on `token.confirmed` when a paused mandate resumes. Stop scheduling on anything but `confirmed`. | 24, 25 |
| Token ID not stored | You can't charge the customer next cycle. | Store `token_id` from `payment.authorized` or `token.confirmed` against the customer. | 1 |

### Error handling and messaging to the payer

| Gap | What goes wrong in production | What to do | Test with |
| - | - | - | - |
| Treating every subsequent debit failure the same | Retrying a cancelled or paused mandate wastes attempts and annoys the customer. | Stop on `card_mandate_not_active` and `card_mandate_paused`. Retry later on `insufficient_funds`. Prompt the customer on `customer_action_required`. | 4, 5, 6, 13 |
| Customer not told a subsequent debit failed | The customer isn't present at subsequent debit time, so nobody tells them their subscription lapsed. | Notify the customer by email or SMS with the reason and a link to update their card. | 4 |
| AFA approval not explained | Subsequent debits above the AFA limit wait for customer approval. If the customer does not approve within 72 hours, the debit fails with `customer_action_required`. | Tell the customer to approve the bank's AFA request, or keep subsequent debits below the limit. | 13 |

### Fetch API when webhooks are late

| Gap | What goes wrong in production | What to do | Test with |
| - | - | - | - |
| Treating `created` as stuck | A live subsequent debit stays `created` for about 36 hours after the pre-debit notification. You mark it failed and retry, causing a double charge attempt. | Wait for `payment.captured` or `payment.failed`. Poll `GET /v1/payments/:id` instead of timing out. | 3, 26 |
| Missed `token.confirmed` | A webhook outage means you never learn the mandate is active. | Fetch the token from `GET /v1/payments/:id` or `GET /v1/customers/:id/tokens` before scheduling the first subsequent debit. | 16 |

## 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.

| Topic | What to know | On this method | Test it | Read more |
| - | - | - | - | - |
| Difference between order and payment | An order is what you want to collect. A payment is one attempt to pay it. One order can have many attempts but only one captured payment. Create the order on your server so the amount can't be tampered with. | Registration uses an authorisation order. Every subsequent debit needs a new order. | BP-24, BP-25 | [Difference between order and payment ↗](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices/order-payment-relationship.md) |
| Order states | Orders move `created` → `attempted` → `paid`. An order stays `attempted` through failed attempts, and stays `paid` even after a refund. Handle Orders API errors (for example an invalid amount or currency) when you create the order. | A subsequent debit order stays `attempted` (payment `created`) for about 36 hours before the charge. | [Payment states](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices/payment-states-and-late-authorisation.md) · [Orders errors](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/orders.md) | [Order states ↗](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices/payment-states-and-late-authorisation.md) · [Orders API error codes ↗](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/orders.md) |
| Late authorisation | A payment with no bank response is marked `failed` after 10 minutes, but Razorpay keeps polling the bank for 3 days. If the bank confirms, the payment becomes `authorized` (under 0.5% of payments). Listen for `payment.authorized` and capture it, or let it auto-refund. | Applies to the registration payment. Subsequent debits report a final status by webhook. | BP-06 | [Late authorisation ↗](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices/payment-states-and-late-authorisation.md) |
| Capture settings | Choose auto-capture or manual capture and set the timeouts. An `authorized` payment you don't capture in time is auto-refunded. You can set capture behaviour per order through the Orders API. | Decide how the registration payment is captured or refunded. | BP-14 | [Capture settings ↗](https://razorpay.com/docs/payments/payment-gateway/rainy-day/capture-settings) |
| Multiple payments on the same order | Once an order is `paid`, no more payments are accepted on it. While a payment on the order is `authorized`, a new attempt isn't allowed. An attempt that authorises late on a paid order is auto-refunded. Retry on the same order so a customer can't pay twice. | Don't reuse a paid subsequent debit order for the next cycle. Create a fresh one. | BP-09 · BP-08 | [Multiple payments on the same order ↗](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices/payment-states-and-late-authorisation.md) |

All best practices: [Overview](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices) · [Webhooks and Fetch APIs](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices/webhooks-and-fetch-apis.md) · [Payment states and late authorisation](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices/payment-states-and-late-authorisation.md) · [Error handling](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices/error-handling.md) · [Downtime handling](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices/downtime-handling.md) · [Security](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices/security.md)

### Related Information

* [Cards](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/one-time/cards)
* [UPI Autopay Intent](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/recurring/upi-autopay-intent.md)
* [Orders](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/orders.md)
* [Best Practices](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices)
