'woo_conversion_tracking', 'title' => 'WooCommerce conversion tracking', 'desc' => 'Send product view, add-to-cart and purchase events to GTM, GA4 or Meta Pixel', 'icon' => 'code', 'risk' => 'safe', 'level' => 'standard', 'audience' => 'woocommerce', 'priority' => 2, 'benefit' => 'Sends WooCommerce shop events to your analytics and ad tools, so you can see which visits and campaigns led to sales. The tools themselves still need to be set up to use these events.', 'help' => 'Sends view_item on product pages, add_to_cart when an add-to-cart button is clicked, and purchase once per order on the order-received page. Auto mode pushes events to the dataLayer only when a GTM container is in use (GTM in Tracking codes, or Site Kit with its Tag Manager module); otherwise it calls gtag and fbq directly. Direct mode only works when the GA4 tag or Meta Pixel is already on the page, and in direct mode add-to-cart goes to Meta Pixel only, not to GA4.', 'before' => 'No shop events in your analytics, or the same purchase counted twice through GTM and gtag', 'after' => 'Each event sent once per tool, by the route that matches your setup.', 'extras' => array( array( 'key' => 'woo_tracking_mode', 'type' => 'select', 'label' => 'Emission mode', 'default' => 'auto', 'options' => array( 'auto' => 'Auto — detect stack (recommended)', 'datalayer' => 'dataLayer only (your GTM container sends the events)', 'direct' => 'Direct gtag / fbq (no GTM in use)', ), 'desc' => 'Auto uses the dataLayer when a GTM container is in use (GTM in Tracking codes, or Site Kit with its Tag Manager module); otherwise it calls gtag and fbq directly. Change it only if you see duplicate or missing conversions.', ), ), 'detect' => function () { if ( ! function_exists( 'is_plugin_active' ) ) { include_once ABSPATH . 'wp-admin/includes/plugin.php'; } $woo = is_plugin_active( 'woocommerce/woocommerce.php' ); if ( ! $woo ) { return array( 'detected' => false, 'message' => 'WooCommerce not active — this feature requires WooCommerce', 'safe' => false, ); } return array( 'detected' => true, 'message' => 'WooCommerce detected — conversion tracking recommended', 'safe' => true, 'savings' => 'No extra plugins needed', ); }, 'callback' => function () { if ( ! class_exists( 'WooCommerce' ) ) { ovkit_log( 'Woo Conversion Tracking: WooCommerce not loaded, skipping', null, 'warn' ); return; } ovkit_log( 'Woo Conversion Tracking: initializing' ); // Resolve effective emission mode. The goal is exactly one emission // path per event: when a tag manager is in play, we only push to // dataLayer and let the tag manager fan out to gtag / fbq. Without // one, we call gtag / fbq directly. $resolve_mode = function () { $mode = ovkit_get_setting( 'woo_tracking_mode', 'auto' ); if ( in_array( $mode, array( 'datalayer', 'direct' ), true ) ) { return $mode; } if ( ! function_exists( 'is_plugin_active' ) ) { include_once ABSPATH . 'wp-admin/includes/plugin.php'; } // Site Kit counts as a tag manager only with its Tag Manager module on. With only its // Analytics module it loads gtag.js, which ignores plain dataLayer events. $sitekit = is_plugin_active( 'google-site-kit/google-site-kit.php' ) && in_array( 'tagmanager', (array) get_option( 'googlesitekit_active_modules', array() ), true ); // A GTM container counts only if Toolkit is actually emitting it. // tracking_codes feature gates per-service enable flags; when it // is off, a stored GTM id is not loaded into the page, so we // cannot rely on GTM to fan events out. $gtm_id = trim( (string) ovkit_get_setting( 'track_gtm_id', '' ) ); $tc_on = ovkit_is_enabled( 'tracking_codes' ); $gtm_active = '' !== $gtm_id && $tc_on && (bool) ovkit_get_setting( 'track_gtm_enable', 0 ); return ( $sitekit || $gtm_active ) ? 'datalayer' : 'direct'; }; add_action( 'wp_enqueue_scripts', function () use ( $resolve_mode ) { $ga4_id = trim( (string) ovkit_get_setting( 'track_ga4_id', '' ) ); $meta_id = trim( (string) ovkit_get_setting( 'track_meta_pixel', '' ) ); if ( ovkit_is_enabled( 'tracking_codes' ) ) { if ( ! ovkit_get_setting( 'track_ga4_enable', 0 ) ) { $ga4_id = ''; } if ( ! ovkit_get_setting( 'track_meta_enable', 0 ) ) { $meta_id = ''; } } $config = array( 'mode' => $resolve_mode(), // Site Kit's Analytics module also provides gtag() for direct GA4 events. 'ga4' => '' !== $ga4_id || ( is_plugin_active( 'google-site-kit/google-site-kit.php' ) && in_array( 'analytics-4', (array) get_option( 'googlesitekit_active_modules', array() ), true ) ), 'meta' => '' !== $meta_id, 'currency' => get_woocommerce_currency(), 'events' => array(), ); if ( is_product() ) { $product = wc_get_product( get_queried_object_id() ); if ( $product ) { $item = array( 'item_id' => $product->get_sku() ? $product->get_sku() : (string) $product->get_id(), 'item_name' => $product->get_name(), 'price' => (float) $product->get_price(), 'currency' => get_woocommerce_currency(), ); $cat = wp_get_post_terms( $product->get_id(), 'product_cat', array( 'fields' => 'names' ) ); if ( ! empty( $cat ) && ! is_wp_error( $cat ) ) { $item['item_category'] = $cat[0]; } $config['events'][] = array( 'type' => 'view_item', 'currency' => get_woocommerce_currency(), 'value' => (float) $product->get_price(), 'item' => $item, ); } } if ( is_checkout() && is_wc_endpoint_url( 'order-received' ) ) { global $wp; $order_id = absint( $wp->query_vars['order-received'] ?? 0 ); $order = $order_id ? wc_get_order( $order_id ) : null; // Each order is reported once, even if the thank-you page is reloaded. if ( $order && ! $order->get_meta( '_ovkit_tracked' ) ) { $items = array(); foreach ( $order->get_items() as $line ) { $prod = $line->get_product(); $items[] = array( 'item_id' => $prod ? ( $prod->get_sku() ? $prod->get_sku() : (string) $prod->get_id() ) : '', 'item_name' => $line->get_name(), 'price' => (float) $line->get_total() / max( 1, $line->get_quantity() ), 'quantity' => $line->get_quantity(), ); } $config['events'][] = array( 'type' => 'purchase', 'transaction_id' => (string) $order->get_id(), 'value' => (float) $order->get_total(), 'currency' => $order->get_currency(), 'items' => $items, ); $order->update_meta_data( '_ovkit_tracked', '1' ); $order->save(); } } wp_enqueue_script( 'ovkit-woo-tracking', OVKIT_URL . 'assets/js/woo-tracking.js', array(), OVKIT_VERSION, array( 'in_footer' => true ) ); wp_add_inline_script( 'ovkit-woo-tracking', 'window.ovkitWooTracking = ' . wp_json_encode( $config ) . ';', 'before' ); } ); }, );