Passer au contenu principal
Payfonte envoie des événements webhook lorsque le statut d’un décaissement change, par exemple processing, success ou failed. Utilisez les webhooks comme canal principal de mise à jour asynchrone des décaissements.

Champs du payload

ChampDescription
eventType d’événement, par exemple disbursement.status
clientIdVotre identifiant client Payfonte
data.statusStatut du décaissement, processing, success ou failed
data.statusDescriptionDétail de statut côté provider ou plateforme
data.referenceRéférence Payfonte du décaissement
data.externalReferenceRéférence fournie par le marchand
data.providersReferenceRéférence côté provider
data.amountMontant du décaissement en sous-unités
data.chargeFrais de décaissement appliqués
data.providerSlug provider utilisé
data.transferRecipientIdIdentifiant du bénéficiaire
data.transferRecipientLabelLibellé lisible du bénéficiaire
deliveryIdIdentifiant de livraison du webhook
Exemple de payload :
disbursement.status
{
  "event": "disbursement.status",
  "clientId": "payfusion",
  "data": {
    "clientId": "payfusion",
    "type": "disbursement",
    "status": "success",
    "statusDescription": "Disbursement was successful",
    "reference": "L20250614142024AAAAA",
    "providersReference": "reference-from-mno",
    "externalReference": "merchant-reference",
    "currency": "XOF",
    "country": "BJ",
    "transferRecipientId": "684d561743dac722bcd43e9f",
    "transferRecipientLabel": "MTN MoMo | 2290123456789",
    "charge": 180,
    "amount": 10000,
    "provider": "mtn-momo-benin",
    "providerLabel": "MTN MoMo",
    "providerLogo": "https://payfonte.s3.amazonaws.com/mtn-momo.png",
    "channel": "mobile-money",
    "timestamp": "2025-06-14T14:20:25.023Z",
    "narration": "disbursement narration here",
    "completedAt": 1749910827
  },
  "deliveryId": "684d852b27e08e60f4d09103"
}

Vérification de signature

Validez l’authenticité du webhook avant tout traitement.
  • En-tête : x-webhook-signature
  • Algorithme : HMAC sha512 du corps de requête avec votre client-secret
const crypto = require("crypto");

app.post("/payfonte/disbursement-webhook", express.json({ type: "*/*" }), (req, res) => {
  const secret = process.env.PAYFONTE_CLIENT_SECRET;
  const payload = JSON.stringify(req.body);
  const expected = crypto.createHmac("sha512", secret).update(payload).digest("hex");
  const received = req.headers["x-webhook-signature"];

  if (expected !== received) {
    return res.status(401).send("Invalid signature");
  }

  res.status(200).send("OK");
  processDisbursementWebhook(req.body);
});

Schéma de traitement sûr

1

Répondre rapidement

Retournez HTTP 200 immédiatement après la validation de base.
2

Vérifier l'idempotence

Dédupliquez via reference + status, ou deliveryId, avant toute action métier.
3

Mettre à jour l'état et le registre interne

Ne marquez le décaissement comme final que pour les statuts terminaux et conservez statusDescription.
4

Vérifier les décaissements critiques

Si besoin, confirmez l’état final via GET /billing/v1/disbursements/verify/{reference}.

Problèmes fréquents

ProblèmeCause probableCorrectif
Traitement webhook en doublePas de gestion idempotenteStockez les couples référence/statut déjà traités et ignorez les répétitions
Signature invalideMauvais secret ou payload modifiéUtilisez le bon client-secret et hashez le corps exact
Mises à jour manquéesTimeout ou réponse non 200Retournez vite 200 et déplacez la logique lourde vers un worker asynchrone
État de décaissement incohérentTraitement hors ordreComparez toujours l’état courant avant d’appliquer une mise à jour

Documentation associée

Vue d'ensemble des décaissements

Flux de décaissement complet et endpoints.

Exemples de décaissement

Exemples de requêtes et réponses.

Mode d'autorisation

Configuration PIN ou URL d’autorisation.