Webhooks
MabiPay envoie des notifications HTTP POST en temps réel à votre notifyUrl à chaque changement de statut.
URL de callback provider
Les providers envoient leurs notifications vers l'endpoint unifié :
POST /api/v1/webhooks/callback/:providerSlug
Exemple BPay : POST /api/v1/webhooks/callback/bpay
Cet endpoint est exclu de la documentation Swagger car il est réservé aux providers.
Structure du payload
{
"event": "payment.success",
"reference": "PAY-20240615-ABCD",
"merchantRef": "ORDER-001",
"status": "success",
"amount": 5000,
"currency": "XOF",
"timestamp": "2024-06-15T14:32:00.000Z",
"metadata": {}
}
Types d'événements
| Événement | Déclencheur |
|---|---|
payment.success | Paiement confirmé et réussi |
payment.failed | Paiement échoué |
payment.expired | Délai de paiement dépassé |
payment.cancelled | Paiement annulé par le client |
payout.success | Virement réussi |
payout.failed | Virement échoué |
Vérification de signature
Chaque requête webhook inclut le header X-MabiPay-Signature (HMAC SHA-256).
import { verifyWebhookSignature } from "@mabipay/sdk";
const isValid = verifyWebhookSignature({
secret: process.env.MABIPAY_WEBHOOK_SECRET,
signature: req.headers["x-mabipay-signature"],
payload: req.body.toString(), // body brut non parsé
});
if (!isValid) throw new Error("Signature invalide");
Corps brut requis
Parsez le corps uniquement après avoir vérifié la signature. Utilisez express.raw() ou l'équivalent de votre framework.
Politique de retry
En cas d'échec (code HTTP ≠ 2xx ou timeout), MabiPay retente la livraison :
| Tentative | Délai |
|---|---|
| 1 | Immédiat |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 heures |
Bonnes pratiques
- Répondre rapidement — retournez un 200 immédiatement et traitez en arrière-plan
- Idempotence — votre endpoint peut recevoir plusieurs fois le même événement
- Vérifier la signature — ne traitez jamais un webhook sans vérification HMAC
- Vérifier le statut via API — pour les paiements critiques, confirmez avec
GET /payments/:ref
// ✅ Bonne pratique : réponse immédiate + traitement async
app.post("/webhooks", async (req, res) => {
res.status(200).json({ received: true }); // Répondre AVANT le traitement
await processWebhookAsync(req.body); // Traiter en arrière-plan
});