Stripe Connect (OAuth) — connexion en un clic
TL;DR : depuis la Phase 18, tu n'as plus besoin de copier-coller
sk_live_…,pk_live_…etwhsec_…pour connecter ton Stripe. L'opérateur clique sur Connect Stripe, se logge sur Stripe.com, autorise Trackily, et revient avec la lignepayment_accountsdéjà créée + le webhook auto-configuré. Ce document est le walkthrough end-to-end : (a) setup plateforme (opérateur, une fois), (b) flow merchant (à chaque connexion).

Pourquoi Stripe Connect
Avant la Phase 18, connecter un Stripe à Trackily impliquait pour le marchand de :
- aller sur dashboard.stripe.com → API keys → copier
sk_live_… - aller sur dashboard.stripe.com → Webhooks → créer un endpoint avec la bonne URL → copier
whsec_… - revenir sur Trackily → coller les deux clés dans Settings → Payments
Trois étapes, plusieurs onglets, fort risque d'erreur (mauvais endpoint webhook, mauvais events sélectionnés, oubli du whsec_, etc.). Pour un opérateur peu technique, c'était la barrière la plus haute de l'onboarding.
Stripe Connect Standard OAuth résout tout en un clic :
- Marchand clique Connect Stripe.
- Redirigé vers Stripe → se logge → autorise.
- Revient sur Trackily →
payment_accountscréé, webhook créé chez Stripe, secrets stockés chiffrés.
Total : 1 clic + 1 login Stripe.
Architecture
Le système a deux rôles :
┌────────────────────────────┐
│ PLATFORM (trackily.online)│ ← Détient le
│ - commerce_stripe_oauth_ │ client_id +
│ client_id │ secret OAuth.
│ - commerce_stripe_oauth_ │
│ client_secret │ Sert de proxy
│ - /oauth/stripe/start │ pour les
│ - /oauth/stripe/callback │ instances
└─────────────┬──────────────┘ clientes.
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ CLIENT instance │ │ CLIENT instance │ │ CLIENT instance │
│ (self-hosted) │ │ (self-hosted) │ │ (trackily.online │
│ │ │ │ │ pour ses propres│
│ /admin/oauth/ │ │ /admin/oauth/ │ │ besoins) │
│ stripe/start │ │ stripe/start │ │ │
│ /admin/oauth/ │ │ /admin/oauth/ │ │ │
│ stripe/callback │ │ stripe/callback │ │ │
└──────────────────┘ └──────────────────┘ └──────────────────┘
Une instance Trackily peut jouer les deux rôles à la fois : trackily.online est sa propre cliente. Mais le rôle PLATFORM est mutualisable — toutes les instances self-hosted qui pointent leur commerce_oauth_proxy_url sur trackily.online profitent du même client_id Stripe sans avoir besoin du leur.
State signé HMAC
Tout le state (return URL, account name, livemode flag, access_token) transite en clair dans l'URL (Stripe te renvoie sur ton callback avec ?code=…&state=…). Pour qu'un attaquant ne puisse pas forger un state qui te ferait stocker un faux compte, chaque payload est signé HMAC-SHA256 avec SECRETS_MASTER_KEY. Le récepteur vérifie la signature avant de toucher la base.
function _signOauthPayload(payload) {
const sig = crypto.createHmac('sha256', process.env.SECRETS_MASTER_KEY)
.update(payload).digest('hex');
return payload + '.' + sig;
}
TTL du payload : 15 minutes. Au-delà, le callback refuse.
Webhook auto-créé
Après l'échange du code OAuth contre l'access_token, Trackily fait un appel API immédiat à Stripe pour créer le webhook endpoint correspondant à la nouvelle connexion :
POST https://api.stripe.com/v1/webhook_endpoints
Authorization: Bearer <access_token>
Body: {
url: 'https://ton-instance.com/webhook/stripe/native-pending',
enabled_events[]: 'checkout.session.completed',
description: 'Trackily native commerce (auto-created via OAuth)'
}
Le whsec_… retourné est stocké dans webhook_secret. Si l'auto-create échoue (rare — typiquement quand l'access_token n'a pas le scope webhooks:write), la ligne payment_accounts est quand même créée et tu pourras configurer le webhook manuellement.
Setup plateforme (opérateur, UNE FOIS)
Cette section est pour l'opérateur qui héberge l'instance Trackily (toi si tu es self-hosted, ou l'équipe Trackily pour trackily.online). À faire une seule fois, ensuite tous les marchands de l'instance peuvent connecter Stripe en un clic.
Étape 1 — Activer Connect dans Stripe
- Va sur dashboard.stripe.com/settings/connect.
- Tu vois 3 modes : Standard, Express, Custom. Choisis Standard (le plus simple — le marchand garde son propre dashboard Stripe, Trackily récupère juste un access_token).
- Si tu n'as jamais activé Connect, tu auras un onboarding court de Stripe (nom de plateforme, logo, contact). Remplis-le. C'est ce qui apparaîtra sur la page d'autorisation Stripe que verra le marchand.
- Une fois activé, descend à Integration → OAuth settings.
Étape 2 — Récupérer le client_id
Toujours dans Settings → Connect → OAuth :
- Tu trouves ton client_id au format
ca_XXXXXXXXXXXXXXXX. - Copie-le.
C'est l'identifiant de ta plateforme côté Stripe. Public — il vit dans les URLs de redirection vers Stripe.
Étape 3 — Whitelister le redirect URI
Toujours dans la même page :
- Section Redirect URIs.
- Ajoute :
https://<ton-instance>/oauth/stripe/callback.- Exemple pour la plateforme officielle :
https://trackily.online/oauth/stripe/callback. - Exemple pour une instance self-hosted qui sert aussi de proxy :
https://app.monsite.com/oauth/stripe/callback.
- Exemple pour la plateforme officielle :
- Save.
Important : ce redirect URI est la route PLATFORM, pas la route CLIENT. Si tu te trompes, l'OAuth dance échoue avec "redirect_uri mismatch".
Étape 4 — Configurer dans Trackily
- Sur ton instance Trackily, va dans Settings → Payments.
- Tu vois en haut une carte Stripe Connect (platform OAuth) avec un statut "Non configuré".
- Remplis :
- OAuth client_id — le
ca_XXXde l'étape 2. - OAuth client_secret — c'est la secret key de ton compte Stripe principal (pas une nouvelle clé). Va sur dashboard.stripe.com/apikeys, copie ta
sk_live_…(ousk_test_…si tu démarres en test).
- OAuth client_id — le
- Le panneau affiche en encart un Redirect URL to whitelist on Stripe — c'est l'URL de l'étape 3, calculée automatiquement. Copie-la et vérifie qu'elle est dans Stripe.
- Clique Save.
L'admin UI envoie un POST /admin/api/settings qui stocke :
commerce_stripe_oauth_client_id→ca_XXXcommerce_stripe_oauth_client_secret→ task_live_…(chiffrée at-rest)
Ces deux valeurs sont la clé du royaume côté plateforme. Quiconque y accède peut autoriser des connexions OAuth à ton nom. Trackily les stocke dans
settings, chiffrées via le wrappergetSetting/setSettingquand la clé matchecommerce_stripe_oauth_client_secret.
Étape 5 — Configurer le proxy (pour les instances clientes)
Cette étape concerne les instances self-hosted qui veulent utiliser le client_id de trackily.online plutôt que le leur. Si tu as configuré ton propre client_id à l'étape 4, skip.
- Sur l'instance cliente, va dans Settings → Payments (ou Settings → General selon ton thème).
- Champ OAuth proxy URL — colle
https://trackily.online. C'est la default value, donc si tu n'as rien touché, c'est déjà bon. - Save. Désormais, le bouton Connect Stripe redirige le marchand vers
https://trackily.online/oauth/stripe/start, qui à son tour le redirige vers Stripe avec leclient_iddetrackily.online. Au retour, le payload signé revient sur ton instance et la lignepayment_accountsest créée chez toi.
Le secret
SECRETS_MASTER_KEYdoit être partagé entre le proxy et l'instance cliente pour que la signature HMAC soit vérifiable des deux côtés. Pourtrackily.onlineproxifiant des instances tierces, le secret est partagé via une variable d'environnement gérée par l'équipe Trackily.
Flow merchant (à chaque connexion, simple)
C'est l'étape côté marchand. Si l'opérateur a fait son boulot à la section précédente, le marchand n'a quasiment rien à faire.
Étape 1 — Cliquer "Connect Stripe"
Le marchand va dans Settings → Payments.
Il voit le bouton violet Connect Stripe en haut à droite.

Avant de cliquer, il peut donner un nom au compte qui sera créé (champ "Name for this account"). Default : "Stripe Account".
Clique.
Étape 2 — Redirection vers Stripe
L'URL de redirection est construite par buildOAuthAuthorizeUrl :
https://connect.stripe.com/oauth/authorize?
response_type=code&
client_id=ca_XXXXXXXXXXXXXXXX&
scope=read_write&
redirect_uri=https%3A%2F%2Ftrackily.online%2Foauth%2Fstripe%2Fcallback&
state=<HMAC-signed-payload>
Le marchand voit la page d'autorisation Stripe. Si il est déjà loggé, il voit directement le récap. Sinon, il se logge.
L'écran d'autorisation montre :
- Le nom de ta plateforme ("Trackily").
- Le scope demandé (
read_write— Stripe l'affiche en "manage your Stripe data"). - Un bouton Sign in ou Connect my Stripe account.
Étape 3 — Stripe redirige sur PLATFORM/callback
Une fois autorisé, Stripe redirige le marchand sur :
https://trackily.online/oauth/stripe/callback?
code=ac_XXXXXXXXXXXXXX&
state=<HMAC-signed-payload>
Côté Trackily PLATFORM :
/oauth/stripe/callbackvérifie la signature HMAC du state.- Vérifie le TTL (15 min).
- Échange le code via
stripeApi.exchangeOAuthCode({ clientSecret, code }):POST https://connect.stripe.com/oauth/token Authorization: Bearer <sk_live_de_la_platform> Body: grant_type=authorization_code&code=ac_XXX - Stripe répond avec :
{ "access_token": "sk_live_xxx_scopé", "refresh_token": "rt_xxx", "scope": "read_write", "livemode": true, "stripe_publishable_key": "pk_live_xxx", "stripe_user_id": "acct_xxxxxxxxxxxx", "token_type": "bearer" } - Trackily re-signe le payload (HMAC) et redirige le marchand sur :
https://<instance-cliente>/admin/oauth/stripe/callback?oauth_payload=<re-signed-payload>
Étape 4 — Instance cliente reçoit le payload, crée la ligne
Côté Trackily CLIENT (/admin/oauth/stripe/callback) :
- Vérifie la signature HMAC.
- Vérifie le TTL.
- Crée le webhook endpoint chez Stripe (best-effort) :
POST https://api.stripe.com/v1/webhook_endpoints Authorization: Bearer <access_token> Body: url=…/webhook/stripe/native-pending & enabled_events[]=checkout.session.completed - Insert / upsert
payment_accounts:{ provider: 'stripe', name: accountName, // depuis le client_state mode: livemode ? 'live' : 'test', api_key: publishable_key, // pk_live_… api_secret: '', // pas utilisé pour OAuth webhook_secret: whsec_…, // auto-créé is_active: true, oauth_user_id: stripe_user_id, // acct_xxx oauth_access_token: access_token, // sk_live_xxx scopé (chiffré) oauth_refresh_token: refresh_token, // (chiffré) oauth_scope: 'read_write', oauth_livemode: true, oauth_connected_at: NOW() } - Renvoie au marchand une page de confirmation qui auto-redirige vers
/admin#commerce/payment-methods2 secondes plus tard.
Le marchand voit "✓ Stripe connected successfully — Account: Mon compte Stripe (live mode) — Stripe user id: acct_…".
Étape 5 — Le compte est utilisable immédiatement
De retour sur Settings → Payments, le marchand voit sa nouvelle ligne dans la grid des comptes paiement, avec un badge "OAuth ✓" et le acct_… Stripe. Il peut désormais l'attacher à une landing (payment_account_ids: [<id>]) ou le définir comme default.
Disconnect
Pour casser le lien :
- Sur la card du compte → bouton Disconnect.
- Sous le capot :
POST /admin/oauth/stripe/disconnect/:id - Trackily appelle
revokeOAuthGrant({ clientSecret, stripeUserId })côté Stripe pour révoquer le grant. - Côté local, les colonnes
oauth_*sont vidées,is_active=false. La ligne reste pour préserver les commandes historiques (qui ont leurpayment_account_idtoujours valide).
Si le marchand veut re-connecter le même compte Stripe plus tard, il refait Connect → l'upsert détecte le oauth_user_id existant et réactive la ligne (au lieu d'en créer une doublon).
Limitations connues
- Pas de refresh_token usage — Stripe Standard ne fait pas tourner les access_tokens (ils n'expirent pas tant que le grant est actif). Le refresh_token est stocké pour usage futur mais jamais utilisé. Si Stripe change cette politique, on bricolera un cron.
- Pas de multi-account par OAuth (par défaut) — le même compte Stripe ne peut être connecté qu'une seule fois (clé sur
oauth_user_id). Pour des cas multi-marque où tu veux le même compte sous deux noms, fais un Connect puis duplique manuellement avec un nom différent. - Le webhook auto-créé n'écoute QUE
checkout.session.completed— c'est l'event minimum nécessaire pour valider une commande. Si tu veux tracker les subscriptions (customer.subscription.*), ajoute les events manuellement dans Stripe → Webhooks → ton endpoint auto-créé → Edit events. - Live et test sur le même
acct_— un compte Stripe a deux modes (live et test). L'OAuth renvoielivemode=truequand le marchand a autorisé sur le mode live,falsesinon. Pour avoir les deux en parallèle dans Trackily, fais deux Connect (un en live, un en test — Stripe te bascule selon l'état du toggle dans son dashboard au moment du connect).
Erreurs courantes
- "Stripe Connect not configured on this platform" —
commerce_stripe_oauth_client_idest vide. L'opérateur a oublié l'étape 4 du setup plateforme. - "Invalid redirect_uri" côté Stripe — le redirect URI n'est pas whitelisté dans le dashboard Connect. Re-vérifie l'étape 3.
- "Invalid state — possible tampering" — la signature HMAC ne matche pas. Vérifie que
SECRETS_MASTER_KEYest le même sur la PLATFORM et le CLIENT. Si tu as changé la clé entre l'issue et la consume du state, le payload est invalide. - "OAuth state expired, please retry" — le marchand a mis plus de 15 min à compléter le flow. Il refait Connect, ça marche.
- "Webhook auto-create failed" — l'access_token n'a pas le scope
webhooks:write(rare) ou la requête vers Stripe a foiré. Le compte est quand même créé. Va dans Stripe → Webhooks → Add endpoint manuellement (URL =/webhook/stripe/native-pending, events =checkout.session.completed), récupère lewhsec_…, colle-le dans Trackily via Edit. - "Stripe Connect button n'apparaît pas" — soit
commerce_stripe_oauth_client_idest vide côté plateforme, soit la dernière build de l'admin UI n'a pas été déployée. Refresh la page. - "Le compte se reconnecte sur la mauvaise instance" — le marchand a deux instances Trackily ouvertes dans deux onglets. Le state mêle l'origine, le marchand revient sur la mauvaise. Ferme un onglet, recommence.
- "Disconnect a échoué mais le compte semble parti localement" — le revoke vers Stripe est best-effort (catch + ignore). Localement, les
oauth_*sont vidés. Si le revoke a foiré, le marchand peut révoquer manuellement le grant côté Stripe → Settings → Apps & integrations → trouve "Trackily" → Disconnect.
Voir aussi
- payment-accounts.md — la table
payment_accountset le flow manuel équivalent. - orders.md — comment
payment_account_idest utilisé sur les commandes. - refunds.md —
refund_native_orderutilise l'access_token OAuth de la même manière qu'unsk_live_manuel. - Code source :
stripe-helpers.js(buildOAuthAuthorizeUrl,exchangeOAuthCode,revokeOAuthGrant),server.jslignes 11944-12154 (/oauth/stripe/*et/admin/oauth/stripe/*).