Stripe Billing: Complete Guide to Subscription and Usage-Based Payments

Implement recurring payments, subscription management, and metered billing with Stripe's developer-friendly API. Your complete guide to modern payment infrastructure.

Understanding Stripe Billing Fundamentals

Stripe Billing provides a comprehensive payment infrastructure for businesses of all sizes, enabling recurring payments, subscription management, and usage-based billing models. As our recommended payment solution, Stripe Billing offers the developer-friendly APIs and robust infrastructure that modern applications require. This guide covers the essential aspects of implementing Stripe Billing, from basic subscription setup to advanced metered billing configurations.

What Stripe Billing Offers

Stripe Billing serves as a complete billing solution that goes beyond simple payment processing. The platform handles the entire lifecycle of customer billing, from initial subscription creation through ongoing payment collection and renewal management. Unlike basic payment gateways that only process individual transactions, Stripe Billing provides the infrastructure needed for sustainable recurring revenue models.

The billing system integrates seamlessly with other Stripe products, allowing you to combine subscriptions with one-time purchases, handle proration when customers upgrade or downgrade plans, and manage complex billing scenarios such as grandfathered pricing or trial extensions. This integration extends to the broader Stripe ecosystem, meaning billing events flow naturally into reporting, analytics, and revenue recognition workflows.

Stripe Billing supports multiple pricing models within a single integration. You can offer fixed-price subscriptions where customers pay the same amount each billing cycle, tiered pricing that scales with usage or seat count, and metered billing that calculates charges based on actual consumption. This flexibility enables businesses to align their pricing with the value they deliver, whether that value is constant (like access to a service) or variable (like API calls or storage consumed). Our web development services help businesses implement these billing solutions seamlessly into their applications.

Billing Models Compared

Different business models require different billing approaches, and Stripe Billing accommodates this variety through its flexible pricing architecture.

Fixed-Price Subscriptions

Fixed-price subscriptions represent the simplest billing model, where customers pay a set amount at regular intervals. This model works well for services that provide constant value, such as membership access, software licensing, or retainer-based services. The predictability of fixed pricing simplifies customer understanding and reduces billing complexity, while still providing the recurring revenue that subscription businesses require.

Tiered Pricing

Tiered pricing adds complexity by offering multiple price points that provide different levels of service. Customers select a tier based on their needs, and Stripe handles the mechanics of applying the correct price based on the selected tier. Tiered pricing often includes volume discounts, where the per-unit price decreases as customers scale--Stripe supports this through volume-based and graduated pricing configurations that automatically calculate charges based on usage quantities.

Usage-Based Billing (Metered Billing)

Usage-based billing (metered billing) represents the most flexible but also most complex model. Rather than charging based on a predetermined price, usage-based billing calculates charges based on actual consumption measured through meters. This model aligns costs directly with value delivered, making it particularly effective for API services, cloud infrastructure, and AI automation solutions where customer usage directly correlates with the value provided. Stripe's metered billing system tracks usage through a dedicated meter API, allowing you to report consumption events throughout the billing period and calculate final charges at cycle end.

Implementing Metered Billing

Metered billing through Stripe's Billing Meter API provides a sophisticated system for tracking and billing based on variable customer usage. This system decouples the measurement of consumption from the actual billing calculation, allowing for flexibility in how you track and aggregate usage data.

How Metered Billing Works

The metering process begins with creating a meter that defines what you're measuring and how events should be aggregated. A meter represents a specific type of usage--for example, API requests, data storage, or computational units--and specifies the aggregation method that determines how individual events combine into billable units. When you create a meter, you define its display name, the event field to aggregate, and the aggregation type that governs how Stripe combines events into usage totals.

Meter Event Reporting forms the operational backbone of metered billing. Throughout the billing period, your application reports usage events to Stripe's meter event API. Each event represents a discrete unit of consumption--for instance, a single API call, a megabyte-hour of storage, or a processing task completed. Stripe stores these events and aggregates them according to the meter's configuration, maintaining a running total of consumption that can be queried at any point during the billing period.

Billing Cycle Integration connects metered usage to Stripe's standard subscription and invoicing system. At the end of each billing period, Stripe calculates the final usage total by aggregating all meter events reported during that period. This total then feeds into the normal invoice generation process, where it combines with any fixed subscription components, applies proration if needed, calculates taxes, and attempts payment collection through the customer's default payment method.

Creating a Meter for Usage Tracking
1import Stripe from 'stripe';2 3const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {4 apiVersion: '2025-04-30.basil',5});6 7// Create a meter for tracking API usage8export async function createApiUsageMeter() {9 const meter = await stripe.billing.meters.create({10 display_name: 'API Requests',11 default_aggregation: {12 method: 'sum',13 },14 event_name: 'api.request',15 });16 17 return meter;18}

Reporting Usage Events

Once meters exist, your application must report usage events throughout each billing period. This reporting happens through Stripe's meter event API, which accepts individual events and stores them for aggregation.

For applications with high usage volumes, batch event reporting provides significant efficiency gains. The batch endpoint accepts up to 100 events in a single API call, reducing HTTP overhead and ensuring that usage reporting keeps pace with actual consumption. This is particularly important for services like API platforms or cloud infrastructure where a single customer might generate thousands or millions of usage events per day.

Event timing matters for accurate billing. Events are associated with the billing period in which they occur, and Stripe provides flexibility in how you handle events that arrive after the period closes. You can configure grace periods that allow late events to apply to the previous period, or you can require that all events arrive within the current period for strict period-bound billing.

Reporting Usage Events to Stripe
1// Report a single usage event2export async function reportApiUsage(customerId: string, meterName: string) {3 await stripe.billing.meterEvents.create({4 event_name: meterName,5 payload: {6 customer: customerId,7 value: 1,8 },9 });10}11 12// Report batch usage events (more efficient for high-volume scenarios)13export async function reportBatchUsage(14 customerId: string,15 meterName: string,16 count: number17) {18 const events = Array(count).fill(null).map(() => ({19 event_name: meterName,20 payload: {21 customer: customerId,22 value: 1,23 },24 }));25 26 await stripe.billing.meterEvents.createBatch({27 events,28 });29}

Stripe Events and Webhooks

Stripe's event system provides real-time notifications about changes and activities within your Stripe account. Rather than requiring constant polling to detect changes, Stripe pushes events to your application through webhooks, enabling responsive, event-driven architectures that react immediately to billing events.

Key Billing Events to Handle

customer.subscription.created signals the start of a new billing relationship. When this event arrives, your application should provision the appropriate service access for the new subscriber, initialize any customer-specific resources, and potentially send a welcome communication. This event includes the full subscription object, allowing you to extract plan details, billing interval, and any trial period information.

invoice.paid confirms successful payment collection for a billing period. This event indicates that funds have been secured and the customer has been successfully charged, allowing you to unlock or maintain service access for the next period. The invoice object within the event contains detailed line items, payment amounts, and tax information that may be useful for record-keeping or compliance.

invoice.payment_failed requires immediate attention as it indicates that Stripe attempted to collect payment but failed. Common causes include expired cards, insufficient funds, or bank declines. Your application should respond by notifying the customer of the payment issue, potentially restricting service access according to your policies, and providing a path for the customer to update their payment method.

customer.subscription.updated covers various changes to existing subscriptions, including plan changes, quantity adjustments, and modification of subscription attributes. This event allows you to update service provisioning when customer subscriptions change--for example, upgrading to a higher tier or adding additional seats to a team plan.

customer.subscription.deleted indicates that a subscription has ended, either through cancellation, non-payment, or expiration. Your application should revoke service access, clean up any customer-specific resources according to your retention policies, and potentially trigger offboarding workflows.

Production Webhook Handler Implementation
1import Stripe from 'stripe';2import { NextRequest, NextResponse } from 'next/server';3 4const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {5 apiVersion: '2025-04-30.basil',6});7 8const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;9 10export async function POST(request: NextRequest) {11 const body = await request.text();12 const signature = request.headers.get('stripe-signature');13 14 if (!signature) {15 return NextResponse.json(16 { error: 'Missing stripe-signature header' },17 { status: 400 }18 );19 }20 21 let event: Stripe.Event;22 23 try {24 event = stripe.webhooks.constructEvent(body, signature, webhookSecret);25 } catch (err) {26 console.error('Webhook signature verification failed:', err);27 return NextResponse.json(28 { error: 'Invalid signature' },29 { status: 400 }30 );31 }32 33 try {34 switch (event.type) {35 case 'customer.subscription.created':36 await handleSubscriptionCreated(event.data.object as Stripe.Subscription);37 break;38 case 'invoice.paid':39 await handleInvoicePaid(event.data.object as Stripe.Invoice);40 break;41 case 'invoice.payment_failed':42 await handleInvoicePaymentFailed(event.data.object as Stripe.Invoice);43 break;44 }45 46 return NextResponse.json({ received: true });47 } catch (err) {48 console.error('Error processing webhook:', err);49 return NextResponse.json(50 { error: 'Webhook handler failed' },51 { status: 500 }52 );53 }54}

Code Examples for Common Operations

Creating a Subscription

The subscription creation pattern demonstrates several important Stripe conventions including idempotency keys, payment behavior settings, and response expansion. The payment_behavior: 'default_incomplete' setting ensures that subscriptions requiring immediate payment are created in a state that allows the payment intent to be retrieved and presented to the customer. The expand parameter includes related objects in the response, avoiding additional API calls to retrieve invoice details.

Updating Subscription Plans

Plan updates require careful handling of proration, which determines how charges and credits flow when mid-period changes occur. The always_prorate behavior provides the most straightforward customer experience, adjusting the current invoice to reflect the partial value of the old plan and the partial value of the new plan. Implementing these patterns correctly requires careful attention to the subscription lifecycle and proper web development integration patterns.

Creating a Customer Subscription
1export async function createSubscription(2 customerId: string,3 priceId: string,4 trialDays?: number5) {6 const subscription = await stripe.subscriptions.create({7 customer: customerId,8 items: [{ price: priceId }],9 trial_period_days: trialDays,10 payment_behavior: 'default_incomplete',11 expand: ['latest_invoice.payment_intent'],12 proration_behavior: 'create_prorations',13 });14 15 return subscription;16}17 18// Update subscription plan19export async function updateSubscription(20 subscriptionId: string,21 newPriceId: string22) {23 const subscription = await stripe.subscriptions.retrieve(subscriptionId);24 25 const updatedSubscription = await stripe.subscriptions.update(subscriptionId, {26 items: [27 {28 id: subscription.items.data[0].id,29 price: newPriceId,30 },31 ],32 proration_behavior: 'always_prorate',33 });34 35 return updatedSubscription;36}

Best Practices and Security

Security Considerations

Billing systems handle sensitive financial data, requiring strict security measures to protect customer information and prevent unauthorized access.

Secret key management represents the most critical security concern. Never expose Stripe secret keys in client-side code, version control, or logging systems. Use environment variables for all secret storage, rotate keys periodically, and consider using restricted keys that limit capabilities to only what your integration requires.

Webhook verification must be implemented for all production endpoints. The signature verification process confirms that requests genuinely originated from Stripe and haven't been tampered with during transit. Skipping this verification exposes your application to potential spoofing attacks that could trigger fraudulent subscription actions or payment reversals.

PCI compliance is largely handled by Stripe when you use their hosted payment elements, but your code must never directly handle raw card numbers. Use Stripe Elements or Payment Elements for all payment collection, and ensure that your application architecture doesn't store or log card details even in encrypted form.

Idempotency Patterns

Billing operations must handle duplicate requests gracefully, which requires implementing idempotency at key points in your integration.

Stripe's native idempotency support allows you to safely retry operations without creating duplicate records. Include an idempotency key with each request that uniquely identifies the operation--typically a UUID generated by your application. If the same key is used for a subsequent request, Stripe returns the original result rather than creating a duplicate.

For webhook handling, implement application-level idempotency by tracking processed event IDs. Stripe's at-least-once delivery means you might receive the same event multiple times, and your handler must recognize and skip duplicate processing. Store processed event IDs with a TTL that exceeds Stripe's retry window (typically three days) to ensure duplicates are caught.

Integration Checklist

Key steps for implementing Stripe Billing

Set up Stripe account

Configure test and production environments with appropriate settings

Configure webhooks

Implement webhook endpoints for event handling

Customer management

Implement customer creation and management flows

Subscription lifecycle

Create, update, and cancel subscriptions

Event handling

Handle all billing events (invoice paid, payment failed)

Payment methods

Implement payment method management and updates

Metered billing

Set up meters for usage-based pricing

Billing portal

Integrate customer self-service billing portal

Frequently Asked Questions

What billing models does Stripe Billing support?

Stripe Billing supports fixed-price subscriptions, tiered pricing, and usage-based (metered) billing. You can combine these models within a single subscription using Stripe's flexible pricing architecture.

How does metered billing work?

Metered billing uses Stripe's Billing Meter API to track customer usage throughout the billing period. You report usage events as they occur, and Stripe aggregates them to calculate charges at the end of the billing cycle.

What events should I handle via webhooks?

Key billing events include customer.subscription.created, invoice.paid, invoice.payment_failed, customer.subscription.updated, and customer.subscription.deleted. These events enable your application to react to billing changes in real-time.

How do I handle payment failures?

Handle the invoice.payment_failed webhook event to notify customers, restrict service access according to your policies, and provide a path for updating payment methods through the Stripe Billing Portal.

Sources

  1. Stripe Billing - Recurring payments and subscription solutions
  2. Stripe Metered Billing API - Meter creation and event aggregation
  3. Stripe Usage Records API - Reporting customer usage for billing
  4. Stripe Webhooks Documentation - Event-driven payment handling
  5. Stripe Events API - Event types and structure

Ready to Implement Stripe Billing?

Our team specializes in building robust subscription and billing systems with Stripe. Let's discuss how we can help you implement a billing solution that scales with your business.