Most of the code below relates to GUI messaging, but this Android app is full HCE for a Cloud Based Payments card. It is a total of 201 lines of code including comments, and line spaces. It is a single class application for simplicity.
The goal is to show that the libraries for managing CBP on the mobile app are lightweight for developers to use.
The libs are designed to be placed into ANY application that may want to enable CBP. The one below is about the simplest integration.
The full application is called SimplestTapp and can be downloaded and tested here:
http://wiki.simplytapp.com/home/self-driven-hce-pilot/make-a-payment-card/simplytapp-s-vcbp-changelog/software-dev-kits/mobile-sdk
it also may be good to view this help resource for creating and managing the card perso and lifecycle in the cloud:
http://wiki.simplytapp.com/home/using-admin-api-console
The core of the card download and enablement is really contained in this section. This section instantiates a VirtualCard that will be loaded from the remote server. In order to instantiate the CBP card, you will see that there are some required credential information because a request will be made to the CBP server to retrieve the card that will be used.
The data in the snippet is sample data only.
The AsyncTask is used because the load() method makes network calls and can't be run on the UI thread.
// Create a card with data from AdminApi console
// See wiki for help:
// http://wiki.simplytapp.com/home/using-admin-api-console
//
// Values are found within your AdminApi Console. Click Get APP
// Credentials.
//
// card agent code hash is found inside the Issuer Entity portion of the
// web portal
//
final VirtualCard card = VirtualCardBuilder
.cardId("5901")
.mobileAppConsumerKey(
"oJmqoey7I97FBOZQDXoH0A9RbM7BciXbD8CHZDMU")
.mobileAppConsumerSecret(
"ugTD5dGjBUEC1pyzxjMNtw6k9TUocGtY4plta9he")
.walletAccessToken("O15F4jG3PcwC44NpkfEqv1ZD7tjNo5NhGpHHFEax")
.walletTokenSecret("Ur504iPQn2lxgsS5fVBycf2zK9uuSglppvn2mBa3")
.cardAgentCodeHash("aa4185c2364048447a38aa02499a9897")
.context(this).build();
// now load the card...
// this requires network connectivity and is blocking
// so we must use AsyncTask to avoid running this in UI thread
//
new AsyncTask() {
@Override
protected Void doInBackground(Void... params) {
try {
myActivity.postUi("=================================="
+ "\n" + "=====Loading Card From Server====="
+ "\n" + "=================================="
+ "\n");
card.load();
} catch (IOException ex) {
Log.e(TAG, "Failed to load", ex);
}
return (Void) null;
}
}.execute();
Once the VirtualCard successfully loads, it has a mind of it's own and will perform all activities related to making a successful payment when requested to do so. For this, the feedback mechanism from the card to the mobile application is a messaging interface and should be defined in a block similar to the one below. You will notice that the block below is responsible for handling feedback from the card and also notifying the card to activate after it was successfully created:
// define the message default card message handler
// and set it in the lib
static {
VirtualCardMessaging virtualCardMessenger = new VirtualCardMessaging() {
@Override
public void virtualCardMessage(VirtualCardMessaging.Message message) {
switch (message.getCode()) {
case VirtualCardMessaging.CARD_CREATION_COMPLETED:
try {
// when card is created, then activate the card agent,
// then connect it to the ApduService,
// then ask the user if they want this app the default
// app
// for servicing the reader
message.getVirtualCard().activate();
ApduService.setVirtualCard(message.getVirtualCard());
myActivity.postUi("========================"
+ "\n"
+ "=====Card Created======="
+ "\n"
+ "========================"
+ "\n"
+ "==Card Id: "
+ message.getVirtualCard().getVirtualCardId()
+ "\n"
+ "==Num: "
+ message.getVirtualCard()
.getVirtualCardNumber()
+ "\n"
+ "==Card Exp: "
+ message.getVirtualCard()
.getVirtualCardExpDate() + "\n"
+ "==Card Type: "
+ message.getVirtualCard().getVirtualCardType()
+ "\n" + "==Card Logo: "
+ message.getVirtualCard().getVirtualCardLogo()
+ "\n");
myActivity.postUi("========================" + "\n"
+ "=====Card Activating====" + "\n"
+ "========================" + "\n");
} catch (IOException e) {
e.printStackTrace();
}
break;
case VirtualCardMessaging.CARD_ACTIVATE_COMPLETED:
myActivity.postUi("========================" + "\n"
+ "========================" + "\n"
+ "========================" + "\n"
+ "=====Card Activated=====" + "\n"
+ "=====Ready To Tap=======" + "\n"
+ "========================" + "\n"
+ "========================" + "\n"
+ "========================" + "\n");
break;
case VirtualCardMessaging.TRANSACTION_ENDED:
myActivity.postUi("========================" + "\n"
+ "========================" + "\n"
+ "========================" + "\n"
+ "===Ready To Tap Again====" + "\n"
+ "========================" + "\n"
+ "========================" + "\n"
+ "========================" + "\n");
break;
default:
break;
}
// log the message
Log.d(TAG,
message.getMessage() + " cardId="
+ message.getVirtualCardId() + " code="
+ message.getCode());
myActivity.postUi(message.getMessage() + " cardId="
+ message.getVirtualCardId() + " code="
+ message.getCode() + "\n");
}
};
// set the default messenger
VirtualCard.setDefaultVirtualCardMessaging(virtualCardMessenger);
}
You will notice above that the messaging loop is logging all messages from the card and printing them to the screen of the app and the debugger. This can give you an idea of the events the card notifies the app. As far as API requests to the card from the app, the VirtualCard library contains many methods. Here is the JavaDoc:
http://simplytapp.github.io/android/virtualcard/doc/
Full activity class code for this example:
package com.simplytapp.example.simplesttapp;
import java.io.IOException;
import com.simplytapp.virtualcard.ApduService;
import com.simplytapp.virtualcard.VirtualCard;
import com.simplytapp.virtualcard.VirtualCardBuilder;
import com.simplytapp.virtualcard.VirtualCardMessaging;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.cardemulation.CardEmulation;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.widget.TextView;
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
private static MainActivity myActivity = null;
private TextView tv = null;
// this method posts strings to the textview box in the UI thread
private void postUi(final String t) {
new Runnable() {
public void run() {
runOnUiThread(new Runnable() {
public void run() {
tv.append(t);
if (tv.getLayout() != null) {
// auto-scroll vertically
final int scrollAmount = tv.getLayout().getLineTop(
tv.getLineCount())
- tv.getHeight();
// if there is no need to scroll, scrollAmount will
// be <=0
if (scrollAmount > 0)
tv.scrollTo(0, scrollAmount);
else
tv.scrollTo(0, 0);
}
}
});
}
}.run();
}
// define the message default card message handler
// and set it in the lib
static {
VirtualCardMessaging virtualCardMessenger = new VirtualCardMessaging() {
@Override
public void virtualCardMessage(VirtualCardMessaging.Message message) {
switch (message.getCode()) {
case VirtualCardMessaging.CARD_CREATION_COMPLETED:
try {
// when card is created, then activate the card agent,
// then connect it to the ApduService,
// then ask the user if they want this app the default
// app
// for servicing the reader
message.getVirtualCard().activate();
ApduService.setVirtualCard(message.getVirtualCard());
myActivity.postUi("========================"
+ "\n"
+ "=====Card Created======="
+ "\n"
+ "========================"
+ "\n"
+ "==Card Id: "
+ message.getVirtualCard().getVirtualCardId()
+ "\n"
+ "==Num: "
+ message.getVirtualCard()
.getVirtualCardNumber()
+ "\n"
+ "==Card Exp: "
+ message.getVirtualCard()
.getVirtualCardExpDate() + "\n"
+ "==Card Type: "
+ message.getVirtualCard().getVirtualCardType()
+ "\n" + "==Card Logo: "
+ message.getVirtualCard().getVirtualCardLogo()
+ "\n");
myActivity.postUi("========================" + "\n"
+ "=====Card Activating====" + "\n"
+ "========================" + "\n");
} catch (IOException e) {
e.printStackTrace();
}
break;
case VirtualCardMessaging.CARD_ACTIVATE_COMPLETED:
myActivity.postUi("========================" + "\n"
+ "========================" + "\n"
+ "========================" + "\n"
+ "=====Card Activated=====" + "\n"
+ "=====Ready To Tap=======" + "\n"
+ "========================" + "\n"
+ "========================" + "\n"
+ "========================" + "\n");
break;
case VirtualCardMessaging.TRANSACTION_ENDED:
myActivity.postUi("========================" + "\n"
+ "========================" + "\n"
+ "========================" + "\n"
+ "===Ready To Tap Again====" + "\n"
+ "========================" + "\n"
+ "========================" + "\n"
+ "========================" + "\n");
break;
default:
break;
}
// log the message
Log.d(TAG,
message.getMessage() + " cardId="
+ message.getVirtualCardId() + " code="
+ message.getCode());
myActivity.postUi(message.getMessage() + " cardId="
+ message.getVirtualCardId() + " code="
+ message.getCode() + "\n");
}
};
// set the default messenger
VirtualCard.setDefaultVirtualCardMessaging(virtualCardMessenger);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myActivity = this;
tv = (TextView) findViewById(R.id.textView1);
tv.setMovementMethod(new ScrollingMovementMethod());
// try to set this app as the default HCE application app
CardEmulation cardEmulationManager = CardEmulation
.getInstance(NfcAdapter.getDefaultAdapter(this));
ComponentName paymentServiceComponent = new ComponentName(
getApplicationContext(), ApduService.class.getCanonicalName());
if (!cardEmulationManager.isDefaultServiceForCategory(
paymentServiceComponent, CardEmulation.CATEGORY_PAYMENT)) {
Intent intent = new Intent(CardEmulation.ACTION_CHANGE_DEFAULT);
intent.putExtra(CardEmulation.EXTRA_CATEGORY,
CardEmulation.CATEGORY_PAYMENT);
intent.putExtra(CardEmulation.EXTRA_SERVICE_COMPONENT,
paymentServiceComponent);
startActivityForResult(intent, 0);
}
// Create a card with data from AdminApi console
// See wiki for help:
// http://wiki.simplytapp.com/home/using-admin-api-console
//
// Values are found within your AdminApi Console. Click Get APP
// Credentials.
//
// card agent code hash is found inside the Issuer Entity portion of the
// web portal
//
final VirtualCard card = VirtualCardBuilder
.cardId("5901")
.mobileAppConsumerKey(
"oJmqoey7I97FBOZQDXoH0A9RbM7BciXbD8CHZDMU")
.mobileAppConsumerSecret(
"ugTD5dGjBUEC1pyzxjMNtw6k9TUocGtY4plta9he")
.walletAccessToken("O15F4jG3PcwC44NpkfEqv1ZD7tjNo5NhGpHHFEax")
.walletTokenSecret("Ur504iPQn2lxgsS5fVBycf2zK9uuSglppvn2mBa3")
.cardAgentCodeHash("aa4185c2364048447a38aa02499a9897")
.context(this).build();
// now load the card...
// this requires network connectivity and is blocking
// so we must use AsyncTask to avoid running this in UI thread
//
new AsyncTask() {
@Override
protected Void doInBackground(Void... params) {
try {
myActivity.postUi("=================================="
+ "\n" + "=====Loading Card From Server====="
+ "\n" + "=================================="
+ "\n");
card.load();
} catch (IOException ex) {
Log.e(TAG, "Failed to load", ex);
}
return (Void) null;
}
}.execute();
}
}