Runbooks & opérationsLe compte Stripe de developpement, et ce qui reste a faire dessus

Le compte Stripe de developpement, et ce qui reste a faire dessus

Cet etat n'existait que dans une conversation. Il est ici pour survivre a sa fermeture : quel compte, quels Price IDs, ce qui est configure, ce qui ne l'est pas et pourquoi.

Cet etat n'existait que dans une conversation. Il est ici pour survivre a sa fermeture : quel compte, quels Price IDs, ce qui est configure, ce qui ne l'est pas et pourquoi.

Le compte

acct_1T7oJZ3Puq5YY65L, sandbox, FR, devise par defaut EUR.

Le mot compte, et ce document a d'abord ecrit l'autre. Un compte Stripe ordinaire a deux modes, test et live, sous un seul acct_ : « passer en live » y veut dire basculer un interrupteur et recreer les objets de l'autre cote. Un sandbox n'est pas ce compte-la. C'est un compte a part entiere, permanent, qui n'a aucun mode live et n'en aura jamais.

La preuve est un refus de l'API, pas une lecture de tableau de bord :

GET /v1/products  (acct_1T7oJZ3Puq5YY65L, livemode: true)
-> The provided account acct_1T7oJZ3Puq5YY65L is a sandbox account.
   Retry with livemode set to false.

Ce que « sandbox » coute se lit plus bas, dans Le passage en live : rien de ce qui suit n'est migrable.

Il y a deux comptes Stripe nommes boostecom.app, tous les deux des sandbox, et l'API ne les distingue ni par leurs produits (les deux etaient vides) ni par leur solde (les deux en EUR). Ce qui les separe :

acct_1T7oFI3OYmwbBTJNacct_1T7oJZ3Puq5YY65L
Cree17727645681772764833, 265 s plus tard
business_profileaucun« Environnement de test boostecom.app » + URL
Libelle bancaireCHRISTOPHER LASGIBOOSTECOM.APP
FuseauEtc/UTCEurope/Paris

Le premier est un brouillon, le second celui qui a ete configure deliberement. Tout a ete monte sur le second. Le doublon n'est pas supprimable par l'API : aucune operation de suppression de compte n'existe dans la surface exposee, c'est une action de tableau de bord.

Les Price IDs

Sept prix, tous en USD et non en EUR : PLAN_PRICING (src/types/billing-plans.ts) est libelle en dollars, et la devise par defaut du compte ne change pas ce que le catalogue declare. C'est le piege devise que cac-report.ts nomme deja pour la depense publicitaire, rencontre ici pour de vrai.

NEXT_PUBLIC_STRIPE_PRO_MONTHLY=price_1UEfAj3Puq5YY65LHh37xyaz      #    79 $/mois
NEXT_PUBLIC_STRIPE_PRO_YEARLY=price_1UEfAl3Puq5YY65LkFEh4Bjl       #   750 $/an
NEXT_PUBLIC_STRIPE_MAX_5X_MONTHLY=price_1UEfAw3Puq5YY65LP58txpmG   #   199 $/mois
NEXT_PUBLIC_STRIPE_MAX_5X_YEARLY=price_1UEfB03Puq5YY65LlGd4IPyi    #  1910 $/an
NEXT_PUBLIC_STRIPE_MAX_20X_MONTHLY=price_1UEfB83Puq5YY65LImyOUouV  #   399 $/mois
NEXT_PUBLIC_STRIPE_MAX_20X_YEARLY=price_1UEfBC3Puq5YY65LWkPD3ePU   #  3830 $/an

Ces sept-la ne sont pas des secrets : six sont NEXT_PUBLIC_ par construction et partent dans le bundle navigateur. Le huitieme l'est et n'est donc pas ici :

STRIPE_WEBHOOK_SECRET=whsec_…   # rendu UNE SEULE FOIS, a la creation

Stripe ne re-affiche jamais un signing secret. Perdu = recreer l'endpoint.

Tous les prix portent tax_behavior: "exclusive" : la taxe s'ajoute au-dessus, ce qui correspond au fait que le grant de credits lit metadata.amount (hors taxe) et jamais amount_total.

Le webhook

we_1UEfBm3Puq5YY65L4xDQ2C91, status: enabled, vers https://boostecom.app/api/webhooks/stripe.

Les 14 evenements sont exactement ceux que src/services/webhooks.ts traite, ni un de plus :

checkout.session.completed
customer.subscription.created | updated | deleted
invoice.paid | payment_failed | payment_action_required
payment_intent.succeeded | payment_failed
charge.refunded | dispute.created | dispute.closed
payout.failed
account.updated

Ce qui N'A PAS ete cree, et pourquoi

Pas de prix trimestriel

Il n'y a pas de cadence trimestrielle a configurer : le proprietaire l'a supprimee le 2026-10-02 (billing/0373). Les trois variables NEXT_PUBLIC_STRIPE_*_QUARTERLY ont quitte le schema (src/env/server.ts), .env.example et l'audit d'environnement. Deux cadences sont vendues : mensuel et annuel, six Price IDs.

Rien pour trois des quatre entrees d'argent

Le systeme complet d'entree et de sortie d'argent, releve depuis le code :

FluxCheminBesoin Stripe
Abonnementsapi/stripe/checkout6 Price IDs — faits
Boutique additionnellenon vendue (ADR 0037, puis 0039 : boutiques incluses par palier, sans add-on)l'ancien Price ID price_1UEfCo3Puq5YY65L1Dq4YEOb reste dans le sandbox, porte par aucun abonnement (verifie le 2026-09-25). A garder pour un eventuel retour de l'add-on, a ne pas copier en live
Achat de creditsapi/credits/purchaseaucun : price_data, montant libre USD
Sponsorsapi/sponsor/checkoutaucun : price_data, montants de SPONSOR_PRICE_CENTS (services/billing/items.ts), USD depuis billing/3137
Marketplaceapi/marketplace/.../checkoutConnect
Remboursementsservices/stripe/refundsaucun
Payouts vendeurs + clawbackservices/stripe/connectConnect

Le 9 $/boutique n'invente rien : ADDITIONAL_STORE_PRICE_MONTHLY = 9, et les cartes de pricing le vendent deja. C'est ce qui le distingue du trimestriel.

Le portail client (sandbox)

Le sandbox n'avait aucune configuration de portail : billing-portal et le changement de plan de checkout (qui ouvrent la configuration par defaut) echouaient donc. Creee le 2026-09-25 et marquee par defaut : bpc_1UJUs53Puq5YY65Lh7zaYgcI.

ReglageValeurPourquoi
Changement de planPro, Max 5x, Max 20x, mensuel et annuelles 6 prix du catalogue ; pas le trimestriel (non vendu), pas l'ancien add-on
Montee de palieralways_invoicefacturee tout de suite : le webhook recredite sur la montee, donc elle doit etre payee avant
Descente de palier ou d'intervallea la fin de la periodesinon monter puis redescendre dans l'heure donnait les credits du palier superieur quasi gratuitement
Annulationen fin de periode, motif demande
Factures, moyen de paiement, adresse, numero de TVAmodifiables

En live, la meme configuration est a recreer (ou « Copy to live mode » depuis le Dashboard), avec les Price IDs live.

Tester de bout en bout (sandbox)

boostecom.app tourne sur les cles du sandbox (les sessions Checkout du sandbox renvoient vers www.boostecom.app). Carte de test : 4242 4242 4242 4242, date future, CVC quelconque. Carte refusee : 4000 0000 0000 9995. Carte 3DS : 4000 0025 0000 3155.

#ActionAttendu
1Creer un compte neuf (/auth), une organisationPlan Free, 0 credit
2Connecter 1 boutique, puis tenter une 2eLa 2e est refusee (402) et propose Pro
3/pricing → Pro mensuel → payer avec 4242~/billing/activate passe au vert, plan Pro, 49 $ de credits, bonus quotidien 1 $
4Creer les boutiques 2 et 3, puis une 4e2 et 3 passent ; la 4e est refusee et propose Max 5x
5~/billing → Gerer l'abonnement → passer a Max 5xFacture immediate au prorata, plan Max 5x, credits completes a 149 $
6Portail → redescendre a ProProgramme pour la fin de periode, plan inchange d'ici la
7Portail → annulerActif jusqu'a la fin de periode, puis Free ; les boutiques restent, aucune creation au-dela d'une
8Refaire 3 avec la carte 4000 0000 0000 9995Echec affiche, plan inchange
9Refaire 3 avec la carte 3DSDefi 3DS, puis actif
10/admin/revenue/webhooksChaque evenement en succes, aucun en rouge

L'annuel ne s'ouvre qu'a une organisation de plus de 30 jours (billing-cadence) : pour le tester, utiliser une organisation ancienne.

Releve par l'API le 2026-09-12, pas par impression :

Preuve
Compte activecharges_enabled: true, payouts_enabled: true, details_submitted: true, ToS acceptes, compte bancaire verifie
Connect activePOST /v1/_unstable/connect/enable rend {"enabled": true}. Marketplace et payouts vendeurs sont donc debloques

Le premier appel a EnableConnect avait rendu un timeout a 9,9 s, et GET /v1/accounts repondait ensuite sans erreur. Ce n'etait PAS une preuve : le meme appel repond pareil sur le compte doublon, ou Connect n'a jamais ete active. Le test controle ne discriminait pas. Seul le rappel de l'operation a tranche.

Ce qui reste, et qui ne peut pas etre fait par l'API

  1. Poser les 8 variables ci-dessus.
  2. Remplir head_office. GET /v1/tax/settings rend status: "pending", missing_fields: ["head_office"]. C'est une adresse d'etablissement, donc une declaration fiscale : elle ne s'invente pas.
  3. Supprimer le doublon acct_1T7oFI3OYmwbBTJN. Toujours present au 2026-09-12.

Numero de TVA intracommunautaire (autoliquidation B2B)

La saisie du numero de TVA au Checkout n'est pas un reglage du Dashboard : c'est le parametre d'API tax_id_collection: { enabled: true }, pose par src/app/api/stripe/checkout/route.ts (abonnement) et src/app/api/credits/purchase/route.ts (credits). Quand un customer existant est passe, la session porte aussi customer_update: { name: "auto", address: "auto" } (Stripe refuse tax_id_collection sur un customer existant sans customer_update.name) ; sans customer, l'abonnement en cree un et l'achat de credits passe customer_creation: "always", ce qui garde le numero sur le customer.

Ce qui reste cote Dashboard, d'apres la doc Stripe Tax : Stripe Tax actif avec head_office rempli (point 2 ci-dessus) et les enregistrements fiscaux (Tax > Registrations) des pays ou BoostEcom est immatricule. L'autoliquidation n'est calculee que pour un acheteur situe dans un pays de l'UE autre que celui d'un enregistrement, avec un numero valide. Le checkout sponsor (pilier marketplace) et le checkout marketplace (retire par marketplace/3145) ne collectent pas le numero.

Le passage en live

Le compte ci-dessus etant un sandbox, il n'y a rien a basculer. Aucun des objets releves dans ce document ne traverse : ni les sept Price IDs, ni le webhook we_…, ni l'activation Connect, ni les reglages Tax. Le jour du passage en production, tout est a recreer sur un autre acct_, et les identifiants seront differents jusqu'au dernier caractere.

Ce n'est pas une mauvaise nouvelle, c'est une nouvelle a connaitre avant d'y etre : travailler en sandbox reste le bon choix tant qu'aucun vrai utilisateur n'est la, parce qu'un sandbox ne peut structurellement pas encaisser un vrai paiement par erreur.

Les comptes joignables, et lequel est lequel

Compteacct_LiveSandbox
boostecom.appacct_1T7oJZ3Puq5YY65Lnonoui (celui-ci)
boostecom.appacct_1T7oFI3OYmwbBTJNoui (vide au 2026-09-25 : 0 produit, 0 prix, 0 abonnement)oui (vide)
boostecom.fracct_1FrGnWKdjXqy9xFNouioui
easyconnector.appacct_1T7od5Kn1YFvny11ouioui

Au 2026-09-25, acct_1T7oFI3OYmwbBTJN repond en live sous le nom boostecom.app : c'est le compte de production de la plateforme, et il est vide. Il n'est donc plus a supprimer comme doublon. Le releve du 2026-09-12 (« aucun compte live ne s'appelle boostecom.app ») est perime.

Le piege : une cle live branchee sur le mauvais compte

Il decoule directement du tableau. Une cle live « de boostecom.app » donnee a un tiers (DataFast, un tableau de bord, un connecteur) ne peut pas venir d'un compte qui n'existe pas : elle vient de boostecom.fr ou d'easyconnector.app.

Le tiers rapporte alors du revenu reel, arrive vraiment, correctement attribue... a la mauvaise entreprise. Rien n'echoue, rien ne s'affiche en rouge, et le chiffre a l'air juste. Le seul controle qui tranche est de comparer, dans le tableau de bord du tiers, l'acct_ connecte a la colonne acct_ ci-dessus : le nom affiche ne suffit pas, puisque deux comptes portent le meme.

Ce que la migration demande reellement

Douze variables portent Stripe dans src/env/server.ts. La liste est derivee, pas retapee : src/test/stripe-setup-doc-covers-the-price-keys.test.ts echoue si une cle du schema manque ici.

VariableA refaire en livePourquoi
STRIPE_SECRET_KEYouila cle EST le compte
STRIPE_WEBHOOK_SECRETouirendu une seule fois, a la creation de l'endpoint
NEXT_PUBLIC_STRIPE_PRO_MONTHLYouiPrice ID, lie au compte
NEXT_PUBLIC_STRIPE_PRO_YEARLYouiPrice ID, lie au compte
NEXT_PUBLIC_STRIPE_MAX_5X_MONTHLYouiPrice ID, lie au compte
NEXT_PUBLIC_STRIPE_MAX_5X_YEARLYouiPrice ID, lie au compte
NEXT_PUBLIC_STRIPE_MAX_20X_MONTHLYouiPrice ID, lie au compte
NEXT_PUBLIC_STRIPE_MAX_20X_YEARLYouiPrice ID, lie au compte

Et trois choses qui ne sont pas des variables : les 14 evenements du webhook, l'activation Connect (sans quoi marketplace et payouts vendeurs sont morts) et les reglages Tax, head_office compris.

Une seule chose traverse reellement : ce fichier. Il decrira alors deux jeux d'identifiants et non un.