Skip to main content
Business & GrowthE-commerce Services189 lines

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.

Quick Summary29 lines
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 lines
Paste into your CLAUDE.md or agent config

Stripe 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_failed events, 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

Get CLI access →

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.

E-commerce Services225L

Bigcommerce

Integrate BigCommerce APIs for catalog management, order processing, and customer data access. Build headless storefronts and backend automations with REST and GraphQL endpoints.

E-commerce Services230L

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.

E-commerce Services172L

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.

E-commerce Services285L

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.

E-commerce Services253L

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.

E-commerce Services236L