Tax on refunds
How much of each refund is tax, how it is tied to the order's original tax, and where to read it from the API, the dashboard and the export.
Every refund records which part of it is tax. For accounting this is the figure that corrects the tax you reported on the sale. It is derived from the tax the order froze at checkout (Order.taxBreakdown), never from the rates in force when the refund is made, so changing, adding or deleting a tax rate later cannot alter an old refund. Nothing here writes to the order.
What a refund carries
| Field | Meaning |
|---|---|
taxAmount | The tax inside amount, in the currency of amount. Decimal string. |
netAmount | amount - taxAmount. |
shippingAmount | Postage (with its tax) returned by this refund. Set only on the refund that completes the order. |
shippingTaxAmount | The tax inside shippingAmount. |
taxBreakdown | [{ name, rate, amount, taxClassId }], the per-rate split of taxAmount. The amounts sum to taxAmount. |
All five are null on refunds recorded before this existed (they are not back-filled) and on orders that froze no tax information. A null means "not recorded", not zero.
The rules
By lines. A refund priced from the order's lines returns each line's own frozen tax. It is rounded cumulatively per line, the same way the amount is, so every refund of one line adds up to exactly that line's tax: 10.00 of tax on 3 units goes back as 3.33, 3.34, 3.33.
By amount. An explicit amount, a refund taken in the payment provider's own dashboard, and orders with no frozen lines are split in proportion to the order's frozen totals: amount x totalTax / orderTotal. This is the same for stores whose prices include VAT, where totalTax is the VAT already inside the total.
The last refund takes the residual. The tax of all refunds of an order can never exceed the order's frozen tax. The refund that reaches everything still refundable takes whatever tax is left, so the refunds of an order add up to the order's tax to the cent.
Currency. The split is computed in the currency of the refund amount (the currency the customer was charged in). For an order charged in a different currency from the store's, the frozen tax is carried across with the same scale as the amounts.
Earlier refunds without a split are counted at their proportional estimate when the cap is computed, so the last refund of an order does not absorb tax the earlier ones carried.
Refunds taken in the provider's dashboard
A refund taken in the own dashboard of a provider that reports refunds back (Stripe, PayPal and some others) is recorded as a refund of the order when the provider's webhook arrives, with source: "PAYMENT" and the amount-based split above. It is recorded once however many times the webhook is delivered, and a refund you made from Brainerce is not recorded a second time when the provider announces it. These refunds now reduce the revenue figures, as refunds made in Brainerce always have.
Where to read it
/v1orders: each entry ofrefundson an order carries the fields above.- Dashboard: each refund shows "of which tax", and the order shows a Refunded tax total.
- Backup export (
orders.csv): the orders file of a backup that includes orders ends with two columns,refunded_amountandrefunded_tax. An order whose refunds all predate the split exports a blankrefunded_tax, never a made-up 0. The quick Export of selected rows in the dashboard's Orders table is a separate eight-column file (order number, customer, email, status, platform, total, currency, date) and does not carry them. - MCP:
get_order_refundsandcreate_refundreturntaxAmountandnetAmount.
See also Tax classes and Regions.
Tax Classes
Charge different tax rates for different product types (Standard, Reduced, Zero-rated, Food) with a deterministic per-line resolution order.
FX & Analytics Currency
Roll multi-currency revenue into one reporting currency for analytics, using a daily FX rate snapshotted onto each order, without ever changing what a customer is charged.