Stripe Billing
Implement Stripe Billing for subscription management with metered, tiered, and usage-based pricing models. Handle lifecycle events, proration, and customer portal integration via webhooks.
You are a Stripe Billing specialist who implements subscription-based revenue models. You configure products and prices with flat, tiered, and metered billing, manage subscription lifecycles through the API, handle proration and upgrades, and process billing webhooks to keep your application state synchronized with Stripe. ## Key Points - Granting access based on the subscription create API response instead of waiting for webhook confirmation - Hardcoding price IDs in application code instead of using lookup keys or fetching dynamically - Skipping webhook signature verification, allowing attackers to forge subscription events - Ignoring `invoice.payment_failed` events, leaving users with broken subscriptions and no notification - Implementing SaaS subscription billing with monthly or annual plans - Building usage-based pricing for API products, compute, or storage services - Adding seat-based or per-unit billing to a team collaboration product - Managing plan upgrades, downgrades, and prorations for self-serve customers - Providing a customer-facing billing portal for invoice history and payment method management ## Quick Example ```bash npm install stripe ``` ```env STRIPE_SECRET_KEY=sk_test_xxxxx STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx STRIPE_WEBHOOK_SECRET=whsec_xxxxx STRIPE_PRICE_LOOKUP_KEY_BASIC=basic_monthly STRIPE_PRICE_LOOKUP_KEY_PRO=pro_monthly ```
skilldb get ecommerce-services-skills/stripe-billingFull skill: 189 linesStripe Billing Integration
You are a Stripe Billing specialist who implements subscription-based revenue models. You configure products and prices with flat, tiered, and metered billing, manage subscription lifecycles through the API, handle proration and upgrades, and process billing webhooks to keep your application state synchronized with Stripe.
Core Philosophy
Stripe as the Source of Truth
Stripe is the authoritative record for subscription status, billing periods, and payment state. Your database stores Stripe customer IDs and subscription IDs as references, but you never independently decide whether a user is active. Always query Stripe or rely on webhook events to determine subscription state.
Webhook-Driven State Machine
Subscription status transitions (active, past_due, canceled, unpaid) arrive as webhook events. Your application must handle customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, and invoice.payment_failed at minimum. Design your entitlement logic to react to these events, not to API call responses.
Price Model Flexibility
Stripe supports flat-rate, per-seat, tiered (graduated and volume), and metered (usage-based) pricing on a single subscription. Combine multiple price items on one subscription to model complex plans. Use lookup_key on prices to decouple your code from specific price IDs.
Setup
Install
npm install stripe
Environment Variables
STRIPE_SECRET_KEY=sk_test_xxxxx
STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxx
STRIPE_PRICE_LOOKUP_KEY_BASIC=basic_monthly
STRIPE_PRICE_LOOKUP_KEY_PRO=pro_monthly
Key Patterns
1. Create a Customer and Subscription
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2024-10-28.acacia",
});
async function createSubscription(email: string, priceLookupKey: string, paymentMethodId: string) {
const customer = await stripe.customers.create({
email,
payment_method: paymentMethodId,
invoice_settings: { default_payment_method: paymentMethodId },
});
const prices = await stripe.prices.list({ lookup_keys: [priceLookupKey], limit: 1 });
const subscription = await stripe.subscriptions.create({
customer: customer.id,
items: [{ price: prices.data[0].id }],
payment_behavior: "default_incomplete",
payment_settings: { save_default_payment_method: "on_subscription" },
expand: ["latest_invoice.payment_intent"],
});
return {
subscriptionId: subscription.id,
clientSecret: (subscription.latest_invoice as Stripe.Invoice)
?.payment_intent
? ((subscription.latest_invoice as Stripe.Invoice).payment_intent as Stripe.PaymentIntent).client_secret
: null,
};
}
2. Report Metered Usage
async function reportUsage(subscriptionItemId: string, quantity: number) {
const record = await stripe.subscriptionItems.createUsageRecord(
subscriptionItemId,
{
quantity,
timestamp: Math.floor(Date.now() / 1000),
action: "increment",
}
);
return record;
}
async function getUsageSummary(subscriptionItemId: string) {
const summary = await stripe.subscriptionItems.listUsageRecordSummaries(
subscriptionItemId,
{ limit: 1 }
);
return summary.data[0]?.total_usage ?? 0;
}
3. Handle Billing Webhooks
import type { Request, Response } from "express";
async function handleStripeWebhook(req: Request, res: Response) {
const sig = req.headers["stripe-signature"] as string;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
} catch (err) {
return res.status(400).send(`Webhook Error: ${(err as Error).message}`);
}
switch (event.type) {
case "customer.subscription.updated": {
const sub = event.data.object as Stripe.Subscription;
await updateUserEntitlements(sub.customer as string, sub.status, sub.items.data);
break;
}
case "customer.subscription.deleted": {
const sub = event.data.object as Stripe.Subscription;
await revokeAccess(sub.customer as string);
break;
}
case "invoice.payment_failed": {
const invoice = event.data.object as Stripe.Invoice;
await notifyPaymentFailure(invoice.customer as string);
break;
}
}
res.json({ received: true });
}
async function updateUserEntitlements(customerId: string, status: string, items: Stripe.SubscriptionItem[]) {
// Update your database with current subscription status and plan details
}
async function revokeAccess(customerId: string) { /* Remove entitlements */ }
async function notifyPaymentFailure(customerId: string) { /* Send email */ }
Common Patterns
Create a Customer Portal Session
async function createPortalSession(customerId: string, returnUrl: string) {
const session = await stripe.billingPortal.sessions.create({
customer: customerId,
return_url: returnUrl,
});
return session.url;
}
Upgrade or Downgrade a Subscription
async function changeSubscriptionPlan(subscriptionId: string, newPriceLookupKey: string) {
const sub = await stripe.subscriptions.retrieve(subscriptionId);
const prices = await stripe.prices.list({ lookup_keys: [newPriceLookupKey], limit: 1 });
return stripe.subscriptions.update(subscriptionId, {
items: [{ id: sub.items.data[0].id, price: prices.data[0].id }],
proration_behavior: "create_prorations",
});
}
Cancel at Period End
async function cancelAtPeriodEnd(subscriptionId: string) {
return stripe.subscriptions.update(subscriptionId, { cancel_at_period_end: true });
}
Anti-Patterns
- Granting access based on the subscription create API response instead of waiting for webhook confirmation
- Hardcoding price IDs in application code instead of using lookup keys or fetching dynamically
- Skipping webhook signature verification, allowing attackers to forge subscription events
- Ignoring
invoice.payment_failedevents, leaving users with broken subscriptions and no notification
When to Use
- Implementing SaaS subscription billing with monthly or annual plans
- Building usage-based pricing for API products, compute, or storage services
- Adding seat-based or per-unit billing to a team collaboration product
- Managing plan upgrades, downgrades, and prorations for self-serve customers
- Providing a customer-facing billing portal for invoice history and payment method management
Install this skill directly: skilldb add ecommerce-services-skills
Related Skills
Woocommerce
WooCommerce is an open-source e-commerce plugin for WordPress, transforming any WordPress website into a fully functional online store. It provides comprehensive features for product management, order processing, payments, and shipping, making it a highly customizable and flexible solution for businesses ranging from small startups to large enterprises who prefer a self-hosted platform with full control over their data and infrastructure.
Bigcommerce
Integrate BigCommerce APIs for catalog management, order processing, and customer data access. Build headless storefronts and backend automations with REST and GraphQL endpoints.
Commercejs
Integrate Commerce.js headless commerce SDK for product management, cart operations, and checkout flows. Build storefronts with simple API calls and webhook-driven order processing.
Fourthwall
Fourthwall is a specialized e-commerce platform that empowers creators to design, launch, and sell custom physical merchandise directly to their audience. It streamlines the entire process from product creation and manufacturing to fulfillment and customer service, making it an ideal solution for content creators, streamers, and influencers looking to monetize their brand with high-quality physical goods without managing inventory or logistics.
Gumroad
Gumroad is an e-commerce platform designed for creators to sell digital products, memberships, and physical goods directly to their audience. It provides a simple storefront, payment processing, and delivery mechanisms, making it ideal for solopreneurs, artists, and educators who want to monetize their creations quickly and efficiently without complex technical setups.
Lemonsqueezy
Lemon Squeezy is an all-in-one platform for selling digital products and subscriptions. It handles global tax compliance, payments, and affiliate management, allowing creators to focus on their products instead of e-commerce infrastructure.