How to Track Post Purchase Upsells
Learn how to update your tracking to ensure maximum conversion tracking coverage for your upsells in the native Shopify checkout.
Overview
If you have an upsell app such as Rebuy, CartHook, or Zipify installed on your Shopify Store you have the ability to offer additional products after the checkout has completed. This post-purchase page is presented to your customer after the payment is processed before the thank you page.
How Upsell Funnels Work
If a customer purchases the upsell, a new event called dl_upsell_purchase will fire. This happens before the final thank you page loads.
The purchase information sent on this event refers only to the upsell products (think of it as a completely separate purchase).
Next, when your customer reaches the final thank you page we fire the dl_purchase event. If you user never reaches the thank you page we've got you covered with your server-side purchase event triggered by the order being created in Shopify.
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. Follow this guide to learn how to locate your 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
Publish Updates & QA
- One big limitation with the new post-purchase page settings is that you can't use GTM preview mode since it's sandboxed JavaScript. This means you'll need to publish your container, place a test order, and verify using dev tools in your browser OR inside your events manager.
Frequently Asked Questions:
Will I miss out on purchase tracking if my customer doesn't reach the Thank You Page?
You may find that many of your users don't navigate to the thank you page when you present a post-purchase upsell. If you are using our server-side tracking for available destinations this is not an issue as the server-side purchase trigger is based on the order creation within Shopify to trigger the server-side event to send. This trigger still occurs even if the customer closes out at the post-purchase step.
We recommend leaving your dl_purchase to fire on the thank_you page for a few reasons:
- The purchase data available on the post-purchase is limited and will prevent you from capturing some information like new vs returning users.
- There is a 500ms time limit for all tracking script to process on the post-purchase page, this prevents most web tags from firing which then leads to additional customizations for the tags that don't fire to get them to fire on the thank you page.
Updated 3 months ago