Provisioning turnkey — wizard brand (Figma) + wizard store (pool Shopify)
Audit + finalisation juin 2026. Ce document décrit le flux COMPLET "je veux lancer un store en quelques minutes" tel qu'il est réellement implémenté, et surtout pourquoi il est implémenté ainsi — chaque contrainte vient…
Audit + finalisation juin 2026. Ce document décrit le flux COMPLET "je veux lancer un store en quelques minutes" tel qu'il est réellement implémenté, et surtout pourquoi il est implémenté ainsi — chaque contrainte vient des plateformes elles-mêmes (Shopify, Figma), pas de nos choix.
Contraintes plateformes (vérifiées, juin 2026)
| Souhait | Réalité plateforme | Réponse produit |
|---|---|---|
| Créer un dev store Shopify par API | Impossible — le Partner API public n'expose aucune mutation devStoreCreate; la création vit dans le Dev Dashboard (UI). | Pool pré-provisionné (DevStorePool) : l'ops pré-crée les stores, le wizard en réclame un en quelques millisecondes — plus rapide qu'un appel de création ne le serait. |
| Transférer la propriété par API | Impossible — transfert déclenché depuis le Dev Dashboard ; le destinataire reçoit un email, accepte sous 7 jours et choisit un plan payant (help.shopify.com). | Machine à états (CLAIMED → TRANSFER_REQUESTED → TRANSFERRED) + alerte ops + détection automatique de l'acceptation (poll shop.plan_name : un dev store répond partner_test/affiliate, un vrai plan = transfert accepté). |
| Connexion auto post-transfert (custom app) | La création de custom app n'a pas d'API… | …mais la custom app installée par l'ops survit au transfert. L'IntegrationConnection créée au claim continue de fonctionner pour le nouveau propriétaire — zéro ré-autorisation. Le problème est résolu en amont, pas après coup. |
| Cloner notre template Figma par API | Impossible — l'API REST Figma ne duplique pas de fichiers. | Template publié sur Figma Community ; duplication en un clic par l'utilisateur ("Open in Figma"), puis il colle l'URL de SA copie. |
| Écrire dans le fichier Figma par API | Impossible — l'API REST est en lecture seule sur le contenu (l'écriture passe par un plugin dans l'éditeur ; l'API Variables en écriture est Enterprise-only). | Le wizard vérifie la copie via l'OAuth de l'utilisateur (preuve qu'elle est dans son compte), lit ses tokens, et exporte le brand kit en design-tokens JSON (W3C) que l'utilisateur importe dans sa copie via un plugin tokens. La mémoire système, elle, est nourrie directement par le wizard. |
| Installer des apps tierces automatiquement | Impossible — toute installation d'app passe par l'OAuth du marchand (anti-abus Shopify). | Les milestones "apps" restent des checkpoints guidés (1 clic chacun) ; tout le reste (theme, produits, collections, pages, menus, blog, légal, SEO) est 100 % automatisé. |
Wizard brand (Figma)
1. L'utilisateur choisit la source : Prompt | Upload | Figma.
2. Figma → OAuth (scope file_read) via popup
(/api/connectors/figma/authorize → callback, token chiffré AES-256-GCM).
3. Étape template : GET /api/wizard/brand/template renvoie l'URL
Community du template BoostEcom (env FIGMA_BRAND_TEMPLATE_URL).
L'utilisateur l'ouvre, clique "Open in Figma" (duplication 1 clic),
colle l'URL de sa copie.
4. POST /api/wizard/brand/template :
- rejette l'URL du template maître (preuve de non-duplication),
- vérifie la copie via le token OAuth de l'utilisateur,
- extrait palette + typographies (styles paint/text),
- persiste la référence fichier dans
StoreContext.modules.brandKit.figma { fileKey, fileName, verifiedAt }.
5. Les réponses du wizard adaptent le kit (7 étapes, draft IA streamé).
6. Étape résumé : bouton "Figma tokens" → export design-tokens JSON
(W3C) construit côté client — c'est le pont retour vers la copie
Figma (import via plugin tokens, ou notre futur plugin BoostEcom).
7. Mémoire : au launch, les faits brand.* / store.* (nom, tagline,
mission, voix, palette, typo, catégorie, audience, marchés) sont
persistés en StoreFacts (tags ["brand"], demi-vie 180 j) — chaque
spécialiste les résout sans re-demander.
Wizard store (création, pas connexion)
1. 6 étapes (identité → positionnement → audience → catalogue →
marchés → résumé + thème), draft IA streamé, seed depuis le brand
kit si l'utilisateur vient du wizard brand.
2. Submit :
a. POST /api/stores → Store row.
b. POST /api/wizard/store/provision → claim atomique d'un dev store
du pool ; Store.domain + IntegrationConnection active (token pool
chiffré). Pool vide → "waitlisted" (ops alerté, le flux continue).
c. POST /api/wizard/store/launch → spec + brandKit persistés,
29 milestones (13 auto + 16 checkpoints humains) matérialisés en
PlatformActivity, mémoire nourrie.
3. Le cron launch-tick (toutes les 15 min, `*/15 * * * *`) exécute les 13 milestones auto en
respectant le DAG dependsOn : theme (couleurs brand via
themeFilesUpsert), produits (productSet, DRAFT), collections,
pages, menus, blog, légal, SEO meta. Tout est idempotent
(tag boostecom-seed / lookup par handle / dedupeKey).
4. Écran de succès du wizard :
- lien PREVIEW (storefront derrière la password page Shopify),
- CTA "Transfer ownership" → POST /api/wizard/store/transfer
(email résolu côté serveur depuis la session — jamais du client),
- puis "Open the cockpit" → dashboard.
5. Transfert : l'ops reçoit l'alerte (email + /api/admin/devstore-pool),
déclenche l'invitation dans le Dev Dashboard ; l'utilisateur accepte
l'email Shopify (≤ 7 jours) et choisit son plan. Le cron détecte
l'acceptation (plan_name), marque TRANSFERRED, journalise
launch.transfer.accepted et envoie l'email "your store is yours"
avec l'URL du dashboard. Invitation expirée → retour CLAIMED +
re-demande possible.
6. Post-transfert : RIEN à faire — la custom app du pool survit au
transfert, la connexion, les agents, les données et le dashboard
restent actifs pour le nouveau propriétaire.
Surfaces
| Surface | Rôle |
|---|---|
POST /api/wizard/store/provision | Claim pool → binding Store + connexion |
POST/GET /api/wizard/store/transfer | Demande de transfert / état (polling UI) |
POST /api/wizard/store/launch | Spec + brand kit + milestones + mémoire |
POST /api/wizard/skill/run | Exécution manuelle d'un milestone (cockpit) |
GET /api/cron/launch-tick | Worker : milestones (DAG) + détection transferts |
GET/POST /api/admin/devstore-pool, DELETE /api/admin/devstore-pool/[id] | Inventaire ops du pool (token vérifié via shop.json, plan dev exigé, chiffré at-rest, jamais renvoyé) |
GET/POST /api/wizard/brand/template | Template Figma : URL publique / vérification de la copie + extraction + persistance |
Sécurité
- Tous les tokens (pool, custom app, Figma access + refresh) sont
chiffrés AES-256-GCM at-rest ; les lectures passent par
safeDecryptToken/getValidShopifyToken(refresh client_credentials automatique). L'audit de juin 2026 a corrigé trois écritures/lectures plaintext (connect wizard, callback Figma, runner de skills). - L'email de transfert est résolu serveur-side depuis la session (un email fourni par le client serait falsifiable).
themeUrl(zip de thème) est restreint à une allowlist d'hôtes (github.com / codeload.github.com / cdn.shopify.com).- Le pool row est lié au Store par String libre (pas de FK) : la suppression d'un Store ne peut jamais remettre en circulation un dev store déjà transféré.
- Une ligne CLAIMED ou TRANSFER_REQUESTED passe en RETIRED quand son
Store est supprimé (
releaseDevStorePoolForStores, appelé parDELETE /api/stores/[storeId]et par la cascadeDELETE /api/organizations/[orgId]). L'absence de FK justifiée ci-dessus ne couvrait que le cas TRANSFERRED : pour les deux autres, la ligne restait CLAIMED sur unstoreIdinexistant — hors du backfill waitlist delaunch-tick, et refusée parretireDevStorequi rejette précisément ces deux statuts. Le shop n'était plus joignable depuis aucune surface, et le pool se vidait sans cause affichée. - RETIRED, jamais AVAILABLE. Un shop CLAIMED porte déjà le
catalogue, le thème et le contenu du marchand qui l'avait réclamé : le
remettre en rotation le sert au marchand suivant. Un shop
TRANSFER_REQUESTED porte en plus une invitation Shopify que le
marchand peut encore accepter pendant sept jours, et qui lui donnerait
un store désormais rattaché à quelqu'un d'autre. Le
storeIdet l'orgIdsont conservés (c'est la seule trace de qui a consommé le shop) et la raison est écrite dansnotesavec sa date. Retour en rotation = action opérateur : vérifier viashop.json, vider, puis ré-enregistrer. Un statutORPHANEDdédié demanderait une migration d'enum et n'est pas posé ici.
Runbook ops — garder le pool chaud
- dev.shopify.com → Stores → Create a dev store (préfixe
boostecom-pool-…). - Installer la custom app BoostEcom (scopes Admin complets : products, content, themes, menus, pages, blogs…).
POST /api/admin/devstore-pool { shopDomain, accessToken, notes }— le token est vérifié (GET /shop.json, plan dev exigé) puis chiffré.- Surveiller les alertes "pool EMPTY" (email ADMIN_EMAIL + activité
launch.provision.waitlisted) et viser ≥ 3 stores AVAILABLE. - Sur alerte "transfer requested" : Dev Dashboard → Client transfer → Transfer store → email indiqué. Le reste (détection, email de félicitations, dashboard) est automatique.
Was this page helpful?