Article

Getting started with Milefy API v2.8

This tutorial shows how to integrate Milefy API into your flight search app in just a few minutes. Our goal is to display number of award miles, that traveler is going to earn by taking particular flight. You may treat it as 30K’s “Hello world” example.

Prerequisites

This tutorial uses as a base simple flight search engine project, that is written primarily in PHP. Because of that you need:

  1. Elementary knowledge of server-side programming language such as PHP,
  2. Apache 2.0 server or similar for running the flight search engine app with installed PHP5,
  3. Installed Git to clone the project.

1. Setup

Clone Getting starged with 30K API project:

git clone git@github.com:30kcom/getting-started-30k-api.git .

 

To verify, that your copy is working, launch your web server and access index.html file in your browser. You should see loaded application just like on a screenshot below.

Successfully cloned application ran in the browser.

Try to select different flight searches from the dropdown and display results using Search button.

These are real flight search results, but without mileage earnings. We are going to fix it.

2. API credentials

In order to use Milefy API, you need first to request the credentials: API username and password. If you haven’t done this before, request Milefy and Wallet API testing credentials now:

Request Milefy and Wallet API testing credentials

3. Workflow

We will keep things simple and add only one piece of functionality — number of award miles for every flight result. How would that work?

  1. User selects one of available searches,
  2. User hits Search button,
  3. Browser sends request to load flight search results using /api/search/single.php script,
  4. Request is passed to external flight search API,
  5. Response from external flight search API containing results is loaded,
  6. Parameters for mileage calculation are collected from the received flight results,
  7. Milefy API CalculateMiles method is invoked along with one other supplimentary method,
  8. Response from Milefy API is parsed and combined with flight information received in step 5,
  9. Flight results including mileage earnings are embeded into HTML template partials/results.php,
  10. Ready HTML with flight results is sent back to the browser,
  11. Results are injected into the web page.

Looks complicated? In fact everything, but steps 6-8 are already done, so let’s focus just on these missing bits.

4. HTML template

First of all, we’re going to modify the HTML template, that is used to present flight search results. We will add there placeholder for mileage earnings.

Open partials/results.php file and search for the part including code:


<div class="flight-header">

    <span class="flight-price"><?php echo $this->_getPrice($flight); ?></span>

    <!-- FREQUENT FLYER INFO PLACEHOLDER -->

</div>

 

Replace the FREQUENT FLYER INFO PLACEHOLDER comment with:


<!-- FREQUENT FLYER INFO PLACEHOLDER -->

<?php if(isset($flight['frequentFlyer'])): ?>

    <span class="flight-freq-flyer">
        <strong><?php echo $flight['frequentFlyer']['program']; ?>:</strong> 
        <span class="flight-miles"><?php echo $flight['frequentFlyer']['awardMilesValue']; ?></span>
        <span class="flight-mile-type"><?php echo $flight['frequentFlyer']['awardMilesName']; ?></span>
    </span>

<?php else: ?>

    <span class="flight-no-miles">No earnings :(</span>

<?php endif; ?>

<!-- FREQUENT FLYER INFO PLACEHOLDER -->

 

This code will be responsible to display three things in our template:

  1. Number of earned award miles,
  2. Name of these award miles as branded by the applicable frequent flyer program,
  3. Name of a frequent flyer program, that is default for the marketing airline of a particular flight.

This information is going to be displayed only if flight earns some miles. Otherwise we’ll write No earnings :( message.

5. Injecting frequent flyer info

Now, when we have ready HTML template, we need to provide data for it. There is already FlightResults class, that does that for flight results information. Let’s open api/FlightResults.php file and modify it to include there frequent flyer data too.


public function __construct($response){

    $this->_helper = new Helper();

    $this->_response = $response;

}

 

Constructor of the FlightResults class takes as argument response with flight information. This is later used in toHtml method, that produces final markup. We’re going to re-write constructor logic to inject frequent flyer information into _response property:


public function __construct($response){

    $this->_helper = new Helper();

    // appends frequent flyer information to flight search results data
    $this->_response = $this->_appendFrequentFlyerInfo($response);

}

 

We need to add _appendFrequentFlyerInfo method too:


protected function _appendFrequentFlyerInfo($response){

    // validates flight search results
    if(!is_array($response['flights']) || count($response['flights']) <= 0)="" return="" $response;="" creates="" milefy="" api="" client="" $milefyapiclient="new" milefyapiclient($response['flights']);="" fetches="" array="" of="" flights="" containing="" frequent="" flyer="" info="" $milefyflights="$milefyApiClient-">getFlights();

    if(!$milefyFlights) return $response;

    // for every flight append frequent flyer info if exists
    foreach($response['flights'] as &$flight){

        // find flight with frequent flyer info
        $milefyFlight = $this->_helper->find($milefyFlights, $flight['flightId'], 'flightId');

        // no frequent flyer info?
        if(!$milefyFlight) continue;

        // append frequent flyer info to the flight
        $flight['frequentFlyer'] = $milefyFlight;

    }

    return $response;

}

 

This method simply merges each flight from result set with computed mileage earnings referenced by $milefyApiClient object. Each flight has its own ID, that is used to match it with its mileage earnings returned by Milefy API.

Finally the frequent flyer information is stored under frequentFlyer key for every flight to be used in HTML template.

Since we’re going to use MilefyApiClient class, we need to include it into the script. Add this line just after PHP opening tag:


require('MilefyApiClient.php');

 

6. Milefy API client

In the step before we used MilefyApiClient class, that does not exist yet. We’re going to add it right now. It’s going to perform communication with Milefy API and produce array of flight IDs with their respective mileage earnings.

Create a file named MilefyApiClient.php inside api folder and copy the code below:


<?php
class MilefyApiClient{

    public function __construct($flightResults){

        session_start();

        // Read Milefy API credentials stored as environmental variables
        $this->_apiBaseUrl = getenv('MILEFY_BASE_URL');
        $this->_apiUsername = getenv('MILEFY_USERNAME');
        $this->_apiPassword = getenv('MILEFY_PASSWORD');

        // Store flight search results info
        $this->_flightResults = $flightResults;

        $this->_helper = new Helper();

    }

    public function getFlights(){
        // TO DO
    }

    // Flight search results - list of flights
    protected $_flightResults = null;

    // A helper object
    protected $_helper = null;

    / Milefy API access credentials
    protected $_apiBaseUrl;
    protected $_apiUsername;
    protected $_apiPassword;

    // Default error
    const DEFAULT_FAILURE_MESSAGE = 'Unknown processing error.';

    // Default timeout
    const REQUEST_TIMEOUT = 120000;
}

 

MilefyApiClient class contains just a few lines of code for the initialization: creates PHP session, copies API credentials from environmental variables to local ones and stores flight results for further reference.

Now we need to create a factory method, that will return us object capable of sending HTTP requests to Milefy API. We’ll name it _createHttpClient and specify two parameters it uses: HTTP method and API endpoint.


protected function _createHttpClient($method, $endpoint){

    $client = EasyRequest::create($this->_apiBaseUrl . $endpoint);

    $client

        // Basic authentication using API credentials
        ->withAuth($this->_apiUsername . ':' . $this->_apiPassword)

        // Headers sent with every request
        ->withHeader(self::$_DEFAULT_HEADERS)

        // Timeout to prevent request termination
        ->withTimeout(self::REQUEST_TIMEOUT);

    return $client;

}

 

This way we already set up authentication for the Milefy API using HTTP Basic Authentication header. Additionally we set other request headers to default values:


protected static $_DEFAULT_HEADERS = array(
    'Accept-Language' => 'en-US,en;q=1',
    'Accept' => 'application/hal+json;q=1, application/json;q=0.8',
    'Content-Type'=> 'application/json;charset=UTF-8',
    'X-Api-Version' => 'v2.8'
);

 

Make sure to set properly X-Api-Version header, because if absent, you will receive as fallback, eldest version of our API.

7. Create a user

Since mileage earnings and other calculations can be personalized (not covered in this tutorial), you need to specify unique user ID for which requests are performed. There is however no need to create a new user every time you need to calculate miles. One traveler can make many flight searches. He can even leave your website or application and return later. We recommend to identify returning users and re-use their existing user IDs. For this purpose we’ll add another method to the MilefyApiClient class:


protected function _getUserId(){

    // our user id is stored only in cookie
    if(!isset($_COOKIE['userId'])) setcookie('userId', uniqid(), time() + 3600 * 24 * 365);
    return $_COOKIE['userId'];

}

 

The _getUserId method serves as a proxy, trying first to identify existing user with his user ID stored in the cookie and then creating new user ID if there is none available.

8. Calculate mileage earnings

The CalculateMiles method requires two major parameters: user ID and flight itineraries to compute miles for. We have both of them, so we can extend content getFlights method using following code:


public function getFlights(){

    // creates HTTP client to communicate with Milefy API
    // CalculateMiles API method returns mileage earnings for specified list of flights
    $client = $this->_createHttpClient('POST', '/api/miles/calculate');

    // generates CalculateMiles request body
    $body = $this->_getCalculateRequestBody();

    // appends request body to the request
    $client->withJson($body);

    // sends CalculateMiles request to Milefy API
    $client->send();

    // response HTTP status code validation
    if($client->getResponseStatus() < 400){

        $response = json_decode($client->getResponseBody());

        // response body validation
        if(isset($response) && $response->Success && $response->Value && is_array($response->Value->flights)){

            // success

            // returns award miles for calculated flights
            return $this->_getFlightsAwardMiles($response->Value->flights);

        }else{

            // failure

            return false;

        }

    }else{

        // failure

        return false;

    }

}

 

The method sends POST request to the /api/miles/calculate method request body containing flight itineraries returned by _getCalculateRequestBody method as well as clientUserId property containing our generated user ID.

Finally successful response is processed by _getFlightsAwardMiles method to return mileage earnings in the simple form of flat array.

Request body

How to prepare request body for the CalculateMiles method? This code snippet should be self-explainatory:


protected function _getCalculateRequestBody(){

    $body = array(

        // User id used by client - can be stored in database with user account or in cookie for temporary visitors. Required.
        'clientUserId' => $this->_getUserId(), 

        // List of flights to calculate miles for. Required.
        'flights' => array()
    );

    // iterate flights to calculate miles for...
    foreach($this->_flightResults as $flight){

        $bodyFlight = array(
            'flightId' => $flight['flightId'],                      // Flight Id. Required.
            'price' => array(                                       // Price. Required.
                'currencyCode' => $flight['price']['currencyCode'], // Currency code (3 letters). Required.
                'total' => $flight['price']['total'],               // Total price. Required.
                'fare' => $flight['price']['fare'],                 // Base fare. Recommended.
                'taxes' => $flight['price']['taxes'],               // Taxes. Recommended.
                'surcharges' => $flight['price']['surcharges']      // Airline surcharges. Recommended.
            ),
            'legs' => array()                                       // Flight legs. Round trip has two, one-way one. Required.
        );

        // iterate throught flight legs
        foreach($flight['legs'] as $leg){

            $bodyLeg = array(
                'legId' => $leg['legId'],                           // Flight leg id. Optional for CalcualteMiles method.
                'segments' => array()                               // Flight segments - every pair of departure and landing. Required.
            );

            // iterate through flight segments
            foreach($leg['segments'] as $segment){

                $bodySegment = array(
                    'segmentId' => $segment['segmentId'],                        // Segment Id. Optional for CalculateMiles.
                    'marketingAirlineCode' => $segment['marketingAirlineCode'],  // Marketing airline IATA code. Required.
                    'operatingAirlineCode' => $segment['operatingAirlineCode'],  // Operating airline IATA code. Required.
                    'deptCode' => $segment['deptCode'],                          // Departure airport IATA code. Required.
                    'destCode' => $segment['destCode'],                          // Destination airport IATA code. Required.
                    'deptDate' => $segment['deptDate'],                          // Departure date in format: YYYY-MM-DD. Required.
                    'fareCode' => $segment['fareCode'],                          // Booking code, a single letter. Required.
                    'flightNumber' => $segment['flightNumber'],                  // Flight number, just digits. Required.
                    'fareBasisCode' => $segment['fareBasisCode'],                // Fare basis code. Recommended.
                    'distance' => $segment['distance']                           // Distance in miles. Recommended.
                );

                $bodyLeg['segments'][] = $bodySegment;

            }

            $bodyFlight['legs'][] = $bodyLeg;

        }

        $body['flights'][] = $bodyFlight;

    }

    return $body;

}

 

9. Format mileage earnings

There is only one task left — to transform response from CalculateMiles method into simple array with itineraries containing number of award miles, name for this mile type as well as program name. That’s what _getFlightsAwardMiles method is responsible for.

The API response, that we got in the previous step doesn’t contain frequent flyer program name, neither name of the award miles for this program. In order to get these, we need to use another API method — Programs. It returns list of all supported frequent flyer programs. This list can (and should) be cached on client. Recommended period to store this data is 24 hours. In this tutorial we’ll use just simple cache in PHP session:


public function getPrograms(){

    // Try to read list of programs from cache
    // - this information can be stored on client server for 1 day

    if(!isset($_SESSION['programs']) || !is_array($_SESSION['programs'])){

        // create HTTP client to connect with Milefy API
        // using Programs method
        $client = $this->_createHttpClient('GET', '/api/miles/programs');

        $client->send();

        // HTTP status code validation
        if($client->getResponseStatus() < 400){

            $response = json_decode($client->getResponseBody());

            // response body validation
            if(isset($response) && $response->Success && is_array($response->Value)){

                // success

                // list of programs stored in cache
                $_SESSION['programs'] = $response->Value;

            }else{

                // failure
                return false;

            }

        }else{

            // failure
            return false;

        }

    }

    // from cache

    return $_SESSION['programs'];

}

 

Now, when we have access to list of supported frequent flyer programs, we should define which type of mile do we want to display to the user. In Milefy API we use constants to identify types of miles (see documentation). Let’s use them to identify award miles in our code:


// Code of award miles in Milefy API
const AWARD_MILES_CODE = 1;

 

Finally we can return mileage earnings with necessary meta data using _getFlightsAwardMiles method:


protected function _getFlightsAwardMiles($responseFlights){

    // result
    $flights = [];

    // returns list of supported frequent flyer programs in Milefy API
    $programs = $this->getPrograms();

    if(!is_array($programs)) return false;

    // for every flight in CalculateMiles mehtod response
    foreach($responseFlights as $responseFlight){

        // Skip flights with no frequent flyer program specified.

        // There might be more than one program earning miles to one flight, but
        // for the sake of simplicity we will display just one.
        if(!is_array($responseFlight->programs) || count($responseFlight->programs) <= 0)="" continue;="" $flight="array(" 'flightid'=""> $responseFlight->flightId
        );

        // skip flights with no mileage earnings
        $responseProgram = $responseFlight->programs[0];
        if(!is_array($responseProgram->earnings) || count($responseProgram->earnings) <= 0)="" continue;="" skip="" flights="" with="" no="" award="" miles="" earned="" $responseawardmiles="$this-">_helper->find($responseProgram->earnings, self::AWARD_MILES_CODE, 'metricCode');
        if(!$responseAwardMiles) continue;

        // fetch frequent flyer program details
        $program = $this->_helper->find($programs, $responseProgram->programCode, 'programCode');
        if(!$program || !is_array($program->metrics)) continue;

        // fetch award miles name specific for the program
        $awardMiles = $this->_helper->find($program->metrics, self::AWARD_MILES_CODE, 'metricCode');
        if(!$awardMiles) continue;

        // for majority of programs mileage earnings are integer values,
        // but sometimes there might be decimals too!
        $decimal = ($responseAwardMiles->value * 100) % 100;
        $precision = $decimal > 0 ? strlen(strval($decimal)) : 0;

        // save number of earned award miles, name of this type of miles and name of default frequent flyer program
        $flight['awardMilesValue'] = number_format($responseAwardMiles->value, $decimal);
        $flight['program'] = $program->programNameWithoutAirline;
        $flight['awardMilesName'] = $awardMiles->metricName;

        $flights[] = $flight;

    }

    return $flights;

}

 

10. Ready!

Now you can test your integration with Milefy API. Run the server and open index.html in your browser. If everything is all right, you should see mileage earnings on every flight result.

Ready flight search result page with mileage earnings.

What’s next?

Discover how to use other features and other developer resources.