Gyakorlati útmutató: építs tartalomvezérelt Stripe-áruházat a Profound CMS-en — egy kód nélkül szerkeszthető katalógust, két paraméterezett útvonalat, headless kosarat és Stripe által hosztolt pénztárat.
A kész üzlet működés közben — kategória böngészése, termék megnyitása, kosárba helyezés és fizetés.
Gyakorlati útmutató egy tartalomvezérelt üzlet felépítéséhez a Profound CMS-en: a CMS-ben modellezett termékkatalógus (kategóriák és termékek), egyetlen útvonalrendszerből származó lista- és részletezőoldalak, valamint headless komponensként megvalósított Stripe által hosztolt pénztár.
Az alapot kézzel írt Next.js és a Profound adminisztrációs felülete adja. A Claude Code (a Profound MCP-n keresztül) elvégzi a három fő feladatot — a katalógus feltöltését, a designrendszer bekötését és az áruház komponenseinek megírását, beleértve a headless kosarat is. Három rész: Beállítás, Építés, Élesítés.
Fizetések egy mondatban. Stripe által hosztolt Checkoutot használunk: a vásárló a Stripe oldalán fizet, nem a tiéden. Az alkalmazás mindössze két szerveroldali műveletet végez — létrehoz egy Checkout Sessiont és ellenőriz egy webhookot. Nincsenek kártyamezők, Stripe Elements vagy PCI-terhelés.
/products/{item_code}, /categories/{category_code}), valamint egy statikus /cart oldalt, mindet egyetlen komponenskészletből.useCart) és Stripe által hosztolt pénztárat, ahol az ár mindig szerveroldalon, Stripe Price ID alapján kerül feloldásra.curl -fsSL https://bun.sh/install | bashstripe login).gh) és egy GitHubhoz kapcsolt Vercel-fiók.A Profound különválasztja a tartalmat és a megjelenítést:
category, item); az UI Element címkével ellátott komponens elhelyezhető egy oldalon (nav, product_grid, …).meta.params.* CEL-ben, routeParams Reactben).cms-renderer segítségével olvassa ezeket; a Stripe szokásos API-útvonalakként kerül hozzáadásra.Az építést meghatározó szabály: a CEL csak string/number mezőkhöz köt adatot. Ezért a skaláris felületi elemeket (navigációs márka, lábléc, címsorok) CEL-lel kötjük be, míg minden gazdag tartalmat vagy gyűjteményt (termékrács, képgaléria, rich text) a React-komponens kér le az útvonalparaméter alapján. A Stripe az árak egyetlen hiteles forrása — a CMS price mezője csak megjelenítésre szolgál; a terhelés mindig szerveroldalon, Stripe Price ID alapján történik.
A végállapot: egy kis közzétett katalógus, az olvasásra bekötött alkalmazás, telepített Stripe és elkészített dizájn — egyelőre még semmi sem jelenik meg.
Regisztrálj a Profoundnál (WorkOS-hitelesítés). Hozz létre store nevű webhelyet, majd másold ki a website ID-t (az adminisztrációs URL-ben található UUID-t) és egy read-tier API-kulcsot (Deployments → Create API key). Az alkalmazás csak olvas; a katalógus feltöltése később az MCP-n keresztül, külön hitelesítéssel történik.
bunx create-profound-next store
cd store
bun add stripe
A scaffold egy Profoundhoz előre bekötött Next.js App Router projektet hoz létre (a cms-renderer SDK-val, gyűjtőútvonallal, generate-schemas parancsfájllal és <Refresher> komponenssel). Stílusokat nem tartalmaz. A bun add stripe a szerveroldali SDK-t telepíti — ez az egyetlen fizetési függőség, amelyre a hosztolt pénztárnak szüksége van.
Add hozzá az értékeket a .env.local fájlhoz:
# CMS
PROFOUND_API_KEY=<your read key>
NEXT_PUBLIC_PROFOUND_WEBSITE_ID=<your website id>
NEXT_PUBLIC_CMS_API_URL=https://cms.dev.tryprofound.com
NEXT_PUBLIC_BUNNY_CDN_URL=https://cms-profound.b-cdn.net # CMS-ben hosztolt képeket szolgál ki
# Stripe
STRIPE_SECRET_KEY=sk_test_... # itt tesztkulcs; élesítéskor cseréld éles kulcsra
STRIPE_WEBHOOK_SECRET=whsec_... # a Build 4. lépése tölti ki
NEXT_PUBLIC_SITE_URL=http://localhost:3000
A STRIPE_SECRET_KEY értékét a Stripe → Developers → API keys menüpontból szerezd be. Tesztkulcsot (sk_test_…) használunk, így az építés során nem mozog valódi pénz; valódi fizetések fogadásakor válts éles kulcsra. Futtasd a bun dev parancsot, és nyisd meg a localhost:3000 címet — megjelenik a kezdőalkalmazás.
A hosztolt pénztár a böngészőt egy Stripe-URL-re irányítja át, ezért a szerver titkos kulcsa az egyetlen szükséges Stripe-érték — nincs szükség közzétehető kulcsra vagy kliensoldali Stripe SDK-ra.
category, product_image és item komponensek definiálásaHozz létre három Custom Componentet (Components → Create new component) — ezek lesznek az adatforrások, ezért ne adj hozzájuk UI Element címkét. Mindegyiket állítsd Active állapotúra.
A CMS-ben nincs „képek tömbje” mező, ezért a galéria egy kis product_image komponensre mutató referenciák tömbje. A category és product_image komponenseket (és az Active állapotot) az item előtt hozd létre — a referencia csak Active komponenseket célozhat.
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 (referenciák tömbje → product_image), price (Number, centben — csak megjelenítésre), currency (Select, usd), stripePriceId (Text), category (Reference → category), active (Boolean)Minden mezőt hagyj opcionálisnak. Az admin alsó aláhúzásos formára alakítja a mezőneveket („Stripe Price Id” → stripe_price_id) — a kód erre épít, ezért a tényleges neveket a generate-schemas futtatásából olvasd vissza. A bejárható azonosító neve code (nem slug): ez a Route Slug és egyben a későbbi tiszta documents.getByCode keresés kulcsa.
Az item komponens — a code Route Slugként, az images product_image referenciákkal, továbbá a stripePriceId és a category referencia.
bun run generate-schemas
Zod-sémákat és típusokat ír a generated/cms-schemas.ts fájlba (categorySchema/Category, itemSchema/Item). Kapcsolatellenőrzésként is szolgál — hibás hitelesítő adatok esetén itt hibát jelez.
Telepítsd és hitelesítsd az MCP-t egyszer:
claude mcp add --transport http Profound http://107.21.107.99:8081/mcp
Futtasd az mcp__Profound__authenticate parancsot, végezd el a WorkOS-folyamatot, majd kérd meg a Claude-ot:
Hozz létre egy kis e-kereskedelmi katalógust egy Edison's Inventions nevű üzlethez — három kategóriát és az alábbi termékeket, mindegyikhez rövid, korhű
descriptionmezővel, centben megadottpriceértékkel,currency: "usd"ésactive: truebeállítással:
- 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)Minden kategóriához szükség van
nameés kisbetűscodemezőre; minden elemhezname, kisbetűscode,description, centben megadottprice,currencyésactivekell. Mentsd az adatokat adata/catalog.jsonfájlba, és ellenőrizd őket acategoryésitemkomponensek alapján. Ezután a Profound MCP segítségével hozd létre mindegyiket közzétett dokumentumként: először a kategóriákat hozd létre, mentsd el az azonosítóikat, majd hozd létre a termékeketcategoryreferenciával —{ "_type": "reference", "_ref": "<category-id>", "_schema": "category" }. AstripePriceIdmezőt egyelőre hagyd üresen. A termékeket párhuzamosan hozd létre.
A Claude létreírja és ellenőrzi a data/catalog.json fájlt, majd párhuzamos create_document hívásokat indít (status: "published"). A kategóriákat a termékek előtt töltsd fel, hogy a referenciák már létező azonosítókra mutassanak.
A három feltöltött, közzétett és Live kategória.
A nyolc feltöltött termék, mindegyik kategóriához kapcsolva.
A katalógus már a CMS-ben van; most adj valódi Stripe-árat néhány termékhez — ezt a kereskedő két adminfelületen, kód nélkül végzi el:
price_…).stripePriceId mezőbe, majd mentsd.A CMS tartja a katalógust, a Stripe a hivatalos árat; a kapcsolat egyetlen, a kereskedő által beillesztett szöveg. (Automatizálnád? A hivatalos Stripe MCP létrehozhatja helyetted a Products/Prices elemeket — a visszakapott azonosítókat ugyanígy illeszd be.)
A scaffold stílusok nélkül érkezik. Helyezz el egy DESIGN.md fájlt (Tailwind v4 @theme blokk + tokenek) a projekt gyökerében — sajátot, vagy tölts le egyet a refero.design oldalról. Ezután csak a stílusozásra korlátozva kérd meg a Claude-ot:
Olvasd el az imént hozzáadott designfájlt. Ha szükséges, állítsd be a Tailwindet, majd kösd be a témát és a betűtípusokat, hogy a stílusozás működjön. A betűtípusokhoz használd a
next/font-ot — futásidőben ne töltsd be őket a Google-ről. Csak a stílusozást végezd el — még ne építs oldalakat vagy komponenseket.
Ellenőrizd, hogy a src/app/globals.css tartalmazza az @import "tailwindcss"; sort és az @theme blokkot, valamint hogy a localhost:3000 megjeleníti a tokeneket. Tartsd szűken a promptot, és a betűtípusokat mindig next/font segítségével töltsd be, soha ne futásidejű Google-importtal.
Opcionális — képek nélkül is működő pénztárhoz juthatsz. Képek hozzáadásához hozz létre képenként egy product_image dokumentumot (töltsd fel a képet az image mezőbe), majd hivatkozz rájuk a termék images tömbjéből. Használd saját termékfotóidat, vagy készíts egységes készletet képgeneráló modellel.
Az önálló
cms-renderernem tartalmaz kép-URL-segédet, ezért másold be abuildAssetUrlfüggvényt asrc/lib/image.tsfájlba (körülbelül 40 sor) — ez elé fűzi aNEXT_PUBLIC_BUNNY_CDN_URLértékét, és hozzáadja a kiterjesztést. A Build 3. lépésének komponensei ezt használják.
Építsd fel a renderelési réteget és a pénztárat, a végén pedig hajts végre egy valódi, tesztmódú vásárlást.
Öt komponens, mindegyik Active állapotban és UI Element címkével (Settings → Tags), Route Slug nélkül:
nav → brand · product_grid → heading · product_detail → heading · cart_summary → heading · footer → text (mindegyik Text)A UI Element címke teszi lehetővé, hogy egy komponens megjelenjen a Page Builder Add UI Element listájában — az Active állapot önmagában nem elég. Minden mező skaláris, vagyis CEL-lel köthető; a tényleges katalógusadat nem itt található, hanem a ProductGrid/ProductDetail komponens kéri le az útvonalparaméter alapján.
bun run generate-schemas
Egyetlen prompt létrehozza az olvasósegédet, az öt komponenst, a kosarat és a regisztrációt:
Építsd fel az áruházunkat a
src/alatt a Profoundcms-rendererSDK használatával.
src/lib/catalog.ts— szerveroldali CMS-olvasó. Hozz létre klienst acms-renderer/lib/cms-apigetCmsClient({ cmsUrl: process.env.NEXT_PUBLIC_CMS_API_URL!, apiKey: process.env.PROFOUND_API_KEY, websiteId: process.env.NEXT_PUBLIC_PROFOUND_WEBSITE_ID! })függvényével. Exportáld agetItemByCode(code)függvényt, amely acms.documents.getByCode.query({ websiteId, schemaName: "item", code })hívást használja, és ares.document.published_contentértéket adja vissza. Exportáld alistItems(categoryCode?)függvényt is: használja acms.documents.list.query({ websiteId, schemaName: "item", status: "published", limit: 100 })hívást, alakítsa ares.documentselemeit.published_contentértékre, szűrje ki azactive !== falseelemeket, és ha meg van advacategoryCode, csak azokat tartsa meg, amelyekcategory._refértéke megegyezik a kategóriadocument.idértékével. Exportáld aresolveImages(refs)függvényt, amely acms.documents.get.query({ websiteId, id: ref._ref })hívással oldja fel azitem.imagesreferenciáit, majd a vendorizáltbuildAssetUrlsegítségével URL-lé alakítja a képmezőt.
src/components/— öt UI-element komponens a gyűjtőútvonal regisztrációjában komponensnév szerint, snake_case formában, az adminnal egyezően:{ nav, product_grid, product_detail, cart_summary, footer }. ANavés aFooteracontentpropból olvassa skaláris mezőjét (acms-renderer/lib/typesBlockComponentProps<T>típusával). AProductGridés aProductDetailaszinkron szerverkomponensek, amelyek arouteParamsértékét olvassák és acatalog.tsfájlból kérnek adatot: arouteParams.<param>értéke{ value, … }— a.valuemezőt olvasd. AProductGridhívja alistItems(routeParams.category_code?.value)függvényt (a kártyák/products/{code}címre mutatnak), aProductDetailpedig agetItemByCode(routeParams.item_code?.value)függvényt (galériaresolveImagessegítségével, rich-text leírás, ár, kosárba helyezés). ACartSummaryauseCartsegítségével jelenítse meg a kosarat, Pay gombbal. AformatPricemaradjon tisztasrc/lib/format.tsfájlban, hogy a klienskomponensek ne importálják a csak szerveroldalicatalog.tsmodult.
src/components/AddToCartButton.tsx—"use client"gomb, amely{ code, name, priceLabel }propokat fogad, és auseCart().addItem({ code, name, priceLabel, quantity: 1 })hívást végzi. Használd aProductDetailkomponensben.
src/lib/useCart.ts— headless kosár:{ code, name, priceLabel, quantity }sorok állapotban,localStorage-ba mentve,addItem/removeItem/updateQty/subtotalértékekkel, valamintcheckout()függvénnyel, amely a{ lines: [{ code, quantity }] }adatot küldi POST-tal a/api/stripe/checkoutcímre (csak kódok és mennyiségek — árakat soha ne küldj), majd a visszakapotturlcímre irányítja át a böngészőt.Mindent a saját designrendszerünkkel, saját komponensként stílusozz — ne másold a forrásoldal elrendezését.
A futtatás után három dologra figyelj:
content propban érkeznek ({ content }: BlockComponentProps<T>). A mezőket legfelső szintű propként bontsd ki, különben a blokk üresen jelenik meg. A katalógusadat az útvonalparaméterből és egy catalog.ts-lekérésből érkezik, mert a CEL nem tud listákat vagy galériákat kötni. A kosár csak termékkódokat, árakat soha nem tartalmaz.routeParams.<param> értéke { value, schemaName, document } — a .value mezőt olvasd. Az olvasások published_content értéket adnak vissza, nem .content-et. A regisztráció kulcsai az adminnal egyező snake_case formát használják.@types/react és @types/react-dom csomagokat v19-re — a scaffold v18-at tartalmaz, ami megszakítja az aszinkron szerverkomponens-blokkokat React 19 alatt.Három rövid szerverfájl — az alkalmazás egyetlen fizetési kódja. Ezek újrahasznosítják a getItemByCode függvényt, így a terhelés szerveroldalon oldódik fel.
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 — az egyes termékek feloldása a CMS-ből és a Stripe-ár terhelése:
import { NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
import { getItemByCode } from "@/lib/catalog"; // szerveroldali, read-tier kulcs
export async function POST(req: Request) {
const { lines } = await req.json(); // [{ code, quantity }] — a kliens nem küld árakat
const line_items = await Promise.all(
lines.map(async ({ code, quantity }: { code: string; quantity: number }) => {
const item = await getItemByCode(code); // a szerver a CMS-ből oldja fel
return { price: item!.stripe_price_id, quantity }; // ár a CMS-ből, soha nem a kliensből
})
);
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 }); // a kliens ide irányít át
}
src/app/api/stripe/webhook/route.ts — a megbízható teljesítési jelzés:
import { stripe } from "@/lib/stripe";
export async function POST(req: Request) {
const body = await req.text(); // NYERS törzs — szükséges az aláírás ellenőrzéséhez
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") {
// teljesítés: rendelés rögzítése / nyugta küldése.
}
return new Response(null, { status: 200 });
}
Szükséges scaffold-javítás: a scaffold
src/proxy.tsfájlja minden/api/*útvonalat a CMS-hez továbbít, ezért a Stripe-útvonalak nem futnak le. Először engedd át őket helyben: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(); // helyben kezelendő } return cmsProxy(request as unknown as Parameters<typeof cmsProxy>[0]); }; // a scaffold `export const config = { matcher: [...] }` sorát hagyd változatlanulEllenőrzés: a
curl -X POST localhost:3000/api/stripe/webhook -d xparancsBad signatureválaszt ad.
Futtasd a Stripe CLI-t a helyi webhookokhoz:
stripe login
stripe listen --forward-to localhost:3000/api/stripe/webhook
# másold a whsec_... értéket a STRIPE_WEBHOOK_SECRET változóba, majd indítsd újra a bun dev-et
A whsec_… munkamenetenként eltérő. Két szabály biztosítja a biztonságot: a pénztár a code alapján újra feloldja az árat a CMS-ből, így a módosított kosár nem változtathatja meg azt; a webhook pedig a nyers törzsön ellenőrzi az aláírást.
Admin → Pages → Create page, három alkalommal. Rendeld az egyes paramétereket a komponenshez (a slug mező neve code):
/products/{item_code} → item/categories/{category_code} → category/cart — statikus oldal (szó szerint /cart, ne /{cart})Minden útvonalnál: Page Builder → Add UI Element → Custom → add hozzá a komponenseket a megadott sorrendben, töltsd ki a skaláris mezőket (statikus értékkel vagy CEL-lel), majd Publish.
/products/{item_code}: nav, product_detail, footer/categories/{category_code}: nav, product_grid, footer/cart: nav, cart_summary, footerA nav.brand és footer.text mezőkhöz adj statikus szöveget; a címsorokhoz statikus címkéket.
A Page Builder kategóriaútvonala — a product_grid elem kiválasztva, címsora CEL-lel bekötve.
A Page Builder termékútvonala — a product_detail elem egy termékhez kötve.
A paraméterezett Page Builder buktatója: a két paraméterezett útvonalon a UI-elemek hozzáadása nem marad meg (a blokkok árván maradnak, az oldal pedig üresen jelenik meg). A javításig közvetlenül a Profound MCP
update_pagehívásával állítsd be az oldalakblock_idsértékét, majd publikáld őket. A statikus/cartnormálisan csatolódik. Ugyanebből az okból aProductGrida lekért kategóriából származtatja a címsorát, nem CEL-lel kapja.
/categories/lighting → a rács. Kattints egy termékre → részletek és kosárba helyezés. /cart → Pay./cart?status=success címre, a stripe listen pedig megjeleníti a checkout.session.completed eseményt.Megjelenített termékoldal — galéria, ár és kosárba helyezés.
A kosár — terméksorok és egyetlen Pay-with-Stripe gomb.
Csak az árral rendelkező termékek vásárolhatók meg — válassz egyet abból a körülbelül háromból, amelyekhez a 6. lépésben árat rendeltél.
Opcionális — nemzetköziesítés. Fordítsd le az egyes komponenseket (mind a 35 nyelvet egyszerre), adj hozzá egy
/{language}/…szegmenst a beépítettlanguageSystem komponenshez rendelve, és állítsd át a CEL-lel kötött mezőketdocuments.translatedhasználatára. Lásd a repülőtéri címtár útmutatójának 2. részét, 7. lépését.
git init && git add -A && git commit -m "Stripe storefront"
gh repo create store --private --source=. --push # a --public is megfelelő
Build parancs: a
generated/cms-schemas.tsfájl figyelmen kívül van hagyva a Gitben, ezért rögzítsd a buildet az újrageneráláshoz — adj hozzá egyvercel.jsonfájlt:{ "$schema": "https://openapi.vercel.sh/vercel.json", "buildCommand": "bun run generate-schemas && next build" }
A Vercelben: Add New → Project, importáld a store projektet, majd add hozzá a környezeti változókat — PROFOUND_API_KEY, NEXT_PUBLIC_PROFOUND_WEBSITE_ID, NEXT_PUBLIC_CMS_API_URL, NEXT_PUBLIC_BUNNY_CDN_URL, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET (az alábbi telepített végpont értéke) és NEXT_PUBLIC_SITE_URL (az éles URL). Telepítsd az alkalmazást.
Ezután kösd be a telepített webhookot (a helyi stripe listen titka csak helyben érvényes): Stripe → Developers → Webhooks → + Add endpoint → https://<prod>/api/stripe/webhook, esemény: checkout.session.completed. Másold a hozzá tartozó whsec_… értéket a Vercelbe, majd telepítsd újra az alkalmazást.
Hiányzó környezeti változók = „helyben működik, élesben üres” — ez a telepítések leggyakoribb buktatója. Itt tesztkulcsokkal telepítünk; valódi fizetések fogadásakor válts a live STRIPE_SECRET_KEY és webhook-titokra.
Mindkettőt tartalmazza a scaffold.
<Refresher> frissíti az éppen előnézetben megnyitott oldalt, amikor egy szerkesztő ment az adminban — újratelepítés nélkül. (Ez a szerkesztő előnézete; a látogatók normál újraérvényesítéskor a közzétett tartalmat látják.)?edit_mode=true paramétert a szerkesztési rétegekhez. A nyilvános látogatók a tiszta oldalt kapják.Add hozzá a scaffoldból hiányzó előnézeti útvonalat. Az admin az előnézeti iframe-et a
/cms-preview_<path>címen tölti be; enélkül minden előnézet 404-et ad. Add hozzá:// src/app/cms-preview_/[...slug]/page.tsx import { ParametricRoutePreviewPage } from "cms-renderer/lib/renderer"; import { registry } from "../../registry"; // emeld ki a registryt megosztott modulba export default async function Page({ params, searchParams }) { const { slug } = await params; const PreviewPage = ParametricRoutePreviewPage as any; // aszinkron RSC; React 19 típusok 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} />; }Add hozzá a
src/app/cms-preview_/page.tsxfájlt is ugyanígy (slug: []) a szegmens gyökeréhez.
Egy tartalomvezérelt Stripe-áruház: CMS-katalógus, egyetlen útvonalrendszerből származó lista- és részletezőoldalak, valamint működő hosztolt pénztár. A MI feltöltötte a katalógust, bekötötte a dizájnt, és megírta a katalógusolvasót, a komponenseket és a headless kosarat; te hoztad létre a komponenseket, a Stripe-árkapcsolatokat, a három útvonalat, a CEL-felületi elemeket és a három rövid Stripe-fájlt. A CEL a felületi elemeket köti; a komponensek kérik le a katalógust. A Stripe-kód pedig kicsi maradt — egy sessions.create hívás és egy aláírt webhook, miközben a vásárló közvetlenül a Stripe saját oldalán fizet.