---
updatedAt: 2026-05-15T23:53:11.000Z
agentTools:
  projectIndex: https://developer.scantopay.io/llms.txt
---

# USSD purchase API

The WIG (Wallet Initiation Gateway) endpoints for USSD-driven payments, balance queries, and card lookups.

The **WIG (Wallet Initiation Gateway) API** is the set of endpoints your USSD application calls to initiate payments and balance checks from a USSD session. These endpoints are grouped under `/purchase/network/*` and tagged `WIG API` in the OpenAPI reference.

For the high-level integration overview, when-to-use, and customer flow, see [USSD payment](ussd-payment). This page is the endpoint reference.

***

## Authentication and base URL

|              | Value                                     |
| ------------ | ----------------------------------------- |
| Sandbox      | `https://qa.scantopay.io/pluto`           |
| Production   | `https://scantopay.live/pluto`            |
| Auth         | Basic Auth or JWT Bearer, `EXTERNAL` role |
| Content-Type | `application/json`                        |

Full auth setup is on [Authentication](../Introduction/authentication).

***

## Endpoint summary

| Operation                                     | Method | Path                                  | Use when                                                                                                        |
| --------------------------------------------- | ------ | ------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| **Network code purchase — auto network**      | POST   | `/purchase/network/code`              | Customer pays an existing Scan to Pay code via USSD; let the platform detect the customer's network from MSISDN |
| **Network code purchase — specified network** | POST   | `/purchase/network/{network}/code`    | Same as above but you already know which mobile network operator handles the customer                           |
| **Network purchase — auto network**           | POST   | `/purchase/network`                   | Direct purchase (e.g. airtime) without a pre-existing code; auto-detect network                                 |
| **Network purchase — specified network**      | POST   | `/purchase/network/{network}`         | Direct purchase with explicit network                                                                           |
| **Network balance — auto network**            | POST   | `/purchase/network/balance`           | Customer's wallet balance check; auto-detect network                                                            |
| **Network balance — specified network**       | POST   | `/purchase/network/{network}/balance` | Balance check with explicit network                                                                             |
| **Card lookup**                               | GET    | `/purchase/network/cards`             | List the cards the customer has linked to their MSISDN — used to populate "which card?" menu in your USSD app   |
| **Strike purchase**                           | POST   | `/purchase/strike`                    | Re-attempt a previous transaction using its `originalTxId` (failure-recovery pattern)                           |

### Auto-detect vs network-specified — pick one

The `Harbour`-suffixed endpoints (auto-detect) determine the customer's network from the supplied MSISDN. Use these when:

* You don't already know the customer's network operator
* You're operating through a USSD aggregator that doesn't surface the network identity to your application

The `{network}`-path endpoints take an explicit network value (e.g. `MTN`, `VODACOM`, `CELLC`, `TELKOM`). Use these when:

* You already have the network identity from your USSD gateway integration
* You're routing through a network-specific USSD short code
* You want to fail fast if the customer is on an unexpected network

Both end in the same `TxState` machine — the difference is purely in routing.

***

## Two payment patterns

### Pattern A — Pay an existing code

Your backend has already created a Scan to Pay code (via the standard `POST /code/create` — see [Dynamic QR](qr-payments/dynamic-qr-payments)). The customer's USSD session quotes that code and the API initiates payment.

```bash
curl -X POST "https://qa.scantopay.io/pluto/purchase/network/code" \
  -u "$USERNAME:$PASSWORD" \
  -H 'Content-Type: application/json' \
  -d '{
    "msisdn": "27831234567",
    "code": "0123456789"
  }'
```

The platform looks up the code, pushes a PIN-entry request to the customer's phone, processes the resulting authorisation, and ends with the standard webhook.

### Pattern B — Direct network purchase

For airtime, data bundles, or other immediate-fulfilment products where you don't want to round-trip through `/code/create` first, call the network purchase endpoint directly with the basket details.

```bash
curl -X POST "https://qa.scantopay.io/pluto/purchase/network/MTN" \
  -u "$USERNAME:$PASSWORD" \
  -H 'Content-Type: application/json' \
  -d '{
    "msisdn": "27831234567",
    "amount": 50.00,
    "merchantReference": "topup-2025-05-14-00037",
    "cartItems": [
      {
        "description": "R50 airtime",
        "quantity": "1",
        "value": 50.00,
        "destination": "27831234567",
        "itemType": "AIRTIME"
      }
    ]
  }'
```

The `cartItems` array enables the platform's limit-checking machinery — see the [airtime / airtime-bundle limits](qr-payments/qr-payments-business-rules#airtime-limits) in the QR Code Payments business rules.

***

## Card lookup

Before triggering a purchase, your USSD app often needs to ask the customer "which card?" — present the list of cards they've linked to their MSISDN.

```bash
curl -X GET "https://qa.scantopay.io/pluto/purchase/network/cards?msisdn=27831234567" \
  -u "$USERNAME:$PASSWORD"
```

Response includes the customer's linked cards with masked BIN + last 4 (no full PAN ever crosses the wire). Use the returned card IDs to drive your USSD menu, and pass the chosen card on the subsequent purchase call.

***

## Balance check

Provides the customer's wallet balance — useful for "how much do I have left?" menus before initiating a purchase.

```bash
curl -X POST "https://qa.scantopay.io/pluto/purchase/network/balance" \
  -u "$USERNAME:$PASSWORD" \
  -H 'Content-Type: application/json' \
  -d '{
    "msisdn": "27831234567"
  }'
```

***

## Strike purchase

A *strike* re-runs a previously-failed transaction. Use it to recover from transient failures (network blip, bank timeout) by replaying against the same `originalTxId` rather than creating a new transaction from scratch.

```bash
curl -X POST "https://qa.scantopay.io/pluto/purchase/strike" \
  -u "$USERNAME:$PASSWORD" \
  -H 'Content-Type: application/json' \
  -d '{
    "originalTxId": 81234,
    "msisdn": "27831234567"
  }'
```

The platform retains the original transaction's amount, merchant reference, and basket; only the underlying card auth attempt is re-run. The webhook fires with the new `TxState` as if it were a fresh attempt.

***

## What you get back

Every USSD payment ends in the same `TransactionNotificationV3` webhook payload your other channels produce. The `status` field uses the `NotificationStatus` enum, mapping from the richer USSD-specific timeout `TxState` values (e.g. `END_TIMEOUT_PURCHASE` → `TIMEOUT_PURCHASE`). See [Webhooks](../Platform/webhooks) and [Transaction states](../Platform/transaction-states).

***

## Errors specific to USSD

| Code                                                     | Meaning                                                                                |
| -------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `433 INVALID_NETWORK`                                    | The network in the path (or auto-detected) isn't recognised. Common with unusual MNOs. |
| `434 CLIENT_SUSPENDED`                                   | The customer's account is suspended.                                                   |
| `465 MISSING_MSISDN`                                     | MSISDN required but not provided.                                                      |
| `468 WIG_CNP_PURCHASE_FAIL_CARD_EXPIRY_BIN_NOT_WHIELIST` | USSD CNP purchase failed: card expiry or BIN not whitelisted.                          |
| `469 WIG_3DS_PURCHASE_FAIL_CARD_EXPIRY_BIN_NOT_WHIELIST` | USSD 3DS purchase failed: same reasons.                                                |

Plus all the generic 4xx codes from [Errors](../Platform/errors).

***

## STK API — for MNO partners only

A separate endpoint group at `/stk/*` is reserved for Mobile Network Operator partners doing direct integration. It requires the `STK` role rather than `EXTERNAL` and isn't relevant for most merchants or PSPs. Endpoints include `/stk/code`, `/stk/cards/list`, `/stk/cards/link`, `/stk/cards/delink`, `/stk/cards/binInfo`, `/stk/purchase`, `/stk/balance`.

If you're an MNO and need access, contact <support@scantopay.io>.

***

## What's next

* **Customer-flow overview** → [USSD payment](ussd-payment)
* **Standard code-create endpoint (for Pattern A)** → [Dynamic QR](qr-payments/dynamic-qr-payments)
* **Receive the payment notification** → [Webhooks](../Platform/webhooks)
* **Airtime / data limit rules** → [QR Code Payments — business rules](qr-payments/qr-payments-business-rules)
* **Full OpenAPI reference for the WIG endpoints** → API Reference (auto-rendered)