Skip to main content

Checkout Tracking and PCI DSS Compliance

D
Written by Damian Dawber
Updated over 2 weeks ago

Implementing third-party scripts on checkout pages may have additional compliance and security requirements, e.g. PCI DSS requirements.

Our iframe-based script solves this by isolating third-party JavaScript inside a fully sandboxed iframe.

Because the code runs within the iframe, it is separated from the main checkout page and never gains direct access to sensitive payment data.

This containment significantly reduces risk while preserving flexibility, allowing teams to add analytics, personalisation, or optimisation scripts without compromising security.

The result is a PCI DSS–compliant approach to checkout extensibility: powerful integrations, minimal attack surface, and peace of mind for merchants handling cardholder data.

Replace the ingestion ID on line 5 with your own ingestion ID and run this script once on each checkout page. It will send checkout page view events so we know where users are in the buying funnel.

(function () {
// ------------------------
// Your Intent Ingestion ID
// ------------------------
const ingestionId = "mwit-aftjkxvbbbbb-xx";

// ------------------------
// Create a sandboxed iframe in the parent window
// - allow-scripts allows script execution in the context of the frame only
// - the iframe is not able to talk to the parent window
// - embed in page source markup, or inject as below (e.g. via GTM)
// ------------------------
document.body.insertAdjacentHTML(
"beforeend",
`
<iframe id="intent-iframe" width="1" height="1" style="display: none;"
sandbox="allow-scripts"
srcdoc="
<!DOCTYPE html>
<html>
<body>
<script>
window.addEventListener('message', e => {
if (e.data?.type !== 'SET_CONTEXT' || !e.data.userId) return;

const ctx = e.data.context;
const createStore = () => {
let store = {};
return {
getItem: k => store.hasOwnProperty(k) ? store[k] : null,
setItem: (k, v) => store[k] = String(v),
removeItem: k => delete store[k],
clear: () => store = {},
_getStore: () => ({ ...store })
};
};

ctx.window.localStorage = createStore();
ctx.window.sessionStorage = createStore();
ctx.window.localStorage.setItem('intent.user.id', e.data.userId);

window.intent = window.intent || {};
window.intent.dataLayer = window.intent.dataLayer || [];
window.intent.config = {
contextOverride: ctx,
ingestionId: '${ingestionId}',
iso31661CountryCode: 'gb',
iso6391LanguageCode: 'en'
};

const s = document.createElement('script');
s.src = 'https://intentclientscriptslon.s3.eu-west-2.amazonaws.com/intent-v3.js';
s.async = true;
document.body.appendChild(s);

window.intent.dataLayer.push({ type: 'Pageview', data: { page_type: 'checkout' } });
});
</script>
</body>
</html>
">
</iframe>
`
);

// ------------------------
// Pass limited (safe) context variables from the parent window
// - Evaluate document, navigator and window props
// - This should be evaluated once on page load on each checkout page
// and will in turn dispatch a checkout page_view to the Intent endpoint
// ------------------------
const iframe = document.getElementById("intent-iframe");

iframe.onload = () => {
const context = {
document: {
cookie: "",
referrer: document.referrer,
title: document.title,
location: {
href: document.location.href,
hostname: document.location.hostname,
pathname: document.location.pathname,
},
},
navigator: {
userAgent: navigator.userAgent,
language: navigator.language,
platform: navigator.platform,
},
window: {
innerWidth: window.innerWidth,
innerHeight: window.innerHeight,
location: {
href: window.location.href,
},
},
};

const getCookie = (name) => {
const match = document.cookie.match(
new RegExp("(?:^|; )" + name + "=([^;]*)")
);
return match ? match[1] : null;
};

iframe.contentWindow.postMessage(
{
type: "SET_CONTEXT",
context,
userId:
getCookie("intent.user.id") ||
localStorage.getItem("intent.user.id") ||
null,
},
"*"
);
};
})();
Did this answer your question?