# Mobile Money
Source: https://paylinky.vercel.app/docs/mobile-money

Create and track Mobile Money collections.

# Mobile Money

```http
POST https://paylinky.vercel.app/api/v1/collect
```

## Request

| Field | Required | Description |
| - | - | - |
| `amount` | Yes | Positive number, within the platform min/max |
| `currency` | Yes | The currency of the country (`XAF`, `XOF`, `CDF`…) |
| `country_code` | Yes | ISO code returned by `/countries`, e.g. `CM` |
| `operator` | Yes | Exact operator name from `/countries` (case-insensitive) |
| `phone` | Yes | International wallet number, e.g. `+237670000000`. Spaces, dashes and brackets are accepted |
| `reference` | No | Your order reference (≤ 191 chars). Returned as `merchant_reference`; reusing it returns the existing payment |
| `notify_url` | No | Public http(s) URL for this payment's webhook (overrides your default endpoint) |
| `otp` | No | OTP when confirming an `otp_required` flow (see [Payment flows](/docs/payment-flows)) |

:::tabs
```bash cURL
curl -X POST https://paylinky.vercel.app/api/v1/collect \
  -H "Authorization: Bearer $PAYLINKY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 5000,
    "currency": "XAF",
    "country_code": "CM",
    "operator": "MTN Mobile Money",
    "phone": "+2376XXXXXXXX",
    "reference": "order_cm_12345",
    "notify_url": "https://shop.example/webhooks/paylinky"
  }'
```
```javascript Node.js
const payment = await plk.collect({
  amount: 5000, currency: "XAF", country_code: "CM",
  operator: "MTN Mobile Money", phone: "+2376XXXXXXXX",
  reference: "order_cm_12345",
});
```
```python Python
payment = plk.collect(amount=5000, currency="XAF", country_code="CM",
                      operator="MTN Mobile Money", phone="+2376XXXXXXXX",
                      reference="order_cm_12345")
```
```php PHP
$payment = $plk->collect(["amount" => 5000, "currency" => "XAF", "country_code" => "CM",
  "operator" => "MTN Mobile Money", "phone" => "+2376XXXXXXXX", "reference" => "order_cm_12345"]);
```
:::

## Response — `202 Accepted` (USSD push)

```json
{
  "transaction_id": "0b9c7f0e-2d0b-4d76-9f5a-6b8a1c1d2e3f",
  "reference": "PLK-7XK2M9QPLA",
  "merchant_reference": "order_cm_12345",
  "status": "pending",
  "payment_method": "mobile_money",
  "amount": 5000,
  "fee_amount": 300,
  "credited_amount": 4700,
  "currency": "XAF",
  "sandbox": false,
  "country_code": "CM",
  "operator": "MTN Mobile Money",
  "phone": "+2376XXXXXXXX",
  "created_at": "2026-01-01T10:00:00.000Z",
  "expires_at": null
}
```

The customer receives a prompt on their phone and approves with their Mobile Money PIN. `status` stays `pending` until the provider confirms — **never deliver on the `202`**.

| Field | Description |
| - | - |
| `transaction_id` | Paylinky UUID — use with `GET /transaction/{id}` |
| `reference` | Paylinky reference |
| `merchant_reference` | Your `reference`, or `null` |
| `flow` / `wave_url` | Present for redirect flows such as Wave |
| `fee_amount` / `credited_amount` | Fee and the amount credited to your balance on success |
| `sandbox`, `simulation` | `true` for sandbox keys |

## Idempotent replays

Sending the same `reference` with identical `amount`, `currency` and `country_code` returns the existing payment with `idempotent_replay: true`. A different payload with the same `reference` returns `409 reference_conflict`.

:::warning
After a network timeout (`504 upstream_timeout`) do **not** create a second payment. Check the status of the original `reference`/`transaction_id` first.
:::

## Check the status

```http
GET https://paylinky.vercel.app/api/v1/transaction/{transaction_id}
```

You can pass the `transaction_id`, the Paylinky `reference` or your `merchant_reference`. Paylinky re-checks pending live payments with the provider on each call. A `200` means the transaction was found — always read `status`.

```bash
curl https://paylinky.vercel.app/api/v1/transaction/order_cm_12345 -H "Authorization: Bearer $PAYLINKY_KEY"
```
