Full Screen Embedding
Embed the Viewer anywhere on your website using an iframe.
Important: embedding requires a session token and a postMessage handshake. Public/private visibility does not affect embedding — the token controls access.
Flow:
- Your frontend loads the iframe.
- Your website and the iframe perform a postMessage handshake.
- The iframe requests a session token from your website.
- Your backend requests the token from Alter Product API and your website sends it back to the iframe.
<!DOCTYPE html>
<html>
<head>
<title>Alter Product - Full Screen Embed</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
* { box-sizing: border-box; }
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
iframe { width: 100%; height: 100%; border: 0; display: block; }
#wrapper { width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="wrapper">
<iframe
id="alter-iframe"
title="Alter Product Tool"
src="https://alterproduct.com/app/viewer/1"
allowfullscreen
></iframe>
</div>
<script>
/**
* ✅ CONFIG
* Tool URL examples:
* - Viewer: https://alterproduct.com/app/viewer/1
*/
const IFRAME_ORIGIN = "https://alterproduct.com";
const iframe = document.getElementById("alter-iframe");
// Keep nonce per handshake (from iframe) to prevent random messages from being accepted.
let handshakeNonce = "";
let handshakeOk = false;
function postToIframe(payload) {
iframe.contentWindow.postMessage(payload, IFRAME_ORIGIN);
}
/**
* 1) Your backend must request the session token.
* This function calls YOUR backend endpoint (recommended),
* which then calls Alter Product API using your server-side credentials.
*/
async function getSessionTokenFromYourBackend({ tool, mode, uiDesignId }) {
const url = new URL("/api/alter/session-token", window.location.origin);
url.searchParams.set("tool", tool); // viewer | configurator | customizer
url.searchParams.set("mode", mode || "design"); // e.g. "design"
url.searchParams.set("uiDesignId", String(uiDesignId || 0));
const res = await fetch(url.toString(), { method: "GET" });
if (!res.ok) throw new Error("Failed to get session token");
const data = await res.json();
// Expect: { sessionToken: "..." }
if (!data || !data.sessionToken) throw new Error("Missing sessionToken");
return data.sessionToken;
}
window.addEventListener("message", async (event) => {
// ✅ 1) Always validate origin
if (event.origin !== IFRAME_ORIGIN) return;
// ✅ 2) Always validate source
if (event.source !== iframe.contentWindow) return;
const msg = event.data || {};
// -----------------------
// A) HANDSHAKE
// iframe -> parent: ALTER_CHILD_HELLO { nonce }
// parent -> iframe: ALTER_PARENT_ACK { nonce }
// -----------------------
if (msg.type === "ALTER_CHILD_HELLO") {
if (!msg.nonce || typeof msg.nonce !== "string") return;
handshakeNonce = msg.nonce;
handshakeOk = true;
postToIframe({ type: "ALTER_PARENT_ACK", nonce: handshakeNonce });
return;
}
// Ignore everything until handshake is done
if (!handshakeOk) return;
// -----------------------
// B) TOKEN REQUEST (on-demand)
// iframe -> parent: ALTER_TOOL_INIT_SESSION
// payload: { tool, mode, uiDesignId }
//
// parent -> iframe: ALTER_TOOL_SESSION_READY
// { token }
// -----------------------
if (msg.type === "ALTER_TOOL_INIT_SESSION") {
try {
const payload = msg.payload || {};
const tool = String(payload.tool || "viewer").toLowerCase();
const mode = String(payload.mode || "design");
const uiDesignId = Number(payload.uiDesignId || 0);
const token = await getSessionTokenFromYourBackend({
tool,
mode,
uiDesignId
});
postToIframe({
type: "ALTER_TOOL_SESSION_READY",
token
});
} catch (e) {
postToIframe({
type: "ALTER_TOOL_SESSION_ERROR",
error: String(e && e.message ? e.message : e)
});
}
return;
}
// -----------------------
// C) OPTIONAL: listen to tool events (examples)
// -----------------------
if (msg.type === "ALTER_TOOL_READY") {
console.log("[Alter] Tool ready:", msg);
return;
}
if (msg.type === "ALTER_VIEWER_ADD_TO_CART") {
console.log("[Alter] Add to cart:", msg.payload);
return;
}
});
</script>
</body>
</html>