Test with the sandbox
Soxara’s sandbox lives at the same URL as production. The prefix on your API key —
sxm_test_* vs sxm_live_* — routes you into one or the other. See
Environments for the full mental model; this page is the practical “how
do I actually generate a test payment.”
The one real lever: simulate
There’s no matrix of magic test card numbers or MSISDNs today. Instead, a test-key payment link
can be settled directly through
POST /v1/api/payment-links/{id}/simulate — it runs
a test link only (a live link 404s) through the exact same settlement fan-out production uses,
so you get a real payment.completed/payment.failed webhook without touching a card network or
MoMo at all:
# 1. Create a test link
curl -X POST "$SOXARA_BASE/v1/api/payment-links" \
-H "Authorization: Bearer $SOXARA_TEST_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Test order", "amount": 1250, "currency": "USD"}'
# → { "data": { "id": "…", "link_code": "PL-TESTXXXX", ... } }
# 2. Settle it however you want to exercise
curl -X POST "$SOXARA_BASE/v1/api/payment-links/{id}/simulate" \
-H "Authorization: Bearer $SOXARA_TEST_KEY" \
-H "Content-Type: application/json" \
-d '{"method": "momo", "outcome": "completed"}'method is card, momo, or wallet; outcome is completed or failed. For a pay-any-amount
link, pass amount (minor units) too. This is the path to exercise both the happy path and the
failure path in CI without any hosted-checkout browser step.
Recurring billing (mandates)
Test-key mandates activate immediately — POST /v1/api/mandates with a test key skips the
customer approval step (card save / wallet PIN) that a live mandate requires, so you can start
charging right away. On a test mandate, POST /v1/api/charges also takes an "outcome": "completed"|"failed" field to force the result, the same idea as simulate. See
Recurring billing.
Sub-merchants and payroll
Sub-merchant provisioning (/v1/api/sub-merchants/*) and payroll
(/v1/api/payroll/*) run their normal flow in test mode too — the OTP
round-trip and the PIN-gated approval both still happen, just against test accounts/wallets rather
than real ones. There’s no shortcut that skips either step, in test mode or live.
Gift cards and fleet cards
Redemption (/v1/gift-cards/redeem,
/v1/fleet-cards/redeem) needs a real test gift card or fleet card
issued against your test merchant first (from the dashboard) — there’s no synthetic “any id
redeems successfully” shortcut. Use the fleet-card error codes
(FLEET_CARD_INACTIVE/CATEGORY_NOT_ALLOWED/INSUFFICIENT_BALANCE/SPEND_LIMIT_EXCEEDED) to
exercise your handling of each rejection by setting up a test card in the matching state.
Rate limits
Sandbox and live traffic share the same rate-limiting middleware and budgets — there’s no separate,
more generous sandbox limit. If your test suite hits 429 RATE_LIMITED running back-to-back, add
backoff in the suite itself rather than assuming it won’t happen live.
Webhooks in sandbox
Register a separate webhook endpoint for your test key — it gets its own signing secret, independent of your live endpoint’s. Don’t reuse a secret across environments; verification will fail. See Testing webhooks for tunneling a local handler and redelivering a failed test delivery.
Test-mode payment payloads carry "environment": "test" inside data.payment — check that instead
of a top-level livemode field, which doesn’t exist in the payload.
What sandbox doesn’t simulate
- Real settlement timing.
simulateresolves instantly; a live card or MoMo payment settles on its own schedule. - Real fraud/KYC screening. Test accounts and test payment methods don’t go through the checks a live customer or merchant does.
- Real network failures. Sandbox is more reliable than production. Don’t assume your retry
logic is sound just because sandbox tests pass — the real value of
simulate’s"outcome": "failed"is exercising your failure-handling code path deliberately, not standing in for chaos testing.