# Tax amount is calculated on correct subtotal (pre-discount vs. post-discount)

- **Pattern:** `ab-001226` (`ecommerce-shipping-tax.tax-computation.discount-application`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001226
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001226)

## Why it matters

Applying a 10% coupon discount after tax rather than before it systematically overcharges US customers. In most US states, sales tax is calculated on the post-discount price — applying tax first and then the discount is incorrect under standard US sales tax rules (CWE-682, ISO 25010:2011 functional correctness). A $100 cart with a $10 coupon should produce $90 × 0.085 = $7.65 tax; applying tax before discount produces $8.50 — a $0.85 overcharge per transaction that compounds across every discounted order. When the discount/tax order differs between cart preview and checkout confirmation, customers see a lower total in the cart and a higher total at payment, which triggers abandonment and disputes.

## Severity rationale

Medium because per-order overcharges from incorrect operation order are small individually but accumulate across every discounted transaction and produce checkout total inconsistencies that customers recognize and flag.

## Remediation

Enforce the correct US order of operations in `lib/checkout.ts`: subtract discount from subtotal before computing tax:

```ts
// lib/checkout.ts — correct US: tax after discount
function calculateOrderTotal(
  items: OrderItem[],
  discountCode: string | undefined,
  address: Address
): OrderSummary {
  const subtotal = items.reduce((sum, i) => sum + i.price * i.quantity, 0)
  const discount = discountCode ? applyDiscount(subtotal, discountCode) : 0
  const subtotalAfterDiscount = subtotal - discount
  // Tax is computed on the post-discount taxable amount
  const tax = calculateTax(items, address, subtotalAfterDiscount)
  const shipping = calculateShipping(items, address)
  const total = subtotalAfterDiscount + tax + shipping
  return { subtotal, discount, tax, shipping, total }
}
```

Add a code comment citing the jurisdiction rule. If cart preview uses a separate path, consolidate both onto the same `calculateOrderTotal` function so the operation order is never divergent.

## Detection

- **ID:** `discount-application`
- **Severity:** `medium`
- **What to look for:** List all steps in the order total calculation function in sequence: (1) subtotal, (2) discount application, (3) tax calculation, (4) shipping, (5) total. Classify the order of operations: is tax applied to the pre-discount or post-discount subtotal? Count how many calculation paths exist (e.g., checkout vs cart preview).
- **Pass criteria:** Tax is calculated on the post-discount subtotal for US-based stores (the standard approach), with the discount applied before the tax calculation step. The order of operations is consistent across at least 2 calculation paths (cart summary and checkout confirmation). A code comment or documentation note explains the jurisdiction rule if a non-standard approach is used.
- **Fail criteria:** Tax is applied to the pre-discount subtotal in a US-based store (overcharging the customer), or the discount/tax order differs between cart and checkout calculation paths.
- **Skip (N/A) when:** The business does not offer discounts (no discount/coupon code logic found) or does not collect sales tax.
- **Detail on fail:** `"Tax applied before discount in lib/checkout.ts: subtotal ($100) → tax ($8.50) → discount ($10) → total. Customer overcharged by $0.85."` or `"Cart calculates tax post-discount but checkout calculates pre-discount — inconsistent across 2 paths."`
- **Remediation:** Apply discounts before tax (US standard) in `lib/checkout.ts`:

  ```ts
  // US: Tax after discount (standard)
  function calculateOrderTotal(items: OrderItem[], discountCode?: string, address?: Address): OrderSummary {
    const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0)
    const discount = discountCode ? applyDiscount(subtotal, discountCode) : 0
    const subtotalAfterDiscount = subtotal - discount
    const tax = calculateTax(subtotalAfterDiscount, address, items)
    const shipping = calculateShipping(items, address)
    const total = subtotalAfterDiscount + tax + shipping

    return { subtotal, discount, tax, shipping, total }
  }
  ```

## External references

- cwe CWE-682
- iso-25010 functional-correctness
- external us-state-sales-tax-discount-timing — https://www.cdtfa.ca.gov/formspubs/pub31.pdf

Taxons: data-integrity

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