1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1" />
6 <title>Alter Product Customizer — postMessage (Handshake + Token)</title>
7 <style>
8 body { font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; margin: 24px; }
9 iframe { width: 100%; height: 720px; border: 0; display: block; border-radius: 12px; }
10 pre { background: #0b1020; color: #d7e1ff; padding: 12px; border-radius: 12px; overflow:auto; }
11 </style>
12</head>
13<body>
14 <h1>My E-commerce</h1>
15
16 <iframe
17 id="customizerWidget"
18 src="https://alterproduct.com/app/customizer/1"
19 title="Alter Product Customizer"
20 allowfullscreen>
21 </iframe>
22
23 <h3>Logs</h3>
24 <pre id="log"></pre>
25
26 <script>
27 const IFRAME_ORIGIN = 'https://alterproduct.com';
28 const iframe = document.getElementById('customizerWidget');
29 const logEl = document.getElementById('log');
30
31 let handshakeOk = false;
32
33 function log(...args) {
34 const line = args.map(a => (typeof a === 'string' ? a : JSON.stringify(a, null, 2))).join(' ');
35 logEl.textContent += line + '\n';
36 }
37
38 function postToIframe(payload) {
39 iframe.contentWindow.postMessage(payload, IFRAME_ORIGIN);
40 }
41
42 4546
47 async function getCustomizerSessionFromYourBackend(payload) {
48 const res = await fetch('/api/alter/customizer-session', {
49 method: 'POST',
50 headers: { 'Content-Type': 'application/json' },
51 body: JSON.stringify(payload || {}),
52 });
53
54 if (!res.ok) throw new Error('Failed to create customizer session');
55
56 const data = await res.json();
57 if (!data || !data.sessionToken || !data.cartKey) {
58 throw new Error('Missing sessionToken or cartKey');
59 }
60
61 return data;
62 }
63
64 window.addEventListener('message', async (event) => {
65 // ✅ 1) Validate origin
66 if (event.origin !== IFRAME_ORIGIN) return;
67
68 // ✅ 2) Validate source
69 if (event.source !== iframe.contentWindow) return;
70
71 const msg = event.data || {};
72 if (!msg.type || typeof msg.type !== 'string') return;
73
74 // -----------------------
75 // A) HANDSHAKE
76 // Customizer -> Host: ALTER_CHILD_HELLO { nonce }
77 // Host -> Customizer: ALTER_PARENT_ACK { nonce }
78 // -----------------------
79 if (msg.type === 'ALTER_CHILD_HELLO') {
80 const nonce = msg.nonce;
81 if (!nonce || typeof nonce !== 'string') return;
82
83 handshakeOk = true;
84 log('[Customizer] -> Host: ALTER_CHILD_HELLO', { nonce });
85
86 log('[Host] -> Customizer: ALTER_PARENT_ACK');
87 postToIframe({ type: 'ALTER_PARENT_ACK', nonce });
88 return;
89 }
90
91 // Ignore everything until handshake is done
92 if (!handshakeOk) return;
93
94 // -----------------------
95 // B) CUSTOMIZER SESSION INIT
96 // Customizer -> Host: ALTER_CUSTOMIZER_INIT_SESSION { payload: { alterProductId, uiDesignId, mode } }
97 // Host -> Customizer: ALTER_CUSTOMIZER_SESSION_READY { token, cartKey, mode }
98 // -----------------------
99 if (msg.type === 'ALTER_CUSTOMIZER_INIT_SESSION') {
100 try {
101 const payload = msg.payload || {};
102 const alterProductId = Number(payload.alterProductId || 0);
103 const uiDesignId = Number(payload.uiDesignId || 0);
104 const mode = payload.mode === 'edit' ? 'edit' : 'view';
105
106 log('[Customizer] -> Host: ALTER_CUSTOMIZER_INIT_SESSION', {
107 alterProductId,
108 uiDesignId,
109 mode,
110 });
111
112 const session = await getCustomizerSessionFromYourBackend({
113 alterProductId,
114 uiDesignId,
115 mode,
116 });
117
118 log('[Host] -> Customizer: ALTER_CUSTOMIZER_SESSION_READY');
119 postToIframe({
120 type: 'ALTER_CUSTOMIZER_SESSION_READY',
121 token: session.sessionToken,
122 cartKey: session.cartKey,
123 mode: session.mode || mode,
124 });
125 } catch (e) {
126 log('[Host] -> Customizer: ALTER_CUSTOMIZER_SESSION_ERROR', String(e && e.message ? e.message : e));
127 postToIframe({
128 type: 'ALTER_CUSTOMIZER_SESSION_ERROR',
129 message: String(e && e.message ? e.message : e),
130 });
131 }
132 return;
133 }
134
135 // -----------------------
136 // C) Customizer events
137 // -----------------------
138 if (msg.type === 'ALTER_CUSTOMIZER_ADD_TO_CART') {
139 log('[Customizer] -> Host: ALTER_CUSTOMIZER_ADD_TO_CART', msg.payload);
140 // Send payload to your backend and add item to cart in your e-commerce system
141 return;
142 }
143
144 if (msg.type === 'ALTER_CUSTOMIZER_UPDATE_DESIGN') {
145 log('[Customizer] -> Host: ALTER_CUSTOMIZER_UPDATE_DESIGN', msg.payload);
146 // Update an existing cart line/order in your e-commerce system
147 return;
148 }
149 });
150 </script>
151</body>
152</html>
{
"id": 481,
"customizerId": 10,
"userDesignId": 10,
"isSeenByOwner": false,
"orderStatus": "shopping_cart",
"createdAt": "2026-05-28T10:15:00.000Z",
"customizerOrderURL": "https://alterproduct.com/app/customizer/10/481",
"customizerURL": "https://alterproduct.com/app/customizer/10",
"customizerName": "Mug 450ml (15oz)",
"totalPrice": {
"value": 24.99,
"currency": "EUR"
},
"productGroup": {
"id": 1,
"name": {
"pl": "Kubek 450ml (15oz)",
"en": "Mug 450ml (15oz)"
}
},
"productItems": [
{
"id": 901,
"model3d": {
"id": 3
},
"size": {
"id": 9,
"name": {
"pl": "450ml (15oz)",
"en": "450ml (15oz)"
},
"measureSize": {
"D": 8.65,
"H": 11.95
},
"externalMapping": {
"attribute": {
"internalId": 123,
"slug": "pa_size",
"label": "Size"
},
"term": {
"internalId": 456,
"slug": "450ml-15oz",
"label": "450ml (15oz)"
}
}
},
"material": {
"id": 3,
"name": {
"pl": "Ceramika",
"en": "Ceramic"
},
"externalMapping": {
"attribute": {
"internalId": 124,
"slug": "pa_material",
"label": "Material"
},
"term": {
"internalId": 457,
"slug": "ceramic",
"label": "Ceramic"
}
}
},
"printType": {
"id": 5,
"name": {
"pl": "Sublimacja",
"en": "Sublimation"
},
"externalMapping": {
"attribute": {
"internalId": 125,
"slug": "pa_printing-method",
"label": "Printing method"
},
"term": {
"internalId": 458,
"slug": "sublimation",
"label": "Sublimation"
}
}
},
"printingMethod": {
"id": 5,
"name": {
"pl": "Sublimacja",
"en": "Sublimation"
},
"externalMapping": {
"attribute": {
"internalId": 125,
"slug": "pa_printing-method",
"label": "Printing method"
},
"term": {
"internalId": 458,
"slug": "sublimation",
"label": "Sublimation"
}
}
},
"color": {
"id": 11,
"name": {
"pl": "Domyślny",
"en": "Default"
},
"hex": "#FFFFFF",
"customColor": false,
"pickedColors": {},
"patternId": null,
"externalMapping": {
"attribute": {
"internalId": 126,
"slug": "pa_color",
"label": "Color"
},
"term": {
"internalId": 459,
"slug": "default",
"label": "Default"
}
}
},
"variant": {
"id": 11,
"productGroupId": 1,
"productModel3dId": 3,
"sizeId": 9,
"materialId": 3,
"printingMethodId": 5,
"colorId": 11,
"metadata": {
"sku": "MUG-450-WHITE"
},
"minOrderQuantity": 1,
"processingTime": null,
"stockQuantity": null,
"volume": null,
"weight": null
},
"unitPrice": {
"value": 24.99,
"currency": "EUR"
},
"totalPrice": {
"value": 24.99,
"currency": "EUR"
},
"quantity": 1,
"mockups": [
{
"productModel3dId": 3,
"img": {
"thumb": "https://api.example.com/file/protected/user_1/10/e-commerce/orders/481/mockup-thumb.webp",
"large": "https://api.example.com/file/protected/user_1/10/e-commerce/orders/481/mockup-large.webp"
}
}
],
"updatedAt": "2026-05-28T10:15:10.000Z"
}
],
"cartKey": "wc_cart_item_key"
}