# Webhook Stripe : email, SMS ou WhatsApp sur impayé, gratuit ?

> Stripe relance une carte refusée jusqu'à 8 fois en 2 semaines, mais n'écrit au client que par email. Ajoutez WhatsApp et SMS à vos relances, gratuitement.

Source: https://unlimitedmessaging.app/fr/articles/stripe-payment-notifications-whatsapp-email

Guide Stripe

# Une carte est refusée sur Stripe,  
_votre client reçoit un WhatsApp._

Un seul webhook écoute invoice.paid, invoice.payment\_failed et customer.subscription.trial\_will\_end, et envoie le reçu, la relance d'impayé ou l'avis de fin d'essai par email, WhatsApp ou SMS depuis vos propres comptes. Gratuit, avec le code pour Next.js.

[Obtenir une clé API gratuite](https://app.unlimitedmessaging.app)[Voir l'API](https://unlimitedmessaging.app/fr/api)

La réponse courte

## Comment envoyer un email, un WhatsApp ou un SMS depuis un webhook Stripe ?

Créez un endpoint de webhook, vérifiez chaque événement avec stripe.webhooks.constructEvent sur le corps brut, répondez 2xx tout de suite, puis envoyez le message en un appel POST /message. Stripe envoie [customer.subscription.trial\_will\_end](https://docs.stripe.com/billing/subscriptions/webhooks) 3 jours avant la fin d'un essai et invoice.payment\_failed après chaque tentative échouée : chaque événement donne un message.

Stripe fait déjà une partie du travail. Smart Retries retente un paiement d'abonnement échoué, jusqu'à 8 fois en 2 semaines par défaut sur les nouveaux comptes, et les emails clients de Stripe peuvent prévenir d'un paiement échoué, d'un essai qui se termine dans 7 jours ou d'une carte qui expire. Uniquement par email.

Un email sur une carte refusée passe facilement inaperçu. Un WhatsApp ou un SMS venant d'un numéro que le client connaît est lu, et le lien vers la page de facture hébergée lui permet de payer en une minute. Votre webhook choisit le canal et le moment : email et WhatsApp au premier échec, SMS aux tentatives suivantes.

Stripe peut livrer le même événement deux fois et dans n'importe quel ordre, et relance un endpoint en production pendant 3 jours maximum s'il ne reçoit pas de 2xx. Votre handler doit être rapide et idempotent : traitez chaque event.id une seule fois.

À lire aussi : [les notifications transactionnelles par WhatsApp](https://unlimitedmessaging.app/fr/articles/notifications), et [comment éviter un bannissement WhatsApp](https://unlimitedmessaging.app/fr/articles/avoid-whatsapp-ban).

![Dessin de l'application Unlimited Messaging montrant une liste de messages avec leur statut (lu, distribué, envoyé, en file), avec le titre « Relances Stripe par WhatsApp, SMS et email ».](https://unlimitedmessaging.app/_next/image?url=%2Fassets%2Farticles%2Fcovers%2Fstripe-payment-notifications-whatsapp-email-fr.png&w=3840&q=75)

Définition
Relance d'impayé (dunning)

Récupérer un paiement récurrent échoué : retenter le prélèvement et demander au client de mettre à jour son moyen de paiement avant l'annulation de l'abonnement.

Aussi appelé recouvrement des paiements échoués ou revenue recovery.

[Source : Stripe Docs, relances de paiement automatiques↗](https://docs.stripe.com/billing/revenue-recovery/smart-retries)

Pas à pas

## Comment le construire, étape par étape ?

Cinq étapes dans un projet Next.js App Router, environ 20 minutes. Tout backend fonctionne de la même façon : vérifier, répondre 2xx, envoyer.

1. 01  
### Rassemblez les trois secrets  
Votre clé secrète Stripe, le secret de signature (whsec\_) de votre endpoint de webhook, et une clé API Unlimited Messaging une fois votre boîte mail, votre numéro WhatsApp ou votre téléphone Android connecté. Les trois restent côté serveur, jamais avec un préfixe NEXT\_PUBLIC\_.  
.env.local.env.local  
```  
# .env.local (server only, never NEXT_PUBLIC_)  
STRIPE_SECRET_KEY=sk_test_xxxx  
# Printed by "stripe listen" in dev, or shown on the endpoint in the Dashboard  
STRIPE_WEBHOOK_SECRET=whsec_xxxx  
UM_API_KEY=um_live_xxxx  
```
2. 02  
### Ajoutez le helper notify  
Une fonction réservée au serveur autour de POST /message. Elle ignore les canaux sans destinataire et marque le message comme transactionnel, pour qu'il passe devant un envoi en masse déjà dans votre file.  
lib/notify.tsTypeScript  
```  
// lib/notify.ts  
import 'server-only';  
export type Channel = 'EMAIL' | 'WHATSAPP' | 'SMS';  
export async function notify(input: {  
  email?: string;  
  phone?: string; // E.164, e.g. +33612345678  
  channels: Channel[];  
  subject: string;  
  text: string;  
}) {  
  const channels = input.channels  
    .filter((c) => (c === 'EMAIL' ? input.email : input.phone))  
    .map((channel) => ({ channel }));  
  if (channels.length === 0) return null;  
  const res = await fetch('https://api.unlimitedmessaging.app/message', {  
    method: 'POST',  
    headers: {  
      Authorization: `Bearer ${process.env.UM_API_KEY}`,  
      'Content-Type': 'application/json',  
    },  
    body: JSON.stringify({  
      to: { email: input.email, phone: input.phone },  
      channels,  
      subject: input.subject,  
      text: input.text,  
      priority: 'transactional',  
    }),  
  });  
  if (!res.ok) throw new Error(`Unlimited Messaging ${res.status}: ${await res.text()}`);  
  return res.json();  
}  
```
3. 03  
### Créez la route du webhook  
Lisez le corps brut avec req.text() : Stripe signe les octets exacts, et parser le JSON avant casse la vérification. constructEvent rejette aussi les événements plus vieux que sa tolérance par défaut de 5 minutes. Répondez 200 tout de suite et envoyez dans after().  
app/api/stripe/webhook/route.tsRoute Handler  
```  
// app/api/stripe/webhook/route.ts  
import type Stripe from 'stripe';  
import { after } from 'next/server';  
import { stripe, handleStripeEvent } from '@/lib/stripe-notify';  
export async function POST(req: Request) {  
  // The raw body: parsing it as JSON first breaks the signature check  
  const body = await req.text();  
  const signature = req.headers.get('stripe-signature') ?? '';  
  let event: Stripe.Event;  
  try {  
    event = stripe.webhooks.constructEvent(body, signature, process.env.STRIPE_WEBHOOK_SECRET!);  
  } catch {  
    return new Response('Invalid signature', { status: 400 });  
  }  
  // Answer Stripe with a 2xx right away, send the messages after the response  
  after(() => handleStripeEvent(event).catch(console.error));  
  return new Response('ok', { status: 200 });  
}  
```
4. 04  
### Associez chaque événement à un message  
invoice.paid envoie un reçu par email. invoice.payment\_failed envoie un email et un WhatsApp avec le lien de la facture hébergée, puis ajoute le SMS quand attempt\_count dépasse 1\. customer.subscription.trial\_will\_end arrive 3 jours avant la fin de l'essai : récupérez le client pour l'email et le téléphone.  
lib/stripe-notify.tsTypeScript  
```  
// lib/stripe-notify.ts  
import 'server-only';  
import Stripe from 'stripe';  
import { notify, type Channel } from '@/lib/notify';  
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);  
const money = (amount: number, currency: string) =>  
  new Intl.NumberFormat('en', { style: 'currency', currency }).format(amount / 100);  
export async function handleStripeEvent(event: Stripe.Event) {  
  // Stripe can send the same event twice: skip the ones already handled  
  if (!(await markProcessed(event.id))) return;  
  switch (event.type) {  
    case 'invoice.paid': {  
      const inv = event.data.object;  
      await notify({  
        email: inv.customer_email ?? undefined,  
        channels: ['EMAIL'],  
        subject: 'Payment received',  
        text: `Thanks, we received ${money(inv.amount_paid, inv.currency)}.`,  
      });  
      break;  
    }  
    case 'invoice.payment_failed': {  
      const inv = event.data.object;  
      // First failure: email + WhatsApp. Next retries: SMS as well.  
      const channels: Channel[] =  
        inv.attempt_count > 1 ? ['EMAIL', 'WHATSAPP', 'SMS'] : ['EMAIL', 'WHATSAPP'];  
      await notify({  
        email: inv.customer_email ?? undefined,  
        phone: inv.customer_phone ?? undefined,  
        channels,  
        subject: 'Your payment did not go through',  
        text: `Your payment of ${money(inv.amount_due, inv.currency)} failed. Update your card here: ${inv.hosted_invoice_url}`,  
      });  
      break;  
    }  
    case 'customer.subscription.trial_will_end': {  
      const sub = event.data.object; // sent 3 days before the trial ends  
      const customer = await stripe.customers.retrieve(sub.customer as string);  
      if (customer.deleted) break;  
      await notify({  
        email: customer.email ?? undefined,  
        phone: customer.phone ?? undefined,  
        channels: ['EMAIL', 'WHATSAPP'],  
        subject: 'Your trial ends in 3 days',  
        text: 'Your trial ends in 3 days. Nothing to do if your card is up to date.',  
      });  
      break;  
    }  
  }  
}  
async function markProcessed(eventId: string): Promise<boolean> {  
  // e.g. INSERT INTO stripe_events (id) VALUES ($1) ON CONFLICT DO NOTHING,  
  // then return true only when a row was inserted  
  return true;  
}  
```
5. 05  
### Testez avec la CLI Stripe, puis passez en production  
stripe listen transfère les événements de test vers localhost et affiche le secret de signature à utiliser. Déclenchez un paiement échoué, vérifiez les messages, puis ajoutez votre URL de production comme endpoint dans le Dashboard avec les trois événements et son propre secret whsec\_.  
TerminalStripe CLI  
```  
# Forward test events to your local route (prints a whsec_ secret)  
stripe listen --forward-to localhost:3000/api/stripe/webhook  
# In another terminal: fire a failed payment  
stripe trigger invoice.payment_failed  
```

Vibe coding

## Vous codez avec Cursor, Lovable ou Claude Code ? Collez ce prompt

Le prompt donne à votre éditeur IA le bon schéma : vérification de signature sur le corps brut, 2xx rapide, handler idempotent, messages par canal. Il vous demande vos contacts de test avant d'envoyer.

Prompt à copier

Ajoute des notifications client sur les événements Stripe à ce projet Next.js (App Router) avec Unlimited Messaging : email, WhatsApp et SMS envoyés depuis mes propres comptes.

1. Utilise STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET et UM_API_KEY depuis .env.local, et liste-les dans .env.example. N'utilise jamais de préfixe NEXT_PUBLIC_.
2. Crée lib/notify.ts (import 'server-only') qui exporte notify({ email, phone, channels, subject, text }) et envoie un POST https://api.unlimitedmessaging.app/message avec l'en-tête Authorization: Bearer UM_API_KEY et le JSON { to: { email, phone }, channels: [{ channel: 'EMAIL' | 'WHATSAPP' | 'SMS' }], subject, text, priority: 'transactional' }. Ignore les canaux sans destinataire.
3. Crée app/api/stripe/webhook/route.ts : lis le corps brut avec req.text(), vérifie-le avec stripe.webhooks.constructEvent et l'en-tête stripe-signature, renvoie 400 en cas d'échec, renvoie 200 tout de suite et traite l'événement dans after() de next/server.
4. Gère invoice.paid (reçu par email), invoice.payment_failed (email + WhatsApp avec hosted_invoice_url, ajoute le SMS quand attempt_count > 1) et customer.subscription.trial_will_end (récupère le client, email + WhatsApp). Enregistre event.id dans une table avec une contrainte d'unicité et ignore les événements déjà traités.
5. Référence de l'API : https://docs.unlimitedmessaging.app. Webhooks Stripe : https://docs.stripe.com/webhooks.

Montre-moi le diff et les commandes stripe listen / stripe trigger pour tester, et demande-moi mon email et mon numéro de test avant d'envoyer quoi que ce soit.

Les options

## Les emails intégrés de Stripe ou vos propres messages par webhook ?

Les emails clients de Stripe ne demandent aucun code. Un webhook ajoute les canaux et le calendrier de votre choix. Les deux se combinent.

__Les emails intégrés de Stripe ou vos propres messages par webhook ?__
| [Emails clients Stripe](https://docs.stripe.com/billing/revenue-recovery/customer-emails)Intégrés à Billing | [Webhook + API email](https://resend.com/pricing)Par exemple Resend | [Webhook + Twilio](https://www.twilio.com/en-us/sms/pricing)API SMS | Webhook + Unlimited MessagingEmail, WhatsApp et SMS |                                                            |
| ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------- | ---------------------------------------------------------- |
| Canaux                                                                                                      | Email                                                               | Email                                                               | SMS, WhatsApp avec modèles Meta                     | Email, WhatsApp et SMS en une requête                      |
| Paiement échoué                                                                                             | Un email après chaque paiement par carte échoué                     | Votre email, votre calendrier                                       | Votre SMS, votre calendrier                         | Escalade de l'email vers WhatsApp et SMS                   |
| Fin d'essai                                                                                                 | Email de rappel 7 jours avant                                       | Événement 3 jours avant, votre email                                | Événement 3 jours avant, votre SMS                  | Événement 3 jours avant, tout canal                        |
| Coût des messages                                                                                           | Compris dans Stripe Billing                                         | Gratuit jusqu'à 3 000 emails par mois, 100 par jour                 | Facturé au segment SMS                              | Plan Free, sans frais par message                          |
| Code                                                                                                        | Aucun, réglages du Dashboard                                        | Un handler de webhook                                               | Un handler de webhook                               | Un handler de webhook                                      |
| Expéditeur                                                                                                  | Stripe, à vos couleurs                                              | Votre domaine vérifié                                               | Un numéro Twilio                                    | Votre boîte mail, votre numéro WhatsApp et votre téléphone |

Vérifié le 9 novembre 2026 dans la documentation de Stripe, sur resend.com/pricing et twilio.com (liens dans les sources). Vous pouvez garder les emails de Stripe et ajouter WhatsApp ou SMS par-dessus.

Les contreparties

## Quelles sont les limites ?

Gratuit supprime la facture au message. Les webhooks demandent toujours du soin, et WhatsApp et le SMS ont toujours leurs règles.

1. 01  
### Les numéros dans Stripe sont du texte libre  
customer\_phone et customer.phone contiennent ce qui a été saisi. WhatsApp et le SMS exigent le format E.164 (+33612345678) : normalisez les numéros à la collecte, par exemple avec la collecte du téléphone dans Checkout, et ignorez les canaux téléphone sans numéro.
2. 02  
### Répondre 200 d'abord, c'est gérer vous-même les relances  
after() envoie les messages une fois le 2xx reçu par Stripe : Stripe ne relivrera pas si votre envoi échoue. Pour les messages à ne pas perdre, enregistrez l'événement et traitez-le depuis une file qui réessaie.
3. 03  
### Ni l'unicité ni l'ordre ne sont garantis  
Stripe peut livrer un événement plusieurs fois et ne garantit pas l'ordre. Dédoublonnez sur event.id, pas sur created, et récupérez l'objet à jour via l'API quand l'état courant compte.
4. 04  
### WhatsApp est non officiel : relance oui, campagne non  
Votre numéro est lié comme un appareil, hors de la plateforme Business de Meta. Un rappel de paiement à votre propre client est attendu ; une campagne vers tous vos anciens clients est le genre de trafic qui fait bannir un numéro.
5. 05  
### Le SMS demande un téléphone Android allumé  
Les SMS partent de la SIM de votre téléphone, sur votre forfait. Android demande une confirmation au-delà de 30 SMS en 30 minutes, l'application espace donc les envois à environ 30 toutes les 30 minutes : largement assez pour des relances, pas pour une campagne de masse.
6. 06  
### Les messages du plan Free portent une signature  
Sur le plan Free, les messages se terminent par une courte ligne "Sent with UnlimitedMessaging.app", et les médias et les webhooks de statut sont payants. Medium et Max retirent la ligne.

Quand choisir autre chose

### Zéro code, ou très gros volume ?

Si l'email suffit, activez les emails de paiement échoué et de fin d'essai de Stripe dans le Dashboard et arrêtez-vous là : aucun endpoint à héberger. Si vous envoyez des milliers de SMS par jour ou avez besoin des modèles WhatsApp officiels de Meta, un fournisseur comme Twilio convient mieux. Votre propre webhook avec Unlimited Messaging convient à un SaaS qui veut ses relances sur les canaux que ses clients lisent, depuis son propre numéro, gratuitement pendant sa croissance.

FAQ

## Questions sur les notifications par webhook Stripe

Vérifiez l'événement avec stripe.webhooks.constructEvent sur le corps brut, répondez 2xx, puis appelez une API email. Avec Unlimited Messaging, POST /message avec le canal EMAIL envoie depuis votre propre boîte mail, sans frais par message sur le plan Free.

invoice.paid pour les reçus, invoice.payment\_failed pour les relances (attempt\_count indique quelle tentative a échoué) et customer.subscription.trial\_will\_end pour les rappels de fin d'essai, envoyé 3 jours avant la fin. invoice.payment\_action\_required couvre les paiements qui demandent une authentification du client.

Oui. Dans le handler invoice.payment\_failed, envoyez POST /message avec le canal SMS vers invoice.customer\_phone. Avec Unlimited Messaging, il part de la SIM de votre téléphone Android, sans frais par SMS de notre part.

Non. Les emails clients automatiques de Stripe sont uniquement des emails. Les relances par WhatsApp ou SMS passent par votre propre webhook.

Pas forcément. Vous pouvez garder l'email de Stripe, qui renvoie vers sa page hébergée, et ajouter un WhatsApp ou un SMS. Évitez simplement d'envoyer deux emails pour le même échec.

Le plus souvent parce que le corps a été parsé avant la vérification. Dans un Route Handler App Router, lisez-le avec await req.text() et passez cette chaîne à constructEvent avec l'en-tête stripe-signature et le secret whsec\_ du bon endpoint : celui affiché par stripe listen diffère de celui du Dashboard.

Le plan Free d'Unlimited Messaging n'a aucun frais par message, sur aucun canal, et ne demande pas de carte bancaire ; les messages portent une courte signature. Stripe facture les paiements et Billing, pas l'envoi des événements de webhook.

Sources

## Sources & références

Pour vérifier par vous-même les informations de cet article :

1. [01Utilisation des webhooks avec les abonnements (trial\_will\_end 3 jours avant, invoice.payment\_failed)↗Stripe Docs](https://docs.stripe.com/billing/subscriptions/webhooks)
2. [02Recevez les événements Stripe dans votre endpoint de webhook (signatures, 2xx, doublons, tolérance de 5 minutes)↗Stripe Docs](https://docs.stripe.com/webhooks)
3. [03Automatiser les relances de paiement (Smart Retries : jusqu'à 8 relances en 2 semaines par défaut)↗Stripe Docs](https://docs.stripe.com/billing/revenue-recovery/smart-retries)
4. [04Automatiser les e-mails aux clients (paiements échoués, rappel 7 jours avant la fin de l'essai)↗Stripe Docs](https://docs.stripe.com/billing/revenue-recovery/customer-emails)
5. [05after (stable depuis la v15.1.0)↗Next.js Docs](https://nextjs.org/docs/app/api-reference/functions/after)
6. [06route.js (Route Handlers)↗Next.js Docs](https://nextjs.org/docs/app/api-reference/file-conventions/route)
7. [07Pricing (Free : 3 000 emails par mois, 100 par jour)↗Resend](https://resend.com/pricing)
8. [08Tarifs SMS, au segment et par pays↗Twilio](https://www.twilio.com/en-us/sms/pricing)
9. [09SmsUsageMonitor (seuil de 30 SMS en 30 minutes)↗Android Open Source Project](https://android.googlesource.com/platform/frameworks/opt/telephony/+/refs/heads/main/src/java/com/android/internal/telephony/SmsUsageMonitor.java)
10. [10Référence de l'API : POST /message↗Unlimited Messaging Docs](https://docs.unlimitedmessaging.app)

![Ilian, ingénieur logiciel senior](https://ilianazz.com/images/iliazz.png)

Écrit par

[Ilian](https://unlimitedmessaging.app/fr/authors/ilian) · Senior Software Engineer

Ingénieur logiciel senior, il fonde et dirige plusieurs sociétés tech. Il conçoit des produits autour des API de messagerie, de l’automatisation et de l’intégration de l’IA, et partage ici ce qu’il apprend en construisant.

[Voir le profil LinkedIn↗](https://www.linkedin.com/in/ilianAzz/)Mis à jour le 9 novembre 2026

![](https://unlimitedmessaging.app/assets/logos/whatsapp.svg)![](https://unlimitedmessaging.app/assets/logos/email.svg)![](https://unlimitedmessaging.app/assets/logos/sms.svg)

## Transformez vos impayés en rappels WhatsApp dès aujourd'hui.

Créez un compte gratuit, connectez une boîte mail, un numéro WhatsApp ou un téléphone Android, collez le handler. Sans carte bancaire, sans frais par message.

[Commencer gratuitement→](https://app.unlimitedmessaging.app)

Articles similaires
* [![](https://unlimitedmessaging.app/_next/image?url=%2Fassets%2Farticles%2Fcovers%2Fnotifications-fr.png&w=3840&q=75)Confirmations de commande sur WhatsApp : le guide 2026Envoyez confirmations de commande, alertes et reçus via WhatsApp.](https://unlimitedmessaging.app/fr/articles/notifications)
* [![](https://unlimitedmessaging.app/_next/image?url=%2Fassets%2Farticles%2Fcovers%2Fecommerce-order-notifications-whatsapp-sms-fr.png&w=3840&q=75)E-commerce : 5 notifications WhatsApp et SMS qui rassurentE-commerceCommande confirmée, expédiée, en livraison : prévenez vos clients par WhatsApp, SMS et mail depuis vos comptes, sans frais par message.](https://unlimitedmessaging.app/fr/articles/ecommerce-order-notifications-whatsapp-sms)
* [![](https://unlimitedmessaging.app/_next/image?url=%2Fassets%2Farticles%2Fcovers%2Femail-whatsapp-single-api-request-fr.png&w=3840&q=75)Une requête API, deux canaux : email et WhatsApp d'un coupEnvoyez le même message par WhatsApp et par mail en un appel, depuis votre numéro et votre boîte mail, gratuit sur toutes les offres.](https://unlimitedmessaging.app/fr/articles/email-whatsapp-single-api-request)
