Webhooks expliqués : payloads signés et retries
Vous venez d'intégrer une passerelle de paiement à l'aide de webhooks. Tout fonctionne en test, mais en production, des événements disparaissent parfois, ou vous recevez des notifications en double qui provoquent des doubles débits. La cause racine réside souvent dans la façon dont vous gérez les payloads signés et les retries. Cet article explique la mécanique des webhooks et fournit des étapes pratiques pour construire une intégration robuste et sécurisée.
Que sont les webhooks ?
Les webhooks sont des callbacks HTTP définis par l'utilisateur. Lorsqu'un événement se produit dans un système source (par exemple, un paiement est traité), il envoie une requête HTTP POST vers une URL que vous spécifiez, contenant les données de l'événement. Contrairement au polling, les webhooks poussent les données en quasi temps réel, réduisant la latence et la charge serveur.
Cependant, les webhooks introduisent des défis : vérifier l'authenticité, gérer les échecs et garantir un traitement exactement-une-fois. Abordons chacun d'eux.
Payloads signés : vérifier l'authenticité
Comme les endpoints de webhook sont accessibles publiquement, n'importe qui pourrait envoyer de fausses requêtes. Pour éviter cela, les fournisseurs signent le payload avec une clé secrète. Vous vérifiez la signature pour vous assurer que la requête est authentique.
Comment fonctionnent les signatures HMAC
La plupart des fournisseurs utilisent HMAC (Hash-based Message Authentication Code) avec SHA-256. Le fournisseur calcule une signature à partir du corps de la requête et d'un secret partagé, puis l'inclut dans un en-tête (par exemple, X-Signature). Vous recalculez la signature de votre côté et comparez.
Exemple en Python :
import hmac
import hashlib
def verify_signature(payload, secret, received_signature):
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_signature)
Important : Utilisez le corps brut de la requête, pas le JSON parsé, car le parsing peut modifier les espaces et casser la signature.
Pièges courants
- Utiliser des données parsées : Vérifiez toujours par rapport aux octets bruts.
- Attaques temporelles : Utilisez une comparaison à temps constant (par exemple,
hmac.compare_digest). - Ignorer les horodatages : Certains fournisseurs incluent un horodatage dans la signature pour prévenir les attaques par rejeu. Validez qu'il est dans une fenêtre de tolérance (par exemple, 5 minutes).
Retries : gérer les échecs avec grâce
Votre endpoint peut être temporairement indisponible, ou des problèmes réseau peuvent causer des échecs. Les systèmes de webhooks fiables réessaient les livraisons échouées avec un backoff exponentiel.
Stratégies de retry
Les fournisseurs réessaient généralement sur des réponses non-2xx ou des timeouts. Modèles courants :
| Stratégie | Description | Exemple |
|---|---|---|
| Intervalle fixe | Réessayer toutes les N secondes | Toutes les 30s, jusqu'à 5 fois |
| Backoff exponentiel | Doubler le délai à chaque tentative | 1s, 2s, 4s, 8s... |
| Exponentiel avec jitter | Ajouter de l'aléatoire pour éviter l'effet de troupeau | 1s ± 0,5s, 2s ± 1s... |
En tant que récepteur, vous ne pouvez pas contrôler la politique de retry de l'émetteur, mais vous pouvez concevoir votre endpoint pour être résilient.
Bonnes pratiques pour les récepteurs
- Répondez rapidement : Retournez un 2xx en quelques secondes. Déchargez le traitement vers une tâche en arrière-plan.
- Soyez idempotent : Utilisez une clé d'idempotence du payload pour éviter le traitement en double.
- Journalisez tout : Stockez les webhooks entrants pour le débogage et le rejeu.
- Surveillez les échecs : Configurez des alertes pour les échecs répétés.
Implémenter l'idempotence
Les doublons arrivent : l'émetteur peut réessayer parce que votre réponse était lente, ou vous pourriez traiter accidentellement le même événement deux fois. L'idempotence garantit que traiter un événement plusieurs fois a le même effet qu'une seule fois.
Utilisez un ID d'événement unique (souvent fourni dans le payload ou les en-têtes) et stockez-le dans une base de données avec une contrainte unique. Avant de traiter, vérifiez si l'ID existe ; si oui, passez.
def process_event(event_id, data):
if EventLog.exists(event_id):
return # déjà traité
EventLog.create(event_id)
# traiter les données...
Pour les systèmes à haut débit, utilisez un verrou distribué ou une transaction de base de données pour éviter les conditions de course.
Considérations de sécurité
Au-delà de la vérification de signature, considérez :
- HTTPS : Utilisez toujours TLS pour éviter l'écoute clandestine.
- Liste blanche d'IP : Si le fournisseur publie des plages d'IP, restreignez les requêtes entrantes.
- Limitation de débit : Protégez votre endpoint contre les abus.
- Validation du payload : Même après vérification de signature, validez le schéma des données pour éviter les attaques par injection.
Tester les webhooks localement
Pendant le développement, vous avez besoin d'une URL publique pour recevoir les webhooks. Des outils comme ngrok ou localtunnel exposent votre serveur local. Alternativement, de nombreux fournisseurs proposent une CLI pour transférer les événements vers votre localhost.
Simulez des échecs pour tester la gestion des retries : retournez intentionnellement des erreurs 500 et observez le modèle de retry du fournisseur.
FAQ
Comment vérifier une signature de webhook ?
Utilisez HMAC avec le secret partagé. Calculez le hash du corps brut de la requête et comparez-le à l'en-tête de signature à l'aide d'une fonction de comparaison à temps constant.
Que faire si je reçois des webhooks en double ?
Implémentez l'idempotence en stockant les ID d'événements. Vérifiez si l'événement a déjà été traité avant d'exécuter la logique métier.
Puis-je me fier uniquement aux retries de webhooks pour la fiabilité ?
Non. Les retries aident mais ne peuvent pas garantir la livraison. Pour les événements critiques, combinez les webhooks avec un fallback de polling ou une file de messages qui persiste les événements.
Lors du débogage des payloads de webhooks, vous pourriez avoir besoin d'inspecter des données JSON. Utilisez notre JSON Formatter pour joliment imprimer et valider les payloads rapidement.