The brief
LovedPDF charged one flat monthly fee. A handful of accounts ran thousands of documents through it and cost more than they paid. Most accounts ran a few dozen and quietly subsidized them. The brief: move to metered billing on document volume, keep the flat tier as an option, lose nobody.
The constraint
Billing code is the one place a bug is also a refund and a support ticket and a trust problem. The constraint was simple to state and hard to honor: every billable event must be counted exactly once, even if a request retries, even if a webhook arrives twice, even if a deploy lands mid-request.
The second constraint was the migration. Existing subscribers could not see a price change they did not agree to. The new meter had to run silently for a full cycle before anyone was billed on it.
The approach
We made the usage event the source of truth and made it idempotent. Every document processed writes one row keyed by a deterministic event id. Stripe meter events are reported from that table, not from the request path, so a retry can never double-count.
For the migration we ran the meter in shadow mode. For one full billing cycle it counted everything and billed nothing. We compared shadow totals against what each account would have paid and sent every affected user the exact number before it ever hit a card.
The build
The schema and the idempotency key took two days. The reconciler and the Stripe meter integration took four. The shadow-mode tooling and the comparison report took two. One day for handoff.
-- One row per billable event. The unique key is the whole defense.
create table usage_events (
id uuid primary key default gen_random_uuid(),
event_key text not null unique, -- deterministic, dedupes retries
account_id uuid not null references accounts(id),
reported_at timestamptz, -- null until the reconciler runs
created_at timestamptz not null default now()
);The outcome
Metered billing went live on day 9. After the shadow cycle, light accounts saw their bill drop and the heavy accounts moved to usage pricing with the numbers in front of them. There were zero billing disputes in the first three months.
Revenue from the top decile of accounts rose because they were finally paying for what they used. Nobody churned over the change.
“They shipped billing code I did not have to double-check. The shadow cycle meant every customer saw their number before it hit a card. That is how you change pricing without losing people.”