GA4 ecommerce tracking gives you the numbers that connect your marketing spend to actual sales: purchase revenue, conversion rate, average order value, and product-level performance. But it requires a specific dataLayer implementation — and any deviation breaks your funnel reports silently. Unlike Universal Analytics, where ecommerce implementations varied by platform and plugin, GA4 has a canonical event schema. Miss a required field or use the wrong data type and GA4 either drops the data entirely or undercounts revenue in ways that are difficult to detect.
This guide covers the six required funnel events, the correct dataLayer structure for each, the GTM implementation step by step, platform-specific notes for Shopify and WooCommerce, and the five mistakes responsible for most GA4 ecommerce problems. If you already have tracking in place and want to verify it, the GA4 auditor runs automated checks against your configuration including ecommerce event coverage.
The GA4 Ecommerce Event Schema
The 6 core funnel events
GA4's Shopping Behavior Analysis report and funnel visualizations are built on six specific event names. These are not suggestions — they are the identifiers GA4 recognises as ecommerce funnel steps:
- view_item — fired when a user views a product page. Populates the "Product Detail Views" step in funnel reports.
- add_to_cart — fired when a user adds a product to their cart. Feeds the "Add to Cart" step.
- begin_checkout — fired when the checkout flow starts (typically on the first checkout page). Feeds the "Initiated Checkout" step.
- add_shipping_info — fired when the user selects or confirms a shipping method.
- add_payment_info — fired when the user enters or selects a payment method.
- purchase — fired when the transaction is complete, on the order confirmation page.
Missing any one of these breaks the funnel at that step. If begin_checkout never fires, the checkout entry point in your funnel report shows zero, and you can't measure checkout drop-off. The events upstream and downstream still fire — the gap just looks like a data problem rather than a configuration problem, which is exactly why these omissions are easy to miss.
The items array
Every ecommerce event must include an items array. Each object in the array represents one product. The required and optional fields:
- item_id (required) — your SKU or product ID. This is the join key for product performance reports.
- item_name (required) — the product name as it should appear in reports.
- price (required) — a numeric value, not a string.
29.99not"29.99". - quantity (required for
add_to_cartandpurchase) — numeric, not a string. - item_brand (optional) — brand name, useful for brand-level reporting.
- item_category (optional) — top-level category. Use
item_category2throughitem_category5for subcategories. - item_list_name (optional) — the name of the list or collection where the product appeared (e.g., "Homepage Recommendations", "Search Results"). Required for list-level click-through reporting.
- item_variant (optional) — size, colour, or other variant identifier.
The currency parameter belongs at the event level, not the item level. Setting it on individual item objects has no effect — GA4 reads currency from the top-level event parameters only.
The purchase event
The purchase event is the most consequential event in any ecommerce implementation. It populates revenue, transaction count, and conversion rate. At minimum it requires:
- transaction_id — a unique identifier per order. This is how GA4 deduplicates purchases. Without it, every page reload of the confirmation page counts as an additional purchase. Use your platform's order ID.
- value — the total transaction revenue as a number. This should match the order total, not the sum of individual item prices (to handle discounts and shipping correctly).
- currency — ISO 4217 currency code:
"USD","EUR","GBP", etc. - items — the full array of purchased products.
Optional but useful: coupon (discount code used), shipping (shipping cost as a number), tax (tax amount as a number). These populate the corresponding columns in GA4's Monetisation reports.
Setting Up with Google Tag Manager
Step 1 — The dataLayer push
Your platform or development team needs to push ecommerce events to the dataLayer at the right moment. The structure is the same across all six events: the event name at the top level, and an ecommerce object containing the event parameters.
Best practice: push { ecommerce: null } immediately before each ecommerce push to clear any previous ecommerce data from the dataLayer. Without this, GTM may read stale data from the previous push.
A complete purchase push looks like this:
// Clear previous ecommerce data first
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: 'T-12345',
value: 89.99,
currency: 'USD',
coupon: 'SUMMER10',
shipping: 4.99,
tax: 7.20,
items: [
{
item_id: 'SKU-001',
item_name: 'Running Shoes',
item_brand: 'TrailMaster',
item_category: 'Footwear',
item_variant: 'Blue / Size 10',
price: 89.99,
quantity: 1
}
]
}
});
An add_to_cart push follows the same pattern with fewer required fields:
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: 'add_to_cart',
ecommerce: {
currency: 'USD',
value: 89.99,
items: [
{
item_id: 'SKU-001',
item_name: 'Running Shoes',
price: 89.99,
quantity: 1
}
]
}
});
Step 2 — Create the GTM trigger
In GTM, create a new GTM trigger for each ecommerce event name:
- Trigger Type: Custom Event
- Event Name: the exact event name —
purchase,add_to_cart, etc. (case-sensitive) - This trigger fires on: All Custom Events
You need one trigger per event name. Name them clearly: "CE - purchase", "CE - add_to_cart", and so on.
Step 3 — Create the GA4 Event tag
Create a new tag for each ecommerce event:
- Tag Type: Google Analytics: GA4 Event
- Configuration Tag: select your existing GA4 Config tag (or enter your Measurement ID directly)
- Event Name: the matching event name —
purchase - Event Parameters: add one parameter named
ecommercewith value{{dlv - ecommerce}}(a Data Layer Variable pointing to theecommercekey) - Triggering: select the matching Custom Event trigger
The data layer variable needs to be set up once and reused. In GTM's Variables section, create a new User-Defined Variable: type "Data Layer Variable", Variable Name: ecommerce. This reads the entire ecommerce object from the dataLayer and passes it to GA4 as a single parameter. GA4 then unpacks it into the standard ecommerce fields automatically.
Step 4 — Verify in DebugView
Before publishing, verify the implementation works:
- Enable GTM Preview Mode and open your site
- Complete the action you want to test (add to cart, reach the confirmation page, etc.)
- In GTM Preview, confirm the Custom Event fires and the GA4 tag triggers
- In GA4, go to Admin → DebugView
- Find the
purchaseevent and expand it — confirmvalue,transaction_id,currency, and theitemsarray are present and correct
DebugView shows events within a few seconds. If the event arrives without the ecommerce fields, the Data Layer Variable is likely misconfigured or the ecommerce object was not present in the dataLayer at fire time.
Platform-Specific Notes
Shopify
Shopify's native GA4 integration (Admin → Preferences → Google) sets up basic event tracking automatically, but it has a known limitation: item-level data in purchase events is incomplete. For accurate ecommerce reporting — including product-level revenue, category breakdowns, and full items arrays — you need a custom implementation via GTM.
For standard Shopify (non-Plus), the purchase event push goes in the "Additional Scripts" section of the Order Status page (Admin → Settings → Checkout → Order status page). The order object available in that liquid context provides what you need:
{% if first_time_accessed %}
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: '{{ order.order_number }}',
value: {{ order.total_price | divided_by: 100.0 }},
currency: '{{ order.currency }}',
items: [
{% for line_item in order.line_items %}
{
item_id: '{{ line_item.sku }}',
item_name: '{{ line_item.title }}',
price: {{ line_item.price | divided_by: 100.0 }},
quantity: {{ line_item.quantity }}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
}
});
{% endif %}
The {% if first_time_accessed %} guard prevents the push from firing on order status page reloads — this is your duplicate-purchase defence on Shopify.
For Shopify Plus merchants, use the Checkout Extensibility Pixel API instead of checkout.liquid. The checkout.liquid template is being deprecated for Plus stores.
WooCommerce
WooCommerce gives you more control than Shopify's sandboxed checkout, which means more implementation options — and more ways to introduce conflicts. The two main paths:
Plugin approach: plugins like MonsterInsights and WooCommerce Google Analytics Pro push dataLayer events automatically on cart interactions and checkout steps. They handle the PHP-to-JavaScript handoff for server-rendered WooCommerce pages. If you use a plugin, disable any native GA4 snippet you may have added manually — dual firing is a common source of duplicate events.
Custom code approach: add dataLayer pushes directly in functions.php using WooCommerce hooks. The woocommerce_thankyou hook fires on the order confirmation page and gives you access to the WC_Order object for building the purchase push. Use wc_get_order and check $order->get_status() before pushing to avoid firing on reloads of orders in non-complete states.
Custom and headless
On custom or headless storefronts, your development team implements the dataLayer pushes in JavaScript. The same event names and object structure apply — the only difference is that you control exactly when and how each push fires.
One important consideration for headless checkouts: if the payment step involves a redirect to a third-party payment processor and back, the confirmation page may not receive the dataLayer context from the original session. In these cases, use the GA4 Measurement Protocol as a server-side fallback. After a successful order is confirmed server-side, send the purchase event via Measurement Protocol with the same transaction_id. Because GA4 deduplicates on transaction_id, you won't get double-counted purchases even if the client-side event also fires.
The 5 Most Common GA4 Ecommerce Mistakes
These five errors account for the majority of ecommerce tracking problems. Some are silent — they don't throw errors, they just produce wrong numbers.
1. Duplicate purchase counting from confirmation page reloads. If the confirmation page is reloaded, a browser back-forward navigation replays it, or the page is bookmarked and revisited, GA4 fires the purchase event again. The fix is transaction_id: GA4 deduplicates purchase events with the same transaction_id within a 24-hour window for the same client. Without it, every visit to the confirmation page is a new purchase in your reports. Set transaction_id to your platform's order ID on every implementation.
2. Revenue passed as a string instead of a number. GA4 requires value and price to be numeric. If your platform renders prices as strings — which is common when pulling from templating systems — you end up with value: "89.99" instead of value: 89.99. GA4 rejects non-numeric revenue values and the purchase registers with £0 revenue. Parse to a float: parseFloat("89.99") or in Liquid: {{ order.total_price | divided_by: 100.0 }}. The same applies to quantity.
3. Missing ecommerce null push. Without pushing { ecommerce: null } before each ecommerce event, GTM's Data Layer Variable may read stale data from a previous push. This is most visible on category/search pages where view_item_list events fire in sequence — if the null push is missing, item 2's push may include items from item 1. The ecommerce: null push is considered a required best practice in the GA4 ecommerce documentation.
4. Missing currency at the event level. GA4 reads currency from the top-level ecommerce object, not from individual items. If currency is set on items but not at the event level, GA4 cannot interpret the revenue values. Revenue shows as 0 in reports. Add currency to the ecommerce object directly alongside value.
5. Using UA parameter names in GA4. Universal Analytics used revenue as the purchase event parameter name. GA4 uses value. If you migrated an existing UA ecommerce setup to GA4 without updating the field names, your revenue has been reporting as zero since migration. Other renamed fields: UA's id → GA4's transaction_id; UA's name (item) → GA4's item_name; UA's category → GA4's item_category.
FAQ
How do I set up GA4 ecommerce tracking?
Set up GA4 ecommerce tracking by implementing dataLayer pushes for the six core events (view_item, add_to_cart, begin_checkout, add_shipping_info, add_payment_info, purchase) via Google Tag Manager. Each push includes an ecommerce object with the required fields: items array, currency, and for the purchase event, transaction_id and value. In GTM, create one Custom Event trigger and one GA4 Event tag per event, with a Data Layer Variable reading the ecommerce object.
What are the required GA4 ecommerce events?
The six required events for GA4's built-in ecommerce funnel reports are: view_item, add_to_cart, begin_checkout, add_shipping_info, add_payment_info, and purchase. Of these, purchase is the most critical — it drives revenue, transaction count, and conversion rate. Missing any event breaks the funnel visualization at that step, but the events upstream and downstream continue to fire normally.
What is the items array in GA4?
The items array is a required parameter in every GA4 ecommerce event. It contains one object per product involved in the action, with required fields item_id (your SKU), item_name, price (numeric), and quantity (required for add_to_cart and purchase events). Optional fields include item_brand, item_category, item_variant, and item_list_name. The items array is how GA4 attributes revenue and actions to individual products in product-level reports.
How do I prevent duplicate purchase events in GA4?
Prevent duplicate purchase events by including a unique transaction_id in every purchase event push — typically your platform's order ID. GA4 deduplicates purchase events with matching transaction_id values within a 24-hour window for the same client. On Shopify, wrap the confirmation page push in {% if first_time_accessed %} to prevent the push from firing on page reloads. On server-side implementations, check order status before pushing to avoid firing on non-complete order states.
Does GA4 ecommerce tracking work with Shopify?
Yes, but Shopify's native GA4 integration (via Admin → Preferences → Google) only tracks basic events with incomplete item data. For full ecommerce accuracy — correct revenue, item-level data, and all six funnel events — implement via Google Tag Manager using the Additional Scripts section of the Order Status page. Use {% if first_time_accessed %} to prevent duplicate purchase events on page reload, and map Shopify's order.total_price (in cents) to GA4's value field by dividing by 100.
What is the difference between GA4 and UA ecommerce tracking?
GA4 ecommerce uses a standardised event schema that differs from Universal Analytics in several ways. The revenue field is now called value (not revenue). Items use item_id instead of id, item_name instead of name, and item_category instead of category. GA4 introduces required currency at the event level, a mandatory transaction_id for deduplication, and the new funnel events add_shipping_info and add_payment_info which did not exist in UA. UA's Enhanced Ecommerce had optional steps; GA4's funnel steps are all required for funnel reporting to work correctly.
How do I verify GA4 ecommerce is working?
Verify GA4 ecommerce by using GTM Preview Mode alongside GA4 DebugView (Admin → DebugView). Enable Preview Mode, trigger an ecommerce event on your site, and confirm in GTM that the Custom Event fired and the GA4 tag triggered. In GA4's DebugView, find the event and expand it to verify the ecommerce parameters — specifically that value is a number (not a string), transaction_id is present, currency is set at the event level, and the items array contains the correct product data. Revenue in DebugView should match the pushed value exactly.
What does transaction_id do in GA4 purchase events?
transaction_id is a unique identifier for each order that GA4 uses to deduplicate purchase events. If the same transaction_id is received more than once within 24 hours for the same client, GA4 counts it as one purchase — not two. This prevents your revenue from inflating when a user reloads the order confirmation page, uses the browser back button, or bookmarks and revisits the page. Set transaction_id to your platform's order number or order ID. Without transaction_id, every confirmation page visit is recorded as a separate purchase.
Check your GA4 ecommerce setup automatically
The GA4 auditor checks for ecommerce event coverage, missing transaction IDs, revenue field types, and 58 other configuration issues — without needing access to your raw data.
Run a free GA4 auditAnalytics consultant turned founder. After years running the same GA4 and GTM audits across client engagements, Ludde built the audit into a product — so the pattern-matching takes a minute, not a meeting. More about Ludde →
Run a free GA4 audit.
Connect your Google Analytics 4 property. Our auditor runs 61 checks and gives you an instant health score with a plain-English action plan.