Comment fonctionnent les webhooks
Un webhook est simplement une requête POST
Vous savez déjà à quoi ressemble une requête POST. Un webhook, c'est exactement ça, mais dans l'autre sens. Au lieu que votre serveur envoie un POST à un service externe, le service externe envoie un POST à votre serveur.
Quand un paiement réussit sur Stripe, Stripe envoie une requête POST à la webhook URL que vous avez enregistrée. Le body de la requête contient tous les détails de ce qui s'est passé : qui a payé, combien, quel plan a été souscrit.
Votre serveur la reçoit, lit les données et agit en conséquence (mettre à jour la base de données, envoyer un email de confirmation, débloquer une fonctionnalité). Du point de vue de Stripe, il envoie simplement une requête. Du point de vue de votre serveur, il en reçoit une.
Anatomie d'un webhook Stripe
Décortiquons à quoi ressemble un vrai webhook quand Stripe l'envoie à votre serveur.
Remarquez ce qui vous est familier :
- Method : POST, comme pour créer une ressource. Le service externe "crée" une notification d'événement sur votre serveur.
- URL : votre webhook endpoint, l'URL que vous avez enregistrée auprès de Stripe.
- Headers : incluent
Content-Type(c'est du JSON) et un headerStripe-Signature(on y revient plus bas). - Body : un payload JSON avec les détails de l'événement. Le champ
typevous dit ce qui s'est passé, etdata.objectcontient les informations spécifiques.
C'est la même structure de requête que vous avez apprise au chapitre 2. La seule différence : vous êtes du côté récepteur.
Les types d'événements
Les services externes n'envoient pas un webhook générique "quelque chose s'est passé". Ils envoient des event types spécifiques qui vous disent exactement ce qui s'est produit. Chaque service nomme ses événements différemment. Voici à quoi ressemblent ceux de Stripe :
| Event type | When it fires | What you'd do with it |
|---|---|---|
| payment_intent.succeeded | Un paiement est validé | Activer l'abonnement du client |
| customer.subscription.deleted | Un abonnement est annulé | Rétrograder le compte vers le plan gratuit |
| invoice.payment_failed | Une tentative de paiement échoue | Afficher un bandeau d'alerte, envoyer un email de relance |
Et voici comment Shopify nomme ses événements webhook :
| Event type | When it fires | What you'd do with it |
|---|---|---|
| orders/create | Un client passe une commande | Lancer le processus de préparation |
| products/update | Un produit est modifié | Synchroniser le changement dans votre catalogue |
| customers/delete | Un compte client est supprimé | Nettoyer les données associées |
Des conventions de nommage différentes (Stripe utilise des points, Shopify utilise des slashes), mais la même idée. Quand vous enregistrez votre webhook URL, vous choisissez les event types auxquels vous souhaitez vous abonner. Vous n'êtes pas obligé de tout écouter. Si vous ne vous intéressez qu'aux nouvelles commandes, vous vous abonnez uniquement à orders/create.
Votre équipe technique utilisera l'event type dans le payload du webhook pour le router vers le bon handler : "si le type est payment_intent.succeeded, mettre à jour le plan de l'utilisateur ; si c'est customer.subscription.deleted, le rétrograder."
Voyez-le en action
Imaginez que votre app propose un plan Pro que les clients paient via Stripe. Quand un client fait un upgrade ou annule sur votre app, Stripe traite le paiement et envoie un webhook à votre serveur pour qu'il mette à jour la base de données.
Cliquez sur un bouton ci-dessous pour simuler une action client sur votre app. Observez le flux du webhook de Stripe vers votre serveur.
Webhook entrant
Base de données
| Nom | Forfait |
|---|---|
| Alice | free |
| Bob | free |
| Clara | pro |
C'est exactement ce qui se passe en production. Un client fait quelque chose sur votre app, Stripe le traite, et votre serveur l'apprend grâce à un webhook. La seule différence, c'est qu'en conditions réelles, la requête POST transite par internet au lieu de se produire dans votre navigateur.
Sécurité : comment savoir que c'est vraiment Stripe ?
Voici le problème. Votre webhook URL est simplement une URL. N'importe qui la connaissant pourrait envoyer une fausse requête POST, en se faisant passer pour Stripe. Imaginez quelqu'un envoyant un faux événement "paiement réussi" pour un client qui n'a jamais payé. Votre serveur le passerait en premium gratuitement.
C'est pourquoi les fournisseurs de webhooks incluent une signature dans chaque requête. Stripe ajoute un header Stripe-Signature que votre serveur peut vérifier. La signature est générée à l'aide d'une clé secrète que seuls vous et Stripe connaissez. Quand votre serveur reçoit un webhook, il recalcule la signature avec le même secret. Si les signatures correspondent, la requête est légitime. Sinon, quelqu'un essaie de l'usurper.
Vous n'avez pas besoin d'implémenter ça vous-même. Mais quand votre équipe met en place une intégration webhook, vous pourriez les entendre mentionner "vérifier la signature du webhook" ou "vérifier le signing secret." Maintenant vous savez ce que ça signifie : s'assurer que la requête vient bien de Stripe et non d'un imposteur.
Les retries : que se passe-t-il si votre serveur est down ?
Que se passe-t-il si Stripe envoie un webhook et que votre serveur est temporairement indisponible ? La requête échoue. Est-ce que Stripe abandonne ?
Non. Les fournisseurs de webhooks retentent les livraisons échouées. Stripe réessaiera après quelques minutes, puis après un délai plus long, et continuera à retenter avec des intervalles croissants (c'est ce qu'on appelle l'exponential backoff). Si votre serveur est down pendant quelques minutes, vous ne manquerez aucun événement. Ils arriveront une fois votre serveur de retour.
Cela signifie aussi que votre serveur pourrait recevoir le même événement plus d'une fois. Si Stripe envoie un webhook mais que votre serveur met trop de temps à répondre, Stripe pourrait considérer que ça a échoué et le renvoyer. Les bons handlers de webhooks sont conçus pour gérer les doublons correctement (c'est ce qu'on appelle l'idempotency). Encore une fois, ce n'est pas quelque chose que vous construisez vous-même, mais un terme que vous pourriez entendre dans votre équipe.
Points clés
- Un webhook est une requête POST envoyée par un service externe à votre serveur. Même structure que n'importe quelle requête API.
- Chaque webhook a un event type (comme
payment_intent.succeeded) qui indique à votre serveur ce qui s'est passé. - Le payload JSON contient tous les détails dont votre serveur a besoin pour agir sur l'événement.
- Les fournisseurs incluent une signature pour la sécurité, afin que votre serveur puisse vérifier que la requête est authentique.
- Si la livraison échoue, les fournisseurs retentent avec des délais croissants. Votre serveur ne manquera aucun événement.
- Les webhooks complètent le tableau : les API vous permettent de demander des données, les webhooks permettent aux données de venir à vous.