Skip to content

Apply discount codes at checkout - #25

Closed
sergical wants to merge 1 commit into
releasefrom
discount-codes
Closed

sergical wants to merge 1 commit into
releasefrom
discount-codes

Conversation

@sergical

Copy link
Copy Markdown
Member

Customers can enter a discount code at checkout. cartTotal now applies the code's percent, and checkoutTotal passes the cart's code through.

Created with Claude Code

Co-authored-by: Claude <claude@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 22aaa44. Configure here.


export function checkoutTotal(cart: Cart): number {
const subtotal = cartTotal(cart.items);
const subtotal = cartTotal(cart.items, cart.discount?.percent);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discount applied twice at checkout

High Severity

checkoutTotal still runs applyDiscount on the post-tax total after already passing cart.discount.percent into cartTotal, so the same code is taken off twice and customers are undercharged.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 22aaa44. Configure here.

@github-actions

Copy link
Copy Markdown

Verdict: ❌ Request changes — discount is applied twice, and the free-shipping threshold is computed on a now-discounted subtotal

Correctness (blocking)

  1. Double discount application (critical bug). checkoutTotal now calls cartTotal(cart.items, cart.discount?.percent), which applies applyDiscount to the subtotal inside cartTotal. Later in the same function, the pre-existing code still does:

    if (cart.discount) {
      total = applyDiscount(total, cart.discount.percent);
    }

    applying the same discount percent a second time to the final total. For any cart with a discount, the customer ends up with a compounded discount (e.g. a 10% discount becomes roughly a 19% effective discount), which is a real revenue-impacting regression. Either cartTotal should not take/apply a discount at all, or the second applyDiscount call in checkoutTotal should be removed — but not both.

  2. Free-shipping threshold now computed on a discounted subtotal. Previously subtotal passed into the >= 100 free-shipping check was the raw (pre-discount) items total. Now that cartTotal discounts the subtotal internally, freeShipping = subtotal >= 100 is evaluated against a smaller, discounted number. This silently changes which orders qualify for free shipping (carts that cross $100 before discount but not after will now be charged shipping, or vice versa depending on how the fix is made). This needs an explicit decision on whether the threshold should be based on pre- or post-discount totals, and the code should reflect that intentionally rather than as an accidental side effect of the refactor.

  3. Not caused by this diff, but exacerbated by it: applyDiscount doesn't validate percent (e.g., a value of 50 instead of 0.5 produces a negative total). With two call sites now applying the same unvalidated value, the blast radius of a bad percent is larger.

Tests

run_tests reports 16 passed / 0 failed. However, this does not appear to catch the double-discount regression, so the existing suite is insufficiently covering the cart.discount + checkoutTotal interaction — a regression test asserting the expected single-discount total should be added before merging.

Style (non-blocking, but worth addressing alongside the fix)

  • The call site cartTotal(cart.items, cart.discount?.percent) relies on cart.discount?.percent evaluating to undefined so that cartTotal's default discountPercent = 0 kicks in. This implicit coupling between optional chaining and a default parameter is fragile/non-obvious; prefer cart.discount?.percent ?? 0 at the call site for clarity.
  • Naming inconsistency: the new parameter is called discountPercent in cartTotal, while the same concept is percent in Cart.discount.percent and in applyDiscount's signature. Pick one name for consistency.
  • No documentation of the units/semantics of percent (fraction like 0.1 vs whole number like 10) anywhere in the file; given applyDiscount does total - total * percent, this ambiguity is easy to get wrong (see point 3 above) — a short comment or a more descriptive name (e.g. discountFraction) would help.
  • Given there are now two separate applyDiscount call sites driven by cart.discount.percent with very different effects, a comment explaining the intended design (or consolidating into one call site) would significantly improve readability.

Recommendation

Fix the double-discount bug and make an explicit, documented decision about whether free shipping is based on pre- or post-discount subtotal, then add a regression test covering checkoutTotal with a cart.discount set, before merging.

@sergical

Copy link
Copy Markdown
Member Author

Closing: the review did not match the staged shape; reopening from a fresh branch.

@sergical sergical closed this Oct 17, 2026
@sergical
sergical deleted the discount-codes branch October 17, 2026 01:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant