A refund-calculation endpoint can be correct for two-decimal currencies and still fail for currencies whose smallest unit is one whole unit. Extensions, gateways, tax engines, and reporting systems may pass amounts as decimal strings, integer minor units, or formatted values. Discounts and proportional tax allocation can create fractions that the order currency cannot represent. Rounding each line independently may differ from rounding the final refund and can leave the order, gateway, and ledger one unit apart.
Use this for WooCommerce 11.1 stores that sell or refund in JPY, KRW, VND, CLP, or another zero-decimal currency, especially with tax, coupons, fees, partial quantities, or currency switching.
Quick answer
Build fixed orders in at least one zero-decimal and one two-decimal currency. Record WooCommerce currency decimals, original order line totals, taxes, discounts, fees, shipping, and gateway amounts. Request full and partial refund calculations, including awkward quantity splits, and compare the endpoint with WooCommerce order methods, admin preview, gateway request, receipt, and accounting export. Use one documented rounding point and allocate any remainder deterministically so line components sum to the approved refund.
What to check first
- Record store currency settings, order currency, configured decimal count, gateway currency support, tax rounding mode, and multicurrency extension version.
- Create full, line, quantity, shipping, fee, tax-inclusive, tax-exclusive, coupon, and odd-remainder refund fixtures.
- Compare decimal strings and integer minor-unit representations at the API, PHP, gateway, database, email, and export boundaries.
- Confirm negative values, maximum refundable amount, already refunded lines, and mixed tax rates cannot produce an unrepresentable result.
- Retest saved orders created before the upgrade because their stored rates and rounding context may differ from new orders.
Test scenarios to run
Run the same controlled fixture across these branches. Write down the expected result before testing so a surprising response is easy to identify.
| Scenario | Fixture | Expected result |
| JPY partial line | 3 units, uneven price | Whole-unit refund with owned remainder |
| KRW coupon | Order-level discount | Lines sum to approved total |
| GBP control | Two-decimal order | Pence preserved |
| Prior refund | Remaining balance | Cannot exceed refundable amount |
Decision rule
Release only when each component is representable in the order currency and line, tax, fee, discount, gateway, receipt, and ledger totals reconcile exactly.
Safe fix order
Use a sequence that makes each result easy to prove. Stop when new evidence changes the scope or owner of the problem.
- Define the currency minor-unit contract
- Build awkward refund fixtures
- Choose one rounding and remainder rule
- Compare WooCommerce and gateway values
- Protect historical-order context
Diagnostic table
Use this table to connect the observed behavior to evidence and a verification step.
| Action | Evidence | Verification |
| Define the currency minor-unit contract | Record store currency settings, order currency, configured decimal count, gateway currency support, tax rounding mode, and multicurrency extension version. | Zero-decimal fixtures contain no hidden fractional minor units. |
| Build awkward refund fixtures | Create full, line, quantity, shipping, fee, tax-inclusive, tax-exclusive, coupon, and odd-remainder refund fixtures. | Full and partial refunds never exceed the remaining refundable amount. |
| Choose one rounding and remainder rule | Compare decimal strings and integer minor-unit representations at the API, PHP, gateway, database, email, and export boundaries. | WooCommerce, gateway, email, accounting, and customer receipt show the same total. |
| Compare WooCommerce and gateway values | Confirm negative values, maximum refundable amount, already refunded lines, and mixed tax rates cannot produce an unrepresentable result. | Historical and multicurrency orders use their owned currency and rate context. |
Useful command or data shape
Adapt paths, IDs, and privacy handling to the site before running commands or storing data on production.
currency,decimals,line_total,tax,requested,expected
JPY,0,1001,91,547,547
KRW,0,19999,1818,10000,10000
GBP,2,19.99,3.33,10.00,10.00
Production verification checklist
- Zero-decimal fixtures contain no hidden fractional minor units.
- Full and partial refunds never exceed the remaining refundable amount.
- WooCommerce, gateway, email, accounting, and customer receipt show the same total.
- Historical and multicurrency orders use their owned currency and rate context.
Why this usually happens
- Code assumes every currency has two decimal places or multiplies every amount by 100.
- Line, tax, discount, and gateway layers round at different points.
- A multicurrency extension displays one currency while refund logic reads another context.
- Old orders retain exchange-rate and tax information that current settings cannot safely recreate.
Field notes
- Write the expected result before changing anything and keep one repeatable canary fixture for the full test window.
- Record exact versions and UTC timestamps because caches, retries, scheduled actions, and deployments can change the evidence between checks.
- Test the public browser path and the stored server-side result, not only an admin preview or isolated API response.
- Close the task only after the visible workflow, logs, stored record, and relevant downstream system agree.
Mistakes to avoid
- Changing production before recording the exact version, URL, role, request, UTC time, fixture, and rollback point.
- Treating one successful browser view as proof while logs, stored records, background jobs, caches, and downstream systems remain unchecked.
- Testing a different role, locale, cache state, form, order, device, or integration path from the one users actually reach.
- Leaving debug output, temporary exclusions, test accounts, broad permissions, helper code, or relaxed firewall rules active after verification.
What to tell the client or owner
Give the owner a concise evidence packet with the affected workflow, exact versions, UTC test time, fixture ID, expected result, actual result, key logs, change made, rollback point, final result, owner, and next review date. Remove credentials and personal data before sharing it.
Questions teams ask during testing
Should I test this directly in production?
Start with read-only evidence and reproduce on staging when the change affects code, security, checkout, forms, indexing, or permissions. If production is required, use one named canary, a short monitored window, and a tested rollback.
How do I avoid a false positive?
Repeat the same fixture with the same versions, URL, role, locale, cache state, and downstream integration. Compare stored evidence and logs instead of relying on a visual impression.
What evidence should I keep?
Keep UTC time, exact versions, URL or record ID, expected result, actual result, relevant log lines, change made, rollback point, owner, and final verification. Redact credentials and personal data.
When should I escalate?
Escalate when the site is unavailable, security scope is unclear, checkout or lead capture is affected, several systems disagree, or the next action could destroy evidence.
When HandL WP should help
Bring in help when this affects leads, checkout, search visibility, security, paid media reporting, or a client production site. HandL WP can trace the issue through WordPress, hosting, cache, tracking, and Search Console, then verify the workflow after the technical fix.
If this is active on a production site, have HandL WP test WooCommerce refund calculations.
Related HandL WP guides
Use these related guides when the same issue touches tracking, security, checkout, or crawler visibility.
Helpful references