Praktiline juhend: ehita Profound CMS-is sisupõhine Stripe'i vitriin — kataloog, mida kaupmees muudab ilma koodita, kaks parametrilist marsruuti, peata ostukorv ja Stripe'i hallatav kassaprotsess.
Valmis pood tegevuses — sirvi kategooriat, ava toode, lisa korvi, vormista ost.
Praktiline juhend, mis ehitab Profound CMS-is sisupõhise poe: CMS-is modelleeritud tootekataloog (kategooriad + tooted), nimekirja- ja detaillehed ühest marsruudikomplektist ning peata komponendina tarnitav Stripe'i hallatav kassaprotsess.
Selgroog on käsitsi kirjutatud Next.js koos Profoundi adminiga. Claude Code (Profound MCP kaudu) teeb ära põhiosa kolmest ülesandest — kataloogi külvamine, disainisüsteemi ühendamine ja vitriinikomponentide (sh peata ostukorvi) kirjutamine. Kolm osa: Seadistus, Ehitus, Tööversioon.
Maksed ühe reaga. Kasutame Stripe'i hallatavat kassaprotsessi: ostja maksab Stripe'i lehel, mitte sinu omal. Sinu rakendus teeb serveris vaid kaks asja — loob Checkout Sessioni ja valideerib ühe veebikonksu. Ei ühtegi kaardivälja, ei Stripe Elementsit, pole PCI koormust.
/products/{item_code}, /categories/{category_code}) pluss staatiline /cart, kõik ühest komponentide komplektist.useCart) ja Stripe'i hallatav kassaprotsess, kus hind lahendatakse alati serveris Stripe'i Price ID põhjal.curl -fsSL https://bun.sh/install | bashstripe login) lokaalsete veebikonksude jaoks.gh) ja Verceli konto, mis on GitHubiga ühendatud.Profound eraldab sisu ja renderdamise:
category, item); üks märgendiga UI Element on paigutatav lehele (nav, product_grid, …).meta.params.* CEL-is, routeParams Reactis).cms-rendereri kaudu; Stripe lisatakse tavaliste API marsruutidena.Üks reegel, mis kujundab kogu ehituse: CEL seob ainult string/number välju. Seega skalaarkromi (navibränd, jaluse tekst, pealkirjad) seotakse CEL-iga, samas kui kõik rikkalik või kogumik (tooteruudustik, pildigalerii, rikkalik tekst) tuuakse Reacti komponendi sees marsruudiparameetri alusel. Ja Stripe on hinnainfo tõeallikas — CMS-i price on vaid kuvamiseks; tasu lahendatakse alati serveris Stripe'i Price ID-st.
Lõpptulemus: väike avaldatud kataloog, rakendus loeb seda, Stripe on paigaldatud, disain on paigas — renderdust veel pole.
Registreeru Profoundis (WorkOS autent). Loo store-nimeline veebisait, seejärel kopeeri selle veebisaidi ID (UUID admini URL-is) ja lugemistaseme API-võti (Deployments → Create API key). Rakendus ainult loeb; kataloogi külvamine käib hiljem MCP kaudu, mis autentib eraldi.
bunx create-profound-next store
cd store
bun add stripe
Skafold on Next.js App Routeri projekt, mis on Profoundi jaoks eelhäälestatud (cms-rendereri SDK, kõike püüdva marsruudi, generate-schemas skripti, <Refresher>i) abil. Kujundust see kaasa ei too. bun add stripe toob serveri SDK — ainus maksetesõltuvus, mida hallatav kassaprotsess vajab.
Lisa oma väärtused faili .env.local:
# CMS
PROFOUND_API_KEY=<sinu lugemisvõti>
NEXT_PUBLIC_PROFOUND_WEBSITE_ID=<sinu veebisaidi ID>
NEXT_PUBLIC_CMS_API_URL=https://cms.dev.tryprofound.com
NEXT_PUBLIC_BUNNY_CDN_URL=https://cms-profound.b-cdn.net # pakub CMS-is hostitud pilte
# Stripe
STRIPE_SECRET_KEY=sk_test_... # siia testvõti; päris võtme vahetad sisse, kui lähed live'i
STRIPE_WEBHOOK_SECRET=whsec_... # täidetakse Ehitus, samm 4
NEXT_PUBLIC_SITE_URL=http://localhost:3000
Haara STRIPE_SECRET_KEY saidilt Stripe → Developers → API keys. Kasutame testvõtit (sk_test_…), nii et ehituse ajal pärisraha ei liigu; kui oled valmis pärismakseid vastu võtma, lülita elusvõtmele. Käivita bun dev ja ava localhost:3000 — stardirakendus renderdub.
Hallatav kassaprotsess suunab brauseri Stripe'i URL-ile, seega serveri salavõtmest piisab — pole avalikku võtit ega kliendipoolset Stripe'i SDK-d.
category, product_image ja itemLoo kolm kohandatud komponenti (Components → Create new component) — need on andmeallikas, seega ilma UI Elementi märgendita. Määra iga komponent Active olekusse.
CMS-il pole "pildimassiivi" välja, seega on galerii väike viidete massiiv product_image komponendile. Loo category ja product_image (ja märgi need Active) enne item komponenti — viitevälja sihtmärgiks saab valida ainult aktiivseid komponente.
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 (viidete massiiv → product_image), price (Number, sendid — ainult kuvamine), currency (Select, usd), stripePriceId (Text), category (Reference → category), active (Boolean)Jäta kõik väljad valikuliseks. Admin muudab väljanimed alamkriipsudega variandiks ("Stripe Price Id" → stripe_price_id) — sinu kood kasutab just neid nimesid, seega loe tõelised nimed hiljem generate-schemas abil tagasi. Marsruuditava handle'i nimeks paneme code (mitte slug): see on Route Slug ja puhta documents.getByCode päringu võti.
Komponent item — code Route Slug'ina, images viidetena product_image-le, lisaks stripePriceId ja category viide.
bun run generate-schemas
Kirjutab Zodi skeemid + tüübid faili generated/cms-schemas.ts (categorySchema/Category, itemSchema/Item). Toimib ka ühendustestina — valed mandaadid annavad siin veateate.
Paigalda ja autendi MCP ühekordselt:
claude mcp add --transport http Profound http://107.21.107.99:8081/mcp
Käivita mcp__Profound__authenticate, lõpeta WorkOS-i voog ja palu siis Claudelt:
Genereeri väike e-poe kataloog nimega Edison's Inventions — kolm kategooriat ja järgmised tooted, igale lühike ajastutruu
description,pricesentides,currency: "usd"jaactive: 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 $)Igal kategoorial olgu
nameja see väikeste tähtedegacode; igal tootelname, seesama väiketähtedegacode,description,price(sentides),currencyjaactive. Salvesta see failidata/catalog.jsonja valideeri meiecategoryjaitemkomponentide vastu. Seejärel kasuta Profound MCP-d, et luua igaüks avaldatud dokumendina: loo kategooriad esimesena, püüa kinni nende ID-d, seejärel loo tooted, seadescategoryviiteks —{ "_type": "reference", "_ref": "<category-id>", "_schema": "category" }. Tee toodete loomine paralleelselt.
Claude kirjutab faili data/catalog.json, valideerib selle ning käivitab paralleelsed create_document-kutsed (status: "published"). Külva kategooriad enne tooteid, et viited osutaksid juba olemasolevatele ID-dele.
Kolm külvatud kategooriat, avaldatud ja Live.
Kaheksa külvatud toodet, igaüks seotud kategooriaga.
Kataloog on CMS-is; nüüd anna mõnele tootele päris Stripe'i hind — see on kaupmehe töö, mis toimub kahes halduspaneelis ilma koodita:
price_…).stripePriceId väljale vastav Price ID ja salvesta.CMS hoiab kataloogi; Stripe hoiab hinnatõde; link on üks string, mille kaupmees kleebib. (Soovid automatiseerida? Ametlik Stripe MCP saab Products/Prices sinu eest luua — kleebi tagastatud ID-d samal moel.)
Skafold on stiilita. Aseta projekti juurkausta DESIGN.md (Tailwind v4 @theme plokk + tokenid) — kas enda oma või laadi alla refero.design lehelt. Seejärel palu Claudel, keskendudes ainult stiilile:
Loe disainifail, mille just lisasin. Sea vajadusel üles Tailwind, seejärel ühenda teema ja fondid, et stiil toimiks. Kasuta fonte laadides
next/fonti — ära lae neid Google'ist jooksu pealt. Ainult stiil — ära ehita veel lehti ega komponente.
Veendu, et src/app/globals.css sisaldab @import "tailwindcss"; + @theme plokki ning et localhost:3000 kuvab tokenid. Hoia prompt lühike (liiga avatud ülesanne paneb agendi ehitama terve avalehe) ja laadi fondid next/fonti kaudu, mitte runtime'is Google'ist.
Valikuline — toimiva kassani jõuad ka ilma piltideta. Kui soovid neid lisada: loo iga pildi jaoks product_image dokument (lae pilt selle image väljale), seejärel viita neile toote images massiivis. Too omad tootefotod või genereeri ühtne komplekt mõne pildimudeliga (lase Claudel tuletada DESIGN.md põhjal brändikohased promptid ja lukusta üks Midjourney --sref, et kõik võtted sobiksid kokku).
Iseseisval
cms-rendereril pole pildi-URL-i abifunktsiooni, seega vendoribuildAssetUrlfailisrc/lib/image.ts(~40 rida) — see lisab etteNEXT_PUBLIC_BUNNY_CDN_URLja laiendi. Komponendid Ehitus, samm 3 kasutavad seda.
Ehita renderduskiht ja kassaprotsess, mis lõpeb päris testmaksega.
Viis komponenti, igaüks Active ja märgendiga UI Element (Settings → Tags), ilma Route Slugita:
nav → brand · product_grid → heading · product_detail → heading · cart_summary → heading · footer → text (kõik Text)UI Element märgend paneb komponendi ilmuma Page Builderi Add UI Element loendis — Active olekust üksi ei piisa. Iga väli on skalaar (need, mida CEL seob); tegelik kataloog info pole siin väli — ProductGrid/ProductDetail toovad selle marsruudiparameetri põhjal (samm 3).
bun run generate-schemas
Üks prompt loob lugemispõhise abisti, viis komponenti, ostukorvi ja registri:
Ehita meie vitriin kataloogi
src/kausta, kasutades Profoundicms-rendererSDK-d.
src/lib/catalog.ts— serveripoolne CMS-i lugeja. Loo klientgetCmsClient({ cmsUrl: process.env.NEXT_PUBLIC_CMS_API_URL!, apiKey: process.env.PROFOUND_API_KEY, websiteId: process.env.NEXT_PUBLIC_PROFOUND_WEBSITE_ID! })abil teegistcms-renderer/lib/cms-api. EkspordigetItemByCode(code)→cms.documents.getByCode.query({ websiteId, schemaName: "item", code }), mis tagastabres.document.published_content. EkspordilistItems(categoryCode?)→cms.documents.list.query({ websiteId, schemaName: "item", status: "published", limit: 100 }), kaardistares.documentsväärtuseks.published_content, filtreeri väljaactive !== false, ja kuicategoryCodeon antud, jäta alles üksused, millecategory._refvõrdub kategooriadocument.id-ga. EkspordiresolveImages(refs), mis lahendab igaitem.imagesviite kauducms.documents.get.query({ websiteId, id: ref._ref })ja muudab selle pildivälja URL-iks vendordatudbuildAssetUrlabil (Osa 1 samm 8).
src/components/— viis UI elementide komponenti, registreeritud kõike püüdvas marsruudis registrisse komponentide nimede järgi, snake_case kujul, et kattuks adminiga:{ nav, product_grid, product_detail, cart_summary, footer }.NavjaFooterloevad oma skalaarkentcontentpropilt (tüüpBlockComponentProps<T>teegistcms-renderer/lib/types).ProductGridjaProductDetailon asünkroonsed serverikomponendid, mis loevadrouteParamsja toovad andmed failistcatalog.ts:routeParams.<param>on kujul{ value, … }— kasuta.value, seegaProductGridkutsublistItems(routeParams.category_code?.value)(kaardid viitavad aadressile/products/{code}) jaProductDetailkutsubgetItemByCode(routeParams.item_code?.value)(galeriiresolveImageskaudu, rikkalik kirjeldus, hind, Lisa korvi nupp).CartSummaryrenderdab ostukorviuseCartist koos maksenupuga. HoiaformatPricepuhtas failissrc/lib/format.ts, et kliendikomponendid ei impordiks serveripoolsetcatalog.tsi.
src/components/AddToCartButton.tsx—"use client"nupp proportsiooniga{ code, name, priceLabel }, mis kutsubuseCart().addItem({ code, name, priceLabel, quantity: 1 }). Kasuta sedaProductDetailis.
src/lib/useCart.ts— peata ostukorv: read{ code, name, priceLabel, quantity }seisundis, püsivlocalStorageis, eksponeeribaddItem/removeItem/updateQty/subtotalningcheckout(), mis POSTitab{ lines: [{ code, quantity }] }(ainult koodid ja kogused — mitte kunagi hindu) aadressile/api/stripe/checkout, seejärel suunab edasi tagastatudurlile.Stiili kõike meie disainisüsteemiga, kasutades oma komponente — ära kopeeri allikveebi paigutust.
Pärast käivitamist tea kolme asja:
content propilt ({ content }: BlockComponentProps<T>) — kui destruktureerid väljad otse ülemise taseme propsidesse, jääb plokk tühjaks. Kataloogandmed tulevad routeParams + catalog.ts päringu kaudu, sest CEL ei seo loendeid ega galeriisid. Ostukorv kannab kaasa tootekoodid, mitte hindu.routeParams.<param> on { value, schemaName, document } — loe .value. Lugemised tagastavad published_content, mitte .content. Registri võtmeks on snake_case, et see langeks admini nimetusega kokku.@types/react/@types/react-dom versioonile 19 — skafold on 18. versioonis, mis rikub asünkroonseid serverikomponentide plokke React 19 vastu.Kolm lühikest serverifaili — ainsad maksekoodid rakenduses. Need kasutavad getItemByCode funktsiooni, nii et tasu lahendatakse serveris.
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 — lahenda iga toode CMS-ist, võta Stripe'i hind:
import { NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
import { getItemByCode } from "@/lib/catalog"; // serveripoolne, lugemisvõtmega
export async function POST(req: Request) {
const { lines } = await req.json(); // [{ code, quantity }] — kliendilt ei tule hindu
const line_items = await Promise.all(
lines.map(async ({ code, quantity }: { code: string; quantity: number }) => {
const item = await getItemByCode(code); // server toob CMS-ist
return { price: item!.stripe_price_id, quantity }; // hind CMS-ist, mitte kliendilt
})
);
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 }); // klient suunatakse siia
}
src/app/api/stripe/webhook/route.ts — usaldusväärne täitmissignaal:
import { stripe } from "@/lib/stripe";
export async function POST(req: Request) {
const body = await req.text(); // TOOREE keha — vajalik allkirja kontrolliks
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") {
// täida tellimus: salvesta order / saada kviitung.
}
return new Response(null, { status: 200 });
}
Vajalik skafoldi parandus: skafoldi
src/proxy.tssuunab kõik/api/*päringud CMS-i, mistõttu Stripe'i marsruudid ei tööta. Lase neil kõigepealt läbi: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(); // käitle lokaalselt } return cmsProxy(request as unknown as Parameters<typeof cmsProxy>[0]); }; // jäta skafoldi `export const config = { matcher: [...] }` muutmataKontroll:
curl -X POST localhost:3000/api/stripe/webhook -d xpeab tagastamaBad signature.
Käivita Stripe CLI lokaalsete veebikonksude jaoks:
stripe login
stripe listen --forward-to localhost:3000/api/stripe/webhook
# kopeeri whsec_... STRIPE_WEBHOOK_SECRET väärtuseks, taaskäivita bun dev
whsec_… kehtib iga seansi kohta eraldi. Turvalisuse tagavad kaks reeglit: kassaprotsess tuletab hinna uuesti CMS-ist koodi järgi (võltsitud ostukorv ei saa seda muuta) ning veebikonks valideerib allkirja toore keha vastu.
Admin → Pages → Create page, kolm korda. Seo iga parameeter vastava komponendiga (slugi väli code):
/products/{item_code} → item/categories/{category_code} → category/cart — staatiline leht (sisesta sõna-sõnalt /cart, mitte /{cart})Iga marsruudi jaoks: Page Builder → Add UI Element → Custom → lisa komponendid õiges järjekorras, täida skalaarkennad (staatiline väärtus või CEL), Publish.
/products/{item_code}: nav, product_detail, footer/categories/{category_code}: nav, product_grid, footer/cart: nav, cart_summary, footerMäära nav.brand ja footer.text staatiliste stringidena; pealkirjade väljadesse kirjuta staatilised sildid.
Page Builder kategooria marsruudil — valitud product_grid element, selle pealkiri seotuna CEL-iga.
Page Builder toote marsruudil — product_detail element seotud tootega.
Parametrilise Page Builderi iseärasus: kahel parametrilisel marsruudil UI elementide lisamine ei püsi (plokid orvustuvad ja leht jääb tühjaks). Kuni see on parandatud, sea nende lehtede
block_idsotse Profound MCPupdate_pagekäsuga, seejärel avalda. (Staatiline/cartseob elemendid tavapäraselt.) Samal põhjusel tuletabProductGridpealkirja hoopis kategooriast, mille ta toob, mitte CEL-iga.
/categories/lighting → ruudustik. Klõpsa tootel → detailvaade + Lisa korvi. /cart → maksa./cart?status=success, ja stripe listen kuvab sündmuse checkout.session.completed.Renderdatud tooteleht — galerii, hind ja Lisa korvi nupp.
Ostukorv — read ja üks Pay-with-Stripe nupp.
Vaid hinnaga tooted on ostetavad — osta üks neist ~3, mille hinnad samm 6-s lisasid.
Valikuline — rahvusvahelistamine. Tõlgi iga komponent (kõik 35 keelt korraga), lisa
/{language}/…segment, mis on seotud sisseehitatudlanguagesüsteemikomponendiga, ja vaheta CEL-i abil seotud väljaddocuments.translatedpeale. Vaata lennujaama kataloogi juhendi osa 2 sammu 7.
git init && git add -A && git commit -m "Stripe storefront"
gh repo create store --private --source=. --push # --public sobib samuti
Ehituskäsk:
generated/cms-schemas.tson.gitignoreall, seega pane ehitus käsule tuginema selle regenereerimine — lisavercel.json:{ "$schema": "https://openapi.vercel.sh/vercel.json", "buildCommand": "bun run generate-schemas && next build" }
Vercelis: Add New → Project, impordi store ja lisa keskkonnamuutujad — PROFOUND_API_KEY, NEXT_PUBLIC_PROFOUND_WEBSITE_ID, NEXT_PUBLIC_CMS_API_URL, NEXT_PUBLIC_BUNNY_CDN_URL, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET (deployitud lõpp-punkti väärtus, vt allpool) ja NEXT_PUBLIC_SITE_URL (sinu tootmiskeskkonna URL). Deploy.
Seejärel seo deployitud veebikonks (lokal stripe listeni salavõti kehtis vaid lokaalselt): Stripe → Developers → Webhooks → + Add endpoint → https://<prod>/api/stripe/webhook, sündmus checkout.session.completed. Kopeeri selle whsec_… Vercelisse ja redeploy.
Puuduvad keskkonnamuutujad = "töötab lokaalselt, on produktsioonis tühi" — levinum deploy'ga seotud viga. Siin deployme testvõtmetega; kui oled valmis pärismakseid vastu võtma, vaheta STRIPE_SECRET_KEY ja veebikonksu salavõti live väärtuste vastu.
Mõlemad tulevad skafoldiga kaasa.
<Refresher> uuendab eelvaate lehte, kui toimetaja adminis salvestab — redeployd pole vaja. (See on eelvaade toimetajale; külastajad näevad avaldatud sisu tavapärase revalideerimisega.)?edit_mode=true, et näha muutmis-ülekateid. Tavakülastajad näevad puhast lehte.Lisa eelvaate marsruut, mida skafold ei sisalda. Admin laadib oma eelvaate iframe'i aadressilt
/cms-preview_<path>; ilma selle marsruudita lähevad kõik eelvaated 404-ks. Lisa see:// src/app/cms-preview_/[...slug]/page.tsx import { ParametricRoutePreviewPage } from "cms-renderer/lib/renderer"; import { registry } from "../../registry"; // tõsta oma register jagatud moodulisse export default async function Page({ params, searchParams }) { const { slug } = await params; const PreviewPage = ParametricRoutePreviewPage as any; // asünkroonne RSC; React 19 tüübid 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} />; }Lisa ka
src/app/cms-preview_/page.tsx(sama,slug: []) segmendi juure jaoks.
Sisupõhine Stripe'i vitriin: CMS-i kataloog, nimekirja- ja detaillehed ühest marsruudikomplektist ning toimiv hallatav kassaprotsess. AI külvas kataloogi, ühendas disaini ja kirjutas kataloogilugeja + komponendid + peata ostukorvi; sina tegid komponendid, Stripe'i hinnalinkid, kolm marsruuti, CEL-i kromi ning kolm lühikest Stripe'i faili. CEL seob kromi; komponendid toovad kataloogi. Ja Stripe jäi väikeseks — üks sessions.create kõne ja üks allkirjastatud veebikonks, kus ostja maksab Stripe'i enda lehel.