with one click
payments
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.
Menu
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.
Submit compact NCBI Entrez E-Utilities requests for PubMed, Gene, Protein, Nucleotide, PMC metadata, and GEO metadata workflows. Use when a user wants concise Entrez search, fetch, summary, or link results; save raw JSON or XML only on request.
Use BrightHire tools when a user asks about BrightHire interview intelligence, calls, candidates, roles, scorecards, transcripts, hiring decisions, or organization-level interview data.
Expert coding assistant for Catalyst by Zoho — full-stack serverless cloud platform. Trigger on any mention of Catalyst, zcatalyst, AppSail, Data Store, ZCQL, Cache, Stratus, Circuits, SmartBrowz, ConvoKraft, Slate, Signals, Pipelines, QuickML, NoSQL, Job Scheduling, Zia Services, CodeLib, API Gateway, Connections, Zoho MCP, CatalystbyZoho, catalyst init/deploy/serve, zcatalyst-sdk-node, or catalyst-config.json. Covers all 7 function types, full service catalog, architectural guidance, and Zoho MCP tool-based resource management. Also trigger on migration/comparison with AWS Lambda, S3, DynamoDB, Vercel, Netlify, Supabase, Firebase, Heroku, Cloud Run, Cloudflare R2, Railway. Trigger on Catalyst pricing, cost estimation, or "create tables for me", "set up the database", "deploy to Catalyst", "build on Zoho's platform", or "is Catalyst like Firebase". Do NOT use for generic Zoho CRM questions unless Catalyst is the target.
Google Slides work for finding, reading, summarizing, creating, importing, template following, visual cleanup, source-deck adaptation, structural repair, and content edits in native Slides decks.
Manage Gmail inbox triage, mailbox search, thread summaries, action extraction, reply drafting, and email forwarding through connected Gmail data. Use when the user wants to inspect a mailbox or thread, search email with Gmail query syntax, summarize messages, extract decisions and follow-ups, prepare replies or forwarded messages, or organize messages with explicit confirmation before send, archive, delete, or label actions.
Use when the user mentions MagicPath, designs, UI components, themes, canvas selections, or repo-to-canvas UI work; run magicpath-ai to search, inspect, install, or author components.
| 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. |
| metadata | {"priority":5,"docs":["https://docs.stripe.com","https://docs.stripe.com/payments/quickstart"],"sitemap":"https://docs.stripe.com/sitemap.xml","pathPatterns":["app/api/webhook/stripe/**","app/api/webhooks/stripe/**","src/app/api/webhook/stripe/**","src/app/api/webhooks/stripe/**","pages/api/webhook/stripe.*","pages/api/webhooks/stripe.*","app/api/checkout/**","src/app/api/checkout/**","app/api/stripe/**","src/app/api/stripe/**","lib/stripe.*","src/lib/stripe.*","utils/stripe.*","src/utils/stripe.*"],"bashPatterns":["\\bnpm\\s+(install|i|add)\\s+[^\\n]*\\bstripe\\b","\\bpnpm\\s+(install|i|add)\\s+[^\\n]*\\bstripe\\b","\\bbun\\s+(install|i|add)\\s+[^\\n]*\\bstripe\\b","\\byarn\\s+add\\s+[^\\n]*\\bstripe\\b","\\bnpm\\s+(install|i|add)\\s+[^\\n]*@stripe/stripe-js\\b","\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@stripe/stripe-js\\b","\\bbun\\s+(install|i|add)\\s+[^\\n]*@stripe/stripe-js\\b","\\byarn\\s+add\\s+[^\\n]*@stripe/stripe-js\\b","\\bnpm\\s+(install|i|add)\\s+[^\\n]*@stripe/react-stripe-js\\b","\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@stripe/react-stripe-js\\b","\\bbun\\s+(install|i|add)\\s+[^\\n]*@stripe/react-stripe-js\\b","\\byarn\\s+add\\s+[^\\n]*@stripe/react-stripe-js\\b"]} |
You 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.
Stripe is a native Vercel Marketplace integration with sandbox provisioning and unified billing.
# Install Stripe from Vercel Marketplace (auto-provisions sandbox + env vars)
vercel integration add stripe
Auto-provisioned environment variables:
STRIPE_SECRET_KEY — server-side API keySTRIPE_PUBLISHABLE_KEY — client-side publishable keySTRIPE_WEBHOOK_SECRET — webhook endpoint signing secret# Server-side SDK
npm install stripe
# Client-side SDK (for Stripe Elements / Checkout)
npm install @stripe/stripe-js @stripe/react-stripe-js
// lib/stripe.ts
import Stripe from "stripe";
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2026-02-25.clover",
typescript: true,
});
Server Actions are the preferred pattern for creating Checkout Sessions in Next.js 15+, eliminating the need for API routes:
// app/actions/checkout.ts
"use server";
import { redirect } from "next/navigation";
import { stripe } from "@/lib/stripe";
export async function createCheckoutSession(priceId: string) {
const session = await stripe.checkout.sessions.create({
mode: "payment",
line_items: [{ price: priceId, quantity: 1 }],
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/cancel`,
});
redirect(session.url!);
}
// app/pricing/page.tsx
import { createCheckoutSession } from "@/app/actions/checkout";
export default function PricingPage() {
return (
<form action={createCheckoutSession.bind(null, "price_xxx")}>
<button type="submit">Buy Now</button>
</form>
);
}
// app/api/checkout/route.ts
import { NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
export async function POST(req: Request) {
const { priceId } = await req.json();
const session = await stripe.checkout.sessions.create({
mode: "payment", // or "subscription" for recurring
payment_method_types: ["card"],
line_items: [{ price: priceId, quantity: 1 }],
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/cancel`,
});
return NextResponse.json({ url: session.url });
}
"use client";
import { loadStripe } from "@stripe/stripe-js";
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);
export function CheckoutButton({ priceId }: { priceId: string }) {
const handleCheckout = async () => {
const res = await fetch("/api/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ priceId }),
});
const { url } = await res.json();
window.location.href = url;
};
return <button onClick={handleCheckout}>Subscribe</button>;
}
Stripe sends events to your webhook endpoint for asynchronous payment processing. Always verify the signature.
// app/api/webhook/stripe/route.ts
import { NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
import Stripe from "stripe";
export async function POST(req: Request) {
const body = await req.text();
const signature = req.headers.get("stripe-signature")!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
return NextResponse.json({ error: "Invalid signature" }, { status: 400 });
}
switch (event.type) {
case "checkout.session.completed": {
const session = event.data.object as Stripe.Checkout.Session;
// Fulfill the order — update database, send confirmation, etc.
break;
}
case "invoice.payment_succeeded": {
const invoice = event.data.object as Stripe.Invoice;
// Handle successful subscription renewal
break;
}
case "customer.subscription.deleted": {
const subscription = event.data.object as Stripe.Subscription;
// Handle cancellation — revoke access
break;
}
}
return NextResponse.json({ received: true });
}
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.
const session = await stripe.checkout.sessions.create({
mode: "subscription",
line_items: [{ price: priceId, quantity: 1 }],
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard`,
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
});
Allow customers to manage their subscriptions:
// app/api/portal/route.ts
import { stripe } from "@/lib/stripe";
export async function POST(req: Request) {
const { customerId } = await req.json();
const session = await stripe.billingPortal.sessions.create({
customer: customerId,
return_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard`,
});
return Response.json({ url: session.url });
}
Stripe's Embedded Checkout renders inside your page via an iframe, keeping users on your domain while offloading PCI compliance to Stripe:
// app/actions/embedded-checkout.ts
"use server";
import { stripe } from "@/lib/stripe";
export async function createEmbeddedCheckout(priceId: string) {
const session = await stripe.checkout.sessions.create({
mode: "payment",
line_items: [{ price: priceId, quantity: 1 }],
ui_mode: "embedded",
return_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
});
return { clientSecret: session.client_secret! };
}
"use client";
import { loadStripe } from "@stripe/stripe-js";
import { EmbeddedCheckoutProvider, EmbeddedCheckout } from "@stripe/react-stripe-js";
import { createEmbeddedCheckout } from "@/app/actions/embedded-checkout";
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);
export function CheckoutEmbed({ priceId }: { priceId: string }) {
return (
<EmbeddedCheckoutProvider
stripe={stripePromise}
options={{ fetchClientSecret: () => createEmbeddedCheckout(priceId).then(r => r.clientSecret) }}
>
<EmbeddedCheckout />
</EmbeddedCheckoutProvider>
);
}
"use client";
import { Elements, PaymentElement, useStripe, useElements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);
function CheckoutForm() {
const stripe = useStripe();
const elements = useElements();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!stripe || !elements) return;
const { error } = await stripe.confirmPayment({
elements,
confirmParams: { return_url: `${window.location.origin}/success` },
});
if (error) console.error(error.message);
};
return (
<form onSubmit={handleSubmit}>
<PaymentElement />
<button type="submit" disabled={!stripe}>Pay</button>
</form>
);
}
export function PaymentForm({ clientSecret }: { clientSecret: string }) {
return (
<Elements stripe={stripePromise} options={{ clientSecret }}>
<CheckoutForm />
</Elements>
);
}
| Variable | Scope | Description |
|---|---|---|
STRIPE_SECRET_KEY | Server | API secret key (starts with sk_) |
STRIPE_PUBLISHABLE_KEY | Client | Publishable key (starts with pk_) |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Client | Alias exposed to browser via Next.js |
STRIPE_WEBHOOK_SECRET | Server | Webhook signing secret (starts with whsec_) |
⤳ skill: marketplace⤳ skill: routing-middleware⤳ skill: env-vars⤳ skill: vercel-functions