Jump to content

Bug: Non-applicable promotion code silently attached to domain on cart renewal, causing incorrect discounts on future renewal invoices


Tam Pham

Recommended Posts

 Summary

  When a client renews a domain through the shopping cart while ANY valid promotion code is present in the cart session, WHMCS stores that promotion's ID in `tbldomains.promoid` — even when the promotion does NOT apply to the domain's TLD (the cart correctly shows "no items qualify" and no discount is given on the renewal order).

  One billing cycle later, the automated renewal invoice for that domain is generated with a `PromoDomain` discount line item for that promotion. This happens even though the promotion's "Applies To" configuration contains no TLDs, and even after the promotion's expiry date has passed — so the client receives a recurring discount from a promotion that was never valid for domains and is no longer active.

  Environment

  - WHMCS 8.13 (reproduced on demand; also observed on earlier 8.x)
  - Promotion: Percentage, Recurring enabled, "Applies To" contains hosting products only (no TLDs)

  Steps to Reproduce

  1. Create a recurring percentage promotion (e.g. 5%) whose "Applies To" contains only hosting products — no domain TLDs.
  2. As a client with an active domain, start a domain renewal in the cart.
  3. Apply the promotion code. The cart correctly shows: "The promotion code you entered has been applied to your cart but no items qualify for the discount yet" and the discount line shows 0.
  4. Complete checkout. The renewal invoice is generated at full price with no discount line — correct so far.
  5. Inspect the database: `SELECT promoid FROM tbldomains WHERE id = <domain>`
     → the promotion's ID has been stored, despite the promotion not applying.
  6. Wait for (or trigger) the next automated renewal invoice generation for this domain.

  Expected Result

  The promotion is never associated with the domain (it does not apply to the TLD), and future renewal invoices are generated at full price.

 Actual Result

  The next automated renewal invoice contains a `PromoDomain` line item discounting the renewal (e.g. "Promotion Code: XXXX - 5% Recurring Discount"). This repeats every renewal cycle. The discount is applied even after the promotion's expiry date has passed, and even though the promotion's "Applies To" has never included any TLD — so neither appears to be validated when the renewal invoice is generated.

  The issue is also effectively invisible to auditing: because the promotion was not applied at checkout, `tblorders.promocode` remains empty, and no activity log entry records the change to `tbldomains.promoid`.

  Real-World Occurrence (our production data)

  - 2024-09: Domain registered normally, no promotion (order promocode empty, promoid = 0).
  - 2025-08: Client renewed the domain via cart with a hosting-only recurring promo code in the cart session. Renewal invoice was full price, order promocode empty — but `tbldomains.promoid` was set to that promotion.
  - 2026-07: Cron-generated renewal invoice included a PromoDomain discount line for that promotion — which had also expired in 2025-08.

  We then reproduced this on demand with a second domain and a different non-applicable promo code: the cart showed "no items qualify", the checkout invoice had no discount, yet `tbldomains.promoid` was written, and a forced next-cycle invoice generation produced the erroneous PromoDomain discount line.

 Suggested Fix

  1. During cart domain renewal checkout, only associate the promotion with the domain when the promotion actually applied to that domain's TLD/period — i.e. the same situations in which the order itself records the promotion code and a discount is given.
  2. During automated invoice generation, validate the stored promotion against its "Applies To" configuration and expiration date before adding a PromoDomain line item.

  Related history for context: Per the published changelogs, CORE-7379 (7.1) introduced applying recurring promotion codes to renewal invoices; CORE-14101 (8.1) fixed the one-time promo path for domain renewals. The recurring path described above remains affected.

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