> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.crisscross.money/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.crisscross.money/_mcp/server.

# Mobile Money

> Overview of mobile money on CrissCross — how a payment works, what you collect, per-country operators, and sandbox test numbers.

### Overview

Mobile money is the dominant consumer payment method across most of Africa. Customers hold a wallet balance with a mobile network operator (MTN, M-Pesa, Airtel, Orange, and others) and authorise payments from their handset using a PIN. CrissCross gives you a **single integration** that covers every supported operator in every supported country.

This page is the umbrella reference for the payment method: how a payment works, what you collect, sandbox testing, and the error reference. The available operators differ by country — see the [per-country pages](#supported-countries) for the operator list and any country-specific notes. For the end-to-end Direct API request/response sequence, see the [Direct API: Mobile Money guide](/direct-api-integration-mobile-money).

---

### How a mobile money payment works

Unlike a card payment, a mobile money payment is **asynchronous**: the customer approves the debit outside your checkout. Most operators use a **handset prompt** flow:

1. The merchant initiates a payment with the customer's provider and phone number.
2. The operator sends a push or USSD prompt to the customer's handset.
3. The customer enters their PIN to approve (or rejects, or lets it time out).
4. The operator confirms the outcome back to CrissCross, which notifies you via webhook and exposes the final state on the transaction.

While the customer is approving, the transaction sits in `AUTH_REQUIRED` (not `PENDING`), and `authState.authMethodType` tells you what is happening:

* `pendingApproval` — the prompt has been pushed to the customer's handset. Show a "check your phone" screen; there is **no `POST /v1/payment/authorize` step** — approval happens entirely on the handset, and you either poll or wait for the webhook.
* `ussd` — the customer must dial themselves: render `authState.ussd.instruction` **verbatim** in your checkout.
* `fields` — the rail asks for a **one-time PIN inside your checkout**: the `authState` lists a single `otp` field, submitted via `POST /v1/payment/authorize`.
* `redirect` — the customer authenticates on the **operator's own page**: send them to `authState.redirect.url`.

The `fields` and `redirect` rails are covered below and can be exercised in sandbox.

Typical approval takes 10 – 30 seconds. The provider will eventually time the prompt out (usually around 60 – 120 seconds) if the customer takes no action.

#### Redirect-based operators (Wave)

Some operators approve through a **redirect** rather than a handset prompt — the customer is sent to the operator's page (a QR code on desktop, an app deep link on mobile), approves there, and is redirected back. **Wave** (Francophone West Africa) works this way; see [Wave](/payment-method-senegal-mobile-money#wave) for the full flow. Country pages note which of their operators use the redirect flow.

---

### Supported countries

The available operators are determined by the `payerDetails.location` (ISO 3166 alpha-3) you set when creating a checkout session, filtered by what is enabled for your account — operators are configured during onboarding. On Hosted Checkout the customer sees the right operators automatically.

Pick your country for its operators and any country-specific notes:

| Country                                                                   | Operators                                                  |
| ------------------------------------------------------------------------- | ---------------------------------------------------------- |
| [Benin](/payment-method-benin-mobile-money)                               | Moov, Celtiis, Wave                                        |
| [Burkina Faso](/payment-method-burkina-faso-mobile-money)                 | Moov, Orange, Wave                                         |
| [Cameroon](/payment-method-cameroon-mobile-money)                         | MTN, Orange                                                |
| [Republic of the Congo](/payment-method-congo-mobile-money)               | Airtel, MTN                                                |
| [Côte d'Ivoire](/payment-method-cote-divoire-mobile-money)                | MTN, Orange, Moov, Wave                                    |
| [Democratic Republic of the Congo](/payment-method-dr-congo-mobile-money) | Vodacom M-Pesa, Airtel, Orange                             |
| [Ethiopia](/payment-method-ethiopia-mobile-money)                         | Telebirr, M-Pesa, CBE Birr, Coopay E-Birr, AwashBirr, YaYa |
| [Gabon](/payment-method-gabon-mobile-money)                               | Airtel, Moov                                               |
| [Ghana](/payment-method-ghana-mobile-money)                               | MTN, AirtelTigo, Telecel Money                             |
| [Kenya](/payment-method-kenya-mobile-money)                               | M-Pesa, Airtel                                             |
| [Malawi](/payment-method-malawi-mobile-money)                             | Airtel, TNM                                                |
| [Mozambique](/payment-method-mozambique-mobile-money)                     | Vodacom M-Pesa                                             |
| [Nigeria](/payment-method-nigeria-mobile-money)                           | Paga, OPay, PalmPay                                        |
| [Rwanda](/payment-method-rwanda-mobile-money)                             | MTN, Airtel                                                |
| [Senegal](/payment-method-senegal-mobile-money)                           | Orange, Free, Wave                                         |
| [Sierra Leone](/payment-method-sierra-leone-mobile-money)                 | Orange                                                     |
| [Tanzania](/payment-method-tanzania-mobile-money)                         | M-Pesa (Vodacom), Airtel, Tigo, Halotel                    |
| [Togo](/payment-method-togo-mobile-money)                                 | Moov, Wave                                                 |
| [Uganda](/payment-method-uganda-mobile-money)                             | MTN, Airtel                                                |
| [Zambia](/payment-method-zambia-mobile-money)                             | Airtel, MTN, Zamtel                                        |

---

### What you collect from the customer

For any mobile money payment, you need three things:

| Field         | Where it goes                      | Notes                                                                                                                                                                                                                                               |
| ------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Operator      | `paymentDetails.provider`          | The operator slug for the payer's country — see the country page. Present a country-filtered list; don't ask the customer to scroll through operators that don't exist in their country.                                                            |
| Mobile number | `paymentDetails.payerMobileNumber` | Local or E.164 format. CrissCross normalises to E.164 using the payer's `location`, so `0701234567` and `+254701234567` both work for a Kenyan payer. Validate and normalise before submission — see [Mobile Number Format](/mobile-number-format). |
| Full name     | `paymentDetails.payerFullName`     | Some operators require this; we always pass it through. Falls back to `payerDetails.fullName` from the session if omitted.                                                                                                                          |

The country code itself comes from `payerDetails.location` on the session — an ISO 3166 alpha-3 code like `KEN`, `NGA`, `ZAF`. You don't pass the country code on the payment request again.

---

### Integration options

You can accept mobile money through either of the standard CrissCross integration paths:

* **[Hosted Checkout](/hosted-checkout)** — CrissCross renders the operator selector, mobile number field, and pending-approval screen. Lowest engineering cost.
* **[Direct API: Mobile Money](/direct-api-integration-mobile-money)** — full control. You collect the inputs, call `POST /v1/payment`, and either poll or rely on webhooks for completion.

The Direct API guide is the most concrete reference for what fields are sent and what responses look like.

---

### Sandbox testing

Mobile money can be tested end to end in the sandbox. The outcome is selected by the payer's mobile number. The most common ones:

| Mobile number       | Outcome                             |
| ------------------- | ----------------------------------- |
| Any unlisted number | Succeeds                            |
| `0970000005`        | Fails with `payinInsufficientFunds` |
| `0970000006`        | Fails with `customerRejected`       |
| `0970000013`        | Errors with `downstreamError`       |

System errors, transient faults, delayed confirmations, and the OTP and redirect rails all have their own trigger numbers. See [Trigger Numbers](/testing-trigger-numbers) for the full set, and [Testing Overview](/testing-overview) for how to work through them.

---

### Failure and error reference

The full set of `authState.code` values you can see on a mobile money transaction:

| Code                       | Group        | Description                                       |
| -------------------------- | ------------ | ------------------------------------------------- |
| `payinInsufficientFunds`   | Auth failure | Wallet balance too low.                           |
| `customerRejected`         | Auth failure | Customer declined on handset.                     |
| `timeout`                  | Auth failure | Customer didn't respond in time.                  |
| `accountNotActive`         | Auth failure | Wallet exists but is inactive / suspended.        |
| `payinPayerInvalidAccount` | Auth failure | No wallet for this number on the chosen operator. |
| `payinPayerLimitExceeded`  | Auth failure | Daily transaction limit would be exceeded.        |
| `monthlyLimitExceeded`     | Auth failure | Monthly transaction limit would be exceeded.      |
| `transactionNotFound`      | Auth failure | Provider can't locate the transaction (rare).     |
| `declined`                 | Auth failure | Catch-all decline from the operator.              |
| `invalidPin`               | Auth failure | The customer entered a wrong OTP / PIN.           |
| `internalError`            | Error        | CrissCross-side error.                            |
| `downstreamError`          | Error        | Operator returned an error. May be retryable.     |

Auth failures are terminal for the transaction — the customer would need to retry with a fresh payment. Errors marked as retryable in the response (`authState.canRetry`) can be re-executed against the same checkout session.

---

### Next steps

* Find your country in the [supported countries](#supported-countries) table for its operators and notes.
* Walk through the end-to-end API sequence in **[Direct API: Mobile Money](/direct-api-integration-mobile-money)**.
* Configure your webhook subscription to receive `transaction.completed`, `transaction.failed`, `transaction.errored`, `transaction.cancelled`, and `transaction.expired` — see [Webhooks](/core-concepts-webhooks).