Currency Conversion Rounding Abuse in Checkout and Wallets
Foreign exchange in web apps is a pile of floats pretending to be money. When the service rounds in your favor per hop—and lets you hop often—you can manufacture value.
This shows up in multi-currency carts, "pay in local currency" toggles, crypto on-ramps, and in-app wallets that convert on deposit and again on spend.
Find the round-trip
Locate endpoints like /fx/quote, /wallet/convert, or checkout fields currency=EUR with a server-supplied rate. Note decimal places: some APIs return 2 dp for fiat, 8 for crypto display, and still compute with binary floats underneath.
Test pattern:
- Start with a fixed balance in currency A (your funds).
- Convert A→B at the quoted rate.
- Convert B→A immediately.
- Compare final A to start.
If you gain even a fraction repeatedly, you have rounding arbitrage. Multiply carefully in the report—show per-cycle gain and a modest N-cycle total you actually executed, not a fantasy overnight millions projection.
Lock quote IDs when present. Reusing an expired quoteId with a newer rate, or applying quote buy-side on a sell path, sometimes yields a one-way favorable round. That is still monetary logic abuse even if it is not a pure float bug.
Checkout-only variants
Maybe you cannot loop convert. You can still switch currency mid-checkout so line items round down twice, or apply a coupon in one currency while paying in another. Example: item 9.99 USD becomes 9.00 EUR after a coarse rate, then tax and discount apply on the floored figure.
Watch for client-supplied fxRate or displayCurrencyTotal. Editing those is payment manipulation; combine with rounding notes only if the server accepts them.
Split shipments and partial captures deserve a pass. Capturing 10.005 twice with half-up rounding can beat a single 20.01 capture depending on who rounds where. Document which service rounded.
Integer money beats float stories
Triage engineers argue when the skim is sub-cent. Your job is to show a deterministic, repeatable increase above fee costs. If the platform charges a conversion fee larger than the rounding gain, say so—severity may call it informative. If fees are absent on internal wallet convert, severity climbs.
Avoid hammering FX APIs with tens of thousands of conversions. A tight loop of 20–50 controlled calls with balance screenshots is enough.
Include exact major/minor units in the report (1099 cents, not 10.99 float prose). Finance reviewers trust integer ledgers.
Crypto display precision is a frequent trap: eight decimal places on screen, two on the fiat ledger, and a third service truncating mid-hop. Align which layer rounded in every screenshot.
Remediation worth naming
Use integer minor units, banker's rounding rules applied once per commercial event, lock the quote ID to the payment, and forbid client rates. Half-up at each microservice boundary is how drift appears.
PortSwigger-style logic-flaw thinking applies: the vulnerability is inconsistent monetary policy across steps, not a flashy injection string. Write the ledger math in the report so a finance reader can follow it without burp jargon.