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 Integrationsהגדרות-מפתחות-APISettings UsageSettings Websites
All Systems Operational
Powered Byprofound-logo
Theme

Build & Ship a Stripe Storefront

מדריך מעשי: בנו חנות Stripe מונעת-תוכן על Profound CMS — קטלוג שסוחר עורך ללא קוד, שני נתיבים פרמטריים, עגלה Headless וצ'ק-אאוט שמתארח ב-Stripe.

החנות המוגמרת בפעולה — עיינו בקטגוריה, פתחו מוצר, הוסיפו לעגלה, בצעו צ'ק-אאוט.

מדריך מעשי שבונה חנות מונחית-תוכן על Profound CMS: קטלוג מוצרים (קטגוריות + פריטים) מדולל ב-CMS, עמודי רשימה ופרטי מאותה קבוצת נתיבי ניווט, וצ'ק-אאוט מתארח ב-Stripe שנשלח כרכיב Headless.

עמוד השדרה הוא Next.js שנכתב ביד וכן הממשק של Profound. Claude Code (באמצעות ה-Profound MCP) מבצע את עיקר העבודה בשלושה תפקידים — זריעת הקטלוג, חיבור מערכת העיצוב וכתיבת רכיבי החנות (כולל העגלה ה-Headless). שלושה חלקים: הגדרה, בנייה, פרודקשן.

תשלומים בשורה אחת. אנחנו משתמשים ב-Stripe-hosted Checkout: הלקוח משלם בדף של Stripe, לא שלכם. האפליקציה שלכם עושה רק שני דברים בצד השרת — יוצרת סשן Checkout ומוודאת webhook אחד. אין שדות כרטיס, אין Stripe Elements, אין נטל PCI.

מה תבנו

  • קטלוג קטן שפורסם ב-CMS — שלוש קטגוריות ושמונה מוצרים (הדמו "Edison's Inventions") — כל אחד ניתן לעריכה על ידי סוחר ללא קוד.
  • שני נתיבים פרמטריים (/products/{item_code}, /categories/{category_code}) ועוד /cart סטטי, כולם מאותה קבוצת רכיבים.
  • עגלה Headless (useCart) וצ'ק-אאוט שמתארח ב-Stripe, כשמחיר המוצר נפתר תמיד בצד השרת מתוך מזהה מחיר של Stripe.
  • החנות נפרסת ל-Vercel עם תצוגה מקדימה חיה ועריכה במקום עבור הצוות.

דרישות מקדימות

  • Bun ≥ 1.3 — curl -fsSL https://bun.sh/install | bash
  • Claude Code עם Profound MCP (חלק 1 מתקין אותו).
  • חשבון Profound CMS.
  • חשבון Stripe. המדריך הזה פועל במצב בדיקה כך ששום דבר אינו מחייב כסף אמיתי בזמן הבנייה — אבל הזרימה זהה עם מפתחות חיים, אז תוכלו להשתמש במפתחות האמיתיים שלכם אם תרצו. (מצב בדיקה לא דורש פרטי עסק או בנק.)
  • Stripe CLI (stripe login) עבור webhooks מקומיים.
  • לפריסה: GitHub CLI (gh) וחשבון Vercel המחובר ל-GitHub.

כיצד החלקים מתחברים

Profound מפריד בין תוכן להצגה:

  • Components מגדירים את צורת התוכן. רכיב מותאם אישית עם שדה Route Slug הוא בר-ניתוב (category, item); רכיב שמתוייג כ-UI Element ניתן להצבה בעמוד (nav, product_grid, …).
  • Documents הם התוכן (מוצר, קטגוריה).
  • UI elements הם מקטעי עמוד; כל שדה סקלרי מקבל ערך סטטי או ביטוי CEL, שמוערך בזמן ההצגה.
  • נתיבים פרמטריים ממפים URL למסמך + רכיבי UI, ומעבירים פרמטרים של הנתיב (meta.params.* ב-CEL, routeParams ב-React).
  • אפליקציית Next.js שלכם קוראת זאת דרך cms-renderer; Stripe מתווספת כנתיבי API רגילים.

הכלל היחיד שמעצב את הבנייה: CEL נקשר רק לשדות מסוג string/number. לכן אלמנטים סקלריים (לוגו בניווט, פוטר, כותרות) נקשרים עם CEL, בעוד שכל דבר עשיר או אוסף (רשת מוצרים, גלריית תמונות, טקסט עשיר) נשלף בתוך רכיב ה-React לפי פרמטר הנתיב. ו-Stripe היא מקור האמת לתמחור — השדה price ב-CMS הוא להצגה בלבד; החיוב תמיד נפתר בצד השרת מתוך מזהה מחיר של Stripe.

חלק 1 — הגדרה

מצב סיום: קטלוג קטן שפורסם, האפליקציה מחוברת לקרוא אותו, Stripe מותקן, העיצוב במקומו — עדיין שום דבר לא מוצג.

1. הרשמה ויצירת האתר

הירשמו ל-Profound (אימות WorkOS). צרו אתר בשם store, ואז העתיקו את מזהה האתר שלו (ה-UUID בכתובת ה-URL של האדמין) ואת מפתח ה-API ברמת קריאה (Deployments → Create API key). האפליקציה רק קוראת; זריעת הקטלוג מאוחר יותר תעשה דרך ה-MCP, שמאמת בנפרד.

2. יצירת שלד האפליקציה, חיבורו והוספת Stripe

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

השלד הוא פרויקט Next.js App Router שמוכן מראש ל-Profound (SDK של cms-renderer, נתיב catch-all, סקריפט generate-schemas, <Refresher>). הוא אינו מגיע עם סגנון. bun add stripe מושך את ה-SDK של השרת — התלות היחידה שתהליך צ'ק-אאוט מתארח דורש.

הוסיפו את הערכים שלכם ל-.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_...            # מפתח בדיקה כאן; החליפו למפתח החי כשתעלו לאוויר
STRIPE_WEBHOOK_SECRET=whsec_...          # מתמלא בשלב הבנייה 4
NEXT_PUBLIC_SITE_URL=http://localhost:3000

קחו את STRIPE_SECRET_KEY מתוך Stripe → Developers → API keys. אנחנו משתמשים במפתח בדיקה (sk_test_…) כדי שהבנייה לא תעביר כסף אמיתי; החליפו למפתח החי כשהבאתם מוכנים לקבל תשלומים אמיתיים. הריצו bun dev ופתחו localhost:3000 — השלד מוצג.

צ'ק-אאוט מתארח מפנה את הדפדפן ל-URL של Stripe, לכן מפתח השרת הסודי הוא כל מה ש-Stripe צריך — אין מפתח פומבי, אין SDK קליינט של Stripe.

3. הגדרת רכיבי category, product_image ו-item

צרו שלושה Custom Components (Components → Create new component) — מקור הנתונים, לכן בלי תג UI Element. קבעו כל אחד ל-Active.

ל-CMS אין שדה "מערך תמונות", לכן הגלריה היא מערך הפניות לרכיב קטן product_image. צרו category ו-product_image (וקבעו אותם ל-Active) לפני item — שדה הפניה יכול לכוון רק לרכיבים פעילים.

  • category — name (טקסט), code (טקסט, Route Slug), description (טקסט עשיר), heroImage (תמונה)
  • product_image — image (תמונה)
  • item — name (טקסט), code (טקסט, Route Slug), description (טקסט עשיר), images (מערך הפניות → product_image), price (מספר, בסנטים — להצגה בלבד), currency (Select, usd), stripePriceId (טקסט), category (Reference → category), active (בוליאני)

השאירו את כל השדות כלא חובה. האדמין מתרגם את שמות השדות ל-snake_case תחתון ("Stripe Price Id" → stripe_price_id) — זה מה שהקוד שלכם ישתמש בו, לכן קראו את השמות האמיתיים בחזרה מ-generate-schemas בהמשך. אנו משתמשים ב-code (לא slug) כידית הניתוב: זהו Route Slug וגם המפתח לשליפת documents.getByCode נקייה מאוחר יותר.

רכיב item — code כ-Route Slug, images כהפניות ל-product_image, יחד עם stripePriceId והפניה ל-category.

4. משיכת הרכיבים לסוגים מקומיים

bun run generate-schemas

כותב סכמות Zod + טיפוסים ל-generated/cms-schemas.ts (categorySchema/Category, itemSchema/Item). משמש גם כבדיקת חיבור — פרטי התחברות שגויים ייכשלו כאן.

5. זריעת הקטלוג באמצעות Profound MCP

התקינו ואמתו את ה-MCP פעם אחת:

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

הריצו mcp__Profound__authenticate, השלימו את זרימת WorkOS, ואז בקשו מ-Claude:

צור קטלוג מסחר קטן לחנות בשם Edison's Inventions — שלוש קטגוריות והמוצרים האלה, עם description קצר ומדויק לתקופה לכל אחד, price בסנטים, 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 באותיות קטנות; לכל פריט דרושים name, אותו code באותיות קטנות, description, price (בסנטים), currency, ו-active. שמור את זה ב-data/catalog.json ואמת מול רכיבי category ו-item שלנו. לאחר מכן השתמש ב-Profound MCP כדי ליצור כל אחד כמסמך שפורסם: צור תחילה את הקטגוריות, קלט את המזהים שלהן, ואז צור את הפריטים עם category שהוגדר כהפניה — { "_type": "reference", "_ref": "<category-id>", "_schema": "category" }. השאר את stripePriceId ריק לעת עתה. בצע את יצירת הפריטים במקביל.

Claude כותב את data/catalog.json, מאמת אותו, ומפרס מקבילית של קריאות create_document (status: "published"). זריעו קטגוריות לפני פריטים כך שההפניות יצביעו על מזהים קיימים.

שלוש הקטגוריות שנזרעו, פורסמו וחיות.

שמונה המוצרים שנזרעו, כל אחד מקושר לקטגוריה.

6. יצירת מחירי Stripe וחיבור מספר מוצרים

הקטלוג נמצא ב-CMS; עכשיו תנו למספר מוצרים מחיר אמיתי ב-Stripe — העבודה של הסוחר, נעשית בשני ממשקי ניהול, ללא קוד:

  1. לוח הבקרה של Stripe → Products → + Add product, הגדירו מחיר חד-פעמי, העתיקו את מזהה המחיר (price_…).
  2. עשו זאת עבור כ-3 מוצרים מובילים (למשל Lightbulb, Phonograph, Kinetoscope).
  3. אדמין של Profound → item → Documents → הדביקו כל מזהה מחיר אל stripePriceId, שמרו.

ה-CMS מחזיק את הקטלוג; Stripe מחזיק את המחיר הרשמי; הקישור הוא מחרוזת אחת שהסוחר מדביק. (מעדיפים לאוטומט? Stripe MCP הרשמי יכול ליצור עבורכם את המוצרים/מחירים — הדביקו את המזהים שהוחזרו באותה צורה.)

7. הטמעת מערכת העיצוב וחיבורה עם AI

השלד מגיע ללא עיצוב. הציבו DESIGN.md (בלוק @theme של Tailwind v4 + טוקנים) בשורש הפרויקט — שלכם או כזה שהורדתם מ-refero.design. לאחר מכן בקשו מ-Claude, בתחום הסגנון בלבד:

קרא את קובץ העיצוב שהוספתי. הגדר Tailwind אם צריך, ואז חבר את התמה והגופנים כך שהסגנון יעבוד. השתמש ב-next/font עבור גופנים — אל תטען אותם מ-Google בזמן ריצה. רק הסגנון — אל תבנה עדיין עמודים או רכיבים.

ודאו של-src/app/globals.css יש @import "tailwindcss"; + בלוק @theme וש-localhost:3000 מציג את הטוקנים. שמרו על הנחיה ממוקדת (אם פתוחה מדי, סוכן יוצר עמוד בית שלם), וטענו גופנים דרך next/font, לעולם לא יבוא ריצה מגוגל.

8. הוספת תמונות מוצר (אופציונלי)

רשות — ניתן להגיע לצ'ק-אאוט עובד בלי תמונות. כדי להוסיף אותן: צרו מסמך product_image לכל תמונה (העלו אל שדה image שלו), ואז הפנו אליהם ממערך images של המוצר. הביאו צילום מוצר משלכם, או צרו סדרה קוהרנטית עם מודל תמונה (תנו ל-Claude להפיק הנחיות מותאמות-מותג מתוך DESIGN.md ונעלו --sref אחד של Midjourney כדי שכל צילום יתאים).

ל-cms-renderer העצמאי אין עזר לכתובת תמונה, לכן הוסיפו פונקציית buildAssetUrl ל-src/lib/image.ts (~40 שורות) — היא מוסיפה קידומת NEXT_PUBLIC_BUNNY_CDN_URL ומוסיפה את הסיומת. הרכיבים בשלב הבנייה 3 משתמשים בה.

חלק 2 — בנייה

בנו את שכבת ההצגה ואת הצ'ק-אאוט, וסיימו ברכישה אמיתית במצב בדיקה.

1. הגדרת חמשת רכיבי ה-UI Element

חמישה רכיבים, כל אחד Active ומתויג כ-UI Element (Settings → Tags), ללא Route Slug:

  • nav → brand · product_grid → heading · product_detail → heading · cart_summary → heading · footer → text (כולם טקסט)

תג UI Element הוא מה שמאפשר לרכיב להופיע ברשימת Add UI Element של Page Builder — Active לבדו לא מספיק. כל שדה הוא סקלרי (הסוג ש-CEL נקשר אליו); נתוני הקטלוג עצמם אינם שדה כאן — ProductGrid/ProductDetail משיגים אותם לפי פרמטר נתיב (שלב 3).

2. יצירת הטיפוסים מחדש

bun run generate-schemas

3. יצירת קורא הקטלוג, הרכיבים והעגלה ה-Headless

הנחיה אחת בונה את עזר הקריאה, חמשת הרכיבים, העגלה והרישום:

בנה את החנות שלנו בתוך src/, באמצעות SDK של Profound cms-renderer.

src/lib/catalog.ts — קורא CMS בצד השרת. צור קליינט עם 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 ניתן — שומר פריטים שה- category._ref שלהם שווה למזהה המסמך של הקטגוריה. ייצא resolveImages(refs) שמפתר כל הפניה ב- item.images דרך cms.documents.get.query({ websiteId, id: ref._ref }) והופך את שדה התמונה ל-URL עם buildAssetUrl שהוספנו (חלק 1 שלב 8).

src/components/ — חמישה רכיבי UI רשומים ברישום של הנתיב ה-catch-all לפי שם הרכיב, ב-snake_case המתאים לאדמין: { nav, product_grid, product_detail, cart_summary, footer }. Nav ו-Footer קוראים את השדה הסקלרי דרך ה-prop content (מטיפוס BlockComponentProps<T> מתוך cms-renderer/lib/types). ProductGrid ו-ProductDetail הם רכיבי שרת אסינכרוניים שקוראים את routeParams ומשיגים מתוך catalog.ts: routeParams.<param> הוא { value, … } — קראו את .value, כך ש-ProductGrid קורא listItems(routeParams.category_code?.value) (קלפים מקשרים ל-/products/{code}) ו-ProductDetail קורא getItemByCode(routeParams.item_code?.value) (גלריה דרך resolveImages, תיאור טקסט עשיר, מחיר, כפתור הוספה לעגלה). CartSummary מציג את העגלה מ-useCart עם כפתור תשלום. השאירו את formatPrice בקובץ טהור src/lib/format.ts כדי שרכיבי קליינט לא ייבאו את catalog.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 } במצב, נשמרים ב-localStorage, חושפים addItem/removeItem/updateQty/subtotal ו-checkout() ששולח POST עם { lines: [{ code, quantity }] } (קודים וכמויות בלבד — לעולם לא מחירים) ל- /api/stripe/checkout, ואז מפנה ל-url שהוחזר.

עצבו הכול עם מערכת העיצוב שלנו, כרכיבים משלנו — אל תעתיקו את פריסת אתר המקור.

שלושה דברים לדעת לאחר שזה רץ:

  • אלמנטים סקלריים מגיעים דרך content ({ content }: BlockComponentProps<T>) — פרקו שדות כ-props ברמת העל ובלוק לא יתציג. נתוני הקטלוג מגיעים מ-routeParams + שליפה מתוך catalog.ts לפי פרמטר, כי CEL לא נקשר לרשימות או גלריות. העגלה מחזיקה קודי פריטים בלבד, לעולם לא מחירים.
  • routeParams.<param> הוא { value, schemaName, document } — קראו .value. שליפות מחזירות את published_content, לא .content. מפתחות הרישום הם ב-snake_case כדי להתאים לאדמין.
  • עדכנו את @types/react/@types/react-dom ל-v19 — השלד מגיע עם v18, מה ששובר רכיבי שרת אסינכרוניים מול React 19.

4. כתיבת קוד השרת של Stripe (עמוד השדרה)

שלושה קבצי שרת קצרים — קוד התשלומים היחיד באפליקציה. הם עושים שימוש חוזר ב-getItemByCode, כך שהחיוב נפתר בצד השרת.

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"; // בצד השרת, מפתח ברמת קריאה

export async function POST(req: Request) {
  const { lines } = await req.json();                // [{ code, quantity }] — אין מחירים מהלקוח
  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, אף פעם לא מהלקוח
    })
  );
  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 });     // הלקוח מופנה לכאן
}

src/app/api/stripe/webhook/route.ts — אות מילוי מהימן:

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

export async function POST(req: Request) {
  const body = await req.text();                      // גוף RAW — נדרש לאימות החתימה
  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") {
    // מילוי: תעדו את ההזמנה / שלחו קבלה.
  }
  return new Response(null, { status: 200 });
}

תיקון חובה לשלד: src/proxy.ts של השלד מעביר כל /api/* ל-CMS, כך שנתיבי Stripe שלכם לא יפעלו. אפשרו להם לעבור קודם:

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: [...] }` של השלד ללא שינוי

ודאו: 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 מאמת את החתימה מול הגוף הגולמי.

5. יצירת הנתיבים

אדמין → Pages → Create page, שלוש פעמים. מיפו כל פרמטר לרכיב שלו (שדה slug code):

  1. /products/{item_code} → item
  2. /categories/{category_code} → category
  3. /cart — עמוד סטטי (הזינו את /cart כערך מילולי, לא /{cart})

6. הוספת רכיבי UI, חיבור CEL, פרסום

לכל נתיב: Page Builder → Add UI Element → Custom → הוסיפו רכיבים בסדר, מלאו שדות סקלריים (ערך סטטי או CEL), פרסמו.

  • /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 על קשירת מוצר.

מוקש Page Builder פרמטרי: בשני הנתיבים הפרמטריים, הוספת רכיבי UI אינה נשמרת (בלוקים יתייתמו והעמוד יוצג ריק). עד שזה יתוקן, חברו את block_ids של העמודים האלה ישירות דרך Profound MCP update_page, ואז פרסמו. (העמוד הסטטי /cart מתחבר כרגיל.) מאותה סיבה, ProductGrid מפיק את הכותרת מהקטגוריה שהוא משיג במקום דרך CEL.

7. הצגה ורכישה

  • /categories/lighting → הגריד. לחצו על מוצר → פירוט + הוספה לעגלה. /cart → תשלום.
  • התשלום מפנה ל-Stripe-hosted checkout. השתמשו בכרטיס בדיקה 4242 4242 4242 4242, כל תוקף עתידי/CVC. תחזרו ל-/cart?status=success, ו-stripe listen יציג checkout.session.completed.

עמוד מוצר מוצג — גלריה, מחיר וכפתור הוספה לעגלה.

העגלה — פריטי שורה וכפתור תשלום יחיד עם Stripe.

רק פריטים שמחירם הוגדר ניתנים לרכישה — קנו אחד משלושת הפריטים שהגדרתם בשלב 6.

אופציונלי — בינאום. תרגמו כל רכיב (כל 35 השפות בבת אחת), הוסיפו מקטע /{language}/… שממופה לרכיב המערכת המובנה language, והחליפו שדות הקשורים ב-CEL ל-documents.translated. ראו את מדריך ספר הטלפונים בשדה התעופה, חלק 2 שלב 7.

חלק 3 — פרודקשן

1. העלאה: GitHub ואז Vercel

git init && git add -A && git commit -m "Stripe storefront"
gh repo create store --private --source=. --push   # --public גם בסדר

פקודת Build: generated/cms-schemas.ts אינו נכלל ב-git, לכן הצמידו את הבילד כך שייווצר מחדש — הוסיפו vercel.json:

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

ב-Vercel: Add New → Project, ייבאו את store, והוסיפו את משתני הסביבה — PROFOUND_API_KEY, NEXT_PUBLIC_PROFOUND_WEBSITE_ID, NEXT_PUBLIC_CMS_API_URL, NEXT_PUBLIC_BUNNY_CDN_URL, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET (הערך של נקודה קצה בפרודקשן, להלן), ו- NEXT_PUBLIC_SITE_URL (כתובת הפרודקט שלכם). פרסו.

לאחר מכן חברו את ה-webhook המשוגר (סוד ה-stripe listen המקומי היה רק לוקאלי): Stripe → Developers → Webhooks → + Add endpoint → https://<prod>/api/stripe/webhook, אירוע checkout.session.completed. העתיקו את ה-whsec_… ל-Vercel ופרסו מחדש.

משתני סביבה חסרים = "עובד מקומית, ריק בפרודקשן" — מלכודת הפריסה מספר 1. אנחנו מפרסים עם מפתחות בדיקה כאן; החליפו STRIPE_SECRET_KEY וסוד ה-webhook לערכי חי כשאתם מוכנים לקבל תשלומים אמיתיים.

2. תצוגה מקדימה חיה ועריכה במקום

שניהם מגיעים עם השלד.

  • תצוגה מקדימה חיה: <Refresher> מעדכן את העמוד שאתם מציגים כשעורך שומר באדמין — בלי פריסה מחדש. (זו תצוגה מקדימה לעורך; מבקרים רואים תוכן שפורסם עם ריענון רגיל.)
  • עריכה במקום: הוסיפו ?edit_mode=true לכל URL לקבלת שכבות עריכה. מבקרים רגילים מקבלים את העמוד הנקי.

הוסיפו את נתיב התצוגה המקדימה שהשלד השמיט. האדמין טוען את iframe התצוגה המקדימה שלו ב- /cms-preview_<path>; ללא הנתיב הזה כל תצוגה מקדימה תחזיר 404. הוסיפו אותו:

// src/app/cms-preview_/[...slug]/page.tsx
import { ParametricRoutePreviewPage } from "cms-renderer/lib/renderer";
import { registry } from "../../registry";   // חלצו את הרישום למודול משותף
export default async function Page({ params, searchParams }) {
  const { slug } = await params;
  const PreviewPage = ParametricRoutePreviewPage as any; // רכיב שרת אסינכרוני; טיפוסי 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} />;
}

הוסיפו גם את src/app/cms-preview_/page.tsx (אותו הדבר, slug: []) עבור שורש הסגמנט.

זו הבנייה

חנות Stripe מונעת-תוכן: קטלוג ב-CMS, עמודי רשימה + פרטים מאותה קבוצת נתיבים, וצ'ק-אאוט מתארח שעובד. ה-AI זרעה את הקטלוג, חיברה את העיצוב וכתבה את קורא הקטלוג + הרכיבים + העגלה ה-Headless; אתם ביצעתם את הרכיבים, את קישורי המחיר ב-Stripe, את שלושת הנתיבים, ה-Chrome ב-CEL, ושלושה קבצי Stripe קצרים. CEL קושר את האלמנטים הסקלריים; הרכיבים משיגים את הקטלוג. ו-Stripe נשאר קטן — קריאה אחת ל-sessions.create ו-webhook חתום אחד, כשהלקוח משלם בדף של Stripe עצמו.

Continue Reading
Previous‹Deployments