profound-logoProfound CMS
⌘K
Admin
Theme
DocsTutorialBlogPhilosophy
DocsTutorialBlogPhilosophy

Tutorials

Build & Ship an Airport DirectoryDeploymentsBuild & Ship a Stripe Storefront

Feature

Documentation Site TemplateFeature Template BuilderTranslation ServiceOrganizations & Website HeirarchyConnect Profound CMS to your AI clientSettings IntegrationsSettings API KeysSettings UsageSettings Websites
All Systems Operational
Powered Byprofound-logo
Theme

Build & Ship a Stripe Storefront

Un tutoriel pratique : construisez une boutique Stripe pilotée par le contenu sur Profound CMS — un catalogue qu’un commerçant édite sans code, deux routes paramétriques, un panier headless et un checkout hébergé Stripe.

La boutique terminée en action — naviguez dans une catégorie, ouvrez un produit, ajoutez-le au panier, passez en caisse.

Un tutoriel pratique qui construit une boutique pilotée par le contenu sur Profound CMS : un catalogue de produits (catégories + articles) modélisé dans le CMS, des pages de liste et de détail issues d’un même ensemble de routes, et un checkout hébergé Stripe livré en tant que composant headless.

L’ossature repose sur du Next.js écrit à la main associé à l’admin Profound. Claude Code (via le MCP Profound) fait l’essentiel du travail sur trois tâches — le préremplissage du catalogue, le câblage du système de design et l’écriture des composants de la boutique (y compris le panier headless). Trois parties : Configuration, Construction, Production.

Des paiements en une ligne. Nous utilisons Stripe Checkout hébergé : l’acheteur paie sur la page de Stripe, pas la vôtre. Votre application fait seulement deux choses côté serveur — créer une session Checkout et vérifier un webhook. Pas de champs carte, pas de Stripe Elements, pas de charge PCI.

Ce que vous allez construire

  • Un catalogue publié de petite taille dans le CMS — trois catégories et huit produits (la démo « Edison's Inventions ») — chacun éditable par un commerçant sans code.
  • Deux routes paramétriques (/products/{item_code}, /categories/{category_code}) plus un /cart statique, tous basés sur un seul ensemble de composants.
  • Un panier headless (useCart) et un checkout hébergé Stripe, avec le prix toujours résolu côté serveur à partir d’un identifiant de prix Stripe.
  • La boutique déployée sur Vercel avec aperçu en direct et édition in situ pour l’équipe.

Prérequis

  • Bun ≥ 1.3 — curl -fsSL https://bun.sh/install | bash
  • Claude Code avec le MCP Profound (installé dans la partie 1).
  • Un compte Profound CMS.
  • Un compte Stripe. Ce tutoriel se déroule en mode test pour que rien ne prélève de véritable argent pendant la construction — mais le flux est identique avec des clés live, donc utilisez les clés de votre compte réel si vous le souhaitez. (Le mode test ne nécessite aucun détail d’entreprise ou bancaire.)
  • Le Stripe CLI (stripe login) pour les webhooks locaux.
  • Pour le déploiement : le GitHub CLI (gh) et un compte Vercel connecté à GitHub.

Comment les pièces s’assemblent

Profound sépare le contenu du rendu :

  • Les composants définissent la forme du contenu. Un composant personnalisé avec un champ Route Slug est routable (category, item) ; un composant marqué UI Element est plaçable sur une page (nav, product_grid, …).
  • Les documents sont le contenu (un produit, une catégorie).
  • Les éléments d’interface sont des sections de page ; chaque champ scalaire prend une valeur statique ou une expression CEL, évaluée au rendu.
  • Les routes paramétriques associent une URL à un document + des éléments d’interface, en transmettant les paramètres de route (meta.params.* en CEL, routeParams en React).
  • Votre application Next.js les lit via cms-renderer; Stripe est ajouté comme de simples routes API.

La règle qui structure la construction : CEL ne lie que les champs string/number. Donc l’habillage scalaire (marque de la nav, pied de page, titres) est lié avec CEL, tandis que tout ce qui est riche ou une collection (une grille de produits, une galerie d’images, du rich text) est récupéré à l’intérieur du composant React par paramètre de route. Et Stripe est la source de vérité tarifaire — le price du CMS n’est qu’un affichage ; le débit est toujours résolu côté serveur à partir d’un identifiant de prix Stripe.

Partie 1 — Configuration

État final : un petit catalogue publié, l’application câblée pour le lire, Stripe installé, design en place — rien n’est encore rendu.

1. Inscrivez-vous et créez le site web

Inscrivez-vous sur Profound (authentification WorkOS). Créez un site nommé store, puis copiez son ID de site web (l’UUID dans l’URL de l’admin) et une clé API niveau lecture (Deployments → Create API key). L’application ne fait que lire ; le préremplissage du catalogue passe ensuite par le MCP, qui s’authentifie séparément.

2. Générez l’application, connectez-la et ajoutez Stripe

bunx create-profound-next store
cd store
bun add stripe

Le squelette est un projet Next.js App Router pré-câblé pour Profound (le SDK cms-renderer, une route catch-all, un script generate-schemas, un <Refresher>). Il n’embarque aucun style. bun add stripe récupère le SDK serveur — la seule dépendance de paiement nécessaire pour un checkout hébergé.

Ajoutez vos valeurs à .env.local :

# CMS
PROFOUND_API_KEY=<votre clé lecture>
NEXT_PUBLIC_PROFOUND_WEBSITE_ID=<votre ID de site>
NEXT_PUBLIC_CMS_API_URL=https://cms.dev.tryprofound.com
NEXT_PUBLIC_BUNNY_CDN_URL=https://cms-profound.b-cdn.net   # sert les images hébergées par le CMS

# Stripe
STRIPE_SECRET_KEY=sk_test_...            # clé test ici ; remplacez-la par votre clé live lorsque vous passez en production
STRIPE_WEBHOOK_SECRET=whsec_...          # à renseigner à l’étape 4 de la Construction
NEXT_PUBLIC_SITE_URL=http://localhost:3000

Récupérez STRIPE_SECRET_KEY dans Stripe → Developers → API keys. Nous utilisons une clé test (sk_test_…) pour que la construction ne manipule jamais de vrai argent ; basculez sur votre clé live quand vous serez prêt à accepter des paiements réels. Exécutez bun dev et ouvrez localhost:3000 — le starter s’affiche.

Le checkout hébergé redirige le navigateur vers une URL Stripe, donc la clé secrète serveur suffit à Stripe — pas de clé publiable, pas de SDK client Stripe.

3. Définissez les composants category, product_image et item

Créez trois Custom Components (Components → Create new component) — source de données, donc sans tag UI Element. Activez chacun (Active).

Le CMS n’a pas de champ « tableau d’images », donc une galerie est un tableau de références vers un petit composant product_image. Créez category et product_image (et activez-les) avant item — un champ de référence ne cible que des composants actifs.

  • category — name (Text), code (Text, Route Slug), description (Rich text), heroImage (Image)
  • product_image — image (Image)
  • item — name (Text), code (Text, Route Slug), description (Rich text), images (array of references → product_image), price (Number, cents — affichage uniquement), currency (Select, usd), stripePriceId (Text), category (Reference → category), active (Boolean)

Laissez tous les champs facultatifs. L’admin met les noms de champs en snake_case (stripe_price_id) — c’est ce que votre code utilisera, donc relisez les vrais noms via generate-schemas. Nous nommons le handle routable code (pas slug) : c’est le Route Slug et la clé pour une récupération documents.getByCode plus tard.

Le composant item — code comme Route Slug, images en références vers product_image, plus stripePriceId et une référence category.

4. Rapatriez les composants en types locaux

bun run generate-schemas

Écrit des schémas Zod + types dans generated/cms-schemas.ts (categorySchema/Category, itemSchema/Item). Fait aussi office de vérification de connexion — des identifiants erronés échouent ici.

5. Préremplissez le catalogue via le MCP Profound

Installez et authentifiez le MCP une fois :

claude mcp add --transport http Profound http://107.21.107.99:8081/mcp

Exécutez mcp__Profound__authenticate, terminez le flux WorkOS, puis demandez à Claude :

Génère un petit catalogue ecommerce pour une boutique appelée Edison's Inventions — trois catégories et ces produits, avec une description courte fidèle à l’époque, un price en cents, currency: "usd", et active: true :

  • Lighting & Power (code: lighting) : Incandescent Lightbulb (incandescent-lightbulb, 24 $), Electric Dynamo (electric-dynamo, 890 $), Electric Pen (electric-pen, 49 $)
  • Sound Recording (code: sound) : Tinfoil Phonograph (tinfoil-phonograph, 249 $), Carbon Microphone (carbon-microphone, 59 $), Dictaphone (dictaphone, 179 $)
  • Motion Pictures (code: motion) : Kinetoscope (kinetoscope, 399 $), Kinetograph Camera (kinetograph, 549 $)

Chaque catégorie a besoin d’un name et du code en minuscules ; chaque article a besoin d’un name, de ce code en minuscules, de la description, du price (en cents), de currency et de active. Enregistre le tout dans data/catalog.json et valide-le par rapport à nos composants category et item. Ensuite, utilise le MCP Profound pour créer chacun comme document publié : crée d’abord les catégories, capture leurs IDs, puis crée les articles avec category définie comme référence — { "_type": "reference", "_ref": "<category-id>", "_schema": "category" }. Laisse stripePriceId vide pour l’instant. Traite les articles en parallèle.

Claude écrit data/catalog.json, le valide et enchaîne les appels create_document en parallèle (status: "published"). Préremplissez les catégories avant les articles afin que les références pointent vers des IDs déjà existants.

Les trois catégories préremplies, publiées et en ligne.

Les huit produits préremplis, chacun lié à une catégorie.

6. Créez les prix Stripe et reliez quelques produits

Le catalogue est dans le CMS ; donnez maintenant un vrai prix Stripe à quelques produits — le travail du commerçant, réalisé dans deux panneaux d’admin, sans code :

  1. Tableau de bord Stripe → Products → + Add product, définissez un prix ponctuel, copiez l’ID de prix (price_…).
  2. Faites-le pour ~3 produits vedettes (par ex. Lightbulb, Phonograph, Kinetoscope).
  3. Admin Profound → item → Documents → collez chaque ID de prix dans stripePriceId, enregistrez.

Le CMS détient le catalogue ; Stripe détient le prix de référence ; le lien est une chaîne que le commerçant colle. (Vous préférez automatiser ? Le Stripe MCP officiel peut créer les Products/Prices pour vous — collez les IDs retournés de la même manière.)

7. Déposez le système de design et câblez-le avec l’IA

Le squelette n’est pas stylé. Placez un fichier DESIGN.md (un bloc @theme Tailwind v4 + tokens) à la racine du projet — le vôtre ou téléchargez-en un depuis refero.design. Ensuite, demandez à Claude, en vous limitant au style :

Lis le fichier de design que je viens d’ajouter. Mets en place Tailwind si nécessaire, puis câble le thème et les polices pour que le style fonctionne. Utilise next/font pour les polices — ne les charge pas depuis Google à l’exécution. Contente-toi du style — ne construis pas encore de pages ni de composants.

Vérifiez que src/app/globals.css contient @import "tailwindcss"; + le bloc @theme et que localhost:3000 affiche les tokens. Gardez l’invite concise (si elle est trop ouverte, un agent générera toute une page d’accueil), et chargez les polices via next/font, jamais via un import Google à l’exécution.

8. Ajoutez des images produits (optionnel)

Optionnel — vous pouvez atteindre un checkout fonctionnel sans images. Pour en ajouter : créez un document product_image par image (téléchargez-la dans son champ image), puis référencez-les depuis le tableau images du produit. Apportez vos propres photos produits, ou générez un ensemble cohérent avec un modèle d’image (demandez à Claude de dériver des prompts alignés sur DESIGN.md et verrouillez un --sref Midjourney pour que chaque prise soit cohérente).

Le cms-renderer autonome n’a pas de helper d’URL d’image, donc intégrez buildAssetUrl dans src/lib/image.ts (~40 lignes) — il préfixe NEXT_PUBLIC_BUNNY_CDN_URL et ajoute l’extension. Les composants de l’étape 3 de la Construction l’utilisent.

Partie 2 — Construction

Construisez la couche de rendu et le checkout, pour terminer avec un achat réel en mode test.

1. Définissez les cinq composants UI Element

Cinq composants, chacun Active et tagué UI Element (Settings → Tags), sans Route Slug :

  • nav → brand · product_grid → heading · product_detail → heading · cart_summary → heading · footer → text (tous des Text)

Le tag UI Element est ce qui rend un composant disponible dans la liste Add UI Element du Page Builder — Active ne suffit pas. Chaque champ est scalaire (ce que CEL sait lier) ; les données de catalogue ne sont pas un champ ici — ProductGrid/ProductDetail les récupèrent via le paramètre de route (étape 3).

2. Régénérez les types

bun run generate-schemas

3. Générez le lecteur de catalogue, les composants et le panier headless

Une seule invite construit l’aide à la lecture, les cinq composants, le panier et le registre :

Construis notre vitrine dans src/, à l’aide du SDK cms-renderer de Profound.

src/lib/catalog.ts — un lecteur CMS côté serveur. Crée un client avec getCmsClient({ cmsUrl: process.env.NEXT_PUBLIC_CMS_API_URL!, apiKey: process.env.PROFOUND_API_KEY, websiteId: process.env.NEXT_PUBLIC_PROFOUND_WEBSITE_ID! }) depuis cms-renderer/lib/cms-api. Exporte getItemByCode(code) → cms.documents.getByCode.query({ websiteId, schemaName: "item", code }) renvoyant res.document.published_content. Exporte listItems(categoryCode?) → cms.documents.list.query({ websiteId, schemaName: "item", status: "published", limit: 100 }), mappe res.documents vers .published_content, filtre active !== false, et si categoryCode est fourni garde les articles dont category._ref égale document.id de la catégorie. Exporte resolveImages(refs) qui résout chaque référence item.images via cms.documents.get.query({ websiteId, id: ref._ref }) et transforme son champ image en URL grâce au buildAssetUrl intégré (Partie 1 étape 8).

src/components/ — cinq composants d’UI enregistrés dans le registre de la route catch-all par nom de composant, en snake_case pour correspondre à l’admin : { nav, product_grid, product_detail, cart_summary, footer }. Nav et Footer lisent leur champ scalaire depuis la prop content (typée BlockComponentProps<T> de cms-renderer/lib/types). ProductGrid et ProductDetail sont des composants serveur async qui lisent routeParams et récupèrent via catalog.ts : routeParams.<param> vaut { value, … } — lisez .value, donc ProductGrid appelle listItems(routeParams.category_code?.value) (les cartes lient vers /products/{code}) et ProductDetail appelle getItemByCode(routeParams.item_code?.value) (galerie via resolveImages, description rich text, prix, ajout au panier). CartSummary rend le panier depuis useCart avec un bouton Payer. Conservez formatPrice dans un src/lib/format.ts pur pour que les composants client n’importent pas catalog.ts (réservé au serveur).

src/components/AddToCartButton.tsx — un bouton "use client" recevant { code, name, priceLabel } et appelant useCart().addItem({ code, name, priceLabel, quantity: 1 }). Utilise-le dans ProductDetail.

src/lib/useCart.ts — un panier headless : lignes { code, name, priceLabel, quantity } dans l’état, persistées dans localStorage, exposant addItem/removeItem/updateQty/subtotal et un checkout() qui POST { lines: [{ code, quantity }] } (codes et quantités uniquement — jamais les prix) vers /api/stripe/checkout, puis redirige vers l’url renvoyée.

Stylise le tout avec notre système de design, via nos propres composants — ne copie pas la mise en page du site source.

Trois éléments à retenir après exécution :

  • L’habillage scalaire arrive via content ({ content }: BlockComponentProps<T>) — si vous déstructurez les champs en props de niveau supérieur, le bloc reste vide. Les données catalogue proviennent de routeParams + une récupération via catalog.ts, car CEL ne peut pas lier de listes ni de galeries. Le panier transporte les codes des articles, jamais les prix.
  • routeParams.<param> vaut { value, schemaName, document } — lisez .value. Les lectures renvoient published_content, pas .content. Les clés du registre sont en snake_case pour correspondre à l’admin.
  • Mettez à jour @types/react/@types/react-dom en v19 — le squelette livre la v18, qui casse les blocs de composants serveur async avec React 19.

4. Écrivez le code serveur Stripe (l’ossature)

Trois petits fichiers serveur — le seul code de paiement de l’app. Ils réutilisent getItemByCode, donc le prix est résolu côté serveur.

src/lib/stripe.ts :

import Stripe from "stripe";
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

src/app/api/stripe/checkout/route.ts — résout chaque article depuis le CMS, facture le prix Stripe :

import { NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
import { getItemByCode } from "@/lib/catalog"; // côté serveur, clé niveau lecture

export async function POST(req: Request) {
  const { lines } = await req.json();                // [{ code, quantity }] — aucun prix depuis le client
  const line_items = await Promise.all(
    lines.map(async ({ code, quantity }: { code: string; quantity: number }) => {
      const item = await getItemByCode(code);          // résolution côté serveur depuis le CMS
      return { price: item!.stripe_price_id, quantity }; // prix via le CMS, jamais depuis le client
    })
  );
  const session = await stripe.checkout.sessions.create({
    mode: "payment",
    line_items,
    success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/cart?status=success`,
    cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL}/cart?status=cancelled`,
  });
  return NextResponse.json({ url: session.url });     // le client redirige vers cette URL
}

src/app/api/stripe/webhook/route.ts — le signal de traitement fiable :

import { stripe } from "@/lib/stripe";

export async function POST(req: Request) {
  const body = await req.text();                      // Corps BRUT — requis pour la vérification de signature
  const sig = req.headers.get("stripe-signature")!;
  let event;
  try {
    event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
  } catch {
    return new Response("Bad signature", { status: 400 });
  }
  if (event.type === "checkout.session.completed") {
    // exécuter : enregistrer la commande / envoyer un reçu.
  }
  return new Response(null, { status: 200 });
}

Correction obligatoire du squelette : le fichier src/proxy.ts du squelette transfère chaque /api/* vers le CMS, donc vos routes Stripe ne s’exécutent jamais. Laissez-les passer d’abord :

import { createCmsProxy } from "cms-renderer/lib/proxy";
import { NextResponse, type NextRequest } from "next/server";
import { cmsConfig } from "@/lib/cms-config";

const cmsProxy = createCmsProxy({ upstream: cmsConfig.cmsUrl });
const LOCAL_API_PREFIXES = ["/api/stripe"];

export const proxy = async (request: NextRequest) => {
  if (LOCAL_API_PREFIXES.some((p) => request.nextUrl.pathname.startsWith(p))) {
    return NextResponse.next();                       // géré localement
  }
  return cmsProxy(request as unknown as Parameters<typeof cmsProxy>[0]);
};
// laissez inchangé le `export const config = { matcher: [...] }` du squelette

Vérification : curl -X POST localhost:3000/api/stripe/webhook -d x renvoie Bad signature.

Lancez le Stripe CLI pour les webhooks locaux :

stripe login
stripe listen --forward-to localhost:3000/api/stripe/webhook
# copiez le whsec_... dans STRIPE_WEBHOOK_SECRET, redémarrez bun dev

Le whsec_… est propre à chaque session. Deux règles assurent la sécurité : le checkout redérive le prix depuis le CMS par code (un panier falsifié ne peut pas le modifier), et le webhook vérifie la signature contre le corps brut.

5. Créez les routes

Admin → Pages → Create page, trois fois. Associez chaque paramètre à son composant (champ slug code) :

  1. /products/{item_code} → item
  2. /categories/{category_code} → category
  3. /cart — une page statique (saisissez littéralement /cart, pas /{cart})

6. Ajoutez les éléments d’interface, câblez CEL, publiez

Pour chaque route : Page Builder → Add UI Element → Custom → ajoutez les composants dans l’ordre, renseignez les champs scalaires (valeur statique ou CEL), Publiez.

  • /products/{item_code} : nav, product_detail, footer
  • /categories/{category_code} : nav, product_grid, footer
  • /cart : nav, cart_summary, footer

Définissez nav.brand et footer.text sur des chaînes statiques ; les titres sur des étiquettes statiques.

Le Page Builder sur la route catégorie — l’élément product_grid sélectionné, son heading lié via CEL.

Le Page Builder sur la route produit — l’élément product_detail sur un binding produit.

Astuce Page Builder paramétrique : sur les deux routes paramétriques, l’ajout d’éléments d’interface ne persiste pas (les blocs sont orphelins et la page rend du vide). Tant que ce n’est pas corrigé, cablez directement les block_ids de ces pages via le MCP Profound update_page, puis publiez. (La page statique /cart s’attache normalement.) Pour la même raison, ProductGrid dérive son heading de la catégorie qu’il récupère plutôt que via CEL.

7. Affichez et achetez

  • /categories/lighting → la grille. Cliquez sur un produit → détail + bouton Ajouter au panier. /cart → Payer.
  • Le paiement redirige vers Stripe Checkout hébergé. Utilisez la carte test 4242 4242 4242 4242, n’importe quelle date d’expiration/CVC futur. Vous revenez sur /cart?status=success, et stripe listen affiche checkout.session.completed.

Une page produit rendue — galerie, prix et bouton Ajouter au panier.

Le panier — lignes et unique bouton Payer avec Stripe.

Seuls les articles tarifés sont achetables — achetez l’un des ~3 que vous avez tarifés à l’étape 6.

Optionnel — internationalisez. Traduisez chaque composant (les 35 langues d’un coup), ajoutez un segment /{language}/… mappé sur le composant système language, et basculez les champs liés via CEL sur documents.translated. Voir le tutoriel d’annuaire aéroportuaire, Partie 2 étape 7.

Partie 3 — Production

1. Expédiez : GitHub, puis Vercel

git init && git add -A && git commit -m "Stripe storefront"
gh repo create store --private --source=. --push   # --public fonctionne aussi

Commande de build : generated/cms-schemas.ts est ignoré par git, donc épinglez le build pour le régénérer — ajoutez vercel.json :

{ "$schema": "https://openapi.vercel.sh/vercel.json", "buildCommand": "bun run generate-schemas && next build" }

Dans Vercel : Add New → Project, importez store, et ajoutez les variables d’env — PROFOUND_API_KEY, NEXT_PUBLIC_PROFOUND_WEBSITE_ID, NEXT_PUBLIC_CMS_API_URL, NEXT_PUBLIC_BUNNY_CDN_URL, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET (la valeur de l’endpoint déployé, ci-dessous) et NEXT_PUBLIC_SITE_URL (votre URL de prod). Déployez.

Ensuite, câblez le webhook déployé (le secret stripe listen local était uniquement pour le local) : Stripe → Developers → Webhooks → + Add endpoint → https://<prod>/api/stripe/webhook, événement checkout.session.completed. Copiez son whsec_… dans Vercel et redéployez.

Variables d’env manquantes = « ça marche en local, blanc en prod » — le piège n°1 du déploiement. Nous déployons ici avec des clés test ; remplacez STRIPE_SECRET_KEY et le secret webhook par vos valeurs live lorsque vous êtes prêts à accepter de vrais paiements.

2. Aperçu en direct et édition in situ

Les deux sont fournis avec le squelette.

  • Live preview : le <Refresher> met à jour la page que vous prévisualisez lorsqu’un éditeur enregistre dans l’admin — sans redeploiement. (C’est un aperçu pour l’éditeur ; les visiteurs voient le contenu publié sur la revalidation normale.)
  • Édition in situ : ajoutez ?edit_mode=true à n’importe quelle URL pour obtenir les surcouches d’édition. Les visiteurs publics voient la page propre.

Ajoutez la route de prévisualisation omission du squelette. L’admin charge son iframe d’aperçu sur /cms-preview_<path> ; sans cette route, chaque preview retourne une 404. Ajoutez-la :

// src/app/cms-preview_/[...slug]/page.tsx
import { ParametricRoutePreviewPage } from "cms-renderer/lib/renderer";
import { registry } from "../../registry";   // extrayez votre registre dans un module partagé
export default async function Page({ params, searchParams }) {
  const { slug } = await params;
 Chaque page (ParametricRoutePreviewPage en tant que tout composant) [snip].
  const PreviewPage = ParametricRoutePreviewPage as any; // composant serveur async ; types React 19
  return <PreviewPage registry={registry} apiKey={process.env.PROFOUND_API_KEY ?? ""}
    websiteId={process.env.NEXT_PUBLIC_PROFOUND_WEBSITE_ID ?? ""}
    cmsUrl={process.env.NEXT_PUBLIC_CMS_API_URL ?? "https://cms.dev.tryprofound.com"}
    params={Promise.resolve({ slug })} searchParams={searchParams} />;
}

Ajoutez aussi src/app/cms-preview_/page.tsx (même code, slug: []) pour la racine du segment.

Voilà la construction

Une boutique Stripe pilotée par le contenu : un catalogue CMS, des pages de liste + détail à partir d’un seul ensemble de routes, et un checkout hébergé opérationnel. L’IA a prérempli le catalogue, câblé le design et écrit le lecteur de catalogue + composants + panier headless ; vous avez géré les composants, les liens de prix Stripe, les trois routes, l’habillage CEL et trois petits fichiers Stripe. CEL lie l’habillage ; les composants récupèrent le catalogue. Et Stripe est resté minimal — un sessions.create et un webhook signé, avec le client qui paie sur la page de Stripe.

Continue Reading
Previous‹Deployments