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.