Skip to content

SDK Integration Guide

Akinduko Akinwumi edited this page Nov 17, 2025 · 1 revision

H5 Widget Integration Guide

Introduction

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.

Prerequisites

Before you begin, you will need:

  1. Your Widget Client Key and Widget Client Secret provided by Elife.
  2. A website with a client-side JavaScript environment.
  3. A server-side backend capable of making HTTP requests and performing cryptographic operations (HMAC SHA256).

Environment Variables

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.

Integration Overview

The integration process involves two main parts: a secure backend endpoint and a frontend integration.

  1. 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.
  2. 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.

Step 1: Create a Secure Backend Endpoint

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 POST requests.
  • Input: It should expect a JSON payload in 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:
    1. 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.
    2. Generate a Payload Hash: It will create a SHA256 HMAC hash of the JSON payload it received from your frontend. The WIDGET_CLIENT_SECRET is used as the key for this hash.
  • Output: It must return a JSON response to the frontend containing the auth_token and the payload_hash.

Example Implementation (Node.js / Bun)

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" });
    }
}

Step 2: Integrate the Widget on Your Frontend

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.

1. Add the Elife SDK Script

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>

2. Create the Search Page

This page will contain the search widget.

Container Element

Add a div element to your HTML where you want the search widget to appear.

<div id="wd-elife-search"></div>

Launching the Search Widget

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();

3. Create the Booking Page

This page will receive the data from the search page and display the booking widget.

Container Element

Add a div element to your HTML where you want the booking widget to appear.

<div id="wd-elife-book"></div>

Launching the Booking Widget

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();