OnyxMetric
Integration Guides

WooCommerce

Add OnyxMetric analytics to your WooCommerce store

WooCommerce runs on WordPress, so the tag goes in the same way as on any WordPress site. This guide adds the WooCommerce action hooks that send purchases, add-to-carts, product views, and checkouts to OnyxMetric as custom events.

Copy your tracking snippet

In OnyxMetric, open your site's Site Settings and copy the tracking script. It looks like this:

<script src="https://app.onyxmetric.com/api/script.js?siteId=YOUR_SITE_ID" async></script>

YOUR_SITE_ID is the numeric ID of your site.

Add the snippet to WooCommerce

Follow the WordPress guide to add the snippet. To track the e-commerce events below, use the functions.php or mini-plugin method, since the event hooks are PHP and belong in the same file.

function onyxmetric_add_tracking_script() {
    ?>
    <script src="https://app.onyxmetric.com/api/script.js?siteId=YOUR_SITE_ID" async></script>
    <?php
}
add_action( 'wp_head', 'onyxmetric_add_tracking_script' );

Check that it works

Load your live site and click through a few pages. Pageviews show up in the OnyxMetric dashboard within seconds.

If nothing arrives:

  • View the page source and search for script.js?siteId= to confirm the tag is present.
  • In the browser's Network tab, check that script.js returns 200 and that requests go to /api/track.
  • Turn off ad blockers, or proxy the script through your own domain.
  • Read script troubleshooting for other causes.

Track custom events

WooCommerce fires PHP action hooks at each stage of the shopping flow. The code below prints a small inline script from those hooks. Each waits for DOMContentLoaded so that window.onyx exists before it is called.

Add these to your child theme's functions.php or a site-specific plugin, never to the parent theme.

Purchase

woocommerce_thankyou fires on the order-received page with the order ID.

add_action( 'woocommerce_thankyou', 'onyxmetric_track_purchase', 10, 1 );
function onyxmetric_track_purchase( $order_id ) {
    $order = wc_get_order( $order_id );
    if ( ! $order ) {
        return;
    }

    $items = array();
    foreach ( $order->get_items() as $item ) {
        $product = $item->get_product();
        $items[] = array(
            'item_id'   => $product && $product->get_sku() ? $product->get_sku() : $item->get_product_id(),
            'item_name' => $item->get_name(),
            'price'     => (float) $order->get_line_subtotal( $item, false, false ),
            'quantity'  => (int) $item->get_quantity(),
        );
    }

    $purchase = array(
        'transaction_id' => $order->get_order_number(),
        'value'          => (float) $order->get_total(),
        'tax'            => (float) $order->get_total_tax(),
        'shipping'       => (float) $order->get_shipping_total(),
        'currency'       => $order->get_currency(),
        'items'          => $items,
    );
    ?>
    <script>
    document.addEventListener('DOMContentLoaded', function () {
        window.onyx.event('purchase', <?php echo wp_json_encode( $purchase ); ?>);
    });
    </script>
    <?php
}

Event properties are capped at 2048 characters of JSON, so trim items to ID, quantity, and price, or drop it, for large orders.

Add to cart

Two hooks cover both paths: the product-page form submit, and WooCommerce's added_to_cart jQuery event for AJAX buttons on shop and category pages.

// Single product page: track on form submit
add_action( 'woocommerce_after_add_to_cart_button', 'onyxmetric_track_add_to_cart' );
function onyxmetric_track_add_to_cart() {
    global $product;
    if ( ! $product instanceof WC_Product ) {
        return;
    }
    $data = array(
        'item_id'   => $product->get_sku() ? $product->get_sku() : $product->get_id(),
        'item_name' => $product->get_name(),
        'price'     => (float) $product->get_price(),
        'currency'  => get_woocommerce_currency(),
    );
    ?>
    <script>
    document.addEventListener('DOMContentLoaded', function () {
        var form = document.querySelector('form.cart');
        if (!form) return;
        form.addEventListener('submit', function () {
            var data = <?php echo wp_json_encode( $data ); ?>;
            var qty = form.querySelector('input.qty');
            data.quantity = qty && parseInt(qty.value, 10) > 0 ? parseInt(qty.value, 10) : 1;
            window.onyx.event('add_to_cart', data);
        });
    });
    </script>
    <?php
}

// AJAX add-to-cart buttons on shop and category pages
add_action( 'wp_footer', 'onyxmetric_track_ajax_add_to_cart' );
function onyxmetric_track_ajax_add_to_cart() {
    if ( is_admin() ) {
        return;
    }
    ?>
    <script>
    jQuery(document.body).on('added_to_cart', function (event, fragments, cartHash, $button) {
        if (!$button || !$button.length) return;
        window.onyx.event('add_to_cart', {
            item_id: $button.data('product_id'),
            quantity: $button.data('quantity') || 1,
            source: 'ajax_add_to_cart'
        });
    });
    </script>
    <?php
}

The AJAX handler sees only the product ID and quantity from the button's data-* attributes. Add attributes to the button in your theme if you also want name and price.

View product and begin checkout

The same pattern works for the remaining hooks:

  • woocommerce_after_single_product_summary runs on product pages. Guard with is_product(), then send view_item with item_id, item_name, price, and currency from the global $product.
  • woocommerce_before_checkout_form runs on the checkout page. Build begin_checkout from WC()->cart: loop over get_cart() for the items and send the cart total as value.

Troubleshooting

  • Events fire before the tracker loads: keep the tracking snippet in wp_head and the DOMContentLoaded wrapper in place. If a call still runs too early, wrap it in window.onyx.onReady().
  • AJAX carts and mini-carts: themes that update the cart without a page load never hit the PHP hooks. Rely on the added_to_cart listener for those and test each add-to-cart path.
  • Other analytics plugins: plugins that also print e-commerce data layers can conflict. Test with a real order after enabling both.

Next steps

On this page