Introduction
Pourquoi les webhooks sont indispensables
- 1. Un client paie sur votre site
- 2. Le paiement réussit côté Stripe
- 3. Le client ferme son navigateur avant la redirection
- 4. Votre base de données n’est jamais mise à jour ❌
Architecture recommandée
┌─────────┐ ┌─────────┐ ┌─────────────┐
│ Client │────▶│ Stripe │────▶│ Votre API │
└─────────┘ └─────────┘ │ (webhook) │
│ └──────┬──────┘
│ │
│ ┌──────▼──────┐
│ │ Base de │
│ │ données │
│ └─────────────┘
│
Notification asynchrone
Configurer le endpoint webhook
Installation
"`bash npm install stripe "`
Créer le endpoint (Express/Hono/Fastify)
typescript
// routes/webhook.ts
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2024-12-18.acacia',
});
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET!;
export async function handleWebhook(request: Request) {
const body = await request.text(); // Corps brut, pas JSON
const signature = request.headers.get('stripe-signature')!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(body, signature, endpointSecret);
} catch (err) {
console.error('Erreur de signature webhook:', err);
return new Response('Signature invalide', { status: 400 });
}
// Traitement de l'événement
await processEvent(event);
return new Response('OK', { status: 200 });
}
Traiter les événements
typescript
async function processEvent(event: Stripe.Event) {
switch (event.type) {
case 'payment_intent.succeeded':
await handlePaymentSuccess(event.data.object as Stripe.PaymentIntent);
break;
case 'payment_intent.payment_failed':
await handlePaymentFailed(event.data.object as Stripe.PaymentIntent);
break;
case 'charge.refunded':
await handleRefund(event.data.object as Stripe.Charge);
break;
case 'charge.dispute.created':
await handleDispute(event.data.object as Stripe.Dispute);
break;
default:
console.log(`Événement non géré: ${event.type}`);
}
}
Les événements essentiels à gérer
| Événement | Description | Action recommandée | |-----------|-------------|-------------------| | `payment_intent.succeeded` | Paiement réussi | Mettre à jour la commande | | `payment_intent.payment_failed` | Paiement échoué | Notifier le client | | `charge.refunded` | Remboursement effectué | Mettre à jour le statut | | `charge.dispute.created` | Litige ouvert | Alerter l'équipe | | `checkout.session.completed` | Session Checkout terminée | Valider la commande | | `checkout.session.expired` | Session expirée | Libérer le stock |
Implémentation robuste avec idempotence
typescript
async function handlePaymentSuccess(paymentIntent: Stripe.PaymentIntent) {
const reservationId = paymentIntent.metadata.reservationId;
// Récupérer le paiement existant
const existingPayment = await db.payment.findFirst({
where: { stripePaymentIntentId: paymentIntent.id },
});
// Vérifier si déjà traité (idempotence)
if (existingPayment?.status === 'SUCCEEDED') {
console.log(`Paiement ${paymentIntent.id} déjà traité, ignoré`);
return; // Déjà traité, on ignore
}
// Mettre à jour le paiement
await db.payment.update({
where: { id: existingPayment.id },
data: {
status: 'SUCCEEDED',
paidAmount: paymentIntent.amount_received,
},
});
// Mettre à jour la réservation
await updateReservationStatus(reservationId);
}
Sécuriser vos webhooks
1. Valider la signature
typescript
try {
event = stripe.webhooks.constructEvent(body, signature, endpointSecret);
} catch (err) {
// Ne JAMAIS traiter un événement sans signature valide
return new Response('Signature invalide', { status: 400 });
}
2. Utiliser HTTPS en production
3. Répondre rapidement
typescript
async function handleWebhook(event: Stripe.Event) {
// Enregistrer l'événement dans une queue
await queue.add('process-stripe-event', { eventId: event.id, data: event });
// Répondre immédiatement à Stripe
return new Response('OK', { status: 200 });
}
Tester les webhooks en local
Avec Stripe CLI
bash # Installer Stripe CLI brew install stripe/stripe-cli/stripe # Se connecter stripe login # Écouter les webhooks stripe listen --forward-to localhost:3000/api/webhook # Dans un autre terminal, déclencher un événement stripe trigger payment_intent.succeeded
Événements de test utiles
bash stripe trigger payment_intent.succeeded stripe trigger payment_intent.payment_failed stripe trigger charge.refunded stripe trigger checkout.session.completed
Configurer les webhooks en production
- 1. Allez dans le Dashboard Stripe → Développeurs → Webhooks
- 2. Cliquez sur « Ajouter un endpoint »
- 3. Entrez l’URL de votre webhook (ex: `https://api.monsite.com/webhook/stripe`)
- 4. Sélectionnez les événements à recevoir
- 5. Copiez le secret de signature dans vos variables d’environnement
Gestion des erreurs et retry
| Tentative | Délai | |-----------|-------| | 1 | Immédiat | | 2 | 5 minutes | | 3 | 1 heure | | 4 | 3 heures | | ... | Jusqu'à 3 jours |
typescript // ✅ 200 : Événement traité avec succès // ✅ 202 : Événement accepté, traitement différé // ❌ 400-499 : Erreur client, pas de retry // ❌ 500+ : Erreur serveur, Stripe va retry
Logging et monitoring
typescript
async function processEvent(event: Stripe.Event) {
console.log(`[Stripe Webhook] ${event.type} - ${event.id}`);
const startTime = Date.now();
try {
// ... traitement
console.log(`[Stripe Webhook] ${event.type} traité en ${Date.now() - startTime}ms`);
} catch (error) {
console.error(`[Stripe Webhook] Erreur ${event.type}:`, error);
// Envoyer une alerte à votre système de monitoring
throw error; // Retourner 500 pour que Stripe retry
}
}
Checklist de production
- [ ] HTTPS activé sur le endpoint - [ ] Signature Stripe validée - [ ] Idempotence implémentée - [ ] Logging en place - [ ] Alertes configurées pour les événements critiques - [ ] Tests avec Stripe CLI effectués - [ ] Événements essentiels configurés dans le Dashboard
Conclusion
- Toujours valider la signature
- Implémenter l’idempotence
- Répondre rapidement (< 30s)
- Logger tous les événements
- Tester avec Stripe CLI avant la production
📚 Série complète : Maîtriser Stripe avec Node.js et React
-
Chapitre 1 : Configuration initiale (Précédent) Intégrer Stripe dans React/Next.js Premier formulaire de paiement et installation.
-
Chapitre 2 : Automatisation (Vous êtes ici) Gérer les webhooks Stripe dans Node.js Recevoir et traiter les événements de paiement en temps réel.
-
Chapitre 3 : Stratégie de Paiement (Suivant) Stripe Checkout vs Elements : Quel choix pour votre projet ? Comparatif complet pour choisir la meilleure UX.
-
Chapitre 4 : Gestion après-vente Implémenter les remboursements Stripe Gérer les annulations, les remboursements et les litiges.
-
Chapitre 5 : Concepts Avancés PaymentIntent et capture différée Autoriser les paiements et les capturer plus tard.
