← VercelCONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
changed
Update to Vercel
Snapshot Sep 30, 2026 · 23:18 UTC · version 0.21.4
Collection source: not recorded for this historical snapshot. These snapshots do not have a confirmed matching collection source. Differences in file lists alone do not establish changes to the package.
Supporting file metadata differs
Newly listed paths: agents/openai.yaml. This compares saved file lists, not package contents; a different collection source can change the list.
Observed in package metadata. These changes alone do not establish a new customer-facing feature.
Supporting files
Before
[]
After
[{"relative_path":"agents/openai.yaml","size_in_bytes":174}]
Compare saved observations
Download comparison JSONFull technical diff · 1 changed fields
changed /included_files
BEFORE
[]
AFTER
[
{
"relative_path": "agents/openai.yaml",
"size_in_bytes": 174
}
]Full snapshot data
{
"name": "payments",
"description": "Stripe payments integration guidance — native Vercel Marketplace setup, checkout sessions, webhook handling, subscription billing, and the Stripe SDK. Use when implementing payments, subscriptions, or processing transactions.",
"included_files": [
{
"relative_path": "agents/openai.yaml",
"size_in_bytes": 174
}
],
"skill_md_contents": "---\nname: payments\ndescription: Stripe payments integration guidance — native Vercel Marketplace setup, checkout sessions, webhook handling, subscription billing, and the Stripe SDK. Use when implementing payments, subscriptions, or processing transactions.\nmetadata:\n priority: 5\n docs:\n - \"https://docs.stripe.com\"\n - \"https://docs.stripe.com/payments/quickstart\"\n sitemap: \"https://docs.stripe.com/sitemap.xml\"\n pathPatterns:\n - 'app/api/webhook/stripe/**'\n - 'app/api/webhooks/stripe/**'\n - 'src/app/api/webhook/stripe/**'\n - 'src/app/api/webhooks/stripe/**'\n - 'pages/api/webhook/stripe.*'\n - 'pages/api/webhooks/stripe.*'\n - 'app/api/checkout/**'\n - 'src/app/api/checkout/**'\n - 'app/api/stripe/**'\n - 'src/app/api/stripe/**'\n - 'lib/stripe.*'\n - 'src/lib/stripe.*'\n - 'utils/stripe.*'\n - 'src/utils/stripe.*'\n bashPatterns:\n - '\\bnpm\\s+(install|i|add)\\s+[^\\n]*\\bstripe\\b'\n - '\\bpnpm\\s+(install|i|add)\\s+[^\\n]*\\bstripe\\b'\n - '\\bbun\\s+(install|i|add)\\s+[^\\n]*\\bstripe\\b'\n - '\\byarn\\s+add\\s+[^\\n]*\\bstripe\\b'\n - '\\bnpm\\s+(install|i|add)\\s+[^\\n]*@stripe/stripe-js\\b'\n - '\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@stripe/stripe-js\\b'\n - '\\bbun\\s+(install|i|add)\\s+[^\\n]*@stripe/stripe-js\\b'\n - '\\byarn\\s+add\\s+[^\\n]*@stripe/stripe-js\\b'\n - '\\bnpm\\s+(install|i|add)\\s+[^\\n]*@stripe/react-stripe-js\\b'\n - '\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@stripe/react-stripe-js\\b'\n - '\\bbun\\s+(install|i|add)\\s+[^\\n]*@stripe/react-stripe-js\\b'\n - '\\byarn\\s+add\\s+[^\\n]*@stripe/react-stripe-js\\b'\n---\n\n# Stripe Payments Integration\n\nYou are an expert in Stripe payments for Vercel-deployed applications — covering the native Vercel Marketplace integration, Checkout Sessions, webhook handling, subscription billing, and the Stripe Node.js SDK.\n\n## Vercel Marketplace Setup (Recommended)\n\nStripe is a native Vercel Marketplace integration with sandbox provisioning and unified billing.\n\n### Install via Marketplace\n\n```bash\n# Install Stripe from Vercel Marketplace (auto-provisions sandbox + env vars)\nvercel integration add stripe\n```\n\nAuto-provisioned environment variables:\n- `STRIPE_SECRET_KEY` — server-side API key\n- `STRIPE_PUBLISHABLE_KEY` — client-side publishable key\n- `STRIPE_WEBHOOK_SECRET` — webhook endpoint signing secret\n\n### SDK Setup\n\n```bash\n# Server-side SDK\nnpm install stripe\n\n# Client-side SDK (for Stripe Elements / Checkout)\nnpm install @stripe/stripe-js @stripe/react-stripe-js\n```\n\n### Initialize the Stripe Client\n\n```ts\n// lib/stripe.ts\nimport Stripe from \"stripe\";\n\nexport const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {\n apiVersion: \"2026-02-25.clover\",\n typescript: true,\n});\n```\n\n## Checkout Sessions\n\n### Server Action (Recommended for 2026)\n\nServer Actions are the preferred pattern for creating Checkout Sessions in Next.js 15+, eliminating the need for API routes:\n\n```ts\n// app/actions/checkout.ts\n\"use server\";\nimport { redirect } from \"next/navigation\";\nimport { stripe } from \"@/lib/stripe\";\n\nexport async function createCheckoutSession(priceId: string) {\n const session = await stripe.checkout.sessions.create({\n mode: \"payment\",\n line_items: [{ price: priceId, quantity: 1 }],\n success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,\n cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/cancel`,\n });\n\n redirect(session.url!);\n}\n```\n\n```tsx\n// app/pricing/page.tsx\nimport { createCheckoutSession } from \"@/app/actions/checkout\";\n\nexport default function PricingPage() {\n return (\n <form action={createCheckoutSession.bind(null, \"price_xxx\")}>\n <button type=\"submit\">Buy Now</button>\n </form>\n );\n}\n```\n\n### Create a Checkout Session (API Route)\n\n```ts\n// app/api/checkout/route.ts\nimport { NextResponse } from \"next/server\";\nimport { stripe } from \"@/lib/stripe\";\n\nexport async function POST(req: Request) {\n const { priceId } = await req.json();\n\n const session = await stripe.checkout.sessions.create({\n mode: \"payment\", // or \"subscription\" for recurring\n payment_method_types: [\"card\"],\n line_items: [{ price: priceId, quantity: 1 }],\n success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,\n cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/cancel`,\n });\n\n return NextResponse.json({ url: session.url });\n}\n```\n\n### Redirect to Checkout (Client)\n\n```tsx\n\"use client\";\nimport { loadStripe } from \"@stripe/stripe-js\";\n\nconst stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);\n\nexport function CheckoutButton({ priceId }: { priceId: string }) {\n const handleCheckout = async () => {\n const res = await fetch(\"/api/checkout\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ priceId }),\n });\n const { url } = await res.json();\n window.location.href = url;\n };\n\n return <button onClick={handleCheckout}>Subscribe</button>;\n}\n```\n\n## Webhook Handling\n\nStripe sends events to your webhook endpoint for asynchronous payment processing. Always verify the signature.\n\n```ts\n// app/api/webhook/stripe/route.ts\nimport { NextResponse } from \"next/server\";\nimport { stripe } from \"@/lib/stripe\";\nimport Stripe from \"stripe\";\n\nexport async function POST(req: Request) {\n const body = await req.text();\n const signature = req.headers.get(\"stripe-signature\")!;\n\n let event: Stripe.Event;\n try {\n event = stripe.webhooks.constructEvent(\n body,\n signature,\n process.env.STRIPE_WEBHOOK_SECRET!\n );\n } catch (err) {\n return NextResponse.json({ error: \"Invalid signature\" }, { status: 400 });\n }\n\n switch (event.type) {\n case \"checkout.session.completed\": {\n const session = event.data.object as Stripe.Checkout.Session;\n // Fulfill the order — update database, send confirmation, etc.\n break;\n }\n case \"invoice.payment_succeeded\": {\n const invoice = event.data.object as Stripe.Invoice;\n // Handle successful subscription renewal\n break;\n }\n case \"customer.subscription.deleted\": {\n const subscription = event.data.object as Stripe.Subscription;\n // Handle cancellation — revoke access\n break;\n }\n }\n\n return NextResponse.json({ received: true });\n}\n```\n\n**Important**: Webhook routes must read the raw body as text (not JSON) for signature verification. Do not add `bodyParser` or JSON middleware to webhook routes.\n\n## Subscription Billing\n\n### Create a Subscription Checkout\n\n```ts\nconst session = await stripe.checkout.sessions.create({\n mode: \"subscription\",\n line_items: [{ price: priceId, quantity: 1 }],\n success_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard`,\n cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,\n});\n```\n\n### Customer Portal\n\nAllow customers to manage their subscriptions:\n\n```ts\n// app/api/portal/route.ts\nimport { stripe } from \"@/lib/stripe\";\n\nexport async function POST(req: Request) {\n const { customerId } = await req.json();\n\n const session = await stripe.billingPortal.sessions.create({\n customer: customerId,\n return_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard`,\n });\n\n return Response.json({ url: session.url });\n}\n```\n\n## Embedded Checkout (Recommended)\n\nStripe's Embedded Checkout renders inside your page via an iframe, keeping users on your domain while offloading PCI compliance to Stripe:\n\n```ts\n// app/actions/embedded-checkout.ts\n\"use server\";\nimport { stripe } from \"@/lib/stripe\";\n\nexport async function createEmbeddedCheckout(priceId: string) {\n const session = await stripe.checkout.sessions.create({\n mode: \"payment\",\n line_items: [{ price: priceId, quantity: 1 }],\n ui_mode: \"embedded\",\n return_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,\n });\n\n return { clientSecret: session.client_secret! };\n}\n```\n\n```tsx\n\"use client\";\nimport { loadStripe } from \"@stripe/stripe-js\";\nimport { EmbeddedCheckoutProvider, EmbeddedCheckout } from \"@stripe/react-stripe-js\";\nimport { createEmbeddedCheckout } from \"@/app/actions/embedded-checkout\";\n\nconst stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);\n\nexport function CheckoutEmbed({ priceId }: { priceId: string }) {\n return (\n <EmbeddedCheckoutProvider\n stripe={stripePromise}\n options={{ fetchClientSecret: () => createEmbeddedCheckout(priceId).then(r => r.clientSecret) }}\n >\n <EmbeddedCheckout />\n </EmbeddedCheckoutProvider>\n );\n}\n```\n\n## Stripe Elements (Custom Forms)\n\n```tsx\n\"use client\";\nimport { Elements, PaymentElement, useStripe, useElements } from \"@stripe/react-stripe-js\";\nimport { loadStripe } from \"@stripe/stripe-js\";\n\nconst stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);\n\nfunction CheckoutForm() {\n const stripe = useStripe();\n const elements = useElements();\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!stripe || !elements) return;\n\n const { error } = await stripe.confirmPayment({\n elements,\n confirmParams: { return_url: `${window.location.origin}/success` },\n });\n\n if (error) console.error(error.message);\n };\n\n return (\n <form onSubmit={handleSubmit}>\n <PaymentElement />\n <button type=\"submit\" disabled={!stripe}>Pay</button>\n </form>\n );\n}\n\nexport function PaymentForm({ clientSecret }: { clientSecret: string }) {\n return (\n <Elements stripe={stripePromise} options={{ clientSecret }}>\n <CheckoutForm />\n </Elements>\n );\n}\n```\n\n## Environment Variables\n\n| Variable | Scope | Description |\n|----------|-------|-------------|\n| `STRIPE_SECRET_KEY` | Server | API secret key (starts with `sk_`) |\n| `STRIPE_PUBLISHABLE_KEY` | Client | Publishable key (starts with `pk_`) |\n| `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` | Client | Alias exposed to browser via Next.js |\n| `STRIPE_WEBHOOK_SECRET` | Server | Webhook signing secret (starts with `whsec_`) |\n\n## Cross-References\n\n- **Marketplace install and env var provisioning** → `⤳ skill: marketplace`\n- **Webhook route patterns** → `⤳ skill: routing-middleware`\n- **Environment variable management** → `⤳ skill: env-vars`\n- **Serverless function config** → `⤳ skill: vercel-functions`\n\n## Official Documentation\n\n- [Stripe + Vercel Marketplace](https://vercel.com/marketplace/stripe)\n- [Stripe Node.js SDK](https://docs.stripe.com/sdks)\n- [Stripe Checkout](https://docs.stripe.com/payments/checkout)\n- [Stripe Webhooks](https://docs.stripe.com/webhooks)\n- [Stripe Elements](https://docs.stripe.com/payments/elements)\n"
}SHA-256: 6609d35499bf7713d2bc34ac08fbd8840a9e3d5f5fae7763fff911983d0b8e75