Configurator UI API
Summary
-
Communicate with the IFrame
3.1 Workflow
3.2 Authentication and authorization
3.3 IFrame configuration
3.4 Product price and availability
3.5 Add to cart
1. Overview
The Configurator UI is the visual gateway that empowers users to personalize and modify products interactively. This dynamic and interactive interface will be loaded within an IFrame to be hosted within an ecommerce page. Communication between the IFrame and the hosting page will take place via an exchange of messages, managed with JavaScript code.
JavaScript provides the capability for communication with an IFrame's content through the use of the postMessage
method. This allows secure messaging across different origins by enabling scripts to programmatically send and receive messages between the parent page (the hosting page) and the embedded IFrame. The postMessage
method facilitates this interaction, ensuring data exchange is conducted safely and efficiently.
The data exchange between the hosting page and IFrame concerns the following fundamental aspects:
- initialize the product configuration
- obtain the correct price and availability of the configurable product
- add the configured product to the shopping cart
The integration of the Zakeke Configurator UI will then basically consist of including the aforementioned IFrame and managing the exchange of messages between the host page and this IFrame.
2. Include the Zakeke IFrame
Add the following HTML tag to include the configurator IFrame to your system:
<iframe id="zakeke-frame" src="https://portal.zakeke.com/Configurator/index.html" allowfullscreen></iframe>
The Zakeke UI is fully responsive, and it's up to you to set the IFrame css styling.
3. Communicate with the IFrame
Other than adding the IFrame tag, your system will have to handle the communication between the Zakeke IFrame and hosting page.
3.1 Workflow
The following diagram illustrates the dialogue between the various components of the integrated system (ecommerce - Zakeke configurator) based on the actions performed by the site visitor.
Let's go through the steps:
- The end-user visit the ecommerce site page.
- The code present on the ecommerce page requests an authentication token from the Zakeke backend to be passed to the IFrame.
- The Zakeke backend authenticates the merchant and issues an authorization token.
- The code present on the ecommerce page send configuration information (setup) to the IFrame.
- Zakeke displays the product to be configured within the IFrame
- The end-user configures the product by choosing from the various possibilities offered by the user interface.
- During the configuration phase, Zakeke requests the information necessary from the e-commerce site to determine the right price for the customized product.
- The ecommerce responds by providing the price and availability of the configured product.
- Zakeke shows the price of the product configured in the user interface inside the IFrame.
- The end user decides to add the configured product to the cart.
- Zakeke provides the ecommerce with an identifier assigned to the configuration.
- The ecommerce redirects the end-user to the cart page.
In the next sections we will analyze in more detail the phases of the dialogue between the IFrame and the hosting page that require implementation on the ecommerce side.
In the following explanation we will refer to a configuration, i.e. the set of values chosen for the various attributes that identify the configurable features of the product, also with the term composition.
3.2 Authentication and authorization
In order to establish secure communication between the hosting page and the configurator IFrame, the first fundamental step is to authenticate with Zakeke and obtain an authorization token.
For every detail go to Authentication and Authorization section)
The authentication must be C2S type and among the information passed to obtain the token there must be the Visitor Code or the Customer Code.
Important: The API call for obtaining an OAuth 2.0 token using Client Credentials should be performed strictly in server-to-server contexts. This approach ensures that client_id
and client_secret
are not exposed to clients or transmitted over less secure channels. Implementing this call from client-side applications or exposing sensitive credentials to the public can lead to serious security vulnerabilities. Always ensure that such API calls and the handling of credentials are securely managed within server-side environments.
3.3 IFrame configuration
To initialize the Zakeke IFrame so that it loads the configurator for the desired product, the ecommerce JavaScript code must send via postMessage
an object with the following structure to the IFrame:
{
"type": "load",
"parameters": {
"integrationVersion": 2,
"name": "Woman handbag",
"token": "tXyHtJgiZK11yUQm7amGwTn7",
"ecommerce": "api",
"currency": "USD",
"culture": "en",
"modelCode": "242422342",
"compositionId": "315-e4848045-91c2-44b2",
"qty": 1,
"attributes": [
[{"id": "34523"},{"id": "537564567"}],
[{"id": "12986"},{"id": "114588521"}],
[{"id": "90871"},{"id": "109771119"}],
],
"customAddToCartButtonText": ""
}
}
where:
Property | Data type | Description |
---|---|---|
type | string | Message type |
properties | object | Message content |
The properties
property is an object with the folling properties:
Property | Data type | Description |
---|---|---|
integrationVersion | integer | The version of the protocol to use for the integration. It must be 2. |
name | string | The name of the product. |
token | string | C2S authorization token with a valid customer code or a visitor code. |
ecommerce | string | Always set to "api". |
currency | string | The three-letter ISO code for the currency is used to format the prices. |
culture | string | The two letter language code (ISO 639-1). |
modelCode | string | The unique identifier for the product to load. For products uploaded via CSV it corresponds to the value of the MasterProductID field |
compositionId | string | Unique identifier of the configuration. Used to change the configuration of a product already configured and added to cart (Optional) |
qty | integer | Number of products that the user has chosen to purchase. |
attributes | array | The list of attributes and respective values to start the configuration with. Each value is an array of two objects, containing respectively the id of the attribute and the id of the option. |
customAddToCartButtonText | string | Alternate text for the "add to cart" button displayed within the IFrame. Optional |
In order to handle cases where you send the message before the Zakeke code inside the iframe loaded, you must continue to post this message until the IFrame post to you a message with type loaded.
It is possible to modify a previously created product configuration ("composition") by passing the property compositionId to the Zakeke IFrame configuration object.
Below is an example of JavaScript code that initializes the IFrame of the Zakeke configurator.
// Retrieve the Zakeke IFrame
const iframe = document.querySelector("#zakeke-frame");
// Function that sends the initialization message to the Zakeke IFrame
function zakekeLoad() {
iframe.contentWindow.postMessage({
type: "load",
parameters: {
"name": "Woman handbag",
"token": "tXyHtJgiZK11yUQm7amGwTn7",
"ecommerce": "api",
"integrationVersion": 2,
"currency": "USD",
"culture": "us",
"modelCode": "242422342",
"qty": 1,
"attributes": [
[{"id": "34523"}, {"id": "537564567"}]
]
}
}, '*');
}
// Repeat the initialization at a timed interval...
const zakekeLoadInterval = setInterval(zakekeLoad, 500);
// ...until we receive the 'loaded' message from the IFrame
window.addEventListener('message', event => {
if (event.origin !== new URL(iframe.src).origin) {
return;
}
if (event.data.zakekeType === 'loaded') {
clearInterval(zakekeLoadInterval);
}
}, false);
3.4 Product price and availability
Each time that a product configuration is changed by a customer, Zakeke will ask to the hosting page about the price and stock availability of that specific product configuration, passing the list of attributes with the respective values chosen and the global unit price (mark-up price) given as the sum of any prices assigned in the back-office for those chosen values.
The monetary values passed in the request by Zakeke are always in the base currency set in the API settings of your Zakeke account. The response value must be converted in the currency indicated by the "currency" property of the IFrame configuration.
Depending on the settings of your system, product, and logged customer, the tax settings and any applicable discount must be calculated for the response value.
Again, communication occurs via message exchange via the postMessage
JavaScript function.
3.4.1 Request message from IFrame
The request message that Zakeke sends to the hosting page has the following structure:
{
"zakekeMessageType": "Price",
"messageId": 2,
"message": {
"compositionPrice": 13.45,
"quantity": 1,
"attributes": [
{
"attributeCode": "{\"id\":\"242422342\",\"label\":\"Color\",\"zakekePlatform\":true}",
"attributeName": "Color",
"attributeIsEnabled": true,
"selectedOptionCode": "{\"id\":\"537564567\",\"label\":\"White\",\"zakekePlatform\":true}",
"selectedOptionName": "White",
"optionIsEnabled": true
}
]
}
}
where:
Property | Data type | Description |
---|---|---|
zakekeMessageType | string | "Price" is used for price requests. |
messageId | integer | The message identifier that must be present in the response. It acts as a correlation id between the request and the response. |
message | object | The message content |
The object specified for the message
property is an object which represents the actual content of the message and which has the following structure:
{
"compositionPrice": 13.45,
"quantity": 1,
"attributes": [
{
"attributeCode": "{\"id\":\"242422342\",\"label\":\"Color\",\"zakekePlatform\":true}",
"attributeName": "Color",
"attributeIsEnabled": true,
"selectedOptionCode": "{\"id\":\"537564567\",\"label\":\"White\",\"zakekePlatform\":true}",
"selectedOptionName": "White",
"optionIsEnabled": true
}
]
}
where:
Property | Data type | Description |
---|---|---|
compositionPrice | money | The sum of all the unit price of the chosen attribute options. The value is always in the base currency. |
quantity | integer | Product quantity to be added to the cart. |
attributes | array | The list of chosen attributes and respective values. |
The attributes
property contains the list of all the attributes provided in the configuration with the respective values selected by the user. Specifically, for each attribute the array will contain an object of the following type:
{
"attributeCode": "{\"id\":\"242422342\",\"label\":\"Color\",\"zakekePlatform\":true}",
"attributeName": "Color",
"attributeIsEnabled": true,
"selectedOptionCode": "{\"id\":\"537564567\",\"label\":\"White\",\"zakekePlatform\":true}",
"selectedOptionName": "White",
"optionIsEnabled": true
}
For attributes that are linked to attributes on the store, the attributeCode
and selectedOptionName
properties each contain a JSON serialized with the sentinel zakekePlatform
property and the id
that the attribute or option has in the ecommerce system.
What is the meaning of the attributeIsEnabled
and optionIsEnabled
properties? When you define the attributes and their related options that will guide the end user in configuring the product in Zakeke, it is also possible to define rules that relate attributes and options to each other in order to indicate that, for example, an attribute makes no sense (it must be disabled) if for another attribute it is a certain option is selected, or that a particular option does not make sense (it should be disabled) if a specific option is selected for another attribute. In the Zakeke back-office we called these rules "links" between attributes and options. Therefore the meaning of the aforementioned attributeIsEnabled
and optionIsEnabled
properties is precisely that of indicating if whether the particular attribute and the particular attribute option are enabled or not based on the "links" defined.
3.4.2 Response message from Hosting Page
The page hosting the Zakeke IFrame must respond to the previous message with a message having the following structure:
{
"zakekeMessageType": "Price",
"messageId": 2,
"message": {
"price": 149.99,
"isOutOfStock": false
}
}
where:
Property | Data type | Description |
---|---|---|
zakekeMessageType | string | "Price" is used for price requests. |
messageId | integer | The message identifier. Must be the same messageId as the request. |
message | object | The message content |
The object specified for the message
property is an object which represents the actual content. The object contains the final price to be shown in the configurator UI and a flag indicating if the selected configuration is out of stock. The price
property must be in the same currency as the "currency" property of the Zakeke IFrame configuration object. If the product can't be sold for the requested attributes and quantity, the message the property isOutOfStock
must be false.
Below is an example of JavaScript code inserted in the page that hosts the IFrame for managing the request to calculate the price of the configured product:
// Handle the price request
function handlePriceRequest(request) {
var iframe = document.querySelector("#zakeke-frame");
// Send the response
iframe.contentWindow.postMessage({
messageId: request.messageId,
zakekeMessageType: "Price",
message: {
price: calculateFinalPrice(request.message.attributes,
request.message.quantity,
request.message.compositionPrice),
isOutOfStock: checkStock(request.message.attributes)
}, "*");
}
}
// Intercept the price request
window.addEventListener("message", function (event) {
if (event.data.zakekeMessageType === "Price") {
handlePriceRequest(event.data);
}
}, false);
3.5 Add to Cart
When the end user confirms that he wants to purchase the configured product, the Zakeke IFrame sends a message to the hosting page containing the composition identifier, a preview image of the product and the requested quantity.
For security reasons, information on the attributes selected by the user and the mark-up price managed by Zakeke is not passed via message to the hosting page, but must necessarily be obtained via a server to server API call (Cart API).
The request message that Zakeke sends to the hosting page has the following structure:
{
"zakekeMessageType": "AddToCart",
"message": {
"composition": "315-e4848045-91c2-44b2",
"preview": "https://....",
"quantity": 1
}
}
The property zakekeMessageType
has the value "AddToCart" and the property message
has the real content of the message with an object of the following structure:
{
"composition": "315-e4848045-91c2-44b2",
"preview": "https://....",
"quantity": 1
}
where:
Property | Data type | Description |
---|---|---|
composition | string | Unique identifier generated by Zakeke for the composition (product configuration). |
preview | string | Url to image preview of the configured product. |
quantity | integer | Product quantity to be added to the cart. |
Below is an example of JavaScript code inserted in the page that hosts the IFrame for handling the "AddToCart" message:
// Handle the AddToCart message
function handleAddToCartRequest(compositionId, previewUrl, quantity) {
// ...
// Invoke code to be executed server side (on the ecommerce system) for calling the Zakeke Cart API (S2S).
// ...
}
// Intercept the price request
window.addEventListener("message", function (event) {
if (event.data.zakekeMessageType === "AddToCart") {
handleAddToCartRequest(event.data.message.composition, event.data.message.preview, event.data.message.quantity);
}
}, false);
9. CONFIGURE button
In the standard Zakeke flow, the Configurator UI is opened from the product page through the 'Configure' call to action. This button becomes visible when a product is configured as configurable in the Zakeke back-office. Here's a summary of the standard flow: Configure > Zakeke UI > Add to Cart > Cart.
It's essential to note that this flow is optional, as long as you implement an alternative system to open the iframe. However, if you choose to implement the 'Configure' button, follow these steps:
9.1 Adding Configure Button
This is usually an HTML button to be placed next to the "ADD TO CART" button:
<button id="btnConfigure" type="button">Configure<button>
Clearly you can use any HTML element as long as it has as btnConfigure identifier.
9.2 HTML FORM
Before closing the body tag, you will need to add the following html FORM:
<form id="frmConfigurator" action="{Customizer URL}">
<input type="hidden" name="quantity" />
<input type="hidden" name="productid" />
<input type="hidden" name="{attributeKey1}" />
..
<input type="hidden" name="{attributeKeyn}" />
</form>
Where:
Attribute | Description |
---|---|
Configurator | URL of the created configurator page (refer to the reference section of the URL documentation). |
quantity | Quantity to add to cart. |
productid | Unique product ID. |
attributeKey | Product attribute key (e.g. "color", "size", etc.). |
N.B. Attribute keys must match those defined in the imported products CSV. Go to how to import products for more details. |
9.3 Javascript to Send the Product Selection to the Configurator
After the HTML form and before closing the body, enter the following script. It allows you to send the product selection to the configurator after clicking the "configure" button:
<script>
var btnConfigurer = document.getElementById('btnConfigure');
btnConfigurer.addEventListener('click',
function(evt) {
evt.preventDefault();
var formCustomizer = document.getElementById("frmConfigurator");
var formProductPage = document.getElementById("{idFormProduct}");
formCustomizer.elements["productid"].value = { productid };
formCustomizer.elements["quantity"].value = formProductPage.elements["quantity"].value;
formCustomizer.elements["attributeKey1"].value = formProductPage.["attributeKey1"].value;
...
formCustomizer.elements["attributeKeyn"].value = formProductPage.elements["attributeKeyn"].value;
formCustomizer.submit();
});
</script>
Where:
Attribute | Description |
---|---|
idFormProduct | FORM tag ID of the product page |
productid | Unique product I |
9.4 Full HTML code Example
Below the sample code of a product page with the "configurator" button integrated:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Custom Shoe</title>
</head>
<body>
<div class="content">
<div class="foto">
<img alt="Customizable T-Shirt" src="http://your.store.com/images/product.jpg" />
</div>
<div class="details">
<h1>Custom Shoe - sku: 00001</h1>
<p>Description product.....</p>
<form id="frmAddToCart" action="https://your.store.com/shopping-cart.html">
<select name="quantity">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
<select name="color">
<option value="0">Black</option>
<option value="1">Red</option>
<option value="2">White</option>
<option value="3">Green</option>
</select>
<select name="size">
<option value="0">38</option>
<option value="1">39</option>
<option value="2">40</option>
<option value="3">41</option>
</select>
<button id="addToCart" type="submit">Add to shopping cart</button>
<button id="btnConfigure" type="button">Configure</button>
</form>
</div>
</div>
<form id="frmConfigurator" action="https://your.store.com/customizer.html">
<input type="hidden" name="sku" />
<input type="hidden" name="quantity" />
<input type="hidden" name="color" />
<input type="hidden" name="size" />
</form>
<script>
var btnConfigurer = document.getElementById('btnConfigure');
btnConfigurer.addEventListener('click',
function(evt)
{
evt.preventDefault();
var formCustomizer = document.getElementById("frmConfigurator");
var formProductPage = document.getElementById("frmAddToCart");
formCustomizer.elements["productid"].value = '00001';
formCustomizer.elements["quantity"].value = formProductPage.elements["quantity"].value;
formCustomizer.elements["color"].value = formProductPage.elements["color"].value;
formCustomizer.elements["size"].value = formProductPage.elements["size"].value;
formCustomizer.submit();
});
</script>
</body>
</html>