Widget Personalizado E-commerce (BOOKING) - Guía para Desarrollador

Introducción y Conceptos

A lo largo de este instructivo, el equipo ténico del cliente podrá crear sus propios widgets para embeber en su página web.

Este documento es una guía para el desarrollador web, que explica como obtener y conectar el Widget desarrollado con el motor de reservas. 

Esta guía se adentra en como crear el widget, detallando los servicios y campos que debe tener en cuenta para poder armar la herramienta y lograr realizar las búsquedas a desplegarse luego en la siguiente spa, según el flujo seleccionado.

La pantalla de booking, es configurable, ya que en esta convergen distintos diversos flujos, listados a continuación:

Seach flight: Dedicado únicamente para la búsqueda de vuelos y generado de reservas.

Manage my booking: Usado para recuperar la reserva previamente generada, desde ecommerce-searchflight, o cualquier otro método, revisar estado de la misma y realizar modificaciones.

Web checkin: Dedicado a la gestión del checkin de una reserva próxima a efectuar el viaje.

Otros formularios embebidos: Servicios foráneos, como reservas de hoteles, alquiler de autos, etc.
Para habilitar esta opción, se necesita enrolar la aplicación en cuestión al listado de aplicaciones pertenecientes al cliente.

Que es un widget? 

Es una aplicación pequeña que se incluye en la web del cliente, con el objetivo de ampliar la funcionalidad que ofrece E-COMMERCE booking.

Ofrece KIU un widget? 

Si. KIU provee de un widget integrable de forma embebida mediante un iframe cuando el producto E-COMMERCE es creado, pero, posiblemente este no contempla todos los aspectos que requiere el cliente por lo tanto, este puede desarrollar su propio widget y conectarse con E-COMMERCE, con la ayuda de nuestros servicios API REST, los cuales proveen todos los datos necesarios para armar un formulario personalizado y garantizar la compatibilidad con el resto del flujo de reserva.

Embebido de Widget via Iframe

Url para usar iframe

Esta url, es para ser usada en caso de preferir utilizar el widget que Kiu proporciona, dentro de un iframe en la pagina web del cliente.

https://ecommerce-xx-stage.kiusys.net/booking/widget/

De usarse esta opción, solo se necesita agregar esta url a un iframe, como el siguiente ejemplo: <iframe src="https://ecommerce-xx-stage.kiusys.net/booking/widget/"</iframe>

El widget embebido, gracias a su caracteristica full responsive, puede ser incluido de forma vertical u horizontal, según las medidas con las que se crea el iframe.

Widget personalizado

Formulario desarrollado totalmente por el cliente. Suponiendo que el desarrollador crea el formulario en HTML (por ejemplo), para generar el widget, hay ciertos campos mandatorios, que deben existir para poder enviar la información mínima necesaria a E-COMMERCE flightresults o pantalla que corresponda según el flujo, y de esta manera obtener resultados.

Consideraciones generales en cuanto al desarrollo del widget personalizado

E-COMMERCE, es una aplicación diseñada bajo la arquitectura cliente-servidor, en donde el cliente está desarrollado bajo el paradigma de Simple Web Application (SPA), que en resumen, cada paso del flujo, es independiente entre el resto, y obviamente es independiente del servidor backend, en donde se mantiene la lógica de negocio, que proveen de las configuraciones. Esta arquitectura, brinda al cliente, la posibilidad de desarrollar en cualquier tecnología, con cualquier patrón de diseño, solo debe contemplar que debe mantener comunicación con nuestro servidor, quien provee servicios para compartir información.

El Widget debe contemplar y obtener el contenido en base a la configuración de la brindada por kiu, con el objetivo de realizar las búsquedas de acuerdo a los requerimientos comerciales del cliente. Esta información reside en el Backoffice y debe estar completamente configurado al momento de desarrollar e implementar el widget.
En esta guía se explica como conectarse y obtener el contenido a desplegar de acuerdo a la configuración mencionada. 

Url general para implementar widget personalizado

La url a utilizar para poder consumir de los servicios necesarios para poder implementar y utilizar el widget personalizado, para por ejemplo, carrier XX y ambiente STAGE:

https://ecommerce-xx-stage.kiusys.net/

Servicios

Get Applications

E-COMMERCE, es un sistema compuesto por componentes, reutlizables entre si, sin embargo, se dividen en aplicaciones, por ejemplo, searchflight, manage my booking y webcheckin. Las aplicaciones se pueden configurar en cuanto al flujo, o simplemente, si se quiere habilitar, o no.
Por tal razón, es importante configurar las aplicaciones en el backoffice, y tener muy presente, que ante cualquier cosa, al momento de implementar el widget, necesitamos obtener el listado de aplicaciones disponibles. Para ello se hace uso de este servicio.

Url:

https://ecommerce-xx-stage.kiusys.net/searchflight/api/v1/applications/xx

Implementación:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let headers = { 'Content-Type': 'application/json', 'accept': 'application/json', 'carrier': 'xx', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'referer': 'https://ecommerce-xx-stage.kiusys.net/searchflight/', 'authority': 'ecommerce-xx-stage.kiusys.net' } var url = 'https://ecommerce-xx-stage.kiusys.net/searchflight/api/v1/applications/xx' fetch(url, { method: 'GET', headers: headers } ).then(res => res.json()) .catch(error => console.error('Error:', error)) .then( response => handleAppsResponse(response) )

 

Respuesta:

1 2 3 4 5 6 7 8 9 10 11 { "kiu_internals":{ "duration":3.25775146484375, "id_tracking":"backendsearchflight.5b8e970fa8d1_04245c28f792435f92d1f513d4824d41" }, "response":[ "ecommerce", "manage", "webcheckin" ] }

Diccionario de respuesta:

Response: No es mas que el listado de aplicaciones o módulos dados de alta en el backoffice.

Get Flow

Es el segundo servicio a utilizar, su función es obtener la session key y la configuración del flujo que debe seguir la aplicación seleccionada. Es importante que se implemente, ya que con la sessión key es que podremos emitir el request hacia …/configs en la siguiente pantalla. Adicionalmente, con el flow, sabremos a que pantalla vamos a renderizar, una vez enviemos los datos del formulario. Por esto, es importante tener presente este primer request

Url:

https://ecommerce-xx-stage.kiusys.net/searchflight/api/v1/flow/?carrier=xx&app=ecommerce

Implementación:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let headers = { 'Content-Type': 'application/json', 'accept': 'application/json', 'carrier': 'xx', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'referer': 'https://ecommerce-xx-stage.kiusys.net/searchflight/', 'authority': 'ecommerce-xx-stage.kiusys.net' } var url = 'https://ecommerce-xx-stage.kiusys.net/searchflight/api/v1/flow/?carrier=xx&app=ecommerce' fetch(url, { method: 'GET', headers: headers } ).then(res => res.json()) .catch(error => console.error('Error:', error)) .then( response => customHandleResponseFunction(response) )

Respuesta:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 { "kiu_internals":{ "duration":0.19812583923339844, "id_tracking":"backendsearchflight.29bad7c320a2_6a0a84986ffd41cc8fb90dee945baf21" }, "response":{ "flow":[ { "id":1, "spaflow_id":1, "url":"/searchflight/", "zorder":0 }, { "id":2, "spaflow_id":1, "url":"/flightresults/", "zorder":1 }, { "id":5, "spaflow_id":1, "url":"/travellerdetails/", "zorder":2 }, { "id":8, "spaflow_id":1, "url":"/extraservices/", "zorder":3 }, { "id":6, "spaflow_id":1, "url":"/purchase/", "zorder":4 }, { "id":7, "spaflow_id":1, "url":"/endflow/", "zorder":5 } ], "session_key":"fake-test-key.rPnZwFJPXkQ" } }

Diccionario de respuesta:

Campo

Campo hijo

Descripción

Valores/formato

Campo

Campo hijo

Descripción

Valores/formato

flow

 

Listado de pantallas dentro del flujo e-commerce que debe recorrer el usuario

Array → Object

 

spaflow_id

id del registro dentro de spa flow manager

Int

 

url

Path de la pantalla correspondiente al flujo

String, Ej. '/flightresults/'

 

zorder

Indica el orden de la pantalla. Nos ayuda a saber a cual debemos ir, según en donde estemos posicionados

Int

Get configs

Es el servicio principal, proveedor de toda la configuración de la aplicación seleccionada.
Hay que destacar que no todas las aplicaciones necesitan de este request.
Por el momento, solo searchflight y webcheckin, necesitan ejecutar este request.
Por tal motivo, recomendamos tener este aspecto muy presente, y gestionar la funcionalidad, apoyados en un esquema de configuración, respecto a la lista de aplicaciones.
Por ejemplo:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const appsSchema = { ecommerce: { app: 'ecommerce', module: 'searchflight', needConfigs: true }, manage: { app: 'manage', module: 'managebooking', needConfigs: false }, webcheckin: { app: 'manage', module: 'retrievebooking', needConfigs: true } };

Este json, no es mas que una sugerencia, que sirve para configurar lo que, hasta ahora, el widget necesita.
app: Sirve para setear la aplicación seleccionada.
module: Para definir el path url que necesita el request al backend.
needConfigs: Para indicar que necesita ejecutar el getConfigs.

Url (app searchflight):

https://ecommerce-xx-stage.kiusys.net/searchflight/api/v1/configs/


Implementación:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let headers = { 'Content-Type': 'application/json', 'accept': 'application/json', 'carrier': 'xx', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'referer': 'https://ecommerce-xx-stage.kiusys.net/searchflight/', 'authority': 'ecommerce-xx-stage.kiusys.net', 'authorization': 'Token session_key obtenido en get_sessions' } fetch('https://ecommerce-xx-stage.kiusys.net/searchflight/api/v1/configs/', { method: 'GET', headers: headers } ).then(res => res.json()) .catch(error => console.error('Error:', error)) .then( response => customHandleResponseFunction(response) )

Respuesta:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 { "kiu_internals":{ "duration":2645.9577083587646, "id_tracking":"backendsearchflight.29bad7c320a2_3f5d1423f4504527b433ede5a4cbd14c" }, "response":{ "cabin":{ "business":true, "business_default":false, "economy":true, "economy_default":false, "economy_premium":false, "economy_premium_default":false, "first":true, "first_default":true, "id":2, "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6" }, "carrier":{ "code":"XX", "enable":true, "name":"EQUIS", "numcode":"001", "uuid":"982726d9-965b-4ea4-a22b-aa765dbe8dc7" }, "country_settings":[ { "currency_code":"USD", "default":true, "device_id":"AEP00XXW04", "enable":true, "id":45, "iso_country_code":"AR", "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6", "user_id":"AEP00XXSM" } ], "display_logics":[ { "airport_code":true, "airport_name":true, "city_code":true, "city_name":true, "country_code":true, "country_name":true, "device_type":"m", "id":4, "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6" }, { "airport_code":true, "airport_name":true, "city_code":true, "city_name":true, "country_code":true, "country_name":true, "device_type":"d", "id":5, "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6" }, { "airport_code":true, "airport_name":true, "city_code":true, "city_name":true, "country_code":true, "country_name":true, "device_type":"t", "id":6, "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6" } ], "eligibilities":[ { "code":"KIU", "enable":true, "id":3, "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6" } ], "journey_type":{ "id":2, "multicity":false, "multicity_default":false, "one_way":true, "one_way_default":true, "round_trip":true, "round_trip_default":false, "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6" }, "languages":[ { "lang":"en" }, { "lang":"es" } ], "passenger_types":[ { "code":"ADT", "enable":true, "id":9, "max_quantity":6, "ptc":"ADT", "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6" }, { "code":"INF", "enable":true, "id":10, "max_quantity":6, "ptc":"INF", "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6" }, { "code":"CHD", "enable":true, "id":11, "max_quantity":3, "ptc":"CHD", "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6" } ], "privacy_policy":{ "confirmation_button":false, "enable":true, "id":2, "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6" }, "promo":{ "codes":[ { "code":"PROMO1", "id":4, "promo":2 } ], "enable":true, "id":2, "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6" }, "routes":[ { "carrier":"982726d9-965b-4ea4-a22b-aa765dbe8dc7", "destination":{ "city":{ "code":"BUE", "country":{ "code":"AR", "description":"ARGENTINA", "id":17 }, "description":"BUENOS AIRES", "id":35 }, "code":"AEP", "description":"AEROPARQUE JORGE NEWBERY", "id":79 }, "id":47, "origin":{ "city":{ "code":"COR", "country":{ "code":"AR", "description":"ARGENTINA", "id":17 }, "description":"CORDOBA", "id":26 }, "code":"COR", "description":"PAJAS BLANCAS", "id":27 } } ], "setting":{ "carrier":"982726d9-965b-4ea4-a22b-aa765dbe8dc7", "enable":true, "name":"init", "uuid":"0be29d7e-c626-4d07-b60f-6917cc890bc6" }, "temporary_message":{ "color":"primary", "details":[ { "datetime":"2020-03-17T16:20:41.331326Z", "enable":true, "id":26, "link":"http:XX.com", "name":"b'U1\\x03\\x9d'", "show_datetime":false, "temporary_message":2 } ], "id":2, "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6" }, "translations":{ "en":{ "aaaa":"aaaa", "child":"Child", "descriptions":"xczvzxv", "descriptionseeeee":"dddddd", "language_description":"English", "lugateste":"lugatest", "luggage":"Luggage", "meal":"Meal", "pax":"Passenger", "test123":"test" }, "es":{ "aaaa":"12345", "child":"Ni\u00f1o", "descriptions":"zsvzvxdv", "descriptionseeeee":"dddd", "infant":"infante", "meal":"Comida", "pax":"Pasajero", "testing":"fff" }, "kiu_internals":{ "id_tracking":"kiu-translate.kiu-translate-qa_fe9212c0a9604a619fa34deab8341f61" } } } }

Diccionario de respuesta:

Campo

Campo hijo

Descripción

Valores/formato

Campo

Campo hijo

Descripción

Valores/formato

routes

 

Listado de combinaciones por origen y destinos.

Array → Object

 

origin

Información del origen. Contiene información de la ciudad y país, además del código y descripción del aeropuerto

Object

 

destination

Información del destino. Contiene información de la ciudad y país, además del código y descripción del aeropuerto

Object

country_settings

 

Configuración de país y moneda, desde backoffice. Indispensable para armar el point of sale, si queremos obtener disponibilidad. Estos datos influyen directamente en RES al momento de cotizar. Entre otras utilidades, podemos tener mas de una configuración para que el carrier pueda tener configurado un validador de pagos en USD y otro en ARS. (Por ejemplo)

Array → Object

 

currency_code

Código de moneda

String, Ej. ‘USD’

 

default

Indica si el registro debe estar seleccionado por defecto

Boolean

 

device_id

Código de 10 caracteres correspondiente a la terminal_id/device_id usada para conectarse con RES

String. Ej. ‘AEP00XXW04’

 

user_id

Código de 9 caracteres correspondiente a la user_id/agente_id usada para conectarse con RES

String. Ej. ‘AEP00XXSM’

 

enable

Indica si el registro está habilitado

Boolean

 

iso_country_code

Código de país

String. Ej. ‘AR’

display_logics

 

Configuración desde backoffice que indica como mostrar los origen/destino en el formulario, por tipo de dispositivo

Array → Object

 

airport_code

Indica si se debe mostrar el código de aeropuerto

Boolean

 

airport_name

Indica si se debe mostrar el nombre de aeropuerto

Boolean

 

city_code

Indica si se debe mostrar el código de ciudad

Boolean

 

city_name

Indica si se debe mostrar el nombre de ciudad

Boolean

 

country_code

Indica si se debe mostrar el código de país

Boolean

 

country_name

Indica si se debe mostrar el nombre de país

Boolean

 

device_type

Tipo de dispositivo: m: Teléfono, d: Escritorio, t: Tableta

String: Ej. ('m', ‘t', 'd’)

journey_type

 

Configuración de tipo de viaje. Actualmente solo están en uso one_way y round_trip.

Object

 

one_way

Indica si el tipo de viaje one_way está configurado, de venir en false, no debe usarse

Boolean

 

one_way_default

Indica si one_way debe estar seleccionado por defecto al momento de cargar el widget

Boolean

 

round_trip

Indica si el tipo de viaje round_trip está configurado, de venir en false, no debe usarse

Boolean

 

round_trip_default

Indica si round_trip debe estar seleccionado por defecto al momento de cargar el widget

Boolean

 

multicity

Indica si el tipo de viaje multicity está configurado, de venir en false, no debe usarse (Actualmente no disponible)

Boolean

 

multicity_default

Indica si multicity debe estar seleccionado por defecto al momento de cargar el widget (Actualmente no disponible)

Boolean

passenger_types

 

Configuración referente a tipos de pasajeros

Array → Object

 

code

Código de tipo de pasajero, ADT: Adultos, INF: Infantes, CHD: menores

String. Ej. 'ADT'

 

enable

Indica si está habilitado para su uso

Bolean

 

max_quantity

Indica Máxima cantidad de pasajeros admitidos por el tipo

Int

 

ptc

PTC al que pertenece el registro. Ej. MIL: Militar

String. Ej. 'MIL'

eligibilities

 

Listado de eligibilities para desplegar en el formulario.

Array → Object

 

code

Código del eligibilitiy

String

 

enable

Indica si el eligibilitiy está habilitado para su uso

Boolean

cabin

 

Configuración de cabinas, según lo configurado en backoffice. Viene de a pares, nombre de la cabina y si la misma es configurada por defecto. Por ejemplo, business y business_default. Sólo viene un key default = true.

Objet

 

“Business“

Nombre de la cabina. Indica si está habilitada

Boolean

 

“Business_default“

Indica si la cabina está configurada por defecto.

Boolean

temporary_message

 

Trae la configuración de los mensajes temporales dados de alta en el backoffice

Object

 

color

Sirve para ser asociado a una clase con el objetivo de cambiar el color del grupo de mensajes

String

 

details

El campo details, es el que contiene el listado de mensajes a mostrar en el widget (De ser deseado).

Array -> Object

translations

 

Trae un listado de traducciones desplegado por index de idioma, Ej. es y value igual a un objeto de traducciones que se pueden utilizar. La idea de esta data, es poder mostrar todos los labels traducidos dinámicamente

Object → Object

promo

 

Trae la configuración de códigos promocionales disponibles, dados de alta en el backoffice

Object

 

enable

Indica si está habilitado su uso

Boolean

 

codes

Listado de códigos promocionales

Array → Object

privacy_policy

 

Configuración proveniente del backoffice, referente a políticas de privacidad

Object

 

confirmation_button

Indica si debe mostrar botón de confirmación para cerrar la notificación

Boolean

 

enable

Indica si está habilitado su uso

Boolean

languages

 

Listado de idiomas disponibles

Array → Object

Basado en esta respuesta, el desarrollador cuenta con la suficiente información para desplegar dentro de los distintos campos del widget:

  • Aeropuertos de salida y arribo

  • Rutas habilitadas

  • Tipos de pasajeros soportados

  • Cantidad máxima permitida de pasajeros para búsquedas

  • Tipo de viajes soportados (Ida o Ida y vuelta)

  • Monedas soportadas, por país

  • Mensajes temporales y políticas de privacidad

  • Códigos promocionales

  • Listado de Cabinas

  • Traducciones

También en base a esta información puede generar filtros y validaciones del lado del cliente. 

 

Url (app webcheckin):

https://ecommerce-xx-stage.kiusys.net/retrievebooking/api/v1/configs/

Implementación:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let headers = { 'Content-Type': 'application/json', 'accept': 'application/json', 'carrier': 'xx', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'referer': 'https://ecommerce-xx-stage.kiusys.net/searchflight/', 'authority': 'ecommerce-xx-stage.kiusys.net', 'authorization': 'Token session_key obtenido en get_sessions' } fetch('https://ecommerce-xx-stage.kiusys.net/retrievebooking/api/v1/configs/', { method: 'GET', headers: headers } ).then(res => res.json()) .catch(error => console.error('Error:', error)) .then( response => customHandleResponseFunction(response) )

Respuesta:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 { "kiu_internals":{ "duration":15.575170516967773, "id_tracking":"backend_retrieve_booking.1a3b98991b77_20b1ae1b2abf4dc5b1c0b04a1c4ddefe" }, "response":[ { "currency_code":"ARS", "default":true, "device_id":"AEP00XXW04", "enable":true, "id":3, "iso_country_code":"AR", "setting":"c256b8d5-dd08-45ba-a546-7057bef51199", "user_id":"AEP00XXSM" } ] }

Diccionario de respuesta:

 

Campo

Descripción

Valores/formato

 

Campo

Descripción

Valores/formato

-

 

Lista con la Configuración de país y moneda, desde backoffice. Indispensable para armar el point of sale.

Array

 

currency_code

Código de moneda

String, Ej. ‘USD’

 

default

Indica si el registro debe estar seleccionado por defecto

Boolean

 

device_id

Código de 10 caracteres correspondiente a la terminal_id/device_id usada para conectarse con RES

String. Ej. ‘AEP00XXW04’

 

user_id

Código de 9 caracteres correspondiente a la user_id/agente_id usada para conectarse con RES

String. Ej. ‘AEP00XXSM’

 

enable

Indica si el registro está habilitado

Boolean

 

iso_country_code

Código de país

String. Ej. ‘AR’

Por el momento, solo devuelve la configuración de country settings, necesaria para hacer la búsqueda de la reserva. Se espera que mas adelante, vengan mas configuraciones…

Get Dates (Usado para searchflight)

Devuelve el listado de fechas con disponibilidad de vuelos desde el día actual hasta los posteriores 330 días. Este servicio debe ser usado teniendo implementado los inputs de origen y destino y tipo de viaje, (este último no es requerido). De manera que una vez se seleccione el valor para los tres campos especificados anteriormente, puede ejecutar la petición http.

Parámetros:

origin: El código del aeropuerto de origen. Ejemplo: 'AEP'.

destination: El código del aeropuerto de destino. Ejemplo: 'BUE'.

Url:

https://ecommerce-xx-stage.kiusys.net/searchflight/api/v1/dates/?origin=${origin}&destination=${destination}


Implementación:

Este endpoint se ejecuta por get, enviado query params

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 let origin = 'AEP'; let destination = 'COR'; let headers = { 'Content-Type': 'application/json', 'accept': 'application/json', 'carrier': 'xx', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'referer': 'https://ecommerce-xx-stage.kiusys.net/searchflight/', 'authority': 'ecommerce-xx-stage.kiusys.net', 'authorization': 'Token session_key obtenido en get_sessions' } fetch(`https://ecommerce-xx-stage.kiusys.net/searchflight/api/v1/dates/?origin=${origin}&destination=${destination}`, { method: 'GET', headers: headers } ).then(res => res.json()) .catch(error => console.error('Error:', error)) .then( response => customHandleResponseFunction(response) )

Respuesta:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 { "kiu_internals":{ "duration":39.411306381225586, "id_tracking":"backendsearchflight.29bad7c320a2_650ed89aeee64f209f6f9b4357a57a6a" }, "response":{ "ow":[ "2020-04-03", "2020-04-04", "2020-04-05", "2020-04-06", "2020-04-07", "2020-04-08", "2020-04-09", "2020-04-10", "2020-04-11", "2020-04-12", "2020-04-13", "2020-04-14", "2020-04-15", "2020-04-16", ], "rt":[ "2020-04-03", "2020-04-04", "2020-04-05", "2020-04-06", "2020-04-07", "2020-04-08", "2020-04-09", "2020-04-10", "2020-04-11", "2020-04-12", "2020-04-13", "2020-04-14", ] } }

Diccionario de respuesta:

En esta respuesta obtenemos todas las fechas con disponibilidad desde el día actual hasta 330 días después.
ow: Representa las fechas disponibles para vuelos one_way
rt: Representa vuelos round_trip.
La idea de esta información, es tener lo necesario para iniciar el componente “datepicker” indicándole que fechas son las que debe habilitar. En el caso de que el componente usado no tenga este atributo y por el contrario admita fechas para des-habilitar, se puede usar este listado, comparándolo contra los 330 días, de modo que obtendríamos los días sin disponibilidad que necesitaríamos, (según el componente usado).

Post action (searchflight)

Este servicio se encarga de enviar los datos para la búsqueda de disponibilidad, una vez el usuario haya completado todos los campos mínimos requeridos. La petición debe hacerse vía POST AJAX y al obtener la respuesta, basta con hacer una re-dirección a la siguiente url según el flujo obtenido, mediante el servicio get_flow

Parámetros:

country_setting: Configuración de país seleccionada por el pasajero.
session_key: session_key obtenido en get_sessions.
journey_type: Tipo de viaje.
origin: Código de origen seleccionado por el pasajero.
destination: Código de destino seleccionado.
departure_date: Fecha de salida seleccionada por el pasajero.
return_date: Fecha de retorno, en el caso de vuelos ida y vuelta.
adults: Cantidad de pasajeros adultos.
minors: Cantidad de pasajeros menores de edad.
infants: Cantidad de pasajeros infantes.
cabin: Cabina seleccionada por el pasajero.
promo: Código promocional.

Url:

https://ecommerce-xx-stage.kiusys.net/searchflight/api/v1/action/


Implementación:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 var formData = { country_setting: { "currency_code":"USD", "default":true, "device_id":"AEP00XXW04", "enable":true, "id":45, "iso_country_code":"AR", "setting":"0be29d7e-c626-4d07-b60f-6917cc890bc6", "user_id":"AEP00XXSM" }, session_key: 'session_key obtenido en get_sessions', journey_type: 'one_way', origin: 'AEP', destination: 'COR', departure_date: '2021-02-03', return_date: null, // si journey_type = 'round_trip' return_date debe completarse adults: 1, minors: 0, infants: 0, cabin: 'economy', // Su envío no es requerido promo: 'PROMO1' // Su envío no es requerido } let headers = { 'Content-Type': 'application/json', 'accept': 'application/json', 'carrier': 'xx', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'referer': 'https://ecommerce-xx-stage.kiusys.net/searchflight/', 'authority': 'ecommerce-xx-stage.kiusys.net', 'authorization': 'Token session_key obtenido en get_sessions' } fetch('https://ecommerce-xx-stage.kiusys.net/searchflight/api/v1/action/', { method: 'POST', headers: headers, body: JSON.stringify(formData) } ).then(res => res.json()) .catch(error => console.error('Error:', error)) .then( response => customHandleResponseFunction(response) )

Post action (manage booking)

Este servicio se encarga de enviar los datos para la búsqueda de una reserva, una vez el usuario haya completado todos los campos mínimos requeridos. La petición debe hacerse vía POST AJAX y al obtener la respuesta, basta con hacer una re-dirección a la siguiente url según el flujo obtenido, mediante el servicio get_flow

Parámetros:

point_of_sale: Por el momento, tiene el campo “user“, el cual debe ser igual al código del carrier (object)
record_locator: Código de reserva ingresado por el usuario. (string)
last_name_or_grp_name: Apellido del pasajero. (string)

Url:

https://ecommerce-xx-stage.kiusys.net/managebooking/api/v1/action/

Implementación:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 var formData = { point_of_sale: { user: 'XX', }, record_locator: 'UZAZHI', last_name_or_grp_name: 'CARRILLO' } let headers = { 'Content-Type': 'application/json', 'accept': 'application/json', 'carrier': 'xx', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'referer': 'https://ecommerce-xx-stage.kiusys.net/booking/', 'authority': 'ecommerce-xx-stage.kiusys.net', 'authorization': 'Token session_key obtenido en get_sessions' } fetch('https://ecommerce-xx-stage.kiusys.net/managebooking/api/v1/action/', { method: 'POST', headers: headers, body: JSON.stringify(formData) } ).then(res => res.json()) .catch(error => console.error('Error:', error)) .then( response => customHandleResponseFunction(response) )

 

Post action (Web checkin)

Este servicio se encarga de enviar los datos para la búsqueda de una reserva a checkear, una vez el usuario haya completado todos los campos mínimos requeridos. La petición debe hacerse vía POST AJAX y al obtener la respuesta, basta con hacer una re-dirección a la siguiente url según el flujo obtenido, mediante el servicio get_flow

Parámetros:

point_of_sale: Debe contener los datos obtenidos previamente en el get_configs (object)
record_locator: Código de reserva ingresado por el usuario. (string)
last_name_or_grp_name: Apellido del pasajero. (string)

Url:

https://ecommerce-xx-stage.kiusys.net/retrievebooking/api/v1/action/

Implementación:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 var formData = { point_of_sale: { user: "XX", agent_preferred_language: "es-AR", kiu_device_id: "AEP00XXW04", agent_id: "AEP00XXSM", preferred_display_currency: "ARS", country: "AR" }, record_locator: 'UZAZHI', last_name_or_grp_name: 'CARRILLO' } let headers = { 'Content-Type': 'application/json', 'accept': 'application/json', 'carrier': 'xx', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'referer': 'https://ecommerce-xx-stage.kiusys.net/booking/', 'authority': 'ecommerce-xx-stage.kiusys.net', 'authorization': 'Token session_key obtenido en get_sessions' } fetch('https://ecommerce-xx-stage.kiusys.net/retrievebooking/api/v1/action/', { method: 'POST', headers: headers, body: JSON.stringify(formData) } ).then(res => res.json()) .catch(error => console.error('Error:', error)) .then( response => customHandleResponseFunction(response) )

 

Respuesta (searchflight, manage my booking, web checkin):

Es importante obtener un estado 200 y que dentro de response.response['session-display'] tengamos el valor de nuestra session key mencionada anteriormente. En ese escenario sabemos que tenemos ya todo preparado para re-direccionar a la siguiente pantalla.

Manage booking y web checkin, puede devolver un 400, indicando que la reserva o el pasajero no existe. Es importante tener en cuenta este caso, para, en tal caso, mostrar un mensaje de error al usuario.

1 2 3 4 5 6 7 8 9 { "kiu_internals":{ "duration":3.2041072845458984, "id_tracking":"backendsearchflight.29bad7c320a2_953a5c297e464e7191b869b658cfa32c" }, "response":{ "session-display":"https://ecommerce-xx-stage.kiusys.net/spa-session-display/show?app=ecommerce&session_key=fake-test-key.odfcPLsiwJY" } }

 

Construcción del Widget

El objetivo de este apartado es aprovechar el contenido obtenido en los servicios disponibles, para armar el formulario de búsqueda correspondiente, realizar las validaciones pertinentes y finalmente poder enviar información a la siguiente pantalla del flujo que corresponda.

Web assembly (searchflight).

Para evitar ataques contra el servicio de shopping, usado en la pantalla flightresults, se implementó un control mediante WebAssembly, en donde generamos un token válido que debe ser enviado en el payload de https://ecommerce-[carrier]-[env].kiusys.net/searchflight/api/v1/action/. Ya que este será validado en nuestro backend por fecha de vencimiento y datos.
En este sentido, es importante seguir los siguientes pasos:


Incluir carpeta wasm en el proyecto:
Debido a que el widget implementado por el carrier tendría un DNS distinto al provisto por Kiu, para generar la pantalla de búsqueda, el cliente debe incluir la carpeta wasm en su desarrollo evitando errores por CORS ORIGIN. De esta manera, podrá generar el token de webassembly.
La carpeta wasm, debe estar ubicada de manera que, el index en donde se encuentre el widget, pueda hacer fetch de los archivos.

De los tres archivos dentro de la carpeta wasm, solo puede modificarse el archivo wasm.css.

Para poder servirse el archivo test.wasm es indispensable configurar location y mimetypes en el servidor configurado. Por ejemplo, si usamos servidor nginx, es como se muestra en el siguiente ejemplo:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # path usado para resolver la carpeta wasm location /wasm/ { alias /var/www/tests/custom_widget/wasm/test.wasm; # alias Ubicación de la carpeta add_header Access-Control-Allow-Origin *; add_header Access-Control-Request-Method GET; } types { text/css css; text/html html; text/plain txt; application/wasm wasm; # mimetype para test.wasm image/svg+xml svg svgz; image/gif gif; image/png png; image/jpeg jpeg jpg; image/webp webp; image/x-icon ico; } gzip on; # habilita servir el archivo como comprimido gzip_types application/wasm; # especifica el mimetype del archivo a comprimir

El archivo test.wasm, se sirve únicamente usando el mimetype application/wasm wasm;. Sin esta configuración, no podrá resolverse. Adicionalmente, debido a que es un archivo pesado, debe comprimirse. Para ello usamos gzip;

Insertar WASM_URL: Esta es requerida para utilizarse dentro del js cargado mas adelante. Especial atención en que finalice con '/' y que la variable se inyecta al objeto window. (El DNS debe ser el del carrier)
<script type="text/javascript"> window.WASM_URL = "https://airline-webpage/wasm/"; </script>

Insertar Polyfill: Hace posible la compatibilidad con navegadores de versiones antiguas.

<script crossorigin="anonymous" src="https://polyfill.io/v3/polyfill.min.js?features=blissfuljs%2Cdefault%2Ces2015%2Ces2016%2Ces2017%2Ces2018%2Ces5%2Ces6%2Ces7%2CBlob%2CArray.prototype.forEach"</script>

Insertar validación para Microsoft IE: Internet Explorer no es soportado, por tal motivo, es importante avisar al usuario que debe cambiar de navegador. Por eso, se recomienda agregar el siguiente tag, pudiendo ademas modificar el alerta por uno de preferencia del cliente.
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function(event) {
var ua = window.navigator.userAgent;
if ( ua.indexOf("MSIE ") > -1 || ua.indexOf("Trident/") > -1) {
alert('This website does not support your current version of your web browser')
}
});
</script>

Insertar css: El DNS debe ser el del carrier.

<link rel="stylesheet" type="text/css" href="https://airline-webpage/wasm/wasm.css">

Insertar wasm_load.js: Tag muy importante, puesto que es el que hace la carga del archivo WebAssembly, encargado de generar el token. (El DNS debe ser el del carrier).

<script type="text/javascript" src="https://airline-webpage/wasm/wasm_load.js"</script>

Una vez se hayan cargado los scripts descritos anteriormente, solo podremos generar el token en un solo momento, luego de que el usuario completó el formulario, por lo que debemos generar el token en el momento que el usuario hace click en el botón “search“. Agregando de alguna manera la key wasm_token, al objeto que será enviado por post al backend.


Debemos hacer uso de la funcion validateForm() (Recordar que se puede invocar una sola vez, de querer re usarla, es necesario recargar la pantalla). Suponiendo que nuestro payload está dentro una variable “formData”:
formData.wasm_token = validateForm(JSON.stringify(formData))

Posteriormente se haría el post hacia …/action.
Nota: Verificar que se envía el wasm_token.

HTML

NOTA IMPORTANTE

El objetivo de este documento y ejemplo es ser utilizado como guía de implementación por parte de los desarrolladores; no está destinado a ser utilizado como parte del desarrollo.

Este HTML no deberá ser utilizado como está. No está adaptado para funcionar en todos los navegadores, dispositivos o sus diferentes versiones.

Este widget debe ser ajustado y se requiere un proceso de certificación antes de la salida a producción donde todas las partes deberán estar involucradas (aerolínea, desarrolladores de la web, y Kiu).

Lo que a continuación se mostrará, es basado en un desarrollo sencillo, usando bootstrap, javascript y jquery.

En vista de que en este widget, debemos agregar mas de un formulario, Una manera muy sencilla de resolverlo, es implementando tabs, como los que ofrece boostraps. La idea es que al ir navegando entre pestañas, cambie el formulario y las variables de entorno necesarias, para poder hacer request al backend correspondiente.

El el siguiente archivo .zip, puedes obtener la versión de inicio, para implementar un widget desde cero.

 

 

Si necesita probarlo en local, puede instalarse nodejs, y con seguir los pasos del readme, inicia. Si cuentan con un servidor nginx o se prefiere probarlo dentro de un VPS (por ejemplo), puede tomarse el archivo widgetBooking.html y utilizarlo directamente.

Link de pruebas:

Por medio de este, puede probarse directamente en el DNS de kiusys.com. Puede usarse dev, qa o stage. Para ello, basta con agregar via queryparams, la key env con el valor que se prefiera, así como cambiar de carrier.

https://apps-dev.kiusys.com/tests/custom_widget/booking.html?env=stage&carrier=xx

Validaciones requeridas

Mas allá que la aplicación E-COMMERCE tiene sus propias validaciones en el backend, debemos realizar ciertas recomendaciones a los desarrolladores, sobre algunas validaciones a realizar del lado del cliente (Front-end), con el objetivo de evitar errores de búsqueda inconsistentes y generar un proceso mas eficiente. 

Código de reserva - Apellido:

Debe procurarse que la reserva exista, y el apellido sea extactamente el mismo usado en la reserva. Importante, validar la cantidad de caracteres, acentos, o la letra “Ñ”, por ejemplo.

Origen - Destino:

La respuesta de Get_config devuelve la lista de rutas habilitadas para búsquedas configuradas en el backoffice. Basado en esta información, el desarrollador puede filtrar y validar las opciones que estén relacionadas y así optimizar los resultados de las búsquedas. Este filtro se realiza por ejemplo en la funcion javascript filterRoute(), Esta debe usarse en un evento onkeyup

Fechas:

El formato soportado de fechas es YYYY-MM-DD (ejemplo: 2020-04-06). El desarrollador puede desplegar en el formulario en diferentes formatos de acuerdo a la zona en la que se encuentre, pero debe considerar hacer la transformación para respetar el formato soportado y evitar de esta manera que E-COMMERCE devuelva error de formato. 

Fecha de salida:

La fecha de salida debe considerarse desde la fecha actual a futuro y no mas allá de 330 días en futuro. La aplicación por estándar de la industria soporta búsquedas de disponibilidad hasta 330 días a futuro de la fecha actual. Esto lo especificamos en el apartado Get_dates, y le damos uso en la funcion javascript manageDates().

Fecha de regreso:

La fecha de regreso debe validarse contra la fecha de salida. Esta no puede ser anterior a la fecha fijada como salida y no debe ser superior a 330 días a futuro. 

Pasajero adulto:

Recomendamos que el valor default sea 1. No se acepte 0, para evitar la solicitud vía canal de ventas web de menores no acompañados que lleva consigo una proceso interno de la línea aérea para su autorización. De igual manera, estas configuraciones vienen incluidas en la respuesta de Get_configs, campo passenger_types.

Pasajeros infantes:

Por una lógica propia de la industria, debe existir previamente un adulto antes de agregar un infante. Si existe mas de un pasajero Infante, debe existir la misma cantidad de adultos puesto que solamente se puede asociar un pasajero infante y solo uno a un pasajero adulto.

Envío de datos a E-COMMERCE:

El servicio Post action es usado para el envío de datos de búsqueda vía AJAX, no debe ser usado en un evento form submit, ya que no admite re-direcciones. El uso correcto de este servicio, es: una vez obtenida la respuesta con status 200, debemos hacer una re-dirección hacia la siguiente pantalla E-COMMERCE, Dicha información, la provee el servicio Get flow, utilizado de primero en el flujo de integración, cuya información debe ser guardada en el cliente.

Para hacer la re-dirección, basados en el flujo, podemos hacer uso de la función handlePostResponse(), o directamente, teniendo en cuenta la url general, los datos de sesión obtenidos previamente y la respuesta de Post Action. Partiendo de que la primera pantalla de cualquiera de los flujos, es la que estamos, y sería la posición 0 de la lista, podemos simplemente obtener el siguiente, es decir, posición 1, y de este, obtener el path correspondiente para hacer la re-dicección. Bajo ese contexto, una opción sería utilizar algo como lo siguiente:

1 2 let nextUrl = session.flow[1].url window.location.href = `${mainUrl}${nextUrl}?carrier=${carriercode}&lang=${language}&session_id=${session.session_key}


Espero la información expuesta sea de su total utilidad.

Suerte!