Tous les articles
Stripe 7 min 15 mars 2026

Pourquoi Stripe refuse vos webhooks : guide complet

La signature invalide, le mauvais endpoint, le body parsé trop tôt — les 4 raisons pour lesquelles Stripe rejette vos webhooks et comment les corriger.


Tu as configuré les webhooks Stripe, le dashboard affiche "Delivered", mais ta base de données ne se met pas à jour. Ou pire : Stripe retourne 400 Bad Request sur chaque tentative. Voici les 4 causes exactes — et les corrections.

1. La signature du webhook est invalide

C'est la raison n°1. Stripe signe chaque webhook avec ton STRIPE_WEBHOOK_SECRET. Si tu lis le body de la requête avec req.json() avant de valider la signature, ça casse tout — JSON parsing transforme le body et la signature ne correspond plus.

Ce qui ne marche pas :

// ❌ MAUVAIS — body déjà parsé
export async function POST(req: Request) {
  const body = await req.json(); // Transforme le buffer
  const sig = req.headers.get("stripe-signature");
  const event = stripe.webhooks.constructEvent(body, sig!, secret);
}

Ce qui marche :

// ✅ CORRECT — body brut (Buffer)
export async function POST(req: Request) {
  const body = await req.text(); // Garde le string brut
  const sig = req.headers.get("stripe-signature")!;
  const event = stripe.webhooks.constructEvent(body, sig, secret);
}

Dans Next.js App Router, utilise req.text() (pas req.json(), pas req.arrayBuffer()).

2. Le mauvais endpoint est configuré dans Stripe

En développement local, tu utilises stripe listen --forward-to localhost:3000/api/webhook. En production, tu dois configurer l'URL complète dans Stripe Dashboard → Developers → Webhooks.

Points à vérifier :

  • L'URL doit être https://tondomaine.fr/api/stripe/webhook (pas http)
  • Le chemin doit correspondre exactement à ta route Next.js
  • Le Webhook Secret dans Stripe Dashboard est différent du secret de développement local — n'utilise pas celui de stripe listen en production

3. Les événements à écouter ne sont pas sélectionnés

Quand tu crées un webhook dans Stripe Dashboard, tu dois choisir explicitement quels événements déclencher. Un webhook sans événements sélectionnés ne reçoit rien.

Pour un e-commerce classique, sélectionne :

  • payment_intent.succeeded
  • payment_intent.payment_failed
  • checkout.session.completed (si tu utilises Checkout)
  • customer.subscription.updated / deleted (si abonnements)

4. Timeout de la fonction webhook

Stripe attend une réponse 200 en moins de 30 secondes. Si ton webhook fait des opérations longues (envoi d'emails, mise à jour multiple en DB), tu peux dépasser ce délai et Stripe marquera le webhook comme échoué.

Fix : Réponds 200 immédiatement, puis traite la logique en arrière-plan :

export async function POST(req: Request) {
  const body = await req.text();
  const sig = req.headers.get("stripe-signature")!;

  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(body, sig, secret);
  } catch (err) {
    return Response.json({ error: "Invalid signature" }, { status: 400 });
  }

  // Répond 200 IMMÉDIATEMENT
  const response = Response.json({ received: true });

  // Traite en background (Next.js permet ça avec waitUntil en Edge,
  // ou simplement async sans await en Serverless)
  handleWebhookEvent(event).catch(console.error);

  return response;
}

Débugger un webhook en production

Stripe Dashboard → Developers → Webhooks → clique sur ton endpoint → tu vois tous les événements envoyés avec les réponses. Tu peux re-envoyer manuellement un événement pour tester.

Checklist webhook Stripe :

  1. Utiliser req.text() pour lire le body brut
  2. Utiliser le Webhook Secret de production (pas celui de stripe listen)
  3. Vérifier que l'URL HTTPS est correcte dans Stripe Dashboard
  4. Sélectionner les bons événements dans la config du webhook
  5. Répondre 200 avant tout traitement long

Tu as ce problème sur ton projet ?

Un expert Last 20% peut le résoudre pour toi en 24h. Diagnostic gratuit, garanti.

Diagnostic gratuit