None of this data is private, so here is all of it. Every order, every reason a payment failed, and the exact shape of the records the agent reads.
A Razorpay test account starts empty. You cannot fill it with a year of payment history, and you cannot make old payments fail on demand. To show recovery working at any scale, the history had to be created.
So the history is made up, but the actions are real. The agent thinks about this data, but when it takes a payment or sends a link, that is a real call to Razorpay.
The data is built from a fixed starting number, so it comes out identical every single time. That keeps the numbers on Command Center comparable, instead of quietly shifting underneath you.
Every order has a hidden label saying what kind of situation it was built to represent. The agent never sees these labels. Only the scoring code can read them, so the answer cannot leak into the AI's view by accident.
Paid with no trouble. These exist so customers have a real payment history to look back on.
The customer never typed the OTP. They walked away at the last step.
The connection timed out. Nothing wrong with the customer or the card.
The card is expired or blocked. The card is the problem, not the customer.
Not enough money in the account.
Three to five tries over several days. These are the ones that hit the retry limit.
These shapes are not invented. They copy Razorpay's own, which is why the exact same code works on this made-up data and on a real test account, with nothing in between to translate.
Order order_A05tdszK59Ud5Y, taken straight from the data. This customer tried several times and failed each time, so you can see the full error detail the agent reads. This is exactly what the agent gets back, minus the hidden label.
{
"order": {
"id": "order_A05tdszK59Ud5Y",
"customer_id": "cust_N653RPhCWJtK1K",
"amount_paise": 99900,
"currency": "INR",
"receipt": "rcpt_00216",
"status": "attempted",
"created_at": "2026-03-09T00:45:00.000Z"
},
"customer": {
"id": "cust_N653RPhCWJtK1K",
"email": "isha.banerjee99@outlook.com",
"contact": "+917943837410",
"created_at": "2025-05-19T22:28:00.000Z"
},
"payments": [
{
"id": "pay_8RgvMRVOO7W4hk",
"order_id": "order_A05tdszK59Ud5Y",
"customer_id": "cust_N653RPhCWJtK1K",
"amount_paise": 99900,
"method": "upi",
"created_at": "2026-03-09T00:56:00.000Z",
"status": "failed",
"error": {
"code": "BAD_REQUEST_ERROR",
"description": "Payment failed due to insufficient funds in the account.",
"source": "customer",
"step": "payment_authorization",
"reason": "insufficient_funds"
}
},
{
"id": "pay_KvxOsUhysDgKNT",
"order_id": "order_A05tdszK59Ud5Y",
"customer_id": "cust_N653RPhCWJtK1K",
"amount_paise": 99900,
"method": "upi",
"created_at": "2026-03-09T21:58:00.000Z",
"status": "failed",
"error": {
"code": "BAD_REQUEST_ERROR",
"description": "Payment failed due to insufficient funds in the account.",
"source": "customer",
"step": "payment_authorization",
"reason": "insufficient_funds"
}
},
{
"id": "pay_81s9vNag7E9NAQ",
"order_id": "order_A05tdszK59Ud5Y",
"customer_id": "cust_N653RPhCWJtK1K",
"amount_paise": 99900,
"method": "upi",
"created_at": "2026-03-11T16:59:00.000Z",
"status": "failed",
"error": {
"code": "BAD_REQUEST_ERROR",
"description": "Payment failed due to insufficient funds in the account.",
"source": "customer",
"step": "payment_authorization",
"reason": "insufficient_funds"
}
},
{
"id": "pay_Iy8tvYxh1cMM1T",
"order_id": "order_A05tdszK59Ud5Y",
"customer_id": "cust_N653RPhCWJtK1K",
"amount_paise": 99900,
"method": "upi",
"created_at": "2026-03-13T10:06:00.000Z",
"status": "failed",
"error": {
"code": "BAD_REQUEST_ERROR",
"description": "Payment failed due to insufficient funds in the account.",
"source": "customer",
"step": "payment_authorization",
"reason": "insufficient_funds"
}
}
]
}The bank was down. These are grouped around the same bank and time, the way a real outage would be.
Failed, then the customer paid on their own later. These check that the agent looks again before acting.
The money was approved but never actually taken. A direct capture fixes these.
The bank thought it looked risky and blocked it.
One attempt to pay an order. It can be created, authorized, captured, refunded or failed. Once a payment fails it stays failed forever, so recovery always means making a new one.
Money is always counted in whole paise, never as a decimal, so rounding can never lose a rupee.