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

# Statut d'un paiement

> Comment savoir si un paiement a réellement eu lieu — webhooks, votre transaction, ou interroger directement l'opérateur.

Un encaissement ne se termine pas au moment où vous appelez `/pay`. Cet appel se contente
d'envoyer une demande de confirmation sur le téléphone du client (ou de créer un
checkout Wave). Le client valide, l'opérateur confirme à Kori, et c'est seulement alors
que votre solde est crédité.

Chaque paiement a donc trois états possibles :

| État      | Signification                                            | Que faire                                  |
| --------- | -------------------------------------------------------- | ------------------------------------------ |
| `pending` | La demande est partie. Le client n'a pas encore répondu. | Patientez. **Ne livrez pas la commande.**  |
| `success` | L'opérateur a confirmé. Votre solde est crédité.         | Livrez la commande.                        |
| `failed`  | L'opérateur a refusé, ou le client a annulé.             | Rien n'a été débité. Laissez-le réessayer. |

<Warning>
  `pending` n'est **pas** un échec. Un paiement peut rester en attente plusieurs minutes,
  le temps que le client retrouve son téléphone. Considérez-le comme non résolu, jamais
  comme refusé.
</Warning>

## 1. Les webhooks — la bonne façon de construire

Abonnez-vous à `payment_received` et laissez Kori vous prévenir. Aucun polling, aucun
minuteur, et cela fonctionne même si personne n'a de navigateur ouvert.

```json Contenu du webhook theme={null}
{
  "event": "payment_received",
  "data": { "transaction_id": 88213, "reference": "commande_1024", "amount": 5000 }
}
```

Voir [Webhooks](/fr/webhooks) pour la vérification de signature et les relances.

## 2. Lire votre transaction

```bash theme={null}
curl https://api.kori.ml/merchant/api/transactions/88213 \
  -H "Authorization: Bearer <token>"
```

Renvoie l'enregistrement conservé par Kori, `status` compris. C'est ce que vous utilisez
pour un tableau de bord, un travail de rapprochement, ou chaque fois que vous voulez la
réponse telle que Kori la connaît actuellement. Nécessite le scope `balance`.

## 3. Interroger l'opérateur maintenant

```bash theme={null}
curl https://api.kori.ml/merchant/api/payment/status/commande_1024
```

```json theme={null}
{ "message": "SUCCESS", "state": "success", "status": true }
```

Cet appel interroge directement l'opérateur mobile money et règle la transaction si elle
a abouti : c'est donc la réponse faisant autorité tant que le paiement est en cours. Il
s'utilise avec la `reference`, pas avec le `transaction_id`.

Il ne demande aucune authentification — la référence fait office de justificatif — ce qui
permet à une page de paiement de l'interroger depuis le navigateur du client.

<Note>
  Utilisez-le pendant qu'un client attend le résultat. Pour tout le reste, préférez le
  webhook : il ne coûte rien et arrive même si le client ferme l'onglet.
</Note>

## Si vous devez faire du polling

* Interrogez toutes les **3 à 5 secondes**, pas plus souvent.
* Arrêtez dès que `state` vaut `success` ou `failed`.
* Renoncez après environ **deux minutes** d'attente côté client — mais traitez cela comme
  « toujours inconnu », pas comme un échec.
* Un `500` signifie que l'opérateur est injoignable. Réessayez : cela ne dit rien sur le
  paiement.

Le règlement est idempotent : le webhook, cet endpoint et le réconciliateur interne de
Kori peuvent tous résoudre le même paiement, et votre solde n'est crédité qu'une seule
fois, quel que soit celui qui arrive en premier.

## Les paiements jamais confirmés

Si un client ne répond jamais à la demande, le paiement reste `pending` et Kori le marque
`expired` au bout de 24 heures. Une confirmation tardive vous crédite quand même :
`expired` signifie « nous avons cessé d'attendre », pas « l'argent est perdu ».

<Warning>
  Ne concluez jamais qu'un paiement a échoué simplement parce que vous n'en entendez plus
  parler. Les seuls échecs sont `state: "failed"` et `status: "failed"`.
</Warning>
