Client-Trusted Tax Calculation: Paying Less by Editing the Total
Tax looks boring until the JSON includes "tax": 8.25 and the server stores whatever you send.
Shops sometimes compute VAT or sales tax in the browser (or a BFF) and post the breakdown with the order. If the payment amount is taken from that client total instead of a server recalculation, you can zero tax—or invent a negative line—and check out cheaper.
Spot the trust boundary
During checkout, inspect payloads for:
taxAmount,vat,salesTax,dutytotal,grandTotal,amountDue- Address fields that should drive tax but are ignored when totals are supplied
Change taxAmount to 0 while leaving the address in a taxed region. Complete payment in a sandbox or with a minimal SKU. If the charged amount matches your edited total, the server trusted you.
I've also seen tax set to zero when country=XX was accepted without validating the ship-to against the payment billing country. That is less "edited field" and more "logic loophole," but the money trail looks the same.
Try taxAmount as a string, a negative number, or a value larger than the subtotal (credit). Weird types sometimes bypass validators that only check >= 0 on numbers.
Marketplace and drop-ship checkouts sometimes post per-line tax arrays. Zero one seller's tax while leaving another's intact. Partial underpayment still proves client trust.
Cross-check the payment intent
The smoking gun is the processor. If Stripe/Adyen/Braintree intent amount equals the tampered figure, impact is proven. If the intent is created server-side with a correct total but the order record shows tax 0, you still have an integrity bug—fulfillment and finance reports will disagree.
Always compare:
- UI displayed total
- Order API total
- Payment provider captured amount
Mismatch among those three is your writeup structure.
Quote endpoints matter too. Some apps POST /tax/quote with an address, then let you ignore the returned quoteId and submit your own amounts. Bind failures—using quote A while paying with totals from quote B—are worth a paragraph.
Do not file cosmetic bugs
A response that echoes your taxAmount in a preview yet charges the correct sum is not underpayment. Watch the capture. Similarly, tax-inclusive vs tax-exclusive display confusion without a wrong charge is a UX issue.
Exempt-organization flows deserve a look. If taxExempt=true is a client boolean with no certificate ID checked server-side, you found the same class of bug with different field names.
Fix language
Recalculate tax on the server from shipping address, product tax codes, and current rates. Treat client totals as display hints only. Create payment intents only after server-side pricing. Log discrepancies when client and server totals diverge.
For the report, include the pre-tamper quote, the modified request, and the provider receipt. Severity scales with margin: unpaid tax on digital goods in a high-VAT region gets attention faster than a rounding nickel—but even small, reliable underpayment demonstrates broken pricing integrity.