Skip to main content

E-commerce Analytics

Comprehensive guide to implementing e-commerce analytics with GA4, including enhanced e-commerce events, product performance tracking, and revenue optimization.

E-commerce Event Schema

Full Shopping Funnel

┌─────────────────────────────────────────────────────────┐
│                  E-commerce Funnel                       │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  view_item_list → view_item → add_to_cart → purchase   │
│       │              │            │             │       │
│       ▼              ▼            ▼             ▼       │
│  Product         Product      Cart/         Transaction │
│  impressions     detail       Checkout                  │
│                                                         │
│  Optional events:                                       │
│  • select_item (click from list)                       │
│  • add_to_wishlist                                     │
│  • begin_checkout                                      │
│  • add_shipping_info                                   │
│  • add_payment_info                                    │
│  • refund                                              │
│                                                         │
└─────────────────────────────────────────────────────────┘

Item Object Structure

Every e-commerce event uses a consistent item structure:

const item = {
  item_id: 'SKU_12345',           // Required
  item_name: 'Product Name',       // Required
  affiliation: 'Store Name',       // Optional
  coupon: 'SUMMER20',              // Optional
  discount: 5.00,                  // Optional
  index: 0,                        // Position in list
  item_brand: 'Brand Name',        // Recommended
  item_category: 'Apparel',        // Recommended
  item_category2: 'Mens',          // Up to 5 levels
  item_category3: 'Shirts',
  item_category4: 'T-Shirts',
  item_category5: 'Graphic Tees',
  item_list_id: 'related_products', // List identifier
  item_list_name: 'Related Products', // List name
  item_variant: 'Blue / Large',    // Size, color, etc.
  location_id: 'store_123',        // For local inventory
  price: 29.99,                    // Unit price
  quantity: 1                      // Quantity
};

Core E-commerce Events

View Item List (Product Impressions)

// Category page, search results, or product list
dataLayer.push({ ecommerce: null });  // Clear previous
dataLayer.push({
  event: 'view_item_list',
  ecommerce: {
    item_list_id: 'category_shirts',
    item_list_name: 'Shirts Category',
    items: [
      {
        item_id: 'SKU_001',
        item_name: 'Classic T-Shirt',
        item_brand: 'Brand X',
        item_category: 'Apparel',
        item_category2: 'Shirts',
        price: 29.99,
        index: 0
      },
      {
        item_id: 'SKU_002',
        item_name: 'Premium T-Shirt',
        item_brand: 'Brand X',
        item_category: 'Apparel',
        item_category2: 'Shirts',
        price: 39.99,
        index: 1
      }
      // ... more items
    ]
  }
});

Select Item (Product Click)

// User clicks product from list
dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'select_item',
  ecommerce: {
    item_list_id: 'category_shirts',
    item_list_name: 'Shirts Category',
    items: [{
      item_id: 'SKU_001',
      item_name: 'Classic T-Shirt',
      item_brand: 'Brand X',
      item_category: 'Apparel',
      price: 29.99,
      index: 0
    }]
  }
});

View Item (Product Detail)

// Product detail page view
dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'view_item',
  ecommerce: {
    currency: 'USD',
    value: 29.99,
    items: [{
      item_id: 'SKU_001',
      item_name: 'Classic T-Shirt',
      item_brand: 'Brand X',
      item_category: 'Apparel',
      item_category2: 'Mens',
      item_category3: 'T-Shirts',
      item_variant: 'Blue / Large',
      price: 29.99,
      quantity: 1
    }]
  }
});

Add to Cart

// Add item to cart
dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'add_to_cart',
  ecommerce: {
    currency: 'USD',
    value: 29.99,
    items: [{
      item_id: 'SKU_001',
      item_name: 'Classic T-Shirt',
      item_brand: 'Brand X',
      item_category: 'Apparel',
      item_variant: 'Blue / Large',
      price: 29.99,
      quantity: 1
    }]
  }
});

Checkout Process

// Begin checkout
dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'begin_checkout',
  ecommerce: {
    currency: 'USD',
    value: 59.98,
    coupon: 'SAVE10',
    items: [
      // All cart items
    ]
  }
});

// Add shipping info
dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'add_shipping_info',
  ecommerce: {
    currency: 'USD',
    value: 59.98,
    shipping_tier: 'Express',
    items: [/* cart items */]
  }
});

// Add payment info
dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'add_payment_info',
  ecommerce: {
    currency: 'USD',
    value: 59.98,
    payment_type: 'Credit Card',
    items: [/* cart items */]
  }
});

Purchase

// Order completion
dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'purchase',
  ecommerce: {
    transaction_id: 'T_12345',
    value: 65.97,
    tax: 5.99,
    shipping: 9.99,
    currency: 'USD',
    coupon: 'SAVE10',
    items: [
      {
        item_id: 'SKU_001',
        item_name: 'Classic T-Shirt',
        item_brand: 'Brand X',
        item_category: 'Apparel',
        item_variant: 'Blue / Large',
        price: 29.99,
        quantity: 2,
        coupon: 'SAVE10',
        discount: 3.00
      }
    ]
  }
});

Refund

// Full refund
dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'refund',
  ecommerce: {
    transaction_id: 'T_12345',
    value: 65.97,
    currency: 'USD'
    // No items = full refund
  }
});

// Partial refund
dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'refund',
  ecommerce: {
    transaction_id: 'T_12345',
    value: 29.99,
    currency: 'USD',
    items: [{
      item_id: 'SKU_001',
      quantity: 1
    }]
  }
});

GTM Configuration

E-commerce Variable

  1. VariablesNewData Layer Variable
  2. Name: DLV - Ecommerce
  3. Data Layer Variable Name: ecommerce
  4. Data Layer Version: Version 2

GA4 E-commerce Tags

For each event, create a GA4 Event tag:

| Event | Tag Name | Trigger | |-------|----------|---------| | view_item_list | GA4 - View Item List | Custom Event: view_item_list | | select_item | GA4 - Select Item | Custom Event: select_item | | view_item | GA4 - View Item | Custom Event: view_item | | add_to_cart | GA4 - Add to Cart | Custom Event: add_to_cart | | begin_checkout | GA4 - Begin Checkout | Custom Event: begin_checkout | | purchase | GA4 - Purchase | Custom Event: purchase |

Important: Enable "Send Ecommerce Data" in each tag.

Product Performance Metrics

Key Metrics

| Metric | Calculation | Use | |--------|-------------|-----| | Product Views | count(view_item) | Interest | | Add-to-Cart Rate | add_to_cart / view_item | Intent | | Cart-to-Purchase | purchase / begin_checkout | Friction | | Revenue per View | revenue / view_item | Efficiency | | Product Revenue | sum(item revenue) | Performance |

BigQuery Analysis

-- Product performance analysis
WITH product_metrics AS (
  SELECT
    items.item_id,
    items.item_name,
    COUNTIF(event_name = 'view_item') as views,
    COUNTIF(event_name = 'add_to_cart') as adds,
    COUNTIF(event_name = 'purchase') as purchases,
    SUM(CASE WHEN event_name = 'purchase' THEN items.price * items.quantity END) as revenue
  FROM `project.analytics.events_*`,
  UNNEST(items) as items
  WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
    AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
  GROUP BY 1, 2
)
SELECT
  item_id,
  item_name,
  views,
  adds,
  purchases,
  revenue,
  SAFE_DIVIDE(adds, views) as add_to_cart_rate,
  SAFE_DIVIDE(purchases, adds) as purchase_rate,
  SAFE_DIVIDE(revenue, views) as revenue_per_view
FROM product_metrics
WHERE views > 100  -- Minimum sample
ORDER BY revenue DESC
LIMIT 50

Category Performance

-- Category performance
SELECT
  items.item_category as category,
  COUNT(DISTINCT user_pseudo_id) as shoppers,
  COUNTIF(event_name = 'purchase') as transactions,
  SUM(CASE WHEN event_name = 'purchase' THEN items.price * items.quantity END) as revenue,
  SAFE_DIVIDE(
    COUNTIF(event_name = 'purchase'),
    COUNTIF(event_name = 'view_item')
  ) as conversion_rate
FROM `project.analytics.events_*`,
UNNEST(items) as items
GROUP BY 1
ORDER BY revenue DESC

Shopping Behavior Analysis

Funnel Analysis

-- Shopping funnel by day
SELECT
  event_date,
  COUNTIF(event_name = 'view_item_list') as product_list_views,
  COUNTIF(event_name = 'view_item') as product_views,
  COUNTIF(event_name = 'add_to_cart') as add_to_carts,
  COUNTIF(event_name = 'begin_checkout') as checkouts,
  COUNTIF(event_name = 'purchase') as purchases
FROM `project.analytics.events_*`
WHERE event_name IN ('view_item_list', 'view_item', 'add_to_cart', 'begin_checkout', 'purchase')
GROUP BY 1
ORDER BY 1 DESC

Cart Abandonment

-- Cart abandonment analysis
WITH cart_sessions AS (
  SELECT
    user_pseudo_id,
    (SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') as session_id,
    MAX(CASE WHEN event_name = 'add_to_cart' THEN 1 ELSE 0 END) as added_to_cart,
    MAX(CASE WHEN event_name = 'purchase' THEN 1 ELSE 0 END) as purchased
  FROM `project.analytics.events_*`
  GROUP BY 1, 2
)
SELECT
  COUNT(*) as total_cart_sessions,
  SUM(purchased) as completed_purchases,
  SUM(added_to_cart) - SUM(purchased) as abandoned_carts,
  SAFE_DIVIDE(SUM(purchased), SUM(added_to_cart)) as cart_conversion_rate,
  SAFE_DIVIDE(SUM(added_to_cart) - SUM(purchased), SUM(added_to_cart)) as abandonment_rate
FROM cart_sessions
WHERE added_to_cart = 1

Platform-Specific Implementation

Shopify

// Shopify data layer integration
// Add to theme.liquid before </head>

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

  {% if template contains 'product' %}
  dataLayer.push({
    event: 'view_item',
    ecommerce: {
      currency: '{{ shop.currency }}',
      value: {{ product.price | divided_by: 100.0 }},
      items: [{
        item_id: '{{ product.variants.first.sku | default: product.id }}',
        item_name: '{{ product.title | escape }}',
        item_brand: '{{ product.vendor | escape }}',
        item_category: '{{ product.type | escape }}',
        price: {{ product.price | divided_by: 100.0 }}
      }]
    }
  });
  {% endif %}
</script>

WooCommerce

// WooCommerce: Add to functions.php or custom plugin
add_action('wp_footer', 'custom_ga4_ecommerce');
function custom_ga4_ecommerce() {
    if (is_product()) {
        global $product;
        ?>
        <script>
        dataLayer.push({
            event: 'view_item',
            ecommerce: {
                currency: '<?php echo get_woocommerce_currency(); ?>',
                value: <?php echo $product->get_price(); ?>,
                items: [{
                    item_id: '<?php echo $product->get_sku(); ?>',
                    item_name: '<?php echo esc_js($product->get_name()); ?>',
                    price: <?php echo $product->get_price(); ?>
                }]
            }
        });
        </script>
        <?php
    }
}

Customer Lifetime Value

LTV Tracking

// Include customer data with purchase
dataLayer.push({
  event: 'purchase',
  ecommerce: {
    transaction_id: 'T_12345',
    value: 99.99,
    // ... standard fields
  },
  customer_data: {
    customer_id: 'cust_abc',
    is_new_customer: false,
    order_count: 5,
    lifetime_value: 450.00,
    customer_tier: 'gold',
    days_since_first_order: 365
  }
});

LTV Analysis in BigQuery

-- Customer LTV by acquisition source
WITH customer_purchases AS (
  SELECT
    user_id,
    MIN(event_date) as first_purchase_date,
    (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') as acquisition_source,
    SUM(ecommerce.purchase_revenue) as total_revenue,
    COUNT(*) as order_count
  FROM `project.analytics.events_*`
  WHERE event_name = 'purchase'
    AND user_id IS NOT NULL
  GROUP BY 1
)
SELECT
  acquisition_source,
  COUNT(DISTINCT user_id) as customers,
  AVG(total_revenue) as avg_ltv,
  AVG(order_count) as avg_orders,
  SUM(total_revenue) as total_revenue
FROM customer_purchases
GROUP BY 1
ORDER BY avg_ltv DESC

Verification Checklist

E-commerce Testing

  • [ ] view_item_list fires on category pages
  • [ ] view_item fires on product pages
  • [ ] add_to_cart fires when adding items
  • [ ] Cart quantity/variant captured correctly
  • [ ] begin_checkout fires at checkout start
  • [ ] purchase fires exactly once per order
  • [ ] Transaction ID is unique
  • [ ] Revenue matches actual order value
  • [ ] All items included in purchase event
  • [ ] Refunds tracked with correct transaction_id

Data Quality Checks

  • [ ] No duplicate transactions
  • [ ] Item prices match reality
  • [ ] Currency code correct
  • [ ] No test transactions in production
  • [ ] PII not included in item names

Previous: B2B Lead Gen Related: Conversion Tracking