Test Mode
Build and verify your whole integration without moving real money. Test mode uses the same endpoints, the same SDKs and the same webhooks as live. Payments are completed by a simulator instead of a bank.
Test keys and live keys
| Test | Live | |
|---|---|---|
| Secret key | sk_test_... | sk_live_... |
| Public key | pk_test_... | pk_live_... |
| Payments | Simulated | Real |
| Settlement and payouts | Never | T+1 |
| Available before verification | Yes | No |
The key decides the mode. Everything a test key creates (sessions, transactions, invoices, webhook events) is test data, and stays test data: a test object can never be paid with real money, settled or paid out, and a live object can never be reached with a test key.
Getting test keys
- Sign in to the Dashboard.
- Turn on Test mode in the top bar.
- Go to Developers → API Keys and choose Create test key. A test key needs only a name: no settlement account, no domain restrictions.
You can create test keys while your business verification is still in review. The dashboard opens in test mode until you are approved, so you can finish your integration while you wait.
Simulating a payment
Test sessions offer bank transfer, which is what the simulator completes today. The customer sees an account at ZevPay Test Bank. Nothing can be sent to it; you complete the payment with the simulator instead, in whichever way suits your test:
- On the checkout page, the inline modal or a test invoice's payment page, the pay button reads Simulate successful transfer. Every screen of a test session or test invoice carries a TEST MODE ribbon.
- From the dashboard, open the transaction and choose Simulate transfer.
- From your server, call the API with your test secret key. The customer must have chosen bank transfer first.
curl -X POST https://api.zevpaycheckout.com/v1/checkout/session/{session_id}/simulate-payment \
-H "Authorization: Bearer sk_test_..."{
"success": true,
"data": {
"session_id": "4e466b30-d598-498d-8541-453a4017db7b",
"reference": "ZVP-CKO-S-mvnyed41-0c0c4fc0a41e",
"status": "completed",
"amount": 250000,
"fees": 3750,
"currency": "NGN",
"livemode": false
}
}A simulated payment behaves like a real one from your integration's point of view:
- your account's real fee schedule is applied, so amounts, fees and net match what you will see live;
- the session completes, so verify returns
completed; charge.successis sent to your test key's webhook URL, signed with its webhook secret;- an invoice payment is credited to the invoice.
Simulating twice is refused, the same way a session can only be paid once.
Telling test from live in your code
Every response that describes a session, and every webhook, carries livemode:
{
"event": "charge.success",
"livemode": false,
"data": { "reference": "ZVP-CKO-S-abc123", "amount": 250000, "currency": "NGN", "status": "completed" }
}livemode is set from the key that created the object, never from anything in the request. Use it to keep test orders out of your fulfilment: fulfil only when livemode is true.
Give each test key its own webhook URL (it is set per key, like on live keys), so test events reach your development endpoint and never your production one. Webhook URLs must be reachable from the internet: localhost and private network addresses are refused. To receive test webhooks on your machine, use a tunnel such as ngrok.
What test keys cannot do
Test mode exists to exercise your integration, so anything that would touch real money or a real person is refused with the error code TEST_MODE_BOUNDARY:
| Action | In test mode |
|---|---|
| Card, ZevPay ID, PalmPay, mobile money, crypto | Not offered yet; sessions show only the methods the simulator can complete |
| Creating PayIDs (static or dynamic) | Refused: a PayID can be paid by a real ZevPay user |
| Static NGN accounts | Refused |
| Refunds | Refused: a test payment took no money in |
| Settlement destinations, transfer wallets, payouts | Not applicable to test keys |
Methods that are enabled per account, such as mobile money or crypto, appear in test mode only for accounts that have them enabled live. Test mode never widens what your account can use.
The dashboard in test mode
With Test mode on, the dashboard shows test data only: transactions, customers, invoices, webhook logs and API keys. Totals never mix the two, so a test payment can never appear in your live revenue. Switch back with Switch to live in the banner or the top bar.
Test mode emails reach you, marked [TEST]. Nothing is sent to the customer addresses used in test sessions or invoices, since those are often real people's.
Teammates who only need test mode
When you invite a teammate, you can give them Test mode only access. They can create and use test keys, simulate payments and read test webhooks, but they never see live payments, customers or settings, and cannot move money. This is enforced on our side for every request, not just hidden in the dashboard, so it is safe for a contractor building your integration.
Errors you may see
| Code | Meaning |
|---|---|
TEST_MODE_BOUNDARY | The action is not available in test mode, or a test key reached for a live object (or the reverse) |
TEST_ONLY_ACCESS | Your teammate access is limited to test mode |
If your test keys stop working with Test mode has been switched off for this account, contact support.
Going live
- Turn off Test mode in the dashboard and create live keys with your settlement account.
- Swap
sk_test_/pk_test_forsk_live_/pk_live_. Nothing else in your code changes. - Set your production webhook URL on the live key.
- Confirm your handler fulfils only when
livemodeistrue, verifies signatures, and is idempotent onreference.
TIP
Make your first live payment a small one and confirm it end to end, webhook included, before opening checkout to everyone.