Sandbox vs Live
How the two environments differ, how to keep them isolated, and what changes when you flip your code from test mode to production.
Every MintCash merchant runs in two parallel worlds: sandbox and live. They share zero state. Different API endpoints, different keys, different webhook destinations, different ledgers.
At a glance
| Property | Sandbox | Live |
|---|---|---|
| Base URL | https://sandbox.mintcash.me | https://api.mintcash.me |
| API key prefix | pk_test_… / sk_test_… | pk_live_… / sk_live_… |
| Webhook environment | "test" | "live" |
| Cards charged | Reusable test numbers only | Real cards |
| Money movement | None — no settlement | Real settlement to your accounts |
| Provider behaviour | Deterministic by test card | Provider's actual decision |
| IP allow lists | Off by default | Recommend on |
| Rate limits | Generous | Per-merchant configured |
The webhook envelope tells you which one
Every webhook MintCash sends includes an environment field. Use it as a defensive check so a misconfigured test webhook can never trigger live-side fulfilment:
if (event.environment !== process.env.MINTCASH_ENV) {
return Response.json({ error: "wrong environment" }, { status: 400 });
}Don't share keys across environments
A live secret key pasted into a staging server is the kind of mistake that becomes a chargeback. Treat the two key pairs like database credentials for prod vs. test — they should never sit next to each other in config.
What test cards can do for you
In sandbox you have a small set of test cards that deterministically produce success, 3DS challenges, or specific failure conditions. The same card always behaves the same way, so you can exercise every branch of your code, and a specific failure resolution can be requested by varying the expiry.
See the test cards reference for the full grid of card numbers, expiries, and outcomes.
What you should test before going live
Run all of these against sandbox, in this order:
- Happy-path one-time payment — HPP flow ends in
payment.succeededand you fulfil correctly. - Decline handling — your code does the right thing on
payment.failed. Refund/return logic is exercised. - Webhook signature verification — you reject unsigned and tampered webhooks with 401.
- Duplicate webhook delivery — your handler is idempotent on
eventId. - Subscription create + retry — first invoice, then a deliberately-failing retry, then
subscription.cancelledafter retries exhaust. - Refund — full and partial refund flows; both
payment.refundedandpayment.partially_refundedreach you. - Wrong-environment webhook rejection — your endpoint refuses webhooks tagged with the wrong
environment.
When all eight pass, swap keys and run the same shape of test against your live integration with a real card you control. After that, you're production-ready.