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

# Encaissements

> Demandez un paiement mobile money à votre client

> [!NOTE]
>
> **Bêta.** Les encaissements sont en cours de déploiement en production. Vous pouvez déjà préparer votre intégration avec ce guide.

Un **encaissement** demande à votre client de payer depuis son compte mobile money. Il reçoit une demande sur son téléphone et confirme avec son code PIN. Vous recevez le résultat par webhook.

## Demander un paiement

```bash cURL theme={null}
curl -X POST https://api.kemypay.com/v1/mobile-money/collections \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "service": "019a8f2e-4c1b-7d3a-9e55-2b6f0c1d8a47",
    "amount": "1000",
    "currency": "XAF",
    "operator": "MTN_CMR",
    "phone_number": "237670000000",
    "reference": "order-2026-10-29-0001",
    "description": "Afroyamo Premium",
    "metadata": { "user_id": "42" }
  }'
```

```javascript Node.js theme={null}
const res = await fetch('https://api.kemypay.com/v1/mobile-money/collections', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.KEMYPAY_SECRET_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    service: process.env.KEMYPAY_SERVICE_CODE,
    amount: '1000',
    currency: 'XAF',
    operator: 'MTN_CMR',
    phone_number: '237670000000',
    reference: `order-${order.id}`, // votre propre référence unique
    description: 'Afroyamo Premium',
    metadata: { user_id: String(user.id) },
  }),
});
const { data: payment } = await res.json(); // statut : "processing"
```

```php PHP theme={null}
$ch = curl_init('https://api.kemypay.com/v1/mobile-money/collections');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('KEMYPAY_SECRET_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'service' => getenv('KEMYPAY_SERVICE_CODE'),
        'amount' => '1000',
        'currency' => 'XAF',
        'operator' => 'MTN_CMR',
        'phone_number' => '237670000000',
        'reference' => 'order-' . $order->id,
        'description' => 'Afroyamo Premium',
        'metadata' => ['user_id' => (string) $user->id],
    ]),
]);
$payment = json_decode(curl_exec($ch), true)['data'];
```

```python Python theme={null}
import os, requests

res = requests.post(
    "https://api.kemypay.com/v1/mobile-money/collections",
    headers={"Authorization": f"Bearer {os.environ['KEMYPAY_SECRET_KEY']}"},
    json={
        "service": os.environ["KEMYPAY_SERVICE_CODE"],
        "amount": "1000",
        "currency": "XAF",
        "operator": "MTN_CMR",
        "phone_number": "237670000000",
        "reference": f"order-{order.id}",
        "description": "Afroyamo Premium",
        "metadata": {"user_id": str(user.id)},
    },
)
payment = res.json()["data"]
```

```json Réponse (201) theme={null}
{
  "success": true,
  "data": {
    "code": "019a9c41-7e2d-4b8f-a1c3-5d6e7f8a9b0c",
    "reference": "order-2026-10-29-0001",
    "service_code": "019a8f2e-4c1b-7d3a-9e55-2b6f0c1d8a47",
    "status": "processing",
    "country": "CMR",
    "operator": "MTN_CMR",
    "phone_number": "237670000000",
    "currency": "XAF",
    "value": "1000.00",
    "amount": "1000.00",
    "fee": "33.00",
    "fee_bearer": "merchant",
    "net_amount": "967.00",
    "description": "Afroyamo Premium",
    "metadata": { "user_id": "42" },
    "failure_code": null,
    "failure_message": null,
    "operator_transaction_id": null,
    "callback_url": null,
    "created_at": "2026-10-29T10:00:00+00:00",
    "completed_at": null
  },
  "meta": { "request_id": "a4f8e2c1b3d97056", "timestamp": "2026-10-29T10:00:00+00:00", "mode": "live" }
}
```

## Champs

| Champ | Obligatoire | Remarques |
| - | - | - |
| `service` | Oui | Le code du service qui encaisse. Il doit être `active` (ou `pending_review` en mode test) |
| `amount` | Oui | La valeur de la marchandise, en chaîne. Respectez les `decimals` de l'opérateur |
| `currency` | Oui | `XAF`, `XOF`, `KES`… Le Congo - Kinshasa accepte `CDF` et `USD` |
| `phone_number` | Oui | Avec l'indicatif du pays. Les espaces, tirets et `+` sont retirés |
| `reference` | Oui | **Votre** référence unique. Voir [Nouvelles tentatives sans risque](#nouvelles-tentatives-sans-risque) |
| `operator` | Non | `MTN_CMR`… Deviné à partir du numéro s'il manque |
| `country` | Non | Une vérification : refusé si l'opérateur est dans un autre pays |
| `fee_bearer` | Non | Remplace le réglage du service |
| `description` | Non | Affiché à votre client sur la demande : 4 à 22 lettres, chiffres ou espaces |
| `metadata` | Non | Jusqu'à 10 clés avec des valeurs texte, renvoyées dans les webhooks |
| `webhook_url` | Non | https. L'emporte sur celle du service pour ce paiement |
| `callback_url` | Non | L'emporte sur celle du service pour ce paiement |

## Statuts

```
pending ──▶ processing ──▶ completed
                      └──▶ failed
```

| Statut | Signification |
| - | - |
| `pending` | Enregistré, l'opérateur n'a pas encore répondu. Nous vérifions pour vous |
| `processing` | Votre client a une demande sur son téléphone |
| `completed` | Payé. `net_amount` est sur le solde de votre service |
| `failed` | Non payé. `failure_code` dit pourquoi. Aucun argent n'a été pris |

**Attendez le webhook**, pas la réponse : votre client peut mettre une minute à taper son code PIN.

## Codes d'échec

| `failure_code` | Ce qui s'est passé | Ce qu'il faut dire à votre client |
| - | - | - |
| `payer_not_found` | Le numéro n'est pas un compte mobile money de cet opérateur | Vérifiez le numéro ou l'opérateur |
| `payment_not_approved` | Mauvais code PIN, refus, ou pas de réponse à temps | Réessayez et validez la demande |
| `insufficient_balance` | Pas assez d'argent sur le compte | Rechargez le compte mobile money |
| `payer_limit_reached` | Un plafond du compte est atteint | Essayez un montant plus petit ou plus tard |
| `invalid_phone_number` | Le numéro n'est pas valide pour cet opérateur | Vérifiez le numéro |
| `invalid_amount` | Montant refusé par l'opérateur (limites, décimales) | Vérifiez le montant |
| `operator_unavailable` | L'opérateur est en panne | Utilisez un autre opérateur ou réessayez plus tard |
| `payment_in_progress` | Un paiement attend déjà sur le téléphone | Validez-le ou annulez-le d'abord |
| `expired` | Le paiement n'a jamais démarré | Réessayez |
| `unknown_error` | Échec inconnu | Réessayez |

Dans tous les cas d'échec, **aucun argent n'a été pris**.

## Nouvelles tentatives sans risque

`reference` rend la demande **idempotente**. Si votre serveur dépasse le délai, renvoyez exactement la même demande :

* même `reference`, même service, montant et numéro → `200` avec le **même paiement**. Votre client n'est jamais débité deux fois ;
* même `reference` avec un autre service, montant ou numéro → `422 VALIDATION_ERROR`.

Utilisez une référence qui identifie la commande dans votre système, comme `order-1234`.

## Pièges fréquents

> **Décimales**
>
> Montants entiers seulement pour la plupart des opérateurs en XAF et XOF. `1000.50` est refusé quand `decimals` vaut `0`.

> **Les limites s'appliquent à ce que paie le client**
>
> Avec `fee_bearer: "customer"`, le client paie plus que la valeur : vérifiez `within_limits` avec [`POST /quote`](https://docs.kemypay.com/fr/api-reference/collections/quote).

> **Opérateur fermé**
>
> `503 OPERATION_FAILED` avec `failure_code: operator_unavailable` : proposez un autre opérateur du pays.

> **La demande de code PIN affiche notre prestataire de paiement**
>
> La demande est envoyée par l'opérateur et affiche le nom de notre prestataire de paiement, pas votre marque. Prévenez vos clients.


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