Ένας πρακτικός οδηγός: δημιουργήστε μια βιτρίνα Stripe με επίκεντρο το περιεχόμενο στο Profound CMS — έναν κατάλογο που ο έμπορος επεξεργάζεται χωρίς κώδικα, δύο παραμετρικές διαδρομές, ένα headless καλάθι και checkout που φιλοξενείται στο Stripe.
Το ολοκληρωμένο κατάστημα σε κίνηση — περιηγηθείτε σε μια κατηγορία, ανοίξτε ένα προϊόν, προσθέστε το στο καλάθι, ολοκληρώστε την αγορά.
Ένας πρακτικός οδηγός που δημιουργεί ένα κατάστημα προσανατολισμένο στο περιεχόμενο στο Profound CMS: έναν κατάλογο προϊόντων (κατηγορίες + αντικείμενα) μοντελοποιημένο στο CMS, σελίδες καταλόγου και λεπτομερειών από ένα σύνολο διαδρομών και checkout που φιλοξενείται στο Stripe αποσταλμένο ως headless στοιχείο.
Η ραχοκοκαλιά είναι χειρόγραφο Next.js μαζί με το Profound admin. Το Claude Code (μέσω του Profound MCP) κάνει το μεγαλύτερο μέρος της δουλειάς σε τρεις εργασίες — γέμισμα του καταλόγου, σύνδεση του συστήματος σχεδίασης και συγγραφή των στοιχείων της βιτρίνας (συμπεριλαμβανομένου του headless καλαθιού). Τρία μέρη: Ρύθμιση, Κατασκευή, Παραγωγή.
Πληρωμές σε μία γραμμή. Χρησιμοποιούμε Checkout που φιλοξενείται στο Stripe: ο αγοραστής πληρώνει στη σελίδα του Stripe, όχι στη δική σας. Η εφαρμογή σας κάνει μόνο δύο πράγματα στην πλευρά του διακομιστή — δημιουργεί ένα Checkout Session και επαληθεύει ένα webhook. Χωρίς πεδία καρτών, χωρίς Stripe Elements, χωρίς βάρος PCI.
/products/{item_code}, /categories/{category_code}) συν ένα
στατικό /cart, όλα από ένα σύνολο στοιχείων.useCart) και checkout που φιλοξενείται στο Stripe, με την τιμή να
επιλύεται πάντα στην πλευρά του διακομιστή από ένα Stripe Price ID.curl -fsSL https://bun.sh/install | bashstripe login) για τοπικά webhooks.gh) και ένας λογαριασμός Vercel συνδεδεμένος με το GitHub.Το Profound διαχωρίζει το περιεχόμενο από την απόδοση:
category, item); ένα με ετικέτα UI Element μπορεί να τοποθετηθεί σε σελίδα
(nav, product_grid, …).meta.params.* στο CEL, routeParams στο React).cms-renderer; το Stripe προστίθεται ως συνηθισμένα API routes.Ο ένας κανόνας που διαμορφώνει την κατασκευή: το CEL δεσμεύει μόνο πεδία string/number. Έτσι
το στατικό περίβλημα (brand στο nav, υποσέλιδο, επικεφαλίδες) δεσμεύεται με CEL, ενώ οτιδήποτε πλούσιο ή
συλλογή (πλέγμα προϊόντων, συλλογή εικόνων, rich text) ανακτάται μέσα στο React component με βάση την παράμετρο διαδρομής.
Και το Stripe είναι η πηγή αλήθειας για την τιμολόγηση — η τιμή στο CMS price είναι μόνο για προβολή· η χρέωση
επιλύεται πάντα στην πλευρά του διακομιστή από ένα Stripe Price ID.
Τελική κατάσταση: ένας μικρός δημοσιευμένος κατάλογος, η εφαρμογή συνδεδεμένη για να τον διαβάζει, το Stripe εγκατεστημένο, το design στη θέση του — τίποτα δεν αποδίδεται ακόμη.
Εγγραφείτε στο Profound (WorkOS auth). Δημιουργήστε έναν ιστότοπο με όνομα store, έπειτα αντιγράψτε το website ID του (το UUID στο URL του admin) και ένα API key μόνο για ανάγνωση (Deployments → Create API key).
Η εφαρμογή μόνο διαβάζει· το γέμισμα του καταλόγου αργότερα περνά μέσα από το MCP, που κάνει ξεχωριστή ταυτοποίηση.
bunx create-profound-next store
cd store
bun add stripe
Το scaffold είναι ένα project Next.js App Router προρυθμισμένο για το Profound (το SDK cms-renderer,
μια catch-all διαδρομή, ένα script generate-schemas, ένα <Refresher>). Δεν περιλαμβάνει styling.
Το bun add stripe φέρνει το server SDK — η μόνη εξάρτηση πληρωμών που χρειάζεται το φιλοξενούμενο checkout.
Προσθέστε τις τιμές σας στο .env.local:
# 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
# Stripe
STRIPE_SECRET_KEY=sk_test_... # δοκιμαστικό κλειδί εδώ· αντικαταστήστε με το live κλειδί σας όταν πάτε live
STRIPE_WEBHOOK_SECRET=whsec_... # συμπληρώνεται στο βήμα Κατασκευής 4
NEXT_PUBLIC_SITE_URL=http://localhost:3000
Πάρτε το STRIPE_SECRET_KEY από Stripe → Developers → API keys. Χρησιμοποιούμε ένα δοκιμαστικό κλειδί
(sk_test_…) ώστε η υλοποίηση να μη μετακινεί πραγματικά χρήματα· αλλάξτε στο live κλειδί όταν είστε έτοιμοι
για πραγματικές πληρωμές. Τρέξτε bun dev και ανοίξτε localhost:3000 — εμφανίζεται το starter.
Το φιλοξενούμενο checkout ανακατευθύνει τον browser σε ένα URL του Stripe, οπότε το server secret key είναι το μόνο που χρειάζεται το Stripe — χωρίς publishable key, χωρίς client Stripe SDK.
category, product_image και itemΔημιουργήστε τρία Custom Components (Components → Create new component) — την πηγή δεδομένων, άρα χωρίς ετικέτα UI Element. Ορίστε καθένα ως Active.
Το CMS δεν έχει πεδίο "array of image", οπότε μια συλλογή είναι array από references σε ένα μικρό component product_image.
Δημιουργήστε τα category και product_image (και ορίστε τα Active) πριν το item — ένα πεδίο reference στοχεύει μόνο Active components.
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 από references → product_image), price (Number, cents — μόνο για προβολή), currency (Select, usd), stripePriceId (Text), category (Reference → category), active (Boolean)Αφήστε όλα τα πεδία προαιρετικά. Το admin μετατρέπει τα ονόματα πεδίων σε lower snake case ("Stripe Price Id" →
stripe_price_id) — αυτό χρησιμοποιεί ο κώδικας σας, οπότε διαβάστε τα πραγματικά ονόματα μέσω του generate-schemas στο επόμενο βήμα. Ονομάζουμε τον δρομολογήσιμο αναγνωριστικό code (όχι slug): είναι το Route Slug και το κλειδί για ένα καθαρό documents.getByCode αργότερα.
Το component item — code ως Route Slug, images ως references στο product_image, μαζί με stripePriceId και reference category.
bun run generate-schemas
Αποθηκεύει Zod schemas + types στο generated/cms-schemas.ts (categorySchema/Category,
itemSchema/Item). Λειτουργεί και ως έλεγχος σύνδεσης — λάθος διαπιστευτήρια αποτυγχάνουν εδώ.
Εγκαταστήστε και κάντε authenticate το MCP μία φορά:
claude mcp add --transport http Profound http://107.21.107.99:8081/mcp
Τρέξτε mcp__Profound__authenticate, ολοκληρώστε το WorkOS flow και έπειτα ζητήστε από το Claude:
Δημιούργησε έναν μικρό ecommerce κατάλογο για ένα κατάστημα που ονομάζεται Edison's Inventions — τρεις κατηγορίες και αυτά τα προϊόντα, με μια σύντομη περιγραφή ακριβή για την εποχή για κάθε ένα,
priceσε cents,currency: "usd", και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)Κάθε κατηγορία χρειάζεται
nameκαι αυτό το πεζόcode; κάθε item χρειάζεταιname, το ίδιο πεζόcode, την περιγραφή,price(σε cents),currencyκαιactive. Αποθήκευσέ το στοdata/catalog.jsonκαι επικύρωσέ το έναντι των components μαςcategoryκαιitem. Έπειτα χρησιμοποίησε το Profound MCP για να δημιουργήσεις καθένα ως δημοσιευμένο έγγραφο: δημιούργησε πρώτα τις κατηγορίες, κράτησε τα IDs τους, μετά δημιούργησε τα items μεcategoryως reference —{ "_type": "reference", "_ref": "<category-id>", "_schema": "category" }. Κάνε τα items σε parallel.
Ο Claude γράφει το data/catalog.json, το επικυρώνει και εκτελεί παράλληλες κλήσεις create_document (status: "published"). Γεμίστε τις κατηγορίες πριν από τα items ώστε τα references να δείχνουν σε ήδη υπάρχοντα IDs.
Οι τρεις κατηγορίες που γεμίστηκαν, δημοσιευμένες και Live.
Τα οκτώ προϊόντα που γεμίστηκαν, το καθένα συνδεδεμένο με μια κατηγορία.
Ο κατάλογος είναι στο CMS· τώρα δώστε σε μερικά προϊόντα μια πραγματική τιμή Stripe — δουλειά του εμπόρου, σε δύο admin panels, χωρίς κώδικα:
price_…).stripePriceId, αποθηκεύστε.Το CMS κρατά τον κατάλογο· το Stripe κρατά την επίσημη τιμή· ο σύνδεσμος είναι ένα string που επικολλά ο έμπορος. (Θέλετε να το αυτοματοποιήσετε; Το επίσημο Stripe MCP μπορεί να δημιουργήσει τα Products/Prices για εσάς — επικολλήστε τα επιστραφέντα IDs με τον ίδιο τρόπο.)
Το scaffold είναι χωρίς στυλ. Τοποθετήστε ένα DESIGN.md (ένα μπλοκ @theme Tailwind v4 + tokens) στη ρίζα του project — δικό σας ή κατεβάστε ένα από το refero.design.
Έπειτα δώστε την εξής εντολή στο Claude, με πεδίο μόνο το styling:
Διάβασε το αρχείο σχεδίασης που μόλις πρόσθεσα. Ρύθμισε το Tailwind αν χρειάζεται, μετά σύνδεσε το θέμα και τις γραμματοσειρές ώστε το styling να λειτουργεί. Χρησιμοποίησε
next/fontγια τις γραμματοσειρές — μην τις φορτώνεις από τη Google κατά το runtime. Μόνο το styling — μην φτιάξεις ακόμα σελίδες ή components.
Επαληθεύστε ότι το src/app/globals.css έχει @import "tailwindcss"; + το μπλοκ @theme και ότι το localhost:3000 εμφανίζει τα tokens. Κρατήστε το prompt αυστηρό (με ανοιχτό prompt, ένας agent μπορεί να στήσει ολόκληρη αρχική σελίδα) και φορτώστε γραμματοσειρές μέσω next/font, ποτέ με runtime import Google.
Προαιρετικό — μπορείτε να φτάσετε σε λειτουργικό checkout χωρίς εικόνες. Για να τις προσθέσετε: δημιουργήστε ένα
έγγραφο product_image ανά εικόνα (ανεβάστε την στο πεδίο image), μετά αναφερθείτε σε αυτά από το array images του προϊόντος.
Φέρτε δικές σας φωτογραφίες ή δημιουργήστε ένα ομοιογενές σύνολο με ένα μοντέλο εικόνων (ζητήστε από το Claude να παράξει prompts σύμφωνα με το DESIGN.md και κλειδώστε ένα Midjourney --sref ώστε κάθε λήψη να ταιριάζει).
Το standalone
cms-rendererδεν έχει helper για URLs εικόνων, οπότε προσθέστε τοbuildAssetUrlστοsrc/lib/image.ts(~40 γραμμές) — κάνει prefix τοNEXT_PUBLIC_BUNNY_CDN_URLκαι προσθέτει την κατάλληλη κατάληξη. Τα components στο βήμα Κατασκευής 3 το χρησιμοποιούν.
Χτίστε το επίπεδο απόδοσης και το checkout, καταλήγοντας σε μια πραγματική αγορά σε λειτουργία δοκιμής.
Πέντε components, καθένα Active και με ετικέτα UI Element (Settings → Tags), χωρίς Route Slug:
nav → brand · product_grid → heading · product_detail → heading ·
cart_summary → heading · footer → text (όλα Text)Η ετικέτα UI Element είναι αυτό που κάνει ένα component να εμφανίζεται στη λίστα Add UI Element του Page Builder — το Active μόνο δεν αρκεί. Κάθε πεδίο είναι scalar (το είδος που δένει το CEL); τα πραγματικά δεδομένα του καταλόγου δεν είναι πεδίο εδώ — τα ProductGrid/ProductDetail τα ανακτούν με βάση την παράμετρο διαδρομής (βήμα 3).
bun run generate-schemas
Ένα prompt δημιουργεί τον helper ανάγνωσης, τα πέντε components, το καλάθι και το registry:
Χτίσε τη βιτρίνα μας στο
src/, χρησιμοποιώντας το SDKcms-rendererτου Profound.
src/lib/catalog.ts— ένας αναγνώστης CMS στην πλευρά του server. Δημιούργησε έναν client μεgetCmsClient({ cmsUrl: process.env.NEXT_PUBLIC_CMS_API_URL!, apiKey: process.env.PROFOUND_API_KEY, websiteId: process.env.NEXT_PUBLIC_PROFOUND_WEBSITE_ID! })από τοcms-renderer/lib/cms-api. ΕξήγαγεgetItemByCode(code)→cms.documents.getByCode.query({ websiteId, schemaName: "item", code })που επιστρέφειres.document.published_content. ΕξήγαγεlistItems(categoryCode?)→cms.documents.list.query({ websiteId, schemaName: "item", status: "published", limit: 100 }), χαρτογράφησε ταres.documentsσε.published_content, φιλτράρισεactive !== falseκαι, αν δοθείcategoryCode, κράτησε items των οποίων τοcategory._refισούται με τοdocument.idτης κατηγορίας. ΕξήγαγεresolveImages(refs)που επιλύει κάθε αναφοράitem.imagesμέσωcms.documents.get.query({ websiteId, id: ref._ref })και μετατρέπει το πεδίο image σε URL με τοbuildAssetUrlπου προστέθηκε (Μέρος 1 βήμα 8).
src/components/— πέντε components UI element καταχωρημένα στο registry της catch-all διαδρομής με βάση το όνομα του component, σε snake_case για να ταιριάζει με το admin:{ nav, product_grid, product_detail, cart_summary, footer }. ΤαNavκαιFooterδιαβάζουν το scalar πεδίο από το propcontent(τυποποιημένο ωςBlockComponentProps<T>απόcms-renderer/lib/types). ΤαProductGridκαιProductDetailείναι async server components που διαβάζουν ταrouteParamsκαι κάνουν fetch από τοcatalog.ts: τοrouteParams.<param>είναι{ value, … }— διάβασε το.value, ώστε τοProductGridνα καλείlistItems(routeParams.category_code?.value)(οι κάρτες οδηγούν σε/products/{code}) και τοProductDetailνα καλείgetItemByCode(routeParams.item_code?.value)(συλλογή μέσωresolveImages, rich-text περιγραφή, τιμή, Add-to-cart). ΤοCartSummaryαποδίδει το καλάθι από τοuseCartμε ένα κουμπί Pay. Κράτησε τοformatPriceσε ένα καθαρόsrc/lib/format.tsώστε τα client components να μην εισάγουν το server-onlycatalog.ts.
src/components/AddToCartButton.tsx— ένα κουμπί"use client"που δέχεται{ code, name, priceLabel }και καλείuseCart().addItem({ code, name, priceLabel, quantity: 1 }). Χρησιμοποίησέ το μέσα στοProductDetail.
src/lib/useCart.ts— ένα headless καλάθι: γραμμές{ code, name, priceLabel, quantity }σε state, αποθηκευμένο στοlocalStorage, που εκθέτειaddItem/removeItem/updateQty/subtotalκαι έναcheckout()που κάνει POST{ lines: [{ code, quantity }] }(μόνο κωδικούς και ποσότητες — ποτέ τιμές) στο/api/stripe/checkout, έπειτα κάνει redirect στοurlπου επιστρέφεται.Στυλάρεις τα πάντα με το δικό μας σύστημα σχεδίασης, ως δικά μας components — μην αντιγράψεις τη διάταξη του πηγαίου site.
Τρία πράγματα που πρέπει να ξέρετε αφού εκτελεστεί:
content ({ content }: BlockComponentProps<T>) — αν κάνετε destructure των πεδίων ως
top-level props, το block αποδίδει κενό. Τα δεδομένα του καταλόγου έρχονται από τα routeParams + ένα fetch από το catalog.ts,
γιατί το CEL δεν δένει λίστες ή galleries. Το καλάθι μεταφέρει κωδικούς αντικειμένων, ποτέ τιμές.routeParams.<param> είναι { value, schemaName, document } — διαβάστε το .value. Τα reads επιστρέφουν
published_content, όχι .content. Τα κλειδιά του registry είναι snake_case ώστε να ταιριάζουν με το admin.@types/react/@types/react-dom στην έκδοση 19 — το scaffold αποστέλλεται με v18, που σπάει τα async
server-component blocks με το React 19.Τρία σύντομα αρχεία server — ο μόνος κώδικας πληρωμών στην εφαρμογή. Επαναχρησιμοποιούν το getItemByCode, ώστε η χρέωση
να επιλύεται στην πλευρά του server.
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 — επιλύει κάθε αντικείμενο από το CMS, χρεώνει την Stripe
τιμή:
import { NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
import { getItemByCode } from "@/lib/catalog"; // server-side, κλειδί μόνο για ανάγνωση
export async function POST(req: Request) {
const { lines } = await req.json(); // [{ code, quantity }] — καμία τιμή από τον client
const line_items = await Promise.all(
lines.map(async ({ code, quantity }: { code: string; quantity: number }) => {
const item = await getItemByCode(code); // ο διακομιστής επιλύει από το CMS
return { price: item!.stripe_price_id, quantity }; // τιμή από το CMS, ποτέ από τον 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 }); // ο client κάνει redirect εδώ
}
src/app/api/stripe/webhook/route.ts — το έμπιστο σήμα εκπλήρωσης:
import { stripe } from "@/lib/stripe";
export async function POST(req: Request) {
const body = await req.text(); // ΑΚΑΤΕΡΓΑΣΤΟ σώμα — απαιτείται για επαλήθευση υπογραφής
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") {
// fulfill: καταγράψτε την παραγγελία / στείλτε απόδειξη.
}
return new Response(null, { status: 200 });
}
Απαραίτητη διόρθωση στο scaffold: το
src/proxy.tsτου scaffold προωθεί κάθε/api/*στο CMS, οπότε τα Stripe routes δεν εκτελούνται. Αφήστε αυτά να περάσουν πρώτα: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(); // χειρισμός τοπικά } return cmsProxy(request as unknown as Parameters<typeof cmsProxy>[0]); }; // κρατήστε το `export const config = { matcher: [...] }` του scaffold όπως είναιΕπαλήθευση:
curl -X POST localhost:3000/api/stripe/webhook -d xεπιστρέφειBad signature.
Τρέξτε το Stripe CLI για τοπικά webhooks:
stripe login
stripe listen --forward-to localhost:3000/api/stripe/webhook
# αντιγράψτε το whsec_... στο STRIPE_WEBHOOK_SECRET, κάντε επανεκκίνηση του bun dev
Το whsec_… είναι ανά συνεδρία. Δύο κανόνες κρατούν την ασφάλεια: το checkout αναπαράγει την τιμή
από το CMS μέσω code (ένα αλλοιωμένο καλάθι δεν μπορεί να την αλλάξει), και το webhook επαληθεύει την
υπογραφή έναντι του ακατέργαστου σώματος.
Admin → Pages → Create page, τρεις φορές. Αντιστοιχίστε κάθε παράμετρο στο component της (πεδίο slug code):
/products/{item_code} → item/categories/{category_code} → category/cart — μια στατική σελίδα (εισάγετε κυριολεκτικά /cart, όχι /{cart})Για κάθε διαδρομή: Page Builder → Add UI Element → Custom → προσθέστε components με τη σειρά, συμπληρώστε scalar πεδία (στατική τιμή ή CEL), Publish.
/products/{item_code}: nav, product_detail, footer/categories/{category_code}: nav, product_grid, footer/cart: nav, cart_summary, footerΟρίστε το nav.brand και το footer.text σε στατικές συμβολοσειρές· τις επικεφαλίδες σε στατικές ετικέτες.
Το Page Builder στη διαδρομή κατηγορίας — το στοιχείο product_grid επιλεγμένο, η επικεφαλίδα του δεμένη με CEL.
Το Page Builder στη διαδρομή προϊόντος — το στοιχείο product_detail σε binding προϊόντος.
Παραμετρική ιδιαιτερότητα του Page Builder: στις δύο παραμετρικές διαδρομές, η προσθήκη UI elements δεν επιμένει (τα blocks μένουν ορφανά και η σελίδα αποδίδει κενή). Μέχρι να διορθωθεί, δέστε τα
block_idsαυτών των σελίδων απευθείας μέσω του Profound MCPupdate_page, μετά δημοσιεύστε. (Το στατικό/cartσυνδέεται κανονικά.) Για τον ίδιο λόγο, τοProductGridαντλεί την επικεφαλίδα του από την κατηγορία που ανακτά αντί να χρησιμοποιήσει CEL.
/categories/lighting → το grid. Κάντε κλικ σε προϊόν → λεπτομέρεια + Add to cart. /cart → Pay./cart?status=success, και το stripe listen δείχνει
checkout.session.completed.Μια αποδομένη σελίδα προϊόντος — gallery, τιμή και κουμπί Add to cart.
Το καλάθι — γραμμές και ένα μοναδικό κουμπί Pay-with-Stripe.
Μόνο τα προϊόντα με τιμή είναι αγοραστέα — αγοράστε ένα από τα ~3 που τιμολογήσατε στο βήμα 6.
Προαιρετικό — διεθνοποιήστε. Μεταφράστε κάθε component (και τις 35 γλώσσες ταυτόχρονα), προσθέστε ένα τμήμα
/{language}/…που αντιστοιχεί στο ενσωματωμένο System componentlanguageκαι αλλάξτε τα πεδία που δένονται με CEL σεdocuments.translated. Δείτε το tutorial του airport directory, Μέρος 2 βήμα 7.
git init && git add -A && git commit -m "Stripe storefront"
gh repo create store --private --source=. --push # --public είναι επίσης εντάξει
Εντολή build: το
generated/cms-schemas.tsδεν παρακολουθείται από git, οπότε σταθεροποιήστε το build ώστε να το αναδημιουργεί — προσθέστεvercel.json:{ "$schema": "https://openapi.vercel.sh/vercel.json", "buildCommand": "bun run generate-schemas && next build" }
Στο Vercel: Add New → Project, εισάγετε το store και προσθέστε τα env vars — PROFOUND_API_KEY,
NEXT_PUBLIC_PROFOUND_WEBSITE_ID, NEXT_PUBLIC_CMS_API_URL, NEXT_PUBLIC_BUNNY_CDN_URL,
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET (η τιμή του deployed endpoint, παρακάτω) και
NEXT_PUBLIC_SITE_URL (το production URL σας). Δημοσιεύστε.
Έπειτα συνδέστε το deployed webhook (το μυστικό από το stripe listen τοπικά ήταν μόνο για τοπική χρήση): Stripe
→ Developers → Webhooks → + Add endpoint → https://<prod>/api/stripe/webhook, event
checkout.session.completed. Αντιγράψτε το whsec_… στο Vercel και ξαναδημοσιεύστε.
Απόντα env vars = «δουλεύει τοπικά, κενό στο production» — η #1 παγίδα deploy. Δημοσιεύουμε εδώ
με δοκιμαστικά κλειδιά· αλλάξτε το STRIPE_SECRET_KEY και το μυστικό webhook στις live τιμές σας όταν είστε έτοιμοι
να δεχθείτε πραγματικές πληρωμές.
Και τα δύο συνοδεύουν το scaffold.
<Refresher> ενημερώνει τη σελίδα που προεπισκοπεί ένας editor όταν αποθηκεύει
στο admin — χωρίς redeploy. (Είναι προεπισκόπηση για τον editor· οι επισκέπτες βλέπουν δημοσιευμένο περιεχόμενο
με κανονική revalidation.)?edit_mode=true σε οποιοδήποτε URL για επεξεργαστικά overlays. Οι δημόσιοι επισκέπτες
βλέπουν την καθαρή σελίδα.Προσθέστε τη διαδρομή προεπισκόπησης που λείπει από το scaffold. Το admin φορτώνει το iframe προεπισκόπησής του στο
/cms-preview_<path>· χωρίς αυτή τη διαδρομή κάθε προεπισκόπηση επιστρέφει 404. Προσθέστε την:// src/app/cms-preview_/[...slug]/page.tsx import { ParametricRoutePreviewPage } from "cms-renderer/lib/renderer"; import { registry } from "../../registry"; // εξάγετε το registry σας σε κοινόχρηστο module export default async function Page({ params, searchParams }) { const { slug } = await params; Χρησιμοποιήστε το PreviewPage = ParametricRoutePreviewPage as any; // async RSC; React 19 types 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} />; }Προσθέστε και το
src/app/cms-preview_/page.tsx(ίδιο,slug: []) για τη ρίζα του segment.
Μια βιτρίνα Stripe με επίκεντρο το περιεχόμενο: ένας κατάλογος στο CMS, σελίδες λίστας + λεπτομερειών από ένα σύνολο
διαδρομών και ένα λειτουργικό φιλοξενούμενο checkout. Το AI γέμισε τον κατάλογο, συνέδεσε το design και έγραψε τον
αναγνώστη καταλόγου + τα components + το headless καλάθι· εσείς φτιάξατε τα components, τους συνδέσμους τιμής Stripe,
τις τρεις διαδρομές, το CEL chrome και τρία σύντομα αρχεία Stripe. Το CEL δένει το chrome· τα components ανακτούν τον κατάλογο.
Και το Stripe έμεινε λιτό — μία κλήση sessions.create και ένα υπογεγραμμένο webhook, με τον αγοραστή να πληρώνει στη δική του σελίδα του Stripe.