> stripe-billing
You are an expert in Stripe Billing, the complete billing platform for SaaS businesses. You help developers implement subscription management, usage-based billing, metered pricing, free trials, proration, invoicing, customer portal, and webhook-driven lifecycle management — building everything from simple monthly plans to complex per-seat + usage hybrid pricing.
curl "https://skillshub.wtf/TerminalSkills/skills/stripe-billing?format=md"Stripe Billing — SaaS Subscription & Usage-Based Billing
You are an expert in Stripe Billing, the complete billing platform for SaaS businesses. You help developers implement subscription management, usage-based billing, metered pricing, free trials, proration, invoicing, customer portal, and webhook-driven lifecycle management — building everything from simple monthly plans to complex per-seat + usage hybrid pricing.
Core Capabilities
Subscription Setup
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
// Create product + price
const product = await stripe.products.create({
name: "Pro Plan",
description: "Full access with API usage",
});
// Fixed recurring price
const monthlyPrice = await stripe.prices.create({
product: product.id,
unit_amount: 2900, // $29.00
currency: "usd",
recurring: { interval: "month" },
});
// Usage-based price (metered)
const apiPrice = await stripe.prices.create({
product: product.id,
currency: "usd",
recurring: { interval: "month", usage_type: "metered" },
billing_scheme: "tiered",
tiers_mode: "graduated",
tiers: [
{ up_to: 1000, unit_amount: 0 }, // First 1000 free
{ up_to: 10000, unit_amount: 1 }, // $0.01 per call (1K-10K)
{ up_to: "inf", unit_amount: 0.5 }, // $0.005 per call (10K+)
],
});
// Create checkout session
const session = await stripe.checkout.sessions.create({
mode: "subscription",
customer_email: user.email,
line_items: [
{ price: monthlyPrice.id, quantity: 1 },
{ price: apiPrice.id }, // Metered — no quantity
],
subscription_data: {
trial_period_days: 14,
metadata: { userId: user.id },
},
success_url: "https://app.example.com/billing/success?session_id={CHECKOUT_SESSION_ID}",
cancel_url: "https://app.example.com/billing/cancel",
});
// Redirect to session.url
Usage Reporting
// Report API usage for metered billing
async function reportUsage(subscriptionItemId: string, quantity: number) {
await stripe.subscriptionItems.createUsageRecord(subscriptionItemId, {
quantity,
timestamp: Math.floor(Date.now() / 1000),
action: "increment", // Adds to current period total
});
}
// In your API middleware
app.use(async (req, res, next) => {
next();
// After response, report usage
const user = req.user;
if (user.stripeSubscriptionItemId) {
await reportUsage(user.stripeSubscriptionItemId, 1);
}
});
Webhook Handler
app.post("/api/webhooks/stripe", async (req, res) => {
const event = stripe.webhooks.constructEvent(
req.body, req.headers["stripe-signature"]!, process.env.STRIPE_WEBHOOK_SECRET!,
);
switch (event.type) {
case "checkout.session.completed": {
const session = event.data.object as Stripe.Checkout.Session;
await db.users.update(session.metadata!.userId, {
plan: "pro",
stripeCustomerId: session.customer as string,
stripeSubscriptionId: session.subscription as string,
});
break;
}
case "invoice.paid": {
const invoice = event.data.object as Stripe.Invoice;
await db.invoices.create({
userId: await getUserByStripeCustomer(invoice.customer as string),
amount: invoice.amount_paid,
pdfUrl: invoice.invoice_pdf,
});
break;
}
case "customer.subscription.deleted": {
const sub = event.data.object as Stripe.Subscription;
await db.users.update({ stripeSubscriptionId: sub.id }, { plan: "free" });
break;
}
case "invoice.payment_failed": {
const invoice = event.data.object as Stripe.Invoice;
await sendPaymentFailedEmail(invoice.customer as string);
break;
}
}
res.json({ received: true });
});
Customer Portal
// Let customers manage their own subscription
app.post("/api/billing/portal", async (req, res) => {
const session = await stripe.billingPortal.sessions.create({
customer: req.user.stripeCustomerId,
return_url: "https://app.example.com/settings",
});
res.json({ url: session.url });
});
// Customer can: change plan, update payment method, cancel, view invoices
Installation
npm install stripe
Best Practices
- Webhooks are truth — Don't rely on checkout redirects alone; webhooks handle edge cases (failed payments, renewals)
- Customer portal — Use Stripe's hosted portal for plan changes, cancellation, invoices; saves weeks of development
- Metered billing — Report usage incrementally via
createUsageRecord; Stripe aggregates and invoices at period end - Trial periods — Set
trial_period_dayson subscription; no payment collected until trial ends - Proration — Stripe auto-prorates when customers upgrade/downgrade mid-cycle; no manual calculations
- Idempotency keys — Pass
idempotencyKeyon creates to prevent duplicate charges on retries - Test mode — Use
sk_test_key for development; Stripe provides test card numbers for all scenarios - Tax automation — Enable Stripe Tax for automatic tax calculation and collection; handles global tax compliance
> related_skills --same-repo
> zustand
You are an expert in Zustand, the small, fast, and scalable state management library for React. You help developers manage global state without boilerplate using Zustand's hook-based stores, selectors for performance, middleware (persist, devtools, immer), computed values, and async actions — replacing Redux complexity with a simple, un-opinionated API in under 1KB.
> zoho
Integrate and automate Zoho products. Use when a user asks to work with Zoho CRM, Zoho Books, Zoho Desk, Zoho Projects, Zoho Mail, or Zoho Creator, build custom integrations via Zoho APIs, automate workflows with Deluge scripting, sync data between Zoho apps and external systems, manage leads and deals, automate invoicing, build custom Zoho Creator apps, set up webhooks, or manage Zoho organization settings. Covers Zoho CRM, Books, Desk, Projects, Creator, and cross-product integrations.
> zod
You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.
> zipkin
Deploy and configure Zipkin for distributed tracing and request flow visualization. Use when a user needs to set up trace collection, instrument Java/Spring or other services with Zipkin, analyze service dependencies, or configure storage backends for trace data.