Carthook
Tracking your Carthook post-purchase upsells with Elevar
For Post-Purchase tracking with Carthook, follow our upsell guide
Follow this guide, How to Track Post Purchase Upsells, for an in-depth review of Upsell Event Tracking and how to set up post-purchase upsells with Elevar.
How to Add Upsell Event Tracking in Your Destinations
Locate Post-Purchase Page Settings:
- Begin on your Shopify homepage and click on the "Settings" button or the gear icon in the lower left-hand corner of the page.
- Using the left-hand menu, locate and click on the "Checkout" tab.
- Navigate to the section of the page titled "Post-purchase page".
- (See Figure 1)
Figure 1
Add Code:
- Copy the script below and add it in the "Additional Scripts" box of your Shopify Post-purchase page checkout settings.
- Be sure to update the GTM-xxxxx near the top of the script with your Web Container ID.
- The code below pushes a dl_upsell_purchase event when a user takes an upsell or downsell offer.
<!-- Begin Elevar Upsell Tracking -->
<script>
// Fires an dl_upsell_purchase event when customer takes upsell offer
(function (w, d, s, l, i) {
w[l] = w[l] || []; w[l].push({
'gtm.start':
new Date().getTime(), event: 'gtm.js'
}); var f = d.getElementsByTagName(s)[0],
j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src =
'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'dataLayer', 'GTM-XXX');
var upsellCount = 0;
(function() {
// EVENT HOOKS -----------------------------------------------------------
if (!Shopify.wasPostPurchasePageSeen) {
onCheckout(window.Shopify.order, window.Shopify);
}
Shopify.on('CheckoutAmended', function (newOrder, initialOrder) {
onCheckoutAmended(newOrder, initialOrder, window.Shopify);
});
// END EVENT HOOKS -------------------------------------------------------
// UTILS -----------------------------------------------------------------
// Function called after original order is placed, pre upsell.
function onCheckout(initialOrder, shopifyObject) {
window.dataLayer = window.dataLayer || [];
pushDLPurchase(initialOrder, initialOrder.lineItems, false, null, shopifyObject);
}
// Function called when upsell is taken. Separate the new/upsell
// items from the items in the initial order and then send a purchase event
// for just the new items.
function onCheckoutAmended(upsellOrder, initialOrder, shopifyObject) {
// identify which items were added to the initial order, if any.
upsellCount++;
// The line item id is unique for order items, even if the items contained are the same.
// We can use this to seperate out items from the initial order from the upsell.
var initialItemIds = initialOrder.lineItems.map(function (line) { return line.id; });
var addedItems = upsellOrder.lineItems.filter(
function (line) { return initialItemIds.indexOf(line.id) < 0; }
);
// if no new items were added skip tracking
if (addedItems.length === 0) return;
pushDLPurchase(upsellOrder, addedItems, true, initialOrder, shopifyObject);
}
function pushDLPurchase(order, addedItems, isUpsell, initialOrder, shopifyObject) {
window.dataLayer.push({
'event': isUpsell ? 'dl_upsell_purchase' : '',
'event_id': getOrderId(order.id, isUpsell),
'user_properties': getUserProperties(order),
'ecommerce': {
'purchase': {
'actionField': getActionField(order, isUpsell, initialOrder, addedItems, shopifyObject),
'products': getLineItems(addedItems),
},
'currencyCode': order.currency,
},
});
}
// Returns a user properties object
function getUserProperties(data) {
return {
'customer_id': data.customer.id,
'customer_email': data.customer.email,
'customer_first_name': data.customer.firstName,
'customer_last_name': data.customer.lastName,
'customer_order_count': data.customer.ordersCount,
}
}
// Gets line items in purchase
function getLineItems(lineItems) {
return lineItems.map(function (item) {
return {
'category': item.product.type,
'variant_id': item.variant.id.toString(),
'product_id': Number(item.product.id).toString(),
'id': item.variant.sku,
// We don't get variant title details
'variant': item.title,
'name': item.title,
'price': item.price.toString(),
'quantity': item.quantity.toString(),
// Not available
// 'brand': orderItem.brand,
}
});
}
function getActionField(order, isUpsell, initialOrder, addedItems, shopifyObject) {
var revenue = isUpsell ? getAdditionalRevenue(order, initialOrder) : order.totalPrice;
var subtotal = isUpsell ? getAdditionalSubtotal(order, initialOrder) : order.subtotalPrice;
try {
affiliation = new URL(shopifyObject.pageUrl).hostname;
} catch (e){
affiliation = '';
}
return {
'action': "purchase",
'affiliation': affiliation,
// This is the longer order id that shows in the url on an order page
'id': getOrderId(order.id, isUpsell).toString(),
// This should be the #1240 that shows in order page.
'order_name': getOrderId(order.number, isUpsell).toString(),
// This is total discount. Dollar value, not percentage
// On the first order we can look at the discounts object. On upsells, we can't.
// This needs to be a string.
'discount_amount': getDiscountAmount(order, isUpsell, addedItems),
// We can't determine shipping & tax. For the time being put the difference between subtotal and rev in shipping
'shipping': (parseFloat(revenue) - parseFloat(subtotal)).toString(),
'tax': '0',
'revenue': revenue,
'sub_total': subtotal,
};
}
function getDiscountAmount(shopifyOrder, isUpsell, addedItems) {
if (shopifyOrder.discounts === null || typeof shopifyOrder.discounts === 'undefined') return '0';
if (shopifyOrder.discounts.length === 0) return '0';
// If this isn't an upsell we can look at the discounts object.
if (!isUpsell) {
// Collect all the discounts on the first order.
return shopifyOrder.discounts.reduce(function (acc, discount) {
return acc += parseFloat(discount.amount);
}, 0).toFixed(2).toString();
// If this an upsell we have to look at the line item discounts
// The discount block provided doesn't only applies to the first order.
} else {
return addedItems.reduce(function (acc, addedItem) {
return acc += parseFloat(addedItem.lineLevelTotalDiscount);
}, 0).toFixed(2).toString();
}
}
function getOrderId(orderId, isUpsell) {
return isUpsell ? orderId.toString() + '-US' + upsellCount.toString() : orderId;
}
function getAdditionalRevenue(newOrder, initialOrder) {
return (parseFloat(newOrder.totalPrice) - parseFloat(initialOrder.totalPrice)).toFixed(2);
}
function getAdditionalSubtotal(newOrder, initialOrder) {
return (parseFloat(newOrder.subtotalPrice) - parseFloat(initialOrder.subtotalPrice)).toFixed(2);
}
function test() {
onCheckoutAmended(newOrder, initialOrder);
}
try {
module.exports = exports = {
onCheckoutAmended: onCheckoutAmended,
onCheckout: onCheckout,
resetUpsellCount: function(){upsellCount = 0;},
};
} catch (e) { }
})();
</script>
<!-- End Elevar Upsell Tracking -->
Import Pre-Built Container(s)
- First you will need to configure upsell revenue and events for Google Analytics and Facebook.
- Download our Upsell Purchases pre-built container from your Elevar dashboard that ultimately looks like this inside of GTM
- (See Figure 2)
Figure 2`
- In the default setup, there is a Facebook Upsell Purchase tag to the dl_upsell_purchase event. This sends a custom conversion event so we dont inflate Facebook Purchase conversions.
- We also attach a custom GA event to the upsell that hyphenates the original order id (for ex. order 1234-US1). This lets you create a view in GA that filters out upsells and prevents inflated conversion rates.
Sending upsell purchase events to non GA & Facebook channels:
- If you are already using Elevar's pre-built container for other channels then you only need to account for the dl_upsell_purchase events.
- For example, let's say you have Google Ads configured in GTM for primary purchase events, but you want to send a Google Ads conversion for upsell purchases as well.
- You can create a new conversion in Google Ads and assign to the same upsell_purchase trigger used by Facebook.
- (See Figure 3)
Figure 3
Updated 6 months ago