GA4 Ecommerce Tracking on Salesforce Commerce Cloud | Blue Frog Docs

GA4 Ecommerce Tracking on Salesforce Commerce Cloud

Complete ecommerce tracking implementation for GA4 on SFCC

GA4 Ecommerce Tracking on Salesforce Commerce Cloud

This guide covers full ecommerce funnel tracking for GA4 on Salesforce Commerce Cloud, from product impressions to purchase confirmation.

Ecommerce Funnel Overview

Product List → Product View → Add to Cart → Checkout → Purchase
     ↓              ↓             ↓           ↓          ↓
view_item_list  view_item   add_to_cart  begin_checkout  purchase

Product List Tracking

Track product impressions on category and search pages.

Category Page Implementation

Controller Extension:

// cartridges/app_custom/cartridge/controllers/Search.js
'use strict';

var server = require('server');
var Search = module.superModule;

server.extend(Search);

server.append('Show', function (req, res, next) {
    var viewData = res.getViewData();
    var productSearch = viewData.productSearch;

    if (productSearch && productSearch.productIds) {
        var impressions = [];
        productSearch.productIds.forEach(function(productId, index) {
            var product = productSearch.getProduct(productId.productSearchHit);
            impressions.push({
                item_id: productId.productID,
                item_name: product.productName,
                item_category: viewData.productSearch.category.displayName || '',
                item_list_name: 'Category: ' + (viewData.productSearch.category.displayName || 'Search'),
                index: index,
                price: product.price.sales ? product.price.sales.value : 0
            });
        });

        viewData.ga4Impressions = {
            item_list_name: 'Category: ' + (viewData.productSearch.category.displayName || 'Search'),
            items: impressions
        };
    }

    res.setViewData(viewData);
    return next();
});

module.exports = server.exports();

Client-side Tracking:

// Track product impressions on page load
(function() {
    var impressions = window.ga4Impressions;
    if (impressions && impressions.items.length > 0) {
        gtag('event', 'view_item_list', {
            item_list_name: impressions.item_list_name,
            items: impressions.items
        });
    }
})();

// Track product click
$(document).on('click', '.product-tile a', function() {
    var $tile = $(this).closest('.product-tile');
    var productData = $tile.data('product');

    gtag('event', 'select_item', {
        item_list_name: window.ga4Impressions.item_list_name,
        items: [{
            item_id: productData.id,
            item_name: productData.name,
            item_category: productData.category,
            index: $tile.index(),
            price: productData.price
        }]
    });
});

Purchase Tracking

The purchase event is critical for revenue tracking.

Order Confirmation Page

Controller Extension:

// cartridges/app_custom/cartridge/controllers/Order.js
'use strict';

var server = require('server');
var Order = module.superModule;

server.extend(Order);

server.append('Confirm', function (req, res, next) {
    var viewData = res.getViewData();
    var order = viewData.order;

    if (order) {
        var items = [];
        order.items.items.forEach(function(item) {
            items.push({
                item_id: item.id,
                item_name: item.productName,
                item_category: item.categoryName || '',
                item_brand: item.brand || '',
                price: item.priceTotal.price.sales.value / item.quantity,
                quantity: item.quantity,
                discount: item.priceTotal.adjustedPrice ?
                    (item.priceTotal.price.sales.value - item.priceTotal.adjustedPrice.value) : 0
            });
        });

        viewData.ga4Purchase = {
            transaction_id: order.orderNumber,
            value: order.totals.grandTotal.replace(/[^0-9.]/g, ''),
            currency: order.totals.currencyCode,
            tax: order.totals.totalTax.replace(/[^0-9.]/g, ''),
            shipping: order.totals.totalShippingCost.replace(/[^0-9.]/g, ''),
            coupon: order.totals.discounts.length > 0 ?
                order.totals.discounts[0].couponCode : '',
            items: items
        };
    }

    res.setViewData(viewData);
    return next();
});

module.exports = server.exports();

ISML Template Integration:

<isif condition="${pdict.ga4Purchase}">
    <script>
        gtag('event', 'purchase', {
            transaction_id: '${pdict.ga4Purchase.transaction_id}',
            value: ${pdict.ga4Purchase.value},
            currency: '${pdict.ga4Purchase.currency}',
            tax: ${pdict.ga4Purchase.tax},
            shipping: ${pdict.ga4Purchase.shipping},
            <isif condition="${pdict.ga4Purchase.coupon}">
            coupon: '${pdict.ga4Purchase.coupon}',
            </isif>
            items: <isprint value="${JSON.stringify(pdict.ga4Purchase.items)}" encoding="off"/>
        });
    </script>
</isif>

Checkout Step Tracking

Track each step of the checkout process.

Checkout Flow Events

// Shipping step
function trackShippingStep(checkoutData) {
    gtag('event', 'add_shipping_info', {
        currency: checkoutData.currency,
        value: checkoutData.subtotal,
        coupon: checkoutData.couponCode || '',
        shipping_tier: checkoutData.shippingMethodName,
        items: checkoutData.items
    });
}

// Payment step
function trackPaymentStep(checkoutData) {
    gtag('event', 'add_payment_info', {
        currency: checkoutData.currency,
        value: checkoutData.subtotal,
        coupon: checkoutData.couponCode || '',
        payment_type: checkoutData.paymentMethodType,
        items: checkoutData.items
    });
}

Promotion Tracking

Track promotional interactions.

// View promotion
function trackViewPromotion(promoData) {
    gtag('event', 'view_promotion', {
        creative_name: promoData.creativeName,
        creative_slot: promoData.slotName,
        promotion_id: promoData.id,
        promotion_name: promoData.name
    });
}

// Click promotion
function trackSelectPromotion(promoData) {
    gtag('event', 'select_promotion', {
        creative_name: promoData.creativeName,
        creative_slot: promoData.slotName,
        promotion_id: promoData.id,
        promotion_name: promoData.name
    });
}

Refund Tracking

Track order refunds server-side.

// Server-side refund tracking via Measurement Protocol
var https = require('https');

function trackRefund(orderNumber, refundAmount, currency) {
    var measurementId = 'G-XXXXXXXXXX';
    var apiSecret = 'YOUR_API_SECRET';

    var payload = {
        client_id: 'SFCC-Backend',
        events: [{
            name: 'refund',
            params: {
                transaction_id: orderNumber,
                value: refundAmount,
                currency: currency
            }
        }]
    };

    var postData = JSON.stringify(payload);

    var options = {
        hostname: 'www.google-analytics.com',
        port: 443,
        path: '/mp/collect?measurement_id=' + measurementId + '&api_secret=' + apiSecret,
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': Buffer.byteLength(postData)
        }
    };

    var req = https.request(options);
    req.write(postData);
    req.end();
}

Data Quality Checklist

Required Validations

  • Transaction ID is unique per order
  • Revenue matches order total (excluding tax and shipping)
  • Product prices are unit prices, not totals
  • Currency code is ISO 4217 format
  • No PII in event parameters

Common Issues

Issue Cause Solution
Duplicate purchases Page refresh Use dataLayer.push with event callback
Missing revenue Formatted currency strings Strip currency symbols
Wrong item count Including bundled items Count parent products only

Testing Recommendations

  1. Use GA4 DebugView for real-time validation
  2. Test complete funnel from product view to purchase
  3. Verify refund tracking with test orders
  4. Check cross-device tracking consistency

Next Steps

// SYS.FOOTER