Skip to content

Instantly share code, notes, and snippets.

@mattpramschufer
Created August 18, 2025 19:12
Show Gist options
  • Save mattpramschufer/1619723ef12a814790d6edeaa959aa49 to your computer and use it in GitHub Desktop.
Save mattpramschufer/1619723ef12a814790d6edeaa959aa49 to your computer and use it in GitHub Desktop.
Fires WooCommerce purchase event on thankyou page.
<?php
/**
* Push GA4 purchase to dataLayer directly from WooCommerce thankyou hook.
* - Fires exactly when woocommerce_thankyou runs (confirmed on your site)
* - Includes a per-order/browser dedupe guard
* - Uses GA4-standard ecommerce payload (transaction_id, value, items, etc.)
*/
add_action( 'woocommerce_thankyou', function ( $order_id ) {
if ( ! $order_id || ! function_exists( 'wc_get_order' ) ) {
return;
}
$order = wc_get_order( $order_id );
if ( ! $order ) {
return;
}
// Only successful purchases (adjust if you also want 'on-hold' or 'completed' only)
if ( ! $order->has_status( array( 'processing', 'completed' ) ) ) {
return;
}
$currency = $order->get_currency();
$value = (float) $order->get_total();
$tax = (float) $order->get_total_tax();
$shipping = (float) $order->get_shipping_total();
$coupon = $order->get_coupon_codes() ? implode( ',', $order->get_coupon_codes() ) : '';
$items = array();
foreach ( $order->get_items() as $item_id => $item ) {
$product = $item->get_product();
$sku_or_id = $product ? ( $product->get_sku() ?: $product->get_id() ) : $item_id;
$cat_path = '';
if ( $product && function_exists( 'wc_get_product_category_list' ) ) {
$cat_path = wp_strip_all_tags( wc_get_product_category_list( $product->get_id(), ' / ' ) );
}
$items[] = array(
'item_id' => (string) $sku_or_id,
'item_name' => (string) $item->get_name(),
'quantity' => (int) $item->get_quantity(),
'price' => (float) $order->get_item_total( $item, false, true ), // unit price ex tax
'item_category' => $cat_path ?: '',
);
}
$payload = array(
'transaction_id' => (string) $order->get_order_number(),
'value' => $value,
'currency' => $currency,
'tax' => $tax,
'shipping' => $shipping,
'coupon' => $coupon,
'items' => array_values( $items ),
);
// Print the push right where the hook runs (works even if wp_footer is missing)
// Includes a sessionStorage guard to avoid re-push on reload/back
echo '<script id="emx-purchase-' . (int) $order_id . '">';
echo '(function(){try{';
echo 'var k="emx_purchase_pushed_' . (int) $order_id . '";';
echo 'if(sessionStorage.getItem(k))return;';
echo 'window.dataLayer=window.dataLayer||[];';
echo 'dataLayer.push({event:"purchase",ecommerce:' . wp_json_encode( $payload ) . '});';
// Optional: also push GTM4WP legacy name in case your trigger listens for it
echo 'dataLayer.push({event:"gtm4wp.orderCompletedEEC",ecommerce:' . wp_json_encode( $payload ) . '});';
// Clear ecommerce object to prevent accidental reuse
echo 'dataLayer.push({ecommerce:null});';
echo 'sessionStorage.setItem(k,"1");';
echo 'console.log("[emx] purchase pushed for order #' . (int) $order_id . '", ' . wp_json_encode( $payload ) . ');';
echo '}catch(e){console&&console.warn&&console.warn("emx purchase push error",e);} })();';
echo '</script>' . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
// Tiny HTML breadcrumb for quick view-source debugging
echo "\n<!-- emx: pushed GA4 purchase for order #{$order_id} -->\n";
}, 20 );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment