Overview
Tax is the part of commerce most likely to differ from what you assumed. A US shopper expects$17.99 on the shelf and a little more at the till. A German shopper expects €17.99 to be the whole story, VAT already inside it. Both are correct, and a store selling to both has to do both.
Spree handles this by keeping three things separate:
- What is being sold — a tax category groups products taxed the same way
- Where the customer is — their market and shipping address
- Who works out the number — Spree’s own rates, or an external tax service
Tax categories
A tax category is how you say “these things are taxed alike”. Children’s clothing, books and hot food are treated differently from general goods in many countries, and a tax category is where that distinction lives. Every product carries one. If you don’t choose, the default is used.tax_code matters once you connect an external service — it is how you tell that service what kind of thing this is, in the vocabulary it already uses.
Tax rates
A tax rate is the number itself, plus where it applies and whether it’s already in the price.DE, US + CA. There’s no separate region record to create first.
Included or added on
included_in_price is the single most consequential setting here, so it’s worth being precise about what it does.
- Added on (US style)
- Included (EU style)
The shelf price is what the product costs. Tax is worked out at checkout, once the address is known, and added.A $17.99 item at 5%:
Set
included_in_price: false.Tax lines
Whatever works out the tax, the record is the same: a tax line per charge, saying what was taxed and how.Tax given back
When a customer returns something, the tax they paid on it goes back with the money. That tax is recorded as tax lines too, markedcredit, on the return, claim or exchange line rather than the order line. Each one names the sale’s tax line it gives back (original_tax_line_id, Admin API), keeps that line’s rate and label, and covers only the units that came back. They never change the order’s own tax totals, and the order’s tax lines list above shows only what was charged.
When tax is worked out
Tax is recalculated whenever the answer could have changed — an item added, an address entered, a delivery option chosen. Every one of those requests returns the updated cart, so a storefront never needs a separate “refresh the tax” call.1
Before an address is known
Prices are shown using the market’s tax treatment. For an inclusive market the displayed price is the real one; for an added-on market tax simply hasn’t been worked out yet.
2
Once the shipping address is entered
The address decides the real rate. Tax lines are written against each item, the delivery charge, and any taxable fee.
3
At completion
Whatever the tax is at that moment is what’s charged, and the order keeps it. A rate change next month does not rewrite an order from last month.
Connecting a tax service
Spree’s built-in rate tables are fine when the rules are simple — one country, or a handful of clearly-defined rates. They become a liability where the rules are not: US sales tax varies by city and changes constantly, and cross-border EU VAT has thresholds that shift with your turnover. For those, connect a real tax service. The provider is chosen per Market, which means one store can use its own rates in a simple market and an external service in a complicated one.estimate_refund and estimate_replacement have no default — a provider without them cannot open a return, claim or exchange. To give back exactly what the sale charged, as Spree’s own rates do, answer estimate_refund with Spree::TaxProvider::RecordedShare:
server/app/models/my_app/tax_provider.rb
refund and commit_replacement are optional: by default they do nothing, which suits a service that keeps no record of sales.
Tax-exempt customers
Business customers are often exempt, and the paperwork is real. Spree records a customer’s or company’s tax identifier — a VAT number, an ABN — and can validate it. Companies can also hold exemption certificates, scoped to the country or state that issued them. Exemption is decided when tax is worked out, not stored as a flag on the customer, so an expired certificate stops applying by itself. See Companies.Related
- Order totals — how tax rolls into what a customer pays
- Markets — where tax treatment and provider are chosen
- Returns, exchanges and claims — how tax goes back with a refund
- Addresses — what determines the rate
- Companies — B2B tax identifiers and exemptions

