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
Follow this guide to learn how to track post purchase upsells. 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.
Using Our Latest Integration for the Checkout Script via Customer Events:
If you are not using our latest integration for the checkout script via customer events, we recommend that you upgrade following our guide because this will ensure your post-purchase tracking is fully up to date.
What's New?
In December 2023, we updated our approach to tracking on the upsell platform. Previously, we triggered the dl_purchase event from the upsell page and then adjusted to prevent it from firing on the thank you page. We are now transitioning to a new approach where the dl_purchase event will be fired from the thank you page, aligning with standard setups. However, we will continue to enable capturing of upsell purchase events from the post-purchase page.
Why Did we Change Our Approach?
- Made the setup easier - The new recommended setup has less tag customization and no manual updates needed to your order status scripts.
- Allows new and returning users to be captured for your purchase event when the user navigates to the thank you page. These values can impact the way you pay on affiliate platforms! Also, allows you to target new vs returning users on your advertising platform. With the previous setup, this data would never be captured.
- The previous advanced setup is not necessary with so many destinations available for server-side setup.
- No longer need to be concerned with the 500ms processing time on the post-purchase page.
- Makes future updates to your Elevar Tracking much easier!
How to Track Post Purchase Upsells
Access Shopify Settings:
- Begin on your Shopify homepage and use the left-hand navigation menu to click on the "Settings" tab or gear icon located in the lower left-hand corner of the page.
- (See Figure 1)
_Figure 1_
Access Checkout Settings:
- Once you have accessed your Shopify settings, use the left-hand navigational menu to click on the "Checkout" tab.
- (See Figure 2)
_Figure 2_
Locate Post-Purchase Page Settings:
- Once you have accessed the Checkout settings in Shopify, navigate to the section of the page titled "Post-purchase page".
- (See Figure 3)
_Figure 3_
Add Code:
- Copy the script below and add it in the "Additional Scripts" box of your Shopify Post-purchase page checkout settings.
- Ensure that you update the code with your GA4 ID. Follow this guide to learn how to locate your GA4 ID.
- Ensure that you update the code with your and Meta (Facebook) ID. Follow this guide to learn how to locate your Meta (Facebook) ID.
<!-- Begin GTM-less Upsell Tracking -->
<script>
// CONFIGURATION - Replace with your actual tracking IDs
const TRACKING_CONFIG = {
ga4_measurement_id: 'XXXXX', // Replace with GA4 Measurement ID
facebook_pixel_id: 'XXXXX', // Replace with Facebook Pixel ID
enable_ga4: true, // Set to false to disable GA4 tracking
enable_facebook: true // Set to false to disable Facebook tracking
};
var upsellCount = 0;
(function() {
// EVENT HOOKS -----------------------------------------------------------
// Only listen for upsell events - no initial purchase tracking
Shopify.on('CheckoutAmended', function (newOrder, initialOrder) {
onCheckoutAmended(newOrder, initialOrder, window.Shopify);
});
// END EVENT HOOKS -------------------------------------------------------
function initializeGA4AndTrack(order, purchaseData) {
// Initialize gtag immediately with synchronous setup
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
window.gtag = gtag;
gtag('js', new Date());
// Configure GA4 with no user data in config
gtag('config', TRACKING_CONFIG.ga4_measurement_id);
// Set user properties (only customer_id, no PII)
gtag('set', 'user_properties', {
'customer_id': order.customer.id
});
// Track the upsell event with only transaction-level data
const eventData = {
transaction_id: purchaseData.transaction_id,
value: parseFloat(purchaseData.value),
currency: purchaseData.currency,
tax: parseFloat(purchaseData.tax || 0),
shipping: parseFloat(purchaseData.shipping || 0),
coupon: purchaseData.coupon || '',
purchase_type: 'upsell',
items: purchaseData.items.map(item => ({
item_id: item.item_id,
item_name: item.item_name,
category: item.category,
quantity: parseInt(item.quantity),
price: parseFloat(item.price),
item_variant: item.item_variant
}))
};
gtag('event', 'upsell_purchase', eventData);
// Load the GA4 script asynchronously (for future events)
const script = document.createElement('script');
script.async = true;
script.src = `https://www.googletagmanager.com/gtag/js?id=${TRACKING_CONFIG.ga4_measurement_id}`;
document.head.appendChild(script);
}
function initializeFacebookAndTrack(order, purchaseData) {
// Initialize Facebook Pixel with the standard implementation
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
// Initialize with customer data
fbq('init', TRACKING_CONFIG.facebook_pixel_id, {
em: order.customer.email,
external_id: order.customer.id.toString(),
ga_id: '', // Can be populated if you have GA Client ID
customer_type: order.customer.ordersCount > 1 ? 'returning' : 'new',
ph: order.customer.phone || '',
fn: order.customer.firstName || '',
ln: order.customer.lastName || '',
ct: order.billingAddress?.city || '',
st: order.billingAddress?.province || '',
zp: order.billingAddress?.zip || '',
country: order.billingAddress?.country || ''
});
// Track PageView
fbq('track', 'PageView');
// Track UpsellPurchase event
const eventData = {
value: parseFloat(purchaseData.value),
currency: purchaseData.currency,
content_type: 'product_group',
contents: purchaseData.items.map(item => ({
id: item.item_id,
quantity: parseInt(item.quantity),
item_price: parseFloat(item.price)
})),
content_ids: purchaseData.items.map(item => item.item_id),
num_items: purchaseData.items.reduce((sum, item) => sum + parseInt(item.quantity), 0),
order_id: purchaseData.transaction_id,
content_name: purchaseData.items.map(item => item.item_name).join(', ')
};
fbq('trackCustom', 'Upsell Purchase', eventData);
}
// TRACKING FUNCTIONS ----------------------------------------------------
function onCheckoutAmended(upsellOrder, initialOrder, shopifyObject) {
upsellCount++;
// Identify which items were added to the initial order
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;
// Initialize tracking pixels and track immediately when ready
initializeTrackingAndFire(upsellOrder, addedItems, initialOrder, shopifyObject);
}
function initializeTrackingAndFire(upsellOrder, addedItems, initialOrder, shopifyObject) {
const purchaseData = buildPurchaseData(upsellOrder, addedItems, true, initialOrder, shopifyObject);
// Initialize and track GA4 immediately
if (TRACKING_CONFIG.enable_ga4) {
initializeGA4AndTrack(upsellOrder, purchaseData);
}
// Initialize and track Facebook immediately
if (TRACKING_CONFIG.enable_facebook) {
initializeFacebookAndTrack(upsellOrder, purchaseData);
}
}
// DATA BUILDING FUNCTIONS -----------------------------------------------
function buildPurchaseData(order, addedItems, isUpsell, initialOrder, shopifyObject) {
const revenue = isUpsell ? getAdditionalRevenue(order, initialOrder) : order.totalPrice;
const subtotal = isUpsell ? getAdditionalSubtotal(order, initialOrder) : order.subtotalPrice;
let affiliation = '';
try {
affiliation = new URL(shopifyObject.pageUrl).hostname;
} catch (e) {
affiliation = '';
}
return {
transaction_id: getOrderId(order.id, isUpsell).toString(),
order_name: getOrderId(order.number, isUpsell).toString(),
value: revenue,
currency: order.currency,
tax: '0',
shipping: (parseFloat(revenue) - parseFloat(subtotal)).toString(),
coupon: getDiscountAmount(order, isUpsell, addedItems),
affiliation: affiliation,
customer_id: order.customer.id,
customer_email: order.customer.email,
customer_first_name: order.customer.firstName,
customer_last_name: order.customer.lastName,
customer_order_count: order.customer.ordersCount,
items: buildItemsData(addedItems)
};
}
function buildItemsData(lineItems) {
return lineItems.map(function (item) {
return {
item_id: item.variant.sku || item.variant.id.toString(),
item_name: item.title,
category: item.product.type || '',
quantity: item.quantity.toString(),
price: item.price.toString(),
item_variant: item.title,
product_id: item.product.id.toString(),
variant_id: item.variant.id.toString()
};
});
}
// UTILITY FUNCTIONS -----------------------------------------------------
function getDiscountAmount(shopifyOrder, isUpsell, addedItems) {
if (shopifyOrder.discounts === null || typeof shopifyOrder.discounts === 'undefined') return '0';
if (shopifyOrder.discounts.length === 0) return '0';
if (!isUpsell) {
return shopifyOrder.discounts.reduce(function (acc, discount) {
return acc += parseFloat(discount.amount);
}, 0).toFixed(2).toString();
} else {
return addedItems.reduce(function (acc, addedItem) {
return acc += parseFloat(addedItem.lineLevelTotalDiscount || 0);
}, 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);
}
// Export functions for testing (optional)
try {
module.exports = exports = {
onCheckoutAmended: onCheckoutAmended,
resetUpsellCount: function(){upsellCount = 0;},
initializeTrackingAndFire: initializeTrackingAndFire
};
} catch (e) { }
})();
</script>
<!-- End GTM-less Upsell Tracking -->
Save Changes:
- Once you have added the required post purchase tracking code into the additional scripts checkout settings in your Shopify account and have added your personal IDs, be sure to save your changes.
- Click on the "Save" button located in the bottom right-hand corner of the Checkout Settings page.
- (See Figure 4)
- Click on the "Save" button located in the bottom right-hand corner of the Checkout Settings page.
_Figure 4_
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 18 days ago