Notes
Money, sales tax and rounding between a store and an ERP
An amount goes wrong between two systems in four places: how it is written (46.29 or 4629 cents), whether tax is in it, which system calculates the sales tax, and when it is rounded. The usual result is one cent per order, and one cent is enough to keep a payment from matching its invoice.
Updated
How does each system write an amount?
The same 46.29 dollars travels in five forms:
| Source | Value | Form |
|---|---|---|
| Shopify GraphQL Admin API | "46.29" |
Decimal as a string, currency in a separate field |
| WooCommerce REST API | "46.29" |
String, in total, total_tax, shipping_total |
| Stripe API | 4629 |
Integer in the smallest currency unit |
| Supplier CSV or Excel export | $46.29 or 46.29 USD |
Text with a symbol, sometimes a thousands separator: $1,234.50 |
| NetSuite | 46.29 | Number in a currency field |
Stripe's documentation is explicit: 100 means one dollar, and 100 means 100 yen, because the yen has no decimals. An integration that reads the Stripe amount into a dollar field without dividing books a 46.29 payment as 4,629.00. The reverse, dividing a yen amount by 100, books 100 yen as 1. The rule per source is one line: integer in minor units, divided by 10 to the power of the currency's decimals.
Strings need the same care. "46.29" is read as a decimal, never as a binary float, and $1,234.50 from a file is stripped of the symbol and the comma before it is a number. More on files in supplier CSV and Excel files.
Is the tax in the price or on top?
In US stores tax is added at checkout. Shopify still sends a flag on every order, taxesIncluded, and WooCommerce sends prices_include_tax. A store that also sells to Canada or Europe can show tax-inclusive prices in those markets, so the flag is read per order and not set once per store.
When the flag is true, the line price contains tax. 21.78 with 21% included is 18.00 net; booking 21.78 as the net price in an ERP that adds tax produces a total of 26.35 for a customer who paid 21.78.
Which system calculates the sales tax?
US sales tax depends on the ship-to address, down to the city and special district. New York State's Publication 718 lists the combined rate per jurisdiction: 8⅞ percent in New York City, 8⅜ in Putnam County, 8 in counties such as Monroe and Schenectady, effective March 1, 2025. The store calculates tax at checkout, with Shopify Tax, manual rates or a tax app, and records it per line item. Each Shopify TaxLine carries a title, a rate and a priceSet with the amount.
NetSuite can calculate tax too. With legacy tax, the sales order has a header taxitem and taxrate, and NetSuite's field help describes taxtotal as the tax rate multiplied by the taxable total of the lines. Left to itself, NetSuite recalculates the tax the store already charged. Three outcomes:
| Approach | Result | When it goes wrong |
|---|---|---|
| ERP recalculates | Two answers to one question | Different rate table, a different taxable flag on an item, different rounding |
| Store's tax posted as an amount | ERP total equals the amount charged | Needs a tax item or tax details override per account setup |
| Tax as a separate line item | Total matches | NetSuite's tax fields stay empty, so tax reporting has to read the line instead |
With SuiteTax, the sales order has a taxdetailsoverride checkbox; NetSuite's field help says only roles with at least Edit on the Tax Details Tab permission can override. The integration role needs that permission, or the store's amounts cannot be posted.
Marketplace orders add one more case. Shopify's TaxLine.channelLiable is true when the channel that submitted the order is liable for remitting the tax. That tax belongs in the order total and not in the seller's sales tax payable.
Rounding per line or per order?
A sales tax amount is rounded to the cent. The question is when. Three lines, ordered from New York City at 8.875 percent:
| Line | Amount | Tax unrounded | Rounded |
|---|---|---|---|
| 3 x 4.99 | 14.97 | 1.3286 | 1.33 |
| 1 x 12.49 | 12.49 | 1.1085 | 1.11 |
| 7 x 2.15 | 15.05 | 1.3357 | 1.34 |
| Total | 42.51 | 3.7728 | 3.78 per line, 3.77 per order |
Rounded per line and summed, the tax is 3.78 and the customer pays 46.29. Calculated once on 42.51, the tax is 3.77 and the invoice says 46.28. The payment of 46.29 does not match the invoice, and 0.01 sits unapplied until someone clears it. If one order in three has such a difference, a store with 2,000 orders a month has 667 payments a month to clear by hand.
The fix is not a better rounding rule in the ERP. It is to post the tax amounts the store calculated, per line, so the ERP total is the amount the card was charged. The rate goes along for reporting; the amount is what counts.
Discounts and shipping
An order-level discount has to be spread over the lines, because the ERP books revenue per line. Shopify returns its own allocation per line in discountAllocations, and totalDiscountsSet holds the order and line discounts together; the integration uses Shopify's allocation so the ERP lines match the store. Where a source sends only an order total, the integration spreads it. A 10.00 discount on the order above, spread by line value:
- 14.97 / 42.51 x 10.00 = 3.5215, rounded 3.52
- 12.49 / 42.51 x 10.00 = 2.9381, rounded 2.94
- 15.05 / 42.51 x 10.00 = 3.5404, rounded 3.54
3.52 plus 2.94 plus 3.54 is 10.00 here. With other amounts the rounded parts add up to 9.99 or 10.01, and the last line takes the difference. The unit price after the discount on line 1 is 11.45 / 3 = 3.8167, which has no exact cent value. Shopify's documentation describes discountedUnitPriceAfterAllDiscountsSet as the approximate unit price. The integration maps the line total and the quantity, never a rounded unit price times the quantity.
NetSuite has its own discount logic. The field help for the discount on a sales order: if the discount item is taxable, the discount is applied before taxes; if not, after. A taxable discount item reduces the tax base, a non-taxable one does not, and the wrong one changes the tax by 8.875 percent of the discount: 0.89 on 10.00.
Shipping has tax lines of its own in Shopify, on each ShippingLine. When the store charged tax on shipping, the NetSuite side needs shippingcost and a shippingtaxcode that produce the same amount.
Tax-exempt customers and other currencies
B2B buyers who resell are often exempt. The two systems model it differently:
| Shopify | NetSuite | |
|---|---|---|
| Exempt or not | taxExempt on the customer |
taxable checkbox on the customer |
| Per state | taxExemptions, values such as US_NY_RESELLER_EXEMPTION |
none; one record |
| Certificate | not a field | resalenumber, one text field |
| Default tax | none | taxitem |
NetSuite's field help adds the catch: a customer is non-taxable only if the Taxable box is clear and the Tax Item field is empty. An integration that clears the box and leaves a tax item creates a customer who is still taxed.
Currency adds a second amount to every money field. Shopify sends shopMoney in the store's currency and presentmentMoney in the currency the customer paid. NetSuite fixes a customer's currency once transactions exist for that customer, and the sales order carries an exchangerate. A Canadian customer who paid in CAD is booked in CAD with the rate of the order date, or in USD with the shop amount. The two give different revenue in the books, and the mapping states which one applies. Order dates themselves have a trap of their own, described in dates and time zones.
What this means for your integration
We map every amount with its source format, its currency and its tax treatment, and post the store's own tax amounts, so the total in your ERP is what the customer paid. Discounts are spread per line with the remainder on the last line, and exemptions are mapped per state to your ERP's customer fields.
In the dry run before go-live your recent orders pass through those rules, and every order whose ERP total differs from the store total by even a cent shows up with the line that causes it. The full field list for orders is on orders, customers and shipping, and the general method on field mapping. Tell us which store and ERP you use, and we show you where the cents go missing.
Frequently asked questions
Should the ERP recalculate sales tax on web orders?
Usually not. The store charged the customer a specific amount, and that amount is what the ERP should record and what goes to the tax return. Recalculating gives a second answer whenever the rate tables, the rounding or the taxability of an item differ, and the difference is a cent or a whole jurisdiction.
Why is the payment one cent off from the invoice?
Because the store and the ERP rounded at a different moment. Tax rounded per line and summed differs from tax calculated once on the order total on part of the orders, as the example on this page shows. The fix is to post the store's tax amounts, not its rate, so the ERP total equals what the card was charged.
Who handles marketplace facilitator tax?
The marketplace collects and remits it. Shopify marks each tax line with channelLiable, true when the channel that submitted the order is liable for remitting. Those lines are recorded so the order total matches, and kept out of the seller's sales tax payable.
How do tax-exempt B2B customers map?
Shopify keeps exemptions per state on the customer, such as US_NY_RESELLER_EXEMPTION. NetSuite keeps one Taxable checkbox, one Resale Number and one Tax Item. The mapping states which exemption makes the NetSuite customer non-taxable, and clears the Tax Item too, because NetSuite treats a customer with a Tax Item as taxable even with the box cleared.
Can an amount in cents be sent to a field that expects dollars?
It can, and it arrives 100 times too high. Stripe sends 4629 for 46.29 dollars; a field that expects dollars stores 4,629.00. The integration converts per source with the number of decimals of the currency, which is 0 for the yen.
Tell us what you need
Tell us which systems you use and what needs to work better. We reply personally within one working day. We help you define the work, then agree the price and timing before you commit.
- You know the price and the timeline before you commit to anything
- We work out the connection that takes the work off your hands
- We handle the build, hosting, monitoring and maintenance
- Your team has less information to retype and fewer discrepancies to chase