Guide · SaaS & dev

Abonnement SaaS avec Checkout Stripe

Guide startup Stripe relu le 2026-10-18 : produit test, Checkout hébergé, portail, trois événements. Montants d’exemple non recopiés. Non rejoué.

La page Sell subscriptions as a SaaS startup, relue le 2026-10-19, vise un forfait fixe facturé en début de période. Le client paie sur une page hébergée par Stripe, puis revient sur votre site. Checkout crée cette page via l’API Checkout Sessions.

Les montants d’exemple de cette page, et ceux de Build a subscriptions integration with Checkout, ne sont pas recopiés. Tapez votre prix de test dans le Dashboard. Les commandes n’ont pas été rejouées. Pas de capture, pas de clé.

Le câblage Next.js du quickstart est déjà dans Stripe Checkout dans une app Next.js. Ce guide-ci ne réécrit pas le Route Handler. Il suit l’ordre de la page startup, et signale où cette page ne parle pas Next.js.

1. Compte Stripe

La page demande un compte : e-mail, nom, pays, mot de passe, profil d’entreprise, puis Verify your email. Nous n’avons pas créé de compte.

2. Produit et prix de test

Dans le Dashboard, la page dit de saisir un nom, un prix, une devise, une image de test, de choisir Recurring, puis Add test product. Le nom d’exemple est « Plus ». Le montant affiché sur la page n’est pas repris ici.

Build a subscriptions integration va plus loin : deux produits, Basic et Premium, chacun avec un prix récurrent mensuel, plus un troisième produit si vous voulez des frais uniques. Les montants d’exemple sont sur la page. Après création, notez les Price ID. Ils ressemblent à price_ suivi d’un identifiant. Le bouton Copy to live mode clone le produit du bac à sable vers le live. Ne le faites pas tant que le test n’a pas abouti.

Le prix reste une donnée serveur. Le client envoie un Price ID, pas un montant.

3. Encaisser, via Checkout hébergé

La page startup dit de suivre le quickstart Checkout en choisissant React côté front et Node côté back. Cet échantillon n’est pas l’onglet Next.js. Si l’app est en Next.js, prenez les fichiers de l’autre guide, et appliquez les règles de cette page :

  1. Installer la bibliothèque serveur. La page startup pointe l’ancre d’install du quickstart.
  2. Créer une Checkout Session. Le chemin de l’endpoint doit correspondre à l’attribut action du bouton.
  3. Mettre le Price ID du produit de test dans les line items.
  4. Mettre mode à subscription. Le quickstart Next.js relu, lui, écrit payment dans l’exemple. Changez-le. Un oubli vend un paiement unique, pas un abonnement.
  5. Fournir les URL de succès et d’annulation, hébergées chez vous.
  6. Rediriger le client vers l’URL renvoyée par la session.

Puis une page d’aperçu de commande, et un bouton qui part vers la page Stripe. Après le départ, la commande ne se modifie plus sans une nouvelle session. C’est la même règle que le quickstart.

Le test décrit sur la page startup ajoute un proxy vers le port 4242 dans package.json, lance npm start, ouvre http://localhost:3000/checkout, puis utilise une carte de test publiée par Stripe. L’onglet Next.js ouvre http://localhost:3000 sans ce proxy. Ces procédures appartiennent à deux exemples différents. Aucune n’a été exécutée ici.

Checkout crée un Customer quand il en faut un, par exemple pour un abonnement. Sinon il peut grouper des paiements en client invité. La page quickstart le dit. Pour un SaaS, vous voulez le Customer, parce que le portail et les factures s’y rattachent.

4. Ce que la page ajoute ensuite

Marque : logo, couleurs, police, dans les réglages de branding Checkout. Moyen de paiement : la page Payment methods. Stripe n’affiche que les méthodes éligibles, selon le lieu et selon qu’elles acceptent les abonnements.

Essai : subscription_data.trial_period_days ou subscription_data.trial_end à la création de la session. L’exemple curl de la page met un nombre de jours. Remplacez-le. Ne traitez pas ce nombre comme une recommandation de ce site.

Portail : créez-le dans le Dashboard. La page startup dit, au minimum, de permettre la mise à jour du moyen de paiement. Provide a customer portal décrit le reste : infos de facturation, changement d’abonnement, annulation immédiate ou en fin de période, factures. Une session de portail neuve expire au bout de 5 minutes si personne ne l’ouvre. Une fois utilisée, elle expire une heure après la dernière activité. Le portail ne s’affiche pas dans une iframe.

Limites écrites sur cette page, à ne pas découvrir en prod :

  • Le client peut annuler, mais pas modifier, un abonnement à plusieurs produits, à l’usage, avec essai, facturé par invoice, ou payé par une méthode non prise en charge.
  • Un abonnement qui a déjà une mise à jour planifiée via un subscription schedule ne se met pas à jour ni ne s’annule dans le portail.
  • Par défaut, une modification d’un abonnement trialing termine l’essai et facture tout de suite. Pour garder l’essai, features.subscription_update.trial_update_behavior doit valoir continue_trial.
  • Au plus 10 produits proposés quand le client change de plan.
  • Deux Price avec le même produit et le même recurring.interval ne se définissent pas. La page donne l’exemple d’un tarif étudiant : autre Product, pas autre Price sur le même.

Le guide Checkout abonnements ajoute un bouton vers une route qui crée une session de portail, et un return_url. Si le portail autorise l’annulation, il faut écouter d’autres événements que les trois du minimum. La page renvoie vers l’intégration du portail pour cette liste. Nous ne l’avons pas reconstituée de mémoire.

5. Trois événements, et pas un de moins

La page startup demande un endpoint, enregistré dans les webhooks du Dashboard, avec vérification de signature. Le secret est dans Workbench, onglet Webhooks, vue de la destination. Le nom de variable cité est STRIPE_WEBHOOK_SECRET. Ne le collez pas dans le dépôt. Sécuriser un webhook traite la signature. Stripe Checkout dans une app Next.js montre que l’exemple Next.js lit le corps brut. La page webhooks dit qu’un corps modifié fait échouer la vérification, et qu’il faut répondre 2xx avant le travail lent.

Minimum écrit sur la page startup :

ÉvénementCe que la page demande
checkout.session.completedLe client a fini Checkout. Vérifier le statut du paiement avant de provisionner l’accès.
invoice.paidChaque période payée. Continuer l’accès. Stocker subscription.id et customer.id.
invoice.payment_failedPrévenir le client et l’envoyer au portail pour changer le moyen de paiement.

checkout.session.completed confirme la fin du parcours Checkout, mais un moyen de paiement à notification différée peut encore être en attente. La documentation de fulfillment demande de vérifier payment_status et d’écouter aussi checkout.session.async_payment_succeeded pour ces moyens de paiement. Pour le cycle d’abonnement, invoice.paid confirme le règlement de la facture. Aucun de ces événements n’a été reçu ici.

Le guide Checkout abonnements présente aussi les entitlements avec checkout.session.completed, puis invoice.paid à chaque période et le portail après invoice.payment_failed. En pratique, la vérification du statut de paiement reste nécessaire avant d’ouvrir l’accès, surtout avec un moyen de paiement différé. Nous n’avons reçu aucun de ces événements.

Ancre de cycle : subscription_data.billing_cycle_anchor, timestamp UNIX futur, avant la prochaine date naturelle. L’exemple de la page est un abonnement mensuel créé un 15, ancré au 1er du mois suivant. Si le client paie après que l’ancre est passée pendant que la session est encore active, Checkout facture la période pleine à partir de l’ancre, pas le prorata d’avant. La page le dit. Nous ne l’avons pas reproduit.

6. Passer en live

L’ordre de la page : réglages du compte, type d’entreprise, taxes, détails, vérification personnelle, descriptor, compte bancaire pour les virements, authentification à deux facteurs, option taxe automatique, relecture, Agree and submit. Ensuite Stripe passe du bac à sable au live. Copier le produit vers le live est un bouton à part, déjà cité. Nous n’avons pas soumis de dossier.

Sources

Pages relues le 2026-10-19. Le guide Checkout affichait toujours la mention « Private preview ». Aucun compte, produit, webhook, portail ou paiement de test n’a été créé.