GA4 ecommerce tracking done right: the purchase event that actually adds up
Most ecommerce GA4 setups double-count revenue, drop the items array, or send a string where a number belongs. The events, the required parameters, and the transaction_id discipline that keeps revenue honest.
TL;DR
GA4 ecommerce runs on a set of recommended events (view_item → add_to_cart → begin_checkout → purchase) carrying a structured items array, and the whole thing lives or dies on the purchase event being shaped correctly: a numeric value, the right currency, a unique transaction_id (the dedup key that stops a page refresh from counting revenue twice), and a populated items array. Get any of those wrong and you don't get an error — you get inflated or missing revenue that looks plausible right up until it doesn't reconcile with the client's store. Below: the events, the parameters that matter, and the transaction_id discipline.
Ecommerce tracking feels like it should be the easy case — there's a real transaction with a real number, unlike the murky world of lead-gen. And yet inherited ecommerce GA4 setups are some of the most quietly broken: revenue double-counted on every back-button, product reports empty because the items array never made it, ROAS built on a value that's secretly a string. Here's the version that adds up.
The event funnel
GA4's ecommerce model is a sequence of recommended events, each carrying an items array describing the products involved:
view_item— product page viewadd_to_cart/remove_from_cartbegin_checkoutadd_payment_info/add_shipping_infopurchase— the conversion that matters mostrefund— the one everyone forgets (without it, revenue only ever goes up)
You don't need every event on day one, but you do need them consistent: the same item_id and item_name across the funnel, or the product-level reports fragment.
The items array is not optional
Every ecommerce event should carry an items array — item_id, item_name, price, quantity, and optionally item_category, item_brand, item_variant. This array is what powers GA4's product-performance reports and feeds dynamic remarketing in Google Ads / Merchant Center. A purchase event with a correct value but an empty items array will report total revenue while leaving every product report blank — a common and confusing half-broken state.
The purchase event: four things that must be right
This is where revenue accuracy is won or lost. On every purchase:
value— the order total, as a number, not the string"129.00"or"undefined". A stringvalueis the single most common silent ROAS-wrecker. Confirm the type, not just the presence.currency— set, and correct (ISO 4217, e.g.USD). Missing currency makesvalueambiguous and can drop the revenue entirely.transaction_id— a unique order ID. This is the deduplication key: GA4 ignores a secondpurchasewith atransaction_idit has already seen. Without it, a customer who refreshes the thank-you page or hits back→forward counts the revenue again — and your GA4 revenue drifts above the store's actuals.items— populated as above.
The double-counting trap (and the fix)
The #1 ecommerce reporting complaint — "GA4 revenue is higher than Shopify" — is almost always missing or non-unique transaction_id. Thank-you pages get reloaded, bookmarked, and re-shared; each reload re-fires purchase; without a stable unique transaction_id, GA4 counts each one. The fix is the dedup key: emit the real order ID as transaction_id and never regenerate it on reload. (The same dedup logic governs sending the purchase to Meta CAPI without double-counting — a shared event_id there, a stable transaction_id here.)
Platform reality: the integration is rarely complete
Shopify, WooCommerce, and the rest ship GA4 integrations, but they're frequently partial — purchase fires, but the funnel events or the items array are thin, or value excludes/includes tax and shipping inconsistently with what the client reports. Don't assume the platform's built-in tracking is complete; verify the actual payload rather than trusting that "the Shopify GA4 app is installed."
Verify it in DebugView, not by vibes
Ecommerce is exactly where GA4 DebugView earns its keep: trigger a real test purchase and read the purchase event's full payload — confirm value is numeric, currency is set, transaction_id is present and unique, and the items array is populated. Then reload the thank-you page and confirm it does not count a second purchase. This 5-minute check belongs in your pre-launch QA pass and your standing audits — because the failure mode here is inaccurate revenue, the most expensive kind of wrong number to hand a client.
Where this fits
Ecommerce revenue tracking touches the store platform, the dataLayer, GTM, GA4, and the ad platforms' pixels/CAPI — and a mismatch anywhere shows up as revenue that doesn't reconcile. Phloz models that chain per client — the purchase event, its dedup key, the items feed, the pixel/CAPI hand-offs — as part of the tracking-infrastructure map with a health state, so "is this store's revenue tracking actually accurate?" is something you verify on a schedule, not discover at the quarterly review. The CRM for ecommerce agencies and pricing pages cover the workflow — but the discipline is small: numeric value, real currency, unique transaction_id, populated items, and a refresh test. Get those right and the revenue adds up.