Wix Stores + Google Analytics 4 Ecommerce Tracking | Blue Frog Docs

Wix Stores + Google Analytics 4 Ecommerce Tracking

Complete guide to implementing GA4 ecommerce tracking for Wix Stores with enhanced ecommerce events.

Wix Stores + Google Analytics 4 Ecommerce Tracking

Implementing comprehensive ecommerce tracking on Wix Stores requires understanding the platform's limitations and using Velo to bridge gaps in the native GA4 integration.

What's Tracked Automatically

With Wix Marketing Integration + GA4

The native Wix GA4 integration provides basic ecommerce tracking:

Event Automatically Tracked? Data Included
view_item_list ✅ Yes Product list views (collections, search results)
view_item ✅ Yes Product detail page views
add_to_cart ✅ Yes Basic product info (name, ID, price)
begin_checkout ✅ Yes Cart contents
purchase ✅ Yes Transaction ID, revenue, products
view_cart ❌ No Not tracked by default
remove_from_cart ❌ No Not tracked by default
add_payment_info ❌ No Not tracked by default
add_shipping_info ❌ No Not tracked by default
Product impressions ⚠️ Partial Limited to first page load

Limitations of Native Integration

  1. No custom parameters (e.g., product variants, categories, brands)
  2. Missing checkout funnel events (shipping, payment info)
  3. No cart removal tracking
  4. Limited product impression tracking on dynamic content
  5. Cannot track cross-sells or upsells separately
  6. No refund tracking through GA4

For full ecommerce tracking, you need Velo implementation.

Prerequisites for Advanced Tracking

  • Wix Business or eCommerce plan (required for Wix Stores)
  • Wix Studio or Velo enabled (for custom tracking code)
  • GA4 installed via custom code (not Marketing Integration)
  • JavaScript/Velo knowledge

Complete Ecommerce Implementation with Velo

1. Product Impressions (Category/Collection Pages)

Track when products appear in lists:

// File: Product List Page Code
import wixStoresFrontend from 'wix-stores-frontend';
import wixLocation from 'wix-location';

$w.onReady(async function () {
  try {
    // Get current collection/category
    const collection = await wixStoresFrontend.category.getCategory();

    // Get products in current view
    const products = await wixStoresFrontend.products.getProducts();

    if (window.gtag && products.items && products.items.length > 0) {
      gtag('event', 'view_item_list', {
        item_list_id: collection?.id || 'all_products',
        item_list_name: collection?.name || 'All Products',
        items: products.items.map((product, index) => ({
          item_id: product.sku || product.id,
          item_name: product.name,
          item_category: collection?.name,
          price: product.price,
          currency: product.currency || 'USD',
          index: index,
          item_brand: product.brand || 'N/A',
          item_variant: product.ribbon || undefined
        }))
      });
    }
  } catch (error) {
    console.error('Product impression tracking error:', error);
  }
});

2. Product Click Tracking

Track clicks on products in lists:

// When user clicks a product in a repeater/gallery
$w('#productRepeater').onItemClicked((event) => {
  const product = event.item;

  if (window.gtag) {
    gtag('event', 'select_item', {
      item_list_id: 'product_gallery',
      item_list_name: 'Product Gallery',
      items: [{
        item_id: product.sku || product._id,
        item_name: product.name,
        price: product.price,
        currency: product.currency || 'USD',
        item_category: product.productType
      }]
    });
  }
});

3. Product Detail View (Enhanced)

Track product page views with complete data:

// File: Product Page Code
import wixStoresFrontend from 'wix-stores-frontend';

$w.onReady(async function () {
  try {
    const product = await wixStoresFrontend.product.getProduct();

    if (window.gtag && product) {
      // Get selected variant if applicable
      const selectedOptions = await wixStoresFrontend.product.getOptionsSelections();

      gtag('event', 'view_item', {
        currency: product.currency || 'USD',
        value: product.price,
        items: [{
          item_id: product.sku || product.id,
          item_name: product.name,
          item_category: product.productType,
          item_brand: product.brand || 'N/A',
          price: product.price,
          quantity: 1,
          item_variant: selectedOptions.map(opt => opt.selection).join(' / ') || undefined,
          // Custom dimensions (optional)
          in_stock: product.inStock,
          discount: product.discountedPrice ? (product.price - product.discountedPrice) : 0
        }]
      });
    }
  } catch (error) {
    console.error('Product view tracking error:', error);
  }
});

4. Add to Cart (Enhanced)

Track add to cart with variant and quantity:

import wixStoresFrontend from 'wix-stores-frontend';

$w.onReady(function () {
  $w('#addToCartButton').onClick(async () => {
    try {
      const product = await wixStoresFrontend.product.getProduct();
      const quantity = $w('#quantityInput').value || 1;
      const selectedOptions = await wixStoresFrontend.product.getOptionsSelections();

      if (window.gtag) {
        gtag('event', 'add_to_cart', {
          currency: product.currency || 'USD',
          value: product.price * quantity,
          items: [{
            item_id: product.sku || product.id,
            item_name: product.name,
            item_category: product.productType,
            price: product.price,
            quantity: quantity,
            item_variant: selectedOptions.map(opt => opt.selection).join(' / ') || undefined
          }]
        });
      }
    } catch (error) {
      console.error('Add to cart tracking error:', error);
    }
  });
});

5. Remove from Cart

Track when items are removed from cart:

// File: Cart Page Code
import wixStoresFrontend from 'wix-stores-frontend';

$w.onReady(function () {
  // Listen for cart changes
  wixStoresFrontend.cart.onChange(async (cartData) => {
    // Track removals (compare previous state)
    // This requires storing previous cart state

    if (window.cartRemovals && window.gtag) {
      window.cartRemovals.forEach(removedItem => {
        gtag('event', 'remove_from_cart', {
          currency: removedItem.currency || 'USD',
          value: removedItem.price * removedItem.quantity,
          items: [{
            item_id: removedItem.sku || removedItem.id,
            item_name: removedItem.name,
            price: removedItem.price,
            quantity: removedItem.quantity
          }]
        });
      });

      window.cartRemovals = [];
    }
  });
});

6. View Cart

Track when users view their cart:

// File: Cart Page Code
import wixStoresFrontend from 'wix-stores-frontend';

$w.onReady(async function () {
  try {
    const cart = await wixStoresFrontend.cart.getCurrentCart();

    if (window.gtag && cart && cart.lineItems) {
      gtag('event', 'view_cart', {
        currency: cart.currency || 'USD',
        value: cart.subtotal,
        items: cart.lineItems.map(item => ({
          item_id: item.sku || item.productId,
          item_name: item.name,
          price: item.price,
          quantity: item.quantity,
          item_variant: item.options?.map(opt => opt.selection).join(' / ') || undefined
        }))
      });
    }
  } catch (error) {
    console.error('View cart tracking error:', error);
  }
});

7. Begin Checkout

Track checkout initiation:

// File: Cart or Checkout Page
import wixStoresFrontend from 'wix-stores-frontend';

$w('#checkoutButton').onClick(async () => {
  try {
    const cart = await wixStoresFrontend.cart.getCurrentCart();

    if (window.gtag && cart) {
      gtag('event', 'begin_checkout', {
        currency: cart.currency || 'USD',
        value: cart.subtotal,
        coupon: cart.appliedCoupon?.code || undefined,
        items: cart.lineItems.map(item => ({
          item_id: item.sku || item.productId,
          item_name: item.name,
          price: item.price,
          quantity: item.quantity
        }))
      });
    }
  } catch (error) {
    console.error('Begin checkout tracking error:', error);
  }
});

8. Add Shipping Info

Track shipping information submission:

// File: Checkout Page (after shipping form)
$w('#shippingForm').onSubmit(async () => {
  try {
    const cart = await wixStoresFrontend.cart.getCurrentCart();
    const shippingMethod = $w('#shippingMethodDropdown').value;

    if (window.gtag && cart) {
      gtag('event', 'add_shipping_info', {
        currency: cart.currency || 'USD',
        value: cart.subtotal,
        shipping_tier: shippingMethod,
        items: cart.lineItems.map(item => ({
          item_id: item.sku || item.productId,
          item_name: item.name,
          price: item.price,
          quantity: item.quantity
        }))
      });
    }
  } catch (error) {
    console.error('Shipping info tracking error:', error);
  }
});

9. Add Payment Info

Track payment method selection:

// File: Checkout Page (payment step)
$w('#paymentMethodSelector').onChange(async () => {
  try {
    const cart = await wixStoresFrontend.cart.getCurrentCart();
    const paymentType = $w('#paymentMethodSelector').value;

    if (window.gtag && cart) {
      gtag('event', 'add_payment_info', {
        currency: cart.currency || 'USD',
        value: cart.subtotal,
        payment_type: paymentType,
        items: cart.lineItems.map(item => ({
          item_id: item.sku || item.productId,
          item_name: item.name,
          price: item.price,
          quantity: item.quantity
        }))
      });
    }
  } catch (error) {
    console.error('Payment info tracking error:', error);
  }
});

10. Purchase Event

Track completed transactions:

// File: Thank You Page Code
import wixLocation from 'wix-location';
import { orders } from 'wix-stores-backend';

$w.onReady(async function () {
  try {
    // Get order ID from URL query parameter
    const orderId = wixLocation.query.orderId;

    if (!orderId) return;

    // Fetch order details from backend
    // Note: This requires backend code (see below)
    const order = await getOrderDetails(orderId);

    if (window.gtag && order) {
      gtag('event', 'purchase', {
        transaction_id: order.number || order._id,
        value: order.totals.total,
        tax: order.totals.tax,
        shipping: order.totals.shipping,
        currency: order.currency || 'USD',
        coupon: order.appliedCoupon?.code || undefined,
        items: order.lineItems.map((item, index) => ({
          item_id: item.sku || item.productId,
          item_name: item.name,
          price: item.price,
          quantity: item.quantity,
          index: index,
          item_category: item.productType,
          item_variant: item.options?.map(opt => opt.selection).join(' / ') || undefined
        }))
      });

      // Store order ID to prevent duplicate tracking
      sessionStorage.setItem('tracked_order_' + orderId, 'true');
    }
  } catch (error) {
    console.error('Purchase tracking error:', error);
  }
});

// Helper function to call backend
async function getOrderDetails(orderId) {
  // Import wix-fetch for HTTP calls or use backend functions
  const { fetch } = await import('wix-fetch');

  const response = await fetch(`/api/getOrder?orderId=${orderId}`);
  return response.json();
}

Backend Code (for order retrieval):

// File: backend/http-functions.js
import { ok, notFound } from 'wix-http-functions';
import { orders } from 'wix-stores-backend';

export async function get_getOrder(request) {
  const orderId = request.query.orderId;

  if (!orderId) {
    return notFound();
  }

  try {
    const order = await orders.getOrder(orderId);
    return ok({
      body: JSON.stringify(order)
    });
  } catch (error) {
    return notFound();
  }
}

11. Refund Tracking (Backend)

GA4 refund events must be sent from backend to prevent fraud:

// File: backend/events.js
import { events } from 'wix-events-backend';

// Listen for order refunds
events.onOrderRefunded(async (event) => {
  const order = event.entity;

  // Send refund event to GA4 via Measurement Protocol
  await sendRefundToGA4({
    transaction_id: order.number || order._id,
    value: order.totals.total,
    currency: order.currency || 'USD'
  });
});

async function sendRefundToGA4(refundData) {
  const measurementId = 'G-XXXXXXXXXX';
  const apiSecret = 'YOUR_API_SECRET'; // From GA4 Admin → Data Streams

  const endpoint = `https://www.google-analytics.com/mp/collect?measurement_id=${measurementId}&api_secret=${apiSecret}`;

  const payload = {
    client_id: 'server', // Or retrieve from order metadata
    events: [{
      name: 'refund',
      params: refundData
    }]
  };

  const { fetch } = await import('wix-fetch');
  await fetch(endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload)
  });
}

Product Data Enrichment

Adding Product Categories

Wix Stores doesn't have built-in category fields. Use product types or ribbons as proxies:

const product = await wixStoresFrontend.product.getProduct();

const category = product.productType || 'Uncategorized';
const subcategory = product.ribbon || undefined;

Adding Product Brands

Store brand information in custom text fields or product descriptions, then parse:

// If brand is in a custom field
const brand = product.customTextFields?.find(field => field.title === 'Brand')?.value || 'N/A';

Enhanced Ecommerce Reporting Checklist

Verify these events are firing correctly:

  • view_item_list - Product category/collection pages
  • select_item - Product clicks from lists
  • view_item - Product detail pages
  • add_to_cart - Add to cart button
  • remove_from_cart - Remove from cart
  • view_cart - Cart page view
  • begin_checkout - Checkout initiation
  • add_shipping_info - Shipping selection
  • add_payment_info - Payment method selection
  • purchase - Thank you page
  • refund - Backend refund processing

Testing Ecommerce Tracking

1. Use GA4 DebugView

Enable debug mode and perform test transactions:

gtag('config', 'G-XXXXXXXXXX', {
  'debug_mode': true
});

Check AdminDebugView in GA4.

2. Test Purchase with Wix Payments Test Mode

  1. Enable Test Mode in Wix Payments settings
  2. Use test credit card numbers
  3. Complete full checkout flow
  4. Verify purchase event in GA4 Realtime

Test card: 4242 4242 4242 4242 (any future expiry, any CVV)

3. Verify in Ecommerce Reports

After 24-48 hours, check:

  • ReportsMonetizationEcommerce purchases
  • ReportsMonetizationPurchase journey

Common Wix Ecommerce Tracking Issues

Issue Cause Solution
Purchase event not firing Order ID not passed to thank you page Ensure Wix redirects to thank you page with ?orderId= parameter
Duplicate purchase events Page refresh on thank you page Implement deduplication with sessionStorage (see code above)
Missing product variants Variant info not captured Use product.getOptionsSelections() to get selected variants
Currency inconsistency Hardcoded currency Always use product.currency or store default
Items array empty Products not properly mapped Verify product object structure with console.log

Wix Stores Limitations

  1. No server-side tracking - All events are client-side (blockable by ad blockers)
  2. No direct checkout API - Cannot track checkout steps without custom form handling
  3. Limited order webhook data - Backend webhooks don't include all product metadata
  4. No native refund events - Requires Measurement Protocol implementation
  5. Multi-currency challenges - Need manual currency conversion for reporting

Revenue Validation

Compare GA4 revenue with Wix:

  1. Wix DashboardReportsSales
  2. GA4ReportsMonetizationEcommerce purchases

Common discrepancies:

  • Ad blocker-prevented tracking
  • Duplicate transaction tracking
  • Refunds not properly tracked
  • Multi-currency conversion differences

Solution: Accept 5-10% variance as normal; investigate if higher.

Next Steps

Additional Resources

// SYS.FOOTER