Royal Finance

Webhooks

Royal Finance pousse des événements signés en HMAC-SHA256 vers vos URLs. Pas de polling à faire.

Configurer une URL

POST/webhooks
curl -X POST https://api.royalstack.com/api/v1/webhooks \
  -H "Authorization: Bearer rs_live_••••" \
  -d '{
    "url":    "https://api.votre-app.bf/rf/webhooks",
    "events": ["payment.succeeded", "payment.failed", "refund.created"]
  }'

Le secret n’est affiché qu’une seule fois. Stockez-le côté serveur chiffré ; il sert à vérifier la signature des événements entrants.

Vérifier la signature

Chaque requête entrante inclut deux headers :

Royal-Signature: t=1717168338,v1=4c2f...8a91
Royal-Webhook-Id: wbk_5K4z2Yp

Recomposez la chaîne ${t}.${rawBody}, calculez HMAC-SHA256 avec le secret, et comparez en temps constant :

import crypto from 'node:crypto'

export function verifyRoyalSignature(rawBody: string, header: string, secret: string, toleranceSeconds = 300) {
  const parts = Object.fromEntries(header.split(',').map(p => p.split('=')))
  const t = Number(parts.t)
  if (!Number.isFinite(t)) return false
  if (Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(parts.v1, 'hex'))
}

Retries

Si votre endpoint répond autre chose que 2xx, Royal Finance retente : immédiat, +30s, +2min, +10min, +1h, +6h, +24h.

Après 7 échecs, l’événement passe en dead-letter queue visible depuis le dashboard pendant 30 jours.

Événements

| Événement | Quand | |------------------------|------------------------------------| | payment.succeeded | Paiement réglé | | payment.failed | Échec définitif | | refund.created | Remboursement initié | | wallet.frozen | Wallet gelé (fraude, KYC) | | kyc.updated | Statut KYC d’un utilisateur changé | | payout.completed | Virement de masse exécuté |