Dynamic Upsell widget
The Dynamic Upsell widget allows you to offer ancillaries within your existing customer journey with a UI built by Gordian. These are some of the benefits of the widget:
- You can add ancillaries onto any flight booking from any ticketing channel.
- You can select which products to display at different points.
- You can save time by leaving the interactive UI development to Gordian.
The following image is an example of how your widget may look. For details on how to customize the appearance of the widget, contact your account manager.
Placing the widget
The Dynamic Upsell widget fits within the customer journey as part of the “Building a trip” step. This means that you must implement the rest of the steps by using the Gordian API directly. See the flights tutorial for more information on using the API.
The following table is a summary of which steps of the customer journey need which implementation:
Step | Implementation | Notes |
---|---|---|
1. Search for flights | API | You can create a new trip from scratch or from a record locator. |
2. Add flight to the basket | API | You can skip this step if you used a record locator. |
3. Add ancillaries | API or Widget | You can select which products to display. |
4. Verify basket items | API or Widget | You must keep track of every item from the widget in your local basket. |
5. Fulfill a trip | API |
Widget implementation
After searching for flights and selecting at least one through the API, you can use the Dynamic Upsell widget to display ancillaries to the customers. To add the widget to your website funnel, add the following tag to the page:
<script src="https://sdk.gordiansoftware.com/javascript/v2.2/gordian.min.js"></script>
Gordian provides the widget through a 5kb SDK. The file is available through a CDN to allow fast loading of your page.
This guide covers the following implementation steps:
Throughout this guide, you can refer to the sandbox example to see the completed backend and frontend integration.
Note
If you fork the sandbox example, add the API_KEY
environment variable in your code sandbox environment. For more information on secrets, see the Codesandbox Secrets documentation.
Initialize the SDK
To initialize the Gordian SDK, call the init function. This function requires:
- The
trip_id
andtrip_access_token
you received from creating a trip. - A callback function into
onBasketChange
. This function helps you keep track of the products the customer has selected and the state of those products. See integrate the basket for more information.
The following code is a sample of the init function:
Gordian.init({
tripId: "c1859ade-6ceb-4a7f-9a08-bf5ff33f1129",
tripAccessToken: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0cl9pZCI6ImMxODU5YWRlLTZjZWItNGE3Zi05YTA4LWJmNWZmMzNmMTEyOSIsImluX2lkIjoiZjM5ZTRkZGQtNGM0Zi00ZTczLWJkOGUtNzBlNjRlZjBhNDdjIiwic2FuZGJveCI6ZmFsc2V9.F3hAHVoHut1Qkpm-wUUkK6b-_DHGMLpJ6CWf9A5ifuLBilwfK52aDVeW8axzwCA2dddKV8oq_Vdo90Z6sv-l_XfVoHsRkSBkSuZsLhj9TZ6WcYElstKr5NJxxUWG6muax_i4ceMTLbWIItr1QUtsSqVGHqrGj46Xvw5hebip-wCnhAjE3-N3rqzM_skDHnJEQwX9OiexrXFXtvwPyhp5UMaDCeuH2j9pw1VnURQVYsv8hBxdyCOWfdiHcwgFzKkSuja2PyYXTk9R1LgJm0KFICEbRer0L31lDYfuxo5_3OK8mnUD0P40xr4g9xoeiQkWumlDJ4fz6crIqlvv1hfj0g",
onBasketChange: onBasketChange
});
Additional Parameters
In addition to the required parameters mentioned above, there are additional optional parameters that can be passed into the init
function.
searchIds
If you've already performed a search for the trip, you can use that specific search by passing in the search_id
into the searchIds
parameter. Without this parameter, a new search will be used.
This is helpful if you've applied any parameters when creating the search that you don't want to include on the client side.
The following is an example for using a pre-existing baggage and seat search:
Gordian.init({
tripId: "c1859ade-6ceb-4a7f-9a08-bf5ff33f1129",
tripAccessToken: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0cl9pZCI6ImMxODU5YWRlLTZjZWItNGE3Zi05YTA4LWJmNWZmMzNmMTEyOSIsImluX2lkIjoiZjM5ZTRkZGQtNGM0Zi00ZTczLWJkOGUtNzBlNjRlZjBhNDdjIiwic2FuZGJveCI6ZmFsc2V9.F3hAHVoHut1Qkpm-wUUkK6b-_DHGMLpJ6CWf9A5ifuLBilwfK52aDVeW8axzwCA2dddKV8oq_Vdo90Z6sv-l_XfVoHsRkSBkSuZsLhj9TZ6WcYElstKr5NJxxUWG6muax_i4ceMTLbWIItr1QUtsSqVGHqrGj46Xvw5hebip-wCnhAjE3-N3rqzM_skDHnJEQwX9OiexrXFXtvwPyhp5UMaDCeuH2j9pw1VnURQVYsv8hBxdyCOWfdiHcwgFzKkSuja2PyYXTk9R1LgJm0KFICEbRer0L31lDYfuxo5_3OK8mnUD0P40xr4g9xoeiQkWumlDJ4fz6crIqlvv1hfj0g",
onBasketChange: onBasketChange,
searchIds: {
bag: '02748ba9-5a86-4321-bfe8-aaedbe172075',
seat: '02748ba9-5a86-4321-bfe8-aaedbe172075',
}
});
eventCallbacks
You may want to attach callbacks to the SDK to be notified of changes to the widgets state. We provide access to the
following optional callbacks:
- onSeatLoad
- onSeatModalClosed
- onBagLoad
- onBagModalClosed
- onSeatFail
- onBagFail
These callbacks may be attached in the Gordian.init
function, for example,
Gordian.init({
tripId: "c1859ade-6ceb-4a7f-9a08-bf5ff33f1129",
tripAccessToken: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0cl9pZCI6ImMxODU5YWRlLTZjZWItNGE3Zi05YTA4LWJmNWZmMzNmMTEyOSIsImluX2lkIjoiZjM5ZTRkZGQtNGM0Zi00ZTczLWJkOGUtNzBlNjRlZjBhNDdjIiwic2FuZGJveCI6ZmFsc2V9.F3hAHVoHut1Qkpm-wUUkK6b-_DHGMLpJ6CWf9A5ifuLBilwfK52aDVeW8axzwCA2dddKV8oq_Vdo90Z6sv-l_XfVoHsRkSBkSuZsLhj9TZ6WcYElstKr5NJxxUWG6muax_i4ceMTLbWIItr1QUtsSqVGHqrGj46Xvw5hebip-wCnhAjE3-N3rqzM_skDHnJEQwX9OiexrXFXtvwPyhp5UMaDCeuH2j9pw1VnURQVYsv8hBxdyCOWfdiHcwgFzKkSuja2PyYXTk9R1LgJm0KFICEbRer0L31lDYfuxo5_3OK8mnUD0P40xr4g9xoeiQkWumlDJ4fz6crIqlvv1hfj0g",
onBasketChange: onBasketChange,
eventCallbacks: {
onSeatLoad: () => { console.log("Foo") },
onSeatFail: () => { console.log("Bar") },
}
})
Display Ancillaries
Once the SDK is initialized, you can display products to the customers. To display products, use the showUpsell
function. The following is a sample code snippet:
Gordian.showUpsell({
container: document.getElementById("upsell-container"),
display: "card", // card | embedded | modal
allowProducts: ["seats"], //"bags", "fare_family_upsell", "priority_boading"...
excludeProducts: [""],
theme: {
textPrimary: '#002244',
},
})
The showUpsell
function has the following inputs:
container
: The element ID of the container you want to display the widget within.display
: (Available forseats
) This controls how the product is presented to the customer.allowProducts
: The product type you want to display. For example, useallowProducts:["seats"]
to display only seats.excludeProducts
: The product type you do not want to display. For example, useexcludeProducts:["seats"]
to display all products but seats.theme
: Parts of the seatmap and baggage selection visualization can be configued through thetheme
parameter. More information on theming can be found here.
For more information about display types, see the product specific guides.
Using multiple display types
Each instance of the Dynamic Upsell widget can only have a single display type. To display two product types in two different displays, add two instances of the Dynamic Upsell widget on your page with different allowed and excluded products.
Integrate the basket
Now that your customers are able to view ancillaries, you need to keep track of the products the customer wants to purchase.
Attention
Any product that the customer selects must be added to your checkout basket and charged with your company as the merchant of record. Therefore, it is critical that you actively track which products are added or removed from the widget.
Whenever the basket has anything added or removed, or if an item in the basket expires, the widget calls the onBasketChange
function. The following is a sample of this function:
const onBasketChange = ({ basket }) => {
var basketBody = document.getElementById("basket-body");
basketBody.innerHTML = "";
for (var key in basket) {
const product = basket[key];
var row = document.createElement("tr");
row.classList.remove("invalid");
var productCell = document.createElement("td");
var priceCell = document.createElement("td");
productCell.innerText = product.display_name;
priceCell.innerText = window.Gordian.formatPrice(
product.price.total.amount,
product.price.total.decimal_places,
product.price.total.currency
);
var message = document.createElement("message");
product.validity = product.validity;
if (product.validity.state === "valid") {
} else if (product.validity.state === "checking") {
row.classList.add("checking");
message.innerText = "Checking if this product is still available...";
} else if (product.validity.state === "price_changed") {
row.classList.add("price_changed");
message.innerText = "The price for this product has changed!";
} else if (product.validity.state === "unavailable") {
row.classList.add("unavailable");
message.innerText = "Sorry, this product is no longer available";
}
priceCell.appendChild(message);
row.appendChild(productCell);
row.appendChild(priceCell);
basketBody.appendChild(row);
}
The single parameter to onBasketChange
is a JSON representation of the whole customer trip. When you receive the JSON object, update your local basket with this new set by replacing all the products previously selected.
The basket includes a lot of granularity on each product. At a minimum, include the display_name
and the price
of each product in your basket for the customer's reference. The function Gordian.formatPrice
helps you render the price for your user.
Products can become invalid because of changed availability or because of new passenger information, such as the age of the passenger or loyalty program information. When the validity changes, the onBasketChanged
function retrieves different values for the validity
field. You must check the new value to decide if there are actions needed. The possible values are the following:
Validity value | Description | Action |
---|---|---|
valid |
This product is still valid. | N/A |
checking |
Checking is in progress. | Show a spinner to the user. |
price_changed |
The price changed. | Show the price change to the user and allow them to accept or reject it. |
unavailable |
The product is not available. | Inform the user, remove the product from the basket and call the showUpsell function to select an alternative. |