Skip to Content

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=<YOUR_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");
			});
			
			
		}
	});
});
To report this post you need to login first.

11 Comments

You must be Logged on to comment or reply to a post.

    1. Yong Zhao Post author

      Thanks Chris!

      This is one of PoC we made to integrate UBER APIs in SAPUI5 application, so only the simplest features are implemented.

      Regarding your request, what is your expectation after clicking images?

      (0) 
      1. Christopher Solomon

        That the images enlarge….some other blogs on here do that…..I know I can right click-“view image” and it does that….but just would be nice to save a step and not navigate off blog. haha

        (0) 
        1. DJ Adams

          I’m just wondering whether that is / should be a feature of the blogging platform itself, rather than individual authors having to build a mechanism for every post.

          (1) 
          1. Yong Zhao Post author

            yes. I have to go through each images and add links on them. It would be good if blogging system can do this automatically.

            (1) 
  1. parthibaraja vijayan

    Hi  Youg,

    Thanks for the wonderful blog. It is very well explained.

     

    I have followed all the steps you have mentioned. But I am getting issue related to CORS.

    Here I’m attaching the console log, CORS settings in Uber admin page. Please let me know

    were I’m doing wrong.

     

    Updated the controller with the Access token ,clinet id, client secret and other details.

     

     

    (0) 
    1. Yong Zhao Post author

      Hi Parthibaraja,

      Can you add “https://api.uber.com” as a destination on SCP, and then using destination in the API calling?

      best regards,

      Yong

      (0) 

Leave a Reply