Most SaaS products start with flat-rate subscriptions: $29/month, $99/month, take it or leave it. That works fine when every customer uses roughly the same amount. But the moment you add an AI feature, a per-seat model, or anything that scales with usage, flat pricing punishes your best customers and rewards your lightest ones.
Usage-based billing fixes that. Customers pay for what they actually consume. You earn more as they grow. And your pricing aligns with the value you deliver.
Here is how to wire it up in a Next.js SaaS using Stripe Meters and Drizzle ORM -- without touching your existing subscription flow.
If you charge $29/month and one customer sends 10 AI messages while another sends 10,000, you are subsidizing the power user and overcharging the light one. Flat pricing works at the median. It fails at the edges -- and the edges are where your most valuable customers live.
Metered billing solves this by recording what each customer actually does and billing for it at period end. Stripe Meters handle the math and the invoicing. Your job is to record the events.
Adding usage-based billing to a Next.js SaaS involves four moving parts:
You buffer usage locally because Stripe Meters accept events asynchronously. Recording locally first lets you batch the reports, retry on failure, and show customers their running total without hitting the Stripe API on every page load.
-- Drizzle ORM schema
id uuid primary key default gen_random_uuid()
user_id uuid not null references users(id) on delete cascade
meter_name text not null -- matches the Stripe Meter event_name
quantity integer not null default 1
reported_at timestamptz -- null until flushed to Stripe
created_at timestamptz not null default now()
Every billable action inserts a row. The reported_at column stays null until the cron job flushes the batch.
Before the billable action executes, call your usage service:
await usageService.record({
userId: user.id,
meterName: 'ai_messages',
quantity: 1,
});
That is the entire product-side change. The billing logic is separate from the feature logic, which means you can add, remove, or reprice meters without touching the feature code.
A Vercel cron job running every few minutes fetches unreported rows, groups them by customer and meter, and sends each group to Stripe:
await stripe.billing.meterEvents.create({
event_name: 'ai_messages',
payload: {
stripe_customer_id: user.stripeCustomerId,
value: String(totalQuantity),
},
timestamp: Math.floor(Date.now() / 1000),
});
After a successful report, you mark the rows with reported_at = now(). Stripe accumulates the events and adds them to the customer's invoice at period end. You never calculate amounts or create invoice line items manually.
Nobody wants a surprise bill. A simple usage display keeps customers informed and reduces churn from sticker shock.
In a server component, query the current-period usage from your local table and show it as a number or a progress bar. Because you are reading from your own database, this is a fast query with no external API call. If the plan includes a free allowance before metered charges kick in, you can show how much of that allowance remains.
Before any of this works end-to-end, you need three things in the Stripe dashboard:
stripe.billing.meterEvents.create().usage_type: metered and link it to the Meter.The Stripe dashboard guides you through each step. The code above is what connects your app to those settings.
Calculating amounts, generating invoices, handling proration, retrying failed charges -- Stripe has solved all of that. Your job is to report what happened. Stripe's job is to charge for it correctly and handle every edge case in payment collection.
With this Next.js boilerplate you get the Drizzle ORM patterns, the service layer structure, the Stripe integration, and the background job setup already in place. Adding metered billing is a schema addition, a service call, and a cron route -- not a month of engineering work.
If you are already running flat-rate Stripe subscriptions, you do not need to scrap your billing flow. Add a metered price to the existing subscription, start recording events, and let Stripe handle the rest.
Get the boilerplate and ship usage-based pricing this week.