Skip to Content

Webhooks

Les événements Payment Session peuvent inclure un objet data.pricing figé avec la session : montant original, total payeur, répartition des frais et net commercial. data.amount reste le total attendu du paiement. Une intégration ayant déclaré pricing_version: 1 doit distinguer ce total du prix de commande dans data.pricing.original_amount. Les nouvelles tentatives de livraison ne recalculent jamais le tarif courant. Ni une cotation ni une livraison webhook ne constituent une disponibilité financière. Les coûts provider/réseau et la marge interne Kadryza ne sont pas exposés dans ces payloads publics.

Kadryza envoie un POST signé à vos endpoints quand un paiement atteint un état qui compte. C’est le canal de référence pour mettre à jour vos commandes.

Les endpoints de configuration utilisent X-API-Key avec la portée manage_webhooks, et sont limités à 20 modifications par 5 minutes. Vous pouvez aussi les gérer depuis le dashboard.

Configurer un endpoint

Créer un endpoint

POST/v1/webhooksX-API-Key
Requête
{ "url": "https://merchant.example.com/webhooks/kadryza" }
201 Created
{ "id": "d0fb9df9-0a9b-4c2f-84f8-e28d1a2b1e7c", "url": "https://merchant.example.com/webhooks/kadryza", "secret": "5f0c8e3a…(64 caractères hexadécimaux)", "is_active": true, "created_at": "2026-06-05T12:00:00Z" }
🔐

secret n’est renvoyé qu’à la création. Stockez-le immédiatement dans un secret serveur. Si vous le perdez, créez un nouvel endpoint et retirez l’ancien.

Règles sur l’URL (400 WEBHOOK_URL_UNSAFE sinon) :

  • elle doit résoudre vers une adresse publique : localhost, adresses privées et réseaux internes sont refusés, y compris en test ;
  • HTTPS est obligatoire pour les livraisons live ; HTTP n’est toléré que pour les événements de test ;
  • les redirections ne sont suivies que si elles conservent la méthode POST.

Lister les endpoints

GET/v1/webhooksX-API-Key
200 OK
{ "endpoints": [ { "id": "d0fb9df9-0a9b-4c2f-84f8-e28d1a2b1e7c", "url": "https://merchant.example.com/webhooks/kadryza", "is_active": true, "created_at": "2026-06-05T12:00:00Z" } ] }

Activer ou désactiver un endpoint

PATCH/v1/webhooks/:idX-API-Key
Requête
{ "is_active": false }

Un endpoint désactivé ne reçoit plus rien ; les livraisons prévues pour lui sont marquées skipped. Renvoie l’endpoint mis à jour.

Retirer un endpoint

DELETE/v1/webhooks/:idX-API-Key

Renvoie 204. L’endpoint disparaît de la liste et ne reçoit plus rien ; l’historique des livraisons est conservé.

Tester un endpoint

POST/v1/webhooks/:id/testX-API-Key

Envoie immédiatement un événement transaction.test signé, avec X-Kadryza-Test: true. Réponse 200 {"status":"ok"} si votre endpoint a répondu 2xx, sinon 502 WEBHOOK_TEST_FAILED.

Événements

ÉvénementEnvoyé quand
payment_session.succeededUne Payment Session passe en SUCCESS
payment_session.under_reviewUne Payment Session passe en UNDER_REVIEW : paiement à vérifier, ou issue inconnue après l’échéance. Jamais transformé automatiquement en succès.
payment_session.expiredUne Payment Session test passe en EXPIRED
transaction.success, transaction.failed, transaction.timeoutUne transaction (API historique) atteint un état final
transaction.testEnvoi manuel par POST /v1/webhooks/:id/test

N’implémentez pas d’autres événements tant qu’ils ne figurent pas ici. payment_session.failed est réservé et n’est pas émis.

Un événement n’est émis qu’après que la décision est enregistrée côté Kadryza. Si la session change d’état avant l’envoi, l’ancien événement est abandonné plutôt qu’envoyé avec des données périmées.

En-têtes

En-têtes d'une livraison
Content-Type: application/json User-Agent: Kadryza-Webhook/1.0 X-Kadryza-Signature: sha256=<hmac_hex> X-Kadryza-Event-ID: 3f1c2b9a-7d4e-5a61-9b2f-0c8d7e6f5a41 X-Kadryza-Test: true
En-têtePrésent
X-Kadryza-SignatureToujours
X-Kadryza-Event-IDÉvénements payment_session.*
X-Kadryza-TestÉvénements payment_session.* de l’environnement test, et transaction.test (pas les autres événements transaction.* de sandbox)

Payloads

payment_session.*

payment_session.succeeded
{ "event_id": "3f1c2b9a-7d4e-5a61-9b2f-0c8d7e6f5a41", "event": "payment_session.succeeded", "data": { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "merchant_id": "1f2e3d4c-5b6a-7980-cdef-001122334455", "reference": "order_2026_001", "ticket": "KDRZ-8F3K2", "amount": 5000, "currency": "XAF", "operator": "AIRTEL", "status": "SUCCESS", "environment": "live", "collection_number_id": "9a8b7c6d-5e4f-3210-fedc-ba9876543210", "expires_at": "2026-06-05T12:40:00Z", "completed_at": "2026-06-05T12:33:10Z", "payment_proof": { "operator_reference": "PP260605.1233.A00001", "payer_name": "IDRISS DEDE", "payer_phone_masked": "+235 66 •• •• 01", "received_at": "2026-06-05T12:33:08Z" }, "created_at": "2026-06-05T12:30:00Z" }, "timestamp": "2026-06-05T12:33:10Z" }
  • event_id est stable entre les retries : c’est votre clé de déduplication.
  • Le payload n’expose ni le téléphone complet du payeur, ni metadata, ni le numéro de collecte en clair (seulement son identifiant opaque collection_number_id).
  • payment_proof est présent dès que Kadryza détient le SMS de l’opérateur (succès ou vérification) : référence de transaction, nom du payeur et numéro masqué. Voir Preuve de paiement.

transaction.*

transaction.success
{ "event": "transaction.success", "data": { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "reference": "order_2026_001", "amount": 5000, "currency": "XAF", "operator": "AIRTEL", "phone_number": "+23566000000", "status": "SUCCESS", "confirmed_at": "2026-06-05T12:01:15Z" }, "timestamp": "2026-06-05T12:01:15Z" }

Pas d’event_id : dédupliquez sur event + data.id + data.status.

Signature

X-Kadryza-Signature vaut sha256= suivi du HMAC-SHA256 du corps brut, calculé avec le secret de l’endpoint et encodé en hexadécimal.

  • Calculez sur les octets reçus, avant tout parsing JSON.
  • Comparez en temps constant.
  • Rejetez toute requête non signée ou mal signée.

Exemples complets (SDK, Node.js, Express, Next.js) : Vérifier les webhooks.

Réponse attendue et retries

Répondez 2xx en moins de 10 secondes. Toute autre réponse, un timeout ou une erreur réseau est un échec.

TentativeDélai après l’échec précédent
1immédiate
21 minute
35 minutes
430 minutes
52 heures

Après la cinquième tentative, la livraison est abandonnée (failed). Vous pouvez consulter l’historique d’une session avec GET /v1/payment-sessions/:id/webhook-deliveries.

Idempotence et ordre

  • Une même livraison peut arriver plusieurs fois (retry après un timeout de votre côté) : stockez event_id avant toute action irréversible et ignorez les doublons.
  • L’ordre d’arrivée entre événements différents n’est pas garanti : appuyez-vous sur data.status, ou relisez la session.
  • Répondez vite, traitez en arrière-plan.