DEV Community

Cover image for Comment Capturer et Valider les Webhooks Stripe en CI avec Apidog
Antoine Laurent
Antoine Laurent

Posted on • Originally published at apidog.com

Comment Capturer et Valider les Webhooks Stripe en CI avec Apidog

Un client paie, Stripe envoie l’événement payment_intent.succeeded à votre backend, puis votre application doit marquer la commande comme payée. Si le gestionnaire échoue silencieusement, vous ne le découvrez souvent qu’après un ticket de support : « J’ai payé, mais ma commande est toujours impayée. » Ce guide montre comment ajouter un test CI qui vérifie que le webhook Stripe est bien reçu, journalisé et traité à chaque déploiement.

Essayez Apidog dès aujourd’hui

Le défi : un webhook est une requête HTTP entrante de Stripe vers votre application. Les outils de test d’API déclenchent généralement des requêtes sortantes et vérifient leurs réponses. Pour tester un webhook en CI, il faut donc rendre son résultat observable et interrogeable. Avec Apidog, le modèle recommandé consiste à capturer l’événement dans votre backend, le stocker, puis l’asserter depuis la base de données. Pour les bases du sujet, consultez comment tester les webhooks et la documentation Stripe sur les webhooks.

La contrainte à connaître

La documentation Apidog l’indique explicitement : Apidog ne prend pas nativement en charge l’écoute des webhooks.

Vous ne pouvez donc pas configurer Stripe pour envoyer directement ses événements à un écouteur Apidog. À la place :

  1. Stripe appelle votre endpoint.
  2. Votre backend vérifie et enregistre l’événement.
  3. Apidog interroge l’enregistrement en base.
  4. Le scénario CI échoue si les données ou l’état métier ne correspondent pas.

Cette séparation rend le test reproductible : l’appel entrant reste géré par votre application, tandis que la validation devient une requête déterministe.

Architecture : capturer, stocker, valider

Le workflow contient quatre composants :

  1. Créez un endpoint backend pour recevoir les webhooks Stripe.
  2. Enregistrez chaque événement dans une table stripe_event_logs.
  3. Configurez la connexion à la base de données dans l’environnement Apidog.
  4. Utilisez un Post-Request Processor pour interroger la table et exécuter vos assertions.

L’objectif n’est pas seulement de prouver que Stripe a livré un événement. Le test doit aussi vérifier que votre logique métier a été exécutée, par exemple que la commande est passée au statut paid.

Étape 1 : créer l’endpoint Stripe

Votre application doit exposer une route HTTP POST accessible par Stripe. Avec Express, conservez le corps brut de la requête afin que la vérification de signature fonctionne correctement.

import express from "express";
import Stripe from "stripe";

const app = express();
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;

app.post(
  "/webhooks/stripe",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    let event;

    try {
      event = stripe.webhooks.constructEvent(
        req.body,
        req.headers["stripe-signature"],
        endpointSecret
      );
    } catch (err) {
      return res.status(400).send(`Signature check failed: ${err.message}`);
    }

    // Journalisez l'événement pour le relire dans les tests.
    await db.query(
      `INSERT INTO stripe_event_logs (event_id, type, payload, handled_at)
       VALUES ($1, $2, $3, now())
       ON CONFLICT (event_id) DO NOTHING`,
      [event.id, event.type, JSON.stringify(event.data.object)]
    );

    if (event.type === "payment_intent.succeeded") {
      const intent = event.data.object;
      await markOrderPaid(intent.metadata.order_id);
    }

    res.json({ received: true });
  }
);
Enter fullscreen mode Exit fullscreen mode

Points importants :

  • Appelez stripe.webhooks.constructEvent() avant de faire confiance au contenu reçu.
  • Utilisez le corps brut avec express.raw().
  • Stockez event.id, event.type et le payload.
  • Ajoutez ON CONFLICT (event_id) DO NOTHING pour gérer les livraisons dupliquées de Stripe.

La vérification de signature est indispensable. Pour comprendre pourquoi, consultez la vérification de signature de webhook.

Exemple de table PostgreSQL

CREATE TABLE stripe_event_logs (
  event_id TEXT PRIMARY KEY,
  type TEXT NOT NULL,
  payload JSONB NOT NULL,
  handled_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Enter fullscreen mode Exit fullscreen mode

Vous pouvez aussi ajouter un identifiant d’exécution de test afin d’isoler les assertions CI :

ALTER TABLE stripe_event_logs
ADD COLUMN test_run_id TEXT;
Enter fullscreen mode Exit fullscreen mode

Étape 2 : connecter la base de données dans Apidog

Dans l’environnement Apidog utilisé par votre CI :

  1. Ouvrez la configuration de l’environnement.
  2. Ajoutez la connexion à votre base de données.
  3. Utilisez la base correspondant à l’environnement testé : test, staging ou autre environnement isolé.
  4. Vérifiez que les identifiants sont fournis via des secrets CI, et non codés dans le scénario.

Le point critique est l’alignement des environnements :

  • le paiement de test déclenche un événement vers votre backend de staging ;
  • votre backend écrit dans la base de staging ;
  • Apidog doit interroger cette même base de staging.

Si Apidog consulte une autre base, votre assertion échouera même si le webhook a bien été traité.

Étape 3 : déclencher le paiement et interroger le journal

Votre scénario de test doit suivre cet ordre :

  1. Déclencher ou confirmer un paiement Stripe en mode test.
  2. Attendre la livraison du webhook.
  3. Interroger stripe_event_logs.
  4. Vérifier les champs de l’événement.
  5. Vérifier l’état métier de la commande.

Dans Apidog, attachez un Post-Request Processor à une étape de votre scénario. Ce processeur exécute une requête SQL après la requête HTTP de test.

Exemple de requête pour récupérer le dernier événement de paiement réussi :

SELECT event_id, type, payload, handled_at
FROM stripe_event_logs
WHERE type = 'payment_intent.succeeded'
ORDER BY handled_at DESC
LIMIT 1;
Enter fullscreen mode Exit fullscreen mode

Évitez toutefois de vous limiter au « dernier événement » dans une suite CI partagée. Préférez une recherche par identifiant connu :

SELECT event_id, type, payload, handled_at
FROM stripe_event_logs
WHERE event_id = '{{stripe_event_id}}'
LIMIT 1;
Enter fullscreen mode Exit fullscreen mode

Vos assertions devraient vérifier au minimum :

  • type = 'payment_intent.succeeded'
  • event_id correspond à l’événement déclenché par le scénario
  • le montant dans payload correspond au montant attendu
  • handled_at est renseigné
  • la commande associée a bien changé d’état

Par exemple, vérifiez ensuite la commande :

SELECT id, status, paid_at
FROM orders
WHERE id = '{{order_id}}';
Enter fullscreen mode Exit fullscreen mode

Le résultat attendu peut être :

status = paid
paid_at IS NOT NULL
Enter fullscreen mode Exit fullscreen mode

Gérer le délai de livraison Stripe

La livraison d’un webhook est asynchrone. Une requête SQL exécutée immédiatement après le déclenchement du paiement peut arriver avant l’événement.

Ajoutez donc un délai court ou un mécanisme de sondage :

tentative 1 → attendre 1 seconde
tentative 2 → attendre 2 secondes
tentative 3 → attendre 3 secondes
échec → arrêter le scénario
Enter fullscreen mode Exit fullscreen mode

Le but est de tolérer le délai de livraison sans masquer un vrai échec. Gardez une fenêtre de réessai courte et bornée.

Tester localement avec la CLI Stripe

Le modèle « capturer puis interroger » est adapté à la CI. En local, Stripe ne peut pas appeler directement votre localhost. Utilisez un relais de webhook.

La CLI Stripe peut transférer les événements vers votre endpoint local :

stripe listen --forward-to localhost:3000/webhooks/stripe
Enter fullscreen mode Exit fullscreen mode

Vous recevez alors les événements Stripe sur :

http://localhost:3000/webhooks/stripe
Enter fullscreen mode Exit fullscreen mode

Vous pouvez également utiliser Ngrok pour exposer un port local sur une URL publique.

Utilisez cette approche pour développer et déboguer le gestionnaire. En CI, revenez à la stratégie persistante : journal en base + assertions via Post-Request Processor.

Ne pas confondre avec la fonctionnalité Webhook native d’Apidog

Apidog possède une fonctionnalité nommée Webhook, mais elle ne sert pas à recevoir les événements Stripe entrants.

Elle sert à définir et documenter les webhooks sortants de votre propre système : lorsqu’un événement se produit dans votre application, votre application appelle une URL externe.

Pour documenter un webhook sortant dans Apidog :

  1. Cliquez sur + dans la barre latérale.
  2. Sélectionnez New Other Protocol APIs.
  3. Sélectionnez Webhook.
  4. Renseignez :
    • Request Method
    • Webhook Name
    • Debug URL
    • les en-têtes et le corps dans Other Info
  5. Cliquez sur Save.

La Debug URL permet de simuler l’appel avec Send, mais elle sert uniquement aux tests et n’apparaît pas dans la documentation publiée ni dans l’export OpenAPI.

Pour approfondir cette distinction, consultez les webhooks dans la conception d’API.

Renforcer votre scénario CI

Une fois le test de base en place, ajoutez des assertions plus strictes.

Isoler chaque exécution

Ne validez pas un événement résiduel provenant d’une exécution précédente. Utilisez un identifiant unique par test :

const testRunId = crypto.randomUUID();
Enter fullscreen mode Exit fullscreen mode

Ajoutez-le aux métadonnées du paiement ou à vos données de test, puis recherchez uniquement les événements associés à cet identifiant.

Tester l’idempotence

Stripe peut renvoyer un même événement. Vérifiez que votre application ne traite pas deux fois la même commande :

SELECT COUNT(*)
FROM stripe_event_logs
WHERE event_id = '{{stripe_event_id}}';
Enter fullscreen mode Exit fullscreen mode

Résultat attendu :

count = 1
Enter fullscreen mode Exit fullscreen mode

Testez également que la commande ne passe pas deux fois dans un flux métier non idempotent.

Tester les erreurs

Ne testez pas uniquement payment_intent.succeeded. Vérifiez aussi que :

  • une signature invalide retourne une erreur ;
  • un type d’événement inattendu ne déclenche pas de mise à jour métier ;
  • une erreur de traitement est journalisée ;
  • aucun handled_at ne signale à tort un traitement réussi.

Les détails d’idempotence et de stratégie de réessai sont abordés dans les bonnes pratiques des webhooks de paiement.

Vérifier l’effet métier final

Un journal d’événements prouve que votre endpoint a reçu quelque chose. Il ne prouve pas nécessairement que le métier a été mis à jour.

Ajoutez toujours une assertion en aval :

SELECT status
FROM orders
WHERE id = '{{order_id}}';
Enter fullscreen mode Exit fullscreen mode

Le test doit échouer si le webhook est présent dans stripe_event_logs mais que la commande reste impayée.

Exécuter le scénario avec l’Apidog CLI

Une fois le scénario enregistré dans Apidog, exécutez-le dans votre pipeline avec l’Apidog CLI.

Installez la CLI et authentifiez-vous :

npm install -g apidog-cli
apidog login --with-token <YOUR_ACCESS_TOKEN>
Enter fullscreen mode Exit fullscreen mode

Exécutez ensuite votre scénario :

apidog run \
  --access-token $APIDOG_ACCESS_TOKEN \
  -t <SCENARIO_ID> \
  -e <ENV_ID> \
  -r cli
Enter fullscreen mode Exit fullscreen mode

Paramètres :

  • -t : ID du scénario de test ;
  • -e : ID de l’environnement ;
  • -r : rapporteur.

Pour produire un rapport HTML exploitable dans les artefacts CI :

apidog run \
  --access-token $APIDOG_ACCESS_TOKEN \
  -t <SCENARIO_ID> \
  -e <ENV_ID> \
  -r html,cli
Enter fullscreen mode Exit fullscreen mode

Le scénario contient vos requêtes, le Post-Request Processor, les requêtes SQL et les assertions. Si l’événement n’est pas trouvé ou si la commande n’est pas marquée comme payée, la commande renvoie un code de sortie non nul et bloque le pipeline.

Consultez le guide d’installation de l’Apidog CLI et le tutoriel de pipeline CI/CD pour l’intégration complète.

Vous pouvez aussi planifier ce scénario dans Apidog afin de détecter une régression entre deux déploiements. Voir la planification des tests d’API dans Apidog.

Foire aux questions

Apidog peut-il recevoir directement un webhook Stripe ?

Non. Apidog ne prend pas nativement en charge l’écoute des webhooks entrants. Votre backend reçoit l’événement, le stocke, puis Apidog l’interroge dans la base de données.

Où les assertions sont-elles exécutées ?

Dans le Post-Request Processor du scénario Apidog. Il exécute une requête SQL sur la connexion de base de données configurée dans l’environnement.

Comment gérer le délai de livraison ?

Ajoutez un court délai ou un sondage avec plusieurs tentatives avant d’échouer. Ne supposez pas que l’événement sera disponible immédiatement après le paiement.

La fonctionnalité Webhook d’Apidog aide-t-elle à recevoir Stripe ?

Non. Elle sert à documenter les webhooks sortants de votre système, pas à intercepter les appels entrants de Stripe.

Dois-je vérifier uniquement le type d’événement ?

Non. Vérifiez aussi l’identifiant exact de l’événement, le payload, l’horodatage de traitement et l’effet métier final sur la commande.

Vous pouvez Télécharger Apidog pour configurer un projet de test et valider ce workflow.

En résumé

Pour tester les webhooks Stripe en CI :

  1. Recevez le webhook dans votre backend.
  2. Vérifiez sa signature.
  3. Enregistrez l’événement dans stripe_event_logs.
  4. Déclenchez un paiement de test.
  5. Interrogez le journal avec un Post-Request Processor.
  6. Vérifiez aussi que la commande est réellement passée au statut paid.
  7. Exécutez le scénario avec apidog run dans votre pipeline.

Vous ne capturez pas Stripe directement dans Apidog : vous rendez le traitement observable dans votre application, puis vous l’assertez de manière fiable.

Top comments (0)