Google Analytics E-commerce Tracking on Umbraco | OpsBlu Docs

Google Analytics E-commerce Tracking on Umbraco

Track e-commerce transactions with Vendr, Umbraco Commerce, and custom .NET shopping cart implementations

Implement GA4 e-commerce tracking for Umbraco using Vendr (Umbraco 8-10), Umbraco Commerce (Umbraco 11+), or custom .NET shopping cart solutions. This guide covers transaction tracking, product impressions, and revenue reporting.

Prerequisites

Before implementing e-commerce tracking:

  • GA4 Setup Complete - Follow GA4 Setup Guide
  • E-commerce Platform - Vendr, Umbraco Commerce, or custom solution
  • Commerce Package Installed - NuGet package configured
  • Test Orders - Test environment for validation

Vendr E-commerce Tracking (Umbraco 8-10)

Install Vendr Analytics Package

Install-Package Vendr.Contrib.GoogleAnalytics

Or via .NET CLI:

dotnet add package Vendr.Contrib.GoogleAnalytics

Configure Vendr Analytics

Register in Startup.cs:

using Vendr.Contrib.GoogleAnalytics.Extensions;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddUmbraco(_env, _config)
            .AddBackOffice()
            .AddWebsite()
            .AddVendr()
            .AddVendrGoogleAnalytics(options => {
                options.MeasurementId = "G-XXXXXXXXXX";
                options.TrackProductImpressions = true;
                options.TrackProductClicks = true;
                options.TrackAddToCart = true;
                options.TrackRemoveFromCart = true;
                options.TrackCheckout = true;
                options.TrackPurchase = true;
            })
            .AddComposers()
            .Build();
    }
}

Track Product Impressions

ProductListing.cshtml:

@using Vendr.Core.Models
@using Vendr.Core.Api
@inject IVendrApi VendrApi

@{
    var store = VendrApi.GetStore(Model.Value<Guid>("store"));
    var products = Model.Children().Where(x => x.ContentType.Alias == "product");
}

<div class="product-listing">
    @foreach (var product in products)
    {
        var productName = product.Name;
        var productId = product.GetProperty("sku")?.Value<string>();
        var price = product.GetProperty("price")?.Value<decimal>() ?? 0;

        <div class="product-item"
             data-product-id="@productId"
             data-product-name="@productName"
             data-product-price="@price">

            <h3>@productName</h3>
            <p>@price.ToString("C")</p>
            <a href="@product.Url()" class="view-product">View Product</a>
        </div>
    }
</div>

<script>
    // Track product impressions
    var products = document.querySelectorAll('.product-item');
    var items = [];

    products.forEach(function(product, index) {
        items.push({
            item_id: product.getAttribute('data-product-id'),
            item_name: product.getAttribute('data-product-name'),
            price: parseFloat(product.getAttribute('data-product-price')),
            index: index,
            item_category: '@Model.Name'
        });
    });

    gtag('event', 'view_item_list', {
        item_list_id: '@Model.Id',
        item_list_name: '@Model.Name',
        items: items
    });

    // Track product clicks
    products.forEach(function(product) {
        product.querySelector('.view-product').addEventListener('click', function(e) {
            gtag('event', 'select_item', {
                item_list_id: '@Model.Id',
                item_list_name: '@Model.Name',
                items: [{
                    item_id: product.getAttribute('data-product-id'),
                    item_name: product.getAttribute('data-product-name'),
                    price: parseFloat(product.getAttribute('data-product-price'))
                }]
            });
        });
    });
</script>

Track Product Detail Views

ProductDetail.cshtml:

@using Vendr.Core.Models
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage

@{
    var productSku = Model.GetProperty("sku")?.Value<string>();
    var productPrice = Model.GetProperty("price")?.Value<decimal>() ?? 0;
    var productCategory = Model.Parent?.Name;
}

<script>
    gtag('event', 'view_item', {
        currency: 'USD',
        value: @productPrice,
        items: [{
            item_id: '@productSku',
            item_name: '@Model.Name',
            price: @productPrice,
            item_category: '@productCategory',
            quantity: 1
        }]
    });
</script>

Track Add to Cart

Add to Cart Razor:

@using Umbraco.Forms.Core
@model Vendr.Core.Models.ProductSnapshot

<form method="post" id="add-to-cart-form">
    <input type="hidden" name="productId" value="@Model.ProductReference" />
    <input type="number" name="quantity" value="1" min="1" />
    <button type="submit">Add to Cart</button>
</form>

<script>
    document.getElementById('add-to-cart-form').addEventListener('submit', function(e) {
        e.preventDefault();

        var quantity = parseInt(this.querySelector('[name="quantity"]').value);

        // Track add to cart
        gtag('event', 'add_to_cart', {
            currency: '@Model.TotalPrice.Value.CurrencyCode',
            value: @Model.TotalPrice.Value.Value,
            items: [{
                item_id: '@Model.Sku',
                item_name: '@Model.Name',
                price: @Model.TotalPrice.Value.Value,
                quantity: quantity
            }]
        });

        // Submit form
        this.submit();
    });
</script>

Track Checkout Process

Checkout.cshtml:

@using Vendr.Core.Models
@using Vendr.Core.Api
@inject IVendrApi VendrApi

@{
    var order = VendrApi.GetCurrentOrder(Model.Value<Guid>("store"));
}

@if (order != null)
{
    <script>
        var items = [];

        @foreach (var orderLine in order.OrderLines)
        {
            <text>
            items.push({
                item_id: '@orderLine.Sku',
                item_name: '@orderLine.Name',
                price: @orderLine.TotalPrice.Value.WithoutAdjustments.Value,
                quantity: @orderLine.Quantity
            });
            </text>
        }

        // Track begin checkout
        gtag('event', 'begin_checkout', {
            currency: '@order.CurrencyCode',
            value: @order.TotalPrice.Value.WithoutAdjustments.Value,
            items: items
        });
    </script>
}

Checkout Steps:

@{
    var currentStep = ViewData["CheckoutStep"]?.ToString() ?? "shipping";
}

<script>
    // Track checkout progress
    gtag('event', 'checkout_progress', {
        checkout_step: '@currentStep',
        checkout_option: '@ViewData["CheckoutOption"]'
    });
</script>

Track Purchase Completion

OrderConfirmation.cshtml:

@using Vendr.Core.Models
@using Vendr.Core.Api
@inject IVendrApi VendrApi

@{
    var orderRef = Request.Query["order"];
    var order = VendrApi.GetOrder(Guid.Parse(orderRef));
}

@if (order != null)
{
    <script>
        var items = [];

        @foreach (var orderLine in order.OrderLines)
        {
            <text>
            items.push({
                item_id: '@orderLine.Sku',
                item_name: '@orderLine.Name',
                price: @orderLine.TotalPrice.Value.WithoutAdjustments.Value,
                quantity: @orderLine.Quantity,
                item_category: '@orderLine.Properties.GetValue("category")'
            });
            </text>
        }

        // Track purchase
        gtag('event', 'purchase', {
            transaction_id: '@order.OrderNumber',
            affiliation: '@order.Properties.GetValue("storeName")',
            value: @order.TotalPrice.Value.WithoutAdjustments.Value,
            tax: @order.TotalPrice.Value.TotalAdjustment.Value,
            shipping: @order.ShippingInfo.TotalPrice.Value.WithoutAdjustments.Value,
            currency: '@order.CurrencyCode',
            coupon: '@order.DiscountCodes.FirstOrDefault()?.Code',
            items: items
        });
    </script>

    <h1>Order Confirmed</h1>
    <p>Order Number: @order.OrderNumber</p>
    <p>Total: @order.TotalPrice.Value.Formatted()</p>
}

Umbraco Commerce Tracking (Umbraco 11+)

Install Umbraco Commerce Analytics

dotnet add package Umbraco.Commerce.GoogleAnalytics

Configure Umbraco Commerce Analytics

appsettings.json:

{
  "Umbraco": {
    "Commerce": {
      "GoogleAnalytics": {
        "Enabled": true,
        "MeasurementId": "G-XXXXXXXXXX",
        "TrackServerSide": false,
        "Events": {
          "ViewItem": true,
          "AddToCart": true,
          "RemoveFromCart": true,
          "BeginCheckout": true,
          "Purchase": true
        }
      }
    }
  }
}

Track with Umbraco Commerce Services

Create Commerce Analytics Service:

using Umbraco.Commerce.Core.Api;
using Umbraco.Commerce.Core.Models;

namespace YourProject.Services
{
    public class CommerceAnalyticsService
    {
        private readonly IUmbracoCommerceApi _commerceApi;
        private readonly IAnalyticsService _analyticsService;

        public CommerceAnalyticsService(
            IUmbracoCommerceApi commerceApi,
            IAnalyticsService analyticsService)
        {
            _commerceApi = commerceApi;
            _analyticsService = analyticsService;
        }

        public async Task TrackPurchase(Guid orderId)
        {
            var order = _commerceApi.GetOrder(orderId);

            if (order == null) return;

            var items = order.OrderLines.Select(line => new Dictionary<string, object>
            {
                { "item_id", line.Sku },
                { "item_name", line.Name },
                { "price", line.UnitPrice.Value.Value },
                { "quantity", line.Quantity }
            }).ToList();

            await _analyticsService.TrackEvent("purchase", new Dictionary<string, object>
            {
                { "transaction_id", order.OrderNumber },
                { "value", order.SubtotalPrice.Value.Value },
                { "tax", order.TotalPrice.Value.TotalAdjustment.Value },
                { "shipping", order.ShippingInfo?.TotalPrice.Value.Value ?? 0 },
                { "currency", order.CurrencyCode },
                { "items", items }
            });
        }
    }
}

Track Order Events

OrderEventHandler.cs:

using Umbraco.Commerce.Core.Events.Notification;
using Umbraco.Cms.Core.Events;

namespace YourProject.EventHandlers
{
    public class OrderEventHandler :
        INotificationHandler<OrderFinalizedNotification>
    {
        private readonly CommerceAnalyticsService _analyticsService;

        public OrderEventHandler(CommerceAnalyticsService analyticsService)
        {
            _analyticsService = analyticsService;
        }

        public async Task Handle(OrderFinalizedNotification notification, CancellationToken cancellationToken)
        {
            var order = notification.Order;

            await _analyticsService.TrackPurchase(order.Id);
        }
    }
}

Custom .NET Shopping Cart Integration

Create Shopping Cart Model

namespace YourProject.Models
{
    public class CartItem
    {
        public string Sku { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public int Quantity { get; set; }
        public string Category { get; set; }
    }

    public class ShoppingCart
    {
        public List<CartItem> Items { get; set; } = new();
        public decimal Subtotal => Items.Sum(i => i.Price * i.Quantity);
        public decimal Tax { get; set; }
        public decimal Shipping { get; set; }
        public decimal Total => Subtotal + Tax + Shipping;
        public string CurrencyCode { get; set; } = "USD";
    }
}

Track Cart Events

CartController.cs:

using Microsoft.AspNetCore.Mvc;
using YourProject.Models;
using YourProject.Services;

namespace YourProject.Controllers
{
    public class CartController : SurfaceController
    {
        private readonly IAnalyticsService _analyticsService;

        public CartController(IAnalyticsService analyticsService, /* other dependencies */)
        {
            _analyticsService = analyticsService;
        }

        [HttpPost]
        public async Task<IActionResult> AddToCart(string sku, string name, decimal price, int quantity)
        {
            // Add to cart logic
            // ...

            // Track add to cart
            await _analyticsService.TrackEvent("add_to_cart", new Dictionary<string, object>
            {
                { "currency", "USD" },
                { "value", price * quantity },
                {
                    "items", new[]
                    {
                        new Dictionary<string, object>
                        {
                            { "item_id", sku },
                            { "item_name", name },
                            { "price", price },
                            { "quantity", quantity }
                        }
                    }
                }
            });

            return Json(new { success = true });
        }

        [HttpPost]
        public async Task<IActionResult> RemoveFromCart(string sku)
        {
            // Get item before removal
            var item = GetCartItem(sku);

            // Remove from cart logic
            // ...

            // Track removal
            await _analyticsService.TrackEvent("remove_from_cart", new Dictionary<string, object>
            {
                { "currency", "USD" },
                { "value", item.Price * item.Quantity },
                {
                    "items", new[]
                    {
                        new Dictionary<string, object>
                        {
                            { "item_id", item.Sku },
                            { "item_name", item.Name },
                            { "price", item.Price },
                            { "quantity", item.Quantity }
                        }
                    }
                }
            });

            return Json(new { success = true });
        }
    }
}

Server-Side E-commerce Tracking

Use GA4 Measurement Protocol

EnhancedAnalyticsService.cs:

using System.Net.Http;
using System.Text;
using System.Text.Json;

namespace YourProject.Services
{
    public class EnhancedAnalyticsService : IAnalyticsService
    {
        private readonly HttpClient _httpClient;
        private readonly IConfiguration _configuration;

        public EnhancedAnalyticsService(HttpClient httpClient, IConfiguration configuration)
        {
            _httpClient = httpClient;
            _configuration = configuration;
        }

        public async Task TrackPurchase(Order order, string clientId)
        {
            var measurementId = _configuration["Analytics:GoogleAnalytics:MeasurementId"];
            var apiSecret = _configuration["Analytics:GoogleAnalytics:ApiSecret"];

            var items = order.Items.Select(item => new
            {
                item_id = item.Sku,
                item_name = item.Name,
                price = item.Price,
                quantity = item.Quantity,
                item_category = item.Category
            }).ToArray();

            var payload = new
            {
                client_id = clientId,
                events = new[]
                {
                    new
                    {
                        name = "purchase",
                        @params = new
                        {
                            transaction_id = order.OrderNumber,
                            value = order.Total,
                            tax = order.Tax,
                            shipping = order.Shipping,
                            currency = order.CurrencyCode,
                            items = items
                        }
                    }
                }
            };

            var json = JsonSerializer.Serialize(payload);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var url = $"https://www.google-analytics.com/mp/collect?measurement_id={measurementId}&api_secret={apiSecret}";

            var response = await _httpClient.PostAsync(url, content);
            response.EnsureSuccessStatusCode();
        }
    }
}

Enhanced E-commerce Tracking

Track Refunds

public async Task TrackRefund(string transactionId, decimal refundAmount, List<RefundItem> items = null)
{
    var eventParams = new Dictionary<string, object>
    {
        { "transaction_id", transactionId },
        { "value", refundAmount },
        { "currency", "USD" }
    };

    if (items != null && items.Any())
    {
        eventParams["items"] = items.Select(item => new Dictionary<string, object>
        {
            { "item_id", item.Sku },
            { "quantity", item.Quantity }
        }).ToList();
    }

    await _analyticsService.TrackEvent("refund", eventParams);
}

Track Promotions

@{
    var promotion = Model.Value<IPublishedContent>("activePromotion");
}

@if (promotion != null)
{
    <div class="promotion-banner"
         data-promotion-id="@promotion.GetProperty("code")?.Value<string>()"
         data-promotion-name="@promotion.Name">
        <p>@promotion.GetProperty("description")?.Value<string>()</p>
    </div>

    <script>
        gtag('event', 'view_promotion', {
            creative_name: '@promotion.Name',
            creative_slot: 'banner',
            promotion_id: '@promotion.GetProperty("code")?.Value<string>()',
            promotion_name: '@promotion.Name'
        });

        document.querySelector('.promotion-banner').addEventListener('click', function() {
            gtag('event', 'select_promotion', {
                creative_name: '@promotion.Name',
                creative_slot: 'banner',
                promotion_id: '@promotion.GetProperty("code")?.Value<string>()',
                promotion_name: '@promotion.Name'
            });
        });
    </script>
}

Testing E-commerce Tracking

Enable E-commerce Reports in GA4

  1. Navigate to Google Analytics → Admin
  2. Select Property → Data Streams → Web
  3. Click Enhanced Measurement
  4. Ensure File Downloads and Page Views enabled
  5. Navigate to Events → Create custom event if needed

Test Purchase Tracking

  1. Place test order on your Umbraco site
  2. Complete checkout process
  3. Navigate to Google Analytics → Realtime → Events
  4. Verify purchase event appears
  5. Check Monetization → E-commerce Purchases report (24-48 hours)

Validate Event Parameters

In GA4 DebugView:

  1. Enable Debug Mode in configuration
  2. Navigate to Google Analytics → Configure → DebugView
  3. Perform purchase
  4. Verify all parameters present:
    • transaction_id
    • value
    • currency
    • items array
    • tax, shipping

Common Issues

Duplicate Transactions

Cause: Page refresh on confirmation page Solution:

@{
    // Only track once using session
    if (Session["OrderTracked_@order.OrderNumber"] == null)
    {
        Session["OrderTracked_@order.OrderNumber"] = true;

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

Missing Transaction Data

Cause: Order not finalized when tracking fires Solution: Use server-side tracking in order finalization event handler

Currency Code Issues

Cause: Incorrect or missing currency code Solution: Always specify currency:

await _analyticsService.TrackEvent("purchase", new Dictionary<string, object>
{
    { "currency", order.Currency.IsoCode }, // Use actual currency
    { "value", order.TotalPrice.Value.Value }
});

Next Steps