Aller au contenu

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.

GEThttps://api.wajub.com/payments/{id}/refunds
curl 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.

RaisonCas d'utilisation
requested_by_customerLe client a changé d'avis, aucun problème n'est survenu
duplicateLe même achat a été débité deux fois
fraudulentLe paiement n'a pas été effectué par le titulaire du compte
product_not_receivedLes marchandises ne sont jamais arrivées
service_not_deliveredUn service a été payé, mais pas fourni
wrong_amountVous avez débité un montant incorrect
merchant_errorToute autre erreur de votre part
network_errorLe paiement a réussi, mais votre système ne l'a pas enregistré
transaction_errorUn échec technique est survenu pendant le paiement
customer_complaintUn geste commercial après une réclamation acceptée
reconciliationUne correction comptable
POSThttps://api.wajub.com/refunds
curl 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.

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é.

Response · 201 Created
{
"code": 201,
"status": "Created",
"message": "Refund created successfully",
"refund": {
"id": "ref_test_9xh0XQ5wu2lxCSUGajfv",
"transaction": "trx_test_CSUGajfv9xh0XQ5wu2lx",
"reference": "order-4172-partial",
"amount": 2000,
"currency": "XAF",
"status": "pending",
"reason": "product_not_received",
"sandbox": true,
"created_at": "2026-09-13T09:12:00Z"
}
}

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énementAction du worker
refund.createdL'enregistrer dans les logs. Utile pour un historique d'audit, jamais comme déclencheur
refund.succeededMarquer votre enregistrement comme réglé, fermer le ticket et informer le client
refund.failedLire data.failure_reason, rouvrir le ticket et ne pas réessayer automatiquement

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_reasonSignification habituelle
insufficient_fundsLes fonds disponibles chez le prestataire étaient insuffisants. Réessayez plus tard, une fois
transaction_already_reversedUne personne a déjà effectué le remboursement. Vérifiez le Dashboard avant d'agir
refund_rejected_by_issuerRefus de l'émetteur de la carte. Le client doit être remboursé autrement
network_errorErreur temporaire. Une nouvelle tentative est raisonnable
provider_errorLe 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.

Contenu d'un remboursement réglé
{
"id": "ref_9xh0XQ5wu2lxCSUGajfv",
"transaction": "trx_CSUGajfv9xh0XQ5wu2lx",
"reference": "order-4172-partial",
"amount": 2000,
"currency": "XAF",
"status": "succeeded",
"ledger_balance_currency": "XAF",
"ledger_balance_before": 412500,
"ledger_balance_after": 410500
}

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éroPaiementRemboursement
+237670000000RéussiteRéussite
+237670000009Ré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.

Que pensez-vous de ce contenu ?