How to install to setup Shopify purchase tracking on Hydrogen stores with a different domain on the checkout?

Integrating Convert experiments into a Shopify Hydrogen store provides valuable insights into customer behavior and conversion metrics, especially when the store's catalog and checkout are hosted on different top-level domains.

This guide will walk you through setting up Convert tracking to ensure seamless functionality across different domains.

Prerequisites

  • A Shopify Hydrogen store set up and running.
  • Access to your store’s backend and frontend codebase.
  • An active Convert account with experiments ready to deploy.
  • The Convert snippet added to your Hydrogen catalog pages.

Step 1: Client-Side Tracking Code Integration

To handle user interactions with your experiments, you need to integrate the Convert tracking code into your Shopify Hydrogen frontend. This code should be added in the Convert app under your project's configuration settings:

  • Change the endpoint to your own.
  • Navigate to Convert App > Your Project > Configuration > Global Project JS.
  • Paste the following JavaScript in the provided area:
// Client-side JavaScript to handle Convert events and send data to the server
if (typeof convert !== 'undefined') {
    convert._conv_q = convert._conv_q || [];
    convert._conv_q.push({
        what: 'addListener',
        params: {
            event: 'snippet.experiences_evaluated',
            handler: function() {
                let session_cookie = convert.getCookie('_conv_s');
                if (!session_cookie) {
                    console.error('Session cookie not found.');
                    return;
                }

                let session_id = session_cookie.substring(
                    session_cookie.indexOf('sh:') + 3,
                    session_cookie.indexOf('*')
                );

                let exp_list = [];
                let variation_list = [];

                function processExperiences(sourceData, allData, isHistorical = false) {
                    for (let expID in sourceData) {
                        let type = allData.experiences[expID]?.type;
                        if (type === "deploy") {
                            console.log('Skipping deploy type experiment:', expID);
                            continue;
                        }

                        let experience = sourceData[expID];
                        let variation = experience.variation || {};
                        let varID = variation.id || experience.variation_id;

                        if (varID && !exp_list.includes(expID)) {
                            exp_list.push(expID);
                            variation_list.push(varID);
                            console.log(
                                'Adding experiment:',
                                expID,
                                'with variation:',
                                varID,
                                'from',
                                isHistorical ? 'historical data' : 'current data'
                            );
                        }
                    }
                }

                if (convert.currentData && convert.currentData.experiences) {
                    processExperiences(convert.currentData.experiences, convert.data);
                }

                if (convert.historicalData && convert.historicalData.experiences) {
                    processExperiences(convert.historicalData.experiences, convert.data, true);
                }

                function alignSegmentsToFirstFormat(segFromSecondFormat) {
                    const alignedSeg = {
                        browser: segFromSecondFormat.browser,
                        devices: segFromSecondFormat.devices,
                        source: segFromSecondFormat.source,
                        campaign: segFromSecondFormat.campaign,
                        ctry: segFromSecondFormat.country || "",
                        cust: Array.isArray(segFromSecondFormat.customSegments) ? segFromSecondFormat.customSegments : [],
                        new: segFromSecondFormat.visitorType === "new" ? 1 : (segFromSecondFormat.visitorType === "returning" ? 0 : undefined)
                    };
                    return alignedSeg;
                }

                let convert_attributes = {
                    cid: convert.data.account_id,
                    pid: convert.data.project.id,
                    vid: session_id,
                    goals: JSON.stringify(convert.currentData.goals || {}),
                    vars: variation_list,
                    exps: exp_list,
                    defaultSegments: alignSegmentsToFirstFormat(convert.getDefaultSegments()),
                    conversionRate: 1,
                    presentmentCurrency: "USD",
                    currencySymbol: "$",
                    max_order_value: convert.data.project.settings.max_order_value,
                    min_order_value: convert.data.project.settings.min_order_value,
                };

                if (typeof Shopify !== 'undefined' && Shopify.currency && typeof Currency !== 'undefined') {
                    let baseCurrency = Shopify.currency.active;
                    let presentmentCurrency = Shopify.currency.current;
                    let conversionRate = Currency.convert(1, baseCurrency, presentmentCurrency);

                    if (!isNaN(conversionRate) && conversionRate !== 0) {
                        convert_attributes.conversionRate = conversionRate;
                        convert_attributes.presentmentCurrency = presentmentCurrency;
                        convert_attributes.currencySymbol = Currency.symbol;
                    } else {
                        console.error('Invalid conversion rate. Not adding currency information.');
                    }
                }

                // Send the gathered data to the Shopify Headless endpoint
                // Change the following to your own endpoint URL.
                fetch('/api/dir/attributes', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        action: 'UpdateCartAttributes',
                        convertData: convert_attributes
                    })
                })
                .then(response => response.json())
                .then(data => console.log('Convert attributes successfully sent to the server:', data))
                .catch(error => console.error('Failed to send convert attributes:', error));
            }
        }
    });
}

This script ensures that once Convert evaluates experiments, the results are sent to the Hydrogen backend to update cart attributes accordingly.

Step 2: Server-Side Setup in Hydrogen

On the server side, set up an endpoint to receive the posted data and update the cart attributes. For more information on managing cart attributes in Shopify Hydrogen, refer to Shopify's official documentation.

import { json } from '@shopify/hydrogen';
import invariant from 'tiny-invariant';

export async function action({ request, context }) {
const formData = await request.json();
const convertData = JSON.parse(formData.convertData);

// Process and apply the convert data as needed
const result = await context.cart.updateAttributes({
customAttributes: { convertAttributes: formData.convertData }
});

return json(result, { status: 200 });
}

Step 3: Setting Up a Shopify Customer Event Pixel

To further enhance your tracking capabilities, you can set up a Shopify Customer Event Pixel. This will allow you to capture and send custom event data from the checkout process back to Convert, ensuring you can measure the effectiveness of your experiments during the purchase phase.

  1. Obtain and Customize the Custom Event Pixel Code:

    • You will need to customize the code by changing the purchase_goalid to your actual Convert goal ID.
  2. Integrate the Custom Event Pixel into Your Shopify Checkout:

    • Shopify Hydrogen does not directly control the checkout pages since they are handled by Shopify's standard checkout system. However, you can inject custom scripts into your Shopify checkout through the Shopify admin settings if your account level supports it (Shopify Plus).
    • Navigate to your Shopify Admin panel. Under Settings, find Checkout and look for the Order processing section.
    • In the Additional scripts text box, paste the customized JavaScript code. This will execute the script during the checkout process, sending conversion data to Convert.
  3. Testing the Integration:

    • Conduct test purchases to ensure the custom event pixel is correctly firing and that Convert is receiving the event data.
    • Verify the results in your Convert dashboard to ensure that the purchase events are being recorded as expected.
  4. Deploy and Monitor:

    • Once confirmed working in test scenarios, deploy the changes to your live environment.
    • Continuously monitor the performance of the tracking setup to ensure data accuracy and troubleshoot any discrepancies.

Conclusion

Adding a Shopify Customer Event Pixel customized for your Convert experiments allows you to track detailed interactions within the Shopify checkout process. This setup ensures that every aspect of the customer journey, from browsing the catalog to completing a purchase, is meticulously captured and analyzed, giving you comprehensive insights into the effectiveness of your marketing strategies and experiments.