Webhooks
À chaque événement, Switag envoie un POST JSON signé à l'URL configurée dans Développeurs → Webhooks.
Événements#
| Événement | Quand |
|---|---|
charge.succeeded | Une charge est payée. |
subscription.charged | Un cycle d'abonnement a été débité. |
subscription.payment_failed | Échec d'un cycle; des échecs répétés peuvent mener à paused. |
subscription.updated | L'offre d'un abonnement a changé. |
subscription.cancelled | Un abonnement a été arrêté. |
bill.paid | Un paiement facturier a été encaissé (après fulfillment si configuré). |
Vérifier la signature#
L'en-tête X-Switag-Signature porte t=<unix>,v1=<hmac> où hmac = HMAC_SHA256(secret_webhook, "<t>.<corps_brut>").
const crypto = require("crypto");
function verify(secret, header, rawBody) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret)
.update(parts.t + "." + rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}Source de vérité
Vérifiez toujours la signature et rejetez un t trop ancien (anti-rejeu). Le webhook — pas le retour navigateur — fait foi pour ouvrir une commande ou prolonger un accès.
Exemple de corps#
{
"type": "charge.succeeded",
"data": {
"charge_token": "AbC123",
"reference": "ORDER-123",
"amount": 5000,
"currency": "XOF",
"transaction_id": "…"
}
}Exemple bill.paid#
{
"type": "bill.paid",
"data": {
"biller_slug": "sodeci",
"product": "water-bill",
"transaction_id": "…",
"amount": 12500,
"currency": "XOF",
"biller_ref": "INV-2026-03-7781",
"form_values": {
"contract_number": "0012457788"
}
}
}Fiabilité#
charge.succeededetsubscription.chargedpassent par le pipeline fiable de livraison et retrysubscription.payment_failed,subscription.updated,subscription.cancelledetbill.paidsont best-effort
Répondez 2xx rapidement. Vous pouvez renvoyer manuellement un webhook de charge via POST /v1/biz/charges/{token}/resend-webhook.