GA4 E-commerce Tracking on Kentico | OpsBlu Docs

GA4 E-commerce Tracking on Kentico

Complete guide to implementing GA4 e-commerce tracking with Kentico E-commerce module

Learn how to implement Google Analytics 4 e-commerce tracking with Kentico's built-in E-commerce module, including product views, add to cart, checkout, and purchase tracking.

Prerequisites

  • GA4 Setup completed
  • Kentico E-commerce module enabled
  • Understanding of Kentico shopping cart and checkout process
  • Access to Kentico MVC or Portal Engine development files

GA4 E-commerce Event Overview

GA4 e-commerce tracking uses these key events:

  • view_item_list - Product listing pages
  • view_item - Product detail page
  • add_to_cart - Adding product to cart
  • remove_from_cart - Removing product from cart
  • view_cart - Viewing shopping cart
  • begin_checkout - Starting checkout process
  • add_payment_info - Adding payment information
  • add_shipping_info - Adding shipping information
  • purchase - Completed transaction

E-commerce Setup

1. View Item List (Product Listing Pages)

Track when users view product category or listing pages:

MVC Implementation

@using CMS.Ecommerce
@model IEnumerable<SKUInfo>

<div class="product-list">
    @foreach (var product in Model)
    {
        <div class="product-item" data-product-id="@product.SKUID">
            @* Product display *@
        </div>
    }
</div>

<script>
  // Track product list view
  gtag('event', 'view_item_list', {
    'item_list_id': '@ViewBag.CategoryID',
    'item_list_name': '@ViewBag.CategoryName',
    'items': [
      @foreach (var product in Model)
      {
        @:{
          'item_id': '@product.SKUNumber',
          'item_name': '@product.SKUName',
          'item_brand': '@(product.Brand?.BrandDisplayName ?? "")',
          'item_category': '@ViewBag.CategoryName',
          'price': @product.SKUPrice,
          'currency': '@CurrencyInfoProvider.GetMainCurrency(SiteContext.CurrentSiteID).CurrencyCode'
        }@(product != Model.Last() ? "," : "")
      }
    ]
  });
</script>

Portal Engine / Web Part Implementation

Create a custom transformation or web part:

<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        var products = GetProducts(); // Your method to get products
        RegisterProductListView(products);
    }
}

private void RegisterProductListView(IEnumerable<SKUInfo> products)
{
    var items = products.Select(p => new
    {
        item_id = p.SKUNumber,
        item_name = p.SKUName,
        price = p.SKUPrice
    });

    var script = $@"
        gtag('event', 'view_item_list', {{
            'items': {JsonConvert.SerializeObject(items)}
        }});
    ";

    ScriptHelper.RegisterStartupScript(this, typeof(string), "ProductListView", script, true);
}
</script>

2. View Item (Product Detail Page)

Track product detail page views:

@using CMS.Ecommerce
@model SKUInfo

<div class="product-detail" itemscope itemtype="https://schema.org/Product">
    <h1 itemprop="name">@Model.SKUName</h1>
    <div class="price" itemprop="price">@Model.SKUPrice</div>
    @* Rest of product details *@
</div>

<script>
  gtag('event', 'view_item', {
    'currency': '@CurrencyInfoProvider.GetMainCurrency(SiteContext.CurrentSiteID).CurrencyCode',
    'value': @Model.SKUPrice,
    'items': [{
      'item_id': '@Model.SKUNumber',
      'item_name': '@Model.SKUName',
      'item_brand': '@(Model.Brand?.BrandDisplayName ?? "")',
      'item_category': '@(Model.PrimaryCategory?.CategoryDisplayName ?? "")',
      'item_variant': '@Model.SKUNumber',
      'price': @Model.SKUPrice,
      'quantity': 1
    }]
  });
</script>

3. Add to Cart Tracking

Track when products are added to the shopping cart:

Client-Side Tracking (AJAX Add to Cart)

<script>
  function addToCart(productId, quantity) {
    // Make AJAX call to add product
    $.ajax({
      url: '/ShoppingCart/AddItem',
      method: 'POST',
      data: {
        skuId: productId,
        quantity: quantity
      },
      success: function(response) {
        // Track add to cart
        gtag('event', 'add_to_cart', {
          'currency': response.currency,
          'value': response.totalPrice,
          'items': [{
            'item_id': response.product.skuNumber,
            'item_name': response.product.skuName,
            'item_brand': response.product.brand,
            'item_category': response.product.category,
            'price': response.product.price,
            'quantity': quantity
          }]
        });

        // Update cart UI
        updateCartDisplay(response);
      }
    });
  }
</script>

<button 1)">Add to Cart</button>

Server-Side Tracking (Traditional Form Post)

In your controller:

using CMS.Ecommerce;
using Newtonsoft.Json;

public ActionResult AddToCart(int skuId, int quantity)
{
    var sku = SKUInfoProvider.GetSKUInfo(skuId);
    var cart = ECommerceContext.CurrentShoppingCart;

    // Add item to cart
    cart.AddItem(skuId, quantity);
    cart.Evaluate();

    // Prepare tracking data
    var trackingData = new
    {
        currency = cart.Currency.CurrencyCode,
        value = sku.SKUPrice * quantity,
        items = new[]
        {
            new
            {
                item_id = sku.SKUNumber,
                item_name = sku.SKUName,
                item_brand = sku.Brand?.BrandDisplayName ?? "",
                item_category = sku.PrimaryCategory?.CategoryDisplayName ?? "",
                price = sku.SKUPrice,
                quantity = quantity
            }
        }
    };

    ViewBag.AddToCartTracking = JsonConvert.SerializeObject(trackingData);

    return View("Cart");
}

In your view:

@if (ViewBag.AddToCartTracking != null)
{
    <script>
      gtag('event', 'add_to_cart', @Html.Raw(ViewBag.AddToCartTracking));
    </script>
}

4. Remove from Cart

Track product removal from cart:

<script>
  function removeFromCart(itemId, itemData) {
    $.ajax({
      url: '/ShoppingCart/RemoveItem',
      method: 'POST',
      data: { cartItemId: itemId },
      success: function(response) {
        gtag('event', 'remove_from_cart', {
          'currency': itemData.currency,
          'value': itemData.price * itemData.quantity,
          'items': [{
            'item_id': itemData.skuNumber,
            'item_name': itemData.skuName,
            'price': itemData.price,
            'quantity': itemData.quantity
          }]
        });
      }
    });
  }
</script>

5. View Cart

Track shopping cart page views:

@using CMS.Ecommerce
@{
    var cart = ECommerceContext.CurrentShoppingCart;
}

<script>
  gtag('event', 'view_cart', {
    'currency': '@cart.Currency.CurrencyCode',
    'value': @cart.TotalPrice,
    'items': [
      @foreach (var item in cart.CartItems)
      {
        var sku = item.SKU;
        @:{
          'item_id': '@sku.SKUNumber',
          'item_name': '@sku.SKUName',
          'item_brand': '@(sku.Brand?.BrandDisplayName ?? "")',
          'item_category': '@(sku.PrimaryCategory?.CategoryDisplayName ?? "")',
          'price': @item.UnitPrice,
          'quantity': @item.CartItemUnits
        }@(item != cart.CartItems.Last() ? "," : "")
      }
    ]
  });
</script>

6. Begin Checkout

Track when customers start the checkout process:

@using CMS.Ecommerce
@{
    var cart = ECommerceContext.CurrentShoppingCart;
}

@* Checkout page *@
<h1>Checkout</h1>

<script>
  gtag('event', 'begin_checkout', {
    'currency': '@cart.Currency.CurrencyCode',
    'value': @cart.TotalPrice,
    'coupon': '@(cart.CouponCodes.FirstOrDefault()?.Code ?? "")',
    'items': [
      @foreach (var item in cart.CartItems)
      {
        var sku = item.SKU;
        @:{
          'item_id': '@sku.SKUNumber',
          'item_name': '@sku.SKUName',
          'price': @item.UnitPrice,
          'quantity': @item.CartItemUnits
        }@(item != cart.CartItems.Last() ? "," : "")
      }
    ]
  });
</script>

7. Add Shipping Info

Track when shipping information is added:

<script>
  // When shipping method is selected
  function selectShippingMethod(shippingId, shippingName, shippingCost) {
    gtag('event', 'add_shipping_info', {
      'currency': '@cart.Currency.CurrencyCode',
      'value': @cart.TotalPrice,
      'shipping_tier': shippingName,
      'items': @Html.Raw(GetCartItemsJson())
    });

    // Submit shipping selection
    submitShippingMethod(shippingId);
  }
</script>

8. Add Payment Info

Track when payment method is selected:

<script>
  function selectPaymentMethod(paymentType) {
    gtag('event', 'add_payment_info', {
      'currency': '@cart.Currency.CurrencyCode',
      'value': @cart.TotalPrice,
      'payment_type': paymentType,
      'items': @Html.Raw(GetCartItemsJson())
    });
  }
</script>

9. Purchase Event (Most Important!)

Track completed transactions:

Method 1: On Order Confirmation Page

@using CMS.Ecommerce
@model OrderInfo

<h1>Thank You for Your Order!</h1>
<p>Order #@Model.OrderID</p>

<script>
  gtag('event', 'purchase', {
    'transaction_id': '@Model.OrderID',
    'affiliation': '@SiteContext.CurrentSite.SiteName',
    'value': @Model.OrderGrandTotal,
    'tax': @Model.OrderTotalTax,
    'shipping': @Model.OrderTotalShipping,
    'currency': '@Model.OrderCurrency.CurrencyCode',
    'coupon': '@(Model.OrderCouponCodes ?? "")',
    'items': [
      @foreach (var item in Model.OrderItems)
      {
        var sku = item.OrderItemSKU;
        @:{
          'item_id': '@sku.SKUNumber',
          'item_name': '@sku.SKUName',
          'item_brand': '@(sku.Brand?.BrandDisplayName ?? "")',
          'item_category': '@(sku.PrimaryCategory?.CategoryDisplayName ?? "")',
          'price': @item.OrderItemUnitPrice,
          'quantity': @item.OrderItemUnitCount
        }@(item != Model.OrderItems.Last() ? "," : "")
      }
    ]
  });
</script>

Method 2: Server-Side Event Handler

Create a custom module to track purchases automatically:

using CMS;
using CMS.Base;
using CMS.DataEngine;
using CMS.Ecommerce;
using Newtonsoft.Json;

[assembly: RegisterModule(typeof(EcommerceTrackingModule))]

public class EcommerceTrackingModule : Module
{
    public EcommerceTrackingModule() : base("EcommerceTracking") { }

    protected override void OnInit()
    {
        base.OnInit();

        // Subscribe to order paid event
        OrderInfoProvider.OnOrderPaid += OrderInfoProvider_OnOrderPaid;
    }

    private void OrderInfoProvider_OnOrderPaid(object sender, OrderEventArgs e)
    {
        var order = e.Order;

        // Build tracking script
        var items = order.OrderItems.Select(item => new
        {
            item_id = item.OrderItemSKU.SKUNumber,
            item_name = item.OrderItemSKU.SKUName,
            item_brand = item.OrderItemSKU.Brand?.BrandDisplayName ?? "",
            item_category = item.OrderItemSKU.PrimaryCategory?.CategoryDisplayName ?? "",
            price = item.OrderItemUnitPrice,
            quantity = item.OrderItemUnitCount
        });

        var purchaseData = new
        {
            transaction_id = order.OrderID.ToString(),
            affiliation = order.OrderSite.SiteName,
            value = order.OrderGrandTotal,
            tax = order.OrderTotalTax,
            shipping = order.OrderTotalShipping,
            currency = order.OrderCurrency.CurrencyCode,
            items = items
        };

        // Store in session for rendering on confirmation page
        SessionHelper.SetValue("GA4PurchaseData", JsonConvert.SerializeObject(purchaseData));
    }
}

Then in your confirmation page:

@{
    var purchaseData = SessionHelper.GetValue("GA4PurchaseData") as string;
    if (!string.IsNullOrEmpty(purchaseData))
    {
        SessionHelper.Remove("GA4PurchaseData");
    }
}

@if (!string.IsNullOrEmpty(purchaseData))
{
    <script>
      gtag('event', 'purchase', @Html.Raw(purchaseData));
    </script>
}

Advanced E-commerce Tracking

Product Impressions with List Position

<script>
  gtag('event', 'view_item_list', {
    'item_list_id': 'related_products',
    'item_list_name': 'Related Products',
    'items': [
      @for (int i = 0; i < Model.Count(); i++)
      {
        var product = Model.ElementAt(i);
        @:{
          'item_id': '@product.SKUNumber',
          'item_name': '@product.SKUName',
          'index': @i,
          'item_list_name': 'Related Products',
          'price': @product.SKUPrice
        }@(i < Model.Count() - 1 ? "," : "")
      }
    ]
  });
</script>

Select Item (Click from List)

<script>
  document.querySelectorAll('.product-item').forEach(function(item, index) {
    item.addEventListener('click', function(e) {
      var productData = JSON.parse(this.getAttribute('data-product'));

      gtag('event', 'select_item', {
        'item_list_id': '@ViewBag.CategoryID',
        'item_list_name': '@ViewBag.CategoryName',
        'items': [{
          'item_id': productData.skuNumber,
          'item_name': productData.skuName,
          'index': index,
          'price': productData.price
        }]
      });
    });
  });
</script>

Refund Tracking

Track order refunds:

// In your refund controller
public ActionResult ProcessRefund(int orderId)
{
    var order = OrderInfoProvider.GetOrderInfo(orderId);

    // Process refund logic...

    var refundData = new
    {
        transaction_id = order.OrderID.ToString(),
        value = order.OrderGrandTotal,
        currency = order.OrderCurrency.CurrencyCode,
        items = order.OrderItems.Select(item => new
        {
            item_id = item.OrderItemSKU.SKUNumber,
            quantity = item.OrderItemUnitCount
        })
    };

    ViewBag.RefundTracking = JsonConvert.SerializeObject(refundData);

    return View("RefundConfirmation");
}
@if (ViewBag.RefundTracking != null)
{
    <script>
      gtag('event', 'refund', @Html.Raw(ViewBag.RefundTracking));
    </script>
}

Testing E-commerce Tracking

1. Use GA4 DebugView

  • Enable debug mode in GA4 config
  • Navigate GA4 → Configure → DebugView
  • Complete a test purchase
  • Verify all events fire in sequence

2. Check Event Parameters

Ensure all required parameters are present:

  • transaction_id for purchase
  • currency for all monetary events
  • value for all monetary events
  • items array with proper structure

3. Test Different Scenarios

  • Complete purchase flow
  • Add/remove items
  • Use discount codes
  • Different payment methods
  • Different shipping options

4. Verify in GA4 Reports

  • Navigate to Monetization → E-commerce purchases
  • Check for test transactions
  • Verify revenue matches order total
  • Confirm product data is correct

Common Issues

Purchase Event Not Firing

  • Check if event is on order confirmation page
  • Verify page loads after payment success
  • Check for JavaScript errors
  • Ensure not firing on page refresh (use session storage)

Duplicate Purchases

Prevent duplicate tracking on refresh:

@{
    var purchaseTracked = SessionHelper.GetValue("PurchaseTracked_" + Model.OrderID);
}

@if (purchaseTracked == null)
{
    SessionHelper.SetValue("PurchaseTracked_" + Model.OrderID, true);

    <script>
      gtag('event', 'purchase', {
        // purchase data
      });
    </script>
}

Missing Product Data

  • Verify SKU information is complete
  • Check for null values in brand/category
  • Use default values for optional fields

Currency Mismatch

  • Ensure consistent currency code across all events
  • Use Kentico's main currency or order-specific currency
  • Format: ISO 4217 (e.g., 'USD', 'EUR', 'GBP')

Best Practices

  1. Track All E-commerce Events: Implement the complete funnel for better insights

  2. Use Consistent Item IDs: Use SKUNumber as item_id across all events

  3. Include Product Hierarchy: Add brand, category, and variant data when available

  4. Test Before Production: Always test the full purchase flow in staging

  5. Prevent Duplicate Transactions: Use session flags to prevent re-tracking on refresh

  6. Add Custom Dimensions: Include Kentico-specific data (customer type, site ID, etc.)

  7. Document Revenue Logic: Clearly document what's included in value/revenue calculations

Helper Function for Cart Items JSON

Create a helper method in your controller or view:

@functions {
    public string GetCartItemsJson()
    {
        var cart = ECommerceContext.CurrentShoppingCart;
        var items = cart.CartItems.Select(item => new
        {
            item_id = item.SKU.SKUNumber,
            item_name = item.SKU.SKUName,
            item_brand = item.SKU.Brand?.BrandDisplayName ?? "",
            item_category = item.SKU.PrimaryCategory?.CategoryDisplayName ?? "",
            price = item.UnitPrice,
            quantity = item.CartItemUnits
        });

        return JsonConvert.SerializeObject(items);
    }
}

Next Steps

Additional Resources