Recevoir les webhooks
Les webhooks sont la manière dont Nex vous notifie en temps réel des événements qui vous concernent (paiements, changements de connexion à venir). Cette page explique le format et les garanties. Pour le code receiver complet, voir le Guide implémentation receiver.
Pourquoi des webhooks
Sans webhook, vous devriez poller GET /orders/<id> toutes les N secondes pour savoir si un paiement a abouti — coûteux côté API, latence variable, mauvaise UX. Le webhook inverse le flux : Nex vous prévient dès que ça arrive, en quelques centaines de millisecondes.
Format générique d'un event
Tous les webhooks Nex partagent la même enveloppe :
{
"event": "order.paid",
"id": "evt-uuid-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"createdAt": "2026-05-24T12:02:34.567Z",
"data": { /* spécifique au type d'event */ }
}| Champ | Type | Description |
|---|---|---|
event | string | Type de l'event (order.paid, autres à venir). |
id | UUID v4 | Identifiant unique de cet event. Clé d'idempotence côté votre receiver (voir plus bas). |
createdAt | ISO 8601 | Moment où Nex a produit l'event. Indicatif. |
data | object | Charge utile spécifique au type. Champs garantis stables (additions non-breaking, retraits via deprecation). |
Headers HTTP envoyés
Chaque POST contient :
POST /votre-endpoint HTTP/1.1
Host: api.maishapay.cd
Content-Type: application/json
User-Agent: Nex-Webhooks/1.0
Nex-Signature: t=1716553354,v1=5e7c1a3f4b8d2c9e6f1a2b3c4d5e6f...
Nex-Event: order.paid
Nex-Event-Id: evt-uuid-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Content-Length: 287| Header | À quoi ça sert |
|---|---|
Nex-Signature | HMAC SHA256 du payload. Vous devez vérifier, sinon n'importe qui peut forger. |
Nex-Event | Type — utile pour router rapidement vers le bon handler sans parser le body. |
Nex-Event-Id | UUID v4 identique au id du body. Clé d'idempotence. |
User-Agent | Préfixé Nex-Webhooks/ — utile pour filtrer côté WAF. |
Catalogue des events V1
| Event | Quand | Payload data |
|---|---|---|
order.paid | Un de vos QR a été payé par un client final. Émis après le commit ledger (l'argent est arrivé). | { orderId, transactionId, amount, currencyCode, paidAt, externalReference?, description? } |
D'autres events viendront (connection.activated, connection.revoked_by_merchant, order.expired…). Quand un nouveau type est ajouté, votre receiver doit l'ignorer proprement (retourner 200 même si vous ne savez pas le traiter), pas crasher.
Garanties Nex
| Garantie | Détail |
|---|---|
| At-least-once delivery | Si votre endpoint répond >= 400 ou timeout, Nex retry 7 fois avec backoff exponentiel (30s → 32 min, ~1h cumulé). |
| Pas de doublon côté Nex | Un event est dispatch au plus une fois pendant 24h grâce à un dedup store Redis. |
| Signature HMAC SHA256 | Garantit l'authenticité (vient de Nex) et l'intégrité (payload non altéré). |
| Pas de garantie d'ordre | Si 2 commandes sont payées dans la même seconde, les webhooks peuvent arriver dans un ordre quelconque. Si l'ordre importe, utilisez paidAt. |
Pas de garantie exactly-once
Le dedup côté Nex échoue fail-open si Redis est HS — vous pourriez recevoir un doublon en cas d'incident infra. C'est votre receiver qui doit garantir l'idempotence finale via Nex-Event-Id. Voir le guide receiver.
Les 4 règles vitales côté votre receiver
Si vous ne respectez qu'une seule chose, respectez ces 4 points. Le guide receiver donne le code complet.
1. Vérifier la signature HMAC
expected = HMAC_SHA256(webhook_secret, "${t}.${rawBody}")t et v1 sont parsés du header Nex-Signature. Comparez en temps constant (crypto.timingSafeEqual en Node, équivalent dans votre langage) — jamais === (timing attack).
2. Rejeter les requêtes anciennes (anti-rejeu)
Si now - t > 5 min, retournez 400. Empêche un attaquant qui aurait capturé une requête signée valide de la rejouer plus tard.
3. Idempotence via Nex-Event-Id
Maintenez une table webhook_events_seen(event_id PK, processed_at). INSERT ... ON CONFLICT DO NOTHING : si l'event_id existe déjà, vous ignorez et retournez 200.
4. Répondre 200 rapidement
Timeout Nex = 5 secondes. Si votre logique métier (déblocage marchandise, impression ticket…) prend plus, enqueue le payload en interne et acquittez 200 immédiatement.
Tester votre receiver localement
En développement, utilisez un tunnel HTTPS public pour exposer votre endpoint local :
- ngrok :
ngrok http 3000→ URLhttps://xxxx.ngrok-free.appà transmettre à Nex - Cloudflare Tunnel :
cloudflared tunnel --url http://localhost:3000
L'équipe Nex peut mettre à jour temporairement votre webhook URL côté CMMS pour pointer vers votre tunnel. Ne pas pointer le webhook prod vers un tunnel personnel : utilisez une intégration staging dédiée.
Voir aussi
- Guide implémentation receiver — code Express et Fastify
- Cycle de vie — rotation du
webhook_secret - Référence API — détail par event