Why a dedicated sandbox changes everything
By 2026, the Senegalese payment ecosystem has matured and Wave Business + Orange Money now offer serious test environments (sandboxes). Before 2024, developers tested against real merchant accounts with tiny real amounts — tedious, expensive, and impossible to test failure modes.
Today, an e-commerce dev integrating Wave + OM must start with the sandbox. Benefits:
- Fast iteration (zero fees, zero real transaction friction).
- Exhaustive error-case testing (timeout, declined, fraud, 3DS).
- Security (no production API keys on the dev machine).
- CI/CD automatable.
This guide covers the full setup: credentials, test numbers, scenarios to cover, webhook mocking, HMAC signature testing. Audience: back-end and full-stack devs integrating payments for the first time.
H2: Wave Business sandbox environment setup
Step 1 — Developer account. Create an account on developer.wave.com (distinct from the prod merchant account). Enable sandbox mode in the dashboard. You receive:
WAVE_API_KEY_SANDBOX(format:wave_sandbox_sk_...)WAVE_WEBHOOK_SECRET_SANDBOX(format:whsec_sandbox_...)- Sandbox endpoint:
https://api.sandbox.wave.com/v1/
Step 2 — Env var configuration. .env.local file:
`
WAVE_API_KEY=wave_sandbox_sk_xxxxxxxxxxxxx
WAVE_WEBHOOK_SECRET=whsec_sandbox_xxxxxxxxxxxxx
WAVE_API_BASE_URL=https://api.sandbox.wave.com/v1
`
Step 3 — Tunneling for local webhooks. Wave needs a public HTTPS endpoint to deliver webhooks. In local dev, use ngrok, localtunnel or cloudflared:
`bash
ngrok http 3000
# → https://abcd-1234.ngrok-free.app
`
Configure this URL in the sandbox dashboard under Webhooks → Endpoint URL.
Step 4 — Wave test phone numbers. Wave sandbox provides these virtual numbers:
| Number | Behavior |
|---|---|
+221700000001 | Payment immediately accepted |
+221700000002 | Payment declined (insufficient funds) |
+221700000003 | Payment timeout (no customer response after 5 min) |
+221700000004 | Accepted then auto-refunded after 60s |
+221700000099 | Simulated API 500 error |
H2: Orange Money sandbox environment setup
OM Business exposes its sandbox via developer.orange.com (Orange Developer Africa program).
Step 1 — Registration. Free account on developer.orange.com → subscribe to Orange Money Web Payment or Orange Money Mobile Money API product. Manual approval 2-5 business days.
Step 2 — Credential retrieval.
OM_CLIENT_IDandOM_CLIENT_SECRET(OAuth2 auth)OM_MERCHANT_KEY_SANDBOX- Sandbox endpoint:
https://api.sandbox.orange-sonatel.com/
Step 3 — OM test numbers.
| Number | Behavior |
|---|---|
+221770000001 | Payment accepted |
+221770000002 | Insufficient funds |
+221770000003 | Wrong PIN x3 → account locked |
+221770000004 | Customer timeout |
+221770000005 | Acceptance then customer cancellation |
Step 4 — OAuth2 flow test. OM requires an OAuth2 token generated per session:
`bash
curl -X POST https://api.sandbox.orange-sonatel.com/oauth/token \
-u $OM_CLIENT_ID:$OM_CLIENT_SECRET \
-d "grant_type=client_credentials"
`
Token valid 1h, refresh required.
H2: Must-have test scenarios
1. Nominal accepted payment
Create an order, launch checkout, pay with +221700000001 (Wave) or +221770000001 (OM). payment.completed webhook must arrive < 30s. Check order status paid.
2. Declined payment (insufficient funds)
Number +221700000002 (Wave). payment.failed webhook with error_code: insufficient_funds. Order should remain pending, not permanently failed (customer can retry with another wallet).
3. Customer timeout
Number +221700000003. No webhook should arrive within 5-10 min. Your back-end must have a cron job that queries Wave GET /payments/:id for pending payments older than 10 min and reconciles.
4. Webhook delivered twice (idempotency)
Need a professional website?
Kolonell builds websites that attract clients, optimized for the Sénégalese market. Free quote in 2 minutes.
Trigger Webhook → Test → Replay in the dashboard. Your endpoint must return already_processed: true on the second call. No double-debit, no double-fulfillment.
5. Invalid HMAC signature
Manually tamper the signature sending a custom payload via curl. Your endpoint must return 401 invalid_signature. Log the incident with source IP.
6. 3DS (if card added)
If Wave Card or OM Card is integrated, test with a 3DS challenge test card: 4000 0027 6000 3184 (Visa 3DS Required). Verify the redirect challenge → callback → webhook flow works.
7. Partial refund
POST /refunds API with amount < original. Webhook refund.created then refund.completed. Verify partial accounting.
8. Order-creation / webhook race condition
Trigger the scenario: pay BEFORE the order is inserted in DB (delay the insert with a dev sleep). The webhook arrives, your endpoint can't find the order. Expected behavior: create the order in pending BEFORE the checkout API call, never after.
H2: Mocking in CI/CD
For automated tests (Jest, Vitest, Pytest), don't call the sandbox on every CI run (rate-limited to ~1000 req/h). Use a local mock:
`ts
// __mocks__/wave-client.ts
export const waveClient = {
createPayment: jest.fn().mockResolvedValue({
id: 'pay_test_01HZK7Y',
status: 'pending',
checkout_url: 'https://sandbox.wave.com/checkout/pay_test_01HZK7Y',
}),
getPayment: jest.fn(),
};
`
To test your webhook handler end-to-end, manually generate a signed payload:
`ts
const payload = JSON.stringify({ id: 'evt_test', type: 'payment.completed', data: {...} });
const ts = Math.floor(Date.now() / 1000);
const sig = crypto.createHmac('sha256', secret).update(${ts}.${payload}).digest('hex');
// POST with header Wave-Signature: t=${ts},v1=${sig}
`
H2: Common sandbox mistakes
- Mixing up sandbox and prod keys (the
_sandbox_prefix helps, but verify in CI:assertSandboxKey()). - Forgetting to restart ngrok and keeping a stale URL in the webhook dashboard (webhooks arrive in the void).
- Testing with a real number — not in sandbox, real fees incurred.
- Not testing failure scenarios — you discover bugs in production.
- Skipping signature verification in dev "because it's sandbox" — you ship untested code.
FAQ
How long until Wave/OM activate the sandbox?
Wave: instant after developer account creation (10 min). OM: manual approval 2-5 business days via Orange Developer Africa. Plan ahead if you have a deadline.
Can I test without a tunnel (ngrok)?
Yes: use Test webhook in the Wave/OM dashboard which manually POSTs a signed payload to a URL of your choice. But for a realistic end-to-end test, ngrok remains essential locally.
Sandbox vs prod API differences?
Endpoints identical (/payments, /refunds, /customers), only the base URL changes. A few restrictions: no real customer KYC in sandbox, no real funds, stricter rate-limits (1000 req/h vs 10000 req/h prod).
How to share the sandbox with a dev team?
Wave/OM developer account shared via password (use a vault like 1Password Teams). Every dev uses the same sandbox credentials. In prod: single merchant account, secrets in a vault (AWS Secrets Manager, Doppler, Infisical).
Does sandbox support multi-currency payments?
Wave sandbox: FCFA only (consistent with prod). OM sandbox: FCFA + EUR for diaspora flow testing. For real multi-currency (USD, XAF, NGN), go through Stripe or an aggregator (cf the Wave vs Stripe article in this batch).
Let's discuss your integration
If you are kicking off a Wave + OM integration on an e-commerce project and want to save 2-3 weeks by setting up a clean sandbox from day one, we can deliver the full boilerplate (Next.js + Prisma + HMAC signature + tests). WhatsApp +221 77 596 93 33.
Mohamed Bah
Fondateur, Kolonell
Passionate about digital and entrepreneurship in Africa, Mohamed has been helping Sénégalese businesses with their digital transformation since 2020. Founder of Kolonell, he believes every SME deserves a professional and accessible online présence.

