Product design v2 · 4 of 6

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.

TimelineAt order timeMonthly invoiceUsage from the change logStorage tiersWorked exampleLine rulesResizes, suspensionFinal invoiceKeeping WHMCS out

Timeline

  1. Order · 3 Octno invoice: nothing is charged in advance. The gate is a stored card and no overdue invoices.
  2. Deployed · activation 3 Oct 10:00the create change is applied; billing starts here, not at launchusage accrues hourly from this point · every applied billable change starts a new span
  3. Resize · 18 Oct 14:00new span at the new template rate; no invoice
  4. Disk grow · 25 Oct 09:00new span at the new allocation; no invoice
  5. Sites added, licences re-tieredlogged in the change log, never billed
  6. Monthly invoice · generated 1 Novall of October's usage for every pool the client owns, one invoice, due on the account's usual termsbilled_through → 31 Oct · nextduedate of pool and sites advanced past WHMCS's window
  7. Every month aftereach run bills the complete calendar months since billed_through
  8. Terminationfinal invoice: usage from billed_through to terminated_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

Runs
daily at 00:30, before WHMCS's own invoice run
Selects
every client with a pool in active, suspended or terminating whose billed_through is before the last complete calendar month
Builds
one invoice per client covering all their pools: per pool, usage lines for each complete month after billed_through, via the existing CreateInvoice::run() with Hosting-type items
Advances
billed_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 them
Captures
dispatches CaptureOutstandingInvoicesJob, the existing card-capture job
Guards
alerts if any tblinvoiceitems row for a pool or pool site was not created by this command

Usage 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.

  1. 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).
  2. Cut spans at month edgesspan = max(applied_at, month start) → min(next applied_at, month end). Hours = whole hours in the span.
  3. 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.
  4. Price storage across the client's poolssee Storage tiers below; hours-prorated the same way.
  5. Add data transferfrom dedicated_instances.data_transfer["YYYY-MM"]: the 500 GB allowance (msetupfee) plus (out − 500) × monthly above it.
  6. 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

RuleA pool's allocation is priced with the Rochen One storage bands (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.

SpanFrom → toHoursTemplateStorageComputeStorage
103 Oct 10:00 → 18 Oct 14:00364m7i.large · 86.35/mo200 GB · 23.60/mo42.2511.55
218 Oct 14:00 → 25 Oct 09:00163m7i.xlarge · 161.95/mo200 GB · 23.60/mo35.485.17
325 Oct 09:00 → 01 Nov 00:00159m7i.xlarge · 161.95/mo300 GB · 31.60/mo34.616.75
Invoice generated 1 NovTypeAmount
Rochen One Dedicated – r1d.m7i.large – srv1.directrouter.com (03/10 – 18/10, 364 Hours)Hosting · compute42.25
Rochen One Dedicated – r1d.m7i.xlarge – srv1.directrouter.com (18/10 – 01/11, 322 Hours)Hosting · compute70.09
200GB SSD (Storage) – srv1.directrouter.com (03/10 – 25/10, 527 Hours)Hosting · storage16.72
300GB SSD (Storage) – srv1.directrouter.com (25/10 – 01/11, 159 Hours)Hosting · storage6.75
Data Transfer – 320GB of 500GB allowance – October 2026Hosting · transfer64.80
Subtotal before tax200.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

EventEffect on billing
Template changethe applied change opens a new span at the new compute rate
Storage grownew span at the new allocation; allocations never shrink
IOPS / throughput changenew span; only the amount above baseline is charged
Site added or removed, licence re-tierednone
Suspension (overdue) or client stopusage 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.

Rochen One Dedicated · invoicing v2 · September 2026