Usage-based billing and invoicing for a wholesale telecom business
US wholesale telecom business (name withheld)
Built before Veldway. Delivered by the people behind Veldway as engineers on another company's team, not as a Veldway engagement. Client, product, and partner names are withheld; the role and period are stated below.
The problem
Wholesale telecom billing is unforgiving. Each product line bills differently: some on metered usage, some on monthly recurring charges, some on one-time fees, all against carrier-specific rates and factors. Every invoice line needs the right tax treatment, every customer expects the invoice on time in the format they receive it, and finance needs to see what was billed, what was paid, and what is outstanding. Mistakes surface as disputes, credits, and lost revenue.
Requirements
- Generate invoices for every product line each bill cycle from usage, recurring, and one-time charges
- Apply tax to every invoice line before anything is issued
- Render PDF invoices and deliver them by email and by SFTP, depending on the customer
- Import payments and adjustments in bulk rather than one record at a time
- Report revenue, outstanding balances, and revenue by state, with Excel exports
- Keep each business line's data separate
The solution
A Ruby on Rails backend in which billing runs as background jobs per bill cycle and product line. Invoice logic for each product line sits on a shared pipeline for rating, tax, PDF rendering, and delivery, so a new product line adds its own charge rules without re-implementing the rest. Long-running work such as imports and exports reports live progress to the browser, changes to billing records are audited, and each business line runs against its own database.
Architecture
Rails API on PostgreSQL with Sidekiq for scheduled and background work — dozens of jobs covering invoice runs, imports, exports, and reports. Usage summaries, rate tables, and recurring and one-time charges feed per-product-line invoice builders; every line passes through tax calculation via Avalara before a PDF is rendered and delivered by email or SFTP. Websocket channels stream job progress to the admin UI. Authentication runs through Auth0, and the application ships as a Docker image built and scanned in CI.
Challenges & resolutions
Product lines bill in fundamentally different ways, and a change for one must not break another.
Resolution — Each product line has its own invoice rules on top of one shared pipeline for tax, PDF rendering, and delivery, so changes stay contained to the product line they belong to.
Billing runs, imports, and exports take minutes, and users need to know they are working.
Resolution — All heavy work runs as background jobs, with progress streamed to the browser over websockets instead of leaving users on a spinning request.
A daily aggregate job was re-scanning a very large usage table on every run.
Resolution — The job was rewritten to read persisted aggregates instead of rescanning raw rows, and frequently repeated lookups were stored and cached.
A redesigned invoice layout must not change a single amount.
Resolution — An invoice preview mode, plus a comparison job that reads the previous cycle's invoice PDFs and produces a variance workbook against the preview before anything is finalised.
Why this matters for your billing system
Billing code is where a business's revenue is calculated, so it rewards a specific kind of discipline: every rule written down, every run repeatable, every change provable before it reaches a customer's invoice.
Billing is a pipeline, not a script. Charges are collected, rated, taxed, rendered, delivered, and reported as separate steps. Keeping them separate is what makes it possible to change one product line's rules, or the invoice layout, without touching how tax or delivery works.
Prove a change before it bills. The preview-and-compare approach — render the new invoice, compare it line by line with what the customer received last cycle — turns "we think the redesign didn't change any amounts" into a spreadsheet anyone in finance can check.
Make long work visible. Billing runs, imports, and exports are slow by nature. Running them in the background with live progress is the difference between a finance team that trusts the system and one that runs it twice.
How we apply this
The same patterns carry over to any Rails application that bills customers: subscription and usage billing, invoicing and payment imports, tax integration, and the reports finance needs at month end. That is the work Veldway's Rails and billing and payment integration services are built around.
What was delivered
- Monthly invoices for every product line generated by background jobs, with tax applied to each line and PDFs delivered by email or SFTP
- Payments and adjustments imported in bulk from spreadsheets, with live progress
- Revenue, outstanding-balance, and revenue-by-state reports with Excel exports for finance
- Invoice previews compared against previous invoices before a cycle is finalised
- Deployment moved to Docker images built and vulnerability-scanned in CI, with a written cutover runbook
Have a similar problem?
Describe the problem in plain language — broken, slow, manual, or missing. We'll tell you honestly whether and how we can help.
- 01We reply within one business day
- 02A short call to understand the problem
- 03A written scope and fixed quote — no obligation