-
Notifications
You must be signed in to change notification settings - Fork 0
SDK Integration Guide
Elife transfer widgets are a set of components that allow partners to integrate the Elife booking flow into their own website without a direct Elife API integration. The widgets are fully customizable to match your brand's look and feel.
This guide will walk you through the process of integrating the Elife widgets into your website from scratch.
Before you begin, you will need:
- Your Widget Client Key and Widget Client Secret provided by Elife.
- A website with a client-side JavaScript environment.
- A server-side backend capable of making HTTP requests and performing cryptographic operations (HMAC SHA256).
Your backend server will require the following environment variables to be set in the root of your project.
-
WIDGET_CLIENT_KEY: Your unique client key from Elife. -
WIDGET_CLIENT_SECRET: Your unique client secret from Elife. This must be kept secure on your server and never exposed to the public. -
AUTH_BASE_URL: The base URL for the Elife authentication API.
The integration process involves two main parts: a secure backend endpoint and a frontend integration.
- Backend: You will create a single, secure API endpoint on your server. This endpoint's role is to securely communicate with the Elife authentication service and to generate secure hashes of your widget data. Your Client Secret should never be exposed to the frontend.
- Frontend: You will add the Elife JavaScript SDK to your site. Your frontend code will call your backend endpoint to get the necessary credentials and then use those credentials to initialize and launch the Elife widgets.
This is the most critical part of the integration. You must create an API endpoint on your server that will act as a bridge between your frontend and the Elife services.
Endpoint Responsibilities:
-
Method: It must accept
POSTrequests. -
Input: It should expect a JSON
payloadin the request body. This payload contains the data for the widget you want to display (e.g., currency, products, locale for the search widget). -
Process:
-
Fetch an Authentication Token: Using your Client Key and Secret, it will make a server-to-server request to the Elife authentication service to get a short-lived
auth_token. -
Generate a Payload Hash: It will create a SHA256 HMAC hash of the JSON
payloadit received from your frontend. TheWIDGET_CLIENT_SECRETis used as the key for this hash.
-
Fetch an Authentication Token: Using your Client Key and Secret, it will make a server-to-server request to the Elife authentication service to get a short-lived
-
Output: It must return a JSON response to the frontend containing the
auth_tokenand thepayload_hash.
Here is an example of how to implement this endpoint in a Node.js or Bun environment. The logic can be adapted to any backend language (Python, Ruby, PHP, Java, etc.).
// Example using Node.js 'crypto' module
import * as crypto from "crypto";
// Your endpoint logic (e.g., in Express, Next.js API Route, etc.)
async function handleTokenRequest(req, res) {
// 1. Load your credentials securely from environment variables
const clientKey = process.env.WIDGET_CLIENT_KEY;
const clientSecret = process.env.WIDGET_CLIENT_SECRET;
const authBaseUrl = process.env.AUTH_BASE_URL;
// 2. Get the widget payload from the frontend request
const widgetPayload = req.body;
try {
// 3. Fetch the auth_token from the Elife API
const basicAuthToken = Buffer.from(`${clientKey}:${clientSecret}`).toString('base64');
const authResponse = await fetch(`${authBaseUrl}/oauth/token`, {
method: "POST",
headers: {
"Authorization": `Basic ${basicAuthToken}`,
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
grant_type: "client_credentials",
}),
});
if (!authResponse.ok) {
throw new Error('Failed to fetch auth token');
}
const authData = await authResponse.json();
const authToken = authData.access_token;
// 4. Create the payload hash using your Client Secret
const hmac = crypto.createHmac('sha256', clientSecret);
hmac.update(JSON.stringify(widgetPayload));
const payloadHash = hmac.digest('hex');
// 5. Return the token, and hash to the frontend
res.status(200).json({
auth_token: authToken,
payload_hash: payloadHash,
});
} catch (error) {
console.error("Error in token endpoint:", error);
res.status(500).json({ error: "Failed to prepare widget token" });
}
}Now that your backend is ready, you can integrate the widget into your website's frontend. The flow involves two pages: a search page and a booking page.
Include the following script tag in your HTML files, preferably in the <head> or before your closing </body> tag. The ASSET_URL and VERSION will be provided by Elife.
<script src="{{ASSET_URL}}/bootstrap-{{VERSION}}.iife.js"></script>This page will contain the search widget.
Add a div element to your HTML where you want the search widget to appear.
<div id="wd-elife-search"></div>The following script will fetch credentials from your backend, launch the search widget, and then pass the search results to the booking page via localStorage.
// Ensure this code runs on your search page after it has loaded
async function launchSearch() {
const searchPayload = {
currency: "GBP",
products: ["rides", "trains"],
locale: "en",
};
try {
const response = await fetch('/api/token', { // The path to YOUR endpoint
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(searchPayload),
});
if (!response.ok) {
throw new Error('Failed to get credentials from backend');
}
const credentials = await response.json();
const { auth_token, payload_hash } = credentials;
const elifeInstance = new window.hoppa({
document: window.document,
global: window,
environment: "sandbox", // or "production"
});
elifeInstance.launchSearchWidget({
resource_token: auth_token,
hash: payload_hash,
target_dom_id: "wd-elife-search",
data: searchPayload, // This MUST be identical to the object you hashed
events: function (props) {
console.log('Elife Widget Event:', props);
if (props.status === 'SEARCH_CREATED' && props.data) {
// Store the entire search result data in localStorage
localStorage.setItem('searchResultData', JSON.stringify(props.data));
// Redirect to the booking page
window.location.href = `/booking?currency=${searchPayload.currency}&locale=${searchPayload.locale}`;
}
},
});
} catch (error) {
console.error("Failed to launch Elife search widget:", error);
}
}
// Call the function to start the process
launchSearch();This page will receive the data from the search page and display the booking widget.
Add a div element to your HTML where you want the booking widget to appear.
<div id="wd-elife-book"></div>The following script, placed on your /booking page, reads the data from localStorage and launches the booking widget.
// Ensure this code runs on your booking page after it has loaded
async function launchBooking() {
// 1. Retrieve data passed from the search page
const params = new URLSearchParams(window.location.search);
const currency = params.get('currency');
const locale = params.get('locale');
const searchDataString = localStorage.getItem('searchResultData');
// It's good practice to clear the data immediately after reading it
localStorage.removeItem('searchResultData');
if (!searchDataString || !currency || !locale) {
console.error("Missing booking information from previous page.");
return;
}
// 2. Construct the booking payload using the retrieved search data
const bookingPayload = {
currency: currency,
locale: locale,
agent_ref: "",
payload: JSON.parse(searchDataString), // The search data is nested here
};
try {
// 3. Call your backend again to get a NEW hash for the booking payload
const response = await fetch('/api/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(bookingPayload),
});
if (!response.ok) {
throw new Error('Failed to get credentials for booking widget');
}
const credentials = await response.json();
const { auth_token, payload_hash } = credentials;
// 4. Initialize the SDK and launch the booking widget
const elifeInstance = new window.hoppa({
document: window.document,
global: window,
environment: "sandbox",
});
elifeInstance.launchBookWidget({
resource_token: auth_token,
hash: payload_hash,
target_dom_id: "wd-elife-book",
data: bookingPayload, // This MUST be identical to the object you hashed
events: function(props) {
console.log('Booking Widget Event:', props);
}
});
} catch (error) {
console.error("Failed to launch Elife booking widget:", error);
}
}
// Call the function to start the process
launchBooking();