Why I keep a ₹300 test bank account for payment testing — and the week it got flagged

I stopped pretending mocked payments were enough. I run a dedicated ₹300 bank account for integration testing — how I set it up, the rules, and the time the bank froze it.

Written by: Arjun Malhotra

A person holding a smartphone showing a payment app, laptop keyboard in the background
Photo by Brooke Cagle on Unsplash

It was 1:17 a.m. when my phone buzzed: our staging webhook queue had exploded. Customers on staging were getting weird payment statuses—success for failed flows, refunds for payments that never happened. I sipped my third coffee and scanned logs: every payment attempt from our test suite was hitting an actual bank endpoint and returning races of real UPI callbacks we hadn’t expected.

We had mocks for the payment gateway, a local ReplayProxy for webhooks, and a “fake” UPI handle in test config. That should have been enough. Except it wasn’t. We were accidentally using a real bank account in one environment. That one mistake is why, two weeks later, I opened a tiny savings account with ₹300 and turned it into our canonical payment test account.

Why a real account beats mocks (most of the time) Mocks are great for unit tests. They’re fast, deterministic, and don’t require a bank. But real integrations fail in ways mocks won’t reproduce:

After the midnight incident, I realized the cost of “not testing with money” was real outages and weird edge cases. The solution needed to be cheap, low-friction, and — importantly — auditable. A ₹300 savings account fit that brief.

How I set it up — boring, repeatable rules I did two things that made the account useful and safe.

  1. Keep it tiny and separate I opened a normal savings account with a regional bank branch that had decent internet banking and UPI support. Initial deposit: ₹300. No other salary or transfers go there. The account name is “Company Test — Payments”. This reduces the chance of the account becoming someone’s personal fallback.

  2. A strict three-rule policy (enforced in CI)

These rules are enforced by a tiny wrapper script we call paytestctl. It rotates API keys with a short TTL, signs all transactions, and writes a human-readable audit line to our repository (commit + timestamp) whenever tests touch the account.

The week it got flagged (and why I still think it was worth it) Two months in, we had reliable coverage, fewer production surprises, and better confidence. Then the bank froze the account.

What happened:

Consequences:

What I changed after the freeze

The honest tradeoffs This setup isn’t free of problems.

Why I recommend this to small startups in India If you’re shipping payments in a small team — and you’re tired of payment bugs that show up only in production — a low-cost, dedicated test account is the fastest way to reduce surprises. For roughly the time it takes to open a branch account (and ₹300), you get reproducible failures, realistic webhook shapes, and clarity on idempotency and retry behaviour.

A few practical tips before you do it

Takeaway Mocks are necessary; a tiny real account fills the gaps mocks miss. Be ready for paperwork and the occasional freeze. The win: fewer midnight surprises and payment behaviour you can actually trust. My open question for teams doing this: what’s the cleanest way to combine recorded real transactions with live testing so you get coverage without baking in bank risk? I’d rather hear a smart, practical solution than another theoretical pattern.