This guide covers how to implement GA4's enhanced ecommerce tracking on Shopware 6 to track the complete customer journey from product views to purchase.
Prerequisites: GA4 must be installed. See Install Google Analytics 4
GA4 Ecommerce Events Overview
GA4 tracks the following ecommerce events:
| Event | Trigger | Purpose |
|---|---|---|
view_item_list |
Category/search results | Track product impressions |
select_item |
Click on product | Track product clicks |
view_item |
Product detail page | Track product views |
add_to_cart |
Add to cart button | Track cart additions |
remove_from_cart |
Remove from cart | Track cart removals |
view_cart |
Cart page view | Track cart views |
begin_checkout |
Checkout start | Track checkout initiation |
add_shipping_info |
Shipping selected | Track checkout progress |
add_payment_info |
Payment selected | Track checkout progress |
purchase |
Order completed | Track transactions |
Implementation Methods
Method 1: Using Shopware Plugin
Many GA4 plugins for Shopware include built-in ecommerce tracking:
Install GA4 Plugin
- See GA4 Setup
- Choose a plugin with ecommerce support
Enable Ecommerce Tracking
- Go to plugin configuration
- Enable "Enhanced Ecommerce" option
- Configure product ID source (SKU, product number, or ID)
- Set currency settings
Test Events
- Navigate through your store
- Check GA4 DebugView for events
- Verify all product data is passed correctly
Method 2: Manual Implementation
For complete control, implement tracking manually in your theme.
Product List Views (view_item_list)
Track when users see product listings on category or search pages.
Category Page Tracking
{# In listing template: src/Resources/views/storefront/page/product-detail/index.html.twig #}
{% block page_product_listing %}
{{ parent() }}
<script>
(function() {
{% if page.listing.elements %}
const items = [
{% for product in page.listing.elements %}
{
item_id: '{{ product.productNumber }}',
item_name: '{{ product.translated.name|e('js') }}',
item_brand: '{{ product.manufacturer.translated.name|default('')|e('js') }}',
item_category: '{{ product.categories.first.translated.name|default('')|e('js') }}',
price: {{ product.calculatedPrice.unitPrice }},
currency: '{{ context.currency.isoCode }}',
index: {{ loop.index0 }}
}{% if not loop.last %},{% endif %}
{% endfor %}
];
if (typeof gtag !== 'undefined' && items.length > 0) {
gtag('event', 'view_item_list', {
item_list_id: '{{ page.category.id|default('search') }}',
item_list_name: '{{ page.category.translated.name|default('Search Results')|e('js') }}',
items: items
});
}
{% endif %}
})();
</script>
{% endblock %}
Product Clicks (select_item)
Track when users click on products in listings:
{% block component_product_box %}
{{ parent() }}
<script>
(function() {
const productBox = document.querySelector('[data-product-id="{{ product.id }}"]');
if (productBox) {
productBox.addEventListener('click', function() {
if (typeof gtag !== 'undefined') {
gtag('event', 'select_item', {
item_list_id: '{{ listingId|default('') }}',
item_list_name: '{{ listingName|default('')|e('js') }}',
items: [{
item_id: '{{ product.productNumber }}',
item_name: '{{ product.translated.name|e('js') }}',
item_brand: '{{ product.manufacturer.translated.name|default('')|e('js') }}',
item_category: '{{ product.categories.first.translated.name|default('')|e('js') }}',
price: {{ product.calculatedPrice.unitPrice }}
}]
});
}
});
}
})();
</script>
{% endblock %}
Product Detail Views (view_item)
Track product detail page views:
{# In product detail template #}
{% block page_product_detail %}
{{ parent() }}
<script>
(function() {
{% set product = page.product %}
if (typeof gtag !== 'undefined') {
gtag('event', 'view_item', {
currency: '{{ context.currency.isoCode }}',
value: {{ product.calculatedPrice.unitPrice }},
items: [{
item_id: '{{ product.productNumber }}',
item_name: '{{ product.translated.name|e('js') }}',
item_brand: '{{ product.manufacturer.translated.name|default('')|e('js') }}',
item_category: '{{ product.categories.first.translated.name|default('')|e('js') }}',
item_variant: '{{ product.variation|default('')|e('js') }}',
price: {{ product.calculatedPrice.unitPrice }},
quantity: 1
}]
});
}
})();
</script>
{% endblock %}
Add to Cart (add_to_cart)
Track when products are added to cart:
{% block page_product_detail_buy_form %}
{{ parent() }}
<script>
document.addEventListener('DOMContentLoaded', function() {
const buyForm = document.querySelector('.product-detail-buy');
if (buyForm) {
buyForm.addEventListener('submit', function(e) {
const quantity = parseInt(this.querySelector('[name="quantity"]')?.value || 1);
{% set product = page.product %}
if (typeof gtag !== 'undefined') {
gtag('event', 'add_to_cart', {
currency: '{{ context.currency.isoCode }}',
value: {{ product.calculatedPrice.unitPrice }} * quantity,
items: [{
item_id: '{{ product.productNumber }}',
item_name: '{{ product.translated.name|e('js') }}',
item_brand: '{{ product.manufacturer.translated.name|default('')|e('js') }}',
item_category: '{{ product.categories.first.translated.name|default('')|e('js') }}',
price: {{ product.calculatedPrice.unitPrice }},
quantity: quantity
}]
});
}
});
}
});
</script>
{% endblock %}
AJAX Add to Cart Tracking
For AJAX-based add to cart (like off-canvas cart):
// Listen for Shopware's cart update event
document.$emitter.subscribe('addToCart', function(event) {
const product = event.detail.product;
const quantity = event.detail.quantity || 1;
if (typeof gtag !== 'undefined') {
gtag('event', 'add_to_cart', {
currency: product.currency,
value: product.price * quantity,
items: [{
item_id: product.productNumber,
item_name: product.name,
price: product.price,
quantity: quantity
}]
});
}
});
Remove from Cart (remove_from_cart)
Track cart item removals:
{% block page_checkout_cart_item %}
{{ parent() }}
<script>
document.addEventListener('DOMContentLoaded', function() {
const removeButtons = document.querySelectorAll('[data-cart-item-remove]');
removeButtons.forEach(function(button) {
button.addEventListener('click', function() {
const lineItem = button.closest('[data-cart-item]');
const lineItemId = lineItem?.dataset.cartItemId;
{% for lineItem in page.cart.lineItems %}
if (lineItemId === '{{ lineItem.id }}') {
if (typeof gtag !== 'undefined') {
gtag('event', 'remove_from_cart', {
currency: '{{ context.currency.isoCode }}',
value: {{ lineItem.price.unitPrice }},
items: [{
item_id: '{{ lineItem.referencedId }}',
item_name: '{{ lineItem.label|e('js') }}',
price: {{ lineItem.price.unitPrice }},
quantity: {{ lineItem.quantity }}
}]
});
}
}
{% endfor %}
});
});
});
</script>
{% endblock %}
View Cart (view_cart)
Track when users view their cart:
{% block page_checkout_cart %}
{{ parent() }}
<script>
(function() {
{% if page.cart.lineItems.count > 0 %}
const items = [
{% for lineItem in page.cart.lineItems %}
{
item_id: '{{ lineItem.referencedId }}',
item_name: '{{ lineItem.label|e('js') }}',
price: {{ lineItem.price.unitPrice }},
quantity: {{ lineItem.quantity }}
}{% if not loop.last %},{% endif %}
{% endfor %}
];
if (typeof gtag !== 'undefined') {
gtag('event', 'view_cart', {
currency: '{{ context.currency.isoCode }}',
value: {{ page.cart.price.totalPrice }},
items: items
});
}
{% endif %}
})();
</script>
{% endblock %}
Begin Checkout (begin_checkout)
Track when checkout starts:
{% block page_checkout_confirm %}
{{ parent() }}
<script>
(function() {
{% if page.cart.lineItems.count > 0 %}
const items = [
{% for lineItem in page.cart.lineItems %}
{
item_id: '{{ lineItem.referencedId }}',
item_name: '{{ lineItem.label|e('js') }}',
price: {{ lineItem.price.unitPrice }},
quantity: {{ lineItem.quantity }}
}{% if not loop.last %},{% endif %}
{% endfor %}
];
if (typeof gtag !== 'undefined') {
gtag('event', 'begin_checkout', {
currency: '{{ context.currency.isoCode }}',
value: {{ page.cart.price.totalPrice }},
items: items
});
}
{% endif %}
})();
</script>
{% endblock %}
Add Shipping Info (add_shipping_info)
Track when shipping method is selected:
// In checkout JavaScript
document.addEventListener('DOMContentLoaded', function() {
const shippingOptions = document.querySelectorAll('[name="shippingMethodId"]');
shippingOptions.forEach(function(option) {
option.addEventListener('change', function() {
const shippingMethod = this.dataset.shippingMethodName;
if (typeof gtag !== 'undefined') {
gtag('event', 'add_shipping_info', {
currency: '{{ context.currency.isoCode }}',
value: {{ page.cart.price.totalPrice }},
shipping_tier: shippingMethod,
items: window.cartItems || []
});
}
});
});
});
Add Payment Info (add_payment_info)
Track when payment method is selected:
// In checkout JavaScript
document.addEventListener('DOMContentLoaded', function() {
const paymentOptions = document.querySelectorAll('[name="paymentMethodId"]');
paymentOptions.forEach(function(option) {
option.addEventListener('change', function() {
const paymentMethod = this.dataset.paymentMethodName;
if (typeof gtag !== 'undefined') {
gtag('event', 'add_payment_info', {
currency: '{{ context.currency.isoCode }}',
value: {{ page.cart.price.totalPrice }},
payment_type: paymentMethod,
items: window.cartItems || []
});
}
});
});
});
Purchase (purchase)
Track completed orders on the order confirmation page:
{% block page_checkout_finish %}
{{ parent() }}
<script>
(function() {
{% set order = page.order %}
{% if order %}
const items = [
{% for lineItem in order.lineItems %}
{
item_id: '{{ lineItem.productNumber|default(lineItem.id) }}',
item_name: '{{ lineItem.label|e('js') }}',
price: {{ lineItem.unitPrice }},
quantity: {{ lineItem.quantity }}
}{% if not loop.last %},{% endif %}
{% endfor %}
];
if (typeof gtag !== 'undefined') {
gtag('event', 'purchase', {
transaction_id: '{{ order.orderNumber }}',
value: {{ order.amountTotal }},
tax: {{ order.amountTotal - order.amountNet }},
shipping: {{ order.shippingCosts.totalPrice }},
currency: '{{ order.currency.isoCode }}',
coupon: '{{ order.promotions.first.code|default('') }}',
items: items
});
}
// Send to data layer for other tags
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'purchase',
'ecommerce': {
'transaction_id': '{{ order.orderNumber }}',
'value': {{ order.amountTotal }},
'currency': '{{ order.currency.isoCode }}',
'items': items
}
});
{% endif %}
})();
</script>
{% endblock %}
Using GTM for Ecommerce Tracking
For easier management, use Google Tag Manager:
1. Push Ecommerce Data to Data Layer
Instead of calling gtag() directly, push to dataLayer:
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'view_item',
'ecommerce': {
'currency': 'EUR',
'value': 29.99,
'items': [{
'item_id': 'SKU123',
'item_name': 'Product Name',
'price': 29.99,
'quantity': 1
}]
}
});
2. Create Ecommerce Tags in GTM
- Trigger: Custom Event (event equals view_item)
- Tag Type: Google Analytics: GA4 Event
- Event Name:
\{\{Event\}\}variable - Ecommerce Data: Pull from data layer
See GTM Data Layer Structure for details.
Enhanced Ecommerce Features
Product Variants Tracking
Track product variants (size, color, etc.):
{
item_id: '{{ product.productNumber }}',
item_name: '{{ product.translated.name }}',
item_variant: '{{ product.options|map(o => o.translated.name)|join(", ") }}'
}
Promotion Tracking
Track promotion impressions and clicks:
// Promotion impression
gtag('event', 'view_promotion', {
'creative_name': 'Summer Sale Banner',
'creative_slot': 'homepage_hero',
'promotion_id': 'SUMMER2024',
'promotion_name': 'Summer Sale',
'items': []
});
// Promotion click
gtag('event', 'select_promotion', {
'creative_name': 'Summer Sale Banner',
'creative_slot': 'homepage_hero',
'promotion_id': 'SUMMER2024',
'promotion_name': 'Summer Sale'
});
Refund Tracking
Track refunds (typically done server-side or manually):
gtag('event', 'refund', {
'transaction_id': 'ORDER123',
'value': 99.99,
'currency': 'EUR',
'items': [{
'item_id': 'SKU123',
'item_name': 'Product Name',
'price': 99.99,
'quantity': 1
}]
});
Data Accuracy Best Practices
1. Use Consistent Product IDs
Choose one product identifier and stick with it:
- Product Number (productNumber) - Recommended
- SKU - If different from product number
- Product ID - Database ID (less portable)
2. Include All Required Parameters
Always include:
currency- ISO currency codevalue- Total transaction valueitemsarray with at least:item_iditem_namepricequantity
3. Match GA4 Currency Settings
Ensure currency in events matches GA4 property settings.
4. Test Thoroughly
Test all events:
- Use GA4 DebugView
- Check all parameters are correct
- Verify values match order totals
- Test on different devices
Troubleshooting
Events Not Firing
Check:
- GA4 is installed and configured
- JavaScript console for errors
- Cookie consent is granted
- Shopware cache is cleared
Incorrect Values
Common Issues:
- Currency mismatch
- VAT/tax included vs. excluded
- Shipping costs not included
- Discounts not calculated correctly
Fix:
{# Use Shopware's calculated prices #}
value: {{ order.amountTotal }} {# Total including tax #}
tax: {{ order.amountTotal - order.amountNet }}
Duplicate Purchase Events
Causes:
- User refreshing confirmation page
- Multiple tracking implementations
- Plugin + manual code both active
Prevention:
// Track purchase only once
if (!sessionStorage.getItem('order_{{ order.orderNumber }}_tracked')) {
gtag('event', 'purchase', { /* ... */ });
sessionStorage.setItem('order_{{ order.orderNumber }}_tracked', 'true');
}
Missing Items Data
Issue: Items array is empty or incomplete.
Solution:
- Verify loop syntax in Twig
- Check product data is available
- Add fallback values for optional fields
item_category: '{{ product.categories.first.translated.name|default('Uncategorized') }}'
Performance Optimization
Lazy Load Tracking Scripts
Don't block page rendering:
// Load tracking after page load
window.addEventListener('load', function() {
// Initialize ecommerce tracking
});
Debounce Frequent Events
For events that fire multiple times:
let addToCartTimeout;
document.addEventListener('cartUpdated', function() {
clearTimeout(addToCartTimeout);
addToCartTimeout = setTimeout(function() {
// Send add_to_cart event
}, 500);
});
Next Steps
- GTM Setup - Easier ecommerce tracking management
- Data Layer Structure - Use Shopware data with GTM
- Troubleshoot Tracking - Fix tracking issues
For general GA4 ecommerce concepts, see Google Analytics 4 Guide.