Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
zhaoyo
Product and Topic Expert
Product and Topic Expert
0 Kudos

Business Scenario


Customer wants to have capability to call UBER from a Fiori Launchpad after he/she books ticket online and needs go to cinema. Call UBER would be a good option to get to cinema.

Solution


A PoC is build up for this purpose. Assume the current customer position (source location) and cinema location (destination) is known. We will focus on how to consume UBER APIs to call ride request from SAP UI5 application.

Customer will have visibility of available UBER product and estimated price. Then customer can select which kind of product and request an UBER service.


Technical Detail


In order to consume UBER API, it is necessary to register UBER development account. https://developer.uber.com/dashboard/

Create an app on UBER development account. In the app there are some important information to consume UBER API:



Finding more detail technical document here: https://developer.uber.com/docs/riders/introduction

In our example application, following activities with UBER are implemented:

  • Get UBER user profile that has authorized with the application

  • Get UBER available product

  • Get price for product

  • Request estimation for specific product

  • Send UBER request for specific product and estimation


Get User Profile


Reference: https://developer.uber.com/docs/riders/references/api/v1.2/me-get

We can test web APIs with different tools. In my example, the tool “Postman” is used.



The “Access Token” of the UBER app can be used here as header parameter “Authorization”. This request returns general information of registered UBER user.

Get Products


Reference: https://developer.uber.com/docs/riders/references/api/v1.2/products-get



The current location (latitude&longitude) is passed to request URL to get available products on this position. The service will return detail information of available products.

Get Prices


Reference: https://developer.uber.com/docs/riders/references/api/v1.2/estimates-price-get



In this request, both source position and destination positon are passed to URL. System will return estimated prices based on available products.

Request Estimation


Reference: https://developer.uber.com/docs/riders/references/api/v1.2/requests-estimate-post





This request will return the estimated information for specific product and location information (source & destination location). The result of estimation will be used in the next step to create ride request.

Create Ride Request


Reference: https://developer.uber.com/docs/riders/references/api/v1.2/requests-post





This service will create ride request of selected product and fare_id which was created in previous step. After service is posted, the request will be processed and waiting for response from UBER drivers. The status will be updated accordingly based on rider interaction.

Note: the access token which is generated in the UBER app configuration page cannot be used to create ride request. Refer to next section for access token generation.

User Access Token Creation


Reference: https://developer.uber.com/docs/riders/guides/authentication/user-access-token

The Uber API uses OAuth 2.0 to allow developers to get a user access token to access a single user’s data or do actions on their behalf. There are following steps to get user access token:

  1. Go to UBER app admin screen. On the tab “Auth” we can select what scope will be available. In this step, make sure the scope “Request” is selected:

  2. UBER provides an authorization page to grant permission to your UBER app. Use following URL to get authorization code:
    https://login.uber.com/oauth/v2/authorize?client_id=<YOUR_CLIENT_ID>&response_type=code&redirect_uri...;

    replace all <PLACEHOLDER> in the URL. The placeholder <YOUR_CLIENT_ID> can be derived from UBER app admin page. You can leave placeholder <YOUR_REDIRECT_URL> blank and system will return authorization code in URL.Note: in the UBER app admin page, the “redirect URI” is configured as “http://localhost/”. So here the authorization code redirect to localhost.

    Copy the authorization code from returned URL. This code will be used to generate user access token in next step.

  3. Use the Token Exchange endpoint to exchange the authorization code for an access_token which will allow you to make requests on behalf of the user.
    In this step, send request “https://login.uber.com/oauth/v2/token” with following parameter to get user access token:The access token has expiry time. So we have to refresh access token after it is expired.

  4. When the user’s access_token has expired, you can obtain a new access_token by exchanging the refresh_token associated with the access_token using the Token Exchange endpoint. Refreshing the user access token means that you don’t need to ask the user to authorize your app for the same permissions again. According to the API document, use following request to refresh access token:


Handle CORS Issue


Reference of CORS concept: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

UBER API will reject request if the origin URIs are not defined in the UBER app admin page. To solve CORS issue, it is necessary to define origin URIs in the tab “Settings” of UBER app admin page.



Here is xml file of main view:
<mvc:View 
controllerName="sap.uber.controller.Main"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:mvc="sap.ui.core.mvc"
displayBlock="true"
xmlns="sap.m"
xmlns:f="sap.ui.layout.form"
xmlns:l="sap.ui.layout"
xmlns:core="sap.ui.core"
xmlns:semantic="sap.m.semantic"
xmlns:table="sap.ui.table">
<App id="idApp">
<pages>
<semantic:FullscreenPage
title="{i18n>title}">
<semantic:content>
<Panel
id="idPanel"
class="sapUiResponsivePadding">
<content>

<f:SimpleForm
editable="true"
layout="ResponsiveGridLayout"
title="Route Information"
labelSpanL="4"
labelSpanM="4"
emptySpanL="4"
emptySpanM="4"
columnsL="2"
columnsM="2">
<f:content>
<core:Title text="Home Location"/>
<Label text="User Name"/>
<Text text="{userapi>/name}"/>
<Label text="First Name"/>
<Text text="{userapi>/firstName}"/>
<Label text="Last Name"/>
<Text text="{userapi>/lastName}"/>
<Label text="Home Address"/>
<Text text="1455 Market St, San Francisco"/>
<core:Title text="Cinema Location"/>
<Label text="Cinema Name"/>
<Text text="Landmark Theaters Embarcadero Center Cinema"/>
<Label text="Cinema Address"/>
<Text text="37.7752415, -122.518075"/>
<core:Title text="Time Information"/>
<Label text="Estimated Arrival Time"/>
<Text id="txtETA" text=""/>
</f:content>
</f:SimpleForm>
<f:SimpleForm
editable="false"
layout="ResponsiveGridLayout"
title="Uber Information"
labelSpanL="4"
labelSpanM="4"
emptySpanL="4"
emptySpanM="4"
columnsL="2"
columnsM="2">
<f:content>
<Label text="Fist Name"/>
<Text text="{profile>/data/first_name}"/>
<Label text="Last Name"/>
<Text text="{profile>/data/last_name}"/>
<Label text="Ride Request Status"/>
<ObjectStatus
class="sapUiSmallMarginBottom"
text="{rideStatus>/data/status}"
state="Success"/>
</f:content>
</f:SimpleForm>
<table:Table
id="productTable"
rows="{product>/data}"
title="Available Products"
selectionMode="Single"
visibleRowCount="5">
<table:columns>
<table:Column width="auto">
<Label text="Product Name"/>
<table:template>
<Text text="{product>display_name}"/>
</table:template>
</table:Column>
<table:Column width="auto">
<Label text="Product Group"/>
<table:template>
<Text text="{product>product_group}"/>
</table:template>
</table:Column>
<table:Column width="auto">
<Label text="Description"/>
<table:template>
<Text text="{product>description}"/>
</table:template>
</table:Column>
<table:Column width="auto">
<Label text="Capacity"/>
<table:template>
<Text text="{product>capacity}"/>
</table:template>
</table:Column>
<table:Column width="auto">
<Label text="Image"/>
<table:template>
<Image src="{product>image}" tooltip="Vehicle Image"/>
</table:template>
</table:Column>
</table:columns>
</table:Table>
<table:Table
id="priceTable"
rows="{prices>/data/prices}"
title="Available Prices"
selectionMode="Single"
visibleRowCount="5">
<table:columns>
<table:Column width="auto">
<Label text="Name"/>
<table:template>
<Text text="{prices>display_name}"/>
</table:template>
</table:Column>
<table:Column width="auto">
<Label text="Distance"/>
<table:template>
<ObjectNumber
number="{prices>distance}"
unit="KM"/>
</table:template>
</table:Column>
<table:Column width="auto">
<Label text="Highest Estimation"/>
<table:template>
<ObjectNumber
number="{prices>high_estimate}"
unit="{prices>currency_code}"/>
</table:template>
</table:Column>
<table:Column width="auto">
<Label text="Lowest Estimation"/>
<table:template>
<ObjectNumber
number="{prices>low_estimate}"
unit="{prices>currency_code}"/>
</table:template>
</table:Column>
<table:Column width="auto">
<Label text="Duration"/>
<table:template>
<ObjectNumber
number="{prices>duration}"
unit="Seconds"/>
</table:template>
</table:Column>
<table:Column width="auto">
<Label text="Estimated Price"/>
<table:template>
<ObjectNumber
number="{prices>estimate}"
unit="{prices>currency_code}"/>
</table:template>
</table:Column>
</table:columns>
</table:Table>
</content>
</Panel>
</semantic:content>
<semantic:customFooterContent>
<Button
id="btnRideRequest"
icon="sap-icon://taxi"
tooltip="Call Uber"
press="onRideRequest"/>
<Button
id="btnRideStatus"
icon="sap-icon://process"
tooltip="Show Status"
press="onRideStatus"/>
</semantic:customFooterContent>
</semantic:FullscreenPage>
</pages>
</App>
</mvc:View>

And the relevant controller class shows below:
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/m/MessageToast"
], function(Controller, MessageToast) {
"use strict";

return Controller.extend("sap.uber.controller.Main", {
onInit: function(){
this.SERVER_TOKEN = "<SERVER_TOKEN>";
this.CLIENT_ID = "<CLIENT_ID>";
this.CLIENT_SECRET = "<CLIENT_SECRET>";
this.ACCESS_TOKEN = "<ACCESS_TOKEN>";
this.oSampleRequest = {
"sourceLat": "37.7752315",
"sourceLng": "-122.418075",
"destinationLat": "37.7752415",
"destinationLng": "-122.518075",
"seat_count": "2"
};
this.oSampleProduct = {
"sourceLat": "37.7752315",
"sourceLng": "-122.418075",
"destinationLat": "37.7899886",
"destinationLng": "-122.4021253",
"seat_count": "2"
};

this.oDialogRequest = sap.ui.xmlfragment("dialogRequest", "sap.uber.view.RideRequest", this.getView().getController());
this.oDialogStatus = sap.ui.xmlfragment("dialogStatus", "sap.uber.view.RideStatus", this.getView().getController());

var meModel = new sap.ui.model.json.JSONModel({
"data": {}
});

var productModel = new sap.ui.model.json.JSONModel({
"data": {}
});

var priceModel = new sap.ui.model.json.JSONModel({
"data": {}
});

var estimateModel = new sap.ui.model.json.JSONModel({
"data": {}
});

var statusModel = new sap.ui.model.json.JSONModel({
"data": {}
});

this.getView().setModel(meModel, "profile");
this.getView().setModel(productModel, "product");
this.getView().setModel(priceModel, "prices");
this.getView().setModel(estimateModel, "estimate");
this.getView().setModel(statusModel, "rideStatus");

this.getMe();
this.getProducts();
this.getPrices();
},

// get uber application data
getMe: function(){
var sUrl = "https://api.uber.com/v1.2/me";
var oModel = this.getView().getModel("profile");

// call uber api
$.ajax({
url: sUrl,
type: "get",
async: true,
headers: {
"Authorization": "Bearer " + this.ACCESS_TOKEN,
"Content-Type": "application/json"
}
}).done(function(results){
$.sap.log.info(results);
oModel.setProperty("/data", results);
}).fail(function(xhr, textStatus, errorThrown){
MessageToast.show("Get Uber User Error");
$.sap.log.error(xhr);
});
},

// get available ride types
getProducts: function(){
var oData = {
"sourceLat": "37.7752315",
"sourceLng": "-122.418075",
"destinationLat": "37.7899886",
"destinationLng": "-122.4021253",
"seat_count": "2"
};
var sUrl = "https://api.uber.com/v1.2/products?latitude=" + oData.sourceLat + "&longitude=" + oData.sourceLng;
var oModel = this.getView().getModel("product");

// call uber api to get available products
$.ajax({
url: sUrl,
type: "GET",
async: true,
headers: {
"Authorization": "Bearer " + this.ACCESS_TOKEN,
"Content-Type": "application/json",
"Accept-Language": "en_EN"
}
}).done(function(results){
$.sap.log.info("get products successfully");
oModel.setProperty("/data", results.products);
}).fail(function(xhr, textStatus, errorThrown){
$.sap.log.error(xhr);
MessageToast.show("Cannot get Uber Products");
});
},

// get available prices
getPrices: function(){
var oData = {
"sourceLat": "37.7752315",
"sourceLng": "-122.418075",
"destinationLat": "37.7752415",
"destinationLng": "-122.518075",
"seat_count": "2"
};

var sUrl = "https://api.uber.com/v1.2/estimates/price?start_latitude=" +
oData.sourceLat +
"&start_longitude=" +
oData.sourceLng +
"&end_latitude=" +
oData.destinationLat +
"&end_longitude=" +
oData.destinationLng;
var oModel = this.getView().getModel("prices");
var that = this;

// call uber api to get estimation
$.ajax({
url: sUrl,
type: "GET",
async: true,
headers:{
"Authorization": "Bearer " + this.ACCESS_TOKEN,
"Content-Type": "application/json"
}
}).done(function(results){
$.sap.log.info("get estimated prices");
oModel.setProperty("/data", results);
}).fail(function(xhr, textStatus, errorThrown){
$.sap.log.error("xhr");
MessageToast.show("Cannot get Price Information");
});
},

// create a ride request
onRideRequest: function(oEvent){
// for test
var oSampleRequest = {
"sourceLat": "37.7752315",
"sourceLng": "-122.418075",
"destinationLat": "37.7752415",
"destinationLng": "-122.518075",
"seat_count": "2"
};

var oTable = this.byId("productTable");
var selectedIndex = oTable.getSelectedIndex();
var oModelProduct = this.getView().getModel("product");
var oModelEstimate = this.getView().getModel("estimate");
var that = this;
// get selected product
if(oModelProduct.getData()){
var oProduct = oModelProduct.getData().data[selectedIndex];
var sUrl = "https://api.uber.com/v1.2/requests/estimate";
// build up the POST data
var oData = {
"product_id": oProduct.product_id,
"start_latitude": oSampleRequest.sourceLat,
"start_longitude": oSampleRequest.sourceLng,
"end_latitude": oSampleRequest.destinationLat,
"end_longitude": oSampleRequest.destinationLng,
"seat_count": oSampleRequest.seat_count
};
// call uber api to request a ride estimation
$.ajax({
url: sUrl,
type: "POST",
async: true,
headers: {
"Authorization": "Bearer " + this.ACCESS_TOKEN,
"Content-Type": "application/json"
},
data: JSON.stringify(oData)
}).done(function(data){
$.sap.log.info("Post ride request is successful");
// update the estimate model
oModelEstimate.setProperty("/data", data);
// open the dialog box to display ride estimation data
if (that.oDialogRequest){
that.oDialogRequest.setModel(oModelEstimate, "estimate");
that.oDialogRequest.open();
}
}).fail(function(xhr, textStatus, errorThrown){
$.sap.log.error("Error during post ride request");
MessageToast.show("Error during post ride request");
});
}
},

// close dialog box
onConfirm: function(oEvent){
// confirm the ride request
var sUrl = "https://sandbox-api.uber.com/v1.2/requests"; //for sandbox only
var modelEstimate = this.getView().getModel("estimate");
var modelProduct = this.getView().getModel("product");
var modelStatus = this.getView().getModel("rideStatus");
var that = this;

// get selected index
var oTable = this.byId("productTable");
var selectedIndex = oTable.getSelectedIndex();

var oPostData = {
"product_id": modelProduct.getData().data[selectedIndex].product_id,
"start_latitude": this.oSampleProduct.sourceLat,
"start_longitude": this.oSampleProduct.sourceLng,
"end_latitude": this.oSampleProduct.destinationLat,
"end_longitude": this.oSampleProduct.destinationLng,
"seat_count": this.oSampleProduct.seat_count,
"fare_id": modelEstimate.getData().data.fare.fare_id
};

$.ajax({
url: sUrl,
type: "POST",
async: false,
headers: {
"Authorization": "Bearer " + this.ACCESS_TOKEN,
"Content-Type": "application/json"
},
data: JSON.stringify(oPostData)
}).done(function(data, statusText, xhr){
switch (statusText){
case "202": //Accepted
// Your Request is successfully being processed
MessageToast.show("Your Request is successfully being processed");
break;
case "409": //Conflict
// An error has occurred, possibly due to no drivers available
MessageToast.show("An error has occurred, possibly due to no drivers available");
break;
case "422": //Unprocessable Entity
MessageToast.show("An error has occurred, most likely due to an issue with the user’s Uber account.");
break;
}
modelStatus.setProperty("/data", data);

// update the ETA
var oText = that.getView().byId("txtETA");
if (oText){
oText.setText("In " + data.pickup_estimate + " Minutes");
}
$.sap.log.info("Request is processed");
}).fail(function(xhr, textStatus, errorThrown){
$.sap.log.info("Error during post ride request");
// show message
var oResponseText = JSON.parse(xhr.responseText);
var sMsg = oResponseText.errors[0].title;
MessageToast.show(sMsg);
});
this.oDialogRequest.close();
},

onCancel: function(oEvent){
this.oDialogRequest.close();
},

onRideStatus: function(oEvent){
if (!this.oDialogStatus){
this.oDialogStatus = sap.ui.xmlfragment("dialogStatus", "sap.uber.view.RideStatus", this.getView().getController());
}

var sUrl = "https://api.uber.com/v1.2/requests/current";
var oModel = this.getView().getModel("status");

// call uber api to get current trip information
$.ajax({
url: sUrl,
type: "GET",
async: true,
headers: {
"Authorization": "Bearer " + this.ACCESS_TOKEN
}
}).done(function(data, statusText, xhr){
oModel.setProperty("/data", data);
this.oDialogStatus.setModel(oModel, "status");
this.oDialogStatus.open();
}).fail(function(xhr, statusText, errorThrown){
MessageToast.show("User is not currently on a trip");
});


}
});
});
13 Comments