Services
Billing & Dashboards
Subscriptions, custom order forms, multi-brand billing, and the reporting to make sense of it all.
Billing is where bugs cost real money, and it is a system we have built and operated end to end — checkout, recurring billing, dunning, refunds, multi-currency, multi-brand, and the reporting on top. We have also migrated live billing pipelines between processors without disrupting active customers.
What makes it a different engineering problem from the rest of an application is the tolerance for error. Money is not eventually consistent. An invoice that double-charges, a refund that silently fails, a partner payout that is off by a rounding error — these are not bugs you fix in the next release, they are bugs you apologize for.
We have come at it from all three directions: a checkout sitting over somebody else’s system of record, an entire billing domain built from nothing, and a live pipeline moved between processors with paying customers on it the whole time.
What a billing system has to get right.
Six stages, and money can be wrong at every one of them. Each is something we have shipped and then had to keep correct while it was charging people.
Taking the order
The form where somebody stops being a visitor and becomes a paying customer. It has to price correctly in their currency, apply the right promotion, screen for fraud, and be wrong about none of it.
GoCart took every sale A2 Hosting made for sixteen months and kept no database of its own, so every price a customer saw came from the system that was going to charge them. The sequence is create, then check, then charge: an order that exists and has not been charged is a support ticket, while a card charged against an order that should not exist is a refund, a chargeback, and a conversation with a processor. Its pricing endpoint finds the best live promotion across the whole cart without the customer typing a code.
Charging on a schedule
Renewals, invoices, and the billing run that has to be right every night without anybody watching it.
Helix moved an order from open to submitted to invoiced to paid as explicit state transitions, so a checkout that failed partway stopped at its last good state and resumed instead of unwinding. Invoices could be paid from account credit without touching a gateway, and charging respected account state — an account frozen or awaiting review was skipped with a logged reason rather than quietly charged. We bill our own platform through Recurly — checkout, renewals, and upgrades — and open-sourced the CLI we run it with. We have built metered pricing in Helix and in Synco, the Site5 billing system Wormhole emptied into it, though most of what we have billed settled on flat recurring.
When a payment fails, and when money goes back
Declines, retries, refunds, credits, and voids. Every one of them ends in a conversation with a customer, so the system has to be able to explain itself.
Refunds, voids, credits, and coupons were modeled explicitly in Helix rather than as adjustments to a balance, and we carried a fork of the payment library to add refunding against a stored card token on the older gateway — a patched dependency taken on knowingly rather than redesigning refunds around a gap in it. When GoCart could not charge the card it still returned success with the underlying message attached, because the order existed and saying otherwise would have been a lie that lost a real sale.
Many brands, many currencies
One deployment billing on behalf of several companies, in more than one currency, through different merchant accounts — and every partner able to audit their own numbers.
Each Helix brand carried its own encrypted gateway credentials, and a separate record mapped currency to merchant account, so the same code charging the same card routed through a different merchant depending on whose storefront the customer had walked in through. Amounts were a type rather than a float, each one carrying its currency. Partner economics lived in the schema — payout records, per-service calculations, and a ledger — because a partner reselling your platform is entitled to a number they can check.
Moving it while it is running
Changing processors, or moving customers onto a different billing system, without anyone noticing. Once a card has been charged there is no rollback, because the outside world has already changed.
Both gateways ran live at once while Helix moved from Vantiv to Braintree across every brand, selecting per brand on which credentials were configured, and without interrupting active billing for anyone. Wormhole moved roughly ten thousand customers off three legacy installs, one brand per announced window, reconciled on account counts, balances, and recurring revenue.
Knowing what happened
The reporting on top: what sold, what was refunded, what a partner is owed, and which orders a human needs to look at.
The admin application in Helix carried order review, fraud triage, financial reporting, and refunds, and its partner panel let partners pull their own revenue reports against the same ledger that calculated their payouts. Reporting that disagrees with the billing run is worse than no reporting, so both read the same models.
Three of them, written up in full.
GoCart
A2 Hosting's checkout — the page every sale went through — rebuilt as a Laravel application with no database of its own, talking to a custom WHMCS addon over HTTP. Two years of work, sixteen months live, and a front end owned by a different team on a different cadence, which turns out to be the whole story.
Read case studyHelix
The billing system and hosting control plane behind three companies over eight years — Rails on one side getting money right, a fleet of Linux servers on the other running thousands of WordPress sites, and a white-label brand model that let dozens of partners sell the whole thing as their own.
Read case studyWormhole
Roughly ten thousand customers moved off three separate WHMCS installations and into Helix — one JSON file per record, one brand per maintenance window, and no billing incident anyone had to apologize for. Two and a half years, a team of ten. The interesting part is everything we decided not to carry.
Read case studyBuilt to be exactly right.
The interesting decisions in a billing system are almost all about what happens when something fails. Helix charged invoices outside the state machine on purpose: if the transition failed, the machine would roll back the whole transaction, including the payment record documenting that we had just taken the customer’s money. An invoice stuck in the wrong state is a support ticket. Losing the record of a charge is something else entirely.
The rest follows the same instinct. Card numbers moved through the Wormhole migration encrypted end to end, never a plain string in a log line or an export, and where our own schema wanted a stored CVV, the card network rules won instead. Every migrated record kept a ledger entry mapping its legacy identifier to its new one, in both directions. And the reporting reads the same models as the billing run, because a dashboard that disagrees with the invoices is worse than no dashboard at all.
What we build with
Payment gateways, and swapping them live.
The one place this practice goes deeper.
Integrating a processor is the straightforward half. Migrating one while it is charging real cards is the half that needs a plan, a way back, and a period where both are live and either can take the transaction. We moved Helix from Vantiv to Braintree that way, brand by brand, with per-brand merchant accounts throughout. GoCart ran Stripe and PayPal alongside bank transfer, with fraud scoring that reads the real client address from behind Cloudflare rather than trusting whatever reached the application last.
If you are building something that charges people, or you have one already and it has started losing money in ways nobody can quite account for, we have probably met the problem before.