Skip to main content
Version: v1

Post Purchase Extension

Beam supports checkout extensibility via a custom app that includes the Beam post purchase extension. The post purchase extension can be installed/updated by Beam integration engineers for partners with checkout extensibility enabled.

This extension is functionally identical to the Beam post purchase widget with a few exceptions:

  • Social share redirects to a separate page instead of a modal
  • Statsig needs to be enabled using a Shopify custom pixel
  • Remote config not available due to CSS restrictions

More on extension limitations

Settings

When customizing the 'Thank You' or 'Order Status' page, clicking on the Beam extension opens up the settings that allow users to configure Beam extensions.

extensibility_settings.png

NOTE: Ensure that any settings made are applied to both pages as the values are maintained separately.

AB Testing

AB Testing can be conducted against a store with checkout extensibility enabled using a custom pixel that enables Statsig functionality in the post purchase extension using Shopify analytics events.

The Statsig custom pixel can be installed by a Beam integration engineer during AB test setup to ensure proper order/redemption tracking.

  1. Open your Shopify store

  2. Click on the Settings tab on the left navigation bar

  3. Click on Customer Events

    customer_events.png

  4. Click on Add custom pixel. If prompted to name the pixel, call it "Beam Statsig"

    custom_pixel_add_button.png

  5. In the next screen, select "Not required" under the Permission dropdown and "Data collected qualifies as data sale" under the Data sale dropdown

    custom_pixel_permissions.png

  6. Paste the following code block into the Code section. The Beam team will provide you with the Statsig API key, which should be pasted into the STATSIG_API_KEY field in the code.

const STATSIG_API_KEY = "STATSIG_API_KEY";
const STATSIG_VERSION = 4;
const STATSIG_SCRIPT_URL = `https://cdn.jsdelivr.net/npm/statsig-js@${STATSIG_VERSION}/build/statsig-prod-web-sdk.min.js`;
const SHOPIFY_CURRENCY_SCRIPT_URL = `/services/javascripts/currencies.js`;

// Shopify currency conversions JS library
const currencyScript = document.createElement("script");
currencyScript.src = SHOPIFY_CURRENCY_SCRIPT_URL;
document.head.append(currencyScript);

// Statsig JS Library
const statsigScript = document.createElement("script");
statsigScript.src = STATSIG_SCRIPT_URL;
statsigScript.setAttribute("async", "");
document.head.append(statsigScript);

function waitFor(predicate, timeout) {
return new Promise((resolve, reject) => {
let running = true;

const check = async () => {
const res = await predicate();
if (res) return resolve(res);
if (running) setTimeout(check, 100);
};
check();
if (!timeout) return;
setTimeout(() => {
running = false;
reject();
}, timeout);
});
}

async function createStatsigClient(stableId) {
const statsigOptions = {
disableErrorLogging: true,
disableAutoMetricsLogging: true,
disableCurrentPageLogging: true,
loggingIntervalMillis: 1000,
loggingBufferMaxSize: 2,
overrideStableID: stableId ?? null,
};

try {
await waitFor(() => window.hasOwnProperty("statsig"), 5000);
await window.statsig?.initialize(STATSIG_API_KEY, {}, statsigOptions);
} catch (err) {
console.debug("🌈 Beam", "timed out waiting for Statsig to load");
}
}

function getRecurringLineItems(lineItems) {
let recurringLineItems = [];
lineItems.forEach((lineItem) => {
if (lineItem?.sellingPlanAllocation?.sellingPlan?.id) {
recurringLineItems.push(lineItem);
}
});
return recurringLineItems;
}

function convertCurrencyAmountToCurrencyCode(amount, fromCode, toCode) {
if (fromCode === toCode) {
return {
amount: parseFloat(amount),
currencyCode: fromCode,
};
}

try {
let convertedAmount = parseFloat(window.Currency.convert(amount, fromCode, toCode)?.toFixed(2));
return !isNaN(parseFloat(convertedAmount)) ? { amount: convertedAmount, currencyCode: toCode } : { amount: parseFloat(amount), currencyCode: fromCode };
} catch (error) {
console.error(
"🌈 Beam",
`error converting currency ${fromCode} to ${toCode}`,
error
);
return { amount: parseFloat(amount), currencyCode: fromCode };
}
}

async function logOrderEvent(event) {
const { currencyCode, subtotalPrice, attributes, order, lineItems } =
event.data.checkout;
const localCartTotal = subtotalPrice?.amount;
const standardizedOrderAmount = convertCurrencyAmountToCurrencyCode(
localCartTotal,
currencyCode,
"USD"
);
const standardizedCartTotal = standardizedOrderAmount?.amount;
const recurringLineItems = getRecurringLineItems(lineItems);
const beamAttribute = attributes.find((attr) => attr.key === "beam");
const beamCheckoutAttribute = attributes.find(
(attr) => attr.key === "beam_checkout_id"
);
const beamAttributeParsed =
beamAttribute && beamAttribute.value ? JSON.parse(beamAttribute.value) : {};
await createStatsigClient(beamAttributeParsed.remote_session_id);
const checkoutCompletedEventDetails = {
currencyCode,
localCartTotal,
cartTotal: standardizedCartTotal,
orderId: order.id,
hasCart: !!beamAttributeParsed.beam_cart_id || !!beamCheckoutAttribute,
hasSubscription: recurringLineItems.length > 0,
};
window.statsig?.logEvent(
"checkout_completed",
standardizedCartTotal,
checkoutCompletedEventDetails
);
console.debug("🌈 Beam", "checkout_completed", checkoutCompletedEventDetails);
}

// omit this block for integrations that leverage serverside Statsig
analytics.subscribe("checkout_completed", async (event) => {
logOrderEvent(event);
});
  1. Click on Save on top of page (you may need to select another modal to get the Save menu to pop up)

  2. Click on Connect on the bottom right side of the page