Testing and Sandbox Scenarios
Simulate approvals, declines, webhooks, refunds, and payouts before going live.
Overview
Watenga's sandbox environment is completely isolated from live payments. Sandbox transactions never move real money and do not appear in live reports. Use the same API host as production with test keys — there is no separate sandbox subdomain.
API base URL: https://api.watenga.africa
Authentication: Authorization: Bearer sk_test_… (or rk_test_… for restricted keys)
Create a sandbox API app at: https://dashboard.watenga.africa/developer → New App → Sandbox
Test Cards
| Card Number | Brand | Scenario | Expiry | CVV |
|---|---|---|---|---|
| 4111 1111 1111 1111 | Visa | Successful payment | 12/26 | 123 |
| 4000 0000 0000 0002 | Visa | Generic decline | 12/26 | 123 |
| 4000 0000 0000 9995 | Visa | Insufficient funds | 12/26 | 123 |
| 4000 0000 0000 0069 | Visa | Expired card | 12/26 | 123 |
| 4000 0000 0000 0127 | Visa | Incorrect CVV | 12/26 | 999 |
| 4000 0000 0000 0119 | Visa | Gateway timeout (slow) | 12/26 | 123 |
| 5200 0000 0000 0007 | Mastercard | Successful payment | 12/26 | 123 |
| 5200 0000 0000 0114 | Mastercard | Generic decline | 12/26 | 123 |
| 6011 0000 0000 0004 | Zimswitch | Successful payment | 12/26 | 123 |
| 6011 0000 0000 0012 | Zimswitch | Declined — card blocked | 12/26 | 123 |
Use any name, billing address, and expiry in the future. Only the card number determines the test scenario.
Test Mobile Money Numbers
| Phone Number | Wallet | Scenario | Timing |
|---|---|---|---|
| 0771111111 | EcoCash | Immediate success | Instant |
| 0772222222 | EcoCash | Delayed success (simulates slow PIN entry) | 30 seconds |
| 0773333333 | EcoCash | Customer cancelled | 30 seconds |
| 0774444444 | EcoCash | Insufficient balance (fails immediately) | Instant |
| 0775555555 | EcoCash | Wrong PIN entered 3 times (fails) | 45 seconds |
| 0776666666 | EcoCash | Network timeout (never responds) | 120 seconds |
Use any of these numbers when testing EcoCash payments. The number simulates the customer's behaviour — you don't need a real phone to test.
Triggering Specific Error Codes
VALIDATION_ERROR — send amount: 0 or a negative number.
Expected response: 400 { code: 'INVALID_AMOUNT' }
PHONE_REQUIRED — send walletType: 'ecocash' without customerPhone.
Expected: 400 { code: 'PHONE_REQUIRED' }
DUPLICATE_REQUEST — send two checkout requests with the same merchantTransactionId.
Second request returns: 409 { code: 'DUPLICATE_REQUEST' }
RATE_LIMIT_EXCEEDED — send more than 60 requests in 60 seconds.
Expected: 429 { code: 'RATE_LIMIT_EXCEEDED' } — the Retry-After header shows seconds to wait.
KYC_REQUIRED — use a sandbox app whose merchant account has kyc_tier = 0.
Expected: 403 { code: 'KYC_REQUIRED' }
To bypass in sandbox: contact support to set your sandbox merchant to approved KYC.
Testing Webhooks Locally
Watenga webhooks are sent from our servers to your notificationUrl. In local development, your server is not publicly accessible. Use one of these tools to expose your local server:
Option 1 — ngrok (recommended)
# Install ngrok from ngrok.com
ngrok http 3000
# ngrok gives you a public URL like:
# https://abc123.ngrok.io
# Set your webhook URL in the Developer Dashboard to:
# https://abc123.ngrok.io/webhooks/watengaOption 2 — Cloudflare Tunnel (free, no account needed)
npx cloudflared tunnel --url http://localhost:3000
# Gives you a permanent-ish URL for the sessionNode.js webhook test script:
// test-webhook.js — simulate a signed webhook event locally
const crypto = require('crypto')
const secret = 'your_webhook_secret_from_dashboard'
// Canonical webhook envelope
const payload = JSON.stringify({
id: 'evt_test_001',
object: 'event',
type: 'payment.capture.completed',
created: Math.floor(Date.now() / 1000),
data: {
id: 'txn_test_001',
amount: '25.0000',
net: '24.1300',
currency: 'USD',
status: 'completed',
},
})
// Watenga-Signature: t=<unix>,v1=HMAC_SHA256(secret, `${t}.${rawBody}`)
const t = Math.floor(Date.now() / 1000)
const v1 = crypto
.createHmac('sha256', secret)
.update(`${t}.${payload}`, 'utf8')
.digest('hex')
fetch('http://localhost:3000/webhooks/watenga', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Watenga-Signature': `t=${t},v1=${v1}`,
},
body: payload,
}).then(r => r.json()).then(console.log)
// Run: node test-webhook.jsTesting Webhook Retries
Watenga retries failed webhooks up to 5 times with exponential backoff. To test retry behaviour in sandbox:
- Set your webhook URL to a URL that returns a non-200 status (e.g.
httpstat.us/500) - Trigger a
payment.capture.completedevent by completing a sandbox payment - Check the Webhook Events log in Developer Dashboard
- See the delivery attempts with timestamps and response codes
- Update your webhook URL to a working endpoint
- Use the "Resend" button in the Webhook Events log to replay the event
Testing Refunds
To test a refund:
- Complete a sandbox payment using test card 4111 1111 1111 1111
- Note the transaction ID from the response or from GET /v1/transactions
- Call
POST /v1/transactions/{id}/refund - Verify the transaction status changes to
reversed - Check that the merchant balance is reduced by the refunded amount
curl -X POST https://api.watenga.africa/v1/transactions/txn_01HQXYZ/refund \
-H "Authorization: Bearer sk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"reason": "Customer request"}'Testing Payouts
Sandbox payouts do not actually transfer money. To test a payout:
- Ensure your sandbox merchant has an available balance (complete a few test payments first)
- Call POST /v1/payouts/request with
method: 'bank_transfer' - In sandbox, payouts auto-complete after 5 minutes
- Check that
available_balancedecreased and apayment.payout-item.succeededwebhook fires
Sandbox Reset
Daily reset
Going Live Checklist
Before switching from sandbox to live:
- All test scenarios pass — approvals, declines, refunds, webhooks
- Webhook signature verification implemented and tested
- Idempotency keys used on all payment creation requests
- Error handling covers all error codes in the Errors reference
- Duplicate payment prevention tested (same merchantTransactionId twice)
- Mobile money polling implemented with timeout and cancel handling
- KYC approved in merchant dashboard (check Status in dashboard sidebar)
- Live API app created in Developer Dashboard (not sandbox)
- Live keys saved securely (never in code — use environment variables)
- Webhook URL updated to production URL in live app settings
- Test with a real small transaction ($1.00) before going fully live
See also: Error codes, Webhooks, API Versioning.
Get notified when InnBucks and OneMoney go live.
