Enhanced Ecommerce Tracking | OpsBlu Docs

Enhanced Ecommerce Tracking

Implement complete GA4 ecommerce tracking for OpenCart sales funnel and revenue analytics

Overview

GA4 Enhanced Ecommerce provides comprehensive tracking of the customer journey from product discovery to purchase completion. This includes:

  • Product impressions and clicks
  • Add to cart and remove from cart
  • Checkout steps and abandonment
  • Transaction and revenue tracking
  • Refunds and returns

Implementation Approaches

Premium marketplace extensions provide complete ecommerce tracking out-of-the-box:

iSenseLabs Enhanced Google Analytics 4

Installation

Admin Panel > Extensions > Installer > Upload extension
Admin Panel > Extensions > Extensions > Analytics > Install
Admin Panel > Extensions > Extensions > Analytics > Edit > Configure

Enable ecommerce features:

  • Enhanced Ecommerce: Enabled
  • Track Transactions: Enabled
  • Track Refunds: Enabled
  • Product Impressions: Enabled

Approach 2: Custom Implementation

For full control or custom requirements, implement ecommerce tracking manually.

Product Impression Tracking

Track when products are viewed in listings (category pages, search results, homepage).

Category Page Impressions

File: catalog/controller/product/category.php

Add to the index() method after product loading:

// After $data['products'] is populated

$data['ga4_items'] = array();

foreach ($data['products'] as $index => $product) {
    $data['ga4_items'][] = array(
        'item_id' => $product['product_id'],
        'item_name' => $product['name'],
        'item_list_name' => 'Category: ' . $this->document->getTitle(),
        'item_category' => $this->getCategoryPath($category_id),
        'price' => $this->currency->format($product['price'], $this->session->data['currency'], '', false),
        'index' => $index,
        'quantity' => 1
    );
}

File: catalog/view/theme/[your-theme]/template/product/category.twig

Add before closing </body> or in footer:

{% if ga4_items %}
<script>
gtag('event', 'view_item_list', {
    'item_list_id': 'category_{{ category_id }}',
    'item_list_name': '{{ heading_title }}',
    'items': {{ ga4_items|json_encode|raw }}
});
</script>
{% endif %}

Product Click Tracking

File: catalog/view/theme/[your-theme]/template/product/category.twig

Add click handler for product links:

<script>
$(document).ready(function() {
    $('.product-thumb').each(function(index) {
        $(this).on('click', 'a', function() {
            var productName = $(this).closest('.product-thumb').find('.caption h4 a').text().trim();
            var productId = $(this).attr('href').match(/product_id=(\d+)/);

            if (productId) {
                gtag('event', 'select_item', {
                    'item_list_id': 'category_{{ category_id }}',
                    'item_list_name': '{{ heading_title }}',
                    'items': [{
                        'item_id': productId[1],
                        'item_name': productName,
                        'index': index
                    }]
                });
            }
        });
    });
});
</script>

Product Detail View Tracking

File: catalog/controller/product/product.php

Add to index() method:

// After product data is loaded

if ($product_info) {
    // Get category name
    $categories = $this->model_catalog_product->getCategories($product_id);
    $category_name = '';

    if ($categories) {
        $category_info = $this->model_catalog_category->getCategory($categories[0]['category_id']);
        $category_name = $category_info ? $category_info['name'] : '';
    }

    $data['ga4_product'] = array(
        'item_id' => $product_info['product_id'],
        'item_name' => $product_info['name'],
        'item_category' => $category_name,
        'price' => $this->currency->format($product_info['price'], $this->session->data['currency'], '', false),
        'currency' => $this->session->data['currency']
    );
}

File: catalog/view/theme/[your-theme]/template/product/product.twig

{% if ga4_product %}
<script>
gtag('event', 'view_item', {
    'currency': '{{ ga4_product.currency }}',
    'value': {{ ga4_product.price }},
    'items': [{{ ga4_product|json_encode|raw }}]
});
</script>
{% endif %}

Add to Cart Tracking

Frontend Implementation

File: catalog/view/theme/[your-theme]/template/product/product.twig

<script>
$('#button-cart').on('click', function() {
    var productId = $('input[name="product_id"]').val();
    var productName = $('h1').first().text().trim();
    var quantity = parseInt($('input[name="quantity"]').val());
    var priceText = $('.price-new').length ? $('.price-new').text() : $('.price').text();
    var price = parseFloat(priceText.replace(/[^0-9.]/g, ''));

    gtag('event', 'add_to_cart', {
        'currency': '{{ currency_code }}',
        'value': price * quantity,
        'items': [{
            'item_id': productId,
            'item_name': productName,
            'price': price,
            'quantity': quantity
        }]
    });
});
</script>

AJAX Add to Cart (with Success Confirmation)

File: catalog/view/theme/[your-theme]/template/common/cart.twig

Modify the cart add function:

<script>
var cart = {
    'add': function(product_id, quantity) {
        $.ajax({
            url: 'index.php?route=checkout/cart/add',
            type: 'post',
            data: 'product_id=' + product_id + '&quantity=' + (typeof(quantity) != 'undefined' ? quantity : 1),
            dataType: 'json',
            success: function(json) {
                if (json['success']) {
                    // Get product data from page
                    var productName = $('h1').first().text().trim();
                    var price = parseFloat($('.price-new, .price').first().text().replace(/[^0-9.]/g, ''));
                    var qty = typeof(quantity) != 'undefined' ? quantity : 1;

                    // Track add to cart
                    gtag('event', 'add_to_cart', {
                        'currency': '{{ currency_code }}',
                        'value': price * qty,
                        'items': [{
                            'item_id': product_id,
                            'item_name': productName,
                            'price': price,
                            'quantity': qty
                        }]
                    });

                    // Original success notification code
                    $('#cart > button').html('<i class="fa fa-shopping-cart"></i> ' + json['total']);
                    $('html, body').animate({ scrollTop: 0 }, 'slow');
                    $('#cart > ul').load('index.php?route=common/cart/info ul li');
                }
            }
        });
    }
};
</script>

Remove from Cart Tracking

File: catalog/view/theme/[your-theme]/template/checkout/cart.twig

<script>
$(document).on('click', '.btn-danger[onclick*="cart.remove"]', function() {
    var row = $(this).closest('tr');
    var productName = row.find('td:eq(1) a').text().trim();
    var productId = $(this).attr('onclick').match(/cart\.remove\('(\d+)'\)/)[1];
    var price = parseFloat(row.find('.text-right:eq(0)').text().replace(/[^0-9.]/g, ''));
    var quantity = parseInt(row.find('input[name^="quantity"]').val());

    gtag('event', 'remove_from_cart', {
        'currency': '{{ currency_code }}',
        'value': price * quantity,
        'items': [{
            'item_id': productId,
            'item_name': productName,
            'price': price,
            'quantity': quantity
        }]
    });
});
</script>

Checkout Process Tracking

Begin Checkout

File: catalog/controller/checkout/checkout.php

Add to index() method:

// Get cart items for GA4
$data['ga4_cart_items'] = array();
$cart_value = 0;

foreach ($this->cart->getProducts() as $product) {
    $price = $this->currency->format($product['price'], $this->session->data['currency'], '', false);
    $cart_value += $price * $product['quantity'];

    $data['ga4_cart_items'][] = array(
        'item_id' => $product['product_id'],
        'item_name' => $product['name'],
        'price' => $price,
        'quantity' => $product['quantity']
    );
}

$data['ga4_cart_value'] = $cart_value;

File: catalog/view/theme/[your-theme]/template/checkout/checkout.twig

<script>
// Fire begin_checkout on checkout page load
gtag('event', 'begin_checkout', {
    'currency': '{{ currency_code }}',
    'value': {{ ga4_cart_value }},
    'items': {{ ga4_cart_items|json_encode|raw }}
});
</script>

Checkout Steps

Track each step of the checkout process:

File: catalog/view/theme/[your-theme]/template/checkout/checkout.twig

<script>
// Track shipping info
$(document).on('click', '#button-shipping-method', function() {
    var shippingMethod = $('input[name="shipping_method"]:checked').val();

    gtag('event', 'add_shipping_info', {
        'currency': '{{ currency_code }}',
        'value': {{ ga4_cart_value }},
        'shipping_tier': shippingMethod,
        'items': {{ ga4_cart_items|json_encode|raw }}
    });
});

// Track payment info
$(document).on('click', '#button-payment-method', function() {
    var paymentMethod = $('input[name="payment_method"]:checked').val();

    gtag('event', 'add_payment_info', {
        'currency': '{{ currency_code }}',
        'value': {{ ga4_cart_value }},
        'payment_type': paymentMethod,
        'items': {{ ga4_cart_items|json_encode|raw }}
    });
});
</script>

Purchase Tracking

Success Page Tracking

File: catalog/controller/checkout/success.php

Add to index() method:

// Get order data
$order_id = $this->session->data['order_id'];

if ($order_id) {
    $this->load->model('checkout/order');
    $order_info = $this->model_checkout_order->getOrder($order_id);

    if ($order_info) {
        // Get order products
        $order_products = $this->model_checkout_order->getOrderProducts($order_id);

        $data['ga4_transaction'] = array(
            'transaction_id' => $order_id,
            'value' => $this->currency->format($order_info['total'], $order_info['currency_code'], '', false),
            'tax' => $this->currency->format($order_info['total'] - $order_info['total'] / (1 + ($order_info['tax'] / 100)), $order_info['currency_code'], '', false),
            'shipping' => $this->currency->format($order_info['shipping'], $order_info['currency_code'], '', false),
            'currency' => $order_info['currency_code'],
            'coupon' => isset($order_info['coupon']) ? $order_info['coupon'] : '',
            'items' => array()
        );

        foreach ($order_products as $product) {
            $data['ga4_transaction']['items'][] = array(
                'item_id' => $product['product_id'],
                'item_name' => $product['name'],
                'price' => $this->currency->format($product['price'], $order_info['currency_code'], '', false),
                'quantity' => $product['quantity']
            );
        }
    }

    // Clear order_id from session to prevent duplicate tracking
    unset($this->session->data['order_id']);
}

File: catalog/view/theme/[your-theme]/template/common/success.twig

{% if ga4_transaction %}
<script>
// Track purchase
gtag('event', 'purchase', {
    'transaction_id': '{{ ga4_transaction.transaction_id }}',
    'value': {{ ga4_transaction.value }},
    'tax': {{ ga4_transaction.tax }},
    'shipping': {{ ga4_transaction.shipping }},
    'currency': '{{ ga4_transaction.currency }}',
    {% if ga4_transaction.coupon %}
    'coupon': '{{ ga4_transaction.coupon }}',
    {% endif %}
    'items': {{ ga4_transaction.items|json_encode|raw }}
});
</script>
{% endif %}

Prevent Duplicate Purchase Events

Add session check to prevent tracking on page refresh:

// In success.php controller
if ($order_id && !isset($this->session->data['ga4_tracked_' . $order_id])) {
    // Transaction tracking code here

    // Mark as tracked
    $this->session->data['ga4_tracked_' . $order_id] = true;
}

Refund Tracking

Admin-Side Refund Tracking

File: admin/controller/sale/order.php

Add custom method for refund tracking:

public function addRefund() {
    $this->load->model('sale/order');

    if (isset($this->request->get['order_id'])) {
        $order_id = $this->request->get['order_id'];
        $order_info = $this->model_sale_order->getOrder($order_id);

        if ($order_info) {
            // Track refund in GA4
            $this->trackRefund($order_id, $order_info);

            // Original refund processing code
        }
    }
}

private function trackRefund($order_id, $order_info) {
    // You can implement server-side tracking via Measurement Protocol
    // or log refund data for batch processing

    $ga4_measurement_id = $this->config->get('analytics_ga4_measurement_id');
    $ga4_api_secret = $this->config->get('analytics_ga4_api_secret');

    if ($ga4_measurement_id && $ga4_api_secret) {
        $order_products = $this->model_sale_order->getOrderProducts($order_id);

        $items = array();
        foreach ($order_products as $product) {
            $items[] = array(
                'item_id' => $product['product_id'],
                'item_name' => $product['name'],
                'price' => $product['price'],
                'quantity' => $product['quantity']
            );
        }

        $payload = array(
            'client_id' => $order_info['customer_id'] ?: 'guest_' . $order_id,
            'events' => array(
                array(
                    'name' => 'refund',
                    'params' => array(
                        'transaction_id' => $order_id,
                        'value' => $order_info['total'],
                        'currency' => $order_info['currency_code'],
                        'items' => $items
                    )
                )
            )
        );

        // Send to GA4 Measurement Protocol
        $url = 'https://www.google-analytics.com/mp/collect?measurement_id=' . $ga4_measurement_id . '&api_secret=' . $ga4_api_secret;

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_exec($ch);
        curl_close($ch);
    }
}

Testing Ecommerce Tracking

1. Enable Debug Mode

gtag('config', 'G-XXXXXXXXXX', {
    'debug_mode': true
});

2. Complete Test Purchase

  1. Add products to cart
  2. Proceed through checkout
  3. Complete a test order
  4. Verify each event in GA4 DebugView

3. Check Event Sequence

Expected event flow:

  1. view_item_list (category page)
  2. select_item (click product)
  3. view_item (product page)
  4. add_to_cart (add to cart)
  5. begin_checkout (checkout page)
  6. add_shipping_info (shipping selected)
  7. add_payment_info (payment selected)
  8. purchase (order success)

4. Verify in GA4 Reports

After 24-48 hours, check:

  • Reports > Monetization > Ecommerce purchases
  • Reports > Monetization > Purchase journey
  • Reports > Monetization > Product performance

Common Issues

Purchase Events Not Tracking

Problem: Success page loads but no purchase event

Solutions:

  1. Check order_id is available in session
  2. Verify currency formatting (must be number, not string)
  3. Check for JavaScript errors on success page
  4. Ensure GA4 code loads before event fires

Duplicate Purchases

Problem: Same transaction tracked multiple times

Solutions:

  1. Implement session-based duplicate prevention
  2. Clear order_id from session after tracking
  3. Use cookie to mark transaction as tracked
  4. Check for multiple GA4 codes on page

Incorrect Revenue Values

Problem: Revenue doesn't match actual order totals

Solutions:

  1. Verify currency conversion is disabled:
    $this->currency->format($price, $currency, '', false)
    // Fourth parameter FALSE prevents formatting
    
  2. Check tax inclusion/exclusion
  3. Verify decimal separator (use period, not comma)

Multi-Currency Setup

For stores with multiple currencies:

// Always track in base currency
$base_currency = $this->config->get('config_currency');
$transaction_value = $this->currency->convert(
    $order_info['total'],
    $order_info['currency_code'],
    $base_currency
);

$data['ga4_transaction'] = array(
    'value' => $transaction_value,
    'currency' => $base_currency,
    // ... other fields
);

Or track in transaction currency:

gtag('event', 'purchase', {
    'transaction_id': '{{ order_id }}',
    'value': {{ order_total }},
    'currency': '{{ order_currency }}',  // USD, EUR, GBP, etc.
    'items': [...]
});

Next Steps

Additional Resources