Gérer les remboursements
Construisez un processus de support qui rembourse en toute sécurité, assure le suivi et effectue le rapprochement.
Émettre un remboursement demande un seul appel d'API. La page Remboursements documente chaque champ. Elle ne peut toutefois pas vous dire quand votre agent de support peut appuyer sur le bouton, ce que le client doit voir pendant l'attente ni quoi faire lorsque l'argent ne revient pas.
C'est l'objet de ce guide. Une erreur de remboursement vous coûte de l'argent réel. L'ordre des étapes est donc particulièrement important.
Un remboursement est un objet distinct avec son propre résultat
Le modèle mental habituel est incorrect et provoque des bugs. Un remboursement n'est pas un état du paiement. Il possède son propre enregistrement, identifiant, statut et ses propres webhooks. Il peut échouer alors que le paiement remboursé conserve définitivement le statut succeeded.
Vous devez donc suivre deux éléments, pas un seul. Le paiement répond à la question « le client a-t-il payé ? ». Le remboursement répond à « avons-nous restitué l'argent ? ». La réponse à la seconde question peut être no longtemps après que vous avez répondu yes au client.
1. Déterminer le montant encore remboursable
Vous pouvez rembourser le montant total, une partie ou plusieurs parties jusqu'à épuisement du montant initial. Le montant autorisé correspond au montant du paiement moins tous les remboursements déjà effectués.
Les remboursements en attente sont également comptabilisés. Un remboursement enregistré, mais pas encore réglé, réserve déjà sa part. Deux agents de support ne peuvent donc pas rembourser deux fois le même argent.
Ne calculez pas ce total uniquement à partir de vos propres tables. Interrogez Wajub, qui connaît aussi les remboursements effectués depuis le Dashboard.
https://api.wajub.com/payments/{id}/refundscurl https://api.wajub.com/payments/trx_test_CSUGajfv9xh0XQ5wu2lx/refunds \
-H "Authorization: sk_test.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…"Affichez ce montant à votre agent avant toute saisie. La plupart des incidents de remboursement ne sont pas des erreurs d'API. Ils viennent d'une personne qui rembourse 5 000 XAF sur une commande dont 3 000 XAF ont déjà été remboursés.
L'API effectue aussi cette vérification et explique son refus
Si votre calcul est incorrect, POST /refunds répond avec une erreur 422 qui précise la limite, par exemple Refund amount exceeds refundable amount (2 000.00 XAF). Affichez ce message tel quel à l'agent plutôt qu'un échec générique.
2. Créer le remboursement
Le remboursement désigne le paiement par son id Wajub. Votre propre référence de commande n'est pas acceptée ici, car la recherche porte uniquement sur l'identifiant du paiement.
reason est obligatoire et provient d'une liste fermée. Ce n'est pas du texte libre. Choisir la valeur exacte est important lorsque vous analysez un mois de remboursements.
| Raison | Cas d'utilisation |
|---|---|
requested_by_customer | Le client a changé d'avis, aucun problème n'est survenu |
duplicate | Le même achat a été débité deux fois |
fraudulent | Le paiement n'a pas été effectué par le titulaire du compte |
product_not_received | Les marchandises ne sont jamais arrivées |
service_not_delivered | Un service a été payé, mais pas fourni |
wrong_amount | Vous avez débité un montant incorrect |
merchant_error | Toute autre erreur de votre part |
network_error | Le paiement a réussi, mais votre système ne l'a pas enregistré |
transaction_error | Un échec technique est survenu pendant le paiement |
customer_complaint | Un geste commercial après une réclamation acceptée |
reconciliation | Une correction comptable |
https://api.wajub.com/refundscurl https://api.wajub.com/refunds \
-H "Authorization: sk_test.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…" \
-H "Idempotency-Key: refund-order-4172-1" \
-H "Content-Type: application/json" \
-d '{
"payment": "trx_test_CSUGajfv9xh0XQ5wu2lx",
"amount": 2000,
"reason": "product_not_received",
"reference": "order-4172-partial"
}'Omettez entièrement amount pour rembourser tout le montant restant. Wajub calcule lui-même le reste, ce qui est plus sûr que d'envoyer une valeur calculée quelques instants auparavant.
Cet endpoint exige votre clé secrète
POST /refunds est protégé par la clé privée. Une clé publique est refusée avec 406 Not Acceptable et le message Private Key Required. Aucune version de cet appel ne doit se trouver dans un navigateur ou une application mobile.
La clé d'idempotence vous protège contre un double clic de l'agent. Créez-la à partir de la tentative de remboursement, pas de la commande, car une commande peut légitimement faire l'objet de plusieurs remboursements. La même clé renvoie le remboursement initial pendant vingt-quatre heures. La même clé associée à un payload différent est refusée.
3. Informer honnêtement le client que le remboursement est en cours
La réponse revient avec 201 Created et le remboursement possède le statut pending. Aucun argent n'a encore été déplacé.
Notez le nom du champ : le paiement se trouve dans transaction, pas dans payment. La valeur reference correspond à celle envoyée. Utilisez-la pour retrouver votre propre enregistrement.
Le crédit d'un portefeuille Mobile Money prend de quelques minutes à quelques heures selon l'opérateur. Dites-le clairement. Un message de support qui promet un remboursement immédiat entraîne un second contact du même client vingt minutes plus tard.
4. Suivre le remboursement jusqu'à son résultat
Trois webhooks terminent le processus. Ils constituent le seul moyen fiable de connaître le résultat. Ils arrivent sur l'endpoint déjà construit dans Accepter un paiement, guide complet. Vous n'avez donc aucune nouvelle vérification à créer, seulement une branche à ajouter.
Le champ data de chacun contient le remboursement lui-même. data.id correspond donc au remboursement, data.transaction au paiement concerné et data.reference à la valeur que vous avez définie.
| Événement | Action du worker |
|---|---|
refund.created | L'enregistrer dans les logs. Utile pour un historique d'audit, jamais comme déclencheur |
refund.succeeded | Marquer votre enregistrement comme réglé, fermer le ticket et informer le client |
refund.failed | Lire data.failure_reason, rouvrir le ticket et ne pas réessayer automatiquement |
Ne marquez pas votre propre enregistrement comme remboursé avant le webhook
Si votre base de données considère qu'un remboursement échoué a réussi, personne ne réclamera jamais le montant manquant. Un remboursement devient réel uniquement à la réception de refund.succeeded.
5. Lorsqu'un remboursement échoue
Un remboursement échoué ne doit pas déclencher une boucle de nouvelles tentatives. Vous détenez toujours l'argent, le client attend toujours son remboursement et un élément du parcours a refusé l'opération. Une nouvelle tentative automatique échoue généralement au même endroit.
Présentez le cas à une personne en joignant la raison.
failure_reason | Signification habituelle |
|---|---|
insufficient_funds | Les fonds disponibles chez le prestataire étaient insuffisants. Réessayez plus tard, une fois |
transaction_already_reversed | Une personne a déjà effectué le remboursement. Vérifiez le Dashboard avant d'agir |
refund_rejected_by_issuer | Refus de l'émetteur de la carte. Le client doit être remboursé autrement |
network_error | Erreur temporaire. Une nouvelle tentative est raisonnable |
provider_error | Le prestataire rencontre un problème. Attendez, puis réessayez une fois |
Une nouvelle tentative crée un nouveau remboursement avec une nouvelle clé d'idempotence, comme pour un paiement. La réutilisation de la clé de la tentative échouée renvoie ce remboursement échoué sans rien déclencher.
6. Effectuer le rapprochement en fin de mois
Deux éléments permettent de rapprocher les remboursements avec votre comptabilité sans fouiller dans des feuilles de calcul.
Le premier est reference. Définissez cette valeur sur chaque remboursement pour pointer vers votre propre enregistrement, comme pour les paiements. Elle est renvoyée à chaque lecture et constitue le seul champ qui vous appartient à survivre à l'aller-retour.
Le second est l'instantané du registre présent sur chaque remboursement. Un remboursement réussi contient le solde avant et après son application, dans la devise du solde.
Ces deux valeurs permettent de reconstituer l'ordre des opérations sur votre solde sans faire de suppositions à partir des horodatages. C'est indispensable lorsque deux remboursements sont réglés pendant la même seconde.
7. Vérifier la gestion des échecs
La sandbox possède un numéro conçu pour ce cas précis. Un paiement effectué avec un numéro qui se termine par 000009 réussit normalement, puis tous ses remboursements échouent.
| Numéro | Paiement | Remboursement |
|---|---|---|
+237670000000 | Réussite | Réussite |
+237670000009 | Réussite | Échec systématique |
Payez donc avec +237670000009, remboursez le paiement et observez l'exécution réelle de votre branche refund.failed. C'est le seul moyen fiable de tester le parcours le plus important, et celui que personne ne teste avant la production.
Pages associées
- RemboursementsL'endpoint, chaque champ et chaque motif de refus.
- Accepter un paiement, guide completLe cycle de vie de la commande qu'un remboursement annule.
- LitigesCe qui se passe lorsque le client contacte l'opérateur au lieu de vous.
- IdempotencePourquoi la clé de remboursement correspond à une tentative et non à une commande.
- Scénarios de testLa table complète des résultats de la sandbox.