Jump to content

BUG: WHMCS 9+ - Domain renewal re-billing strips line item linkage but leaves original invoice as Unpaid


Recommended Posts

TLDR:

If WHMCS auto-generates a domain renewal invoice, and then a client manually orders a domain renewal (ignoring the auto-generated invoice), the domain renews as expected. In WHMCS 9+ the original system-generated invoice is still left in an "unpaid" state, but the items in that invoice are unlinked.

Note this also happens when WHMCS auto-generates a renewal invoice for a domain, and then later auto-generates an invoice with a redemption fee for the same domain - it will make a new invoice for this and leave the previous one as unpaid and unlinked.

 

Summary

WHMCS 9.0 upgrade introduced a regression in the domain-renewal re-billing flow. Where pre-9.0 the system would transition the original renewal invoice to Cancelled when superseded, in 9.0 it strips the line item's linkage (tblinvoiceitems.type='', tblinvoiceitems.relid=0) but leaves tblinvoices.status='Unpaid'. The original invoice persists indefinitely as an orphan with a now-meaningless line item.

Both the old (correct) and new (buggy) code paths are present in 9.0 and fire concurrently for different triggers.

Evidence of regression from tblactivitylog

Activity log strings fall into two camps:

Log message Effect on parent invoice Status in 9.0
Cancelled Previous Domain Renewal Invoice - Invoice ID: X Invoice transitioned to Cancelled Still firing - correct
Removed Previous Domain Renewal Line Item - Invoice ID: X Invoice transitioned to Cancelled Stopped firing in 9.0 - apparently replaced
Cancelled Previous Domain Renewal Line Item - Invoice ID: X Invoice left as Unpaid New in 9.0 - regression
Issued Credit Note for Domain Renewal Line Item - Invoice ID: X Invoice left as Unpaid New in 9.0 - regression

Reproducible triggers

  1. Client-initiated manual renewal via client area while a system-generated renewal invoice is already outstanding. Logs Cancelled Previous Domain Renewal Line Item. New properly-linked invoice is created and typically paid in the same instant via card on file.
  2. Domain entering Redemption Grace Period. System auto-creates a new invoice containing the renewal item plus the redemption fee, and logs Issued Credit Note for Domain Renewal Line Item against the original.

Resulting database state

  • Original invoice: tblinvoices.status='Unpaid', total unchanged.
  • Original line item: type='', relid=0, description and amount preserved.
  • New invoice: created correctly with type='Domain', relid=<tbldomains.id>.

Impact

  • Orphan unpaid invoices accumulating in client portals and overdue reports. The new linked invoice gets paid, the registrar renewal succeeds, but the original invoice persists as Unpaid indefinitely.
  • Risk of double-billing. If both invoices remain Unpaid (e.g. the new one fails to capture), paying the new one renews the domain but the original still shows Unpaid because its line item has no relid to match against. Conversely, paying the original does not push a renewal because the line item is no longer associated with tbldomains.id.

Detection query

SELECT i.id, i.userid, i.status, i.duedate, i.total, ii.amount, ii.description
FROM tblinvoices i
JOIN tblinvoiceitems ii ON ii.invoiceid = i.id
WHERE i.status = 'Unpaid'
AND ii.relid = 0
AND (ii.type = '' OR ii.type IS NULL)
AND ii.description LIKE 'Domain Renewal -%';
 

Suspected cause

The new code path that emits Cancelled Previous Domain Renewal Line Item / Issued Credit Note for Domain Renewal Line Item appears to be missing the UPDATE tblinvoices SET status='Cancelled' step that the pre-9.0 Cancelled Previous Domain Renewal Invoice and Removed Previous Domain Renewal Line Item handlers performed. The line item modification is happening; the parent invoice transition is not.

Likely candidates for review: the manual-renewal handler in the client-area domain UI, and the redemption-grace-period invoice generator. Both were apparently refactored for 9.0 and both are missing the parent-invoice cancellation that the prior versions had.

Reproduction steps

  1. Allow a domain to reach its system-generated renewal invoice creation date so an Unpaid renewal invoice exists with type='Domain', relid=<domain_id>.
  2. As the client, log into the client area and renew the domain via the per-domain "Renew" UI (not the invoice's pay button).
  3. Check tblinvoiceitems for the original invoice: type='', relid=0.
  4. Check tblinvoices.status for the original invoice: still Unpaid.

Severity

Silently accumulating orphan invoices on every WHMCS install upgraded to 9.0 that handles domain renewals. Clients see unpaid invoices in their portal for renewals that have actually been paid via the new linked invoices.

 
 
Link to comment
Share on other sites

Note this issue also affects clients disabling auto-renew. ie if a client disables auto-renew for a domain where an auto-renew invoice has already been generated, WHMCS will strip the linkage in the items on the invoice, but leaves the items there and leave the invoice as unpaid. Overall just a complete mess.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use & Guidelines and understand your posts will initially be pre-moderated