Source: track-thank-you-page.js

import { PurchaseListener } from "./listeners/purchase.js";

export default function trackThankYouPage() {
    const isThankYouPage = window.location.pathname.includes('/r/purchase/thanks');
    if (!isThankYouPage) return;

    // Uses the global `rcData` store to get the purchase data
    new PurchaseListener(rcData).attach();

    document.addEventListener("postPurchaseOfferAccepted", withFlowName(handleAcceptOffer));
    document.addEventListener("postPurchaseOfferDeclined", withFlowName(handleDeclineOffer));
    document.addEventListener("postPurchaseUpsellCompleted", withFlowName(completeUpsellFlow));
}

// The prices of each added item, used to calculate the final order value
const acceptedOfferValues = [];

/**
 * Decorator for event handlers that adds the global flow name to the event.
 * @param {function} handler The event handler to decorate
 * @return {function} The decorated event handler
 */
function withFlowName(handler) {
    return function (event) {
        if (![rebuyWidgets, Shopify, offers].every(Boolean)) return null;

        const flowName = getFlowName(rebuyWidgets[Shopify.shop], offers);
        return handler(event, flowName);
    };
}

/**
 * Maps widget names to flow names.
 * Widget names are keys of the global `rebuyWidgets` object.
 * @type {Map<string, string>}
 */
const widgetFlowMap = [
    ["v1RiverRain12Pack", "Rebuy - V1 River Rain Subscription Flow"],
    ["v2RiverRain12Pack", "Rebuy - V2 Blind River Rain Subscription Flow"],
    ["v3RiverRain12Pack", "Rebuy - V3 Cornucopia River Rain Subscription Flow"],
    ["ecoSheets12Pack", "Rebuy - Default Subscription Flow"],
    ["ecoSheets3Pack", "Rebuy - Default Onetime Flow"]
];

// Flows are prefixed "Rebuy - " to differentiate from the new upsell system.
function getFlowName(widgets, offerElements) {
    const offerWidgets = offerElements.map(({ dataset }) => parseInt(dataset.rebuyId, 10));

    for (const [widgetName, flowName] of widgetFlowMap) {
        if (offerWidgets.includes(widgets[widgetName])) return flowName;
    }
    return null;
}

// Gets flow/offer data and pushes it to the data layer
// The value of the accepted offer is stored in acceptedOfferValues
// Triggered by clicking the 'accept' button on a Rebuy offer
function handleAcceptOffer(event, flowName) {
    const { product, widget_id } = event.detail;
    const isUpsellOffer = widget_id == "34601";
    const itemValue = parseFloat(product.selected_variant.price, 10);

    acceptedOfferValues.push(itemValue);
    // if the offer is an upsell, subtract the value of the removed item 
    if (isUpsellOffer) {
        acceptedOfferValues.push(getRemovedItemValue() * -1);
    }

    dataLayer.push({
        'event': 'post_purchase_offer_accepted',
        'flow_name': flowName,
        'offer_view': widget_id,
        'variants': [product.selected_variant.id.toString()],
        'offer_type': isUpsellOffer ? 'upsell' : 'cross-sell',
        'value': itemValue
    });
}

// Gets flow/offer data and pushes it to the data layer
// Triggered by clicking the 'decline' button on a Rebuy offer
function handleDeclineOffer(event, flowName) {
    const { product, widget_id } = event.detail;
    const isUpsellOffer = widget_id == "34601";
    dataLayer.push({
        'event': 'post_purchase_offer_declined',
        'flow_name': flowName,
        'offer_view': widget_id,
        'variants': [product.selected_variant.id.toString()],
        'offer_type': isUpsellOffer ? 'upsell' : 'cross-sell'
    });
}

// Estimates the final order value and pushes it to the data layer
// Triggered when the last offer is accepted or declined
function completeUpsellFlow(_, flowName) {
    dataLayer.push({
        'event': 'post_purchase_upsell_completed',
        'flow_name': flowName,
        'value': getUpsellOrderTotal()
    });
}

function getUpsellOrderTotal() {
    const addedValue = acceptedOfferValues.reduce((a, b) => a + b, 0);
    const initialOrderValue = rcData.cart.total_price;
    return initialOrderValue + addedValue;
}

function getRemovedItemValue() {
    const subItem = rcData.cart.items.find(isNonDonationSubscriptionItem);
    return subItem ? parseFloat(subItem.price, 10) : 0;
}

function isNonDonationSubscriptionItem(item) {
    return item.properties && item.properties.shipping_interval_frequency && item.price > 0;
}