Overview
An order total is rarely just the sum of the item prices. Tax is added or already inside them, a promo code takes something off, delivery costs something, gift wrapping costs a bit more. Spree records each of those as its own kind of row, and keeps a running total for each kind on the order itself. That means the summary block in your checkout is a handful of fields — you don’t add anything up yourself.The totals
Two forms of every amount
Every total comes twice:total is the raw value, display_total is formatted for the order’s currency.
Response
display_ one. It knows the currency symbol, which side it goes on, and which separator that locale uses — $138.10, 138,10 €, ¥138. Formatting it yourself means reimplementing that, and getting it wrong for somebody.
Money is a string, never a number. JavaScript can’t represent every decimal exactly:
0.1 + 0.2 gives 0.30000000000000004, which is not something you want inside a price.If you must do arithmetic, use a decimal library or work in whole cents. Most of the time you don’t need to — Spree already did it.The double-counting trap
The two exist because the same order can carry both: VAT already inside the goods, and a separately-added tax on something else.tax_total covers both, which is why it’s the safe field to display on its own line.
Totals update themselves
Every change to a cart returns the whole cart, totals included:Once an order is placed
The rows stop being regenerated. Editing a placed order re-adds the rows it already has rather than starting over, so today’s promotions and rates can’t rewrite what a customer agreed to last week.Where each row attaches
The individual rows are there if you need them — an itemised invoice, a tax report:- Tax lines → a line item, a fulfillment, or a fee
- Discounts → a line item or a fulfillment
- Fees → a line item, a fulfillment, or the order itself

