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

# Test Order Creation

> Test order creation, the order lifecycle, idempotent retries and request validation for your S2S integration in Test Mode.

Available in
🇮🇳 India

Every payment on Razorpay starts with an order created on your server. This page holds every order scenario: creating orders, the order lifecycle, idempotent retries and request validation. Method pages link here instead of repeating them.

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

## 1. Test Mode Coverage

Test your order integration with test keys. There are no special test credentials: the outcome depends on the request you send. Start from the request in the [test kit](#2-test-kit), then change one field at a time using the triggers in [Simulate Failures](#7-simulate-failures).

| Area | What you can test | Test mode | How it works in test mode |
| - | - | - | - |
| Create and fetch | Create an order, fetch it and fetch its payments | Available | Test orders exist only in test mode. Use the [Fetch an Order API](https://razorpay.com/docs/api/orders/fetch-with-id.md) (`GET /v1/orders/:id`) with your test keys. |
| Request validation | Every `input_validation_failed` and `extra_field_sent` error | Available | Same validation as live mode. Trigger each error by sending the bad input in its Trigger column in [Simulate Failures](#7-simulate-failures). |
| Maximum amount | Amount above your account's limit | Available | Same check as live mode, using your account settings for test mode: the higher of your maximum transaction amount and your international maximum (₹5,00,000 when no international maximum is set). An amount in another currency is converted to your account currency first. |
| Currencies | Orders in your account currency, and others if international payments are enabled | Available | Same rule as live mode. Activate international payments on the Dashboard (**Account & Settings → International payments**); this updates your account for both live and test mode. See [supported currencies](https://razorpay.com/docs/payments/international-payments). |
| Idempotency | `unique_receipt` | Available | See ORD-05. |
| Lifecycle | `created` → `attempted` → `paid`, the `order.paid` webhook, paying a paid order | Available | Use a payment method's test triggers to make payments fail or succeed. |
| Partial payments | `partial_payment`, `first_payment_min_amount` | Available | — |
| Server errors (5xx) | Retry with exponential backoff | Available in live mode only | Cannot be triggered in test mode. |
| Account activation and IP whitelisting | Authentication failed (account not activated), Access Denied (IP not whitelisted) | Available in live mode only | Do not apply to test keys. |

## 2. Test Kit

There are no special test credentials: the outcome depends on the request you send. Start from this valid request, then change one field at a time using the triggers in [Simulate Failures](#7-simulate-failures).

```bash theme={null}
curl -u [YOUR_TEST_KEY_ID]:[YOUR_TEST_KEY_SECRET] \
-X POST https://api.razorpay.com/v1/orders \
-H "content-type: application/json" \
-d '{
  "amount": 5000,
  "currency": "INR",
  "receipt": "receipt#1",
  "unique_receipt": false,
  "notes": { "key1": "value3" },
  "partial_payment": false,
  "customer_id": "cust_4xbQrmEoA5WJ01",
  "description": "Order for receipt#1"
}'
```

| Parameter | Required | Rules to test |
| - | - | - |
| `amount` | Yes | Whole number in the smallest sub-unit; not negative; at least `100` (₹1.00) for INR; at most your account's maximum |
| `currency` | Yes | Three-letter supported ISO code; your account currency unless international is enabled |
| `receipt` | No | Up to 56 characters; no emoji; unique only with `unique_receipt: true` |
| `unique_receipt` | No | `true` / `false`; needs `receipt` |
| `notes` | No | JSON object up to 20 KB; no emoji; no `<script>` |
| `partial_payment`, `first_payment_min_amount` | No | Boolean; whole number ≥ 0 |
| `customer_id` | No | `cust_` + 14 characters, created on your account |
| `description` | No | Up to 255 characters |

## 3. Scenarios to Test Your Integration

25 scenarios · 21 available in test mode.

| Scenario ID | Type | Scenario | Trigger | Steps to follow | Test mode |
| - | - | - | - | - | - |
| ORD-01 | Create | Create an order | `amount: 100`, `currency: "INR"`, a `receipt` | `POST /v1/orders` with test keys. Confirm `status: created`, `amount_due = amount`, `attempts: 0`. Store `id` against your own receipt or cart. | Supported |
| ORD-02 | Lifecycle | Order moves to `paid` after a payment | ORD-01 + a method success trigger (for example, UPI `amount: 100`) | Create an order. Pay it with a method's success trigger. Confirm `order.paid`, `status: paid`, `amount_paid = amount`. | Supported |
| ORD-03 | Lifecycle | Failed attempt, then a successful retry | A method failure trigger, then a success trigger | Create an order and fail a payment on it. Confirm `status: attempted`, `attempts: 1`. Retry and confirm the order ends `paid`. | Supported |
| ORD-04 | Signature | Verify the payment signature against the order | Callback from ORD-02 | Compute HMAC-SHA256 of `order_id``razorpay_payment_id` with your key secret. Confirm it matches `razorpay_signature`; reject a tampered value. | Supported |
| ORD-05 | Idempotency | Retry create without a duplicate order | `unique_receipt: true` + the same `receipt` twice | Create an order with `unique_receipt: true`. Send the same request again. Confirm you get the same order ID back, not a second order. | Supported |
| ORD-06 | Fetch | Fetch the order and its payments | Order from ORD-02 | `GET /v1/orders/:id` and `GET /v1/orders/:id/payments`. Confirm status and payments match the webhooks. | Supported |
| ORD-07 | Lifecycle | Pay an order that is already paid | Order from ORD-02 + another payment | Start a second payment on the paid order. Confirm `order_already_paid` and that you create a new order instead. | Supported |
| ORD-08 | Validation | Amount errors | `amount` missing, `100.5`, `"100.0"`, `-100`, `0`, `99` | Send each bad `amount`. Confirm HTTP 400 and `input_validation_failed`. Confirm your code logs it and does not show it to the payer as a payment failure. | Supported |
| ORD-09 | Validation | Currency errors | `currency` missing, `"IN"`, `"ABC"`, a non-account currency | Send each bad `currency`. Confirm HTTP 400 with the matching description. | Supported |
| ORD-10 | Validation | Receipt, notes and description limits | 57-char `receipt`, emoji, `notes` over 20 KB, `<script>` in `notes`, 256-char `description` | Send each oversized or invalid field. Confirm HTTP 400. Sanitise these fields before you call the API. | Supported |
| ORD-11 | Validation | Malformed request | Invalid JSON, wrong types (`"currency": 1`, `"notes": "text"`, `"partial_payment": "yes"`) | Send each malformed body. Confirm HTTP 400. | Supported |
| ORD-12 | Auth | Missing or wrong API keys | No Basic Auth; wrong key secret; live key in test mode | Call without auth and with a wrong secret. Confirm HTTP 401 and no order is created. | Supported |
| ORD-13 | Errors | Server error and retry | — | On 5xx, retry with exponential back-off. Use `unique_receipt` so a retry cannot create a duplicate order. | Supported in live mode only |
| ORD-14 | Validation | Customer ID errors | `"customer_id": "cust_123"`; a 19-character ID that does not exist | Send each bad `customer_id`. Confirm HTTP 400. | Supported |
| ORD-15 | Validation | Parameter not enabled for your account | e.g. transfers without Route | Send a feature-gated parameter. Confirm `extra_field_sent`. | Supported |
| ORD-16 | Partial payments | Pay an order in parts | `partial_payment: true`, `first_payment_min_amount` | Create a partial-payment order. Pay less than the full amount, then the rest. Confirm `amount_paid` / `amount_due` after each payment and `order.paid` at the end. | Supported |
| ORD-17 | Account | Account not activated or IP not whitelisted | — | Live-mode conditions: Authentication failed (account not activated) and Access Denied (IP not whitelisted). Check before go-live. | Supported in live mode only |
| ORD-18 | Mandate | Create a mandate authorisation order | `method: "upi"` + token object | Create an order with a token object (`max_amount`, `frequency`). Used to register a UPI Autopay mandate. | Supported |
| ORD-19 | Mandate | Mandate order errors | Bad token object | Send invalid token fields. Confirm HTTP 400. | Supported |
| ORD-20 | Card recurring | Card mandate authorisation order | `customer_id` + a token object for cards | Create the authorisation order for a card mandate. Confirm `status: created`. | Supported |
| ORD-21 | Card recurring | Card mandate order errors | `token.first_payment_amount` set for cards; invalid `auth_type`; `max_amount: 0`; invalid `frequency`; `expire_at` in the past; token without `customer_id` | Send each bad request. Confirm the error below. | Supported |
| ORD-22 | Debit | A new order per subsequent debit | A confirmed token | Create a fresh order for each recurring debit. Confirm each maps to one debit. | Supported |
| ORD-23 | Debit | Decoupled debit order | Order with a `notification` object | Create a debit order with the `notification` object to send a pre-debit notification first. | Supported |
| ORD-24 | Validation | INR only for UPI | `currency` other than `INR` | Confirm UPI orders are rejected for non-INR currencies. | Supported |
| ORD-25 | Fetch | Webhook is late: fetch before you act | Order from ORD-02, with your `order.paid` handler paused | Pay an order but do not process the `order.paid` webhook. Call `GET /v1/orders/:id` and `GET /v1/orders/:id/payments`. Confirm `status: paid` and the captured payment, and fulfil from the fetch result. Resume the handler and confirm the late webhook does not fulfil the order a second time. | Supported |

## 4. Scenario Details

### ORD-01 Create an order

**Before you start** Test keys.

**How to test** Send the request in the [test kit](#2-test-kit) with `amount: 100`.

**Expected result** HTTP 200 with `id` (`order_…`), `status: created`, `amount_due = amount`, `amount_paid: 0`, `attempts: 0`.

**Webhooks** None. Orders fire `order.paid` only when paid.

**Your integration should** Create the order on your server, store `id` against your own receipt, and pass only the `order_id` to the client.

### ORD-02 · ORD-03 Order lifecycle

**Before you start** An order from ORD-01 and a method page's test triggers.

**How to test**

* Fail one payment on the order using a method's failure trigger.
* Fetch the order: `status: attempted`, `attempts: 1`.
* Pay successfully using a method's success trigger.
* Fetch the order: `status: paid`, `amount_paid = amount`.

**Expected result** `created` → `attempted` → `paid`. The order stays `paid` even after a refund.

**Webhooks** `payment.failed`, then `payment.captured` and `order.paid`.

**Your integration should** Fulfil on `order.paid` or `payment.captured`, never on the checkout callback alone.

**Watch out** Reuse the order ID when the same cart is retried after a payment failure. Order IDs should be unique for each checkout session: create a new order only for a new checkout session, such as a new cart.

### ORD-05 Idempotent create with `unique_receipt`

**Before you start** Test keys.

**How to test** Send `unique_receipt: true` with `receipt: "r-123"` twice.

**Expected result** Both calls return the same order ID.

**Your integration should** Use `unique_receipt` whenever you might retry a create (timeouts, 5xx), so a retry cannot create two orders for one cart.

**How receipt uniqueness works**

* Razorpay checks that `receipt` is unique only when you send `unique_receipt: true`, or when Razorpay has turned on receipt uniqueness for your account.
* With `unique_receipt: true`, a receipt you already used returns the existing order with HTTP 200. Retrying a create is safe.
* If uniqueness is on for your account and you do not send `unique_receipt: true`, a receipt you already used fails with HTTP 400 `Order receipt should be unique.`, and `metadata.order_id` holds the existing order's ID.
* Only orders created while uniqueness was on are checked, and only for 180 days. A receipt used on an order created without it does not count. Test mode and live mode are checked separately.
* Two creates with the same receipt at the same moment are handled one at a time. The second can fail with `Another request in progress for the same process.`; retry it and you get the existing order back.

### ORD-07 Pay an order that is already paid

**Before you start** A paid order from ORD-02.

**How to test** Start another payment with the same `order_id`.

**Expected result** The payment is declined with `order_already_paid`: "Your payment has been declined as the order is already paid. Please initiate the payment with a new order."

**Your integration should** Check the order status before showing checkout again. Create a new order for a new purchase.

### ORD-18 · ORD-19 UPI Autopay authorisation order (registration)

**Before you start** A customer (`POST /v1/customers`).

**How to test** Create the order with `amount: 100`, `currency: "INR"`, `method: "upi"`, `customer_id` and `token: {max_amount, frequency, expire_at}`. Then send each bad token below.

**Expected result** The valid order is created. Each bad request fails with the error below.

**Your integration should** Validate `max_amount` against your MCC limit and keep `frequency` to how you really bill before calling the API.

**Errors to test**

| Trigger | `error.reason` | `error.source` | `error.step` | `error.description` |
| - | - | - | - | - |
| Leave out `token.max_amount` | `input_validation_failed` | customer | — | the max\_amount field is mandatory for UPI mandate creation |
| `token.max_amount` below ₹1 | `input_validation_failed` | business | payment\_initiation | Max amount for UPI recurring payment cannot be less than ₹1.00 |
| `token.max_amount` above your MCC limit | `input_validation_failed` | customer | — | max amount for UPI recurring payment for this merchant category code cannot be greater than ₹N.00 |
| `token.frequency` set to a value outside the UPI frequency enum | `input_validation_failed` | business | payment\_initiation | The selected frequency is invalid. |
| `token.expire_at` in the past | `input_validation_failed` | business | payment\_initiation | expire\_at cannot be less than current time |
| `token.expire_at` 31 years out | `input_validation_failed` | customer | — | expire\_at cannot be more than 30 years for upi |
| `recurring_type` without `recurring_value` | `input_validation_failed` | customer | — | recurring\_type or recurring\_value is missing, send both the values for successful registration |
| Mismatched `recurring_type` / `recurring_value` | `input_validation_failed` | customer | — | recurring type and recurring value mismatch |
| `token` sent without `customer_id` | `input_validation_failed` | business | payment\_initiation | customer\_id: cannot be blank. |
| Order `amount` above `token.max_amount` | `input_validation_failed` | business | payment\_initiation | The order amount cannot be greater than the mandate max amount. |
| `token.recurring_type: "bogus"` | `input_validation_failed` | business | payment\_initiation | invalid recurring type |
| `token.recurring_value` outside the range for the frequency (for example, 8 on weekly) | `input_validation_failed` | business | payment\_initiation | recurring value should be between 1 and 7 for weekly frequency |
| `token.start_at` in the past | `input_validation_failed` | business | payment\_initiation | end time must be after start time |

### ORD-20 · ORD-21 Card mandate authorisation order

**Before you start** A customer.

**How to test** Create the authorisation order for a card mandate with `customer_id` and a token object, then send each bad request below.

**Expected result** The valid order is created. Each bad request fails with the error below.

**Errors to test**

| Trigger | `error.reason` | `error.source` | `error.step` | `error.description` |
| - | - | - | - | - |
| `token.first_payment_amount` set with `method: "card"` | `input_validation_failed` | business | payment\_initiation | token.first\_payment\_amount should be "null" for method = "card" |
| `token.auth_type: "xyz"` | `input_validation_failed` | business | payment\_initiation | The selected auth type is invalid. |
| `token.max_amount: 0` | `input_validation_failed` | business | payment\_initiation | The max amount should be greater than zero. |
| `token.frequency: "as_presen"` | `input_validation_failed` | business | payment\_initiation | The selected frequency is invalid. |
| `token.expire_at` in the past | `input_validation_failed` | business | payment\_initiation | expire\_at cannot be less than current time |
| `token` sent without `customer_id` | `input_validation_failed` | business | payment\_initiation | Customer Id is required with token field |
| A non-card token on a card order | `input_validation_failed` | business | payment\_initiation | Not a card mandate token |

### ORD-22 · ORD-23 Debit orders (subsequent debits)

**Before you start** A confirmed token.

**How to test**

* Create a new order for each debit, with its own `receipt`.
* Reuse a paid debit order.
* Debit twice in one billing cycle.
* UPI decoupled: add `notification: {token_id, payment_after}` and try to debit early, or above the notified amount.

**Expected result** Each new order debits once. The other attempts fail with the errors below.

**Your integration should** Create one order per charge. Check the token is confirmed before creating the order.

**Errors to test**

| Trigger | `error.reason` | `error.source` | `error.step` | `error.description` |
| - | - | - | - | - |
| Reuse a paid debit order | `order_already_paid` | business | payment\_initiation | Your payment has been declined as the order is already paid. Please initiate the payment with a new order. |
| Second debit in the same cycle | `duplicate_request` | business | payment\_response | This payment has already been processed for the current cycle. No further executions are permitted. |
| Debit on a token that isn't confirmed | `token_not_eligible` | business | payment\_initiation | Token is not confirmed for recurring payments |
| UPI decoupled: debit before `payment_after` | `invalid_request` | internal | payment\_initiation | Debit can be attempted 25 hours after sending the pre-debit notification |
| UPI decoupled: debit above the notified amount | `pre_debit_notification_request_amount_validation_failed` | internal | payment\_debit\_request | Payment amount exceeds the maximum amount allowed. |
| Debit within 26 hours of mandate expiry | `input_validation_failed` | customer | — | You cannot initiate subsequent payments within 26 hours of expiration of the mandate |

The 25-hour pre-debit-notification-to-debit window is enforced in test mode — it is a hardcoded NPCI rule, so a decoupled debit created inside the window is rejected. The 26-hour mandate-expiry rule is live-only.

## 5. Common Integration Gaps to Avoid

These are the mistakes that most often cause failed payments, missed debits or double charges after go-live.

### Webhook and token updates

| Gap | What goes wrong in production | What to do | Test with |
| - | - | - | - |
| Fulfilling on the checkout callback | The customer closes the page after paying; the callback never reaches you and a paid order looks unpaid. | Fulfil on `order.paid` or `payment.captured`. Map `order_id` to your own receipt so the webhook finds the right cart. | ORD-02 |
| Amount set by the client | A tampered client changes the amount it sends you. | Create the order on your server from your own cart total; pass only the `order_id` to the client. | ORD-01 |
| Partial payments treated as paid | You fulfil after the first part-payment. | Fulfil only when `amount_due` is `0` / `order.paid` fires. | ORD-16 |

### Error handling and messaging to the payer

| Gap | What goes wrong in production | What to do | Test with |
| - | - | - | - |
| Order errors shown to the payer | The customer sees "amount: must be no less than 100" and leaves. | Order errors are your request's problem: log and alert on them, and show the payer a generic "try again". | ORD-08 |
| Retrying 4xx errors | The same bad request fails forever and fills your logs. | Fix 4xx requests; retry only 5xx, with back-off. | ORD-08, ORD-13 |
| Matching on description text | A wording change breaks your error handling. | Branch on `reason` (and `field` when present). | ORD-08 |

### Fetch API when webhooks are late

| Gap | What goes wrong in production | What to do | Test with |
| - | - | - | - |
| Duplicate orders after a timeout | The create call times out, you retry, and the cart now has two orders. | Send `unique_receipt: true`, or fetch by receipt before creating again. | ORD-05 |
| Order status assumed from the last attempt | A late success or a second attempt changes the order after you decided. | Fetch `GET /v1/orders/:id` and `/payments` before acting when a webhook is late. | ORD-06, ORD-25 |

## 6. 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 cannot be tampered with. | This page. Create one order per purchase on your server; each payment attempt is a payment on it. | BP-24, BP-25 | [Difference between order and payment](https://razorpay.com/docs/payments/orders#order-and-payment-flows) |
| 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. | Test every state with ORD-02 and ORD-03, and the error codes in Simulate Failures. | Payment states · Orders errors | [Order states](https://razorpay.com/docs/payments/orders#order-states), [Orders API error codes](https://razorpay.com/docs/api/orders/create.md#errors) |
| 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. | A late-authorised payment on an order that is already paid is auto-refunded. | BP-06 | [Late authorisation](https://razorpay.com/docs/payments/payments/late-authorisation.md) |
| Capture settings | Choose auto-capture or manual capture and set the timeouts. An authorised payment you do not capture in time is auto-refunded. You can set capture behaviour per order through the Orders API. | Capture behaviour can be set per order through the Orders API. | BP-14 | [Capture settings](https://razorpay.com/docs/payments/payments/capture-settings.md) |
| 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 is not allowed. An attempt that authorises late on a paid order is auto-refunded. | Retry on the same order so a customer cannot pay twice. | BP-09 · BP-08 | [Multiple payments on the same order](https://razorpay.com/docs/payments/orders#order-states) |

## 7. Simulate Failures

To test request validation, send the valid request from the [test kit](#2-test-kit) and change one field at a time to an invalid value (see the rules column). Each invalid request returns HTTP 400 with an error object.

* Branch your handling on `error.reason` and `error.source`, never on the `description` text.
* Sanitise `receipt`, `notes` and `description` before you call the API.
* Log validation errors — do not surface them to the payer as a payment failure.

**How to read an error**

* `reason` tells you what went wrong. Build your error handling on it; it is stable.
* `description` is a human-readable message. It can change, so do not match on its text in code.
* `source` tells you who needs to act: `business` means your request needs fixing; `internal` means a problem at Razorpay, so retry.
* `step` tells you where in the flow the error happened.
* `field`, when present, names the request parameter that caused the error.
* The HTTP status tells you whether to retry: 4xx means fix the request first (retrying the same request fails again); 5xx means retry with exponential backoff.

**How to find and fix an error**

1. Take the `reason` from the error response and find it in the tables below.
2. To reproduce it in test mode, send the input in the Trigger column with test keys.
3. Fix the field named in `error.field`, when present, and retry.

### Errors available in test mode (34 triggers → 7 distinct `error.reason` values)

Send the input in the Trigger column with test keys. Every one fails before an order is created, so nothing reaches the payer: log it and fix the request. Retrying the same request fails again.

| Trigger | `error.code` | `error.source` | `error.step` | `error.reason` | `error.description` |
| - | - | - | - | - | - |
| `"amount": 99` with `"currency": "INR"` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | amount: must be no less than 100. (HTTP 400) |
| A parameter your account does not support, e.g. transfers without Route | BAD\_REQUEST\_ERROR | business | payment\_initiation | extra\_field\_sent | `<field>` is/are not required and should not be sent (HTTP 400) |
| Reuse a receipt already on your account | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | Order receipt should be unique. (HTTP 400) |
| `"amount": 100.5` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | The amount must be an integer. (HTTP 400) |
| `"currency": 1` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | expected string but provided `<value>` (HTTP 400) |
| Leave out `amount` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | amount: is required. (HTTP 400) |
| `"amount": -100` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | amount: must be no less than 0. (HTTP 400) |
| Leave out `currency` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | currency: cannot be blank. (HTTP 400) |
| An emoji in `notes` | BAD\_REQUEST\_ERROR | business | order\_creation | input\_validation\_failed | The notes field should contain valid UTF-8 encoded characters. (HTTP 400) |
| `"currency": "IN"` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | currency: the length must be exactly 3. (HTTP 400) |
| `"amount": "100.0"` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | The amount must be a valid integer. (HTTP 400) |
| `"amount": ""` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | amount: cannot be blank. (HTTP 400) |
| A 19-character `customer_id` that does not exist | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | customer\_id: The id provided does not exist. (HTTP 400) |
| A body that is not valid JSON | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | `<JSON parse error>` but provided `<value>` (HTTP 400) |
| Leave out `amount` and `currency` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | amount: is required; currency: cannot be blank. (HTTP 400) |
| A 57-character `receipt` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | receipt: the length must be no more than 56. (HTTP 400) |
| A currency other than your account currency, without international payments enabled | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | Order Currency is not supported (HTTP 400) |
| `"currency": "ABC"` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | currency: validation\_failure: BAD\_REQUEST\_INVALID\_CURRENCY. (HTTP 400) |
| `notes` larger than 20 KB | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | notes exceeded max size of 20KB (HTTP 400) |
| A `<script>` tag in `notes` | BAD\_REQUEST\_ERROR | business | order\_creation | input\_validation\_failed | notes must be valid key-value pairs of strings (HTTP 400) |
| `"notes": "text"` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | Field should be of type list or map. (HTTP 400) |
| An emoji in `receipt` | BAD\_REQUEST\_ERROR | business | order\_creation | input\_validation\_failed | The receipt field should contain valid UTF-8 encoded characters. (HTTP 400) |
| A 256-character `description` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | description: the length must be no more than 256. (HTTP 400) |
| `"amount": 0` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | amount: must be no less than 1. (HTTP 400) |
| `"partial_payment": "yes"` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | Boolean field must be either true or false. (HTTP 400) |
| `"customer_id": "cust_123"` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | customer\_id: the length must be exactly 19. (HTTP 400) |
| Leave out `currency` and send `"method": "xyz"` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | currency: cannot be blank; method: must be a valid value. (HTTP 400) |
| `"amount": -1` and no `currency` | BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | amount: must be no less than 0; currency: cannot be blank. (HTTP 400) |
| An amount above your account's maximum transaction amount | BAD\_REQUEST\_ERROR | business | order\_creation | invalid\_amount | Amount exceeds maximum amount allowed. (HTTP 400) |
| `"currency": "XYZ"` | BAD\_REQUEST\_ERROR | business | order\_creation | invalid\_currency | Currency is not supported (HTTP 400) |
| Call without HTTP Basic Auth | BAD\_REQUEST\_ERROR | business | order\_creation | — | Please provide the Username or Password (HTTP 401) |
| Two parallel creates with the same receipt | BAD\_REQUEST\_ERROR | business | order\_creation | — | Another request in progress for the same process. (HTTP 400) |

### Errors not available in test mode (15 rows → 8 distinct `error.reason` values)

These come from Razorpay's side or from live-account conditions. On 5xx, retry with exponential back-off; with `unique_receipt` a retry will not create a duplicate order.

| `error.code` | `error.source` | `error.step` | `error.reason` | `error.description` |
| - | - | - | - | - |
| BAD\_REQUEST\_ERROR | business | payment\_initiation | input\_validation\_failed | Order receipt should be unique. (HTTP 500) |
| BAD\_REQUEST\_ERROR | business | order\_creation | merchant\_not\_activated | Authentication failed (HTTP 401) |
| BAD\_REQUEST\_ERROR | business | order\_creation | — | Access Denied (HTTP 400) |
| BAD\_REQUEST\_ERROR | business | order\_creation | — | Passport validation failed (HTTP 401) |
| SERVER\_ERROR | internal | payment\_initiation | server\_error | We are facing some trouble completing your request at the moment. Please try again shortly. (HTTP 502) |
| SERVER\_ERROR | internal | payment\_initiation | server\_error | We are facing some trouble completing your request at the moment. Please try again shortly. (HTTP 500) |
| SERVER\_ERROR | internal | NA | server\_error | We are facing some trouble completing your request at the moment. Please try again shortly. (HTTP 500) |
| SERVER\_ERROR | internal | payment\_initiation | server\_error | We are facing some trouble completing your request at the moment. Please try again shortly. (HTTP 400) |
| SERVER\_ERROR | internal | NA | server\_error | We are facing some trouble completing your request at the moment. Please try again shortly. (HTTP 504) |
| SERVER\_ERROR | internal | payment\_authorization | server\_error | We are facing some trouble completing your request at the moment. Please try again shortly. (HTTP 500) |
| SERVER\_ERROR | internal | payment\_validate | server\_error | We are facing some trouble completing your request at the moment. Please try again shortly. (HTTP 400) |
| BAD\_REQUEST\_ERROR | internal | order\_creation | server\_error | DCS Fetch Feature Failure (HTTP 400) |
| SERVER\_ERROR | asv\_server | — | request\_failure | unexpected error received from account service (HTTP 500) |
| SERVER\_ERROR | — | — | — | The server encountered an error. The incident has been reported to admins. (HTTP 400) |

### Related Information

* [Test Your S2S Integration](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s.md)
* [Refunds](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/refunds.md)
* [Best Practices](https://razorpay.com/docs/payments/payment-gateway/s2s-integration/test-s2s/best-practices)
* [Orders API](https://razorpay.com/docs/api/orders.md)
