CX Works

CX Works brings the most relevant leading practices to you.
It is a single portal of curated, field-tested and SAP-verified expertise for SAP Customer Experience solutions

Implementing Google Analytics with SAP Commerce Cloud

23 min read

Implementing Google Analytics with SAP Commerce Cloud and Google Tag Manager

This is part 2 of the SAP Commerce Cloud web analytics integration series (you can read part 1 here).

Google Analytics is one of the most commonly implemented web analytics platforms on the market today. This article will summarize a dataLayer based approach to integrate Google Analytics using Google Tag Manager and SAP Commerce Cloud. As every implementation will be different, the content here is provided as a high-level guide for how to approach the implementation. You will need to tweak the integration to suit your business data needs according to your requirements and type of business. If you get stuck or need assistance, SAP offers expert services consulting that can help you customize your Google Analytics integration.

Table of Contents


Prerequisites

After your SAP Commerce Cloud solution website is ready for integration, there are a number of prerequisites before you can begin an integration.


Google Accounts - User, Analytics, Tag Manager

  1. A Google account needs to be created (https://www.google.com/account/about).
  2. A Google Analytics account needs to be created (https://analytics.google.com/). Create or sign into your Analytics account (read more here). Set up a property and get your Tracking ID, like 'UA-XXXXXXX-X' (read more here).
  3. A Google Tag Manager account needs to be created (https://tagmanager.google.com/). Create a container for your site in the account.


    NOTE: You will need to know the container ID. In Google tag Manager, click Workspace. Near the top of the window, find your container ID, formatted as "GTM-XXXXXX".


Where to Start

Like an architect looking to start on a building, the first thing that you need is a blueprint, a data structure, and a foundation for the dataLayer. This article will focus more on the technical aspects of the integration and will provide examples of the most common integration pieces needed by standard Google Analytics tracking.


This article assumes you have an understanding of the dataLayer array, the key value relationship, and how to push data into the array. Google provides a dataLayer developer guide here.


For example, creating a dataLayer with two key value pairs would look like the following:


<script>
  dataLayer = [{
    'loginStatus': 'guest',
    'analyticsConsent': 'true'
  }];
</script>
<!-- Google Tag Manager -->
...
<!-- End Google Tag Manager -->


Note that the dataLayer is above any of your analytics code or tag managers. In this example, the visitor consent is also stored in the dataLayer and because it exists before any of your analytics code, you can block your analytics code from running if the necessary consent is not obtained. This is important where data protection requires explicit consent, such as regions under General Data Protection Regulation (GDPR). After the initial dataLayer has been specified, you can also add additional value pairs to the dataLayer using the push function (otherwise you will overwrite the original dataLayer). You should specify an event value pair to categorize and organize your dataLayer events.


In this example, we are pushing a dataLayer event 'email-signup' with two pieces of information; the type of e-mail signup and where the signup occurred. For example, you could tie this to a button click of the e-mail sign up button.


dataLayer.push({
  'event': 'email-signup',
  'type': 'newsletter',
  'method': 'global-popup'
});


Using these two dataLayer functions, you are able to create a dataLayer on all pages, as well as push dataLayer events as needed. Where should you start when implementing a dataLayer on your site? The first step is to consider the analytics data structure.


1. Analytics Data Structure

One of the most important aspects of a web analytics integration is mapping out the data structure and foundation. Without adequate preparation, the foundation could be flawed. This is far more difficult (if not impossible) to remedy after data is already flowing into the analytics tools. This dataLayer foundation will feed your front-end analytics tools, digital marketing tags, and other data driven services. This means the dataLayer needs to be consistent and standardized across all your tracked pages.

The dataLayer structure and schema should be guided by two major areas: business data requirements and the web analytics technology landscape. 


Business Requirements

For most businesses, the standard Google Analytics implementation offers the majority of the commonly used web analytics tracking. For those interested in learning about Google Analytics' feature set, the Analytics Academy is a great resource. If your business has specific data or tracking requirements, it is best to compile and surface these at the beginning of the integration to ensure they are covered when building the data structure.

In this article, the focus will be on the commonly implemented Google Analytics tracking requirements and the technical aspects of the Google Analytics integration.


Web Analytics Technology Landscape

The other piece in consideration when building a data structure is what technology and tools are currently in use. In part one of this series, we explored three layers of technology in the landscape. Keeping these layers in mind ensures you build a data structure that supports the tools you use. For example, if you use an A/B testing tool or a product recommendation engine, you would want to ensure the dataLayer contains test variant or product data.

To keep things simple, we will focus on the SAP Commerce Cloud and Google Analytics integration when building out a data structure.

2. Page Categories: DataLayer Templates

When implementing the dataLayer on your SAP Commerce Cloud solution website, it is easier to organize by page categories. By creating dataLayer templates, you ensure that you maintain data consistency across similar pages, as well as easier maintenance when changes are required. Nearly all online storefronts will need the following dataLayer types for a Google Analytics integration.

  • All Pages - Global dataLayer
  • Product List (including search) Pages - List dataLayer
  • eCommerce Events - eCommerce dataLayer
  • Order Confirmation - order dataLayer


Google has some standardized values that it uses for specific Google Analytics tracking features, for example, adding a product to the cart. We will not list them in detail here but they can be found in the Google reference guide. The dataLayer code below are examples. We also provide some sample customizations in later sections to help you understand how to add code to fit your website's needs.


All Pages (Global) DataLayer

Because Google Analytics (GA) collects pageview data on all (or nearly all) of your website pages, it makes sense to have a global dataLayer that exists on all your pages. For example, this dataLayer can be used to:

  1. Track analytics consent.
  2. Pass visitor information (such as login status or customer segment) to GA.
  3. Pass site information (such as site region or server node) to GA.

Because this will exist on all pages, you can either include the variables when you create the dataLayer or use the dataLayer.push function after. 

  dataLayer = [{
    'loginStatus': 'guest',
    'analyticsConsent': 'true',
	'userID': ''
  }];
dataLayer = [];
dataLayer.push({
  	'loginStatus': 'guest',
 	'analyticsConsent': 'true',
	'userID': ''
	});


Login status, analytics consent, and userID are just some common examples you can use for the global dataLayer. You will have to determine which variables you need for your Google Analytics tracking.


eCommerce DataLayer

Google Analytics comes with an enhanced eCommerce tracking that provides data on the primary eCommerce events. These can occur on your site, such as adding products to cart, beginning the checkout process, and completing an online order. Google provides detailed documentation here. This section will highlight some of the important parts of the dataLayer and takes some of the code examples.


Product Detail Views

This dataLayer will need to be created on all your product pages to pass data to Google Analytics about the product that is being viewed. It should load after the global dataLayer on page load (or when page renders in single page applications).


dataLayer.push({
  'ecommerce': {
    'detail': {
      'actionField': {'list': 'Category Name'},    // 'detail' actions have an optional list property.
      'products': [{
        'name': 'Product Example',         // Name or ID is required.
        'id': '12345',
        'price': '123',
        'brand': 'ABC',
        'category': 'Socks',
        'variant': 'Black'
       }]
     }
   }
});


Which attributes you use depends on your business needs. You can add custom dimensions if needed by using the custom dimension index (that is, 'dimension12': 'small') created within Google Analytics.


Add/Remove Product from Cart

Similarly to product detail views, you will want to pass product data to Google Analytics when a visitor adds or removes a product to their cart. This dataLayer should be triggered by the add/remove cart event (on mouseclick or after cart validation). Remember, this triggers the dataLayer event everywhere the cart actions can occur and often product widgets get overlooked.


// Measure adding a product to a shopping cart by using an 'add' actionFieldObject
// and a list of productFieldObjects.
dataLayer.push({
  'event': 'addToCart',
  'ecommerce': {
    'currencyCode': 'USD',
    'add': {                                // 'add' actionFieldObject measures.
      'products': [{                        //  adding a product to a shopping cart.
        'name': 'Product Example',
        'id': '12345',
        'price': '123',
        'brand': 'ABC',
        'category': 'Socks',
        'variant': 'Black',
        'quantity': 1
       }]
    }
  }
});
// Measure the removal of a product from a shopping cart.
dataLayer.push({
  'event': 'removeFromCart',
  'ecommerce': {
    'currencyCode': 'USD',
    'remove': {                               // 'remove' actionFieldObject measures.
      'products': [{                          //  removing a product to a shopping cart.
          'name': 'Product Example',
          'id': '12345',
          'price': '123',
          'brand': 'ABC',
          'category': 'Socks',
          'variant': 'Black',
          'quantity': 1
      }]
    }
  }
});


Ensure that your product attribute keys and values are consistent across your dataLayer on different pages. They are case sensitive and Google Analytics will treat different values independently. Like the product detail view dataLayer, you can add custom dimensions by adding in the custom dimension index value pair when you want to append additional attributes.


Checkout

Checkout pages vary tremendously in implementation and design, so often integrating Google Analytics into checkout pages is one of the most difficult areas. Express checkouts like ApplePay and PayPal increase this complexity. Therefore, it is a good idea to draw out a flow chart of all the possible checkout paths before implementing the tracking.

For checkouts, you will want to track the following (for which we provide examples in the sections below):

  1. Begin Checkout - When a visitor proceeds to start checkout process, typically from the cart page. You can use the eventCallback function to ensure the event gets passed before the page is unloaded.
  2. Checkout Steps - Including shipping, payment, and the summary confirmation.
  3. Complete Checkout - When the checkout process is completed.


The check event has a few special fields:

  • checkout.actionField - To get Google Analytics' checkout flow tracking, you must append the step numbers to each checkout page or step. You can also append other additional data like payment type.
  • checkout.products - This is an array that should contain all the products in the cart at the time of checkout

  dataLayer.push({
    'event': 'checkout',
    'ecommerce': {
      'checkout': {
        'actionField': {'step': 1, 'option': 'Visa'}, 			// You can append data like payment and shipping method to the actionField if you would like this in GA using a custom dimension
        'products': [{
          'name': 'Product Example',
          'id': '12345',
          'price': '123',
          'brand': 'ABC',
          'category': 'Socks',
          'variant': 'Black',
          'quantity': 1
       },
		{
        'name': 'Second Product Example',
        'id': '12346',
        'price': '100',
        'brand': 'DEF',
        'category': 'Pants',
        'variant': 'Grey',
        'quantity': 1					// Add additional products being checked out in the array
       }]
    }
  }
});


Order Confirmation

One of the most commonly viewed metrics in Google Analytics is the total revenue and conversion rate. While basic revenue and order capture is relatively easy to implement, many online stores miss the opportunity to append additional eCommerce tracking on their order confirmation pages. Similarly to checkout tracking, the product array allows product data to be tied together in Google Analytics with other eCommerce activities such as view product and add to cart. This data can be used to generate product reports that contain individual product conversion rates and other valuable information.


// Send transaction data with a pageview if available
// when the page loads. Otherwise, use an event when the transaction
// data becomes available.
dataLayer.push({
  'ecommerce': {
    'purchase': {
      'actionField': {
        'id': 'T12345',                         // Transaction ID. Required for purchases.
        'affiliation': 'Web Storefront',
        'revenue': '245.30',                     // Total transaction value (incl. tax and shipping)
        'tax':'22.30',
        'shipping': '0'							// You can append data like payment and shipping method to the actionField if you would like this in GA using a custom dimension
      },
      'products': [{                            // List of productFieldObjects.
          'name': 'Product Example',
          'id': '12345',
          'price': '123',
          'brand': 'ABC',
          'category': 'Socks',
          'variant': 'Black',
          'quantity': 1
       				                           // Optional fields may be omitted or set to empty string.
       },
		{
        'name': 'Second Product Example',
        'id': '12346',
        'price': '100',
        'brand': 'DEF',
        'category': 'Pants',
        'variant': 'Grey',
        'quantity': 1
       }]
    }
  }
});


Other DataLayers

There are additional dataLayer events that you can implement on your site depending on your Google Analytics requirements. We will not go into them here, but information can be found in the Google documentation.

  • Product Lists
  • Promotions 
  • Other Site Events

Integrating Google Analytics with the DataLayer in your SAP Commerce Cloud Application

This section will provide some real-life examples of how you can integrate Google Tag Manager into a B2C accelerator. We use the name "customstorefront" to denote the name of a custom storefront created with the accelerator template through the "ant modulegen" command. Replace the name with the name of your storefront.


  1. Create a file customstorefront/web/webroot/WEB-INF/tags/shared/analytics/googleTagManagerContainer.tag

    You will need to replace 'GTM-XXXXXX' with the container ID generated in Google Tag Manager (see prerequisites section above)

    <!-- Google Tag Manager -->
    <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXX" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
    <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
    })(window,document,'script','dataLayer','GTM-XXXXXX');</script>
    <!-- End Google Tag Manager -->
  2. Create a file customstorefront/web/webroot/WEB-INF/tags/shared/analytics/googleTagManagerDataLayer.tag

    <%@ tag body-content="empty" trimDirectiveWhitespaces="true" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
    <%@ taglib prefix="ycommerce" uri="http://hybris.com/tld/ycommercetags" %>
    
    <!-- Google Tag Manager Data Layer -->
    <script type="text/javascript">
    <c:set var="path" value="" />
    <c:forEach items="${breadcrumbs}" var="breadcrumb" varStatus="status">
        <c:set var="path">${path}${breadcrumb.name}<c:if test="${not status.last}">/</c:if></c:set>
    </c:forEach>
    window.dataLayer = window.dataLayer || [];
    <c:if test="${not empty user.customerId}">
        window.dataLayer.push({
            'userId': '${user.customerId}'
        });
    </c:if>
    <c:choose>
        <c:when test="${pageType == 'PRODUCTSEARCH' || pageType == 'CATEGORY'}">
            window.dataLayer.push({
                  'ecommerce': {
                    'currencyCode': '${currentCurrency.isocode}',
                    'impressions': [
                            <c:forEach items="${searchPageData.results}" var="product" varStatus="loop">
                                {
                                  'id': '${product.code}',
                                  'name': '${product.name}',
                                  'price': '${product.price.value}',<c:if test="${pageType == 'CATEGORY'}">
                                  'category': '${path}',</c:if>
                                  'list': '${path}',
                                  'position': '${loop.index + 1}'
                                }<c:if test="${!loop.last}">,</c:if>
                            </c:forEach>
                        ]
                    }
            });
        </c:when>
        <c:when test="${pageType == 'PRODUCT'}">
            window.dataLayer.push({
                  'ecommerce': {
                    'currencyCode': '${currentCurrency.isocode}',
                    'detail': {
                      'actionField': {'list': '${path}'}, 
                      'products': [{
                          'id': '${product.code}',
                          'name': '${product.name}',
                          'price': '${product.price.value}',
                          'brand':     <c:choose>
                                            <c:when test="${not empty product.categories}">
                                                '${ycommerce:encodeJavaScript(product.categories[fn:length(product.categories) - 1].name)}'
                                            </c:when>
                                            <c:otherwise>
                                                ''
                                            </c:otherwise>
                                        </c:choose>
                       }]
                     }
                   }
            });
        </c:when>
        <c:when test="${pageType == 'CART' || pageType == 'CHECKOUT'}">
            window.mediator.subscribe('onCheckout_gtm', function(data) {
                if (data.checkoutStep)
                {
                    trackOnCheckout_gtm(data.checkoutStep, data.option);
                }
            });
            function trackOnCheckout_gtm(checkoutStep, option) {
                if (!option) {
                    option = '${path}';
                }
                window.dataLayer.push({
                    'event': 'checkout',
                    'ecommerce': {
                      'checkout': {
                        'actionField': {'step': checkoutStep, 'option': option},
                        'products': [
                               <c:forEach items="${cartData.entries}" var="entry" varStatus="loop">
                            {
                                'id': '${ycommerce:encodeJavaScript(entry.product.code)}',
                                'name': '${ycommerce:encodeJavaScript(entry.product.name)}', 
                                'price': Number(Math.round((parseFloat('${entry.totalPrice.value}')/parseFloat('${entry.quantity}'))+'e2')+'e-2'),
                                'brand': <c:choose>
                                                 <c:when test="${not empty entry.product.categories}">
                                                     '${ycommerce:encodeJavaScript(entry.product.categories[fn:length(entry.product.categories) - 1].name)}',
                                                 </c:when>
                                                 <c:otherwise>
                                                     '',
                                                  </c:otherwise>
                                             </c:choose>
                                'quantity': '${entry.quantity}'
                            }<c:if test="${not loop.last}">,</c:if>
                            </c:forEach> 
                          ]
                     }
                   }
                 });
            }
        </c:when>
        <c:when test="${pageType == 'ORDERCONFIRMATION'}">
            <c:if test="${not empty couponCodes}">
                <c:set var="coupons" value="" />
                <c:forEach items="${couponCodes}" var="couponCode" varStatus="loop">
                    <c:set var="coupons">${couponCode}<c:if test="${!loop.last}">|</c:if></c:set>
                </c:forEach>
            </c:if>
            window.dataLayer.push({
                  'ecommerce': {
                    'purchase': {
                      'actionField': {
                        'id': '${ycommerce:encodeJavaScript(orderData.code)}',
                        'affiliation': '${ycommerce:encodeJavaScript(siteName)}',
                        'revenue': '${orderData.totalPrice.value}',
                        'tax':'${orderData.totalTax.value}',
                        'shipping': '${orderData.deliveryCost.value}',
                        'coupon': '${coupons}'
                      },
                      'products': [
                               <c:forEach items="${orderData.entries}" var="entry" varStatus="loop">
                            {
                                'id': '${ycommerce:encodeJavaScript(entry.product.code)}',
                                'name': '${ycommerce:encodeJavaScript(entry.product.name)}', 
                                'price': Number(Math.round((parseFloat('${entry.totalPrice.value}')/parseFloat('${entry.quantity}'))+'e2')+'e-2'),
                                'brand': <c:choose>
                                                <c:when test="${not empty entry.product.categories}">
                                                    '${ycommerce:encodeJavaScript(entry.product.categories[fn:length(entry.product.categories) - 1].name)}',
                                                </c:when>
                                                <c:otherwise>
                                                    '',
                                                 </c:otherwise>
                                            </c:choose>
                                'quantity': '${entry.quantity}'
                            }<c:if test="${not loop.last}">,</c:if>
                            </c:forEach> 
                      ]
                    }
                  }
                });
        </c:when>
    </c:choose>
    
    window.mediator.subscribe('productClick_gtm', function(data) {
        if (data.productCode && data.productName && data.productPrice && data.productUrl)
        {
            trackProductClick_gtm(data.productCode, data.productName, data.productPrice, data.productUrl);
        }
    });
    
    function trackProductClick_gtm(productCode, productName, productPrice, productUrl) {
        window.dataLayer.push({
            'event': 'productClick',
                'ecommerce': {
                  'click': {
                    'actionField': {'list': '${path}'},
                        'products': [{
                              'id': productCode,
                              'name': productName,
                              'price': productPrice
                          }]
                   }
              },
            'eventCallback': function() {
                if (productUrl) {
                    document.location = productUrl;
                 }
              }
        });
    }
    
    window.mediator.subscribe('addToCart_gtm', function(data) {
        if (data.productCode && data.productName && data.productPrice && data.quantityAdded)
        {
            trackAddToCart_gtm(data.productCode, data.productName, data.productPrice, data.quantityAdded);
        }
    });
    
    function trackAddToCart_gtm(productCode, productName, productPrice, quantityAdded) {
        window.dataLayer.push({
            'event': 'addToCart',
            'ecommerce': {
                'currencyCode': '${currentCurrency.isocode}',
                'add': {                            
                    'products': [{
                          'id': productCode,
                          'name': productName,
                          'price': productPrice,
                         'quantity': Number(quantityAdded)
                     }]
                }
            }
        });
    }
    
    window.mediator.subscribe('removeFromCart_gtm', function(data) {
        if (data.productCode && data.productName && data.productPrice && data.quantityRemoved)
        {
            trackRemoveFromCart_gtm(data.productCode, data.productName, data.productPrice, data.quantityRemoved);
        }
    });
    
    function trackRemoveFromCart_gtm(productCode, productName, productPrice, quantityRemoved) {
        window.dataLayer.push({
            'event': 'removeFromCart',
              'ecommerce': {
                'currencyCode': '${currentCurrency.isocode}',
                'remove': {                            
                      'products': [{
                          'id': productCode,
                          'name': productName,
                          'price': productPrice,
                          'quantity': Number(quantityRemoved)
                       }]
                }
              }
        });
    }
    
    window.mediator.subscribe('onBannerClick_gtm', function(data) {
        if (data.bannerId && data.bannerName && data.bannerCreative && data.bannerPos && data.bannerUrl)
        {
            trackOnBannerClick_gtm(data.bannerId, data.bannerName, data.bannerCreative, data.bannerPos, data.bannerUrl);
        }
    });
    
    function trackOnBannerClick_gtm(bannerId, bannerName, bannerCreative, bannerPos, bannerUrl) {
        window.dataLayer.push({
            'event': 'promotionClick',
            'ecommerce': {
               'promoClick': {
                 'promotions': [
                  {
                    'id': bannerId, 
                    'name': bannerName,
                    'creative': bannerCreative,
                    'position': bannerPos
                  }]
               }
            },
            'eventCallback': function() {
                 if (bannerUrl) {
                     document.location = bannerUrl;
                 }
            }
        });
    }
    </script>
    <!-- End Google Tag Manager Data Layer -->
    
    
    
  3. Create a file storefront/web/webroot/WEB-INF/_ui-src/responsive/lib/ybase-0.1.0/js/acc.gtm.js as well as in storefront/web/webroot/_ui/responsive/common/js/acc.gtm.js:

    ACC.gtm = {
            
        _autoload: [
            "bindtItem"
        ],
    
    
        bindtItem: function ()
        {
            
            $(".product-item > .thumb, .item > a > .thumb, .carousel__item--thumb").on("click", function ()
            {
                var productCode =  $(this).data("productCode");
                var productName =  $(this).data("productName");
                var productPrice =  $(this).data("productPrice");
                var productUrl =  $(this).data("productUrl");
                ACC.gtm.productClick_gtm(productCode, productName, productPrice, productUrl);
            });
    
            $(".btn.btn-primary.btn-block.glyphicon.glyphicon-shopping-cart.js-enable-btn").on("click", function ()
            {
                var productCode =  $(this).data("productCode");
                var productName =  $(this).data("productName");
                var productPrice =  $(this).data("productPrice");
                var initialQuantity =  $(this).data("initialQuantity");
                ACC.gtm.addToCart_gtm(productCode, productName, productPrice, initialQuantity);
            });
            
            $(".btn.btn-primary.btn-block.js-add-to-cart.js-enable-btn.btn-icon.glyphicon-shopping-cart").on("click", function ()
            {
                var productCode =  $(this).data("productCode");
                var productName =  $(this).data("productName");
                var productPrice =  $(this).data("productPrice");
                var initialQuantity = $('.qty').attr('value');
                ACC.gtm.addToCart_gtm(productCode, productName, productPrice, initialQuantity);
            });
            
            $(".simple-banner.banner__component--responsive").on("click", function ()
            {
                var bannerId =  $(this).data("bannerId");
                var bannerName =  $(this).data("bannerName");
                var bannerCreative =  $(this).data("bannerCreative");
                var bannerPos =  $(this).data("bannerPos");
                var bannerUrl =  $(this).data("bannerUrl");
                ACC.gtm.onBannerClick_gtm(bannerId, bannerName, bannerCreative, bannerPos, bannerUrl);
            });
            
            $(".btn.btn-primary.btn-block.btn--continue-checkout.js-continue-checkout-button, .btn.btn-primary.btn-block.checkout-next, .btn.btn-primary.btn-place-order.btn-block").on("click", function ()
            {
                var checkoutStep =  $(this).data("checkoutStep");
                var option = "";
                switch (checkoutStep) {
                    case 1:
                        option = $("#selectAltCheckoutFlow option:selected").val();
                        break;
                    case 2:
                        option = $("#address\\.country option:selected").text();
                        break;
                    case 3:
                        option = $("#delivery_method option:selected").text();
                        break;
                    case 4:
                        option = $("#card_cardType option:selected").text();
                        break;
                    case 5:
                        option = $.trim($(".totals span").first().text());
                        break;
                }
                ACC.gtm.onCheckout_gtm(checkoutStep, option);
            });
    
        },
    
        updateCart_gtm: function(productCode, productName, productPrice, initialQuantity, newQuantity)
        {
            initialQuantity = Number(initialQuantity);
            newQuantity = Number(newQuantity);
            if (initialQuantity != newQuantity) {
                if (initialQuantity > newQuantity) {
                    ACC.gtm.removeFromCart_gtm(productCode, productName, productPrice, initialQuantity - newQuantity);
                } else {
                    ACC.gtm.addToCart_gtm(productCode, productName, productPrice, newQuantity - initialQuantity);
                }
            }
        },
        
        addToCart_gtm: function(productCode, productName, productPrice, quantityAdded)
        {
            window.mediator.publish('addToCart_gtm',{
                productCode: productCode,
                productName: productName,
                productPrice: productPrice,
                quantityAdded: quantityAdded
            });
        },
        
        removeFromCart_gtm: function(productCode, productName, productPrice, quantityRemoved)
        {
            window.mediator.publish('removeFromCart_gtm',{
                productCode: productCode,
                productName: productName,
                productPrice: productPrice,
                quantityRemoved: quantityRemoved
            });
        },
        
        productClick_gtm: function(productCode, productName, productPrice, productUrl)
        {
            window.mediator.publish('productClick_gtm',{
                productCode: productCode,
                productName: productName,
                productPrice: productPrice,
                productUrl: productUrl
            });
        },
        
        onBannerClick_gtm: function(bannerId, bannerName, bannerCreative, bannerPos, bannerUrl)
        {
            window.mediator.publish('onBannerClick_gtm',{
                bannerId: bannerId,
                bannerName: bannerName,
                bannerCreative: bannerCreative,
                bannerPos: bannerPos,
                bannerUrl: bannerUrl
            });
        },
        
        onCheckout_gtm: function(checkoutStep, option)
        {
            window.mediator.publish('onCheckout_gtm',{
                checkoutStep: checkoutStep,
                option: option
            });
        }
    
    };
  4. Use the attached customstorefront_changes.txt to implement the changes to the necessary storefront Java and Java Server Pages (JSP) files.

3. Google Tag Manager

Google Tag Manager (GTM) is a tag management solution that can be used to deployed analytics tags, digital marketing tags, conversion pixels, and other custom code. GTM allows you to create tags, variables and triggers to manage how this code runs and interacts with your website code.


Additional Code Snippets

If you are interested in creating additional code snippets from the examples we provided above, please see Google's quick start guide.


Google Analytics Tags

After the dataLayer has been implemented, you use GTM tags to pass data from the dataLayer to Google Analytics. Google provides detailed documentation on this process, but we will summarize the key points here. The main Google Analytics tags in GTM are:


Pageview Tags

These tags fire and send data to Google Analytics on page load. These are used to calculate pageviews and other behavioral web analytics data. The key-value pairs you specified in your global dataLayer are typically used to append additional information to these pageviews.


eCommerce Tags

Google Analytics standard and enhanced eCommerce tracking are driven by these eCommerce tags in GTM. For the eCommerce events (such as view product details, add to cart, checkout, and confirmation), a tag should be created in GTM with the following settings: More Settings > Ecommerce, set Enable Enhanced Ecommerce Features to True. Select use Data Layer.

More details can be found here.


Events Tags

Events tags should fire when you want a particular action or event to be recorded in Google Analytics. You can create a GTM tag with the Track Type 'Event' and then use a dataLayer event to trigger and feed the data back into Google Analytics. More details can be found here.


Import Tags, Triggers and Custom Variables

At this point, you could manually create the tags and custom variables in Google Tag Manager. However, if you've implemented the accelerator code, you could optionally import the attached GTM-XXXXXXX_workspace.json GTM container file below to your GTM account. It contains all the tags, triggers and custom variables that were created for the sample implementation above. 


Before importing the GTM-XXXXXXX_workspace.json file, please make the following changes in the file:


You should already have the Account and Container IDs from the prerequisites. However, if you need to find them, you could export your existing GTM Container from the GTM console and look for these values in the exported JSON file (GTM Admin >> Export Container).


  • Change UA-000000000-0 to your Google Analytics property.
  • Change GTM-XXXXXXX to your Google Tag Manager Container ID.
  • Change all instance of 1111111111 to your numeric Account ID.
  • Change all instances of 2222222 to your numeric Container ID.
  • Change app.website.com to your website domain name.


To import the file, go to GTM Admin >> Import Container (Read more here)



QA and Publishing

After your tags have been created, you can test the tags using some handy tag management tools such as:


Once you are comfortable that your tags are working as expected, publish the GTM container to get your Google Analytics integration live.


Conclusion

In this article, you learned how to use a dataLayer approach to integrating Google Analytics with your SAP Commerce Cloud solution. By building some core templates, the dataLayer on your web pages contains the analytics data you want to pass to Google Analytics through Google Tag Manager. Developers can focus on maintaining the dataLayer code and analytics experts can focus on managing their analytics tags in Google Tag Manager. This dataLayer approach can be customized to suit your data needs and can also integrate with other analytics and/or marketing tools. This removes redundant code and the need to manage multiple references to analytics data in the front-end code. By centralizing the data within the dataLayer, and utilizing a tag manager to extract and integrate this data, you simplify the process and shorten your development times required to make updates and additions.

Overlay