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

# Deposit collection modes

> How a deposit is collected for each operator, and what your integration must do in each case.

A deposit is collected in one of two modes. The mode depends on the payment channel behind the operator and is returned in every deposit response as `collection_mode`.

| `collection_mode` | Who starts the payment | What the player does |
| - | - | - |
| `merchant_initiated` | winam-payments sends a payment request to the player's phone | Confirms the request with their Mobile Money PIN |
| `customer_initiated` | The player dials a USSD code from their own phone | Dials the code you display, then confirms with their Mobile Money secret code |

## Mode per operator (Cameroon)

| Operator | `operator` | Collection mode |
| - | - | - |
| MTN Mobile Money | `mtn` | `merchant_initiated` |
| Orange Money | `orange` | `customer_initiated` |

<Warning>
  The mode is configured by Winam per payment channel and can change without an API change. **Always branch on `collection_mode` from the response**, never on the operator name. An integration that handles both modes keeps working whatever the configuration.
</Warning>

The request is identical in both modes: same endpoint, same fields. Only the response and the player experience differ. Collection modes apply to **deposits only**; withdrawals work the same way for every operator.

## Side by side

| | `merchant_initiated` | `customer_initiated` |
| - | - | - |
| `instructions` in the response | `null` | Object to display to the player |
| What you display | "Confirm the payment request on your phone" | The USSD code, a **Dial** button, a **Copy** button and the steps |
| `status` right after creation | `pending` or `provider_acknowledged`; `failed` if the operator rejects the request | `pending` |
| Confirmation | The player confirms the request; winam-payments detects the payment | The operator's confirmation SMS reaches winam-payments after the player pays |
| Channel unavailable | `200` with `status: "failed"` (reason in `state_reason`), or `503` | `503`, no transaction created |
| Payment after `expires_at` | Held for a Winam operator decision | Completed automatically when it can be matched: `payment.succeeded` follows `payment.expired` |

## Merchant-initiated flow

<Steps>
  <Step title="Create the deposit">
    Call `POST /api/v1/deposits`. The response has `collection_mode: "merchant_initiated"` and `instructions: null`.
  </Step>

  <Step title="The player receives a payment request">
    winam-payments sends the request to the number given in `msisdn`. Tell the player to check their phone and enter their Mobile Money PIN.
  </Step>

  <Step title="Wait for the outcome">
    Wait for the webhook on your `callback_url`, or poll `GET /api/v1/transactions/{winam_tx_id}`.
  </Step>
</Steps>

If the response already has `status: "failed"`, the operator rejected the request at initiation (invalid number, amount below the operator minimum…). Read `state_reason` from `GET /api/v1/transactions/{winam_tx_id}` and let the player retry with a new `reference`.

## Customer-initiated flow

<Steps>
  <Step title="Create the deposit">
    Call `POST /api/v1/deposits`, optionally with `lang` (`fr` default, or `en`). The response has `collection_mode: "customer_initiated"`, `status: "pending"` and an `instructions` object.
  </Step>

  <Step title="Display the instructions">
    Show `instructions.steps` in order, the code `instructions.ussd_string`, a **Dial** button that opens `instructions.dial_uri` and a **Copy** button that copies `instructions.copy_text`.
  </Step>

  <Step title="The player pays from their phone">
    The player dials the code from the number given in `msisdn` and confirms with their Mobile Money secret code. Nothing is sent to their phone by winam-payments.
  </Step>

  <Step title="Wait for the outcome">
    Poll `GET /api/v1/transactions/{winam_tx_id}` every `instructions.polling_interval_seconds` seconds, or wait for the webhook. The deposit stays `pending` until the payment is confirmed.
  </Step>
</Steps>

### What to display

| Element | Source | Notes |
| - | - | - |
| USSD code | `instructions.ussd_string` | Display it prominently. It already contains the exact amount. |
| Dial button | `instructions.dial_uri` | A `tel:` link that opens the phone dialer with the code pre-filled. |
| Copy button | `instructions.copy_text` | Required: see the iOS note below. |
| Steps | `instructions.steps` | Localized `{order, text}` items, in the language requested with `lang`. |
| Recipient | `instructions.merchant.display_name` | The name the player sees on the operator confirmation screen, so they can check it. May be `null`. |
| Illustration | `instructions.illustration_url`, `illustration_alt` | Optional image of the dialing flow. `null` when not configured. |

<Warning>
  **iOS:** a `tel:` link containing `*` or `#` may not open the dialer. Always offer the **Copy** button so the player can paste the code into the Phone app.
</Warning>

### Rules that decide whether a payment is credited

The payment is matched to the deposit using the **paying number**, the **receiving number** and the **exact amount**.

* **Paying number.** The player must pay from the number sent in `msisdn`. A payment made from another number is not matched to this deposit.
* **Receiving number.** The player must pay to the merchant number contained in the `instructions` of **this** deposit. Several merchant numbers can be in service for the same operator, and each deposit is assigned one of them. A payment sent to another Winam merchant number, for example one saved from an earlier deposit, is not credited automatically.
* **Exact amount.** The player must not change the amount in the code. A payment of a different amount is not matched.
* **One player per number and amount.** If deposits from two different `player_id` values are open (or expired recently) for the same number and the same amount, an incoming payment is not credited automatically.
* **Several deposits from the same player.** One payment completes exactly one deposit. Prefer returning the existing `pending` deposit to the player over creating a new one for the same number and amount.

A payment that cannot be matched automatically is not lost: it is held for review by a Winam operator. To have it resolved, contact support with the paying number, the amount and the time of payment.

### Payments reviewed by an operator

A Winam operator can complete a deposit by hand from the payments received from the player's number that were not matched: a payment sent to another merchant number, or several partial payments.

* The deposit is completed only when the payments received **add up to at least the declared amount**. Below that amount it stays unpaid.
* The amount credited is always the deposit's `amount_xaf`, never the sum of the payments. An overpayment is not added to the deposit; it is handled by Winam support.
* You receive the usual `payment.succeeded` webhook for the same `winam_tx_id`. It can arrive minutes or hours after creation, and after a `payment.expired` if the deposit had expired in the meantime.

Nothing changes in your integration: credit the player when `payment.succeeded` arrives, with the `amount_xaf` it carries.

<Note>
  The player's Mobile Money secret code is typed on their phone only. Never ask for it, transmit it or store it.
</Note>

### Late payments

The player can pay after `expires_at`. You then receive `payment.expired`, and later `payment.succeeded` **for the same `winam_tx_id`** when the payment arrives within the late-payment window (24 hours by default) and matches the deposit. Your webhook handler must accept `payment.succeeded` on a transaction it already saw as expired, and credit the player.

### When the channel is unavailable

`POST /api/v1/deposits` answers **503** and creates no transaction when the channel cannot confirm payments at the moment. Offer another payment method or let the player retry later. [`GET /api/v1/{country}/providers/status`](/api-reference/providers-status) reports the provider as `degraded` or `unavailable`.

## Handling both modes

```javascript theme={null}
const deposit = await createDeposit({ player_id, msisdn, amount_xaf, operator, reference, callback_url, lang: "en" });

if (deposit.status === "failed") {
  showError("The operator rejected the request. Please try again.");
} else if (deposit.collection_mode === "customer_initiated") {
  // The player dials the code themselves.
  showDialScreen(deposit.instructions);       // code, Dial, Copy, steps
  pollUntilDone(deposit.winam_tx_id, deposit.instructions.polling_interval_seconds);
} else {
  // merchant_initiated: a payment request was sent to the player's phone.
  showMessage("Confirm the payment request on your phone with your PIN.");
  pollUntilDone(deposit.winam_tx_id);
}
```

In both branches the final outcome is the same: a `payment.succeeded`, `payment.failed` or `payment.expired` webhook, and the state returned by `GET /api/v1/transactions/{winam_tx_id}`.

## Integration checklist

* Branch on `collection_mode`, not on `operator`.
* Render `instructions` when present: code, **Dial**, **Copy**, steps.
* Tell the player to pay from the number they entered, to the number shown for this deposit, and not to change the amount.
* Keep the deposit screen open while `pending` and poll, or rely on the webhook.
* Accept `payment.succeeded` after `payment.expired` for the same `winam_tx_id`.
* Handle `503` on creation by offering another payment method.
* Reuse the same `reference` when retrying the same payment: the same transaction and instructions are returned.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.