Designing a Discount-Validation API: Catching Stacked-Coupon Math Errors Before They Hit Production
If you've ever wired up a promo engine, you know the cheap-looking line items are where the bugs hide. A single product with three sequential percentage discounts — "20% off, then 10% off, then a 15% loyalty coupon" — is mathematically distinct from a flat 45% off, and most homegrown calculators get it wrong in the same three or four ways. This article walks through the engineering side of that…
Stacked percentage discounts are not additive due to the nature of percentage calculations. A naive implementation that simply calculates price * (1 - (d1 + d2 + d3)) fails because successive discounts multiply the remaining price rather than the original price. This can lead to significant discrepancies when the customer sees the final amount. A better approach is to model discounts as a list of (factor, label) tuples, calculating the result in order and showing each step in the response for the front end to display.
Another common issue is order-dependence. While some discounts like BOGO or fixed-amount coupons may seem commutative, adding them to the mix can change the outcome. It's crucial to decide on the order at the data layer and document it in the API spec so the front-end team doesn't rearrange coupons client-side.
The core calculation involves computing final = price × Π (1 - d_i) − Σ fixed_j clamp(final, 0, price). This formula accounts for percentage discounts and fixed-amount coupons, with a clamp to ensure the final result is never negative. Failure to implement a clamp has resulted in bugs where customers received a credit they shouldn't have.
Rounding is another important consideration, especially given jurisdictional requirements for rounding half-away-from-zero to the nearest cent. Intermediate steps can be rounded differently as long as the consistency is documented, as auditors may ask about it. Using integer cents (or a Decimal type) and only converting to a display string at the boundary helps avoid floating-point arithmetic issues.
To ensure the discount calculator works correctly before shipping, a unit-test checklist should include testing single percentages, two and three stacked percentages, fixed amounts, negative-result clamping, zero-percent edge cases, full-discount edge cases, currency rounding, mutually exclusive coupons, and an unchanged price for an empty discount list.
By systematically going through these test cases, you can uncover issues like additive math, premature rounding, or missing clamps, which are usually easy to fix once identified.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.