Introducing Android Pay

This past September marked the initial rollout of Google's new mobile payments platform called Android Pay. This service allows Android users to make payments with their device at brick-and-mortar stores and in Android apps. These two use cases are referred to as Tap-and-Pay and In-app purchasing respectively. Android's Tap-and-Pay removes the need to swipe your card at a terminal when you're in a store. Instead, you simply hold your device within centimeters of the terminal and your payment information can be transmitted wirelessly using a technology called near-field communication (NFC). In-app purchasing allows customers to complete transactions within a merchant Android app without entering any billing or shipping information-eliminating the need of tediously filling out billing/shipping forms on the limited screens of your device.

One of the core principles that drove the design of Android Pay was simplicity. This was a primary talking point during the Android Pay announcement at Google I/O 2015. Although this almost entirely surrounded the customer experience using Android Pay, it is undoubtedly a theme that merchants will identify when adopting this new method of payment in their apps. This post is going to focus on In-app purchasing from a merchant perspective. It's intended to offer a closer look at how merchants can integrate with this payment service in order to give their customers a secure and streamlined payment experience. Follow this link to view a sample application that accompanies this post.

Payment Gateway

By allowing merchants to maintain their existing payment processor and providing developers with majority of the functionality needed to power an Android Pay transaction, Google has made integrating with Android Pay as painless as possible.

Those less versed in e-commerce, may not initially realize why this is important. There are many players involved when a digital payment occurs. When a customer submits their credit card information via website or mobile app, a payment gateway is the service in charge of forwarding that data to the merchant's payment processor for verification. Once this process is complete, the payment gateway notifies the merchant whether the card was accepted or declined.

Naturally, this draws the question of how the interaction between merchant and payment gateway is affected when card data is transmitted using an Android Pay transaction token. However, leading payment gateways including, Stripe, Braintree, First Data, and Global Payments, have already added API support for integrating with Android Pay.

That being said, I am going to take this post one step further and show how payment information is sent to a payment gateway using this platform. For this post, I will be using Stripe as the payment gateway. Stripe allows you to create a free test account that you can send mock payments to and they are posted to your account dashboard. I will cover exactly how to set up this test account later in this post.

A Departure From Google Wallet

Before jumping into any implementation details using Android Pay, I'd like to briefly go over some background information regarding the history of this platform. For those that don't know, Google also has a similar payment service called Google Wallet that has been around since 2011. Originally, Google Wallet allowed users to store their card information on the app and offered the same Tap-and-Pay functionality that exists with Android Pay. This has since caused a lot of confusion when comparing the two. However, since the release of Android Pay, Google Wallet has shifted gears and is focusing solely on peer-to-peer (P2P) payments. You can think of Android Pay as a revamped and rebranded version of the original Google Wallet. In fact, the implementation of Android Pay reuses a lot of the API's that powered Google Wallet. You'll notice this once we start looking at the code involved in Android Pay integration; a lot of core classes use "Wallet" in their naming conventions.

Android Pay API

Providing a secure payment experience is the central motivation behind Android Pay. What makes this API powerful, though, is that it allows merchants to preserve the payment flow that their users already experience. The only UI changes that need to be made are adding an Android Pay button to your checkout screen and a selection details view to your confirmation screen.

What's even better is both of these added views are provided using Android's WalletFragment. This class handles user events and automates core functionality that drives In-app purchasing. Later, you'll see exactly how to use this class to create the purchase button and selection details.

During your payment flow, Android Pay passes shipping and billing information to your app using MaskedWallet. This java object contains obfuscated billing and shipping information that you will display to your user when navigated to the confirmation screen. When the user confirms their payment information, Android Pay connects to Google Services to retrieve your payment token. Google Services uses a wrapper object, FullWallet, to deliver this payment token, which you will send to your payment processor. When ready to acquire these, your app will fire off corresponding requests-MaskedWalletRequest and FullWalletRequest -using the Android Pay API.

The flow chart below, provided by Google, illustrates this process from a technical perspective. Don't worry about studying this chart too closely right now, the rest of this post will be used to explain in detail how to coordinate these events.

Setup

As with all applications, there's some overhead you need to handle before starting development.

Create Stripe Test Account

Creating a test account with Stripe couldn't be easier. Follow this link to create your user name and password. Click 'Create your Stripe Account' and you're ready to go. In order to send payment tokens under this account you need two API Keys. You will use these later on in your app. For now, follow the images below to obtain these.

Upon logging in, go to Your account > Account Settings in the top right corner of your dashboard

Go to the API Keys tab. This is where you will find your Test Secret Key and Test Publishable Key

Checkout Screen

Your payment flow begins when the user navigates to the checkout screen. This is where you add the Android Pay button, which initiates the entire Android Pay flow. Clicking this button launches the Android Pay app, where the user will choose the card they want to use. Upon selection, Android Pay sends back an object called MaskedWallet containing masked billing information (i.e. last four digits of the card). This information will be used on your confirmation screen.


There are three general updates that need to be made to your checkout screen:


  • Create your MaskedWalletRequest
  • Add WalletFragment to your layout
  • Consume the MaskedWallet in your Activity's onActivityResult() method.

Create Masked Wallet Request

Before your MaskedWalletRequest is ready to be sent, there are two main parameters you need to supply. The first is a Cart object that represents the user's shopping cart. Cart contains a list of LineItem objects that define each individual charge being made including tax and shipping. So, for each item in the user's cart, create a LineItem object, setting basic information like the price, description, and currency.

MaskedWalletRequest.Builder builder = MaskedWalletRequest.newBuilder()
 .setMerchantName("Merchant Name")
 .setPhoneNumberRequired(true)
 .setShippingAddressRequired(true)
 .setCurrencyCode("USD")
 .setEstimatedTotalPrice(cartTotal)
 .setCart(Cart.newBuilder()
 .setCurrencyCode("USD")
 .setTotalPrice(cartTotal)
 .addLineItem(LineItem.newBuilder()
 .setCurrencyCode("USD")
 .setDescription("item description")
 .setQuantity("1")
 .setUnitPrice("100.00")
 .setTotalPrice("100.00")
 .build())
 .addLineItem(LineItem.newBuilder()
 .setCurrencyCode("USD")
 .setDescription("shipping")
 .setRole(LineItem.Role.SHIPPING)
 .setTotalPrice("8.00")
 .build())
 .addLineItem(LineItem.newBuilder()
 .setCurrencyCode("USD")
 .setDescription("tax")
 .setRole(LineItem.Role.TAX)
 .setTotalPrice("5.50")
 .build())
 .build());
builder.setPaymentMethodTokenizationParameters(paymentMethodParameters);
builder.build();

The second parameter you need to pass in is called PaymentMethodTokenizationParameters. This is necessary because Android Pay has support from a handful of different Payment Gateways. As it turns out, each one has requirements for how this payment token is accepted. For this example, we must specify a Stripe token. Then we pass in the publishable key that was issued when we created the Stripe test account. Finally, we set the version of Stripe we are using.

mPaymentMethodParameters = PaymentMethodTokenizationParameters.newBuilder()
 .setPaymentMethodTokenizationType(PaymentMethodTokenizationType.PAYMENT_GATEWAY)
 .addParameter("gateway", "stripe")
 .addParameter("stripe:publishableKey", )
 .addParameter("stripe:version", com.stripe.Stripe.VERSION)
 .build();

The next step is to initialize your WalletFragment and pass this request into it.

Add Wallet Fragment to your Layout

Creating your WalletFragment largely involves setting attributes that will affect its appearance on the screen. As you can see below, this is done using the two classes WalletFragmentStyle and WalletFragmentOptions.

WalletFragmentStyle walletFragmentStyle = new WalletFragmentStyle()
 .setBuyButtonText(BuyButtonText.BUY_WITH_GOOGLE)
 .setBuyButtonAppearance(BuyButtonAppearance.CLASSIC)
 .setBuyButtonWidth(Dimension.MATCH_PARENT);

WalletFragmentOptions walletFragmentOptions = WalletFragmentOptions.newBuilder()
 .setEnvironment(WalletConstants.ENVIRONMENT_SANDBOX)
 .setFragmentStyle(walletFragmentStyle)
 .setTheme(WalletConstants.THEME_LIGHT)
 .setMode(WalletFragmentMode.BUY_BUTTON)
 .build();
mWalletFragment = SupportWalletFragment.newInstance(walletFragmentOptions);

WalletFragmentInitParams.Builder startParamsBuilder = WalletFragmentInitParams.newBuilder()
 .setMaskedWalletRequest(maskedWalletRequest)
 .setMaskedWalletRequestCode(YOUR_MASKED_WALLET_REQUEST_CODE);
mWalletFragment.initialize(startParamsBuilder.build());

// add Wallet fragment to the UI
getActivity().getSupportFragmentManager().beginTransaction()
 .replace(R.id.continueWithAndroidPayButton, mWalletFragment)
 .commit();

There are, however, two lines of code that ultimately affect setting up this fragment correctly. The first is setMode(WalletFragmentMode.BUY_BUTTON). This parameter signifies that we are using WalletFragment as the Android Pay button. Remember, this fragment is used for the Android Pay button and the transaction details view. The second line of code that is important to note is setMaskedWalletRequest(maskedWalletRequest). This is where the MaskedWalletRequest (created in the previous section) is passed in. When the user clicks the Android Pay button, control will be passed to WalletFragment, which handles launching the Android Pay app. It is also necessary to pass in a request code in order to consume the MaskedWallet returned from Android Pay. The following section demonstrates how to retrieve this when the time comes.


Consume Masked Wallet Response

Android Pay will pass back the MaskedWallet in your Activity's onActivityResult() method. Extract this from the returned Intent.

MaskedWallet maskedWallet =
 data.getParcelableExtra(WalletConstants.EXTRA_MASKED_WALLET);

Once you've successfully retrieved the MaskedWallet , you are ready to launch your Confirmation passing this in as an extra.

Confirmation Screen

At this point, you have all the information needed to successfully obtain a payment token and complete a transaction. The steps below capture what needs to be changed to achieve this.

  • Create your FullWalletRequest
  • Add WalletFragment to your layout
  • Consume the FullWallet in your Activity's onActivityResult() method.

Add Wallet Fragment to your Layout

In your code, WalletFragment is created the same way you added it to your checkout screen. This time, however, you are going to set the Mode to WalletFragmentMode.SELECTION_DETAILS and pass in the MaskedWallet that was returned to the checkout screen. This tells WalletFragment that we are on the confirmation screen and it will use the shipping/billing information contained in the MaskedWallet to create a card selection details view.

WalletFragmentStyle walletFragmentStyle = new WalletFragmentStyle()
 .setMaskedWalletDetailsTextAppearance(
 R.style.SelectionDetailsTextStyle)
 .setMaskedWalletDetailsHeaderTextAppearance(
 R.style.SelectionDetailsHeaderTextStyle)
 .setMaskedWalletDetailsBackgroundColor(
 getResources().getColor(R.color.selection_details_background))
 .setMaskedWalletDetailsButtonBackgroundResource(
 R.drawable.btn_default_holo_light);

WalletFragmentOptions walletFragmentOptions = WalletFragmentOptions.newBuilder()
 .setEnvironment(WalletConstants.ENVIRONMENT_SANDBOX)
 .setFragmentStyle(walletFragmentStyle)
 .setTheme(WalletConstants.THEME_LIGHT)
 .setMode(WalletFragmentMode.SELECTION_DETAILS)
 .build();
walletFragment = SupportWalletFragment.newInstance(walletFragmentOptions);

WalletFragmentInitParams.Builder startParamsBuilder = WalletFragmentInitParams.newBuilder()
 .setMaskedWallet(mMaskedWallet)
 .setMaskedWalletRequestCode(REQUEST_CODE_CHANGE_MASKED_WALLET);
walletFragment.initialize(startParamsBuilder.build());

// add Wallet fragment to the UI
getActivity().getSupportFragmentManager().beginTransaction()
 .replace(R.id.dynamic_wallet_masked_wallet_fragment, walletFragment)
 .commit();

Create Full Wallet Request

The FullWalletRequest requires less information than MaskedWalletRequest. It takes in the same Cart object you created on the checkout screen, but also requires a unique id -maskedWallet.getGoogleTransactionID()- that Android Pay returns in yourMaskedWallet .

maskedWallet = getArguments().getParcelable(EXTRA_MASKED_WALLET);
FullWalletRequest fullWalletRequest = FullWalletRequest.newBuilder()
 .setGoogleTransactionId(maskedWallet.getGoogleTransactionId())
 .setCart(Cart.newBuilder()
 .setCurrencyCode("USD")
 .setTotalPrice(cartTotal)
 .setLineItems(lineItems)
 .build())
 .build();

Connect to Google Services

Your payment token is provisioned from Google Play services. Google Play services offer a handful of Google API's you can use to enhance your apps (i.e. Maps, Google+, Cloud Messaging, Analytics). In this instance, you need access to Wallet API. This requires that you open a service connection using GoogleApiClient. To enable Wallet, you simply specify this API using the method GoogleApiClient.addApi(Api).

// Set up an API client;
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
 .addConnectionCallbacks(this)
 .addOnConnectionFailedListener(this)
 .setAccountName("My Account Name")
 .addApi(Wallet.API, new Wallet.WalletOptions.Builder()
 .setEnvironment(WalletConstants.ENVIRONMENT_SANDBOX)
 .setTheme(WalletConstants.THEME_HOLO_LIGHT)
 .build())
.build();

You should instantiate this object in your Activity's onCreate(Bundle) using the code above. Then call mGoogleApiClient.connect() in the onStart() and mGoogleApiClient.disconnect() in the onStop().


You'll notice that in order to set up a GoogleApiClient, your confirmation Activity must implement two interfaces: ConnectionCallbacks and OnConnectionFailedListener. These interfaces provide callbacks that are used to notify your app when the client has connected/disconnected from the service or when a failed attempt to connect occurs. These interfaces are not specific to Wallet, but used with all Google Play service API's. Therefore, responding to these callbacks is mostly standard across all API's. Google offers thorough documentation regarding this that can be found here.

Fire off Full Wallet Request

At this point, you have displayed the user's shipping and masked billing information on the screen and you are ready to fire off your FullWalletRequest as soon as the user initiates the payment by clicking the confirmation button.

The following line of code should be placed in your confirmation button's OnClick() method.

Wallet.Payments.loadFullWallet(mGoogleApiClient, fullWalletRequest,
 REQUEST_CODE_RESOLVE_LOAD_FULL_WALLET);

Entry point for interacting with Wallet buyflow APIs

Wallet.Payments contains several methods for procuring wallet information-including FullWallet-associated with a Google account. You pass three arguments into the loadFullWallet(GoogleApiClient googleApiClient, FullWalletRequest request, int requestCode) method. The FullWalletRequest contains the necessary transaction information for Google to generate a one-time-use payment token, the GoogleApiClient creates the communication channel between your device and Google Services, and the request code will allow you to consume Google's response in youronActivityResult().


Consume Full Wallet Response

Google will invoke your Activity's onActivityResult() callback upon completion of the Full Wallet request. This is where you will retrieve the FullWallet by adding the following code.

FullWallet fullWallet =
 data.getParcelableExtra(WalletConstants.EXTRA_FULL_WALLET);

Google also sends back an error code that can be found in the result intent using the key, WalletConstants.EXTRA_ERROR_CODE. This will be useful when handling errors appropriately. There are a handful of error codes that Google could send back, but you can use the switch statement below to handle different errors uniquely. How you proceed from here really depends on the design of your app. For instance, your app may conform to a global set of rules for handling errors.


void handleError(int errorCode) {
 switch (errorCode) {
 case WalletConstants.ERROR_CODE_SPENDING_LIMIT_EXCEEDED:
 Toast.makeText(getActivity(),
 getString(R.string.spending_limit_exceeded, errorCode),
 Toast.LENGTH_LONG).show();
 break;
 case WalletConstants.ERROR_CODE_INVALID_PARAMETERS:
 case WalletConstants.ERROR_CODE_AUTHENTICATION_FAILURE:
 case WalletConstants.ERROR_CODE_BUYER_ACCOUNT_ERROR:
 case WalletConstants.ERROR_CODE_MERCHANT_ACCOUNT_ERROR:
 case WalletConstants.ERROR_CODE_SERVICE_UNAVAILABLE:
 case WalletConstants.ERROR_CODE_UNSUPPORTED_API_VERSION:
 case WalletConstants.ERROR_CODE_UNKNOWN:
 default:
 // unrecoverable error
 mGoogleWalletDisabled = true;
 displayGoogleWalletErrorToast(errorCode);
 break;
 }
}

Send Payment Token to Stripe

The only steps left before completing this transaction are to extract the token from FullWallet and send it to Stripe. The code below utilizes the Stripe-Java API to send a charge request to Stripe.

RequestOptions requestOptions = (new RequestOptions.RequestOptionsBuilder()).setApiKey(YOUR_STRIPE_SECRET_KEY).build();
Map chargeMap = new HashMap();
chargeMap.put("amount", 100);
chargeMap.put("currency", "usd");
chargeMap.put("source", token); 
try {
 Charge charge = Charge.create(chargeMap, requestOptions);
} catch (StripeException e) {
 e.printStackTrace();
},>,>

Conclusion

As you can see, adding Android Pay payments to your app couldn't be more straightforward. The Android Pay API provides you classes that handle the core functionality involved in making a smooth and secure mobile payment; It bridges the gap between your app, Android Pay, and Google Services.