← All resources Tutorial

E-commerce tracking in GA4 with Google Tag Manager

Every revenue number in GA4, from the Monetization overview to product performance to purchase attribution in Google Ads, is built from one event with one strict shape: purchase, with a unique transaction_id, a value, a currency and an items array. Get that right and the rest of e-commerce tracking is a set of smaller, optional events that use the same items structure. Get it slightly wrong and GA4 shows revenue without products, products without revenue, or orders that quietly disappear. This article builds it properly with Tag Manager: the data layer, the tags, the test order, and the checks.

Part of the GA4 setup guide, step 9. You need the Google tag installed through Tag Manager and a developer, plugin or platform that can write order data into the page.

How GA4 e-commerce works

GA4 does not read your shop. It reads events your site sends, each carrying an items array of product objects. The recommended events, in funnel order: view_item_list, select_item, view_item, add_to_cart, remove_from_cart, view_cart, begin_checkout, add_shipping_info, add_payment_info, purchase, and refund. Only purchase is required for revenue; the others power the checkout funnel and product reports. Implement purchase first, verify it, then add the funnel events in order of how much you will actually look at them (usually view_item, add_to_cart and begin_checkout).

With Tag Manager, the shop pushes each event into the dataLayer, a Tag Manager trigger listens for it, and one GA4 event tag per event forwards it with the e-commerce data read straight from the data layer. The shop never talks to GA4 directly.

The data layer: the purchase push

On the order confirmation page, and only there, the site pushes this before Tag Manager loads or as soon as the order is known:

dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: "purchase",
  ecommerce: {
    transaction_id: "ORD-10482",
    value: 129.90,
    tax: 22.55,
    shipping: 4.95,
    currency: "EUR",
    coupon: "WELCOME10",
    items: [{
      item_id: "SKU-4471",
      item_name: "Trail running shoe",
      item_brand: "Northline",
      item_category: "Shoes",
      item_variant: "Blue / 43",
      price: 124.95,
      quantity: 1
    }]
  }
});

The rules that matter: ecommerce: null first, so a previous event's items do not leak into this one; transaction_id unique per order; value as a number, not a string, and in the same currency as currency; items as an array even for one product; price per unit, not per line. Whether value includes tax and shipping is your decision, but make it the same decision everywhere, including in Google Ads, or your return-on-ad-spend numbers will disagree with finance.

Shop platforms vary in what they push. The plugins for Shopify, WooCommerce and Shopware push the full recommended set in this shape; most themes and generic plugins push something close that needs checking against this list.

Tag Manager: the trigger and the tag

Create a Custom Event trigger with the event name purchase. Then a Google Analytics: GA4 Event tag: Measurement ID from your Google tag, event name purchase, and under More settings › Ecommerce tick Send Ecommerce data with the data source set to Data Layer. That single checkbox reads transaction_id, value, currency, items and the rest from the push; you do not map them one by one.

Repeat for each funnel event you implement: one trigger per event name, one tag per event, all with the ecommerce checkbox. Name the tags consistently (GA4 · purchase, GA4 · add_to_cart) so the container stays readable.

Place a test order and read DebugView

Use Tag Manager Preview, go through the shop and complete an order (a test payment method, a 100 percent coupon, or a real order you refund). On the confirmation page Tag Assistant should show the purchase trigger firing once and the GA4 tag with it. Then in GA4 open Admin › DebugView, click the purchase event and expand it: you should see the transaction parameters and, under Items, every product with its id, name, price and quantity.

If items are missing in DebugView while Tag Assistant shows them in the data layer, the tag is reading from the wrong source or the push happened after the tag fired. If value shows as 0, it was pushed as a string or in the wrong key.

The next day: Monetization reports

Twenty-four hours later, Reports › Monetization › Ecommerce purchases lists the products, and the overview shows revenue and transactions. Compare a day with your shop's order report: transaction count within one or two (declined payments and refunds explain small gaps), revenue matching to the cent if you chose the same definition of value. A large gap in transactions points at a confirmation page some payment methods skip (bank transfers, buy-now-pay-later redirects); a gap in revenue points at currency or tax definitions.

Mark purchase as a key event if it is not already (GA4 usually does it automatically), with counting once per event.

Five mistakes that break revenue

Refunds, and partial refunds

A refund event with the original transaction_id reverses the purchase in GA4's reports (full refund) or the listed items (partial, with items and their quantities). It has to come from the place that knows about refunds, which is rarely the browser; most shops send it server-to-server from the order system through the Measurement Protocol or a server container. If you do not send refunds, remember that GA4 revenue is gross of returns when you compare it with finance.

When you run GA4 through a server container

Everything above stays identical: the data layer, the triggers, the tags, the test order. The purchase hit travels from the browser to your server container and on to GA4 instead of straight to Google, which means ad blockers no longer remove it (on a typical Dutch shop, 10 to 20 percent of orders are invisible to browser-only GA4) and the GA4 cookie survives longer on Safari, so the purchase is attributed to the campaign that brought the visitor instead of to "direct".

Two things become possible that were not before. Server purchases: the shop's backend can send the order to the server container when payment is confirmed, catching the orders whose confirmation page never loaded; the Measurebase plugins for Shopify, WooCommerce and Shopware do this, and the two copies are deduplicated with a shared event ID, so GA4 counts the order once. Profit instead of revenue: the server container can look up the cost price of each item and add mb_profit to the purchase, which is what Profit Tracking does so that campaigns can be optimised on margin. The nightly audit checks that purchase events carry a transaction ID, items and a currency, and that browser and server copies deduplicate.

Frequently asked questions

Do I need all the funnel events?

No. purchase alone gives you revenue and product reports. Add view_item, add_to_cart and begin_checkout next; they power the checkout funnel report and most remarketing audiences. The rest are nice to have.

Should value include tax and shipping?

GA4 does not care, as long as you are consistent. Many shops use the order total the customer paid (including tax and shipping) because it matches the bank; some use net product revenue to compare with margin. Pick one, and use the same in Google Ads conversion values.

What currency code does GA4 expect?

A three-letter ISO code: EUR, USD, GBP. GA4 converts to the property currency in reports using daily rates, so multi-currency shops should send the order currency, not a converted amount.

Can GA4 deduplicate orders by transaction_id?

Within a session on the same device, yes: a reloaded confirmation page with the same transaction_id is ignored. Across devices, browsers or a later visit, no. Prevent the second push on the site rather than rely on it.

How do I test without placing a real order?

Most platforms have a test payment gateway or a 100 percent discount code. Place the order with Tag Manager Preview open so DebugView shows it, then cancel or refund it in the shop. Filter your own IP afterwards if the test order should not count.

Why does Google Ads show different purchase numbers than GA4?

Ads counts conversions on the click date with its own attribution and only for visitors it can tie to an ad click; GA4 counts on the event date across all channels. The totals should move together week by week; identical daily numbers are not expected.

My shop platform already sends e-commerce to GA4. Should I still use Tag Manager?

Only use one path. If the platform's integration sends complete, correct purchase data with items, keep it and do not add Tag Manager tags for the same events. If it is incomplete or you want control, turn it off and build it in Tag Manager as described here.

Continue reading

Ready to move your tagging to Europe?

Start free, no credit card required. Live in about 15 minutes.

Start free