# Discount logic correctly applies percentage or fixed amounts

- **Pattern:** `ab-001124` (`ecommerce-catalog.variant-pricing.discount-logic`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001124
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001124)

## Why it matters

Incorrect discount arithmetic produces wrong prices in every context where the formula runs — cart totals, checkout confirmations, order emails, and refund calculations. The canonical bug: subtracting `percentOff` as a dollar amount instead of a fraction (charging `$100 - 20 = $80` when the intent is `$100 × 0.80 = $80` — those happen to be equal, masking the bug until `10% off $50` yields `$50 - 10 = $40` instead of `$45`). A fixed discount that allows negative final prices means a `$200 off` coupon on a `$150 product` produces a `-$50` cart that Stripe rejects or, worse, creates a refund. ISO 25010:2011 maintainability is the external mapping — calculation logic must be testable and auditable.

## Severity rationale

Low because incorrect discount logic is detectable before release via manual testing, affects a bounded set of promotional transactions, and does not expose a security vector.

## Remediation

Implement all discount types in a single utility in `src/lib/pricing.ts` with explicit floor guards to prevent negative prices.

```ts
export const applyDiscount = (
  basePrice: number,
  discount: { type: 'percent' | 'fixed'; amount: number }
): number => {
  if (discount.type === 'percent') {
    return Math.max(0, basePrice * (1 - discount.amount / 100))
  }
  return Math.max(0, basePrice - discount.amount)
}
```

For stacked discounts, apply percentage discounts first, then fixed amounts — document the order explicitly in `src/lib/pricing.ts` so it doesn't become implicit tribal knowledge.

## Detection

- **ID:** `discount-logic`
- **Severity:** `low`
- **What to look for:** Count all discount or promotion calculation functions in the codebase (search for `discount`, `promo`, `coupon` in `src/lib/`, `src/utils/`, and `src/app/api/`). For each function found, enumerate the discount types supported (percentage, fixed amount, BOGO, tiered). Test the logic with at least 2 sample values mentally to confirm correctness (e.g., 20% off $100 should yield $80, $5 off $100 should yield $95).
- **Pass criteria:** Discount calculations are mathematically correct for all supported discount types. Percentage discounts reduce the price by the correct fraction, fixed discounts subtract the correct amount, and combined discounts apply in the correct order. Report even on pass: "X discount functions found, Y discount types supported."
- **Fail criteria:** Discount calculations are incorrect (e.g., subtracts percentage as dollar amount, applies discount twice, allows negative final price).
- **Skip (N/A) when:** No discount functionality exists in the project — confirmed by finding 0 discount-related functions, no coupon model, and no promotion logic in the codebase.
- **Cross-reference:** For promotional compliance, the FTC Consumer Protection audit covers truthful advertising and discount disclosure.
- **Cross-reference:** For coupon code security, the API Security audit covers input validation and rate limiting on promotion endpoints.
- **Cross-reference:** For discount stacking logic, the Subscription Compliance audit covers pricing transparency in recurring charges.
- **Detail on fail:** `"Discount function in src/lib/pricing.ts subtracts percentage as a dollar amount instead of a fraction. 20% off $100 = $80 instead of $99.80"` or `"Fixed discount in src/utils/discount.ts can produce negative prices — no Math.max(0, ...) guard"`
- **Remediation:** Implement correct discount logic in `src/lib/pricing.ts`:

  ```ts
  const percentDiscountedPrice = basePrice * (1 - percentOff / 100)
  const fixedDiscountedPrice = Math.max(0, basePrice - fixedAmount)
  const combinedPrice = Math.max(0, basePrice * (1 - percentOff / 100) - fixedAmount)
  ```

---

## External references

- iso-25010 maintainability

Taxons: code-quality

HTML version: https://auditbuffet.com/patterns/ab-001124
