Accept PaymentsSandbox Test Mode

Sandbox Test Mode

SaySwitch collection endpoints use sandbox behavior when you authenticate with a test secret key:

Authorization: Bearer sk_test_...

Sandbox requests still pass normal request validation. Send every field required by the payment channel, including a valid encrypted card payload for S2S card payments.

Force a Sandbox Status

Supported collection requests accept an optional status field:

FieldTypeAllowed valuesRequired
statusstringsuccess, failed, pendingNo
  • If status is omitted, the channel’s normal sandbox simulator decides the result. For card payments, an approved simulator card succeeds and an unrecognized card is declined.
  • If status is success, failed, or pending, the requested outcome is applied only in the test environment.
  • An invalid non-empty status value is rejected by request validation with HTTP 400; it is not silently ignored.
  • A valid status value has no forcing effect in live mode. The live payment route processes the transaction normally.

Supported Endpoints

Use v1 for new S2S integrations.

ChannelCurrent endpoint
CardPOST /api/s2s/v1/transaction/initialize
Bank TransferPOST /api/s2s/v1/banktransfer/initialize
Bank Transfer with WalletPOST /api/s2s/v1/banktransfer/initialize/wallet
USSDPOST /api/s2s/v1/ussd/initialize
USSD with WalletPOST /api/s2s/v1/ussd/initialize/wallet
Mobile MoneyPOST /api/s2s/v1/momo/payment_request

The legacy /api/s2s/... collection routes remain available for existing integrations. Do not mix legacy and v1 endpoints within one payment.

Status Behavior

success

The sandbox transaction is marked successful through the normal success-processing event and a successful-payment webhook is sent to your test webhook URL.

failed

The transaction is marked failed and a failed-payment webhook is sent to your test webhook URL.

For the S2S card simulator, the initialize response is:

{
  "status": false,
  "message": "Test payment declined (simulated)"
}

pending

The transaction is created with pending status and no immediate success webhook is sent. A queued job schedules it to resolve successfully after the configured sandbox delay, which defaults to about 20 seconds.

The job only resolves a transaction that is still in the test environment and still pending when it runs. Queue-processing delays or a later status change can affect when or whether that transition occurs, so verify the transaction rather than relying on a timer.

The S2S card initialize response is:

{
  "status": true,
  "message": "Payment Processing"
}

Built-In S2S Sandbox Cards

Use the normal NGN cards listed on Sandbox Test Cards. Recurring-payment cards use a separate hosted checkout flow and must not be used with the S2S card endpoint.

Card Example

Card details must be encrypted even when you force a status.

1. Encrypt the Sandbox Card

curl --request POST \
  --url https://backendapi.sayswitchgroup.com/api/s2s/v1/test/encryption \
  --header "Authorization: Bearer YOUR_TEST_SECRET_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "data": {
      "number": "4005555555000009",
      "expiryMonth": "05",
      "expiryYear": "37",
      "cvv": "000"
    },
    "reference": "SANDBOX_CARD_2026_0001"
  }'

The response body is the encrypted hexadecimal value directly. It is not a JSON object.

2. Initialize With the Same Reference

curl --request POST \
  --url https://backendapi.sayswitchgroup.com/api/s2s/v1/transaction/initialize \
  --header "Authorization: Bearer YOUR_TEST_SECRET_KEY" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --data '{
    "email": "customer@example.com",
    "amount": "1000.00",
    "currency": "NGN",
    "reference": "SANDBOX_CARD_2026_0001",
    "card": "ENCRYPTED_CARD_HEX_STRING",
    "status": "failed"
  }'

The exact same reference must be used for encryption and initialization. The initialize controller decrypts and validates the card before applying the forced result.

Bank Transfer Example

curl --request POST \
  --url https://backendapi.sayswitchgroup.com/api/s2s/v1/banktransfer/initialize \
  --header "Authorization: Bearer YOUR_TEST_SECRET_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "email": "customer@example.com",
    "amount": "1000.00",
    "currency": "NGN",
    "reference": "SANDBOX_BANK_2026_0002",
    "status": "pending"
  }'

The virtual-account response remains the normal channel response. The forced status controls the later sandbox outcome and webhook behavior.

Mobile Money Example

curl --request POST \
  --url https://backendapi.sayswitchgroup.com/api/s2s/v1/momo/payment_request \
  --header "Authorization: Bearer YOUR_TEST_SECRET_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "amount": "1000.00",
    "currency": "KES",
    "bankName": "Mpesa",
    "phone": "0722123456",
    "description": "Sandbox status test",
    "reference": "SANDBOX_MOMO_2026_0003",
    "status": "failed"
  }'

Invalid Status Response

An unsupported status value fails controller validation with HTTP 400:

{
  "status": false,
  "message": "The selected status is invalid.",
  "error": {
    "status": [
      "The selected status is invalid."
    ]
  }
}

The exact message can include other validation errors when more than one request field is invalid.

Notes

  • Sandbox webhooks use your test webhook configuration.
  • The status field is a testing convenience and cannot force a live transaction result.
  • Continue to verify the final transaction status through the matching verification endpoint.
  • Never use real card details in sandbox requests.