Skip to content
Konstantin Kolotyuk edited this page Oct 31, 2016 · 16 revisions

JTB is multi unit supplier which provides properties with instant confirmation over Asia, most of them in Japan. You can try to quote and book JTB properties here: https://www.jtb.com.sg/jp/hotels/tokyo-accommodation/shiba-park-hotel/

Technical information

JTB provides two ways to interact with him:

  • SOAP interface. It provides realtime inventory information and booking/cancellation operation.
  • Master Synchronization. It provides master information as TSV file (tab-delimited format) using SFTP protocol. Some of files are really huge.

Full documentation list

  • Developers Guide - common information about interaction with JTB. If you know nothing about JTB start from here.
  • API References Guide - SOAP API documentation, request/response fields, etc.
  • Data references - Master Synchronization documentation, TSV columns' descriptions, etc.
  • Column Relations - scheme of relations between SOAP API and Master Synchronization files.

Authentication happens with the same credentials described in the Authentication section below.

SOAP API authentication

All calls to JTB's API must be authenticated using an id, user, password and company combination. These are sent as xml block for every method call.

<POS>
   <Source>
      <RequestorID ID="AGTUSER01" UserName="USERNAME" MessagePassword="Agtuser_01">
         <CompanyName Code="AGT01"/>
         <BasicInfo Version="2013" Language="EN"/>
      </RequestorID>
   </Source>
</POS>

WSDL URLs can be found in Developers Guide documentation They are different for different services, for example for GA_HotelAvail service they currently are:

Sandbox: https://trial-www.jtbgenesis.com/genesis2-demo/services/GA_HotelAvail_v2013?wsdl

Production: https://www.jtbgenesis.com/genesis2/services/GA_HotelAvail_v2013?wsdl

Master Syncronization authentication

For SFTP connection you need: user_id, password, port and host Port and host can be found in Developers Guide documentation and currently they are:

port: 10036

production host: "ftp.jtbgenesis.com"

sandbox host: "trial-ftp.jtbgenesis.com"

Room identifier(unit_id)

Each JTB unit has unique room_code, but their API methods (quote, booking) don't allow to work with room_code directly. Instead of this each of them works with rate_plans (every unit can have more then one rate_plan).

Concierge uses combination of room_type_code and room_code as Roomorama unit_id and stores rate_plan_id and room_code relations in jtb_rate_plans table. Using this table c can find exactly unit's rate_plans from quotation response and work with exactly unit further. JTB::UnitId class responsible for converting JTB ids to Roomorama id and vice versa. For more details see the class documentation.

Quoting Prices

NOTE: Quote Prices depends on state of jtb_rate_plan DB table. This table is actualized by sync workers. So it is important to run sync regularly.

Checking if a property is available and getting the price for a given stay is covered by the https://www.jtbgenesis.com/genesis2/services/GA_HotelAvail_v2013 endpoint with called :gby010 operation.

Specific rules

Request

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://service.api.genesis2.jtbgmt.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <GA_HotelAvailRQ>
         <POS>
            <Source>
               <RequestorID ID="AGTUSER01" UserName="USERNAME" MessagePassword="Agtuser_01">
                  <CompanyName Code="AGT01"/>
                  <BasicInfo Version="2013" Language="EN"/>
               </RequestorID>
            </Source>
         </POS>
         <AvailRequestSegments>
            <AvailRequestSegment>
               <HotelSearchCriteria>
                  <Criterion  SortType="PRICE" AvailStatus="ALL">
                     <HotelCode Code="4016001"/>
                     <RoomStayCandidates SearchCondition="OR">
                        <RoomStayCandidate RoomTypeCode="TWN" Quantity="1"/>
                     </RoomStayCandidates>
                     <StayDateRange Start="2013-04-01" End="2013-04-02"/>
                  </Criterion>
               </HotelSearchCriteria>
            </AvailRequestSegment>
         </AvailRequestSegments>
      </GA_HotelAvailRQ>
   </soapenv:Body>
</soapenv:Envelope>
  • HotelCode is the property ID in their system.
  • RoomTypeCode is the unit ID in their system.
  • StayDateRange has two attributes Start and End which means check_in and check_out dates.

Response

{
    ga_hotel_avail_rs: {
        room_stays: {
            room_stay: [* array of availabilities *]
        },
        ... some_useless info
    }
}

The response returned by JTB is huge hash where deepest keys has @ at the beginning.

Example: { room_rates: { room_rate: { total: { :@amount_after_tax => '10000' }, ... } }

!!! IMPORTANT !!!

JTB returns list of availabilities with rate plans for each day of requested period. It means that for the quoted room price they will provide different prices. The request's params don't provide an ability to get quote for exactly room by its id, so to consider only rate plans of requested unit Concierge uses jtb_rate_plans table.

# After small modification of response data looks like:

{ date: '2016-03-04', price: 20000, rate_plan: 'QWERQW222', available: 'OK' },
{ date: '2016-03-04', price: 22000, rate_plan: 'ASDRQW222', available: 'UC' },
{ date: '2016-03-05', price: 20000, rate_plan: 'QWERQW222', available: 'OK' },
....
{ date: '2016-03-06', price: 20000, rate_plan: 'QWERQW222', available: 'OK' },
{ date: '2016-03-06', price: 24000, rate_plan: 'ASDRQW222', available: 'OK' }
  • date is each day of the requested period
  • price is final price after tax
  • rate_plan is plan with some conditions of room (not related to Roomorama businesses logic)
  • available has two varies: OK - available; UC - unavailable

All prices are quoted in JPY.

Create booking

NOTE: Booking depends on state of jtb_rate_plan DB table. This table is actualized by sync workers. So it is important to run sync regularly.

The request to create booking for a property is covered by the https://www.jtbgenesis.com/genesis2/services/GA_HotelRes_v2013 endpoint with called :gby011 operation. The JTB API doesn't have room id as param, but has rate plan id arg. So before booking Concierge call quote request to find the best rate plan for given room.

Request

<HotelReservations>
   <HotelReservation PassiveIndicator="false"> 
      <ResGlobalInfo>
         <RatePlans>
            <RatePlan RatePlanID="TYOHKPT00STD1DBL"/>
         </RatePlans>
         <TimeSpan StartDate="2016-04-01" EndDate="2016-04-02"/>
      </ResGlobalInfo>
      <ResGuests>
         <ResGuest ResGuestRPH="1" PrimaryIndicator="true" AgeQualifyingCode="ADL">
            <Profiles>
               <ProfileInfo>
                  <Profile>
                     <PrefCollections>
                        <PrefCollection>
                           <CommonPref SmokingAllowed="true"/>
                        </PrefCollection>
                     </PrefCollections>
                     <Customer>
                        <PersonName>
                           <GivenName>GivenName1</GivenName>
                           <Surname>Surname1</Surname>
                           <NamePrefix>Mr</NamePrefix>
                        </PersonName>
                     </Customer>
                  </Profile>
               </ProfileInfo>
            </Profiles>
         </ResGuest>
         <ResGuest ResGuestRPH="2" PrimaryIndicator="false" AgeQualifyingCode="ADL">
            ...
         </ResGuest>
      </ResGuests>
      <RoomStays>
         <RoomStay>
            <ResGuestRPHs>
               <ResGuestRPH RPH="1"/> # related to each guest number
               <ResGuestRPH RPH="2"/>
            </ResGuestRPHs>
            <RoomTypes>
               <RoomType RoomTypeCode="DBL"/> # RoomTypeCode is `Unit#hotel_room_type` on Roomorama project
            </RoomTypes>
         </RoomStay>
      </RoomStays>
   </HotelReservation>
</HotelReservations>

Important fields and attributes:

  • PassiveIndicator - allows to simulate request. If "true", returns unique_id="XXXXXXXXXX"
  • RatePlanID - main JTB price related object
  • TimeSpan - check in/out data
  • ResGuest - guest info. JTB requires info for each guest and also don't allow to book double room for one guest. I'll complete this part after getting JTB's answer about guests count for double, triple... room types

Response

{
  "ga_hotel_res_rs": {
    "hotel_reservations": {
      "hotel_reservation": {
        ....
        "unique_id": {
          "@id": "XXXXXXXXXX"
        },
        "@create_date_time": "2016-03-14",
        "@pnr": "XXXXXX",
        "@passive_indicator": "true",
        "@res_status": "OK"
      }
    },
    "success": null,
    "@xmlns": "http://service.api.genesis2.jtbgmt.com/"
  }
}
~~~

as success result response consist `unique_id` which is booking identifier. If `PassiveIndicator` is true returns "XXXXXXXXXX"

Because JTB cancel API method requires `rate_plan_id` as arg Concierge store `reference_number` as combination of `unique_id` and `rate_plan_id`. `JTB::ReferenceNumber` class responsible for converting JTB ids to Roomorama reference_number and vice versa. For more details see the class documentation.

### Cancel reservation

The request to cancel reservation is covered by the `https://www.jtbgenesis.com/genesis2/services/GA_Cancel_v2013` endpoint with called `:gby012` operation.
The method requires `rate_plan_id` param as well as `reservation_id`.

**Request**

~~~xml
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:jtb="http://service.api.genesis2.jtbgmt.com/"
  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <jtb:GA_CancelRQ PassiveIndicator="false">
      <jtb:POS>
        <jtb:Source>
          <jtb:RequestorID ID="id" UserName="name" MessagePassword="pass">
            <jtb:CompanyName Code="code"/>
            <jtb:BasicInfo Version="2013" Language="EN"/>
          </jtb:RequestorID>
        </jtb:Source>
      </jtb:POS>
      <jtb:UniqueID ID="0044UT0001"/>
      <jtb:Verification>
        <jtb:RatePlans>
          <jtb:RatePlan RatePlanID="CUBHC2101STD1DBL"/>
        </jtb:RatePlans>
        <jtb:ReservationTimeSpan Start="2016-11-02"/>
      </jtb:Verification>
    </jtb:GA_CancelRQ>
  </soapenv:Body>
</soapenv:Envelope>
~~~

**Response**

~~~xml
<soap:Envelope
  xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GA_CancelRS
      xmlns="http://service.api.genesis2.jtbgmt.com/" PassiveIndicator="false" Status="XL">
      <CancelInfoRS>
        <CancelRules>
          <CancelRule Amount="0.00" AmountG="0.00">
            <RoomStays>
              <RoomStay>
                <CancelPenalties>
                  <CancelPenalty End="2016-10-31" Start="2014-02-07" duration="1">
                    <AmountPercent Percent="0.00" />
                  </CancelPenalty>
                  <CancelPenalty End="2016-11-01" Start="2016-11-01" duration="1">
                    <AmountPercent Percent="100.00" />
                  </CancelPenalty>
                  <CancelPenalty End="2016-11-02" Start="2016-11-02" duration="1">
                    <AmountPercent Percent="100.00" />
                  </CancelPenalty>
                  <CancelPenalty End="2017-02-09" Start="2016-11-03" duration="1">
                    <AmountPercent Percent="100.00" />
                  </CancelPenalty>
                </CancelPenalties>
                <RoomRates>
                  <RoomRate RoomID="1" TotalForCancellation="0.00">
                    <GuestRate Amount="0.00" PNRPAXID="001" />
                    <GuestRate Amount="0.00" PNRPAXID="002" />
                  </RoomRate>
                </RoomRates>
                <TimeSpan End="2016-11-02" Start="2016-11-02" />
              </RoomStay>
            </RoomStays>
          </CancelRule>
        </CancelRules>
      </CancelInfoRS>
      <Success></Success>
      <UniqueID ID="0044UT0001" />
      <Verification>
        <RatePlans>
          <RatePlan RatePlanID="CUBHC2101STD1DBL" />
        </RatePlans>
        <ReservationTimeSpan Start="2016-11-02" />
      </Verification>
    </GA_CancelRS>
  </soap:Body>
</soap:Envelope>
~~~

### Quote/Booking/Cancel testing

JTB provides two ways to use their API in test mode. The first one is set `PassiveIndicator` attribute in request to `true` (to manipulate this parameter Concierge uses `test` field from JTB credentials). This is useful for onetime testing. The problem is that booking with this parameter always return `XXXXXXXX` as reservation id and developer cannot test the cancellation of the reservation. To solve the problem he can use JTB sandbox (with `PassiveIndicator == false`). The sandbox URLs:
* API: https://trial-www.jtbgenesis.com/genesis2-demo/services
* SFTP: trial-ftp.jtbgenesis.com


### Synchronisation

JTB sync is implemented with usage of Master Synchronisation (TSV files). Some files are really huge (~4Gb) that is why before the first sync Concierge imports files to DB and before further syncs it actualizes the DB with Diff files if it's possible.

There are two kinds of files:
* ALL - file contains all the data from JTB (big files)
* Diff - file contains only diff information (small files)

Concierge required next JTB files for sync:
* _GenericMaster_ - references for JTB enums (location codes, room types, room grades, amenities, etc.). JTB doesn't provide Diff files for this information.
* _HotelInfo_ - all JTB hotels with basic information about them (title, description, etc.). Concierge imports only hotels with type `R` - ryokans.
* _PictureMaster_ - contains images for hotels and rooms
* _RoomType_ - contains information about hotels' rooms
* _RoomPlan_ - each room can have several rate plans, for example they can be different by meal type (breakfast only, etc.), each plan has its price
* _RoomPrice_ - price information for each rate plan for each date
* _RoomStock_ - information about availability for each rate plan for each date. If `sale_status = '0'` and `number_of_units > 0` then rate plan is available for given date. **Note:** JTB documentation contains errors in description of columns for this file, actually `language` column is `city_code` and `option_plan_id` is `rate_plan_id`.

JTB provides information in several languages. Currently Concierge uses only english.

Concierge has separate DB table for each file. `JTB::Sync::Actualizer` class responsible for fetching information from JTB and actualization Concierge's JTB tables. There is also special table `jtb_state` which stores name of last synced file name for each category:

concierge_development=> select * from jtb_state ; prefix | file_name ---------------+--------------------------------------- GenericMaster | GenericMaster_ALL_20161012.zip RoomStock | RoomStock_Diff_20161013104213.zip HotelInfo | HotelInfo_Diff_20161013070036.zip PictureMaster | PictureMaster_Diff_20161013104008.zip RoomPlan | RoomPlan_Diff_20161013081243.zip RoomType | RoomType_Diff_20161013074153.zip RoomPrice | RoomPrice_Diff_20161013083228.zip (7 rows)

TTL of each file on JTB server is one week. During sync the actualizer checks if the last sync was not so long ago (actually it checks if last synced file is still exists on SFTP server) and downloads only Diff files if possible. 

As well as sync workers require actualization of JTB DB tables at the beginning and to avoid simultaneous running of multiple actualization processes Concierge uses one worker to implement metadata and availabilities sync: `Workers::Suppliers::JTB::Metadata`