Aller au contenu
Chargement des API keys…

Motifs d'échec

Lire failure_reason sur un paiement, un transfert ou un remboursement échoué.

Un 4xx signifie que la requête a été refusée. Cette page traite de l'autre type d'échec : une requête qui a réussi, a créé une ressource, puis a échoué plus tard chez l'opérateur. L'objet et son webhook portent deux champs qui l'expliquent.

failure_reasonstringfacultatif
Un code court, lisible par une machine. C'est sur ce champ qu'il faut aiguiller votre code. Présent uniquement une fois que l'objet atteint status: "failed".
failure_messagestringfacultatif
Une phrase destinée à un humain. Affichez-la dans votre back-office, journalisez-la, mais n'aiguillez jamais votre code dessus. Reprend le code quand le prestataire n'a fourni aucun texte.

Où il apparaît

Un transfert échoué
{
"id": "po_225KMKULRQqYvlhrgUOE",
"status": "failed",
"amount": 100000,
"currency": "XAF",
"reason": "Salaire septembre",
"failure_reason": "invalid_recipient",
"failure_message": "Beneficiary has no payout channel."
}

Cette même paire est livrée dans les webhooks payment.failed, transfer.failed et refund.failed, puisque ces payloads sont la ressource elle-même.

ObjetChamps portésPrésents quand
Transfertfailure_reason, failure_messageTant que status vaut failed ou cancelled.
Remboursementfailure_reason, failure_messageUniquement tant que status vaut failed.
Paiementfailure_reason, failure_messageUniquement tant que status vaut failed. Voir Paiements échoués.

Codes émis par Wajub

Ces codes sont stables et la liste est exhaustive.

Transferts

failure_reasonCe qui s'est passéQue faire
team_restrictedLes payouts sont suspendus sur votre compte.Contactez le support. Ne relancez pas.
invalid_recipientLe bénéficiaire est absent, ou n'a aucun canal de payout.Corrigez le bénéficiaire, puis créez un nouveau transfert.
no_providerAucun prestataire n'est éligible pour ce canal et ce montant pour le moment.Réessayez plus tard, ou passez par un autre canal.
provider_rejectedTous les prestataires éligibles ont refusé le payout.Lisez failure_message, corrigez, puis créez-en un nouveau.
admin_rejectedWajub l'a refusé après examen.Contactez le support. Ne relancez pas.
merchant_rejectedVous l'avez rejeté depuis le Dashboard avant son envoi.Rien : vous l'avez arrêté. Ne relancez pas.
cancelled_by_wajubWajub l'a annulé avant son envoi.Contactez le support. Ne relancez pas.
expiredIl n'a pas été envoyé dans le délai de transfert de votre compte (réglages des transferts) : il a été annulé et son montant rendu à votre solde. Live uniquement.Rien à défaire : créez un nouveau transfert s'il est toujours dû.

Les trois derniers accompagnent un payout cancelled.

Remboursements

Un remboursement échoué porte toujours l'un de ces codes, en live comme en sandbox. failure_message garde la phrase du fournisseur quand il en a envoyé une.

failure_reasonCe qui s'est passéQue faire
insufficient_fundsLe fournisseur n'a pas pu financer le remboursement.Réessayez plus tard, une fois.
refund_rejected_by_issuerUn émetteur de carte l'a refusé.Remboursez le client autrement.
transaction_already_reversedLe paiement était déjà annulé ou remboursé chez le fournisseur.Vérifiez le Dashboard avant d'agir.
invalid_numberLe numéro ou le compte du payeur ne peut pas le recevoir. Live uniquement.Demandez au client un autre moyen de le rembourser.
limit_exceededUn plafond du compte destinataire ou du fournisseur a été atteint. Live uniquement.Réessayez plus tard, une fois.
network_errorLe fournisseur n'a pas pu être joint à temps.Une nouvelle tentative est raisonnable.
provider_errorLe fournisseur était indisponible, ou a refusé l'appel lui-même.Attendez, puis réessayez une fois.
provider_declinedLe fournisseur l'a refusé sans dire pourquoi.Lisez failure_message, puis décidez.
ops_failedWajub n'a pas pu mener le remboursement à bien. Live uniquement.Contactez le support avant de rembourser à nouveau.
cancelled_by_wajubWajub a annulé le remboursement avant de l'envoyer : rien n'a quitté votre solde. Le remboursement est cancelled. Live uniquement.Remboursez de nouveau si le client attend toujours son argent.

Un remboursement peut rester pending ou processing un moment ; failed est définitif. Une nouvelle tentative est un nouveau remboursement.

Paiements échoués

Un paiement échoué porte la raison de sa dernière tentative échouée, sur le paiement et dans le webhook payment.failed :

failure_reasonQuand
Le code du prestataireLe prestataire a refusé la tentative avec un code. Il est transmis tel quel, voir Codes émis par l'opérateur, et failure_message porte la phrase du prestataire.
payment_failedLa tentative a échoué sans code du prestataire. failure_message vaut The payment could not be completed.
declined, authentication_failedSandbox uniquement, voir La sandbox est déterministe.

Les paiements échoués plus anciens peuvent ne porter aucun de ces deux champs.

Le status distingue toujours les trois cas qui changent la suite :

statusCe qui s'est passé
failedL'opérateur a refusé le débit.
expiredLe client n'a pas validé à temps et la session a expiré.
cancelledLe client, ou votre propre DELETE /payments/{id}, l'a arrêté.

Pour chaque tentative, pas seulement la dernière, ouvrez le paiement dans Konsole : sa chronologie montre chaque tentative, le prestataire concerné et la réponse brute de la passerelle.

Codes émis par l'opérateur

Tout ce qui ne figure pas dans les tableaux ci-dessus est le code d'erreur propre au prestataire, transmis sans modification. Les opérateurs ne partagent aucun vocabulaire : MTN, Orange, Wave et un acquéreur de cartes nomment chacun différemment la même erreur du client, et un nouveau code peut apparaître à tout moment.

const RETRY_NEVER = new Set([
'team_restricted',
'admin_rejected',
'merchant_rejected',
'invalid_recipient',
]);

function onTransferFailed(transfer) {
const reason = transfer.failure_reason;

if (RETRY_NEVER.has(reason)) {
  return escalateToSupport(transfer);
}

if (reason === 'no_provider') {
  return scheduleRetry(transfer, { in: '15m' });
}

return notifyMerchant(transfer.failure_message);
}

La sandbox est déterministe

La sandbox n'appelle aucun opérateur : le numéro, le compte ou l'adresse de test que vous utilisez décide du résultat, ce qui permet d'exercer volontairement chaque branche de votre handler avant de passer en live. Les tables des numéros de test se trouvent dans Scénarios de test.

Transferts. Les six derniers chiffres d'un numéro de test Mobile Money (et le suffixe d'un compte bancaire ou d'une adresse crypto de test) choisissent le résultat. Un transfert qui échoue porte l'une de ces trois raisons :

SuffixeRésultatfailure_reason
000000succeeded
000001Refusé aussitôt par un 422, error_code: insufficient_funds ; aucun transfert n'est créé.
000002failedfailure
000003failednetwork_error
000004failedinvalid_recipient

Un numéro qui n'est pas un numéro de test est refusé par un 422 qui liste ceux à utiliser.

Remboursements. Le moyen de paiement du paiement remboursé décide : un paiement Mobile Money réglé avec un numéro finissant par 000000 est remboursé, un numéro finissant par 000001 à 000004 ou par 000009 échoue ; la carte 4000 0000 0000 5423 est débitée mais ses remboursements échouent toujours ; les paiements par banque et crypto sont toujours remboursés. Seul un paiement sans moyen de paiement rattaché n'a pas d'issue fixée. Le failure_reason d'un remboursement sandbox échoué est tiré de la même liste qu'en live, dans Remboursements.

Paiements. Les six derniers chiffres du numéro de test décident du statut : 000002 se termine en failed, 000003 en expired, 000004 en cancelled. 000001 est refusé aussitôt par un 402 avec error_code: insufficient_funds, comme un refus en live, et le paiement reste pending. Un paiement sandbox qui échoue en donne la raison avec une paire fixe, choisie par le numéro ou la carte de test utilisé :

Scénariofailure_reasonfailure_message
Échec (…000002, une carte refusée)declinedThe operator declined the payment.
L'émetteur refuse après 3D SecuredeclinedThe issuer declined the card after authentication.
Le payeur échoue au 3D Secureauthentication_failedThe payer did not pass 3D Secure authentication.

Un échec n'est pas toujours la fin

Un no_provider ou une erreur réseau laisse votre solde intact : la réservation est libérée quand le transfert échoue. Vérifiez le solde plutôt que de le supposer, puis créez un nouveau transfert avec une nouvelle clé d'idempotence.

Que pensez-vous de ce contenu ?