Invoicing
rochen-api owns every Dedicated invoice. Nothing is charged in advance: compute, storage, IOPS, throughput and data transfer are metered per hour from the change log and billed at the end of each month. Sites on the pool and the included software never appear on an invoice. WHMCS never sees product 464 or its sites as due.
Timeline
- Order · 3 Octno invoice: nothing is charged in advance. The gate is a stored card and no overdue invoices.
- Deployed · activation 3 Oct 10:00the
createchange is applied; billing starts here, not at launchusage accrues hourly from this point · every applied billable change starts a new span - Resize · 18 Oct 14:00new span at the new template rate; no invoice
- Disk grow · 25 Oct 09:00new span at the new allocation; no invoice
- Sites added, licences re-tieredlogged in the change log, never billed
- Monthly invoice · generated 1 Novall of October's usage for every pool the client owns, one invoice, due on the account's usual terms
billed_through→ 31 Oct ·nextduedateof pool and sites advanced past WHMCS's window - Every month aftereach run bills the complete calendar months since
billed_through - Terminationfinal invoice: usage from
billed_throughtoterminated_at, including the partial data-transfer month
Usage is billed by complete calendar months, as Fully Managed Services does today. A run on 1 Nov bills all of October; the days of November wait for the next run.
At order time
No invoice
The pool's tblhosting.amount is 0.00 like every Rochen One service. CreateInvoice::run() finds nothing to bill; the Dedicated branch of CreateInvoiceItemHosting never emits a line for product 464 or for pool sites.
The gate instead
OrderController::paymentMethodRefusal() extends to Dedicated::handles(): a stored card is required and manual gateways are refused, as for Rochen One. The account must have no overdue invoices, and the existing fraud checks apply. Then the deploy job is dispatched at once.
Why this is safe enough
Capture happens monthly with the card on file; non-payment leads to suspension (instance stopped) and then termination through the normal overdue flow, the same as a Rochen One site that stops paying.
Monthly invoice — dedicated:invoice-renewals
active, suspended or terminating whose billed_through is before the last complete calendar monthbilled_through, via the existing CreateInvoice::run() with Hosting-type itemsbilled_through to the end of the last month billed; nextduedate of each pool and each of its Dedicated Site services by one month so WHMCS's selection never contains themCaptureOutstandingInvoicesJob, the existing card-capture jobtblinvoiceitems row for a pool or pool site was not created by this commandUsage from the change log
No separate billing table. Every applied change that affects price (create, resize_instance, resize_disk, resize_iops_throughput, terminate) is billable and stores the complete configuration in to. Its immutable applied_at is the boundary of a usage span. Licence re-tiers and site events are logged with billable = false and never start a span.
- Load the billable changesfor the pool, ordered by
applied_at, plus the last one before the month starts (carried forward so the month opens with a known configuration). - Cut spans at month edgesspan = max(applied_at, month start) → min(next applied_at, month end). Hours = whole hours in the span.
- Price each span from the pool's own sub-option pricescompute:
monthly ÷ 744 × hours· IOPS:(IOPS − 3000) ÷ 100 × monthly ÷ 744 × hours· throughput:(MiB/s − 125) ÷ 25 × monthly ÷ 744 × hours. - Price storage across the client's poolssee Storage tiers below; hours-prorated the same way.
- Add data transferfrom
dedicated_instances.data_transfer["YYYY-MM"]: the 500 GB allowance (msetupfee) plus(out − 500) × monthlyabove it. - Group into linesconsecutive spans with the same template merge into one line; the same for each storage allocation. Descriptions follow the Rochen One style.
Storage tiers
DiskSize [1-5], [6-20], [21-50], [51-100], [101-200], [201-*], in the client's currency), walked over the client's total dedicated allocation for that hour. With two pools of 200 GB and 300 GB, the first 200 GB fill the lower bands and the next 300 GB continue from where they left off; each pool's line shows its own GB at the rate it landed in.Shared-site storage keeps being billed by WHMCS on the R1 invoice. Rolling it into the same band as dedicated storage would need either WHMCS billing code or rochen-api reproducing WHMCS's bands, so v1 tiers dedicated storage on its own. This is flagged as an open point to confirm with Chris.
Worked example (London, example prices, USD)
One pool, activated 3 Oct 10:00 on r1d.m7i.large with 200 GB. Resized to r1d.m7i.xlarge on 18 Oct 14:00. Storage grown to 300 GB on 25 Oct 09:00. Twelve sites added during the month (free). Invoice generated 1 Nov.
| Span | From → to | Hours | Template | Storage | Compute | Storage |
|---|---|---|---|---|---|---|
| 1 | 03 Oct 10:00 → 18 Oct 14:00 | 364 | m7i.large · 86.35/mo | 200 GB · 23.60/mo | 42.25 | 11.55 |
| 2 | 18 Oct 14:00 → 25 Oct 09:00 | 163 | m7i.xlarge · 161.95/mo | 200 GB · 23.60/mo | 35.48 | 5.17 |
| 3 | 25 Oct 09:00 → 01 Nov 00:00 | 159 | m7i.xlarge · 161.95/mo | 300 GB · 31.60/mo | 34.61 | 6.75 |
| Invoice generated 1 Nov | Type | Amount |
|---|---|---|
| Rochen One Dedicated – r1d.m7i.large – srv1.directrouter.com (03/10 – 18/10, 364 Hours) | Hosting · compute | 42.25 |
| Rochen One Dedicated – r1d.m7i.xlarge – srv1.directrouter.com (18/10 – 01/11, 322 Hours) | Hosting · compute | 70.09 |
| 200GB SSD (Storage) – srv1.directrouter.com (03/10 – 25/10, 527 Hours) | Hosting · storage | 16.72 |
| 300GB SSD (Storage) – srv1.directrouter.com (25/10 – 01/11, 159 Hours) | Hosting · storage | 6.75 |
| Data Transfer – 320GB of 500GB allowance – October 2026 | Hosting · transfer | 64.80 |
| Subtotal before tax | 200.61 |
Hours in October from 3 Oct 10:00 add up to 686, matching the three spans. Compute: 79.95 × 1.08 region factor. Storage bands (example): 0.20 / 0.16 / 0.14 / 0.12 / 0.10 / 0.08 per GB per month, so 200 GB = 23.60 and 300 GB = 31.60. No lines for the twelve sites, cPanel Pro, Imunify360 30 users or LiteSpeed Web Host Essential: all included.
Line rules
Compute
One Hosting-type line per template run per month, hours-prorated from the region's Templates price.
Storage
One line per allocation per month at the band rate it landed in across the client's pools.
Extras
IOPS and throughput only when above the gp3 baseline; data transfer as the allowance plus overage from Cost Explorer via dedicated:log-data-transfer (daily 01:30; a new account reports roughly a day late).
Never
Sites, software licences, management, DNS, backups, monitoring: all included in the compute rate.
Costs card in the app
Dedicated::costs() runs the same maths over the current month for "spend so far" and "estimated end of month", per pool and for the account.
Currency
Prices resolve from the pool's stored sub-option ids and the R1 storage sub-options in the client's currency. A currency with the −1 sentinel is refused at order.
Resizes, suspension
| Event | Effect on billing |
|---|---|
| Template change | the applied change opens a new span at the new compute rate |
| Storage grow | new span at the new allocation; allocations never shrink |
| IOPS / throughput change | new span; only the amount above baseline is charged |
| Site added or removed, licence re-tiered | none |
| Suspension (overdue) or client stop | usage keeps accruing: the instance and volumes stay reserved. The app states this next to Stop. |
Final invoice at termination
The termination job's last step marks the terminate change applied (the end of the timeline) and runs the same routine for billed_through → terminated_at, including the partial data-transfer month, then sets the pool and its site rows Terminated. If nothing is owed, no invoice is created.
Keeping WHMCS out
The pool
WHMCS's invoice run selects hostings by nextduedate. The monthly command advances the pool's date past the CreateInvoiceDaysBefore window, and the pool's amount is 0.00, so even a missed run produces nothing.
Pool sites
Services of the always-free product Rochen One Dedicated Site with parent_id = pool, every price −1, amount 0.00 and no rochen_one_logs rows. They are not children of the R1 container, so the R1 arrears routine never counts them; their nextduedate is advanced with the pool's.
Detection
The command checks for invoice items on pools or pool sites that it did not create and alerts devops.
Legacy module
The aws module's arrears routine finds no snapshot rows for a pool and adds nothing.