Google Ads Enhanced Conversion helps you improve your conversion tracking by sharing more detailed conversion data with Google Ads.
Here, you will get a fully functional solution to set up Google Ads Enhanced Conversions in a few steps.
Before moving on to setting up & optimizing, you are recommended to check out our article on the key benefits of setting up Google Ads Enhanced Conversions.
Key Takeaways:
🎉 Bonus Content: Explore the best Shopify Google Ads apps to improve the quality of your campaigns.
First of all, if you are using an ad blocker, you should note that this tutorial contains many images- most image names include “Google Ads” or “Ads”. Ad blockers think that it is an ad and blocks those. So make sure to temporarily disable your ad blocker to properly view the content.
There are several methods available for setting up Google Ads Enhanced Conversion.
You can easily handle it yourself with the first method.
If you prefer more control over your data, you might choose the Google Tag Manager method.
For those requiring greater flexibility, the Google Ads API is an option.
Our focus is primarily on Purchase tracking, as it’s the key area where user data like email, phone number, and address is available for enhanced conversion tracking on Shopify. We mainly guide you through the Google Ads setup process here.
You can either create a new conversion or apply enhanced conversion tracking to an existing one.
In this tutorial, we’ll use an existing Google Ads conversion. The steps will be very similar if you decide to set up a new conversion instead.
Note: Google frequently updates its interface, and settings may appear in different locations depending on your account type. If you cannot find the “Enhanced Conversions” setting in this location, try these alternatives:
Now that you have enabled Enhanced Conversions, let’s move on to the next section. I will explain how to set up Google Ads Enhanced Conversion on Shopify using the Google Tag method.
Here are the steps and code blocks you’ll need. You can check this official article if you are not using Shopify or if you want to learn more about it.
To proceed, make sure that the Google Tag (gtag.js) is installed on your Shopify store. Enter your URL and click “CHECK URL.” If the Google Tag isn’t installed, you will only see the option for Google Tag Manager. In that case, proceed to Method 2. Alternatively, you can learn how to install Google Tag on Shopify.
If the Google Tag is installed on your Shopify store, you should see the setup options.
Click on “Confirm how you want to set up enhanced conversions” and select “Edit Code.” Click “Save” to proceed.
Next, you need to retrieve your Conversion ID and Label. Scroll up to the “Tag Setup” section and select Google Tag Manager. We won’t be using Google Tag Manager, but this will help you easily access your Conversion ID and Label. Copy these details before moving to the next step.
You likely have a conversion script located in Admin > Settings > Checkout > Additional Scripts. If you don’t see it immediately, search for “AW-” and it should guide you to the script. It should look something like this:
If the script isn’t there, here are three possibilities:
Navigate to Customer Events > Custom Pixel on your Shopify store and add the code block provided below. Before implementing, read these points carefully:
Ready? Great! Now copy the code block below and proceed to the next step.
// Configuration Variables
// Configuration Variables
const px_config = {
GADS: {
id: "AW-XXXXXXXXXX",
business_vertical: "retail",
conversions: {
add_to_cart: null,
view_item: null,
begin_checkout: null,
purchase: null,
},
},
flags: {
product_added_to_cart: false,
product_viewed: false,
checkout_started: false,
checkout_completed: true,
}
};
const consentModeEnabled = false;
/*
consent_mode
Options:
- true: Consent Mode enabled
- false: Consent Mode disabled
*/
const totalValue_option = null;
/*
totalValue_option
Options:
- net: without shipping and taxes
- no_shipping: without shipping
- no_tax: without taxes
- null: total price with shipping and taxes
*/
const add_to_cart_only_collections = false;
const add_to_cart_only_home = false;
const add_to_cart_only_pdp = true;
/*
Configuration for "Add to Cart" event tracking:
add_to_cart_only_pdp:
- When true, tracks add to cart events on product detail pages (/products/*)
- Set to false to disable tracking on product pages
- by default it is true
add_to_cart_only_collections:
- When true, tracks add to cart events on collection pages (/collections/*)
- Set to false to disable tracking on collection pages
- by default it is false
add_to_cart_only_home:
- When true, tracks add to cart events on the homepage (/)
- Set to false to disable tracking on the homepage
- by default it is false
These settings allow you to control where add to cart events are tracked.
You can enable/disable tracking for specific page types independently.
*/
const user_data = 'email';
/*
user_data
Options:
- email: Only email
- all: Email and shipping address parameters
*/
const version = "1.6";
const debug = {
status: true,
dlv: false,
detail: false,
callback: false,
color: "background:#9936ca;color:#fff;",
msg: "Analyzify Ads Conv. Pixel ->",
}
const initData = init?.data || {};
const initCart = initData?.cart || {};
const initContext = init?.context || {};
const initCurrency = initCart?.cost?.totalAmount?.currencyCode || null;
const initCustomer = initData?.customer || {};
const feed_region = 'XX';
const item_id_format = null;
/*
item_id_format
Options:
- variant_id
- product_id (default)
- product_sku
- null (shopify_XX_product_id_variant_id)
*/
const block_items_id = false;
const script = document.createElement("script");
script.setAttribute("src", `https://www.googletagmanager.com/gtag/js?id=${px_config.GADS.id}`);
script.setAttribute("async", "");
document.head.appendChild(script);
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());
// Check if consent mode is enabled
if (consentModeEnabled) gtag_consent_mode(debug.status);
gtag("config", px_config.GADS.id, {
"allow_enhanced_conversions": true,
"send_page_view": false
});
if (debug.status) console.log("%c%s Initialized", debug.color, debug.msg);
async function gtag_consent_mode() {
// Consent Mode Initialization
try {
const consent_version = "1.1";
let initConsent = init?.customerPrivacy || {};
let getCMPCookieValue = null;
let getTrackingConsentValue = null;
// Check cookies only if initConsent is not available
if (!initConsent?.marketingAllowed && !initConsent?.analyticsProcessingAllowed) {
try {
[getCMPCookieValue, getTrackingConsentValue] = await Promise.all([
browser.cookie.get("_cmp_a"),
browser.cookie.get("_tracking_consent"),
]);
if (debug?.status) {
console.log("%c%s Loading cookie consent values", debug.color, debug.msg, {
_cmp_a: getCMPCookieValue,
_tracking_consent: getTrackingConsentValue,
});
}
} catch (error) {
console.warn("%c%s Error fetching cookies:", debug?.color, debug?.msg, error);
}
}
if (debug?.status) {
console.log("%c%s Initial Consent", debug.color, debug.msg, initConsent || { _cmp_a: getCMPCookieValue, _tracking_consent: getTrackingConsentValue });
}
let currentConsent = {};
let currentConsentPurpose = {};
try {
const consentCookie = decodeURIComponent(getCMPCookieValue || getTrackingConsentValue);
currentConsent = JSON.parse(consentCookie) || {};
currentConsentPurpose = currentConsent?.purposes || {};
} catch (parseError) {
console.warn("%c%s Error parsing consent cookie, using defaults:", debug?.color, debug?.msg, parseError);
}
const cmpConsent = getTrackingConsentValue?.con?.CMP || {};
const consentSettings = {
ad_storage: initConsent?.marketingAllowed || currentConsentPurpose?.m === true || (cmpConsent?.m !== undefined && cmpConsent?.m !== '0') ? "granted" : "denied",
analytics_storage: initConsent?.analyticsProcessingAllowed || currentConsentPurpose?.a === true || (cmpConsent?.a !== undefined && cmpConsent?.a !== '0') ? "granted" : "denied",
ad_user_data: initConsent?.marketingAllowed || currentConsentPurpose?.m === true || (cmpConsent?.m !== undefined && cmpConsent?.m !== '0') ? "granted" : "denied",
ad_personalization: initConsent?.marketingAllowed || currentConsentPurpose?.m === true || (cmpConsent?.m !== undefined && cmpConsent?.m !== '0') ? "granted" : "denied",
analyzify_consent:
(
initConsent?.analyticsProcessingAllowed ||
(currentConsentPurpose?.a === true || (cmpConsent?.a !== undefined && cmpConsent?.a !== '0'))
) &&
(
initConsent?.marketingAllowed ||
currentConsentPurpose?.m === true ||
(cmpConsent?.m !== undefined && cmpConsent?.m !== '0')
) ? "granted" : "denied",
};
if (debug?.status) {
console.log("%c%s Final Consent Settings", debug.color, debug.msg, consentSettings);
}
// Custom Event
window.dataLayer.push({
event: "consentUpdate",
status: 'default',
...consentSettings
});
const consentObj = {
...consentSettings,
wait_for_update: 500
};
// GTAG - Consent
gtag("consent", "default", consentObj);
if (debug.status) console.log(`%c%s Consent settings applied (v${consent_version})`, debug.color, debug.msg, consentObj);
} catch (error) {
console.error("%c%s Error fetching or parsing consent cookie:", debug.color, debug.msg, JSON.stringify(error));
}
if (debug.status) console.log('%c%s gtag_consent_mode is initiated!', debug.color, debug.msg);
}
async function hashValue(value) {
if (!value) return null;
// Check if crypto and subtle are available
if (typeof window === 'undefined' || !window.crypto || !window.crypto.subtle) {
console.error('Crypto API is not available in this browser. Unable to hash value.');
return null;
}
try {
const encoder = new TextEncoder();
const data = encoder.encode(value);
const hashBuffer = await window.crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
} catch (error) {
console.error('Error while hashing value:', error);
return null;
}
}
const getTotal = (valueObj, total, shipping, tax, subtotal) => {
try {
const totalValue = total || valueObj?.totalPrice?.amount || 0;
const shippingValue = shipping || valueObj?.shippingLine?.price?.amount || 0;
const taxValue = tax || valueObj?.totalTax?.amount || 0;
const subTotal = subtotal || valueObj?.subtotalPrice?.amount || 0;
let finalValue = 0;
switch (totalValue_option) {
case 'net':
finalValue = subTotal;
break;
case 'no_shipping':
finalValue = subTotal + taxValue;
break;
case 'no_tax':
finalValue = subTotal + shippingValue;
break;
default:
finalValue = totalValue;
break;
}
return Number(parseFloat(finalValue).toFixed(2));
} catch (error) {
console.error('getTotal error:', error);
return 0; // Ensure a fallback return value
}
}
const getItemObj = (itemObj) => {
try {
if (!itemObj) {
console.error('Invalid itemObj:', itemObj);
return null;
}
const item = itemObj.variant || itemObj.merchandise || itemObj.productVariant;
let selected_id;
switch(item_id_format) {
case 'variant_id':
selected_id = item?.id;
break;
case 'product_id':
selected_id = item?.product?.id;
break;
case 'product_sku':
selected_id = item?.sku || item?.id || item?.product?.id;
break;
default:
selected_id = `shopify_${feed_region}_${item?.product?.id || ''}_${item?.id || ''}`;
}
const discountAll = itemObj?.discountAllocations?.reduce((total, discount) => {
return total + Number(parseFloat(discount?.amount?.amount) || 0);
}, 0) || 0;
const computedDiscount = itemObj?.finalLinePrice?.amount ? parseFloat(item?.price?.amount - itemObj.finalLinePrice?.amount) : 0;
const getDiscount = Number((discountAll || computedDiscount).toFixed(2));
const additions = {};
if(block_items_id){
additions.pid = selected_id || item?.id || null;
} else {
// customization part
additions.id = item?.id || null;
}
return {
id: selected_id || null,
business_vertical: px_config.GADS.business_vertical || "retail",
// item_id: selected_id || null,
item_name: item?.product?.title?.trim() || item?.title?.trim() || null,
item_category: item?.product?.type || null,
item_brand: item?.product?.vendor?.trim() || null,
item_sku: item?.sku || null,
discount: getDiscount || 0,
item_variant: item?.title?.trim() || null,
item_variant_id: item?.id || null,
price: item?.price?.amount || 0,
quantity: item?.quantity || itemObj?.quantity || 1,
currency: item?.price?.currencyCode || initCurrency,
...additions
};
} catch (error) {
console.error('getItemObj error:', error);
}
}
function doesPagePath(event, paths, scope = 'contain') {
const document = event.context.document;
const pagePath = document.location.pathname;
return paths.some((path) => {
switch (scope) {
case 'start':
return pagePath.startsWith(path);
case 'equal':
return pagePath === path;
case 'contain':
default:
return pagePath.includes(path);
}
});
}
const getUserData = async (checkoutData, initData) => {
// Extract data from init
const initCustomer = initData?.customer || {};
const initEmail = initCustomer.email || initData?.email || null;
const initPhone = initCustomer.phone || initData?.phone || null;
// Extract data from checkoutData
const checkoutCustomer = checkoutData?.order?.customer || {};
const checkoutEmail = checkoutData?.email || checkoutCustomer?.email || null;
const checkoutPhone = checkoutData?.phone || checkoutCustomer?.phone || null;
const billingAddress = checkoutData?.billingAddress || {};
const shippingAddress = checkoutData?.shippingAddress || {};
// Determine the primary data source for user info
const email = checkoutEmail || initEmail || null;
const phone = checkoutPhone || initPhone || billingAddress?.phone || shippingAddress?.phone || null;
const customerId = checkoutCustomer?.id || initCustomer?.id || null;
const firstName = billingAddress?.firstName || shippingAddress?.firstName || initCustomer?.firstName || null;
const lastName = billingAddress?.lastName || shippingAddress?.lastName || initCustomer?.lastName || null;
const street = `${billingAddress?.address1 || shippingAddress?.address1 || ''} ${billingAddress?.address2 || shippingAddress?.address2 || ''}`.trim();
// Hash email and phone
const customerId_hashed = await hashValue(customerId);
const email_hashed = await hashValue(email);
const phone_hashed = await hashValue(phone);
const firstName_hashed = await hashValue(firstName);
const lastName_hashed = await hashValue(lastName);
const street_hashed = await hashValue(street);
// Create the final user data object
const userData = user_data === 'email' ? {
email: email,
sha256_email_address: email_hashed,
} : {
id: customerId,
user_id: customerId_hashed,
email: email,
sha256_email_address: email_hashed,
phone_number: phone,
sha256_phone_number: phone_hashed,
address: {
first_name: firstName,
sha256_first_name: firstName_hashed,
last_name: lastName,
sha256_last_name: lastName_hashed,
street: street,
sha256_street: street_hashed,
city: billingAddress?.city || shippingAddress?.city || null,
country: billingAddress?.country || shippingAddress?.country || null,
region: billingAddress?.province || shippingAddress?.province || null,
postal_code: billingAddress?.zip || shippingAddress?.zip || null,
}
};
if (debug.status && debug.detail) console.log('%c%s userData', debug.color, debug.msg, userData);
return userData;
};
const getPageType = () => {
const path = initContext?.document?.location?.pathname;
const pathTypeMapping = [
{ type: 'category', includes: '/collections' },
{ type: 'product', includes: '/products' },
{ type: 'basket', includes: '/cart' },
{ type: 'homepage', equals: '/' },
{ type: 'purchase', includes: ['/thank_you', '/thank-you'] },
];
for (const { type, includes, equals } of pathTypeMapping) {
if (equals && path === equals) {
return type;
}
if (includes && (Array.isArray(includes) ? includes.some(i => path.includes(i)) : path.includes(includes))) {
return type;
}
}
return 'other';
};
const GTAG_Event = (event, data) => {
try {
data.event_callback = () => {
if (debug.status && debug.callback) console.log(`%c%s ${event} event sent successfully`, debug.color, debug.msg);
};
data.ecomm_pagetype = getPageType();
gtag("event", event, data);
if (debug.status) console.log(`%c%s ${event}`, debug.color, debug.msg, data);
if (debug.dlv) console.log("%c%s dataLayer", debug.color, debug.msg, window.dataLayer);
} catch (error) {
console.error('GTAG_Event error:', error);
}
};
if (debug.status) console.log(`%c%s Checkout Only Customer Events Pixel for ${px_config.GADS.id} initiated. Version: ${version}`, debug.color, debug.msg);
const commonEventHandler = async (event, flag, eventName, getDataFn) => {
try {
if (px_config.flags[flag]) {
// Set user data
const user_data = await getUserData({}, initData);
gtag('set', 'user_data', user_data);
const data = getDataFn(event);
data.user_data = user_data;
if(px_config.GADS?.conversions[eventName]){
GTAG_Event("conversion", {
send_to: `${px_config.GADS?.id}/${px_config.GADS?.conversions[eventName]}`,
...data,
});
if (debug.status) console.log(`%c%s ${eventName} event sent`, debug.color, debug.msg, data);
}
}
} catch (error) {
console.error('commonEventHandler error:', error);
}
};
function isEmptyObject(obj) {
return (
Object.keys(obj).length === 0 ||
Object.values(obj).every(
(value) => value === undefined || value === null || value === ""
)
);
}
function cleanObject(obj) {
return Object.fromEntries(
Object.entries(obj).filter(([_, value]) => value !== undefined && value !== null && value !== "")
);
}
const checkoutEventHandler = async (event, eventName) => {
const checkoutData = event.data.checkout;
if(debug.status && debug.detail) console.log('%c%s checkoutData', debug.color, debug.msg, checkoutData);
const productData = checkoutData.lineItems.map((item) => {
const itemData = getItemObj(item);
return {
...itemData,
quantity: item.quantity || 1,
};
});
// Set user data
const user_data = await getUserData(checkoutData, initData);
gtag('set', 'user_data', user_data);
const allDiscountCodes = checkoutData?.discountApplications
.filter(discount => discount.type === 'DISCOUNT_CODE' || discount.type === 'CODE')
.map(discount => discount.title) || null;
const shippingTiers = checkoutData?.delivery?.selectedDeliveryOptions
.filter(option => option.type === 'shipping')
.map(option => option.title) || null;
const lineItemPrice = Array.isArray(productData) ? productData.reduce((total, item) => {
const price = parseFloat(item?.price || 0);
const quantity = parseInt(item?.quantity || 1, 10);
return total + (price * quantity);
}, 0) : 0;
const subTotal = checkoutData?.subtotalPrice?.amount;
const discount = checkoutData?.discountsAmount?.amount || checkoutData?.discountApplications[0]?.amount || 0;
const coupon = allDiscountCodes.length > 0 ? allDiscountCodes.join(', ') : checkoutData?.discountApplications[0]?.title || null;
const token = checkoutData?.token || null;
let evObj = {
items: productData,
transaction_id: checkoutData?.order?.id || token || null,
payment_type: checkoutData?.transactions?.[0]?.gateway || null,
coupon: coupon,
discount: (coupon == null) ? 0 : (discount > 0) ? discount : lineItemPrice,
value: totalValue_option ? getTotal(checkoutData) : checkoutData?.totalPrice?.amount || 0,
currency: checkoutData?.currencyCode || initCurrency,
shipping: checkoutData?.shippingLine?.price?.amount || 0,
shipping_tier: Array.isArray(shippingTiers) ? shippingTiers.join(', ') : shippingTiers,
tax: checkoutData?.totalTax?.amount || 0,
user_data: user_data,
new_customer: initCustomer?.ordersCount > 0 ? true : false,
};
if(block_items_id){
evObj.ecomm_prodid = evObj?.items.map(item => item?.pid);
evObj.ecomm_totalvalue = evObj?.value;
evObj.ecomm_ordercount = initCustomer?.ordersCount || 0,
evObj.ecomm_category = evObj?.items.map(item => item?.item_category);
}
evObj = cleanObject(evObj); // Clean the object to remove undefined, null, or empty values
if(px_config.GADS?.conversions[eventName] && !isEmptyObject(evObj)){
GTAG_Event("conversion", {
send_to: `${px_config.GADS?.id}/${px_config.GADS?.conversions[eventName]}`,
...evObj,
});
}
};
const logEventDebug = (eventName) => {
if (debug.status) {
console.log(`%c%s all_standard_events -> ${eventName}`, debug.color, debug.msg);
}
}
// Handle checkout_completed event
analytics.subscribe("all_standard_events", (event) => {
try {
const eventHandlers = {
checkout_completed: async () => {
if (px_config.flags.checkout_completed && !oneTimeEventHandlers.checkout_completed) {
oneTimeEventHandlers.checkout_completed = true;
await checkoutEventHandler(event, 'purchase');
}
}
};
if (eventHandlers[event.name]) {
eventHandlers[event.name]();
logEventDebug(event.name);
}
} catch (error) {
console.error('all_standard_events error:', error);
}
});
const oneTimeEventHandlers = {
payment_info_submitted: false,
checkout_completed: false,
};
Make sure that you have copied it correctly.IMPORTANT:
Once again, ONLY copy; don’t paste it yet.
In this step, you’ll need to update specific sections of the code with your unique information. Do not change any other parts of the code. Here are the details:
Let’s quickly double-check your Google Feed item ID format.
Visit Google Ads and click PRODUCTS on the left menu. You will see the merchant ID as marked below – and your item ID format next to that. In our example it starts with “shopify_AU_…” because the native Google Sales Channel is used for product feed on Shopify.
If you are using a different format here, you should adjust our codes accordingly.
After you replace all these values – now it is time to use this code.
We are done! The enhanced conversion data should be coming into Google Ads shortly.
This is a more complex setup and it requires your store to have a set of data layers and a correctly structured Google Tag Manager container to make it work. We will add a separate series of tutorials for this.
For now, we recommend you use the method explained above. If you still want to use Google Tag Manager;
Google Ads Enhanced Conversion is a feature that allows you to share more detailed conversion data with Google Ads, improving the accuracy of your conversion tracking. It functions similarly to the Facebook Conversion API.
In standard Google Ads conversion tracking, data like order ID, revenue, and optionally purchased products are sent to Google.
With Enhanced Conversion Tracking on Shopify, you can also share customer details such as email, name, address, and phone number for better attribution.
Let’s consider an example:
Google’s Explanation:
The feature uses a secure one-way hashing algorithm called SHA256 on your first party customer data, such as email addresses, before sending to Google. The hashed data is then matched with signed-in Google accounts in order to attribute your campaign conversions to ad events, such as clicks or views. Source
Conversion tracking has become significantly hard after the new changes in the world of data tracking such as iOS updates, Safari ITP, and with arise of adblockers. You can’t optimize and measure your advertising spending if you don’t measure them well. So, the answer is yes if you want to count and attribute more conversions.
Note: After activating Google Ads enhanced conversions, don’t forget to import your Shopify customer data into Google Ads.
Setting up Google Ads Enhanced Conversions on Shopify is vital for accurate conversion tracking, especially in the face of recent changes in data tracking. This feature allows you to share comprehensive conversion data, including customer information.
The tutorial offers two methods:
Editing Code Directly: This is a straightforward approach for those comfortable with coding.
Google Tag Manager: A more advanced setup requiring a structured container and data layers.
With Enhanced Conversions, you not only track order details but also capture valuable customer information, enabling more precise attribution of conversions to your advertising efforts.
In a landscape where accurate measurement of advertising ROI is paramount, adopting Google Ads Enhanced Conversions is a vital step toward optimizing your advertising spend.