Conversion value inconsistent
Conversion value inconsistent is the same single order carrying a different revenue figure to each destination, because value is composed separately per tag. Tax is in one, shipping is in another, discounts are applied in some, currency is defaulted on one. The order is counted once and counted correctly. Only its magnitude disagrees, so every platform reports a different total and ROAS depends on who you ask.
Symptom and cause
- Symptom
- Every platform reports a different revenue figure for the same orders: one includes shipping, one doesn't, one has its own currency idea. ROAS depends on who you ask.
- Cause
- Conversion value is built separately per destination: tax and shipping in one tag, out of another; currency left to default on one platform; discounts applied in some places and not others.
- Where it's caught
- Row-by-row reconciliation: per-order values diverging by exactly the tax or shipping amount; currency mismatches in multi-market accounts.
How one order gets several different values
Each destination builds the purchase value from whatever field its tag happens to read, and nothing forces those fields to share a composition. Google recommends the GA4 purchase value be the sum of (price * quantity) for the items only, with tax and shipping carried as separate parameters and currency required for revenue to compute accurately. That is a recommendation, not an enforced rule. GA4 will ingest a grand-total just as readily, and it reports both Gross purchase revenue (the raw sum of the value you send) and Purchase revenue (that same sum minus refunds). Neither metric adds or removes tax and shipping. Both simply reflect whatever your value parameter contains, so a value with tax and shipping folded in silently inflates both. Google Ads is a second build entirely. Its cart items.price (the per-item entry in the items array) is defined as the sales price of a single item excluding tax, shipping and transaction-level discounts, and value is passed as a bare number with an ISO 4217 currency string. Meta's Purchase event is a third, needing value (and a paired currency) for value optimisation, shown with both in every example, but giving no instruction on whether tax or shipping belong in it. So the same order can leave the item subtotal in one tag, the grand total in another, and a pre-discount or post-discount figure in a third. Currency compounds it. A GA4 property reports in one currency (USD unless changed), and a supplied local currency is converted at the prior day's rate, which drifts from the store's native-currency books and from an ad platform that applies its own conversion or none. The fingerprint is a per-order value that exceeds the item subtotal by exactly the order's tax, exactly its shipping, or by a clean FX factor.
| per order ████-7720 | value sent | currency | vs subtotal |
|---|---|---|---|
| Store (back office) | 125.99 | GBP | subtotal + tax + ship |
| item subtotal | 100.00 | GBP | the canonical base |
| GA4 | 100.00 | GBP | matches subtotal |
| Google Ads | 105.99 | GBP | +5.99: value param = subtotal+shipping (items.price still excludes it) |
| Meta | 100.00 | USD | matches subtotal, but mislabelled USD |
| tax / shipping | 20.00 / 5.99 | GBP | the exact divergences |
How to catch it
- Confirm it is a value fault, not a count fault, before touching anything. In GA4 (Reports → Monetization → Ecommerce purchases) and in your Google Ads and Meta dashboards, set reported orders against the store back office for one window. If every platform agrees on the number of orders but disagrees on revenue, it is F-302. If the order count itself is wrong, that is F-101 or F-104, not this.
- Build the per-order ledger, which is the literal exhibit. For one real order, place five figures side by side: the store back-office net, the GA4 value, the Google Ads conversion value, the Meta value, and the order's own tax and shipping. A platform value that exceeds the item subtotal by exactly the tax, exactly the shipping, or both, is the composition tell.
- Read the raw value GA4 received, not the report. In DevTools → Network, switch on Preserve log, complete one purchase, filter on /g/collect (the GA4 browser endpoint), then in the request's query string or Payload tab confirm tid=G-XXXXXXX (your GA4 measurement ID) to separate the GA4 hit from any Google Ads or Floodlight hit that also posts to /g/collect (those carry an AW-/DC- destination or en=conversion). Open the purchase hit's Payload tab and inspect epn.value (revenue), epn.tax, epn.shipping, the cu parameter (currency, a bare ISO code), and the product params pr1, pr2 … which encode each item's price and quantity. Confirm whether value is the item subtotal or a grand total, and confirm currency is a bare code with no symbol.
- Check what each tag actually sends in GTM Preview. Select the purchase event in the left-hand event stream (not Summary), then click each fired tag (GA4, Google Ads, Meta) in turn. The tag detail shows its Properties, including the value and currency parameters the tag resolved and is configured to send. Use the Names / Values selector at the top of the Tag Assistant tag view to read the resolved values rather than the variable names, and keep the true outbound-payload check anchored to the Network /g/collect hit. Then in Meta Events Manager → your Pixel/Dataset → Test Events, set the Select Channel dropdown to Website, then under Test browser events paste your site URL, click Open Website, and complete one purchase in the window it opens. Confirm the Purchase event that streams in carries the canonical value (a bare decimal) and currency (a 3-letter ISO code). Three different numbers for one order, traced back to three different dataLayer fields, is the structural cause in plain sight.
- Verify currency end to end in a multi-market account. In GA4 Admin → Property → Property details, note the property currency (the "Currency displayed as" field), then in DebugView confirm each market sends its true local currency code rather than defaulting. A market sending the WRONG currency code makes GA4 apply its prior-day conversion on a mislabelled base, so revenue drifts from the native-currency books. A market sending NO currency is worse: in our testing GA4 records the event but reports zero revenue for it (it does not appear to fall back to the property default), so that order's revenue silently vanishes rather than merely drifting.
- Separate the value fault from missing events. In GA4 DebugView or GTM Preview, confirm add_to_cart, begin_checkout and purchase all fire and that purchase carries item-level detail. If events are absent or the items array is empty, that is F-301 (completeness). F-302 assumes the events exist and asks only whether the value they carry agrees across destinations.
What putting it right involves
- Decide the canonical value definition once, on paper, before any tag work. Name the exact field (item subtotal), state what is out (tax, shipping, transaction-level discounts), and fix the currency. 'Use one variable' is incomplete until the variable holds the right field for each destination's intended metric, so write the definition down and treat it as the single source of truth.
- Compose that value once in the data layer at order creation. Push one ecommerce object carrying value, tax, shipping, currency and coupon as discrete fields, exactly as the standard GTM ecommerce pattern does, so the true per-order figures exist in one place rather than being rebuilt in each tag.
- Feed every tag from that one object. Point the GA4, Google Ads and Meta tags at the same Data Layer Variables for value and currency. Where a platform genuinely needs a different basis (for example a grand-total rather than a subtotal), implement that as one explicit, documented transformation of the canonical value, not a third independent ad-hoc build.
- Normalise currency deliberately in multi-market accounts. Send each market's true local currency code with every value. Omitting the currency makes GA4 report zero revenue for that order (there is no property-default fallback); sending the wrong code makes GA4 convert a mislabelled base. With the true code, GA4 converts at its prior-day rate and the revenue registers correctly. Treat GA4's reporting-currency conversion as an expected, explained drift from the native-currency books, not a defect to chase to the penny.
- Do not lean on GA4's default key-event value as a fix. It only substitutes a flat assumed value when both value and currency are unset, and never overrides a value you send, so relying on it re-introduces the very inconsistency you are removing. The fix is sending the true per-order value from the data layer, every time.
- Re-run the ledger and accept an explained tolerance, not identical numbers. With one canonical value the per-order figures should agree on composition, but they will still not tie to the penny: GA4 converts to one reporting currency at its own daily rate, and Google Ads value rules can re-write the reported value by geo, device or audience (the Original conversion value metric exposes that gap). Worked illustration, reconstructed and not a measured client figure: a £100.00 items-subtotal order with 20% VAT (£20.00, a rate that varies by country and product) and £5.99 shipping sends GA4 value 100.00 with tax 20.00 and shipping 5.99, while a tag wired to the gross-total field sends 125.99, a divergence of exactly tax plus shipping. The goal is that every divergence is one you can name.
Sources on file
- GA4 Recommended events reference (Google for Developers), purchase: set value to the sum of (price * quantity), don't include shipping or tax; if you set value then currency is required for revenue metrics to be computed accuratelydevelopers.google.com
- [GA4] Currency reference (Google Analytics Help), default property currency is USD; supplied local currencies converted at the prior day's exchange ratesupport.google.com
- Track transaction-specific conversion values (Google Ads Help), select 'Use different values for each conversion'; pass value as a number and currency as an ISO 4217 string on the event snippetsupport.google.com
- Conversions with cart data (Google Ads Help), items.price is the sales price of a single item, excluding tax, shipping, and any transaction level discountssupport.google.com
- Meta Pixel conversion tracking (Meta for Developers), Purchase value sent as a number with an ISO 4217 currency code, both required for value optimisationdevelopers.facebook.com
- Simo Ahava: GA4 Ecommerce Guide for Google Tag Manager, single dataLayer ecommerce object with value, tax, shipping, currency, coupon as discrete fields read by GTMwww.simoahava.com
Questions on this file
How do I tell F-302 (value inconsistent) apart from F-301 (ecommerce events partial)?
Ask whether the event exists before you ask what value it carries. F-301 is a completeness fault: purchase fires but add_to_cart or begin_checkout do not, or the data layer carries no item-level detail, so the funnel has holes. F-302 assumes the full event set is present and asks only whether the value those events send agrees across GA4, Google Ads, Meta and the store. The test: in GTM Preview, do the events fire with a populated items array? If events are missing, fix F-301 first. If the events are all there but the value differs per platform, it is F-302.
Why does GA4 show more revenue than Shopify when the order counts match?
Because the two numbers are composed differently. The store books a net figure, while GA4 may be receiving a value that folds in tax and shipping, and GA4's Gross purchase revenue simply reflects the value you send, so it shows that inflated figure. If the order counts agree, you are not double-counting (that would be F-101); you are comparing an ex-VAT subtotal against a tax-and-shipping-inclusive total. Build the per-order ledger and the gap should resolve to exactly the order's tax, its shipping, or both.
What should the GA4 purchase value actually be, tax in or out?
Google recommends the sum of (price * quantity) for the items only, with tax and shipping as separate parameters, but it does not enforce that. GA4 will report whatever you put in value: its Gross purchase revenue is the raw sum and Purchase revenue is that same sum minus refunds, so neither metric decides the tax/shipping question for you. There is no single cross-platform law to obey. The honest answer is to pick one definition deliberately, document what is in and out, and apply the same definition to every destination so the numbers are consistent and explainable.
My ROAS is different on Google Ads and Meta for the same orders. Which is right?
First rule out the usual suspect. Differing attribution windows and view-through (Meta's 7-day-click/1-day-view default vs Google Ads' 30-day-click) make the two platforms claim different SETS of orders, which is a counting/attribution gap, not F-302. F-302 is specifically when both platforms agree on WHICH orders converted but disagree on the per-order value. Once that is the case, often neither is right, because each platform built the conversion value from a different field. Google Ads cart items.price excludes tax, shipping and transaction-level discounts; Meta requires a value but gives no instruction on whether tax or shipping belong in it, so an implementer's choice decides. Until both read the same canonical value from one variable, the two ROAS figures are measuring different magnitudes of the same orders, and the right move is to make them agree on composition rather than to trust the larger one.
Do I have to send currency with the value?
You should, but not because GA4 rejects the event without it. GA4 still ingests a purchase that carries a value and no currency. The precise requirement is that currency is needed for revenue metrics to compute accurately, and the value must be a bare number with no currency symbol. In a multi-market account, send each order's true local currency code: omitting it makes GA4 report zero revenue for that order (there is no property-default fallback), and sending the wrong code makes GA4 convert a mislabelled base. With the true code, GA4 converts at the prior day's rate and the revenue registers.
We fixed the value to one variable but the platforms still don't match exactly. Is that a fault?
No, that residual is expected. Even with one canonical value, GA4 converts every currency to one reporting currency at its own daily rate, Google Ads value rules can re-write the reported value by geo, device or audience, and attribution windows, consent modelling and refunds differ per platform. The realistic goal is that the numbers reconcile within a tolerance you can explain, not that they are identical. Attribution-window gaps in particular are a counting and attribution concern, not F-302.