In the modern privacy-first landscape, preserving UTM parameters across a user’s OpenCart session is crucial for accurate attribution, funnel analysis, and marketing ROI. Most users land with UTM tags (like from Google Ads), but lose attribution by the time they convert.
๐งฐ Prerequisites
Component | Purpose |
---|---|
OpenCart 3.x or 4.x | eCommerce platform |
Google Tag Manager | Installed across all pages |
Google Analytics 4 | Configured via GTM |
Consent Mode v2 | Optional but recommended for legality |
๐ UTM Parameters Weโll Track
utm_source
utm_medium
utm_campaign
utm_content
utm_term
๐ฆ Step 1: Create a UTM Capture Script in OpenCart Header
Insert this script in catalog/view/theme/YOUR_THEME/template/common/header.twig
:
<script>
(function() {
const getParam = (name) => {
const match = new RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
};
const utmParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
utmParams.forEach(param => {
const value = getParam(param);
if (value) {
localStorage.setItem(param, value);
document.cookie = `${param}=${value}; path=/; max-age=2592000`; // 30 days
}
});
})();
</script>
โ
This stores the UTM values in both localStorage
and cookie
, making them accessible for both client-side and server-side scripts.
๐๏ธ Step 2: Push Stored UTM Values into dataLayer
Still inside header.twig
or footer.twig
, add:
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
event: 'utm_ready',
utm_source: localStorage.getItem('utm_source') || '',
utm_medium: localStorage.getItem('utm_medium') || '',
utm_campaign: localStorage.getItem('utm_campaign') || '',
utm_content: localStorage.getItem('utm_content') || '',
utm_term: localStorage.getItem('utm_term') || ''
});
</script>
๐ Step 3: Configure GTM to Capture These Values
In GTM:
- Create Data Layer Variables:
DLV - utm_source
โutm_source
DLV - utm_medium
โutm_medium
DLV - utm_campaign
โutm_campaign
DLV - utm_content
โutm_content
DLV - utm_term
โutm_term
- Modify GA4 Event Tags (purchase, add_to_cart, etc.):
- Add custom parameters in the event tag:
utm_source: {{DLV - utm_source}} utm_medium: {{DLV - utm_medium}} utm_campaign: {{DLV - utm_campaign}}
- Add custom parameters in the event tag:
- Optionally send these as user properties in your GA4 Config tag:
user_properties.utm_source: {{DLV - utm_source}}
โ Step 4: Inject UTM into Purchase Event on Success Page
In success.twig
, ensure this is fired with each order:
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: '{{ order_id }}',
value: {{ total }},
currency: '{{ currency }}',
items: [
{% for product in products %}
{
item_id: '{{ product.product_id }}',
item_name: '{{ product.name }}',
price: '{{ product.price }}',
quantity: '{{ product.quantity }}'
}{% if not loop.last %},{% endif %}
{% endfor %}
]
},
attribution: {
utm_source: localStorage.getItem('utm_source'),
utm_medium: localStorage.getItem('utm_medium'),
utm_campaign: localStorage.getItem('utm_campaign'),
utm_content: localStorage.getItem('utm_content'),
utm_term: localStorage.getItem('utm_term')
}
});
</script>
Then in GTM, update your purchase
GA4 tag to include:
utm_source: {{DLV - attribution.utm_source}}
utm_medium: {{DLV - attribution.utm_medium}}
- etc.
๐ Step 5: Consent-Aware Storage (Optional)
If using a CMP (like Cookiebot), wrap the script like:
<script>
if (window.Cookiebot && Cookiebot.consents.given.marketing) {
// store or read UTM
}
</script>
Or use GTM’s consent settings to delay UTM storage until allowed.
๐งช Step 6: QA & Debugging
Tool | Use Case |
---|---|
GTM Preview Mode | Confirm UTM variables are available |
GA4 DebugView | Validate custom UTM parameters in events |
Browser DevTools | Inspect localStorage + cookies |
GA4 โ Explorations | Build audience filters by UTM data |
๐ Strategic Value of UTM Persistence
Benefit | Why It Matters |
---|---|
Survives multi-day journeys | Users can convert days later with credit |
Enables retargeting segmentation | Segment by campaign even after bounce |
Fixes attribution gaps | Avoids (direct) / (none) in GA4 |
Powers server-side attribution | UTM from cookies = backend use |