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
- No custom parameters (e.g., product variants, categories, brands)
- Missing checkout funnel events (shipping, payment info)
- No cart removal tracking
- Limited product impression tracking on dynamic content
- Cannot track cross-sells or upsells separately
- 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 Admin → DebugView in GA4.
2. Test Purchase with Wix Payments Test Mode
- Enable Test Mode in Wix Payments settings
- Use test credit card numbers
- Complete full checkout flow
- Verify
purchaseevent in GA4 Realtime
Test card: 4242 4242 4242 4242 (any future expiry, any CVV)
3. Verify in Ecommerce Reports
After 24-48 hours, check:
- Reports → Monetization → Ecommerce purchases
- Reports → Monetization → Purchase 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
- No server-side tracking - All events are client-side (blockable by ad blockers)
- No direct checkout API - Cannot track checkout steps without custom form handling
- Limited order webhook data - Backend webhooks don't include all product metadata
- No native refund events - Requires Measurement Protocol implementation
- Multi-currency challenges - Need manual currency conversion for reporting
Revenue Validation
Compare GA4 revenue with Wix:
- Wix Dashboard → Reports → Sales
- GA4 → Reports → Monetization → Ecommerce 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.