Webhooks
PayRouter résout les paiements de façon asynchrone. Lorsqu'un fournisseur confirme le
résultat, PayRouter fait progresser la transaction dans sa machine à états et envoie en POST
l'état final à votre callback_url.
Prerequisites
- Un point de terminaison HTTPS public qui accepte des
POSTavec un corps JSON. - La
callback_urldéfinie lorsque vous créez une transaction. - Pas encore de point de terminaison public ? Vous pouvez quand même développer — interrogez
GET /api/payments/transaction/<reference>/et ajoutez le webhook avant le passage en production.
Votre callback marchand
Fournissez callback_url sur la transaction. Lorsque le paiement atteint un état
terminal, PayRouter envoie :
POST https://your-app.com/payments/webhook
Content-Type: application/json
{
"reference": "9F3A1C2D4E5B6A7C8D9E0F1A2B3C4D5E",
"merchant_reference": "INV-2026-0001",
"transaction_status": "Success",
"amount": "100.00",
"currency": "CDF",
"customer_number": "0810000000",
"provider_reference": "FP-558213007"
}
Champs du callback
| Champ | Description |
|---|---|
reference | L'identifiant de transaction de PayRouter — votre clé d'idempotence. |
merchant_reference | La référence que vous avez fournie à la création. |
transaction_status | Statut terminal : Success ou Failed. |
amount / currency | Le montant et la devise réglés. |
customer_number | Le numéro du payeur. |
provider_reference | L'identifiant de transaction du fournisseur. |
Répondre correctement
Renvoyez 200 OK rapidement. Effectuez le travail lourd de façon asynchrone — un gestionnaire
lent peut provoquer des relances.
app.post("/payments/webhook", express.json(), async (req, res) => {
const { reference, transaction_status } = req.body;
// 1. Acknowledge immediately.
res.sendStatus(200);
// 2. Idempotency: skip if we've already finalized this reference.
if (await alreadyProcessed(reference)) return;
// 3. Re-fetch the authoritative status before releasing goods/funds.
const txn = await getTransaction(reference); // GET .../transaction/<reference>/
if (txn.transaction_status === "Success") {
await fulfillOrder(reference);
}
});
Les webhooks sont idempotents — et les montants ne font pas foi
Vous pouvez recevoir le même callback plus d'une fois. Basez tout votre traitement sur
reference afin que les doublons soient sans effet. Et avant de libérer des biens ou des fonds,
ré-interrogez GET /api/payments/transaction/<reference>/ pour confirmer le statut et le montant
faisant autorité — ne faites jamais confiance aveuglément au corps du webhook.
Relances
Si votre point de terminaison ne renvoie pas 2xx, PayRouter retente la livraison. Rendez votre
gestionnaire idempotent et rapide. Vous pouvez toujours rapprocher les callbacks manqués en interrogeant
la transaction ou en listant les transactions récentes (voir Rapports).
Webhooks Fournisseur → PayRouter (entrants)
Pour être complet : les fournisseurs appellent ces points de terminaison de PayRouter. Vous ne les
appelez pas. Chacun est vérifié cryptographiquement et échoue de façon fermée (401) sur une
mauvaise signature — PayRouter ne fait jamais confiance à un callback non vérifié.
| Fournisseur | Point de terminaison | Vérification |
|---|---|---|
| FreshPay | POST /api/callbacks/freshpay/ | AES-128-CBC + HMAC-SHA256 |
| Unipesa | POST /api/callbacks/unipesa/ | HMAC-SHA512 |
Les callbacks vérifiés sont stockés sous forme d'enregistrements RawCallback et traités
de façon asynchrone, ce qui déclenche alors votre callback marchand ci-dessus.
Les administrateurs peuvent auditer chaque callback entrant dans le portail d'administration.