
Why WooCommerce Purchase Tracking Requires a Custom dataLayer Push
WooCommerce does not natively push GA4-compatible purchase event data to the dataLayer. Out of the box, WooCommerce either has no dataLayer at all, or pushes an older Universal Analytics-style transaction format that GA4 cannot use directly. If you install GTM on a WooCommerce store and try to track purchases without a custom dataLayer implementation, you will either see no purchase events in GA4 or see malformed data missing critical ecommerce parameters like item arrays, currency, and transaction ID.
The solution is to add a custom dataLayer push to WooCommerce’s order confirmation (thank you) page that outputs properly structured GA4 ecommerce data from PHP—using the WooCommerce order object to populate every required field. Then in GTM, you create a tag that reads this dataLayer push and sends a GA4 purchase event. This approach gives you 100% reliable purchase tracking that fires once per order, includes complete item-level data, and does not depend on fragile client-side JavaScript that can break when users navigate away mid-checkout.
Step 1: Add the PHP dataLayer Push to WooCommerce
Add the following PHP code to your WooCommerce theme’s functions.php file or a custom plugin. This code hooks into WooCommerce’s woocommerce_thankyou action, which fires on the order confirmation page after a successful purchase. It builds a complete GA4 purchase event dataLayer push using the WooCommerce order object:
add_action( 'woocommerce_thankyou', 'analytigrow_ga4_purchase_datalayer', 10, 1 );
function analytigrow_ga4_purchase_datalayer( $order_id ) {
if ( ! $order_id ) return;
// Prevent duplicate firing on page refresh
if ( get_post_meta( $order_id, '_ga4_purchase_tracked', true ) ) return;
update_post_meta( $order_id, '_ga4_purchase_tracked', '1' );
$order = wc_get_order( $order_id );
if ( ! $order ) return;
$items = [];
foreach ( $order->get_items() as $item_id => $item ) {
$product = $item->get_product();
$categories = wp_get_post_terms( $item->get_product_id(), 'product_cat', [ 'fields' => 'names' ] );
$items[] = [
'item_id' => $product ? $product->get_sku() : $item->get_product_id(),
'item_name' => $item->get_name(),
'item_brand' => get_post_meta( $item->get_product_id(), '_brand', true ) ?: '',
'item_category' => ! empty( $categories ) ? $categories[0] : '',
'item_variant' => $product ? $product->get_attribute_summary() : '',
'price' => (float) $order->get_item_subtotal( $item, false ),
'quantity' => $item->get_quantity(),
];
}
$data = [
'event' => 'purchase',
'ecommerce' => [
'transaction_id' => (string) $order->get_order_number(),
'value' => (float) $order->get_total(),
'tax' => (float) $order->get_total_tax(),
'shipping' => (float) $order->get_shipping_total(),
'currency' => get_woocommerce_currency(),
'coupon' => implode( ', ', $order->get_coupon_codes() ),
'items' => $items,
],
];
echo '<script>window.dataLayer = window.dataLayer || []; dataLayer.push({ ecommerce: null }); dataLayer.push(' . wp_json_encode( $data ) . ');</script>';
}
The dataLayer.push({ ecommerce: null }) line before the purchase push is critical. GA4 requires clearing the previous ecommerce object before pushing a new one, or parameters from earlier events can bleed into the purchase event. This pattern is GA4’s officially recommended approach for ecommerce tracking.
Step 2: Verify the dataLayer Push in GTM Preview
After adding the PHP code, place a test order on your WooCommerce store and activate GTM Preview mode. On the order confirmation (thank you) page, check the dataLayer events panel in the GTM preview pane. You should see a “purchase” event appear. Click on it and examine the event data. Verify that the ecommerce object contains a valid transaction_id, correct value, the right currency, and a populated items array with at least one item containing item_id, item_name, price, and quantity.
If the event does not appear, check that your GTM container snippet is firing on the thank you page—some WooCommerce themes and caching plugins strip GTM from checkout pages for performance reasons. If the event appears but the items array is empty, verify that the order has items attached and that your WooCommerce order IDs are being retrieved correctly.
Step 3: Create the GA4 Purchase Tag in GTM
In GTM, create a new tag of type “Google Analytics: GA4 Event.” Set the Measurement ID to your GA4 property ID (G-XXXXXXXXXX). Set the Event Name to “purchase.” Under “Ecommerce,” check the box labeled “Send Ecommerce data” and set the source to “Data Layer.” This tells the GA4 tag to automatically read ecommerce data from the ecommerce object in the dataLayer, rather than requiring you to manually map every parameter.
For the trigger, create a new Custom Event trigger. Set the “Event name” field to “purchase” (matching exactly the event name in your dataLayer push). This trigger fires the GA4 tag whenever a dataLayer event named “purchase” is detected. Save the tag and trigger, then publish the GTM container.
Step 4: Prevent Duplicate Purchase Tracking
The PHP code above uses a post meta flag (_ga4_purchase_tracked) to prevent the dataLayer push from firing again if the thank you page is refreshed. This server-side deduplication is important because without it, a user who refreshes the order confirmation page would send a duplicate purchase event to GA4, inflating your revenue and conversion counts.
Add a second layer of client-side deduplication using session storage. After the GTM purchase tag fires successfully, store the transaction ID in sessionStorage. Modify the GTM trigger to include an exception: if the transaction ID already exists in sessionStorage, block the tag from firing. This two-layer approach—server-side post meta plus client-side session storage—makes duplicate purchase tracking virtually impossible even across edge cases like slow network connections or browser back-button behavior.
Step 5: Test in GA4 DebugView
After publishing your GTM container, enable GA4 DebugView by adding the query parameter ?gtm_debug=x to your site URL while in GTM preview mode. Place another test order. In the GA4 interface, navigate to Admin → DebugView. Your test purchase event should appear within 1–2 minutes. Click on the purchase event in DebugView to expand it and verify all ecommerce parameters: transaction_id, value, currency, and the items array. Check that the item data matches what you expect from your test order—item name, SKU, price, quantity, and category should all be present.
Handling WooCommerce Subscription and Variable Products
If your WooCommerce store sells variable products (products with size, color, or other variations), the item_variant field in the dataLayer push should reflect the specific variation chosen by the customer. The PHP code above uses get_attribute_summary() to capture this, but you may want to customize it to match your specific attribute names. For subscription products using WooCommerce Subscriptions, send the initial purchase event normally—but be aware that renewal payments create new WooCommerce orders and will trigger additional purchase events for each renewal. This is correct behavior for GA4 revenue tracking but may require filtering in your reports to distinguish initial purchases from renewals.
Conclusion
A custom GTM dataLayer push for WooCommerce purchase events gives you reliable, complete, and GA4-compatible transaction tracking that the default WooCommerce setup cannot provide. By pushing structured ecommerce data from PHP on the order confirmation page, using GTM to relay it to GA4, and implementing server-side deduplication to prevent double-counting, you build a purchase tracking foundation you can trust. Every conversion, every item, every revenue dollar will be captured accurately—giving your marketing and finance teams the data they need to make confident decisions.