NEW APP AVAILABLE FOR DOWNLOAD NOW

Get it on Google PlayDownload on the App Store

First Purchase in 5 Minutes

Complete a sandbox purchase end to end with no real money involved

Make your first purchase against the sandbox: authenticate, check your balance, buy airtime, and verify it completed. Nothing is charged and no real airtime is delivered.

All requests in this guide use the sandbox base URL:

text
https://my.rizpay.app/api/partners/sandbox/v1

Prerequisites

  • A RizPay business account
  • API access enabled in your settings

That's it. No funding required: the sandbox returns a mock balance.

Step 1: Create a Sandbox API Key

  1. Log in at my.rizpay.app
  2. Go to Settings > API Keys
  3. Click Create New Key
  4. Select the Sandbox environment
  5. Select these scopes: read_balance, view_products, purchase_airtime, read_transactions
  6. Copy your secret key (shown only once!)

Your key looks like sk_test_xxxxxxxxxxxx. Sandbox keys only work on sandbox endpoints, so there is no risk of touching production.

Warning: Never share your secret key or commit it to version control. Store it in an environment variable:

bash
export RIZPAY_API_KEY="sk_test_your_test_key"

Step 2: Confirm the API Is Up

The health endpoint needs no authentication:

bash
curl -X GET \
  https://my.rizpay.app/api/partners/sandbox/v1/health

Response:

json
{
  "status": { "code": 200, "message": "OK" },
  "data": {
    "service": "partners-api-sandbox",
    "version": "1.0.0",
    "environment": "sandbox",
    "timestamp": "2026-05-18T11:39:50+01:00"
  }
}

Step 3: Check Your Balance

Verify your key works. The sandbox returns a mock balance:

bash
curl -X GET \
  -H "Authorization: Bearer $RIZPAY_API_KEY" \
  https://my.rizpay.app/api/partners/sandbox/v1/account/balance

Response:

json
{
  "status": { "code": 200, "message": "Balance retrieved" },
  "data": {
    "balance": "100000.00",
    "currency": "NGN",
    "updated_at": "2026-05-18T11:40:02+01:00"
  }
}

If you get an ENVIRONMENT_MISMATCH error, you are using a sk_live_ key. Create a sandbox key (sk_test_) and try again.

Step 4: Pick a Product

List airtime products. The sandbox returns real product data:

bash
curl -X GET \
  -H "Authorization: Bearer $RIZPAY_API_KEY" \
  "https://my.rizpay.app/api/partners/sandbox/v1/products/airtimes?network=MTN"

Response:

json
{
  "status": { "code": 200, "message": "Airtime products retrieved" },
  "data": [
    {
      "id": "prd_42",
      "type": "airtime",
      "attributes": {
        "display_name": "MTN Airtime",
        "network": "MTN",
        "min_amount": "50.0",
        "max_amount": "50000.0",
        "price": {
          "currency": "NGN",
          "basis": "face_value",
          "min_amount": "50.00",
          "max_amount": "50000.00"
        }
      }
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total_pages": 1,
    "total_count": 1
  }
}

Note the product id: you need it for the purchase. Product ids are numeric with a prd_ prefix (e.g. prd_42) and differ per environment, so always use the id from your own response, not the one in this example.

Step 5: Create the Purchase

The sandbox decides the outcome of a purchase from the phone number you send. Only these test numbers are recognized for airtime:

Phone numberOutcome
08011111111successful immediately
08022222222pending, auto-transitions to successful after ~30 seconds
08033333333pending, auto-transitions to failed after ~30 seconds
08044444444failed with INSUFFICIENT_BALANCE
08055555555failed with VALIDATION_ERROR
08066666666reversed
Any other numberfailed with PURCHASE_FAILED

Use 08011111111 for an instant success. Replace prd_42 with the product id from Step 4:

bash
curl -X POST \
  -H "Authorization: Bearer $RIZPAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": "prd_42",
    "phone_number": "08011111111",
    "amount": "100.00",
    "external_reference": "1736234400T3S4T5"
  }' \
  https://my.rizpay.app/api/partners/sandbox/v1/purchases

Response:

json
{
  "status": { "code": 201, "message": "Purchase created successfully" },
  "data": {
    "id": "txn_5f1c3a9e-2b4d-4c6f-8a1e-9d7b5c3a2f4e",
    "type": "transaction",
    "attributes": {
      "amount": "100.0",
      "currency": "NGN",
      "status": "successful",
      "category": "purchase",
      "description": "Successful airtime purchase",
      "reference": "sandbox_1a2b3c4d5e6f7a8b9c0d",
      "external_reference": "1736234400T3S4T5",
      "product_type": "airtime",
      "phone_number": "08011111111",
      "meter_number": null,
      "price": {
        "product_amount": "100.00",
        "fee_amount": "0.00",
        "total_debit": "100.00",
        "currency": "NGN",
        "basis": "face_value"
      },
      "created_at": "2026-05-18T11:41:10+01:00",
      "updated_at": "2026-05-18T11:41:10+01:00"
    }
  }
}

Tip: The external_reference must be exactly 16 characters: a 10-digit Unix timestamp plus 6 alphanumeric characters, and unique per transaction. See Duplicate Prevention for code that generates one.

If you send a phone number that is not in the table above, the sandbox rejects the purchase:

json
{
  "status": { "code": 400, "message": "Bad Request" },
  "error": {
    "code": "PURCHASE_FAILED",
    "message": "Purchase failed. Please try again."
  }
}

Step 6: Walk the Pending Lifecycle

Production purchases usually start as pending and complete on the provider side. To rehearse that flow, create a second purchase with the pending test number 08022222222 (and a fresh external_reference):

bash
curl -X POST \
  -H "Authorization: Bearer $RIZPAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": "prd_42",
    "phone_number": "08022222222",
    "amount": "100.00",
    "external_reference": "1736234460P3N6D1"
  }' \
  https://my.rizpay.app/api/partners/sandbox/v1/purchases

The response has "status": "pending". It transitions to successful on its own after about 30 seconds, or you can query it to complete it immediately (replace the id with the id from your response):

bash
curl -X POST \
  -H "Authorization: Bearer $RIZPAY_API_KEY" \
  https://my.rizpay.app/api/partners/sandbox/v1/purchases/txn_7e2d4b1c-9a3f-4e5d-b6c8-1f0a9e8d7c6b/query

Response:

json
{
  "status": { "code": 200, "message": "Purchase details retrieved" },
  "data": {
    "id": "txn_7e2d4b1c-9a3f-4e5d-b6c8-1f0a9e8d7c6b",
    "type": "transaction",
    "attributes": {
      "amount": "100.0",
      "currency": "NGN",
      "status": "successful",
      "category": "purchase",
      "description": "Pending transaction - transitions to successful after delay",
      "reference": "sandbox_0d9c8b7a6f5e4d3c2b1a",
      "external_reference": "1736234460P3N6D1",
      "product_type": "airtime",
      "phone_number": "08022222222",
      "meter_number": null,
      "price": {
        "product_amount": "100.00",
        "fee_amount": "0.00",
        "total_debit": "100.00",
        "currency": "NGN",
        "basis": "face_value"
      },
      "created_at": "2026-05-18T11:42:30+01:00",
      "updated_at": "2026-05-18T11:42:35+01:00"
    }
  }
}

You can fetch the transaction at any time with GET /purchases/:id and get the same shape back.

Note: POST /purchases/:id/query only works while a transaction is pending. Querying a transaction that already completed returns an INVALID_STATUS error, so there is nothing to query on the Step 5 purchase: it was successful from the start.

That's the complete purchase lifecycle: created, pending, successful. In production the transition happens on the provider side and you can receive it as a webhook event instead of polling.

What You Just Did

StepEndpointWhat it proved
Health checkGET /healthThe API is reachable
BalanceGET /account/balanceYour key authenticates
List productsGET /products/airtimesYou can discover products
PurchasePOST /purchasesYou can create transactions
LifecyclePOST /purchases/:id/query then GET /purchases/:idYou can handle pending statuses

Going Live

When your integration works end to end in the sandbox:

  1. Create a production API key (sk_live_)
  2. Change the base URL to https://my.rizpay.app/api/partners/v1
  3. Fund your account and test with a small amount first

The endpoints and payloads are identical between sandbox and production. The test phone numbers are sandbox-only: in production, every valid number is a real purchase.

Next Steps