# eBay/Amazon order tax: why it's derived from Cydekick's own product VAT rate

This decision was reversed twice in the same session before landing on the current design.
Anyone tempted to "fix" this again should read the whole history below first — both previous
designs looked correct at the time and were each disproven by real data.

## The current design (as of 2026-09-30)

For every eBay/Amazon order line, Cydekick **always** derives tax from the linked product's own
configured VAT rate — `PricingEngine::resolveTaxRate()`'s cascade (channel-specific override →
product's own default "VAT Rate %" → 0). eBay's `pricingSummary.tax` and Amazon's
`OrderItems[].ItemTax.Amount` are **never read or trusted anywhere in this pipeline any more**.

The real, already-charged amount (`lineItemCost` / `ItemPrice.Amount`, VAT-inclusive) is never
changed — only *split* into subtotal + tax at the product's rate. This applies consistently at
every stage an eBay/Amazon order's tax gets touched:

- `EbayOrderImporter` / `AmazonOrderImporter` — direct-to-Cydekick imports (channel destination
  = `cydekick`). Each line's rate is resolved via `computeLines()`, and the **order's own**
  `subtotal`/`tax_amount` are set as the sum of the lines — not from the marketplace's aggregate.
- `EbayToShopifyPusher` / `AmazonToShopifyPusher` — push-to-Shopify orders (destination =
  `shopify`, used to print labels). Each line's `tax_lines` sent to Shopify is now built from the
  product's own rate too, so **Shopify's own admin/invoices show the correct VAT breakdown**, not
  just Cydekick's copy of the order.
- `ShopifyOrderImporter` — reads pushed orders back from Shopify via webhook/sync. Falls back to
  the same product-rate cascade whenever a line has no usable `tax_lines` rate. This applies to
  every source now (the earlier code excluded `ebay`/`amazon` here — see "Second attempt" below —
  that exclusion has been removed).
- `channels:backfill-order-line-tax` then `channels:backfill-marketplace-order-tax` — retroactive
  commands for historical orders imported before this design was settled. Run the line command
  first; the order command just sums the (by-then-corrected) lines. Both are idempotent — safe to
  rerun any time, they no-op on anything already correct.

## Why: the full history

### Original bug (fixed correctly)
`EbayToShopifyPusher`/`AmazonToShopifyPusher` never sent `tax_lines` to Shopify at all — every
eBay/Amazon order showed £0.00 tax everywhere. Confirmed via `tax_lines`/`taxes_included` being
completely absent from the pushed payload.

### First attempt: trust the marketplace's own reported tax, including a reported zero
Reasoning: eBay only reports one order-level aggregate (`pricingSummary.tax.value`), no per-line
breakdown; Amazon reports real per-line tax (`ItemTax.Amount`). Both were read and distributed
across lines, with a genuine `£0.00` taken at face value under the theory that UK/EU
marketplace-facilitator VAT rules mean eBay/Amazon themselves collect and remit VAT directly on
many sales, so the seller's own order data correctly shows nothing collected.

**Caught wrong**: a real production order (`C1035`) had eBay's API genuinely confirm `£0.00` tax
— but before that fix, a naive product-rate fallback had already written a fabricated
`tax_rate 0% -> 20%, tax £0.00 -> £3.11` onto that same order's line. That contradiction is what
triggered the first rework: never guess from the product's rate, always trust the marketplace's
own aggregate/per-line figure — the £0 was "genuinely zero", not a bug.

### Second attempt: never guess a rate when the marketplace reports zero
All four affected files (`EbayOrderImporter`, `AmazonOrderImporter`, `ShopifyOrderImporter`,
`channels:backfill-order-line-tax`) were reworked to derive line tax strictly from the order's own
confirmed real `tax_amount` (eBay: proportional distribution by cost share; Amazon: real per-line
`ItemTax`), never falling back to the product's static config. Verified against synthetic orders
for both the "real tax > 0" and "genuinely zero" cases.

**Caught wrong** (same day): the user pulled up eBay's own payout breakdown for the exact order
behind C1035 (`10-15217-73643`) — eBay paid out the **full £18.64** order amount, minus only its
own selling fees (~£2.68), never withholding or remitting any VAT itself. If eBay were really
acting as deemed supplier under marketplace-facilitator rules, it would have withheld VAT from
that payout — it didn't. This proves `pricingSummary.tax = 0` does **not** mean "eBay collected
it" for this seller's sales; it means eBay's Sell Fulfillment API simply doesn't report seller-side
VAT for ordinary UK domestic sales at all — because collecting/remitting it isn't eBay's job here,
it's the seller's own responsibility (their words: *"eBay i dont think are keeping the vat, this
is my responsibility"*). The same conclusion was extended to Amazon by the user's own
confirmation, absent any contrary evidence for Amazon specifically.

### Current (third, hopefully final) design
Given the marketplace's own reported tax is not a reliable signal either way for this business,
and VAT accounting for these sales is confirmed to be the seller's own responsibility, the
correct source of truth is Cydekick's own product VAT configuration — which is what the seller
actually uses to work out what's owed. This is implemented exactly as it would have been if the
user's original, very first request on this topic had been taken completely literally: *"we
should use the tax rate per product line in cydekick if the tax rate is not provided from the
channel"* — except now understood as: the channel's own figure, even when present, is not a
substitute for the product's own rate here, so it's not read as an input at all any more.

## If this needs revisiting again

- If Amazon turns out to behave differently from eBay (i.e. Amazon payouts genuinely do show less
  than the full sale price on some orders, consistent with real marketplace-facilitator
  withholding), that would justify treating Amazon's per-line `ItemTax` as authoritative again —
  but only with concrete payout evidence, the same standard that overturned the eBay assumption.
- `resolveSourceChannel()` (removed along with the API-fetch code from
  `BackfillMarketplaceOrderTaxCommand`) assumed one channel per marketplace platform — if a second
  eBay/Amazon channel is ever added, the API-fetch approach (if ever reinstated) would need a
  proper source-channel FK on the order rather than a platform-wide lookup. Not currently a
  concern since this design no longer calls the marketplace APIs for tax at all.
- Order.channel_id can be genuinely `null` on some historical orders (their origin channel was
  since deleted) — both backfill commands and `PricingEngine::resolveTaxRate()` handle this by
  coercing to `0`, which matches no real channel's override row and falls through to the
  product's own default rate.
