Sandbox behaviour and test data
Test mode is a separate ledger with its own keys, its own events and its own data. This is the reference for making it produce the outcome you need — including the ones you would rather not meet for the first time in production.
What test mode is
Every account has two ledgers. They share nothing: separate keys, separate objects, separate
webhook endpoints, separate reports. A sk_test_ key cannot read or move live
funds under any circumstance, which makes the sandbox safe to hand to a contractor.
- Isolation Your sandbox is yours alone. It is not a shared mock, so one team's data never appears in another's list call.
- Parity Same endpoints, same fields, same errors, same event names. The only difference in your code should be which key it reads.
- Speed Settlement is compressed: what takes a working day in live mode resolves in seconds, so an end-to-end test finishes inside a CI run.
- Reset Clear the sandbox from the dashboard at any time. Objects go, keys and endpoint configuration stay.
Test cards
Use any future expiry date, any three-digit CVC and any postcode. The number decides the outcome. All of them run through hosted fields exactly as a real card would.
| Number | Brand | Outcome |
|---|---|---|
| 4242 4242 4242 4242 | Visa | succeeded frictionless, no challenge |
| 5555 5555 5555 4444 | Mastercard | succeeded frictionless |
| 4000 0027 6000 3184 | Visa | requires_action always challenges |
| 4000 0000 0000 9995 | Visa | failed insufficient_funds |
| 4000 0000 0000 0002 | Visa | failed do_not_honor |
| 4000 0000 0000 0069 | Visa | failed expired_card |
| 4100 0000 0000 0019 | Visa | failed fraud_suspected |
| 4000 0000 0000 0259 | Visa | succeeded then dispute.opened after 60s |
When you need a decline the card list does not cover, steer it with the amount instead. The trigger works on every method, so the same test fixture exercises cards and APMs alike.
# the last two minor units steer the outcome in test modeamount: 4200 → succeededamount: 4201 → failed, insufficient_fundsamount: 4202 → failed, do_not_honoramount: 4203 → failed, fraud_suspectedamount: 4204 → processing for 90s, then succeededamount: 4205 → canceled by the payer# works on every method, including the APMs below Live mode ignores these entirely — 4201 is simply 42.01 EUR there. The trigger table is a test-ledger behaviour, not a hidden API.
Forcing 3-D Secure outcomes
Authentication is where checkout conversion is won or lost, so it deserves more than one happy path in your suite. The sandbox challenge page offers every outcome the real flow can produce.
| Choose | Result | What it proves |
|---|---|---|
| Complete | succeeded, liability shifted | The happy path and your fulfilment trigger. |
| Fail | authentication_failed | Your retry copy. This one is safe to retry in-session. |
| Abandon | canceled | The payer closing the tab mid-challenge. Basket must survive. |
| Time out | authentication_timeout | A bank app that never answers. The slowest path your UI must tolerate. |
| Unavailable | Frictionless approval, no shift | Issuer directory down. Confirms you handle an unshifted success. |
Send an sca.request of exemption with any amount under 3000 minor
units and the sandbox grants it; above that it forces a challenge regardless. That gives you
both branches of the exemption logic without waiting on a
real issuer's risk engine.
APM and bank simulators
Each alternative method redirects to a simulator that mirrors the real interface closely enough to test your copy and your timing, and offers the same outcome buttons.
| Method | Simulator | Notable behaviour |
|---|---|---|
| iDEAL | Bank picker, then approve or decline | Choosing "Test Bank Slow" holds at processing for 90 seconds. |
| Bancontact | QR page, then approve or decline | The QR is decorative in test mode. Use the buttons. |
| BLIK | Six-digit code entry | 777777 succeeds. 111111 expires so you can test the resend prompt. |
| EPS | Bank picker, then approve or decline | Declines return the same reason codes as cards. |
| Open banking | Consent screen, then approve or decline | "Revoke later" fires mandate.revoked five minutes after approval. |
| Apple Pay · Google Pay | Wallet sheet on a real device | Needs a registered test domain. The sheet does not render in a desktop simulator. |
Payouts and disputes
Money leaving is easier to get wrong than money arriving, because the failure paths are rarer and therefore less tested. Two IBANs and one card cover the cases that matter.
| Value | Outcome |
|---|---|
| NL02 BAZP 0000 0000 01 | Settles over SEPA Instant in seconds. payout.settled. |
| NL02 BAZP 0000 0000 02 | Falls back to SEPA Credit Transfer, settles next simulated working day. |
| NL02 BAZP 0000 0000 09 | Rejected by the beneficiary bank. payout.returned with a reason. |
| 4000 0000 0000 0259 | Charges, then opens a dispute after 60 seconds so you can test evidence upload. |
Disputes in the sandbox run the full lifecycle on a compressed clock: opened, evidence due, submitted, then won or lost depending on whether you uploaded anything. Both endings are worth wiring, because the accounting entries differ. See payouts and anti-fraud for what each state means commercially.
Triggering events by hand
You do not need a real object to test a handler. In test mode a single call fires any event type at every subscribed sandbox endpoint, correctly signed.
# force any event against a sandbox endpointcurl -X POST https://api.bazpay.com/v1/test/events \ -H "Authorization: Bearer sk_test_9f2c…" \ -d '{"type": "dispute.opened", "payment": "pay_3fJ2Qk8vTn"}'{ "id": "evt_9Rt2Ny7cVb", "delivered_to": 1 } delivered_to counts the endpoints that received it. Zero means nothing is subscribed to that type — the usual reason a handler seems not to run.
Three cases worth building into the suite while this is easy:
- The same event twice, to prove your deduplication index actually holds.
- A
refund.settledbefore itspayment.succeeded, to prove out-of-order arrivals do not corrupt an order. - A deliberate
500from your handler, to watch the retry ladder and confirm the event eventually lands.
Signature verification and the retry schedule: the webhooks guide.
What the sandbox cannot tell you
A green test suite is necessary and not sufficient. Four things behave differently once real money and real issuers are involved, and knowing which four is the point of this section.
- Approval rates Sandbox issuers always follow the script. Real ones decline for reasons no simulator models — velocity, geography, an issuer's own outage.
- Latency Test authorisations return in milliseconds. Live card authorisations take a few hundred, and a bank redirect depends on how fast the payer opens their app.
- Settlement timing The sandbox compresses days into seconds. Reconciliation logic that assumes same-day settlement will pass here and fail in production.
- Fraud rules Your live rule set is tuned against your own traffic. Sandbox scoring is deterministic and will not surprise you the way a real ruleset can.
Take one real payment of a small amount on the live account, from a real card, and refund it. It costs a few cents in fees and it is the only test that exercises the whole chain — underwriting, routing, the live issuer, settlement and your reconciliation — at once. The go-live checklist puts it in sequence with everything else.
Keep reading
Stuck on this page?
Integration questions reach an engineer, not a queue. Send the request id and the response body — that is usually enough to answer in one reply.