Exploring Buy with Android Pay

At Google I/O 2016 we saw a lot of attention on Android Pay. It was great to see it finally open up its doors to the UK for us to integrate into our payment experiences. Using Android Pay, applications can implement features that allow users to both buy physical goods and save money in the form of loyalty cards and gift vouchers.

Here at ribot we have a huge interest in payments and loyalty schemes in the retail sector. We’re soon to see it supported in this field by Waitrose, Boots, Aldi, Superdrug, Costa, Starbucks, KFC and Greggs — so in preparation for future projects, I decided to take a deep dive into ‘Buy with Android Pay’ to discover exactly what we could do with it.


Setting up

If you’re not looking to develop for Android Pay just yet, you can skip this step. If you are, there are just a few things that we need to do to get setup to start developing. To begin with, you’ll need to request access to the Android Pay API here.

Next, you’ll need to add the following Google Play Services dependency to your build.gradle file:

compile 'com.google.android.gms:play-services-wallet:8.4.0'

And then you’ll also need to enable the Android Pay API from your Manifest.xml file:

<meta-data
android:name="com.google.android.gms.wallet.api.enabled"
android:value="true" />

Once the above is all complete, we’re ready to take a look at the Android Pay API and its operational flow. 💰

API Process Flow

The Android Pay API is a cloud-based implementation that allows us to both store and access users payment information in-order to buy physical items from merchants. But how does this work? Well rather than transferring all of the details of a users payment card information, Google generates a Virtual Account Number and sends it to the merchant. Using this number, merchants can process payments using their existing payment processor. This generated number can only be used for the transaction in which it was issued, which greatly reduces the risk of any transactional security issues taking place.

In a nutshell, this Android Pay API flow looks like so:

So what’s the point in Android Pay?

Well, the big factor here is security. None of the users credit card details are ever transferred to the merchant — so whilst the merchant is provided with a Virtual Account Number, this only allows them to complete the transaction that it is linked to and only for the amount that it is for. Once this transaction completes, the user will instantly receive a payment confirmation notification with all the relevant details regarding the transaction.

It’s also simpler for the user, there’s no need to fuss over payment and shipping details. You just have to press the ‘Buy with Android Pay’ button on your phone and you’re done!

So increased security + increased simplicity = Happy User 😄

It also makes the whole payment integration process much simpler for us developers. For example, a lot of the hard work is already handled by the framework with out-of-the-box components to implement into our applications. Not only does Android Pay handle this payment processing for us, but also the whole Payment Application Flow in terms of payment method, shipping addresses, error handling and lifecycle events.


There’s obviously a lot that goes on behind the scenes when a transaction takes place, so it’s important to understand when certain events in the flow take place. Not only will this help you for development and debugging purposes, but it will result in a more stable payment flow experience for the user. So to gain a better understanding of the API process flow, let’s take a look at the different points of communication between the merchant, an application and the Android Pay API that take place.

  1. At the first point in the process, the user is shown the checkout screen. The layout of this screen will include an instance of the WalletFragmentclass, which we’ll look at shortly in this article. Here, the class is set to BUY_BUTTON mode, meaning that the WalletFragment will both display an Android Pay button and set up a MaskedWalletRequest in the background.
  2. When the user decides to press the Android Pay button, the application will send this MaskedWalletRequest to the Android Pay API. At this point the Android Pay API loads the MaskedWallet it has been sent.
  3. At this point, an Intent is retrieved in the onActivityResult() method within the application. This Intent contains a MaskedWallet object instance which holds transaction data and shipping / billing address information. Note that this object does not contain any payment details.
  4. Next, the user is shown the Order Confirmation page — this is displayed using an instance of the WalletFragment set to the SELECTION_DETAILS mode. This mode enables the fragment to display user details and options to change both the payment method and shipping/billing addresses used for the transaction.
  5. Once the user is happy with the transaction details, they can continue and confirm the order using the confirmation button displayed on-screen. At this point, the application is required to connect to the API using the GoogleApiClient.
  6. Once we’re successfully connected, our application can continue with the next stage of the process.
  7. Now we’re connected to the Google Api Client, we can make a request to the Pay API and make a FullWalletRequest.
  8. At this point, a FullWallet object instance is returned within an Intent to the onActivityResult() method. This contains similar information to the MaskedWallet object, with the addition of a PaymentMethodTokeninstance and payment descriptions.
  9. Now we have these payment credentials found within the FullWalletobject, our application can send them to the Merchant Server in-order to process the purchase.
  10. Finally, the application notifies the Android Pay API as to whether the transaction was successful or not. At this point we can make the user aware of the result by displaying either a confirmation or error screen to the user.

Constructing the Purchase Flow

Phew 😅 Now we have a better understanding of how the Android Pay API operates, let’s take look at how we can integrate this flow into our application. At an application level, we’re going to end up with something a little like below:

A simple implementation of the Android Pay transactional flow on a mobile device

In order to achieve this, there are some core components that are provided to us by the Android Pay API. These really help to make it simple to implement the payment flow into our application, so let’s take a look.

The WalletFragment Class

Being a core part of the application payment implementation, there’s going to be a few mentions of the WalletFragment class over the next few sections. There are several different parts that make up the WalletFragment, so it would probably be helpful if we take a look at the functionality of them.

WalletFragment

This class is provided by the Android Pay API and is used to:

  • Handle user events in regards to the Android Pay components
  • Automate the Android Pay purchase lifecycle

There are two core components which an application would use an instance of the WalletFragment for, these are the:

Purchase Fragment — This instance of the fragment displays a purchase button to the user and sets up a MaskedWalletRequest which is to be sent to the Android Pay API. This fragment mode is instantiated using the WalletFragmentMode.BUY_BUTTON mode.

Confirmation Fragment — This instance of the fragment displays a set of change controls so that the user can change any details contained in the MaskedWallet. If any changes are made to the displayed details then the MaskedWallet is set with this new information. This fragment mode is instantiated using the WalletFragmentMode.SELECTION_DETAILS mode.

We can create a new instance of the WalletFragment either programatically:

When creating the instance programatically, we simply use the setMode() method with the Builder to state whether we wish the fragment to be of type BUY_BUTTON or SELECTION_DETAILS.

We can also achieve the same effect in our XML layout file:

When declaring the fragment instance via XML, we simply use the fragmentMode attribute to state whether we wish the fragment to be of type buyButton or selectionDetails.

Note: WalletFragment supports API version 12 and above. If you need to support lower versions, be sure to use the SupportWalletFragment.

WalletFragmentOptions & WalletFragmentStyle

When creating the WalletFragment programatically via the newInstance()method, we are required to pass in a WalletFragmentOptions instance to set the options for the fragment (such as styling, displayed controls etc). We can use the WalletFragmentOptions Builder to create a new instance like so:

As you can see there are several different properties we’re setting here, these are:

  • setEnvironment() — The environment to be used by the Wallet, this defaults to ENVIRONMENT_TEST and should be left at that until you have been granted access to the Android Pay production environment.
  • setFragmentStyle() — This sets the style to be used for the fragment. This can be done either by calling setFragmentStyle() passing in a WalletFragmentStyle instance or the resource ID of the style to be used.
  • setTheme() — Sets the theme to be used by the WalletFragment, this can be either THEME_DARK or THEME_LIGHT.
  • setMode() — Sets the mode to be used for the WalletFragment, this can be either BUY_BUTTON or SELECTION_DETAILS.

As mentioned when using the setFragmentStyle() method of the Builder, we can pass in an instance of the WalletFragmentStyle class to set the style of the fragment. We can create a new instance of this like so:

Again, you can see there are several different builder methods being used here to set different properties on the WalletFragmentStyle instance:

  • setBuyButtonText() — Sets the text to be displayed on the button, this can be set to either BUY_WITH, DONATE_WITH or LOGO_ONLY.
  • setBuyButtonAppearance() — Used to set the appearance of the Buy Button. This can be either ANDROID_PAY_DARK, ANDROID_PAY_LIGHT or ANDROID_PAY_LIGHT_WITH_BORDER.
  • setBuyButtonWidth() — Used to set the width of the displayed Buy Button. We can set this to either MATCH_PARENT or WRAP_CONTENT.

There’s plenty of other methods belonging to the WalletFragmentStyle class that we can use when constructing the style for the fragment. Be sure to check them out in the full documentation here.

Now our WalletFragmentStyle instance has been setup to style our fragment, remember to pass it in when calling setFragmentStyle() method to build the WalletFragmentOptions instance!

WalletFragment Initialisation

Finally, before we can display the WalletFragment to our user we must initialize it using the initialize() method.

When doing so, we must prepare an WalletFragmentInitParams instance to pass in during initialization. Here we are required to set several different properties for use when initialization takes place:


Check Android Pay is enabled for the user

Before we give the user the option to carry out a transaction using Android Pay, it’s good practice to first check that the service is enabled and available for them.

The payments API comes with an isReadyToPay() method that we can use to check whether Android Pay is enabled for the current user — this essentially checks that the Android Pay app is installed on the device and ready to be used as the payment method for a transaction.

If the isReadyToPay() method returns true, then we should display the ‘Buy with Android Pay’ button and hide the standard checkout button from the user. This allows them to continue through the transaction using Android Pay as their payment method.

If the method returns false, then the Android Pay button should be hidden and a message displayed to inform the user that Android Pay is not currently available for use. At this point, the standard checkout button should be shown to provide the user with an alternative method to continue with their transaction. This is also a good point to give the user instructions on how (and why) they should setup Android Pay for future transactions.

It’s a good idea to prompt the user to setup Android Pay as it’ll make future checkout experiences quicker

Styling the Payment Button

At this stage of the shopping experience, the user is browsing products in your catalogue and has reached the point where they can purchase an item. Here your application should display a button that allows the user to purchase the item using Android Pay. Clicking this button should always start the payment flow for purchasing a product.

There are currently two approved use cases for offering this functionality to the user, these are when the user is buying an item with Android Pay:

Or making a reservation using Android Pay:

The ‘Buy’ and ‘Book’ text are recommended use cases by the Pay guidelines, it’s probably a good idea not to use your own custom messages as the user is likely to feel more comfortable interacting with the button if it’s consistent with other Android Pay experiences.

We previously looked at the WalletFragmentStyle class, where we can setup the styles to be used for our WalletFragment instance. We know that when creating the WalletFragment for our purchase flow, we have a number of different options when it comes to styling the Android Pay button.

Using the setBuyButtonAppearance() method we looked at, we can theme the button to one of three options:

Dark

walletFragmentStyle.setBuyButtonAppearance(
WalletFragmentStyle.BuyButtonAppearance.ANDROID_PAY_DARK)

Light

walletFragmentStyle.setBuyButtonAppearance(
WalletFragmentStyle.BuyButtonAppearance.ANDROID_PAY_LIGHT)

Light with Border

walletFragmentStyle.setBuyButtonAppearance(WalletFragmentStyle
.BuyButtonAppearance.ANDROID_PAY_LIGHT_WITH_BORDER)

Note: If the standard ‘Buy with Android Pay’ button doesn’t fit in the desired area, it’s recommended to use the smaller ‘simple’ payment button instead.

The simple ‘Android Pay’ button also holds the theming properties as ‘Buy with Android Pay’ button

Creating the Masked Wallet Request

As previously mentioned, when the WalletFragment is initialized we need to create an instance of the MaskedWalletRequest class, this is so that the Masked Wallet information can be retrieved from the Android Pay API. This object contains details such as the Merchant Transaction ID along with the shipping and billing addresses. This MaskedWallet will be retrieved when the user presses the ‘Buy with Android Pay’ button.

Let’s look at the case where we initialize the WalletFragment when we’re at the checkout stage of a transaction. We won’t have the user’s shipping address at this point, so it’s a nice idea to estimate the cost of shipping and tax until the address has been obtained. You can make a proper estimation once the shipping details can be retrieved from the received MaskedWallet.

This MaskedWalletRequest contains several different properties that we can set:

  • setMerchantName() — This is the Merchant Name to be displayed within any UI shown in the checkout process. This is an optional property and if not set then the display name from the merchant’s account will be used by default.
  • setPhoneNumberRequired() — States whether the phone number for the user is required. This should only be set if the phone number is needed to process the order.
  • setShippingAddressRequired() — States whether a shipping address is required from the user.
  • setCurrencyCode() — Set the currency to be used for the transaction. This field is required.
  • setEstimatedTotalPrice() — Set the estimated total price, calculated using the total price of items plus an estimated shipping cost. Note, this amount is limited to $1800, any orders amounting to more than this will be declined.
  • setCart() — Set the shopping card to be used for the transaction. This allows you to set the items and cost for the current purchase, however this is optional.
  • setPaymentMethodTokenizationParameters() — Set the payment method tokenization parameters used when the user selects a payment card to tokenize the details provided by it.

The setPaymentMethodTokenizationParameters() method here takes a parameter in the form of an instance of the PaymentMethodTokenizationParameters class, we use this class to set the parameters for when we’re retrieving the payment credentials within the MaskedWalletRequest. There’s two ways in which this can be done:

Standard payment token request

It’s possible to manually retrieve encrypted Android Pay payment credentials by creating an instance of the PaymentMethodTokenizationParameters class. We can do this by using the Builder class like so:

Payment gateway token request

If using an external payment provider to process payments, then it’s possible to retrieve a gateway token using provider-specific parameters. For example, we can obtain a payment gateway token from stripe like below:

Note: Please check your provider specific documentation on how to achieve this.

This MaskedWalletRequest will then be sent to the API when the user decides to continue to the next stage of the transaction.

Requesting the Masked Wallet

Now we’ve created the MaskedWalletRequest, we need to actually make the request to retrieve it. When the user clicks the ‘Buy with Android pay’ button, the API automatically handles the request for us using our MaskedWalletRequest instance. At this point, Android Pay will present a Chooser Dialog to the user, handle any authorisation and then will return to our activity. Once this does complete, our activity receives the result in the onActivityResult() method in our calling activity.

We can handle this result like so:

Now we’ve retrieved the MaskedWallet instance from the API, we’re ready to confirm the purchase.


Confirming the purchase

Once our application has received back the MaskedWallet instance that we requested, we can now display a confirmation page to the user. This page should contain a total cost for the items being purchased. So at this point, we need to construct a new instance of the WalletFragment class so that we can display these purchase details to the user. You’ll notice that this time around we’re now using the SELECTION_DETAILS WalletFragmentMode:

The MaskedWallet instance that we previously received gives us access to both the shipping and billing address being used in the transaction. Using these, we can now calculate the cost of tax/shipping and add these to the total transaction cost.

At this point, the user has the ability to change both their:

  • Payment method used for the transaction
  • Shipping address used for the transaction

If the user decides to interact with one of the change buttons then a new MaskedWallet instance is returned to the onActivityResult() method of the calling activity — this ensures that we always have a reference to the latest MaskedWallet instance.

If the user has not previously been authorised for Android Pay, then they usually set their preferred payment method and shipping address during setup. However, these change buttons displayed on this screen are important for users that are already authorised when navigating through a transaction flow as they may wish to change these details per-order.

Requesting the Full Wallet

So at this point the user has confirmed their order, great! So now we want to request the FullWallet instance so that we can proceed with processing the user’s payment. In order to request the FullWallet, we need to construct a FullWalletRequest to send to the Android Pay API:

A FullWalletRequest is a request object that contains the transactionId and details regarding the user’s shopping cart. This contains details regarding:

  • The currency code being used for the transaction
  • The total price of the transaction
  • The collection of items being purchased. This is a list of LineIteminstances, containing details such as the description, title, price etc of an item being purchased.

Once constructed, we’re all ready to use it when making the request for the FullWallet as covered in the next step.

Retrieving the Full Wallet

Before we can send payment details to the Merchant we need to retrieve the FullWallet instance from the Android Pay API. To do this, we make use of the FullWalletRequest that we constructed in the previous section. But to begin with, we need to make sure we construct an instance of the GoogleApiClient class in the onCreate() method of our activity so that we can call the loadFullWallet() method from the API when required.

Provided we have done the above, we can then call the loadFullWallet() method to retrieve an instance of the FullWallet, when required.

When this request completes, the onActivityResult() method within our activity will receive the FullWallet instance. We can then retrieve the payment method token from the FullWallet instance like so:

PaymentMethodToken token = fullWallet.getPaymentMethodToken();

And then retrieve the JSON of the token object as a string using the getToken() method:

String tokenJSON = token.getToken();

Once you have this token, it can be sent to the Payment Merchant server to be decrypted and processed. That’s a little out of scope from this article, but it can either be handled by your payment processor or manually as documented by Google here.

Handling Errors

It just so happens that things don’t always work out within applications, so you should be sure to catch any errors that may occur in the returning onActivityResult() methods. We can retrieve the error code from the request like so:

if (data != null) {
int errorCode = data.getIntExtra(
WalletConstants.EXTRA_ERROR_CODE, -1);
}

Once we have the error code we can then handle the error as we desire by checking if it matches an error defined in the WalletConstants class — and if not, we can show a generic error message. The error could be one of:

  • ERROR_CODE_AUTHENTICATION_FAILURE — Occurs when there was an error retrieving the authentication token for the current buyer’s Google Account.
  • ERROR_CODE_BUYER_ACCOUNT_ERROR — Occurs when a problem has been encountered with the buyer’s account, such as it being closed or registered to an unsupported country.
  • ERROR_CODE_INVALID_PARAMETERS — Occurs when the request was missing any parameters, or used any invalid ones, that were required.
  • ERROR_CODE_INVALID_TRANSACTION — Occurs when interactions with either the loadFullWallet() or changeMaskedWallet() methods take place outside of context.
  • ERROR_CODE_MERCHANT_ACCOUNT_ERROR — Occurs when an error takes place with the Google account that belongs to the merchant.
  • ERROR_CODE_SERVICE_UNAVAILABLE — Occurs when the Android Pay service is currently offline for any attempted requests.
  • ERROR_CODE_SPENDING_LIMIT_EXCEEDED — Occurs when the user attempts to make a payment that would put them over their spending limit.
  • ERROR_CODE_UNKNOWN — Occurs when the received error code is of an unknown type.
  • ERROR_CODE_UNSUPPORTED_API_VERSION — Occurs when there is no longer support for the used API request.

Because we have access to these error codes when an error occurs, there’s no excuse not to tailor your applications UI for a specific error case when one occurs!


And that’s it!

Like what you read? Give Mehul Patel a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.