> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zerocash.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Mobile money charge

> Charge a customer's mobile money wallet, handle OTP and redirect steps, and confirm the outcome.

Use a mobile money charge when you already know the customer's phone number, currency, and
operator. This is a direct API flow where your application owns the payment form.

## Happy path

<Steps>
  <Step title="Initiate the charge">
    Send the amount, currency, phone number, and your `externalReference`.
  </Step>

  <Step title="Customer approves">
    They approve the prompt, or complete an OTP or redirect step.
  </Step>

  <Step title="Wait for the outcome">
    Listen for the final webhook, or query the transaction status.
  </Step>

  <Step title="Fulfil">
    Only once `chargeStatus` is `successful`.
  </Step>
</Steps>

## 1. Initiate the charge

Send a request to [Mobile Money Charge](/api-reference/endpoint/mobile-money-charge).

| Field                | Type   | Required | Description                                                                     |
| :------------------- | :----- | :------: | :------------------------------------------------------------------------------ |
| `amount`             | Number |    Yes   | Amount to charge, e.g. `1500`                                                   |
| `currency`           | String |    Yes   | ISO 4217 code, e.g. `KES`, `UGX`, `BWP`                                         |
| `externalReference`  | String |    Yes   | Your own unique ID for reconciliation                                           |
| `phoneNumber`        | String |    Yes   | E.164 without the plus, e.g. `254719624551`                                     |
| `momoOperatorId`     | String |     —    | Operator code. Required for **all currencies except `KES`**                     |
| `otpCode`            | String |     —    | One-time payment code some operators require *before* charge initiation         |
| `voucherPin`         | String |     —    | 16-character OTT voucher PIN. `BWP` OTT payments only                           |
| `walletCurrency`     | String |     —    | Currency to settle into. Defaults to `currency`                                 |
| `walletCountry`      | String |     —    | Country scope of the settlement wallet. Required when `walletCurrency` is `XOF` |
| `successRedirectUrl` | String |     —    | Return URL after a successful provider redirect, where supported                |
| `failureRedirectUrl` | String |     —    | Return URL after a failed provider redirect, where supported                    |

<Warning>
  If `currency` is not `KES`, `momoOperatorId` is **mandatory**. Find the code for the customer's
  network on the [mobile money operators](/guides/mobile-money-operators) page.
</Warning>

<CodeGroup>
  ```json Kenya (no operator needed) theme={null}
  {
    "amount": 100,
    "currency": "KES",
    "externalReference": "order_12345",
    "phoneNumber": "254719624551"
  }
  ```

  ```json With an operator theme={null}
  {
    "amount": 100,
    "currency": "KES",
    "externalReference": "order_12345",
    "phoneNumber": "254719624551",
    "momoOperatorId": "airtel"
  }
  ```

  ```json Senegal Wave with redirects theme={null}
  {
    "amount": 1000,
    "currency": "XOF",
    "externalReference": "order_wave_sn_12345",
    "phoneNumber": "221771234567",
    "momoOperatorId": "wave",
    "successRedirectUrl": "https://merchant.example.com/payments/success",
    "failureRedirectUrl": "https://merchant.example.com/payments/failed"
  }
  ```

  ```json Pre-charge OTP theme={null}
  {
    "amount": 1000,
    "currency": "XOF",
    "externalReference": "order_otp_12345",
    "phoneNumber": "00000000000",
    "momoOperatorId": "operator-id",
    "otpCode": "123456"
  }
  ```

  ```json Botswana OTT voucher theme={null}
  {
    "amount": 150,
    "currency": "BWP",
    "externalReference": "order_12345_bwp",
    "phoneNumber": "26771234567",
    "momoOperatorId": "ott-voucher",
    "voucherPin": "1234567890123456"
  }
  ```
</CodeGroup>

```json Response theme={null}
{
  "success": true,
  "message": "Initiated request.",
  "transactionId": "123456789"
}
```

Track the transaction with the returned `transactionId` or your own `externalReference`.

<AccordionGroup>
  <Accordion title="Pre-charge OTPs" icon="key">
    Some operators require the customer to generate a one-time payment code on their handset
    *before* you initiate the charge. Follow the operator's customer instructions for that market,
    then send the generated code as `otpCode` in the charge request.

    This is different from the `stepRequired: otp` flow in
    [step 4](#4-validate-an-otp), which happens *after* initiation.
  </Accordion>

  <Accordion title="Redirect support" icon="arrow-up-right-from-square">
    `successRedirectUrl` and `failureRedirectUrl` apply to redirect-capable rails such as Wave in
    Senegal (`XOF`). Operators that complete through STK, OTP, USSD, or a provider prompt may
    ignore these fields entirely.

    Always confirm the final status through a webhook or
    [Get Transaction](/api-reference/endpoint/get-transaction) — never treat a redirect return as
    confirmation.
  </Accordion>

  <Accordion title="Wallet country scope" icon="wallet">
    If `walletCountry` is omitted and `walletCurrency` matches `currency`, the country is resolved
    from the customer's phone number. If `walletCurrency` differs from `currency`, the wallet
    currency default is used where supported, such as `USD` → `US`.

    Pass `walletCountry` explicitly when settling into a non-default supported wallet country, and
    **always** pass it when `walletCurrency` is `XOF`.
  </Accordion>
</AccordionGroup>

## 2. Check the transaction status

Verify before providing value. Call
[Get Transaction](/api-reference/endpoint/get-transaction) with either the `transactionId` or your
`externalReference`.

<CodeGroup>
  ```json Successful theme={null}
  {
    "success": true,
    "data": {
      "transactionId": "lBK9bMny2gs4hLsG3XGq",
      "amount": 25,
      "type": "deposit",
      "currency": "KES",
      "senderCurrency": "KES",
      "senderAmount": 25,
      "receiverCurrency": "KES",
      "receiverAmount": 25,
      "chargeStatus": "successful",
      "status": "SUCCESSFUL",
      "method": "momo",
      "note": "TFH174NFSJ Confirmed. Ksh25.00 sent to Zerocash 722223344 on 17/06/25 at 1:17 AM.",
      "fullTimestamp": "2025-06-17T01:16:44+03:00",
      "externalReference": "test",
      "thirdPartyReference": "TFH174NFSJ",
      "phoneNumber": "254722416788"
    }
  }
  ```

  ```json Failed theme={null}
  {
    "success": true,
    "data": {
      "transactionId": "173675873400000038",
      "amount": 102,
      "type": "deposit",
      "currency": "KES",
      "chargeStatus": "failed",
      "status": "FAILED",
      "method": "mpesa",
      "note": "STK Prompt Time Out",
      "fullTimestamp": "2025-01-14T12:13:59+00:00",
      "externalReference": "173675873400000038",
      "phoneNumber": "254700000001"
    }
  }
  ```
</CodeGroup>

## 3. Handle webhooks

Configure a webhook URL so status changes are pushed to you instead of polling. See
[Getting started with webhooks](/guides/webhooks/getting-started).

<CodeGroup>
  ```json Created theme={null}
  {
    "event": "transaction_created",
    "data": {
      "transactionId": "BeOfXV1NVIcZlsSVeQAF",
      "status": "pending",
      "type": "deposit",
      "externalReference": "test"
    },
    "timestamp": "2024-10-03T16:33:14.600Z"
  }
  ```

  ```json Successful theme={null}
  {
    "event": "transaction_updated",
    "data": {
      "transactionId": "BeOfXV1NVIcZlsSVeQAF",
      "status": "successful",
      "type": "deposit",
      "externalReference": "test",
      "method": "momo",
      "thirdPartyReference": "TEQTEYRU"
    },
    "timestamp": "2024-10-03T16:34:02.100Z"
  }
  ```

  ```json Failed theme={null}
  {
    "event": "transaction_updated",
    "data": {
      "transactionId": "BeOfXV1NVIcZlsSVeQAF",
      "status": "failed",
      "type": "deposit",
      "externalReference": "test",
      "method": "momo",
      "note": "User could not be reached by stk"
    },
    "timestamp": "2024-10-03T16:34:02.100Z"
  }
  ```

  ```json OTP required theme={null}
  {
    "event": "transaction_updated",
    "data": {
      "transactionId": "BeOfXV1NVIcZlsSVeQAF",
      "status": "pending",
      "type": "deposit",
      "externalReference": "test",
      "method": "momo",
      "stepRequired": "otp"
    },
    "timestamp": "2024-10-03T16:33:41.200Z"
  }
  ```

  ```json Redirect required theme={null}
  {
    "event": "transaction_updated",
    "data": {
      "transactionId": "BeOfXV1NVIcZlsSVeQAF",
      "status": "pending",
      "type": "deposit",
      "externalReference": "test",
      "method": "momo",
      "stepRequired": "redirect",
      "redirectUrl": "https://test.com/"
    },
    "timestamp": "2024-10-03T16:33:41.200Z"
  }
  ```
</CodeGroup>

When `stepRequired` is present, act on it:

* **`otp`** — prompt the customer for the code they received, then continue to step 4.
* **`redirect`** — send the customer to `redirectUrl`, then continue to step 5.

## 4. Validate an OTP

If you receive `stepRequired: "otp"`, collect the code from the customer and submit it to
[Validate OTP](/api-reference/endpoint/validate-otp).

```bash theme={null}
POST /api/b2b/fiat/deposit/{transactionId}/validate-otp
```

| Field | Type   | Required | Description                                 |
| :---- | :----- | :------: | :------------------------------------------ |
| `otp` | String |    Yes   | The one-time password the customer received |

```json Response theme={null}
{
  "success": true,
  "message": "OTP validated successfully.",
  "transactionId": "123456789"
}
```

<Note>
  After successful validation the transaction keeps processing in the background. You will get a
  final `successful` or `failed` webhook when it completes.
</Note>

## 5. Handle redirects

When `stepRequired` is `redirect`:

<Steps>
  <Step title="Extract the URL">
    Take `redirectUrl` from the webhook or the Get Transaction response.
  </Step>

  <Step title="Redirect the customer">
    They complete authorisation on the provider's or bank's secure page.
  </Step>

  <Step title="Wait for the final webhook">
    A `transaction_updated` event with `successful` or `failed` is the real outcome.
  </Step>
</Steps>

<Warning>
  If you supplied `successRedirectUrl` and `failureRedirectUrl`, the provider may bounce the
  customer back to one of them. Treat that as a **navigation event, not payment confirmation** —
  the customer's browser landing on your success page does not mean funds moved.
</Warning>

## Refunds

A successful mobile money deposit can be reversed through
[Refund Deposit](/api-reference/endpoint/refund-deposit).
