Guide · SaaS & dev
Stripe Checkout dans une app Next.js
Onglet Next.js du quickstart Checkout, relu le 2026-10-18. Mode payment dans l’exemple, subscription à part. Clés absentes. Non rejoué.
L’intégration Next.js relue le 2026-10-19 est l’onglet Next.js de Create a Stripe-hosted checkout page. Le guide SaaS Sell subscriptions as a SaaS startup renvoie plutôt vers l’exemple React et Node. Les fichiers et le parcours diffèrent. Cet article reste donc centré sur Next.js. L’abonnement, le portail et les événements de facturation sont traités dans Abonnement SaaS avec Checkout.
Les commandes n’ont pas été rejouées. Aucune clé n’est collée. Le quickstart affiche des montants d’exemple sur la couverture du produit. Ils ne sont pas recopiés.
1. Installer les bibliothèques indiquées
La page dit d’installer les paquets, ou de télécharger l’exemple si vous partez sans package.json :
npm install --save stripe @stripe/stripe-js next
Cette commande devrait ajouter les trois paquets au projet. Elle n’a pas été lancée ici. La page ne fige pas leurs versions dans cette commande : contrôlez les versions réellement résolues dans votre lockfile.
2. Créer la session côté serveur
L’exemple place un Route Handler dans app/api/checkout_sessions/route.js. Il lit l’en-tête origin, crée une Checkout Session, et répond par une redirection 303 vers session.url.
Dans l’extrait, la session a un seul line_items, un identifiant de prix encore sous la forme {{PRICE_ID}}, quantity: 1, et mode: 'payment'. L’URL de succès concatène l’origine, /success, et session_id={CHECKOUT_SESSION_ID}. Un integration_identifier est aussi un placeholder.
Le prix et le stock restent sur le serveur. La page le dit pour empêcher le client de modifier le montant. Le Price ID vient du Dashboard, ou de price_data si vous créez le prix à la volée. L’exemple Next.js relu utilise un Price ID, pas price_data.
Le mode de l’exemple est payment, un paiement unique. La même page dit de passer mode à subscription pour un abonnement, et à setup pour enregistrer un moyen de paiement. Ne laissez pas payment si vous vendez un accès récurrent. Le détail du cycle, du portail et des webhooks d’abonnement est l’autre guide.
Le client Stripe de l’exemple est dans lib/stripe.js, marqué server-only, construit avec process.env.STRIPE_SECRET_KEY. Pas de clé dans le fichier. Best practices for managing secret API keys dit de ne jamais mettre une clé secrète dans le code, de préférer un coffre ou une variable d’environnement, et d’utiliser une clé restreinte plutôt qu’une clé secrète large. Gérer les secrets d’API couvre le dépôt et la rotation. Ne le réécrivez pas ici. GitHub Actions pour un SaaS couvre les secrets de CI, pas le Route Handler.
3. Page d’aperçu, bouton, retour
L’exemple met la page d’aperçu dans app/page.jsx. Un formulaire POST vers /api/checkout_sessions. Tant que le client n’est pas sur la page hébergée par Stripe, il peut encore changer la commande. Après, il faut une nouvelle session.
La page succès, app/success/page.jsx, lit session_id. Sans cet identifiant, l’exemple lève une erreur. Elle récupère la session, avec line_items et payment_intent en expand. Si status vaut open, elle redirige vers /. Si status vaut complete, elle affiche l’e-mail de customer_details. La page Checkout dit d’attendre le paiement réussi avant d’expédier ou d’envoyer un reçu. Le lien cité est Fulfill orders. L’affichage de l’e-mail n’est pas la livraison de l’accès SaaS.
4. Variables, sans valeurs
L’exemple .env nomme :
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYSTRIPE_SECRET_KEYSTRIPE_WEBHOOK_SECRET, en commentaire, si vous écoutez les webhooks
Les valeurs de l’extrait sont des placeholders. Ne partage aucune valeur ici. Ne les commitez pas, même factices si elles ressemblent à une vraie clé. La page clés dit que des extraits Stripe contiennent parfois une clé de bac à sable pour l’illustration, et qu’il ne faut pas la garder. Stripe ne demande jamais votre clé secrète.
La clé publiable peut aller au navigateur. La clé secrète, non. Le préfixe NEXT_PUBLIC_ de la première variable est celui de l’exemple. Ne mettez pas la clé secrète derrière ce préfixe.
5. Lancer et tester, d’après la page
Développement, dans l’exemple téléchargeable :
npm install- Optionnel : Stripe CLI,
stripe listen --forward-to localhost:3000/api/webhooks - Lancer avec le secret affiché par
stripe listen --print-secretdansSTRIPE_WEBHOOK_SECRET, puisnpm run dev - Ouvrir
http://localhost:3000
La page quickstart liste trois cartes de test : paiement accepté, authentification 3DS, paiement refusé. Les numéros sont sur la page. Ils ne sont pas recopiés ici. Nous n’avons pas soumis de paiement.
Le README affiché par le quickstart donne npm install, npm build, puis npm start pour la production. Cette séquence n’a pas été exécutée ici. Avant de la reprendre, vérifiez les scripts du package.json réellement téléchargé : la commande publiée dans le README ne prouve pas que votre projet expose un exécutable npm build.
Webhook : corps brut
L’exemple Next.js a un handler qui reconstruit l’événement avec le texte brut de la requête (req.text()), l’en-tête stripe-signature, et STRIPE_WEBHOOK_SECRET. La liste d’événements permise dans cet extrait ne contient que checkout.session.completed. Il répond 400 si la construction échoue, 200 pour accuser réception.
Receive Stripe events dit que la signature se vérifie sur le corps brut. Un framework qui réécrit ce corps fait échouer la vérification. La page dit aussi de renvoyer un 2xx avant la logique lente. Un timeout n’est pas un accusé de réception.
Pour un abonnement, checkout.session.completed ne suffit pas à ouvrir l’accès. Le guide SaaS le dit : un moyen de paiement à notification retardée peut encore être en attente. C’est développé dans l’article abonnement, avec Sécuriser un webhook pour la signature en général. Ne dupliquez pas ce guide.
Le CLI local et l’URL publique du Dashboard ne partagent pas le même secret de signature. La page webhooks le montre : stripe listen imprime un secret, le Dashboard en révèle un autre, préfixe whsec_. Utilisez celui de l’environnement qui envoie l’événement.
Ce que l’onglet Next.js ne fait pas
Il ne crée pas le produit. Il ne configure pas le portail. Il ne passe pas le compte en live. Il ne fixe pas de période d’essai. Le mode dans le fichier relu est payment. Pour un SaaS au forfait mensuel, changez ce mode et suivez Abonnement SaaS avec Checkout, qui reprend le guide startup dans l’ordre de sa page.
Le quickstart parle aussi de taxes, de clients invités, et de Checkout Studio. Ce sont des suites, pas l’étape 1.
Sources
- Checkout quickstart, onglet Next.js : install, route,
mode: 'payment', pages, variables, CLI. - Sell subscriptions as a SaaS startup : autre échantillon, React et Node, port 4242. Pas les fichiers de cet article.
- Webhooks : corps brut,
2xxrapide, secret du CLI distinct de celui du Dashboard. - Clés secrètes : pas de clé dans le code, clé restreinte, rotation.
- Fulfill orders : livrer après paiement réussi.
Pages relues le 2026-10-19. npm install, npm run dev, Stripe CLI et Checkout n’ont pas été lancés. Aucun compte Stripe ni Price ID réel n’a été utilisé.