[Huawei Developers] Freemind R:
More information like this, you can visit HUAWEI Developer Forum

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/647/156/2640091000001647156.20200731102142.54291843163100307244427365118302:50510812014741:2800:10A9B9F75D9E5623E131EB866B7B2A7FA90B3740CF29DF9E22718D47672C8AAF.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/647/156/2640091000001647156.20200731102142.54291843163100307244427365118302:50510812014741:2800:10A9B9F75D9E5623E131EB866B7B2A7FA90B3740CF29DF9E22718D47672C8AAF.png[/IMG]​

Co-author by Sanghati Mukherjee and Sujith.

The article is the continuity of our Huawei kit series. The series contain four parts. Below are the links:

1) It’s Show Time using Huawei Auth Service and Account kit.

2) It’s Show Time using Huawei Location and Site kit.

3) It’s Show Time using Huawei Wallet kit.

The fourth part is this article where we would learn how to implement the server side of Huawei wallet kit using local Node js server and how to call them in client side in order to create ticket and save the ticket on user wallet app for future use.

Why having your own server?

Using our own server to communicate with app is generally the best option for creating cards or tickets in our application. Because, our app will recognize and trust our own server, allowing us to control all transactions between our server and user devices. That is what we are going to learn today.

Setting up the server

If you are not familiar with setting up your own local server and connecting the server with the device, I would strongly recommend you to go through my previous article i.e. “Build your own server from scratch to send push notification“. My previous article will help you setting up local server from scratch also it is healthy to learn something new every day as our brain can store more information than a computer.

Prerequisite

1) We must have latest version of Node installed.

2) We must have latest version of Visual Studio Code installed.

3) We must have latest version of MongoDB database installed.

4) Laptop/desktop and Huawei mobile device must share same Wi-Fi connection.

5) We must have a working app integrate with HMS Wallet Kit.

We will divide this article into two parts

1) Server Side: The server side contains Node, Express, Request and JavaScript.

2) Client Side: The client side contains Android Native, Java, Retrofit and HMS Wallet Kit.

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/647/156/2640091000001647156.20200731102440.75868649933724596803536533745864:50510812014741:2800:AC92883FE7BA639AA254B6491EC2A4E3AC1B6BF6724563550C10E63177472DAB.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/647/156/2640091000001647156.20200731102440.75868649933724596803536533745864:50510812014741:2800:AC92883FE7BA639AA254B6491EC2A4E3AC1B6BF6724563550C10E63177472DAB.png[/IMG]​

Demo

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/647/156/2640091000001647156.20200731102534.53928546185339193689143825904996:50510812014741:2800:C3C30288CDEF6B5EC6A20B47C2D9BB93E30F786B3EDA324EFBDCC70097AF9A05.gif"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/647/156/2640091000001647156.20200731102534.53928546185339193689143825904996:50510812014741:2800:C3C30288CDEF6B5EC6A20B47C2D9BB93E30F786B3EDA324EFBDCC70097AF9A05.gif[/IMG]​

Server Side

Obtaining app-level access token API

The request header uses AccessToken for authentication, which is obtained using the service API provided by the open platform. The service API provides two modes to obtain AccessToken: authorization code mode and client password mode. This API uses the client password mode.

Do not apply for a new app-level access token each time before the server API is called. Frequent application requests may trigger rejection, leading to application failure within a specified period. Each access token has a validity period.

Within the validity period, it can be used repeatedly. You are advised to apply for an access token again only when the server API is accessed and HTTP result code 401 is returned.

var request = require("request");

const getAppToken = (callBack) => {

var options = {
method: 'POST',
url: 'https://oauth-login.cloud.huawei.com/oauth2/v3/token',
headers:
{
'content-type': 'application/x-www-form-urlencoded',
host: 'Login.cloud.huawei.com',
post: '/oauth2/v2/token HTTP/1.1'
},
form:
{
grant_type: "client_credentials",
client_secret: 'Put Your Client Secret Here...',
client_id: 'Put Your APP ID Here ...'
}
};

request(options, function (error, response, body) {
if (error) throw new Error(error);
var tokenValue = JSON.parse(body);
callBack(tokenValue.access_token);
});
}
exports.getAppToken = getAppToken;

Wallet server address

Set the {url} variable based on the region where the server is located. For details, please refer to Wallet Server Address as shown below.

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/647/156/2640091000001647156.20200731102721.89931709058520840785008361173009:50510812014741:2800:71AE7A7AF60FEA2BBD806FC04B2BFDBAEB1FDA3B601B5931AC8A972037ADCA78.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/647/156/2640091000001647156.20200731102721.89931709058520840785008361173009:50510812014741:2800:71AE7A7AF60FEA2BBD806FC04B2BFDBAEB1FDA3B601B5931AC8A972037ADCA78.png[/IMG]​

Creating a HwWalletObject

We need to set HwWalletObject as body parameter in order to call wallet event ticket APIs. To know more about HwWalletObject follow the link below:

https://developer.huawei.com/consumer/en/doc/HMSCore-References-V5/def-0000001050160319-V5

Creating an event ticket model API

We call this method to add an event ticket model to the Huawei server.

URL: https://passentrust-dra.wallet.hicloud.com/hmspass/v1/eventticket/model

API

var request = require("request");

const getEventTIcketModelObject = (authorizationVal, ticketBookingDate,callBack) => {
var options = {
method: 'POST',
url: 'https://passentrust-dra.wallet.hicloud.com/hmspass/v1/eventticket/model',
headers:
{
'cache-control': 'no-cache',
accept: 'application/json',
authorization: 'Bearer '+authorizationVal,
'content-type': 'application/json'
},
body:
{ passVersion: '1.0',
passTypeIdentifier: 'YOUR_SERVICE_ID',
passStyleIdentifier: 'YOUR_MODEL_ID',
organizationName: 'Huawei',
fields:
{ countryCode: 'zh',
locationList: [ { longitude: '114.0679603815', latitude: '22.6592051284' } ],
commonFields:
[
{ key: 'logo', value: 'https://contentcenter-drcn.dbankcdn.com/cch5/Wallet-WalletKit/picres/cloudRes/coupon_logo.png' },
{ key: 'name', value: 'Is Show Time Movie Ticket' },
{ key: 'merchantName',
value: 'Huawei',
localizedValue: 'merchantNameI18N' },
{ key: 'address', value: 'INOX Cinema' },
{ key: 'ticketType', value: 'Movie ticket' } ],
appendFields: [ { key: 'backgroundColor', value: '#3e454f' } ],
timeList:
[ { key: 'startTime', value: ticketBookingDate },
{ key: 'endTime', value: '2020-08-20T00:00:00.111Z' } ],
localized:
[ { key: 'merchantNameI18N', language: 'zh-cn', value: '华为' },
{ key: 'merchantNameI18N', language: 'en', value: 'Huawei' }
]
}
},
json: true };

request(options, function (error, response, body) {
if (error) {
callBack(error,error);
}
else{
callBack(body);
}
});
}
exports.getEventTIcketModelObject = getEventTIcketModelObject;

The value of passTypeIdentifier parameter is the Service ID, which can be obtained when we apply for HUAWEI Wallet Kit service in AGC and the value of passStyleIdentifier is the Model ID, which can also be obtained from wallet kit service in AGC. Both are mandatory.

Adding an event ticket instance API

We call this method to add the event ticket instance of a user to the Huawei server. After the instance is added using this API, a thin JWE should be used as well to link the instance to the user's HUAWEI ID. Alternatively, a JWE can be used, rather this API, to directly add the instance to the Huawei server and link it to the user's HUAWEI ID.

URL: https://passentrust-dra.wallet.hicloud.com/hmspass/v1/eventticket/instance

API

var request = require("request");

const getEventTIcketInstanceObject = (authorizationVal, serialNumber, ticketBookingDate,seatNumber,userName, movieName,callback) => {
var options = {
method: 'POST',
url: 'https://passentrust-dra.wallet.hicloud.com/hmspass/v1/eventticket/instance',
headers:
{
'cache-control': 'no-cache',
accept: 'application/json',
authorization: 'Bearer '+authorizationVal,
'content-type': 'application/json' },
body:
{
organizationPassId: 'YOUR_APP_ID',
passTypeIdentifier: 'YOUR_SERVICE_ID',
passStyleIdentifier: 'YOUR_MODEL_ID',
serialNumber: serialNumber,
fields:
{ status:
{ state: 'active',
effectTime: ticketBookingDate,
expireTime: '2020-08-20T00:00:00.111Z' },
barCode:
{ text: '562348969211212',
type: 'codabar',
value: '562348969211212',
encoding: 'UTF-8' },
commonFields:
[ { key: 'ticketNumber', value: serialNumber },
{ key: 'title', value: 'Its Show Time' },
{ key: 'name', value: movieName },
{key:'programImage', value:'https://contentcenter-drcn.dbankcdn.com/cch5/Wallet-WalletKit/picres/cloudRes/coupon_logo.png'}
],
appendFields:
[ { key: 'gate', value: 'Gate 2', label: 'Gate' },
{ key: 'seat', value: '24', label: 'Seat' },
{ key: 'userName', value: userName }
]
}
},
json: true };

request(options, function (error, response, body) {
if (error) throw new Error(error);

//console.log(body);
var data = JSON.parse(JSON.stringify(body));
// console.log(data)
callback(body);
});
}
exports.getEventTIcketInstanceObject = getEventTIcketInstanceObject;

The value of organizationPassId parameter is the APP ID, which can be obtained from our AGC.

Create ticket instance API for client

We need an API for client to call the above two APIs on server in order to fetch wallet instance and use it to create a movie ticket.

app.post('/createTicket', (req, res, next) => {
appTokenWallet.getAppToken((callBack) => {

let authorization = callBack
console.log(authorization);
let serialNumber = Math.floor(Math.random() * 50000) + 100000;
console.log(serialNumber);
let ticketBookingDate = convertDateToUTC();
createTicketModel.getEventTIcketModelObject(authorization,ticketBookingDate, callBackMessage => {
console.log(callBackMessage);
});
createTicketInstance.getEventTIcketInstanceObject(authorization,serialNumber,ticketBookingDate,req.body.seat,req.body.username,"Dil Bechara",callBackMsg => {
if(callBackMsg!=null){
res.send(""+serialNumber);
}
});
});
});

Client Side (Android Native)

We need Retrofit in order to call our restful Apis. Include the following dependencies in app build.gradle file.

implementation 'com.squareup.retrofit2:retrofit:2.8.1'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'

Create retrofit interface

Create a new Interface class and name it GetDataService. Open the class, copy and paste below code:

public interface GetDataService {

@POST("/createTicket")
Call<Object> createMovieTicket(@Body HashMap<String, String> map);
}

Create retrofit instance

Create a new class and name it RetrofitClientInstance. Open the class, copy and paste below code:

public class RetrofitClientInstance {
private static Retrofit retrofit;

public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}

Note: The BASE_URL is important here. We will put our IPv4 Address of our server machine instead localhost. To find our machine IPv4 Address, we will go to command prompt and type ipconfig. Also make sure that the device is connected to the same Wi-Fi the server machine is connected too.

Use retrofit instance in the activity

Create an object of retrofit instance in onCreate() method of the activity as show below:

service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class);

After that use this retrofit instance object to call the create ticket instance API of server in order to fetch instance Id and use it to create thin JWE which will be used to create a ticket.

public void proceedToPay(View view) {
progressBar.setVisibility(View.VISIBLE);
HashMap<String, String> ticket = new HashMap<>();
ticket.put("seat", seats);
ticket.put("username","Sanghati Mukherjee");
Call<Object> call = service.createMovieTicket(ticket);
call.enqueue(new Callback<Object>() {
@Override
public void onResponse(Call<Object> call, Response<Object> response) {

passObject = "{\"instanceIds\": [\""+response.body().toString().replace(".0","").trim()+"\"]}";
progressBar.setVisibility(View.GONE);
generateJWEStr();

}

@Override
public void onFailure(Call<Object> call, Throwable t) {
Toast.makeText(PaymentActivity.this, t.getMessage(), Toast.LENGTH_LONG).show();
System.out.println("RESPONSE >>> " + t.getMessage());

progressBar.setVisibility(View.GONE);
}
});
}

When JWE is created the Huawei server will call browser to create ticket as shown below:

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/647/156/2640091000001647156.20200731103536.40836497084639116022782960822277:50510812014741:2800:0F8C1385D36A403F2B50C418F2207EDD85BDFC778058971C1BC25A7876262766.jpg"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/647/156/2640091000001647156.20200731103536.40836497084639116022782960822277:50510812014741:2800:0F8C1385D36A403F2B50C418F2207EDD85BDFC778058971C1BC25A7876262766.jpg[/IMG]​

Now we need to add the ticket in our wallet app for future use which in this case is a movie ticket for INOX cinemas.

GitHub Links

Client Side: https://github.com/DTSE-India-Community/ItsShowTime

Server Side: https://github.com/DTSE-India-Community/Huawei-In-App-Purchase-Push-Kit-Server_Side-And-Wallet-Kit-Server-Side-Implementation

For More Information

https://developer.huawei.com/consumer/en/doc/development/HMSCore-References-V5/create-model-0000001050158460-V5

https://developer.huawei.com/consumer/en/doc/development/HMSCore-References-V5/add-instance-0000001050158466-V5