Use when the user mentions Stripe, Stripe Checkout, Stripe webhooks, payment intents, Stripe subscriptions, Stripe API, stripe CLI, payment processing, Stripe products/prices, or integrating payments into a web app.
Help users integrate Stripe for payments, subscriptions, and billing.
Use WebFetch against https://docs.stripe.com/... when:
Useful doc URLs:
https://docs.stripe.com/apihttps://docs.stripe.com/checkout/quickstarthttps://docs.stripe.com/payments/payment-intentshttps://docs.stripe.com/webhookshttps://docs.stripe.com/billing/subscriptions/overviewhttps://docs.stripe.com/products-prices/overviewhttps://docs.stripe.com/stripe-clihttps://docs.stripe.com/testinghttps://docs.stripe.com/payments/payment-elementhttps://docs.stripe.com/customer-managementhttps://docs.stripe.com/connecthttps://docs.stripe.com/api/idempotent_requests// Server-side (Node.js)
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
// or
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: [
{
price: 'price_xxxxx', // or use price_data for inline
quantity: 1,
},
],
success_url: `${YOUR_DOMAIN}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${YOUR_DOMAIN}/cancel`,
metadata: { userId: '123' }, // passed to webhook
});
// Redirect client to session.url
res.redirect(303, session.url);
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: 'price_monthly_xxxxx', quantity: 1 }],
success_url: `${YOUR_DOMAIN}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${YOUR_DOMAIN}/cancel`,
customer_email: '[email protected]', // or customer: 'cus_xxxxx'
});
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: [{
price_data: {
currency: 'usd',
product_data: { name: 'Sway Score Lookup' },
unit_amount: 99, // $0.99 in cents
},
quantity: 1,
}],
success_url: `${YOUR_DOMAIN}/success`,
cancel_url: `${YOUR_DOMAIN}/cancel`,
});
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
// CRITICAL: Use raw body, not parsed JSON
export const config = { api: { bodyParser: false } }; // Next.js
// For Vercel serverless: read raw body from request
export default async function handler(req, res) {
const sig = req.headers['stripe-signature'];
const rawBody = await getRawBody(req); // or req.body if raw
let event;
try {
event = stripe.webhooks.constructEvent(rawBody, sig, endpointSecret);
} catch (err) {
console.error('Webhook signature verification failed:', err.message);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
switch (event.type) {
case 'checkout.session.completed':
const session = event.data.object;
// Fulfill order, update DB
await handleCheckoutComplete(session);
break;
case 'invoice.paid':
// Subscription payment succeeded
break;
case 'invoice.payment_failed':
// Subscription payment failed
break;
case 'customer.subscription.updated':
// Plan change, cancellation, etc.
break;
case 'customer.subscription.deleted':
// Subscription ended
break;
default:
console.log(`Unhandled event: ${event.type}`);
}
res.json({ received: true });
}
| Event | When |
|---|---|
checkout.session.completed | Customer finishes Checkout |
invoice.paid | Subscription payment succeeds |
invoice.payment_failed | Subscription payment fails |
customer.subscription.created | New subscription starts |
customer.subscription.updated | Plan changed, renewed, etc. |
customer.subscription.deleted | Subscription canceled/expired |
payment_intent.succeeded | Payment Intent completes |
payment_intent.payment_failed | Payment Intent fails |
charge.refunded | Refund processed |
2xx quickly — do heavy processing asyncevent.id to deduplicate retries// Create a product
const product = await stripe.products.create({
name: 'Sway Score Lookup',
description: 'One-time influence score check',
});
// Create a one-time price
const price = await stripe.prices.create({
product: product.id,
unit_amount: 99, // cents
currency: 'usd',
});
// Create a recurring price
const monthlyPrice = await stripe.prices.create({
product: product.id,
unit_amount: 999,
currency: 'usd',
recurring: { interval: 'month' },
});
Sandbox vs Production: Price IDs differ between modes. Use environment variables:
STRIPE_PRICE_LOOKUP=price_test_xxx # sandbox
STRIPE_PRICE_LOOKUP=price_live_xxx # production
// Create customer
const customer = await stripe.customers.create({
email: '[email protected]',
metadata: { userId: '123' },
});
// Retrieve
const customer = await stripe.customers.retrieve('cus_xxxxx');
// List
const customers = await stripe.customers.list({ limit: 10 });
// Create (if not using Checkout)
const subscription = await stripe.subscriptions.create({
customer: 'cus_xxxxx',
items: [{ price: 'price_xxxxx' }],
trial_period_days: 14,
});
// Cancel at period end
await stripe.subscriptions.update('sub_xxxxx', {
cancel_at_period_end: true,
});
// Cancel immediately
await stripe.subscriptions.cancel('sub_xxxxx');
// Change plan
await stripe.subscriptions.update('sub_xxxxx', {
items: [{
id: 'si_xxxxx', // subscription item ID
price: 'price_new_xxxxx',
}],
proration_behavior: 'create_prorations',
});
# Install (macOS)
brew install stripe/stripe-cli/stripe
# Login
stripe login
# Listen for webhooks locally
stripe listen --forward-to localhost:3000/api/webhooks/stripe
# Trigger test events
stripe trigger checkout.session.completed
stripe trigger payment_intent.succeeded
stripe trigger customer.subscription.created
# View recent events
stripe events list --limit 10
# View logs
stripe logs tail
# Create test resources
stripe products create --name="Test Product"
stripe prices create --product=prod_xxx --unit-amount=999 --currency=usd
| Card | Behavior |
|---|---|
4242 4242 4242 4242 | Succeeds |
4000 0000 0000 3220 | 3D Secure required |
4000 0000 0000 9995 | Declined (insufficient funds) |
4000 0000 0000 0002 | Declined (generic) |
4000 0025 0000 3155 | Requires authentication |
Use any future expiry date, any 3-digit CVC, any postal code.
STRIPE_SECRET_KEY=sk_test_... # Server-side only
STRIPE_PUBLISHABLE_KEY=pk_test_... # Client-side safe
STRIPE_WEBHOOK_SECRET=whsec_... # For webhook verification
export const config = { api: { bodyParser: false } }. In Vercel serverless: read req.body as buffer.stripe.paymentIntents.create({...}, { idempotencyKey: 'unique-key' })_test_ in keys. Switch by changing API key.unit_amount: 999 = $9.99, not $999.