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
- Variables → New → Data Layer Variable
- Name:
DLV - Ecommerce - Data Layer Variable Name:
ecommerce - 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