google analytics4 min readBy Phloz team

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_itemadd_to_cartbegin_checkoutpurchase) 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 view
  • add_to_cart / remove_from_cart
  • begin_checkout
  • add_payment_info / add_shipping_info
  • purchase — the conversion that matters most
  • refund — 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:

  1. value — the order total, as a number, not the string "129.00" or "undefined". A string value is the single most common silent ROAS-wrecker. Confirm the type, not just the presence.
  2. currency — set, and correct (ISO 4217, e.g. USD). Missing currency makes value ambiguous and can drop the revenue entirely.
  3. transaction_id — a unique order ID. This is the deduplication key: GA4 ignores a second purchase with a transaction_id it 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.
  4. 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.