एक व्यावहारिक मार्गदर्शिका: Profound CMS पर एक कंटेंट-प्रधान Stripe स्टोरफ्रंट बनाएं — ऐसा कैटलॉग जिसे व्यापारी बिना कोड के संपादित करे, दो पैरामीट्रिक रूट, हेडलेस कार्ट, और Stripe-होस्टेड चेकआउट।
पूर्ण हुआ स्टोर चलते हुए — एक श्रेणी ब्राउज़ करें, उत्पाद खोलें, कार्ट में जोड़ें, चेकआउट करें।
एक व्यावहारिक मार्गदर्शिका जो Profound CMS पर एक कंटेंट-प्रधान स्टोर बनाती है: उत्पाद कैटलॉग (श्रेणियाँ + आइटम) जिसे CMS में मॉडल किया गया है, एक ही सेट की रूटों से सूची और विवरण पेज, और Stripe-होस्टेड चेकआउट जिसे हेडलेस घटक के रूप में भेजा गया है।
रीढ़ है हाथ से लिखी Next.js और Profound एडमिन। Claude Code (Profound MCP के माध्यम से) तीन कामों पर भारी उठान करता है — कैटलॉग को सीड करना, डिज़ाइन सिस्टम को जोड़ना, और स्टोरफ्रंट घटकों को लिखना (हेडलेस कार्ट सहित)। तीन भाग: सेटअप, बिल्ड, प्रोडक्शन।
एक लाइन में पेमेंट। हम Stripe-होस्टेड Checkout का उपयोग करते हैं: खरीदार भुगतान Stripe के पेज पर करता है, आपके नहीं। आपका ऐप सिर्फ दो सर्वर-साइड काम करता है — एक Checkout Session बनाना और एक webhook सत्यापित करना। कोई कार्ड फ़ील्ड नहीं, कोई Stripe Elements नहीं, कोई PCI भार नहीं।
/products/{item_code}, /categories/{category_code}) और एक स्थिर /cart, सब एक ही सेट के घटकों से।useCart) और Stripe-होस्टेड चेकआउट, जिसमें मूल्य हमेशा सर्वर-साइड Stripe Price ID से तय होता है।curl -fsSL https://bun.sh/install | bashstripe login) स्थानीय वेबहुक के लिए।gh) और GitHub से जुड़ा एक Vercel खाता।Profound कंटेंट को रेंडरिंग से अलग करता है:
category, item); एक टैग किए गए UI Element को पेज पर रखा जा सकता है (nav, product_grid, …)।meta.params.* CEL में, routeParams React में) पास करते हुए।cms-renderer के माध्यम से पढ़ता है; Stripe को साधारण API रूट के रूप में जोड़ा जाता है।बिल्ड को आकार देने वाला एक नियम: CEL सिर्फ string/number फ़ील्ड्स को बाँधता है। इसलिए स्केलर क्रोम (नेव ब्रांड, फुटर, हेडिंग्स) CEL से बाउंड होती है, जबकि अत्यधिक या संग्रहवादी चीज़ें (उत्पाद ग्रिड, इमेज गैलरी, रिच टेक्स्ट) को React कंपोनेंट के अंदर रूट पैराम द्वारा फ़ेच किया जाता है। और Stripe मूल्य निर्धारण का सत्य का स्रोत है — CMS का price केवल प्रदर्शित करने के लिए है; शुल्क हमेशा सर्वर-साइड Stripe Price ID से तय होता है।
अंतिम स्थिति: एक छोटा प्रकाशित कैटलॉग, ऐप इसे पढ़ने के लिए जुड़ा हुआ, Stripe स्थापित, डिज़ाइन तैयार — अभी कुछ रेंडर नहीं हुआ है।
Profound पर साइन अप करें (WorkOS प्रमाणीकरण)। store नामक एक वेबसाइट बनाएँ, फिर उसका website ID (एडमिन URL में UUID) और एक रीड-स्तर का API की (Deployments → Create API key) कॉपी करें। ऐप केवल पढ़ता है; बाद में कैटलॉग सीड MCP से गुजरता है, जो अलग से प्रमाणित करता है।
bunx create-profound-next store
cd store
bun add stripe
स्कैफ़ोल्ड एक Next.js App Router प्रोजेक्ट है जो Profound के लिए पहले से वायर्ड है (cms-renderer SDK, एक कैच-ऑल रूट, 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 खोलें — स्टार्टर रेंडर होता है।
होस्टेड चेकआउट ब्राउज़र को Stripe URL पर रीडायरेक्ट करता है, इसलिए सर्वर सीक्रेट कुंजी ही Stripe को चाहिए — कोई publishable key नहीं, कोई client Stripe SDK नहीं।
category, product_image, और item कंपोनेंट्स परिभाषित करेंतीन Custom Components बनाएँ (Components → Create new component) — ये डेटा स्रोत हैं, इसलिए कोई UI Element टैग नहीं। प्रत्येक को Active सेट करें।
CMS में "array of image" फ़ील्ड नहीं है, इसलिए गैलरी संकेतों के array के रूप में होती है जो छोटे product_image कंपोनेंट को संदर्भित करती है। item से पहले category और product_image (और उन्हें Active करें) बनाएँ — संदर्भ फ़ील्ड केवल Active कंपोनेंट्स को लक्ष्य बना सकता है।
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 → product_image), price (Number, सेंट्स में — केवल प्रदर्शन), currency (Select, usd), stripePriceId (Text), category (Reference → category), active (Boolean)सभी फ़ील्ड वैकल्पिक छोड़ें। एडमिन फ़ील्ड नामों को लोअर-स्नेक-केस करता है ("Stripe Price Id" → stripe_price_id) — आपका कोड इन्हीं नामों पर निर्भर करता है, इसलिए उन्हें generate-schemas से वापस पढ़ें। हम रूट योग्य हैंडल को code नाम देते हैं (ना कि slug): यह Route Slug भी है और बाद में साफ़ documents.getByCode लुकअप के लिए कुंजी भी।
item कंपोनेंट — Route Slug के रूप में code, product_image संदर्भों के रूप में images, साथ ही stripePriceId और category संदर्भ।
bun run generate-schemas
generated/cms-schemas.ts में Zod स्कीमा + टाइप्स लिखता है (categorySchema/Category, itemSchema/Item)। यह कनेक्शन जाँच के रूप में भी काम करता है — गलत क्रेडेंशियल्स यहाँ विफल होंगे।
एक बार 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 का उपयोग करके प्रत्येक को प्रकाशित डॉक्यूमेंट के रूप में बनाएँ: पहले श्रेणियाँ बनाएँ, उनके IDs कैप्चर करें, फिर आइटम बनाएँ जिनमेंcategoryको संदर्भ सेट करें —{ "_type": "reference", "_ref": "<category-id>", "_schema": "category" }।stripePriceIdअभी खाली छोड़ें। आइटम्स को समानांतर में करें।
Claude data/catalog.json लिखता है, उसे सत्यापित करता है, और समानांतर create_document कॉल्स (status: "published") चलाता है। संदर्भ सही IDs की ओर जाएँ इसलिए आइटम से पहले श्रेणियाँ सीड करें।
तीन सीड की गई श्रेणियाँ, प्रकाशित और लाइव।
आठ सीड किए गए उत्पाद, प्रत्येक एक श्रेणी से जुड़े हुए।
कैटलॉग CMS में है; अब कुछ उत्पादों को वास्तविक Stripe मूल्य दें — व्यापारी का काम, दो एडमिन पैनल में, कोई कोड नहीं:
price_…) कॉपी करें।stripePriceId में पेस्ट करें, सेव करें।CMS कैटलॉग को रखता है; Stripe रिकॉर्ड का मूल्य रखता है; लिंक वह एक स्ट्रिंग है जिसे व्यापारी पेस्ट करता है। (इसे स्वचालित करना पसंद है? आधिकारिक Stripe MCP आपके लिए Products/Prices बना सकता है — लौटे हुए IDs को उसी तरह पेस्ट करें।)
स्कैफ़ोल्ड बिना स्टाइलिंग के आता है। प्रोजेक्ट रूट पर एक DESIGN.md रखें (Tailwind v4 का @theme ब्लॉक + टोकन) — अपना खुद का, या refero.design से डाउनलोड करें। फिर Claude को, केवल स्टाइलिंग तक सीमित रखते हुए, प्रॉम्प्ट करें:
अभी जो डिज़ाइन फ़ाइल मैंने जोड़ी है उसे पढ़ो। Tailwind की आवश्यकता हो तो सेटअप करो, फिर थीम और फॉन्ट्स वायर करो ताकि स्टाइलिंग काम करे। फॉन्ट्स के लिए
next/fontका उपयोग करो — रनटाइम में Google से उन्हें लोड मत करो। सिर्फ स्टाइलिंग करो — अभी कोई पेज या कंपोनेंट मत बनाओ।
पुष्टि करें कि src/app/globals.css में @import "tailwindcss"; + @theme ब्लॉक है और localhost:3000 पर टोकन दिख रहे हैं। प्रॉम्प्ट को सटीक रखें (खुला छोड़ें तो एजेंट पूरा होमपेज स्कैफ़ोल्ड कर देता है), और फॉन्ट्स को next/font से लोड करें, कभी रनटाइम Google इम्पोर्ट से नहीं।
वैकल्पिक — आप छवियों के बिना भी कार्यशील चेकआउट तक पहुँच सकते हैं। उन्हें जोड़ने के लिए: प्रत्येक छवि के लिए एक product_image डॉक्यूमेंट बनाएँ (उसके image फ़ील्ड में अपलोड करें), फिर उसे उत्पाद के images array से संदर्भित करें। अपनी खुद की उत्पाद तस्वीरें लाएँ, या किसी इमेज मॉडल से एक समरूप सेट जनरेट करें (Claude से DESIGN.md के आधार पर ब्रांड-उपयुक्त प्रॉम्प्ट तैयार करवाएँ और हर शॉट मैच करने के लिए एक Midjourney --sref लॉक करें)।
स्टैंडअलोन
cms-rendererके पास कोई इमेज-URL हेल्पर नहीं है, इसलिएsrc/lib/image.tsमेंbuildAssetUrlजोड़ें (~40 लाइन) — यहNEXT_PUBLIC_BUNNY_CDN_URLको प्रीफ़िक्स करता है और एक्सटेंशन जोड़ता है। बिल्ड स्टेप 3 के कंपोनेंट्स इसे उपयोग करते हैं।
रेंडरिंग लेयर और चेकआउट बनाएँ, अंत में एक वास्तविक टेस्ट-मोड खरीद के साथ।
पाँच कंपोनेंट्स, प्रत्येक Active और UI Element (Settings → Tags) से टैग्ड, कोई Route Slug नहीं:
nav → brand · product_grid → heading · product_detail → heading · cart_summary → heading · footer → text (सब Text)UI Element टैग ही वह है जो किसी कंपोनेंट को पेज बिल्डर की Add UI Element सूची में दिखाता है — Active होना अकेला काफी नहीं। प्रत्येक फ़ील्ड स्केलर है (जिसे CEL बाँधता है); वास्तविक कैटलॉग डेटा यहाँ फ़ील्ड नहीं है — ProductGrid/ProductDetail रूट पैराम से फ़ेच करते हैं (स्टेप 3)।
bun run generate-schemas
एक प्रॉम्प्ट रीड हेल्पर, पाँच कंपोनेंट्स, कार्ट, और रजिस्ट्री बनाता है:
हमारा स्टोरफ्रंट
src/में बनाओ, Profoundcms-rendererSDK का उपयोग करते हुए।
src/lib/catalog.ts— सर्वर-साइड CMS रीडर।cms-renderer/lib/cms-apiसेgetCmsClient({ cmsUrl: process.env.NEXT_PUBLIC_CMS_API_URL!, apiKey: process.env.PROFOUND_API_KEY, websiteId: process.env.NEXT_PUBLIC_PROFOUND_WEBSITE_ID! })से क्लाइंट बनाओ।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संबंधित श्रेणी केdocument.idके बराबर हो।resolveImages(refs)एक्सपोर्ट करो जो प्रत्येकitem.imagesसंदर्भ कोcms.documents.get.query({ websiteId, id: ref._ref })से रिज़ॉल्व करता है और उसके इमेज फ़ील्ड को vendoredbuildAssetUrl(भाग 1 स्टेप 8) से URL में बदलता है।
src/components/— पाँच UI एलिमेंट कंपोनेंट्स जिन्हें कैच-ऑल रूट की रजिस्ट्री में कंपोनेंट नाम के अनुसार पंजीकृत किया गया है, एडमिन से मेल खाने के लिए snake_case में:{ nav, product_grid, product_detail, cart_summary, footer }।NavऔरFooterअपने स्केलर फ़ील्डcontentप्रॉप से पढ़ते हैं (cms-renderer/lib/typesसेBlockComponentProps<T>टाइप्ड)।ProductGridऔरProductDetailasync सर्वर कंपोनेंट्स हैं जोrouteParamsपढ़ते हैं औरcatalog.tsसे फ़ेच करते हैं:routeParams.<param>{ value, … }होता है —.valueपढ़ें, इसलिएProductGridlistItems(routeParams.category_code?.value)कॉल करता है (कार्ड/products/{code}से लिंक करें) औरProductDetailgetItemByCode(routeParams.item_code?.value)कॉल करता है (गैलरीresolveImagesसे, रिच-टेक्स्ट वर्णन, मूल्य, Add-to-cart)।CartSummaryकार्ट कोuseCartसे रेंडर करता है और Pay बटन के साथ।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— हेडलेस कार्ट: लाइन आइटम{ code, name, priceLabel, quantity }को स्टेट में रखता है,localStorageमें स्थायी बनाता है,addItem/removeItem/updateQty/subtotalऔरcheckout()एक्सपोज़ करता है जो{ lines: [{ code, quantity }] }(केवल कोड और मात्रा — कभी भी कीमतें नहीं) को/api/stripe/checkoutपर POST करता है, फिर लौटी हुईurlपर रीडायरेक्ट करता है।सब कुछ हमारे डिज़ाइन सिस्टम से स्टाइल करें, अपने खुद के कंपोनेंट्स के रूप में — स्रोत साइट का लेआउट कॉपी न करें।
यह चलने के बाद जानने योग्य तीन बातें:
content पर आता है ({ content }: BlockComponentProps<T>) — फ़ील्ड्स को शीर्ष-स्तरीय प्रॉप्स की तरह डीस्ट्रक्चर करें और ब्लॉक खाली रेंडर होगा। कैटलॉग डेटा routeParams + catalog.ts फ़ेच से आता है, क्योंकि CEL सूचियों या गैलरियों को बाँध नहीं सकता। कार्ट आइटमों के कोड ले जाता है, कीमतें कभी नहीं।routeParams.<param> { value, schemaName, document } होता है — .value पढ़ें। रीड्स published_content लौटाते हैं, .content नहीं। रजिस्ट्री की कुंजियाँ एडमिन से मेल खाने के लिए snake_case में हैं।@types/react/@types/react-dom को v19 पर बढ़ाएँ — स्कैफ़ोल्ड v18 शिप करता है, जो React 19 के async सर्वर-कंपोनेंट ब्लॉकों में टूटता है।तीन छोटे सर्वर फ़ाइलें — ऐप में एकमात्र भुगतान कोड। ये 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") {
// fulfill: ऑर्डर रिकॉर्ड करें / रसीद भेजें।
}
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 xBad signatureलौटाता है।
Stripe CLI को स्थानीय वेबहुक के लिए चलाएँ:
stripe login
stripe listen --forward-to localhost:3000/api/stripe/webhook
# whsec_... कॉपी करें और STRIPE_WEBHOOK_SECRET में डालें, bun dev रीस्टार्ट करें
whsec_… प्रति सत्र है। सुरक्षा के दो नियम हैं: चेकआउट कार्ट से code लेकर मूल्य दोबारा CMS से निकालता है (छेड़छाड़ किया कार्ट इसे बदल नहीं सकता), और वेबहुक कच्ची बॉडी के विरुद्ध हस्ताक्षर सत्यापित करता है।
एडमिन → Pages → Create page, तीन बार। प्रत्येक पैराम को उसके कंपोनेंट से मैप करें (slug फ़ील्ड code):
/products/{item_code} → item/categories/{category_code} → category/cart — एक स्थिर पेज (शाब्दिक /cart दर्ज करें, /{cart} नहीं)प्रत्येक रूट के लिए: Page Builder → Add UI Element → Custom → कंपोनेंट क्रम से जोड़ें, स्केलर फ़ील्ड भरें (स्थिर मान या CEL), Publish करें।
/products/{item_code}: nav, product_detail, footer/categories/{category_code}: nav, product_grid, footer/cart: nav, cart_summary, footernav.brand और footer.text को स्थिर स्ट्रिंग सेट करें; हेडिंग्स को स्थिर लेबल दें।
श्रेणी रूट पर पेज बिल्डर — product_grid एलिमेंट चुना हुआ, उसका heading CEL से बाउंड।
उत्पाद रूट पर पेज बिल्डर — product_detail एलिमेंट एक उत्पाद बाइंडिंग पर।
पैरामीट्रिक पेज बिल्डर सावधानी: दो पैरामीट्रिक रूट पर UI एलिमेंट जोड़ना स्थायी नहीं होता (ब्लॉक्स अलग हो जाते हैं और पेज खाली रेंडर होता है)। जब तक यह ठीक नहीं होता, उन पेजों के
block_idsको सीधे Profound MCPupdate_pageसे वायर करें, फिर प्रकाशित करें। (स्थिर/cartसामान्य रूप से जुड़ता है।) इसी कारणProductGridअपना heading उस श्रेणी से निकालता है जिसे वह फ़ेच करता है, CEL से नहीं।
/categories/lighting → ग्रिड। एक उत्पाद पर क्लिक करें → विवरण + Add to cart। /cart → Pay।/cart?status=success पर लौटते हैं, और stripe listen checkout.session.completed दिखाता है।रेंडर किया गया उत्पाद पेज — गैलरी, मूल्य, और Add to cart।
कार्ट — लाइन आइटम और एक Pay-with-Stripe बटन।
केवल कीमत वाले आइटम खरीदे जा सकते हैं — स्टेप 6 में जिन ~3 को आपने कीमत दी थी उनमें से एक खरीदें।
वैकल्पिक — अंतरराष्ट्रीयकरण। प्रत्येक कंपोनेंट का अनुवाद करें (एक साथ सभी 35 भाषाएँ),
/{language}/…सेगमेंट जोड़ें जिसे बिल्ट-इनlanguageसिस्टम कंपोनेंट से मैप करें, और CEL-बाउंड फ़ील्ड्स कोdocuments.translatedपर स्विच करें। विवरण के लिए एयरपोर्ट डायरेक्टरी ट्यूटोरियल, भाग 2 स्टेप 7 देखें।
git init && git add -A && git commit -m "Stripe storefront"
gh repo create store --private --source=. --push # --public भी ठीक है
बिल्ड कमांड:
generated/cms-schemas.tsgitignored है, इसलिए बिल्ड को इसे फिर से जनरेट करने पर पिन करें —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 (डिप्लॉय किए गए एंडपॉइंट का मान, नीचे), और NEXT_PUBLIC_SITE_URL (आपका प्रोड URL)। डिप्लॉय करें।
फिर डिप्लॉय किए गए वेबहुक को वायर करें (स्थानीय stripe listen सीक्रेट केवल स्थानीय था): Stripe → Developers → Webhooks → + Add endpoint → https://<prod>/api/stripe/webhook, इवेंट checkout.session.completed। उसका whsec_… Vercel में कॉपी करें और पुनः डिप्लॉय करें।
गुम env vars = "लोकली काम करता है, प्रोड में खाली" — सबसे बड़ी डिप्लॉय समस्या। यहाँ हम टेस्ट कुंजियों से डिप्लॉय करते हैं; वास्तविक भुगतान लेने के लिए तैयार होने पर STRIPE_SECRET_KEY और वेबहुक सीक्रेट को अपनी लाइव मानों में स्विच करें।
दोनों स्कैफ़ोल्ड के साथ आते हैं।
<Refresher> उस पेज को अपडेट करता है जिसे एडिटर प्रीव्यू कर रहा है जब वह एडमिन में सेव करता है — कोई रीडिप्लॉय नहीं। (यह एडिटर के लिए प्रीव्यू है; आगंतुक प्रकाशित कंटेंट को सामान्य रिवैलिडेशन पर देखते हैं।)?edit_mode=true जोड़ें ताकि एडिट ओवरले दिखें। सार्वजनिक आगंतुक साफ़ पेज देखते हैं।वह प्रीव्यू रूट जोड़ें जो स्कैफ़ोल्ड छोड़ देता है। एडमिन अपना प्रीव्यू 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; // async RSC; 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 ने कैटलॉग सीड किया, डिज़ाइन वायर किया, और कैटलॉग रीडर + कंपोनेंट्स + हेडलेस कार्ट लिखा; आपने कंपोनेंट्स, Stripe मूल्य लिंक, तीन रूट, CEL क्रोम, और तीन छोटे Stripe फ़ाइलें कीं। CEL क्रोम बाँधता है; कंपोनेंट्स कैटलॉग फ़ेच करते हैं। और Stripe छोटा रहा — एक sessions.create कॉल और एक साइन किया हुआ वेबहुक, जिसमें खरीदार Stripe के अपने पेज पर भुगतान करता है।