Webhooks
Royal Finance pousse des événements signés en HMAC-SHA256 vers vos URLs. Pas de polling à faire.
Configurer une URL
/webhooksCréer un endpointcurl -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é |