Integration

Webhooks

Recevez chaque changement d’etat sur votre serveur, signe et rejoue automatiquement en cas d’echec.

Endpoints

GET/v1/webhooksLister vos endpointscle secrete
POST/v1/webhooksCreer un endpointcle secrete
POST/v1/webhooks/:idModifier un endpointcle secrete
DELETE/v1/webhooks/:idSupprimer un endpointcle secrete
POST/v1/webhooks/testEnvoyer un evenement de testcle secrete
GET/v1/webhooks/deliveriesHistorique des livraisonscle secrete

Format d’un evenement

{
  "id": "evt_2p8zk3m1x9",
  "type": "payment.succeeded",
  "created": "2024-03-01T12: 00: 06.000Z",
  "environment": "sandbox",
  "livemode": false,
  "data": {
    "object": {
      "id": "pay_8f2k39dmz0qw",
      "object": "payment",
      "status": "succeeded",
      "amount": 10000,
      "currency": "XOF",
      "fee_amount": 180,
      "net_amount": 9820,
      "payment_method": "mobile_money",
      "network": "tmoney",
      "reference": "ORDER-12345"
    }
  }
}

Evenements disponibles

  • payment.created
  • payment.processing
  • payment.succeeded
  • payment.failed
  • payment.cancelled
  • payment.expired
  • payment.refunded
  • payment.partially_refunded
  • payout.created
  • payout.processing
  • payout.succeeded
  • payout.failed
  • refund.created
  • refund.succeeded
  • refund.failed
  • payment_link.created
  • payment_link.paid

Un endpoint abonne a * recoit tous les evenements, y compris ceux ajoutes ulterieurement.

Verifier la signature

La signature est un HMAC-SHA256 calcule sur timestamp.payload avec le secret de l’endpoint. Verifiez-la systematiquement et rejetez toute requete non signee.

import crypto from 'node:crypto';

const TOLERANCE_SECONDS = 300;

export function verifyWanePaySignature(header, rawBody, secret) {
  const parts = Object.fromEntries(
    header.split(',').map((kv) => kv.split('=')),
  );

  const timestamp = Number(parts.t);
  const age = Math.abs(Math.floor(Date.now() / 1000) - timestamp);
  if (!Number.isFinite(timestamp) || age > TOLERANCE_SECONDS) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(parts.v1 ?? ''),
  );
}

Utilisez le corps brut

La signature porte sur le corps exact recu. Ne re-serialisez pas le JSON avant verification : le moindre espace change le resultat.

Rejeu et fiabilite

Politique de rejeu

ParametreTypeDescription
Tentative 1immediateDes l’emission de l’evenement.
Tentative 2+5 sSi la reponse n’est pas 2xx.
Tentative 3+30 s
Tentative 4+5 min
Tentative 5+30 min
Tentative 6+2 hPuis abandon et statut failed.
  • Repondez 2xx en moins de 8 secondes ; traitez ensuite de maniere asynchrone.
  • Le meme evenement peut etre livre plusieurs fois : rendez votre traitement idempotent en vous appuyant sur event.id.
  • L’ordre de livraison n’est pas garanti : fiez-vous au statut porte par l’objet.
  • L’historique complet (statut HTTP, duree, erreur) est consultable dans le dashboard.