Recurring PaymentsSandbox Testing

Sandbox Testing

Use the sandbox to test the complete recurring-payment flow without creating live charges.

Use the hosted-checkout card details listed under Recurring-payment sandbox cards. These cards are separate from the normal NGN S2S sandbox cards.

Test Checklist

  1. Use your SaySwitch test secret key.
  2. Create a test customer through the Customers API.
  3. Create an NGN recurring plan.
  4. Initialize a subscription with a unique Idempotency-Key.
  5. Open the returned authorization_url in a browser.
  6. Complete the hosted card authorization and recurring-payment consent.
  7. Retrieve the subscription and confirm that its status becomes active.
  8. Confirm that your test webhook endpoint receives the relevant lifecycle event.
  9. Cancel the subscription and confirm the cancellation response and webhook.

Temporary certification note: Certification of some unattended recurring sandbox scenarios is still in progress. If a scheduled off-session recurring-token charge requests additional customer authentication, the charge attempt is recorded as failed and the subscription may move to past_due.

1. Create a Test Customer

curl --request POST \
  --url https://backendapi.sayswitchgroup.com/api/v1/customer \
  --header "Authorization: Bearer YOUR_TEST_SECRET_KEY" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --data '{
    "email": "recurring-test@example.com",
    "first_name": "Ada",
    "last_name": "Okafor",
    "phone": "2348012345678"
  }'

Save the returned customer_code.

2. Create an NGN Plan

Use daily only when you need the shortest publicly supported interval. The API does not provide an accelerated minute-based test interval.

curl --request POST \
  --url https://backendapi.sayswitchgroup.com/api/v1/plan \
  --header "Authorization: Bearer YOUR_TEST_SECRET_KEY" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "Daily Sandbox Access",
    "description": "Daily plan for recurring-payment testing",
    "amount": 100,
    "currency": "NGN",
    "interval": "daily",
    "invoice_limit": 2
  }'

Save the returned plan_code.

3. Initialize a Subscription

Use a new idempotency key for each subscription you intentionally create.

curl --request POST \
  --url https://backendapi.sayswitchgroup.com/api/v1/subscription \
  --header "Authorization: Bearer YOUR_TEST_SECRET_KEY" \
  --header "Idempotency-Key: sandbox-recurring-001" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --data '{
    "customer": "CUS_customer_code",
    "plan_code": "PLN_plan_code",
    "return_url": "https://merchant.example/subscription/complete",
    "metadata": {
      "test_case": "sandbox-recurring-001"
    }
  }'

Open the returned data.authorization_url and complete the hosted authorization flow using supported SaySwitch sandbox card details. Never place real card details in a sandbox request or in your server logs.

4. Retrieve the Subscription

curl --request GET \
  --url https://backendapi.sayswitchgroup.com/api/v1/subscription/SUB_subscription_code \
  --header "Authorization: Bearer YOUR_TEST_SECRET_KEY" \
  --header "Accept: application/json"

Confirm the subscription is active. Depending on the start date and initial charge result, inspect its next_charge_at, invoices, and attempts.

The return-page query parameters are not final confirmation. Use this API response and your signed webhook events to decide whether the subscription is active.

5. Observe Webhooks

Your test webhook URL can receive events such as:

  • subscription.initialized
  • subscription.activated
  • subscription.authentication_required
  • subscription.charge_succeeded
  • subscription.charge_failed
  • subscription.completed
  • subscription.cancelled

Verify each request using the test secret and public keys as described in Recurring Payment Webhooks.

6. Test Cancellation

curl --request POST \
  --url https://backendapi.sayswitchgroup.com/api/v1/subscription/SUB_subscription_code/cancel \
  --header "Authorization: Bearer YOUR_TEST_SECRET_KEY" \
  --header "Accept: application/json"

Confirm that the response status is cancelled, next_charge_at is null, and your webhook endpoint receives subscription.cancelled.

Troubleshooting

IssueWhat to check
HTTP 401Confirm you are sending Authorization: Bearer YOUR_TEST_SECRET_KEY and that the key is valid.
HTTP 422 for the customerCreate the customer with the same test key used for the subscription.
HTTP 422 for the planConfirm the plan belongs to the same test account and its status is active.
Existing subscription returnedYou reused an Idempotency-Key. This is expected for a retry; use a new key for a new subscription.
Authorization expiredInitialize a new subscription with a new idempotency key and complete checkout before its expiry.
Subscription is past_dueReview the failed charge webhook and retrieve the latest subscription details.

Do not modify database records or run internal server commands to force a billing date. Use the public daily interval for the shortest supported schedule.