GA4 E-Commerce Data Layer Bridge (GA4 to JENTIS)
In this article we will create a translation script that takes your website’s window.dataLayer
(GTM Data Layer) and translates ecommerce events using the GA4 ecommerce data model into the JENTIS native data layer.
This creates a fast and easy solution to get started without the need to change your website.
⚠️ Important: Direct integration via
_jts.push
is always preferred. Only use this bridge if direct integration is not feasible.
The Why and The Mission
Google Analytics 4 (GA4) is a powerful tool for tracking and analyzing user behavior. Its ecommerce capabilities give businesses deep insights into purchasing behavior.
However, GA4’s native model does not always align with your existing infrastructure. Migrating from the familiar GTM dataLayer
format to GA4’s ecommerce model can be complex.
Our custom translation script bridges the gap: it allows you to keep using your existing dataLayer
and seamlessly translates events into the JENTIS tool-agnostic data model (JENTIS Data Layer: Push Data).
Goal: simplify the integration of ecommerce tracking into JENTIS without major redevelopment, while ensuring consistency and accuracy.
Key Benefits and Features
Seamless Integration Works with your existing
dataLayer
— no need to rebuild data structures.Familiar Workflow Mimics the GTM/GA4 push mechanism for an easy transition.
Default Settings Ready All JENTIS tools, tags, triggers, and variables work out of the box.
Tool-agnostic Approach Instead of a GA4-only model, JENTIS translates your data once into a uniform structure that scales across tools (e.g. Facebook CAPI, TikTok, etc.).
How It Works
The script acts as a middleware: it intercepts pushes to window.dataLayer
, maps GA4 ecommerce events and properties, and forwards them into the JENTIS _jts
data layer.
This way, ecommerce actions such as product views, add-to-cart, or purchases are captured automatically and transformed into JENTIS’ ecommerce tracking model.
Drawbacks and Pitfalls
Implementing a bridge code should be considered a temporary or MVP solution. Native JENTIS integrations are more stable and performant. Be aware of:
Hidden Dependency: Developers may assume
dataLayer
is optional once GTM is removed. This approach creates a dependency.Data Consistency Risks: Translation may introduce discrepancies between
dataLayer
and_jts
.Customization Limits: Highly specialized use cases may require more than this script can cover.
Performance Overhead: Adds a small amount of extra JavaScript execution.
Maintenance: GA4 evolves, so updates may be needed.
Debugging Complexity: Troubleshooting requires knowledge of both GA4 and JENTIS mappings.
Event Mappings
GA4 event → JENTIS event (product.type
):
view_item_list
→productlist
select_item
→productlistclick
view_item
→productview
add_to_wishlist
→addtowishlist
add_to_cart
→addtocart
remove_from_cart
→removefromcart
view_cart
→cartview
begin_checkout
→checkout
(step 1)add_payment_info
→checkout
(step 2)add_shipping_info
→checkout
(step 3)refund
→refund
purchase
→order
view_promotion
→promotionimpression
select_promotion
→promotionclick
Default (if mapping fails) →
jtm_customcode_mapping_failed
Property Mappings
Each GA4 item property is mapped into a JENTIS product attribute:
{
"track": "product",
"type": "<from event mapping>",
"id": "item.item_id",
"name": "item.item_name",
"group": [
"item.item_category",
"item.item_category2",
"item.item_category3",
"item.item_category4",
"item.item_category5"
],
"variant": "item.item_variant",
"quantity": "item.quantity",
"brutto": "item.price - item.discount (if present)",
"discount": "item.discount",
"position": "item.index",
"oldbrutto": "item.price",
"brand": "item.item_brand",
"coupon": "item.coupon"
}
Implementing the Custom Code
To add the script:
In JENTIS Tag Manager, navigate to Codes.
Add a new Code element, give it a descriptive name, and select the containers where it should run.
Copy and paste the following code into the JS Code field:
// Step 1: Ensure the existence of both dataLayer arrays
window.dataLayer = window.dataLayer || [];
window._jts = window._jts || [];
// Step 2: Intercept push calls to window.dataLayer
(function(originalPush) {
window.dataLayer.push = function() {
// Convert arguments to a real array to work with
var args = Array.prototype.slice.call(arguments);
// Process each object being pushed
for (var i = 0; i < args.length; i++) {
try{
var obj = args[i];
// Step 3: Process and transform the intercepted data
var jtsPushCommands = transformDataLayerObject(obj);
// Step 4: Forward the transformed data to window._jts
for(var j = 0; j < jtsPushCommands.length; j++){
window._jts.push(jtsPushCommands[j]);
}
}catch(e){
console.warn("JENTIS GA4 DataLayer BRIDGE FAILED");
}
}
// Continue with the normal dataLayer push process
originalPush.apply(window.dataLayer, arguments);
};
})(window.dataLayer.push);
function itemToProductMapping(item, jtsType){
return {
"track" : "product",
"type" : jtsType,
"id" : item.item_id,
"name" : item.item_name,
"group" : [
item.item_category,
item.item_category2,
item.item_category3,
item.item_category4,
item.item_category5
],
"variant" : item.item_variant,
"quantity" : item.quantity,
"brutto" : item.price && item.discount ? item.price - item.discount : item.price,
"discount" : item.discount,
"position" : item.index,
"oldbrutto" : item.price,
"brand" : item.item_brand,
"coupon" : item.coupon
};
}
function ecommerceToDocumentMapping(ecomm, event){
var jtsTrack = "";
switch(event) {
case "view_item_list":
jtsTrack = "productlist";
break;
case "select_item":
jtsTrack = "productlistclick";
break;
case "view_item":
jtsTrack = "productview";
break;
case "add_to_wishlist":
jtsTrack = "addtowishlist";
break;
case "add_to_cart":
jtsTrack = "addtocart";
break;
case "remove_from_cart":
jtsTrack = "removefromcart";
break;
case "view_cart":
jtsTrack = "cartview";
break;
case "begin_checkout":
jtsTrack = "checkout";
break;
case "add_shipping_info":
jtsTrack = "checkout";
break;
case "add_payment_info":
jtsTrack = "checkout";
break;
case "refund":
jtsTrack = "refund";
break;
case "purchase":
jtsTrack = "order";
break;
case "view_promotion":
jtsTrack = "promotionimpression";
break;
case "select_promotion":
jtsTrack = "promotionclick";
break;
default:
jtsTrack = "jtm_customcode_mapping_failed";
}
var jtsDoc = {
track: jtsTrack
};
if(jtsTrack == "checkout"){
switch(event) {
case "begin_checkout":
jtsDoc.step = "1";
break;
case "add_payment_info":
jtsDoc.step = "2";
break;
case "add_shipping_info":
jtsDoc.step = "3";
break;
}
}
if(ecomm.item_list_id)
jtsDoc.id = ecomm.item_list_id;
if(ecomm.item_list_name)
jtsDoc.name = ecomm.item_list_name;
if(ecomm.currency)
jtsDoc.currency = ecomm.currency;
if(ecomm.value)
jtsDoc.value = ecomm.value;
if(ecomm.shipping_tier)
jtsDoc.shipping_tier = ecomm.shipping_tier;
if(ecomm.payment_type)
jtsDoc.paytype = ecomm.payment_type;
if(ecomm.transaction_id)
jtsDoc.orderid = ecomm.transaction_id;
if(ecomm.tax)
jtsDoc.tax = ecomm.tax;
if(ecomm.shipping)
jtsDoc.shipping = ecomm.shipping;
if(ecomm.coupon)
jtsDoc.vouchers = [{"code":ecomm.coupon}];
if(jtsTrack == "order")
jtsDoc.brutto = ecomm.value;
return jtsDoc;
}
// Function to transform dataLayer object
function transformDataLayerObject(obj) {
var jtsCommands = [];
if(obj.ecommerce && obj.event){
var jtsDocument = ecommerceToDocumentMapping(obj.ecommerce, obj.event);
if(Array.isArray(obj.ecommerce.items)) {
// Process each product item
obj.ecommerce.items.forEach(function(item) {
// Transform each product into the required format and push to _jts
var transformedProduct = itemToProductMapping(item, jtsDocument.track);
// Push the transformed product to _jts
jtsCommands.push(transformedProduct);
});
}
jtsCommands.push(jtsDocument);
if(obj.event == "view_promotion" || obj.event == "select_promotion"){
var jtsPromo = {
track : "promotion",
id : obj.ecommerce.promotion_id,
name : obj.ecommerce.promotion_name,
creative : obj.ecommerce.creative_name,
creativeslow : obj.ecommerce.creative_slot
}
jtsPromo.type = obj.event == "view_promotion" ? "promotionimpression" : "promotionclick";
jtsCommands.push(jtsPromo);
jtsCommands.push({track:jtsPromo.type});
}
jtsCommands.push({track:"submit"});
}
return jtsCommands;
}
// Step 5: Handle existing data in window.dataLayer
// Process each existing object in dataLayer
window.dataLayer.forEach(function(obj) {
var jtsPushCommands = transformDataLayerObject(obj);
for(var j = 0; j < jtsPushCommands.length; j++){
window._jts.push(jtsPushCommands[j]);
}
});
Conclusion
This bridging script provides a fast way to integrate GA4 ecommerce tracking into JENTIS without rebuilding your site’s data layer.
It enables businesses to benefit from GA4’s enhanced capabilities while leveraging the JENTIS tool-agnostic data layer for long-term scalability.
For production-ready setups, we recommend planning a full migration to the native JENTIS Data Layer.
Last updated
Was this helpful?