3dcart (Shift4Shop) GTM Data Layer Implementation | OpsBlu Docs

3dcart (Shift4Shop) GTM Data Layer Implementation

Complete guide to implementing a data layer for Google Tag Manager on 3dcart/Shift4Shop including template variables and ecommerce data.

Implement a comprehensive data layer for Google Tag Manager on your 3dcart/Shift4Shop store to capture page context, user data, and ecommerce events.

Data Layer Overview

A data layer is a JavaScript object that passes information from your website to GTM. For 3dcart/Shift4Shop, you'll combine:

  • Standard page data
  • 3dcart template variables
  • Ecommerce information
  • User/customer data

Base Data Layer Structure

Initialize Data Layer

Add to Global Footer (BEFORE GTM container code):

<script>
  window.dataLayer = window.dataLayer || [];
</script>

Page-Level Data Layer

Add page context data for all pages:

<script>
  window.dataLayer = window.dataLayer || [];

  // Detect page type
  var pageType = 'other';
  var path = window.location.pathname.toLowerCase();

  if (path === '/' || path === '/default.asp') {
    pageType = 'home';
  } else if (path.indexOf('/product') !== -1) {
    pageType = 'product';
  } else if (path.indexOf('/category') !== -1) {
    pageType = 'category';
  } else if (path.indexOf('/cart') !== -1) {
    pageType = 'cart';
  } else if (path.indexOf('/checkout') !== -1) {
    pageType = 'checkout';
  } else if (path.indexOf('/receipt') !== -1 || path.indexOf('/thankyou') !== -1) {
    pageType = 'confirmation';
  }

  // Push page data
  dataLayer.push({
    'event': 'page_data',
    'pageType': pageType,
    'pagePath': window.location.pathname,
    'pageTitle': document.title,
    'pageUrl': window.location.href
  });
</script>

Page-Specific Data Layers

Homepage Data Layer

<script>
  // Only on homepage
  if (window.location.pathname === '/' || window.location.pathname === '/default.asp') {
    dataLayer.push({
      'event': 'homepage_view',
      'pageType': 'home'
    });
  }
</script>

Product Page Data Layer

Use 3dcart template variables to capture product information:

<script>
  // Only on product pages
  if (window.location.pathname.indexOf('/product') !== -1) {

    dataLayer.push({
      'event': 'product_detail_view',
      'pageType': 'product',
      'ecommerce': {
        'currency': 'USD',
        'value': parseFloat('[productprice]'),
        'items': [{
          'item_id': '[productid]',
          'item_name': '[productname]',
          'item_brand': '[manufacturer]',
          'item_category': '[categoryname]',
          'price': parseFloat('[productprice]'),
          'quantity': 1
        }]
      },
      'product': {
        'id': '[productid]',
        'name': '[productname]',
        'sku': '[productsku]',
        'price': parseFloat('[productprice]'),
        'category': '[categoryname]',
        'manufacturer': '[manufacturer]',
        'inStock': '[stockstatus]' // If available
      }
    });
  }
</script>

Category Page Data Layer

<script>
  // Only on category pages
  if (window.location.pathname.indexOf('/category') !== -1) {

    dataLayer.push({
      'event': 'category_view',
      'pageType': 'category',
      'category': {
        'id': '[categoryid]',
        'name': '[categoryname]'
      }
    });

    // Optional: Track product impressions
    // Parse products from page and push to data layer
    var products = [];
    var productElements = document.querySelectorAll('.product-item, .product-box');

    productElements.forEach(function(element, index) {
      var productId = element.getAttribute('data-product-id');
      var productName = element.querySelector('.product-name')?.textContent;
      var productPrice = element.querySelector('.product-price')?.textContent;

      if (productId && productName) {
        products.push({
          'item_id': productId,
          'item_name': productName.trim(),
          'price': parseFloat(productPrice?.replace(/[^0-9.]/g, '')) || 0,
          'index': index,
          'item_list_name': '[categoryname]',
          'item_list_id': '[categoryid]'
        });
      }
    });

    if (products.length > 0) {
      dataLayer.push({
        'event': 'view_item_list',
        'ecommerce': {
          'items': products.slice(0, 20) // Limit to first 20
        }
      });
    }
  }
</script>

Cart Page Data Layer

<script>
  // Only on cart page
  if (window.location.pathname.indexOf('/cart') !== -1) {

    // Parse cart items from page
    var cartItems = [];
    var cartRows = document.querySelectorAll('.cart-item, table.cart tbody tr');

    cartRows.forEach(function(row) {
      var productId = row.getAttribute('data-product-id') || '';
      var productName = row.querySelector('.product-name, .item-name')?.textContent || '';
      var productPrice = row.querySelector('.price, .item-price')?.textContent || '0';
      var quantity = row.querySelector('input[name="quantity"]')?.value || '1';

      if (productName) {
        cartItems.push({
          'item_id': productId,
          'item_name': productName.trim(),
          'price': parseFloat(productPrice.replace(/[^0-9.]/g, '')),
          'quantity': parseInt(quantity)
        });
      }
    });

    // Get cart total
    var cartTotalElement = document.querySelector('.cart-total, .total-amount');
    var cartTotal = cartTotalElement ? parseFloat(cartTotalElement.textContent.replace(/[^0-9.]/g, '')) : 0;

    dataLayer.push({
      'event': 'cart_view',
      'pageType': 'cart',
      'ecommerce': {
        'currency': 'USD',
        'value': cartTotal,
        'items': cartItems
      },
      'cart': {
        'total': cartTotal,
        'itemCount': cartItems.length
      }
    });
  }
</script>

Checkout Page Data Layer

<script>
  // Only on checkout pages
  if (window.location.pathname.indexOf('/checkout') !== -1) {

    dataLayer.push({
      'event': 'checkout_view',
      'pageType': 'checkout'
    });

    // Optional: Add checkout items if accessible
    var checkoutItems = [];
    var itemRows = document.querySelectorAll('.checkout-item, .order-item');

    itemRows.forEach(function(row) {
      var productName = row.querySelector('.product-name, .item-name')?.textContent;
      var productPrice = row.querySelector('.price')?.textContent;
      var quantity = row.querySelector('.quantity, .qty')?.textContent;

      if (productName) {
        checkoutItems.push({
          'item_name': productName.trim(),
          'price': parseFloat(productPrice?.replace(/[^0-9.]/g, '') || '0'),
          'quantity': parseInt(quantity?.replace(/[^0-9]/g, '') || '1')
        });
      }
    });

    if (checkoutItems.length > 0) {
      var orderTotal = document.querySelector('.order-total, .total-amount');
      var total = orderTotal ? parseFloat(orderTotal.textContent.replace(/[^0-9.]/g, '')) : 0;

      dataLayer.push({
        'event': 'begin_checkout',
        'ecommerce': {
          'currency': 'USD',
          'value': total,
          'items': checkoutItems
        }
      });
    }
  }
</script>

Order Confirmation Data Layer

Critical for purchase tracking:

<script>
  // Only on order confirmation/receipt page
  if (window.location.pathname.indexOf('/receipt') !== -1 ||
      window.location.pathname.indexOf('/thankyou') !== -1) {

    var orderId = '[invoicenumber]';

    // Check if already tracked
    if (!sessionStorage.getItem('purchase_tracked_' + orderId)) {

      // Parse order items
      var orderItems = [];
      var itemRows = document.querySelectorAll('.order-item, .receipt-item');

      itemRows.forEach(function(row) {
        var productName = row.querySelector('.product-name, .item-name')?.textContent;
        var productSku = row.querySelector('.sku')?.textContent;
        var productPrice = row.querySelector('.price')?.textContent;
        var quantity = row.querySelector('.quantity, .qty')?.textContent;

        if (productName) {
          orderItems.push({
            'item_id': productSku?.trim() || '',
            'item_name': productName.trim(),
            'price': parseFloat(productPrice?.replace(/[^0-9.]/g, '') || '0'),
            'quantity': parseInt(quantity?.replace(/[^0-9]/g, '') || '1')
          });
        }
      });

      // Push purchase event
      dataLayer.push({
        'event': 'purchase',
        'pageType': 'confirmation',
        'ecommerce': {
          'transaction_id': orderId,
          'affiliation': 'Online Store',
          'value': parseFloat('[invoicetotal]'),
          'currency': 'USD',
          'tax': parseFloat('[invoicetax]'),
          'shipping': parseFloat('[invoiceshipping]'),
          'coupon': '[couponcode]',
          'items': orderItems
        },
        'transaction': {
          'id': orderId,
          'total': parseFloat('[invoicetotal]'),
          'tax': parseFloat('[invoicetax]'),
          'shipping': parseFloat('[invoiceshipping]'),
          'coupon': '[couponcode]'
        }
      });

      // Mark as tracked
      sessionStorage.setItem('purchase_tracked_' + orderId, 'true');
    }
  }
</script>

User/Customer Data Layer

Customer Information

Add customer data when available:

<script>
  // Check if customer is logged in
  var customerId = '[customerid]';

  if (customerId && customerId !== '[customerid]') {
    dataLayer.push({
      'event': 'customer_data',
      'user': {
        'id': customerId,
        'type': '[customertypeid]',
        'logged_in': true
      }
    });
  } else {
    dataLayer.push({
      'event': 'customer_data',
      'user': {
        'logged_in': false
      }
    });
  }
</script>

Event-Based Data Layer Pushes

Add to Cart Event

<script>
  document.addEventListener('DOMContentLoaded', function() {
    var addToCartForms = document.querySelectorAll('form[action*="addtocart"]');

    addToCartForms.forEach(function(form) {
      form.addEventListener('submit', function() {
        var quantity = form.querySelector('input[name="quantity"]')?.value || 1;

        dataLayer.push({
          'event': 'add_to_cart',
          'ecommerce': {
            'currency': 'USD',
            'value': parseFloat('[productprice]') * parseInt(quantity),
            'items': [{
              'item_id': '[productid]',
              'item_name': '[productname]',
              'price': parseFloat('[productprice]'),
              'quantity': parseInt(quantity)
            }]
          }
        });
      });
    });
  });
</script>

Remove from Cart Event

<script>
  document.addEventListener('DOMContentLoaded', function() {
    if (window.location.pathname.indexOf('/cart') !== -1) {

      var removeButtons = document.querySelectorAll('.remove-item, a[href*="remove"]');

      removeButtons.forEach(function(button) {
        button.addEventListener('click', function() {
          var cartRow = button.closest('tr, .cart-item');
          var productName = cartRow?.querySelector('.product-name')?.textContent;
          var productPrice = cartRow?.querySelector('.price')?.textContent;
          var quantity = cartRow?.querySelector('input[name="quantity"]')?.value || 1;

          if (productName) {
            dataLayer.push({
              'event': 'remove_from_cart',
              'ecommerce': {
                'currency': 'USD',
                'value': parseFloat(productPrice?.replace(/[^0-9.]/g, '') || '0') * parseInt(quantity),
                'items': [{
                  'item_name': productName.trim(),
                  'price': parseFloat(productPrice?.replace(/[^0-9.]/g, '') || '0'),
                  'quantity': parseInt(quantity)
                }]
              }
            });
          }
        });
      });
    }
  });
</script>

GTM Variables for 3dcart Data Layer

Create Data Layer Variables in GTM

For each data point in your data layer, create corresponding GTM variables.

Page Type Variable

  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: pageType
  • Variable Name: DLV - Page Type

Product ID Variable

  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: product.id
  • Variable Name: DLV - Product ID

Product Name Variable

  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: product.name
  • Variable Name: DLV - Product Name

Product Price Variable

  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: product.price
  • Variable Name: DLV - Product Price

Category ID Variable

  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: category.id
  • Variable Name: DLV - Category ID

Category Name Variable

  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: category.name
  • Variable Name: DLV - Category Name

Cart Total Variable

  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: cart.total
  • Variable Name: DLV - Cart Total

Transaction ID Variable

  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: transaction.id
  • Variable Name: DLV - Transaction ID

User ID Variable

  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: user.id
  • Variable Name: DLV - User ID

Ecommerce Items Variable

  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: ecommerce.items
  • Variable Name: DLV - Ecommerce Items

Creating GTM Triggers Based on Data Layer

Product View Trigger

  • Trigger Type: Custom Event
  • Event name: product_detail_view
  • This trigger fires on: All Custom Events
  • Trigger Name: Event - Product View

Category View Trigger

  • Trigger Type: Custom Event
  • Event name: category_view
  • Trigger Name: Event - Category View

Add to Cart Trigger

  • Trigger Type: Custom Event
  • Event name: add_to_cart
  • Trigger Name: Event - Add to Cart

Remove from Cart Trigger

  • Trigger Type: Custom Event
  • Event name: remove_from_cart
  • Trigger Name: Event - Remove from Cart

Purchase Trigger

  • Trigger Type: Custom Event
  • Event name: purchase
  • Trigger Name: Event - Purchase

Complete Data Layer Example

Here's a complete data layer implementation for Global Footer:

<script>
  // Initialize data layer
  window.dataLayer = window.dataLayer || [];

  // Detect page type
  var pageType = 'other';
  var path = window.location.pathname.toLowerCase();

  if (path === '/' || path === '/default.asp') pageType = 'home';
  else if (path.indexOf('/product') !== -1) pageType = 'product';
  else if (path.indexOf('/category') !== -1) pageType = 'category';
  else if (path.indexOf('/cart') !== -1) pageType = 'cart';
  else if (path.indexOf('/checkout') !== -1) pageType = 'checkout';
  else if (path.indexOf('/receipt') !== -1 || path.indexOf('/thankyou') !== -1) pageType = 'confirmation';

  // Push base data
  dataLayer.push({
    'pageType': pageType,
    'pagePath': window.location.pathname,
    'pageTitle': document.title
  });

  // Product page data
  if (pageType === 'product') {
    dataLayer.push({
      'event': 'product_detail_view',
      'ecommerce': {
        'currency': 'USD',
        'value': parseFloat('[productprice]'),
        'items': [{
          'item_id': '[productid]',
          'item_name': '[productname]',
          'price': parseFloat('[productprice]'),
          'item_category': '[categoryname]',
          'quantity': 1
        }]
      }
    });
  }

  // Order confirmation data
  if (pageType === 'confirmation') {
    var orderId = '[invoicenumber]';
    if (!sessionStorage.getItem('purchase_tracked_' + orderId)) {
      dataLayer.push({
        'event': 'purchase',
        'ecommerce': {
          'transaction_id': orderId,
          'value': parseFloat('[invoicetotal]'),
          'currency': 'USD',
          'tax': parseFloat('[invoicetax]'),
          'shipping': parseFloat('[invoiceshipping]'),
          'items': [] // Parse from page
        }
      });
      sessionStorage.setItem('purchase_tracked_' + orderId, 'true');
    }
  }

  // Customer data
  var customerId = '[customerid]';
  if (customerId && customerId !== '[customerid]') {
    dataLayer.push({
      'user': {
        'id': customerId,
        'logged_in': true
      }
    });
  }
</script>

Testing the Data Layer

Browser Console Testing

// View entire data layer
console.table(window.dataLayer);

// View specific event
console.log(window.dataLayer.filter(item => item.event === 'purchase'));

// Monitor data layer pushes
const originalPush = window.dataLayer.push;
window.dataLayer.push = function() {
  console.log('DataLayer Push:', arguments[0]);
  originalPush.apply(window.dataLayer, arguments);
};

GTM Preview Mode

  1. Enable Preview mode in GTM
  2. Visit each page type
  3. Check "Data Layer" tab in Tag Assistant
  4. Verify variables populate correctly
  5. Confirm events fire as expected

Debug Checklist

  • ✓ Data layer initializes before GTM
  • ✓ Page type detected correctly
  • ✓ Template variables populate (not showing [variable] literal)
  • ✓ Numeric values are parsed (not strings with currency symbols)
  • ✓ Events fire on correct user actions
  • ✓ Items array structure is correct
  • ✓ No duplicate events

Common Issues and Solutions

Template Variables Show Literal Text

Problem: Data layer shows [productid] instead of actual ID

Cause: Template variable used on wrong page type

Fix: Add page type checks:

if (pageType === 'product') {
  // Only use product variables here
}

Data Layer Undefined

Problem: window.dataLayer is undefined

Fix: Initialize before GTM:

window.dataLayer = window.dataLayer || [];

Duplicate Events

Problem: Same event fires multiple times

Cause: Multiple data layer pushes or page reloads

Fix: Use sessionStorage for deduplication:

if (!sessionStorage.getItem('event_tracked')) {
  dataLayer.push({...});
  sessionStorage.setItem('event_tracked', 'true');
}

Values Are Strings, Not Numbers

Problem: Prices showing as "$29.99" instead of 29.99

Fix: Parse currency values:

value: parseFloat('[productprice]') // Correct
value: '[productprice]' // Wrong

Next Steps

Additional Resources