GA 4 E-commerce Tracking Masterclass

Last week, we fixed the Critical 15 items that break GA4 for everyone.

This week? We're going deep on e-commerce tracking.

Here's why this matters:

When attribution is wrong by 30%, every decision you make is wrong:

  • You kill profitable campaigns

  • You scale unprofitable campaigns

  • You can't calculate true ROAS

  • Smart Bidding optimizes for the wrong conversions

Today, I'm showing you exactly how to track e-commerce correctly so you never lose another dollar.

🎯 What You’ll Learn Today

By the end of this post, you’ll know:

  1. The complete e-commerce event flow (8 events from browse → purchase)

  2. How to implement each event with code examples

  3. Common mistakes that break revenue tracking

  4. How to validate your tracking is working

  5. Advanced tracking for cart abandonment and product performance

🛒 The Complete E-commerce Event Flow

Here’s the customer journey and which events to fire:

Product Discovery:
1. view_item_list (Category/Collection page)
2. select_item (Click on product)
3. view_item (Product detail page)

Shopping Cart:
4. add_to_cart (Add to cart button)
5. view_cart (Cart page)

Checkout Process:
6. begin_checkout (Start checkout)
7. add_payment_info (Payment method selected)
8. add_shipping_info (Shipping info entered)

Purchase:
9. purchase (Order completed) 🎯

Pro Tip: Implement in this order. Get purchase working first, then work backward.

💰 Event #1: Purchase (Start Here)

This is THE most important event. Get this wrong, and nothing else matters.

Implementation

// Fire on order confirmation page ONLYgtag('event', 'purchase', {
  // REQUIRED FIELDS  'transaction_id': 'ORD_2025_12345',  // Must be unique!  'value': 84.97,                       // Total revenue  'currency': 'USD',                    // ISO 4217 code  // RECOMMENDED FIELDS  'tax': 5.00,                         // Tax amount  'shipping': 9.99,                    // Shipping cost  'coupon': 'SAVE20',                  // Coupon code used  'affiliation': 'Online Store',       // Store name  // ITEMS ARRAY (all products in order)  'items': [
    {
      'item_id': 'SKU_12345',           // Product SKU      'item_name': 'Wireless Mouse',    // Product name      'item_category': 'Electronics',   // Primary category      'item_category2': 'Accessories',  // Secondary category      'item_brand': 'Logitech',        // Brand name      'price': 29.99,                  // Item price      'quantity': 2,                   // Quantity purchased      'item_variant': 'Black'          // Color/size/variant    },    {
      'item_id': 'SKU_67890',      'item_name': 'USB-C Cable',      'item_category': 'Electronics',      'item_category2': 'Cables',      'item_brand': 'Anker',      'price': 14.99,      'quantity': 1    }
  ]
});

Critical Rules for Purchase Event

1. Transaction ID MUST Be Unique

// ❌ BAD: Static transaction ID'transaction_id': 'ORDER_12345'// ❌ BAD: Only timestamp (not unique across users)'transaction_id': Date.now().toString()
// ✅ GOOD: Use your database order ID'transaction_id': orderData.id  // 'ORD_2025_12345'// ✅ GOOD: Generate unique ID'transaction_id': 'ORD_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9)

Why This Matters: Duplicate transaction IDs cause GA4 to:

  • Deduplicate the revenue (losing transactions)

  • Or count duplicates (inflating revenue)

  • Break your ROAS calculations

2. Fire Purchase Event ONLY ONCE

// Prevent duplicate firing on page refreshvar purchaseTracked = sessionStorage.getItem('purchase_' + transactionId);if (!purchaseTracked) {
  gtag('event', 'purchase', { /* data */ });  sessionStorage.setItem('purchase_' + transactionId, 'true');}

3. Value = Total Revenue (Including Tax + Shipping)

// Calculate correctlyvar itemsTotal = 59.97;  // Sum of all item prices × quantitiesvar taxAmount = 5.00;var shippingCost = 9.99;var discount = 10.00;    // If coupon appliedvar totalValue = itemsTotal + taxAmount + shippingCost - discount;  // 64.96gtag('event', 'purchase', {
  'transaction_id': 'ORD_12345',  'value': 64.96,        // ✅ Total customer paid  'tax': 5.00,           // Separate for reporting  'shipping': 9.99,      // Separate for reporting  'items': [ /* ... */ ]
});

4. Currency Must Match Property Settings

// ✅ ALWAYS specify currencygtag('event', 'purchase', {
  'currency': 'USD',  // ISO 4217 code  'value': 99.99});// Common Currency Codes:// USD - US Dollar// EUR - Euro// GBP - British Pound// INR - Indian Rupee// AUD - Australian Dollar

🛍️ Event #2: Add to Cart

Fire when user clicks “Add to Cart” button.

document.getElementById('addToCartBtn').addEventListener('click', function() {
  var product = getProductData(); // Your function to get product info  gtag('event', 'add_to_cart', {
    'currency': 'USD',    'value': product.price * product.quantity,    'items': [
      {
        'item_id': product.sku,        'item_name': product.name,        'item_category': product.category,        'item_brand': product.brand,        'price': product.price,        'quantity': product.quantity,        'item_variant': product.variant  // e.g., "Large / Blue"      }
    ]
  });});

Pro Tip: Track add_to_cart vs purchase to calculate cart abandonment rate:

Cart Abandonment Rate = 1 - (Purchases ÷ Add to Carts)

Example:
- 1,000 add_to_cart events
- 250 purchase events
- Abandonment Rate = 1 - (250 ÷ 1,000) = 75%

🛒 Event #3: Begin Checkout

Fire when user clicks “Proceed to Checkout” or lands on checkout page.

// On checkout page loadwindow.addEventListener('load', function() {
  if (window.location.pathname === '/checkout') {
    var cartItems = getCartItems(); // Your function    var cartTotal = calculateCartTotal(); // Your function    gtag('event', 'begin_checkout', {
      'currency': 'USD',      'value': cartTotal,      'coupon': getCouponCode(),  // If applied      'items': cartItems
    });  }
});

What to Track:

  • Total cart value at checkout start

  • Number of items

  • Whether coupon is applied

Why It Matters:

  • Measure checkout abandonment

  • Identify friction in checkout flow

  • Remarket to users who started but didn’t complete

📦 Event #4: View Item (Product Detail Page)

Fire when user lands on a product page.

// On product page loadgtag('event', 'view_item', {
  'currency': 'USD',  'value': 29.99,  // Product price  'items': [
    {
      'item_id': 'SKU_12345',      'item_name': 'Wireless Mouse',      'item_category': 'Electronics',      'item_category2': 'Accessories',      'item_brand': 'Logitech',      'price': 29.99,      'quantity': 1,  // Default to 1 for view_item      'item_list_name': 'Related Products',  // Where they came from      'item_list_id': 'related_products'    }
  ]
});

Track This To:

  • See which products get the most views

  • Calculate view-to-purchase conversion rate

  • Identify products with high views but low conversions (pricing issue?)

📋 Event #5: View Item List (Category/Collection Page)

Fire when users see a list of products (category page, search results, homepage).

gtag('event', 'view_item_list', {
  'item_list_id': 'category_electronics',  'item_list_name': 'Electronics',  'items': [
    {
      'item_id': 'SKU_12345',      'item_name': 'Wireless Mouse',      'item_category': 'Electronics',      'item_brand': 'Logitech',      'price': 29.99,      'index': 1  // Position in list (important!)    },    {
      'item_id': 'SKU_67890',      'item_name': 'USB-C Cable',      'item_category': 'Electronics',      'item_brand': 'Anker',      'price': 14.99,      'index': 2    }
    // ... up to 200 items max  ]
});

Pro Tip: Use index to track which position in list gets most clicks. Usually, positions 1-3 get 80% of clicks.

🖱️ Event #6: Select Item

Fire when user clicks on a product from a list.

// On product card clickdocument.querySelectorAll('.product-card').forEach(function(card, index) {
  card.addEventListener('click', function() {
    var product = getProductDataFromCard(this);    gtag('event', 'select_item', {
      'item_list_id': 'category_electronics',      'item_list_name': 'Electronics',      'items': [
        {
          'item_id': product.sku,          'item_name': product.name,          'item_category': product.category,          'item_brand': product.brand,          'price': product.price,          'index': index + 1  // Position clicked        }
      ]
    });  });});

Track This To:

  • See which products get clicked most

  • Identify high-click, low-conversion products

  • Optimize product positioning

💳 Event #7: Add Payment Info

Fire when user selects a payment method.

// When payment method radio button changesdocument.querySelectorAll('input[name="payment-method"]').forEach(function(radio) {
  radio.addEventListener('change', function() {
    gtag('event', 'add_payment_info', {
      'currency': 'USD',      'value': calculateCartTotal(),      'coupon': getCouponCode(),      'payment_type': this.value,  // 'credit_card', 'paypal', 'apple_pay'      'items': getCartItems()
    });  });});

🚚 Event #8: Add Shipping Info

Fire when user enters shipping information.

// When shipping form is submitteddocument.getElementById('shippingForm').addEventListener('submit', function() {
  gtag('event', 'add_shipping_info', {
    'currency': 'USD',    'value': calculateCartTotal(),    'coupon': getCouponCode(),    'shipping_tier': getSelectedShippingMethod(),  // 'standard', 'express'    'items': getCartItems()
  });});

🗑️ Event #9: Remove from Cart

Fire when user removes item from cart.

function removeFromCart(item) {
  gtag('event', 'remove_from_cart', {
    'currency': 'USD',    'value': item.price * item.quantity,    'items': [
      {
        'item_id': item.sku,        'item_name': item.name,        'price': item.price,        'quantity': item.quantity      }
    ]
  });}

Why Track This:

  • Identify products often removed (quality issue?)

  • See if shipping cost causes removals

  • Track cart composition changes

🔄 Event #10: View Cart

Fire when user views their shopping cart.

// On cart page loadif (window.location.pathname === '/cart') {
  gtag('event', 'view_cart', {
    'currency': 'USD',    'value': calculateCartTotal(),    'items': getCartItems()
  });}

⚠️ Common E-commerce Tracking Mistakes

Mistake #1: Including Tax/Shipping in Item Prices

// 

WRONG: Tax and shipping in item price'items': [{ 'item_id': 'SKU_12345', 'price': 35.00 // Includes tax + shipping}] // CORRECT: Item price only, tax/shipping separate'value': 35.00, // Total including tax + shipping'tax': 3.00, // Separate'shipping': 2.00, // Separate'items': [{ 'item_id': 'SKU_12345', 'price': 30.00 // Item price only}]

Mistake #2: Not Tracking Refunds

// When order is refundedgtag('event', 'refund', {
  'transaction_id': 'ORD_2025_12345',  // Original transaction ID  'value': 84.97,                       // Refund amount  'currency': 'USD',  // Optional: Partial refund (specific items)  'items': [
    {
      'item_id': 'SKU_12345',      'item_name': 'Wireless Mouse',      'quantity': 1    }
  ]
});

Mistake #3: Firing Purchase on Cart Page

// ❌ WRONG: Purchase event on cart or checkout pageif (window.location.pathname === '/cart') {
  gtag('event', 'purchase', { /* ... */ }); // NO!}
// ✅ CORRECT: Purchase ONLY on confirmation pageif (window.location.pathname === '/order-confirmation') {
  gtag('event', 'purchase', { /* ... */ }); // YES!}

Mistake #4: Not Handling Quantity Correctly

// ❌ WRONG: Quantity not considered in value'value': 29.99,  // Price for 1 item'items': [{
  'price': 29.99,  'quantity': 3    // But ordered 3!}]
// ✅ CORRECT: Value includes quantity'value': 89.97,  // 29.99 × 3'items': [{
  'price': 29.99,  'quantity': 3}]

Validating Your E-commerce Tracking

Step 1: Test Purchase in DebugView

1. Enable debug mode:
   gtag('config', 'G-XXXXXXXXXX', {'debug_mode': true});

2. Make a test purchase

3. Check DebugView (Admin → DebugView):
   - Look for 'purchase' event
   - Verify transaction_id is present and unique
   - Check all parameters are captured
   - Confirm items array is populated correctly

Step 2: Verify in Real-Time Report

1. Reports → Real-time
2. Make a test purchase
3. Event should appear within 60 seconds
4. Check event count = 1 (not duplicate)

Step 3: Revenue Reconciliation

Do this monthly:

GA4 Revenue Check:
1. Reports → Monetization → Ecommerce purchases
2. Date range: Last month
3. Note total revenue

Payment Processor Check:
1. Stripe/PayPal/etc dashboard
2. Same date range
3. Note total revenue

Compare:
- Within 2-5% = Good (normal variance)
- >5% difference = Investigation needed

Common causes of mismatch:
- Refunds not tracked in GA4
- Test transactions not filtered
- Time zone differences
- Orders awaiting payment confirmation

🎯 Advanced E-commerce Tracking

Cart Abandonment Sequence

Track exactly when users abandon carts:

// Set cart sessionsessionStorage.setItem('cart_started', Date.now());// Track abandonment before user leaveswindow.addEventListener('beforeunload', function() {
  var cartStarted = sessionStorage.getItem('cart_started');  var purchased = sessionStorage.getItem('purchase_completed');  if (cartStarted && !purchased) {
    gtag('event', 'cart_abandoned', {
      'cart_value': calculateCartTotal(),      'items_count': getCartItems().length,      'time_in_cart': Date.now() - cartStarted
    });  }
});

Product Performance Analysis

Track which products convert best:

// Create custom metricfunction calculateProductConversionRate(productId) {
  // In GA4 Exploration:  // Metric = (purchase events / view_item events) × 100  // Filter: item_id = productId}
// Results tell you:// - High views + High purchases = Winner (promote more)// - High views + Low purchases = Pricing or quality issue// - Low views + High purchases = Hidden gem (improve visibility)// - Low views + Low purchases = Consider discontinuing

Revenue by Traffic Source

// Automatically captured with purchase event// View in GA4:// Reports → Monetization → Overview// Secondary dimension: Session source/medium// Shows which channels drive most revenue:// - google / cpc = $50,000// - facebook / cpc = $35,000// - organic = $25,000// - email = $15,000

📊 E-commerce Reports to Monitor Weekly

1. Ecommerce Purchases Report

Reports → Monetization → Ecommerce purchases

Track:
- Total revenue trend
- Average order value (AOV)
- Items per purchase
- Top products by revenue

2. Checkout Funnel

Explore → Funnel exploration

Steps:
1. view_item (100% - baseline)
2. add_to_cart (40% - 60% typical)
3. begin_checkout (25% - 35% typical)
4. purchase (15% - 25% typical)

Identify biggest drop-off point and fix it.

3. Product Performance

Reports → Monetization → Ecommerce purchases
Add: item_name dimension

Metrics to watch:
- Total revenue per product
- Quantity sold
- Average price
- Revenue contribution %

🛠️ Implementation Checklist

Phase 1: Core Events (Week 1)

  • [ ] Purchase event implemented

  • [ ] Transaction IDs are unique

  • [ ] Purchase fires only once

  • [ ] Revenue reconciliation within 5%

  • [ ] All required parameters included

Phase 2: Cart Events (Week 2)

  • [ ] add_to_cart tracking

  • [ ] remove_from_cart tracking

  • [ ] view_cart tracking

  • [ ] begin_checkout tracking

Phase 3: Discovery Events (Week 3)

  • [ ] view_item tracking

  • [ ] view_item_list tracking

  • [ ] select_item tracking

Phase 4: Advanced (Week 4)

  • [ ] add_payment_info tracking

  • [ ] add_shipping_info tracking

  • [ ] refund tracking

  • [ ] Cart abandonment tracking

  • [ ] Checkout funnel exploration created

📥 Download Week 2 Resources

E-commerce Tracking Checklist (CSV)

Week_2_Ecommerce_Checklist.csv

Week_2_Ecommerce_Checklist.csv

1.10 KBCSV File

🚀 Next Week Preview

Week 3: Privacy-First Analytics - GDPR, Consent Mode v2, and GA4

With new EU regulations (March 2024 deadline), you NEED to:

  • Implement Consent Mode v2 correctly

  • Avoid €20M GDPR fines

  • Keep Google Ads campaigns running in EU

Can’t wait? Reply with “PRIVACY” for early access.

About This Series:

This is Week 2 of our 4-week GA4 Audit Series:

  • Week 2: E-commerce Tracking (You are here)

  • Week 3: Privacy-First Analytics

  • Week 4: Advanced GA4 Features

The AI Driven Marketer Helping digital marketers leverage AI and automation to work smarter, not harder.