Products · Sandbox

Sandbox + Simulator

Twelve named failure scenarios per provider. Predictable, reproducible test cases — no real wallets, no real money.

12 scenariossk_test_

Getting started

Use API keys starting with sk_test_. All data is fully isolated per sandbox environment — no real money moves and no provider credentials are required. Sandbox keys are issued automatically when you create a merchant account; find them under Settings → API Keys.

How sandbox routing works

Requests authenticated with a sk_test_ key are intercepted by the mock provider adapter before reaching any real payment rail. The mock adapter determines the scenario to simulate by reading one of two inputs:

  • MSISDN triggerSet customer_msisdn to +25300000000X where X is a digit from 1 to 12 corresponding to the scenario index in the table below.
  • Header triggerSet X-Meras-Scenario: <scenario_name> on the create request. The header takes precedence over the MSISDN trigger when both are present.

Scenario reference

IndexScenario nameProvidersOutcome
1successallPaymentIntent transitions to succeeded.
2insufficient_fundswaafi · dmoneyFails with code insufficient_funds.
3otp_expiredwaafi · dmoneynext_action.type collect_otp returned; confirming with any OTP fails with otp_expired.
4card_declinedcybersource_cnpFails with decline_code generic_decline.
5card_3ds_requiredcybersource_cnpForces the 3DS authentication flow — next_action.type redirect_to_url.
6network_timeoutallSimulates a provider timeout: 30 s delay then an error response.
7duplicate_msisdnwaafiSimulates a locked wallet error.
8kyc_blockedallSimulates an AML/KYC compliance hold — intent transitions to compliance_review.
9account_closedcac · eabAccount not found error returned by the bank adapter.
10velocity_limitdmoneyDaily velocity limit exceeded error.
11partial_capturecacCaptures 80% of the requested amount; remainder left uncaptured.
12sandbox_webhook_failallWebhook delivery is marked failed to allow testing of your retry-handling logic.

Example — trigger a scenario via header

insufficient_funds via headerbash
curl -X POST https://api.merashub.com/v1/payment_intents \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: test_order_001" \
  -H "Content-Type: application/json" \
  -H "X-Meras-Scenario: insufficient_funds" \
  -d '{
    "amount": 25000,
    "currency": "DJF",
    "provider": "waafi",
    "customer_msisdn": "+25377000099",
    "description": "Test — insufficient funds scenario"
  }'
402 — insufficient_fundsjson
{
  "id": "pi_01HZGV2KNRW5X8YZ3AE6BL9QF",
  "status": "canceled",
  "last_error": {
    "code": "insufficient_funds",
    "message": "The customer's wallet does not have sufficient funds for this transaction.",
    "provider": "waafi"
  },
  "amount_minor": 25000,
  "currency": "DJF",
  "created_at": "2026-05-25T14:00:00Z"
}
success via MSISDN triggerbash
curl -X POST https://api.merashub.com/v1/payment_intents \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: test_order_002" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 10000,
    "currency": "DJF",
    "provider": "waafi",
    "customer_msisdn": "+253000000001",
    "description": "Test — success scenario (index 1)"
  }'
201 — succeeded immediatelyjson
{
  "id": "pi_01HZGW3LNSX6Y9ZA4BE7CM0RG",
  "status": "succeeded",
  "amount_minor": 10000,
  "currency": "DJF",
  "provider": "waafi",
  "succeeded_at": "2026-05-25T14:01:00Z"
}

Sandbox utility endpoints

POST/v1/sandbox/overrides

Preload scenario state for a specific payment_intent_id — useful for webhook testing without re-creating the full flow.

POST/v1/sandbox/webhooks/trigger

Manually fire any event type to your registered webhook endpoint.

Sandbox override parameters

NameTypeDescription
payment_intent_id
required
pi_The intent whose outcome you want to preload.
scenario
required
stringScenario name from the table above. The next action on this intent will follow this scenario.

Webhook trigger parameters

NameTypeDescription
event_type
required
stringAny valid event type, e.g. payment_intent.succeeded, payout.failed, invoice.paid.
related_object_idstringOptional ID of the object to embed in the event payload. If omitted, a synthetic fixture is generated.

Preloading scenario state

Use POST /v1/sandbox/overrides to preload scenario state for a specific payment_intent_id. This is useful for testing your webhook delivery handler without needing to re-create the full payment flow — create the intent once, then trigger different outcomes by updating the override.

Testing webhook delivery

POST /v1/sandbox/webhooks/trigger fires any event type to your registered endpoint immediately. Use this to verify your HMAC signature verification, retry logic, and idempotency handling without having to complete a real payment flow each time.

Sandbox data retention

Sandbox data is purged every 30 days. Do not store sandbox object IDs (pi_, cus_, sub_, etc.) in production systems or use them as references in live flows. Sandbox and live environments are completely isolated at the database level.