Shipping Information Tracking
What This Means
Shipping tracking issues occur when GA4 doesn't properly capture shipping methods, costs, delivery times, and shipping-related customer behavior. Without accurate shipping data, you can't optimize delivery options, measure free shipping promotions, or understand how shipping costs impact purchase decisions.
Key Shipping Events:
add_shipping_info- Shipping method selected during checkoutpurchase- Should include shipping cost and method- Custom events for shipping-related interactions
Required Parameters:
shipping- Shipping cost in purchase eventshipping_tier- Shipping method name (e.g., "Standard", "Express", "Free Shipping")
Impact Assessment
Business Impact
- Shipping Strategy Unknown: Can't measure which options customers prefer
- Free Shipping ROI: Don't know if free shipping promotions drive conversions
- Lost Optimization: Can't test shipping thresholds for maximum profit
- Carrier Performance: Can't compare delivery methods
- Geographic Insights Missing: Don't know shipping costs by region
Analytics Impact
- Incomplete Purchase Data: Revenue reports missing shipping breakdown
- Funnel Analysis Gaps: Can't identify shipping-related abandonment
- Attribution Issues: Can't credit free shipping for conversions
- Poor Segmentation: Can't analyze customers by preferred shipping method
Common Causes
Implementation Issues
add_shipping_infoevent not firing- Shipping cost missing from purchase event
shipping_tierparameter not included- Single-page checkouts skip shipping event
Technical Problems
- Third-party shipping calculators bypass tracking
- Real-time rates loaded after event fires
- Multiple shipping methods not differentiated
- Shipping costs calculated server-side but not tracked
Data Quality Issues
- Shipping costs include tax/fees inconsistently
- Free shipping not tracked with $0 value
- Shipping tier names not standardized
- International shipping not distinguished
How to Diagnose
Check for Shipping Events
// Monitor shipping-related events
const shippingEvents = ['add_shipping_info', 'purchase'];
const shippingLog = [];
window.dataLayer = window.dataLayer || [];
const originalPush = dataLayer.push;
dataLayer.push = function(...args) {
args.forEach(event => {
if (shippingEvents.includes(event.event)) {
const shipping = event.ecommerce?.shipping;
const shippingTier = event.ecommerce?.shipping_tier;
shippingLog.push({
event: event.event,
shipping_cost: shipping,
shipping_tier: shippingTier
});
console.log('🚚 Shipping Event:', event.event, {
cost: shipping,
tier: shippingTier,
hasShipping: typeof shipping !== 'undefined',
hasTier: !!shippingTier
});
// Validate required fields
if (event.event === 'add_shipping_info' && !shippingTier) {
console.error('⚠️ Missing shipping_tier in add_shipping_info event');
}
if (event.event === 'purchase' && typeof shipping === 'undefined') {
console.error('⚠️ Missing shipping cost in purchase event');
}
}
});
return originalPush.apply(this, args);
};
// Summary after checkout
setTimeout(() => {
console.log('Shipping Tracking Summary:');
console.table(shippingLog);
}, 30000);
Validate Shipping in Purchase Event
// Check if purchase includes shipping data
function validateShippingInPurchase() {
const purchases = dataLayer.filter(e => e.event === 'purchase');
const latestPurchase = purchases[purchases.length - 1];
if (!latestPurchase) {
console.log('No purchase events found yet');
return;
}
const ecom = latestPurchase.ecommerce;
console.log('Purchase Shipping Validation:');
console.log('- Shipping cost:', ecom?.shipping ?? '⚠️ MISSING');
console.log('- Shipping tier:', ecom?.shipping_tier ?? '⚠️ MISSING');
console.log('- Transaction total:', ecom?.value);
// Check if shipping is included in total
const itemsTotal = ecom?.items?.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
) || 0;
const tax = ecom?.tax || 0;
const shipping = ecom?.shipping || 0;
const calculatedTotal = itemsTotal + tax + shipping;
if (Math.abs(calculatedTotal - ecom.value) > 0.01) {
console.warn('⚠️ Total mismatch - shipping may be incorrectly calculated');
console.log(`Items: ${itemsTotal} + Tax: ${tax} + Shipping: ${shipping} = ${calculatedTotal}`);
console.log(`Reported total: ${ecom.value}`);
} else {
console.log('✓ Shipping correctly included in transaction total');
}
}
validateShippingInPurchase();
GA4 DebugView Checklist
- Shipping Selection:
add_shipping_infofires when method selected - Shipping Tier: Parameter includes method name
- Shipping Cost: Numeric value present
- Purchase Event: Includes both
shippingandshipping_tier - Free Shipping: Tracked with
shipping: 0
General Fixes
1. Track Shipping Selection Event
// Fire when customer selects shipping method
function trackAddShippingInfo(shippingMethod, cartData) {
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: 'add_shipping_info',
ecommerce: {
currency: 'USD',
value: cartData.total + shippingMethod.cost,
shipping: shippingMethod.cost,
shipping_tier: shippingMethod.name,
items: cartData.items.map(item => ({
item_id: item.id,
item_name: item.name,
price: item.price,
quantity: item.quantity
}))
},
// Also as top-level parameters for easier reporting
shipping_cost: shippingMethod.cost,
shipping_method: shippingMethod.name,
shipping_carrier: shippingMethod.carrier,
estimated_delivery: shippingMethod.estimatedDays
});
// Store for use in purchase event
sessionStorage.setItem('selected_shipping', JSON.stringify(shippingMethod));
}
// Attach to shipping method selection
document.querySelectorAll('input[name="shipping_method"]').forEach(radio => {
radio.addEventListener('change', (e) => {
const shippingMethod = {
id: e.target.value,
name: e.target.dataset.shippingName,
cost: parseFloat(e.target.dataset.shippingCost),
carrier: e.target.dataset.carrier,
estimatedDays: e.target.dataset.estimatedDays
};
const cartData = getCartData();
trackAddShippingInfo(shippingMethod, cartData);
});
});
2. Include Shipping in Purchase Event
// Track purchase with complete shipping information
function trackPurchase(orderData) {
const shippingMethod = JSON.parse(sessionStorage.getItem('selected_shipping')) || {};
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: orderData.id,
value: orderData.total, // Total including shipping
tax: orderData.tax,
shipping: orderData.shippingCost,
shipping_tier: shippingMethod.name || orderData.shippingMethod,
currency: 'USD',
items: orderData.items.map(item => ({
item_id: item.id,
item_name: item.name,
price: item.price,
quantity: item.quantity
}))
},
// Additional shipping details
shipping_method: shippingMethod.name || orderData.shippingMethod,
shipping_carrier: shippingMethod.carrier || orderData.carrier,
shipping_cost: orderData.shippingCost,
delivery_type: orderData.shippingCost === 0 ? 'free_shipping' : 'paid_shipping'
});
// Clear stored shipping data
sessionStorage.removeItem('selected_shipping');
}
// Call on order confirmation page
if (isOrderConfirmationPage()) {
const orderData = getOrderData();
trackPurchase(orderData);
}
3. Track Free Shipping Qualification
// Track when customer qualifies for free shipping
function checkFreeShippingThreshold(cartTotal, threshold = 50) {
const previousTotal = parseFloat(sessionStorage.getItem('previous_cart_total')) || 0;
const justQualified = previousTotal < threshold && cartTotal >= threshold;
const justLost = previousTotal >= threshold && cartTotal < threshold;
if (justQualified) {
dataLayer.push({
event: 'free_shipping_qualified',
cart_total: cartTotal,
threshold: threshold,
amount_to_qualify: 0
});
// Show notification
showNotification('🎉 You qualify for free shipping!');
} else if (justLost) {
dataLayer.push({
event: 'free_shipping_lost',
cart_total: cartTotal,
threshold: threshold,
amount_to_qualify: threshold - cartTotal
});
}
sessionStorage.setItem('previous_cart_total', cartTotal.toString());
// Return progress toward free shipping
return {
qualified: cartTotal >= threshold,
amountRemaining: Math.max(0, threshold - cartTotal),
percentage: Math.min(100, (cartTotal / threshold) * 100)
};
}
// Update on cart changes
window.addEventListener('cartUpdated', (e) => {
const cartTotal = e.detail.total;
const freeShippingStatus = checkFreeShippingThreshold(cartTotal, 50);
// Update UI
updateFreeShippingProgress(freeShippingStatus);
});
4. Track Shipping Cost Impact
// Track when shipping cost influences behavior
function trackShippingDecision(action, shippingCost, context) {
dataLayer.push({
event: 'shipping_decision',
decision_type: action, // 'selected', 'changed', 'abandoned'
shipping_cost: shippingCost,
context: context, // 'checkout', 'cart'
cart_value: getCartTotal(),
shipping_to_cart_ratio: shippingCost / getCartTotal()
});
}
// Track when expensive shipping might cause abandonment
document.querySelectorAll('input[name="shipping_method"]').forEach(radio => {
radio.addEventListener('change', (e) => {
const shippingCost = parseFloat(e.target.dataset.shippingCost);
const cartTotal = getCartTotal();
// Flag potentially problematic shipping costs
if (shippingCost > cartTotal * 0.3) { // Shipping > 30% of cart
dataLayer.push({
event: 'high_shipping_cost',
shipping_cost: shippingCost,
cart_total: cartTotal,
shipping_ratio: shippingCost / cartTotal
});
}
trackShippingDecision('selected', shippingCost, 'checkout');
});
});
// Track if user abandons after seeing shipping costs
let shippingCostsSeen = false;
window.addEventListener('beforeunload', () => {
if (shippingCostsSeen && !hasCompletedPurchase()) {
const selectedShipping = getSelectedShippingCost();
dataLayer.push({
event: 'shipping_abandonment',
shipping_cost: selectedShipping,
cart_total: getCartTotal(),
checkout_step: getCurrentCheckoutStep()
});
}
});
5. Standardize Shipping Tier Names
// Create consistent shipping tier naming
const SHIPPING_TIERS = {
free: {
name: 'Free Shipping',
code: 'FREE',
cost: 0,
estimatedDays: '5-7'
},
standard: {
name: 'Standard Shipping',
code: 'STANDARD',
cost: 5.99,
estimatedDays: '3-5'
},
express: {
name: 'Express Shipping',
code: 'EXPRESS',
cost: 14.99,
estimatedDays: '1-2'
},
overnight: {
name: 'Overnight Shipping',
code: 'OVERNIGHT',
cost: 24.99,
estimatedDays: '1'
},
international: {
name: 'International Shipping',
code: 'INTERNATIONAL',
cost: null, // Calculated
estimatedDays: '7-14'
}
};
// Get shipping tier by ID
function getShippingTier(tierId, calculatedCost = null) {
const tier = SHIPPING_TIERS[tierId];
return {
...tier,
cost: calculatedCost ?? tier.cost
};
}
// Usage
const selectedTier = getShippingTier('express');
trackAddShippingInfo(selectedTier, cartData);
6. Track Shipping Address Information
// Track shipping destination (anonymized)
function trackShippingAddress(addressData) {
// Never send PII - only aggregate data
const shippingInfo = {
country: addressData.country,
state: addressData.state,
postal_code_prefix: addressData.postalCode?.substring(0, 3), // First 3 digits only
is_international: addressData.country !== 'US',
is_rural: isRuralArea(addressData.postalCode),
is_po_box: addressData.address1?.toLowerCase().includes('po box')
};
dataLayer.push({
event: 'shipping_address_entered',
...shippingInfo
});
// Calculate and track shipping zones
const zone = getShippingZone(addressData);
dataLayer.push({
event: 'shipping_zone_determined',
shipping_zone: zone,
zone_type: zone.type, // 'local', 'regional', 'national', 'international'
estimated_transit_days: zone.transitDays
});
return shippingInfo;
}
// Shipping form submission
document.querySelector('#shipping-form')?.addEventListener('submit', (e) => {
const addressData = {
country: document.querySelector('[name="country"]').value,
state: document.querySelector('[name="state"]').value,
postalCode: document.querySelector('[name="postal_code"]').value,
address1: document.querySelector('[name="address1"]').value
};
trackShippingAddress(addressData);
});
7. Server-Side Shipping Event Tracking
// Node.js example - Track shipping from order processing
const express = require('express');
const router = express.Router();
// Calculate shipping rates
router.post('/api/shipping/calculate', async (req, res) => {
const { address, cartItems, cartTotal } = req.body;
const rates = await getShippingRates(address, cartItems);
// Track that shipping rates were requested
await sendToGA4({
client_id: req.body.clientId,
events: [{
name: 'shipping_rates_requested',
params: {
country: address.country,
state: address.state,
cart_total: cartTotal,
num_options: rates.length,
lowest_rate: Math.min(...rates.map(r => r.cost)),
highest_rate: Math.max(...rates.map(r => r.cost))
}
}]
});
res.json({ rates });
});
// Create shipping label
router.post('/api/shipping/create-label', async (req, res) => {
const { orderId, shippingMethod } = req.body;
const label = await createShippingLabel(orderId, shippingMethod);
// Track shipping label creation
await sendToGA4({
client_id: 'server',
events: [{
name: 'shipping_label_created',
params: {
transaction_id: orderId,
shipping_carrier: shippingMethod.carrier,
shipping_service: shippingMethod.service,
tracking_number: label.trackingNumber,
label_cost: label.cost
}
}]
});
res.json({ label });
});
// Track shipment status updates
router.post('/api/shipping/track', async (req, res) => {
const { trackingNumber } = req.body;
const tracking = await getTrackingInfo(trackingNumber);
// Track delivery status
await sendToGA4({
client_id: 'server',
events: [{
name: 'shipment_status_update',
params: {
tracking_number: trackingNumber,
status: tracking.status, // 'in_transit', 'out_for_delivery', 'delivered', 'exception'
location: tracking.currentLocation,
estimated_delivery: tracking.estimatedDelivery
}
}]
});
res.json({ tracking });
});
// Track successful delivery
router.post('/webhook/shipment-delivered', async (req, res) => {
const { orderId, trackingNumber, deliveredAt } = req.body;
const order = await getOrder(orderId);
await sendToGA4({
client_id: 'server',
events: [{
name: 'shipment_delivered',
params: {
transaction_id: orderId,
tracking_number: trackingNumber,
delivered_at: deliveredAt,
days_to_deliver: calculateDaysToDeliver(order.createdAt, deliveredAt),
on_time: isOnTime(order.estimatedDelivery, deliveredAt)
}
}]
});
res.sendStatus(200);
});
async function sendToGA4(data) {
const measurementId = process.env.GA4_MEASUREMENT_ID;
const apiSecret = process.env.GA4_API_SECRET;
await fetch(`https://www.google-analytics.com/mp/collect?measurement_id=${measurementId}&api_secret=${apiSecret}`, {
method: 'POST',
body: JSON.stringify(data)
});
}
module.exports = router;
Platform-Specific Guides
Shopify
<!-- Checkout - can only be edited with Shopify Plus -->
<!-- Use checkout.liquid or Shopify Scripts -->
<script>
// Track shipping method selection
document.addEventListener('DOMContentLoaded', function() {
// For standard Shopify checkout, use Shopify's checkout object
if (typeof Shopify !== 'undefined' && Shopify.Checkout) {
var shippingRate = Shopify.Checkout.shippingRate;
if (shippingRate) {
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: 'add_shipping_info',
ecommerce: {
currency: Shopify.Checkout.currency,
value: parseFloat(Shopify.Checkout.totalPrice),
shipping: parseFloat(shippingRate.price),
shipping_tier: shippingRate.name,
items: Shopify.Checkout.lineItems.map(function(item, index) {
return {
item_id: item.sku || item.variant_id,
item_name: item.title,
price: parseFloat(item.price),
quantity: item.quantity,
index: index
};
})
},
shipping_method: shippingRate.name,
shipping_carrier: shippingRate.source || 'Shopify'
});
}
}
});
// Store shipping for confirmation page
sessionStorage.setItem('shopify_shipping', JSON.stringify({
cost: parseFloat({{ checkout.shipping_price | money_without_currency | remove: ',' }}),
method: '{{ checkout.shipping_method.title | escape }}'
}));
</script>
<!-- Thank You page - Edit via Settings > Checkout > Order status page -->
<script>
// Track purchase with shipping info
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: '{{ order.order_number }}',
value: {{ order.total_price | money_without_currency | remove: ',' }},
tax: {{ order.tax_price | money_without_currency | remove: ',' }},
shipping: {{ order.shipping_price | money_without_currency | remove: ',' }},
shipping_tier: '{{ order.shipping_method.title | escape }}',
currency: '{{ order.currency }}',
items: [
{% for line_item in order.line_items %}
{
item_id: '{{ line_item.sku | default: line_item.product_id }}',
item_name: '{{ line_item.title | escape }}',
price: {{ line_item.price | money_without_currency | remove: ',' }},
quantity: {{ line_item.quantity }}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
},
shipping_method: '{{ order.shipping_method.title | escape }}',
shipping_cost: {{ order.shipping_price | money_without_currency | remove: ',' }},
delivery_type: {% if order.shipping_price == 0 %}'free_shipping'{% else %}'paid_shipping'{% endif %}
});
</script>
WooCommerce
// Add to functions.php or custom plugin
// Track shipping method selection
add_action('woocommerce_checkout_update_order_review', function($post_data) {
parse_str($post_data, $data);
if (isset($data['shipping_method']) && is_array($data['shipping_method'])) {
$shipping_method_id = array_shift($data['shipping_method']);
$shipping_methods = WC()->shipping()->get_shipping_methods();
foreach (WC()->shipping()->get_packages() as $package) {
foreach ($package['rates'] as $rate) {
if ($rate->id === $shipping_method_id) {
?>
<script>
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: 'add_shipping_info',
ecommerce: {
currency: '<?php echo get_woocommerce_currency(); ?>',
value: <?php echo WC()->cart->get_total(''); ?>,
shipping: <?php echo $rate->cost; ?>,
shipping_tier: '<?php echo esc_js($rate->label); ?>',
items: [
<?php foreach (WC()->cart->get_cart() as $item): ?>
{
item_id: '<?php echo $item['data']->get_sku() ?: $item['product_id']; ?>',
item_name: '<?php echo esc_js($item['data']->get_name()); ?>',
price: <?php echo $item['data']->get_price(); ?>,
quantity: <?php echo $item['quantity']; ?>
},
<?php endforeach; ?>
]
},
shipping_method: '<?php echo esc_js($rate->label); ?>',
shipping_carrier: '<?php echo esc_js($rate->method_id); ?>'
});
</script>
<?php
break;
}
}
}
}
});
// Track purchase with shipping
add_action('woocommerce_thankyou', function($order_id) {
$order = wc_get_order($order_id);
$shipping_methods = $order->get_shipping_methods();
$shipping_method = !empty($shipping_methods) ? reset($shipping_methods) : null;
?>
<script>
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: '<?php echo $order->get_order_number(); ?>',
value: <?php echo $order->get_total(); ?>,
tax: <?php echo $order->get_total_tax(); ?>,
shipping: <?php echo $order->get_shipping_total(); ?>,
shipping_tier: '<?php echo $shipping_method ? esc_js($shipping_method->get_name()) : ''; ?>',
currency: '<?php echo $order->get_currency(); ?>',
items: [
<?php foreach ($order->get_items() as $item): ?>
{
item_id: '<?php echo $item->get_product()->get_sku() ?: $item->get_product_id(); ?>',
item_name: '<?php echo esc_js($item->get_name()); ?>',
price: <?php echo $item->get_total() / $item->get_quantity(); ?>,
quantity: <?php echo $item->get_quantity(); ?>
},
<?php endforeach; ?>
]
},
shipping_method: '<?php echo $shipping_method ? esc_js($shipping_method->get_name()) : ''; ?>',
shipping_cost: <?php echo $order->get_shipping_total(); ?>,
delivery_type: <?php echo $order->get_shipping_total() == 0 ? "'free_shipping'" : "'paid_shipping'"; ?>
});
</script>
<?php
}, 10, 1);
// Track free shipping threshold
add_action('woocommerce_cart_updated', function() {
$cart_total = WC()->cart->get_subtotal();
$free_shipping_threshold = 50; // Configure this
if ($cart_total >= $free_shipping_threshold) {
?>
<script>
dataLayer.push({
event: 'free_shipping_qualified',
cart_total: <?php echo $cart_total; ?>,
threshold: <?php echo $free_shipping_threshold; ?>
});
</script>
<?php
}
});
BigCommerce
// Add to theme/assets/js/theme/global.js
import utils from '@bigcommerce/stencil-utils';
class ShippingTracking {
constructor() {
this.init();
}
init() {
this.trackShippingSelection();
}
trackShippingSelection() {
// Monitor shipping method changes
$(document).on('change', 'input[name="shippingProviderName"]', (event) => {
const selectedShipping = $(event.target);
const shippingCost = parseFloat(selectedShipping.data('shippingCost'));
const shippingName = selectedShipping.val();
utils.api.cart.getCart({}, (err, response) => {
if (response) {
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: 'add_shipping_info',
ecommerce: {
currency: response.currency.code,
value: response.cartAmount + shippingCost,
shipping: shippingCost,
shipping_tier: shippingName,
items: response.lineItems.physicalItems.map(item => ({
item_id: item.sku,
item_name: item.name,
price: item.salePrice,
quantity: item.quantity
}))
},
shipping_method: shippingName,
shipping_cost: shippingCost
});
// Store for order confirmation
sessionStorage.setItem('bc_shipping', JSON.stringify({
cost: shippingCost,
method: shippingName
}));
}
});
});
}
}
export default new ShippingTracking();
Magento
<!-- Add to Magento_Checkout/templates/success.phtml -->
<?php
$order = $block->getOrder();
$shippingMethod = $order->getShippingMethod();
$shippingDescription = $order->getShippingDescription();
?>
<script>
require(['jquery'], function($) {
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: '<?php echo $order->getIncrementId(); ?>',
value: <?php echo $order->getGrandTotal(); ?>,
tax: <?php echo $order->getTaxAmount(); ?>,
shipping: <?php echo $order->getShippingAmount(); ?>,
shipping_tier: '<?php echo esc_js($shippingDescription); ?>',
currency: '<?php echo $order->getOrderCurrencyCode(); ?>',
items: [
<?php foreach ($order->getAllVisibleItems() as $item): ?>
{
item_id: '<?php echo $item->getSku(); ?>',
item_name: '<?php echo esc_js($item->getName()); ?>',
price: <?php echo $item->getPrice(); ?>,
quantity: <?php echo $item->getQtyOrdered(); ?>
},
<?php endforeach; ?>
]
},
shipping_method: '<?php echo esc_js($shippingDescription); ?>',
shipping_cost: <?php echo $order->getShippingAmount(); ?>,
delivery_type: <?php echo $order->getShippingAmount() == 0 ? "'free_shipping'" : "'paid_shipping'"; ?>
});
});
</script>
Testing & Validation
Shipping Tracking Checklist
- Shipping Selection:
add_shipping_infofires when method selected - Shipping Tier: Includes descriptive method name
- Shipping Cost: Numeric cost value present
- Purchase Event: Includes
shippingandshipping_tier - Free Shipping: Tracked with
shipping: 0 - Value Calculation: Transaction total includes shipping
- Threshold Events: Free shipping qualification tracked
- Consistent Naming: Shipping tier names standardized
GA4 Reports to Check
Monetization → E-commerce purchases
- View "Shipping tier" dimension
- Check revenue by shipping method
Engagement → Events → add_shipping_info
- Verify event count
- Check shipping_tier parameter distribution
Custom Report: Shipping Analysis
- Compare conversion rates by shipping tier
- Analyze shipping cost vs. cart value ratio
Console Validation
// Test shipping tracking implementation
function validateShippingTracking() {
console.log('🚚 Shipping Tracking Validation\n');
// Check add_shipping_info events
const shippingInfoEvents = dataLayer.filter(e => e.event === 'add_shipping_info');
console.log(`add_shipping_info events: ${shippingInfoEvents.length}`);
if (shippingInfoEvents.length > 0) {
const latest = shippingInfoEvents[shippingInfoEvents.length - 1];
const ecom = latest.ecommerce;
console.log('\nLatest add_shipping_info:');
console.log('- Shipping cost:', ecom?.shipping ?? '⚠️ MISSING');
console.log('- Shipping tier:', ecom?.shipping_tier ?? '⚠️ MISSING');
console.log('- Total value:', ecom?.value);
}
// Check purchase events
const purchases = dataLayer.filter(e => e.event === 'purchase');
console.log(`\npurchase events: ${purchases.length}`);
if (purchases.length > 0) {
const latest = purchases[purchases.length - 1];
const ecom = latest.ecommerce;
console.log('\nLatest purchase:');
console.log('- Shipping cost:', ecom?.shipping ?? '⚠️ MISSING');
console.log('- Shipping tier:', ecom?.shipping_tier ?? '⚠️ MISSING');
console.log('- Transaction total:', ecom?.value);
console.log('- Tax:', ecom?.tax);
// Validate total calculation
const itemsTotal = ecom?.items?.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
) || 0;
const calculatedTotal = itemsTotal + (ecom?.tax || 0) + (ecom?.shipping || 0);
console.log('\nTotal validation:');
console.log('- Items total:', itemsTotal.toFixed(2));
console.log('- + Tax:', ecom?.tax?.toFixed(2));
console.log('- + Shipping:', ecom?.shipping?.toFixed(2));
console.log('- = Calculated:', calculatedTotal.toFixed(2));
console.log('- Reported:', ecom?.value?.toFixed(2));
if (Math.abs(calculatedTotal - ecom.value) < 0.01) {
console.log('✓ Totals match');
} else {
console.error('✗ Total mismatch!');
}
}
}
validateShippingTracking();