Shipping and tax are recalculated on checkout address change
Why it matters
A checkout form that recalculates shipping but not tax when the customer changes their state leaves the tax line frozen at the California rate even after the customer moves to New York. This is a CWE-682 error in the address change flow — the system accepted new data but used it incompletely. ISO 25010:2011 time-behaviour applies as well: a debounced recalculation that updates both shipping and tax within 500ms of an address change is a correctness requirement, not a UX nicety. When the tax line doesn't update until form submission, the customer pays a different amount than shown — which produces payment disputes. Requiring an "Update" button click instead of automatic recalculation is equally broken: customers who don't click it proceed to payment with stale values.
Severity rationale
High because incomplete recalculation on address change means customers proceed to payment with a visible total that differs from what they will actually be charged, producing disputes and eroding checkout conversion.
Remediation
Add a debounced handler in components/CheckoutForm.tsx that triggers all three calculations — shipping, tax, and total — on any address field change:
// components/CheckoutForm.tsx
export function CheckoutForm() {
const [address, setAddress] = useState({})
const [summary, setSummary] = useState({ shipping: 0, tax: 0, total: 0 })
const recalcRef = useRef<ReturnType<typeof setTimeout>>()
const handleAddressChange = (field: string, value: string) => {
const newAddress = { ...address, [field]: value }
setAddress(newAddress)
clearTimeout(recalcRef.current)
recalcRef.current = setTimeout(async () => {
const [shipping, tax] = await Promise.all([
calculateShippingCost(items, newAddress, selectedMethod),
getTaxAmount(items, newAddress, customer.is_tax_exempt),
])
setSummary({ shipping, tax, total: subtotal - discount + shipping + tax })
}, 500)
}
return (
<form>
<input placeholder="State" onChange={(e) => handleAddressChange('state', e.target.value)} />
<input placeholder="Zip" onChange={(e) => handleAddressChange('zip', e.target.value)} />
{/* checkout summary driven by `summary` state */}
</form>
)
}
Never require a manual "Update" button click for shipping/tax recalculation — the update must be automatic and reflected in the summary without a page reload.
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Shipping & Tax Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe · CWE-682 — Incorrect Calculation
- iso-25010:2011 · functional-correctness — Functional Correctness (Functional Suitability)
- iso-25010:2011 · time-behaviour — Time Behaviour (Performance Efficiency) — recalculation must complete within interactive response time
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ecommerce-shipping-tax·automated