PayRouterDocs

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 POST avec un corps JSON.
  • La callback_url dé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 :

http
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

ChampDescription
referenceL'identifiant de transaction de PayRouter — votre clé d'idempotence.
merchant_referenceLa référence que vous avez fournie à la création.
transaction_statusStatut terminal : Success ou Failed.
amount / currencyLe montant et la devise réglés.
customer_numberLe numéro du payeur.
provider_referenceL'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.

Express
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);
  }
});
Flask
@app.post("/payments/webhook")
def webhook():
    data = request.get_json()
    reference = data["reference"]

    if already_processed(reference):       # idempotency guard
        return "", 200

    # Re-fetch the authoritative status before fulfilling.
    txn = get_transaction(reference)       # GET .../transaction/<reference>/
    if txn["transaction_status"] == "Success":
        fulfill_order(reference)
    return "", 200
PHP
<?php
$data = json_decode(file_get_contents("php://input"), true);
$reference = $data["reference"];

http_response_code(200); // acknowledge first

if (already_processed($reference)) exit;

$txn = get_transaction($reference); // GET .../transaction/<reference>/
if ($txn["transaction_status"] === "Success") {
    fulfill_order($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é.

FournisseurPoint de terminaisonVérification
FreshPayPOST /api/callbacks/freshpay/AES-128-CBC + HMAC-SHA256
UnipesaPOST /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.