SaaS

Subscription Billing Migration

Built by Reymart Dalisay

Moved a B2B SaaS company off hand-rolled billing onto a metered subscription model without reissuing a year of invoices.

  • Builder email verified

    Verification

Type
Professional
Category
SaaS
Published
Jul 25, 2026
Updated
Jul 25, 2026
Built withTypeScriptNode.jsPostgreSQLStripeAWS Step FunctionsAWS Lambda
TopicsFintechProductivity

About this project

The problem

The company had been invoicing manually from spreadsheets since it was five people, and by the time I came in it was a finance person's full week every month plus a long tail of corrections. Sales had also started selling usage-based tiers the system couldn't represent, so those contracts were being tracked in a shared document and billed by hand. The goal was automated metered billing that matched what sales had actually signed, without changing anyone's existing invoice terms mid-contract.

My role

I was brought in as a contractor for what was scoped as three months and ran about five. I worked alone on the billing side, reporting to the head of engineering, with the finance manager as the person who decided what correct meant. Toward the end I also wrote the internal documentation and ran two handover sessions, since I was leaving and nobody else had been in the code.

What I owned

I built the metering pipeline that aggregates raw usage events into billable quantities, the subscription model that maps a signed contract to those quantities, and the Stripe integration that turns the result into an invoice. I owned the migration of about four hundred existing customers, including the reconciliation script that compared what the new system would have billed against what was actually billed for the previous three months. I also wrote the dunning workflow for failed payments. I didn't touch the product's own usage instrumentation — I consumed what was already emitted and worked around its gaps.

Technical & product decisions

The decision I'd defend hardest is that I refused to backfill or restate historical invoices. Finance wanted the new system to be the record for everything; I argued that recomputing a year of invoices with new logic would create differences we'd have to explain to customers one at a time. We froze history in place and ran the new system forward from a cutover date. I also chose to compute billable quantities in a nightly batch rather than continuously, because a subscription invoice is a monthly artifact and streaming aggregation would have added operational surface for no business benefit. AWS Step Functions handled the monthly run because I wanted the retry and audit behaviour without owning a scheduler.

Constraints

Usage instrumentation was incomplete — two of the metered features didn't emit events at all when I started, and I had to get another team to add them, which set the timeline back about a month. Every customer's contract had to bill identically before and after cutover or finance wouldn't sign off, so the reconciliation had to reach exact agreement, not close agreement. There was also no staging environment with realistic data, so I built a fixture generator first, which felt like a detour and saved the project.

Result & impact

The monthly close went from about a week of one person's time to roughly half a day of reviewing exceptions. Usage-based contracts stopped being tracked in a shared document — sales can now sell a tier the system understands, which they did nine times in the first quarter. The reconciliation run caught eleven customers who had been under-billed for months, worth more than my contract, and two who had been over-billed, which finance refunded. About four months after I left they added a new metered dimension themselves without contacting me, which I take as the real result.

Who else worked on it

The finance manager reviewed every reconciliation run and was the reason the migration was trustworthy; she found discrepancies I'd have shipped past. An engineer on the product team added the missing usage events and later took ownership of the whole thing. I also leaned on their head of engineering to say no to a mid-project request to add proration rules, which would have doubled the scope.