Causes d'échec
Le catalogue des 9 catégories normalisées — libellé, origine, action recommandée.
Sur un failed ou un cancelled, az54 normalise la cause de l'échec en une
catégorie fermée : failureCategory. C'est sur cette valeur qu'il faut brancher
votre logique — jamais sur failureReason ni failureCode, le message et le
code bruts du PSP, qui ne sont pas contractuels (même règle que pour
message sur les erreurs API).
Où les trouver
- Le webhook sortant, sur toute notification dont le
statusest terminal en échec (failedoucancelled), pay-in comme pay-out. Peu importe le canal qui a produit la transition : callback PSP, polling, ou dispatch synchrone d'un pay-out. - La réponse HTTP synchrone d'un
POST /v1/pay-in, quand le paiement échoue avant même la sollicitation du payeur (ex. corridor résolu mais rejeté immédiatement par l'opérateur). - La réponse HTTP synchrone d'un
POST /v1/pay-out, quand le prestataire répond en échec sans passer par un callback et qu'aucun autre prestataire du canal ne peut prendre le relais. Si az54 rejoue le versement ailleurs, la réponse porte un statut non terminal (initiated) et aucune cause d'échec : l'issue n'est pas encore acquise, elle vous parviendra par webhook — ou se relira sur les deux lectures ci-dessous. GET /v1/transactions, sur chaque ligne de votre historique, etGET /v1/transactions/by-id/{transactionId}pour une transaction précise (Réconciliation). Ces deux-là portent aussifailedAt(voir plus bas), que les canaux ci-dessus n'ont pas.
Le reçu public ne les porte pas.
Le catalogue
failureCategory | Libellé | Origine | Action recommandée |
|---|---|---|---|
INSUFFICIENT_FUNDS | Solde insuffisant | Compte mobile money du payeur | Demander au client de recharger son compte mobile money, puis relancer avec un nouvel order_id. |
INVALID_RECIPIENT | Destinataire invalide | Numéro du destinataire | Vérifier le numéro et l'opérateur auprès du destinataire (format international, compte mobile money actif), puis relancer avec un nouvel order_id. |
LIMIT_EXCEEDED | Plafond dépassé | Plafond du compte ou de l'opérateur | Découper le montant en plusieurs transactions sous le plafond, ou faire relever le plafond du compte auprès de l'opérateur. Un plafond journalier se réinitialise sous 24 h. |
PROVIDER_ERROR | Erreur opérateur | Opérateur mobile money ou prestataire de paiement | Rien à corriger de votre côté : relancer avec un nouvel order_id. Si l'échec se répète sur le même opérateur, consulter failureReason/failureCode et le journal des tentatives sur GET /v1/transactions/by-id/{transactionId} avant d'escalader ; le support az54 reste le recours si ça ne suffit pas à trancher. |
TIMEOUT | Délai dépassé | Réseau de l'opérateur | Confirmer le statut réel avant toute relance (GET /v1/transactions/by-id/{transactionId}, authentifié) : l'opérateur a pu débiter le client sans nous répondre. Ne relancer qu'une fois confirmé. |
CANCELLED_BY_USER | Annulé par le payeur | Payeur | Le payeur a refusé la demande ou n'a pas saisi son code PIN à temps. Le recontacter, puis relancer le paiement avec un nouvel order_id. |
UNKNOWN | Raison non déterminée | Non identifiée | L'opérateur n'a pas renvoyé de motif exploitable. Consulter le détail technique et le journal des tentatives sur GET /v1/transactions/by-id/{transactionId} ; transmettre l'order_id au support az54 seulement si ça ne suffit pas à trancher. |
PROVIDER_UNAVAILABLE | Prestataire indisponible | Système du prestataire de paiement | Rien à corriger de votre côté : le prestataire a déclaré son système indisponible avant toute sollicitation du payeur, et az54 rejoue l'encaissement chez un autre prestataire du même canal quand il en existe un. Ne relancer qu'après une notification d'échec définitif, avec un nouvel order_id. |
PROVIDER_NOT_AUTHORIZED | Habilitation PSP manquante | Configuration du compte az54 chez le prestataire de paiement | Ne pas relancer : l'échec est persistant, un nouvel order_id échouera à l'identique tant que la configuration n'est pas corrigée côté az54/PSP. Contacter le support az54 avec l'order_id. |
PROVIDER_INSUFFICIENT_LIQUIDITY | Demande prise en charge par le support | Réserve de décaissement az54 chez le prestataire de paiement | Rien à corriger de votre côté : votre solde az54 n'est pas en cause et le montant y a été recrédité. Votre demande est prise en charge, le support az54 revient vers vous dès que le versement peut être relancé. Ne relancez pas d'ici là. |
Ce catalogue est la source de vérité : c'est le même qui s'affiche dans le
tableau de bord marchand et le backoffice az54. Il est aussi servi par
GET /v1/transactions/failure-categories (public, sans authentification),
pour l'obtenir par API plutôt que de le recopier à la main.
failureReason et failureCode : le détail brut, pas contractuel
failureReason porte le message du PSP tel quel — parfois en anglais, avec son
propre vocabulaire ("1006: Insufficient balance").
failureCode en isole la partie code, quand le PSP en fournit une :
{
"failureCategory": "INSUFFICIENT_FUNDS",
"failureReason": "1006: Insufficient balance",
"failureCode": "1006"
}Il est machine-lisible, mais pas plus contractuel que failureReason :
chaque PSP a son propre référentiel de codes, az54 ne s'engage que sur
failureCategory. Servez-vous-en pour vos tickets support et vos statistiques
internes, pas pour brancher votre logique métier.
Il est absent (null, ou omis du corps du webhook) dans deux cas :
- le PSP n'a renvoyé qu'un message, sans code. Le code n'est jamais re-dérivé
de
failureReasonen coupant sur": "— ce séparateur apparaît aussi à l'intérieur des messages ; - la transaction est antérieure au 2026-08-28. Ces échecs gardent leur
failureReasoncomplet, code fusionné compris ; il n'existe aucun moyen fiable de les rétro-remplir.
failedAt : quand l'échec a eu lieu
Les deux lectures par clé d'API — GET /v1/transactions et
GET /v1/transactions/by-id/{transactionId} — servent failedAt, l'instant où
la transaction est passée en failed ou cancelled. Il vaut null tant
qu'elle n'a pas échoué.
Ne le confondez pas avec updatedAt, qui suit la dernière écriture sur la
transaction, quelle qu'elle soit.
Sur les transactions antérieures au 2026-08-28, failedAt a été rempli
depuis updatedAt : c'est une approximation de l'instant réel de l'échec,
exacte tant que la ligne n'a pas été retouchée après coup.
