Google Pay API gives users the opportunity to pay everywhere, using the payment information stored in their Google Accounts. In this lab, you make use of Google Pay's client library for Android to improve the checkout experience of a simplified sample mobile application, by creating a faster, more convenient, and safer experience, which in turn leads to more conversions and happier customers.

Auto T-Shirt Shop is an innovative store that leverages the latest advances in artificial intelligence and using information like style preferences, weather, time of the year, and fashion trends, suggests you the most appropriate item to purchase.

Metrics on engagement are over the roof. Unfortunately, numbers also reflect a large number of abandonments during the checkout process. Determined to tackle that, one of the owners of the project recalls having seen a video showing the promising results that Google Pay yielded for other similar sites, so they decide to give it a go and trust you to take care of the integration.

What you will build

This codelab walks you through integrating Google Pay into an existing application, including determining whether a user is able to pay using a payment method supported by Google Pay, the placement and design of the payment button and the execution of the transaction.

What you will learn

What you will need

Clone the repository from GitHub:

Use the following command to clone the repository into a folder on your computer:

$ git clone https://github.com/google-pay/android-quickstart

Or if you prefer a zip archive:

Download source code

Skim through the sample application

As you can see, the repository features an uncomplicated file structure. The primary objective of this codelab is to give you the ability to adapt this integration to your existing and future applications, independently of the programming language, libraries or tools you choose to work with.

Explore the application

Once your application is built, you can preview it using an emulator or a real device. We recommend you using a device with a Google account and payment methods already associated to it.

The application that this codelab builds on is a demo mobile shop that, using a very advanced machine learning model, suggests one t-shirt to the visitor based on a number of very complex features. Now, if you happen to ask yourself why does the site recommends you a different t-shirt every time you reload the site; well..., truth be told, the very advanced machine learning model is just a random generator (sorry).

This demo shop has been built in a way that it humbly resembles how your existing or potential application might look like today, before you add a means of purchase. In fact, even though we recommend you work on top of this demo application, you are free to go ahead and use this codelab to integrate Google Pay into your existing applications.

Now, if you have not already done so, run the demo application as it currently stands.

If you were able to see the demo application: nothing surprising, right? A product detail view, with a picture, the price, a description and a button to take you to an ordinary payment form.

The objective of this lab is to replace this, in principle, tedious flow with a two-tap experience powered by Google Pay.

Let's plan this!

To better understand this integration, the process is broken apart into the following fundamental steps:

  1. Add the necessary dependencies
  2. Determine ability to pay with Google Pay
  3. Show the button to pay with Google Pay
  4. Create and send the request for payment
  5. Collect the results

Add Google Play services to your build.gradle

The first thing you need to do in order to start using Google Pay API is add the dependency inside of Google Play services containing the packages that you need. To do that, add the following implementation item to your list of dependencies in the build.gradle file inside of your application module. In this codelab, this module is called app:

implementation "com.google.android.gms:play-services-wallet:16.0.1"

Enable the API in your manifest file

Finally, add a meta-data element inside of the application node of your manifest file. This lets the system know that you intend to use this API and enables access to it:

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

The layout and overall experience in your views are crucial aspects that impacts how likely your users are to complete a successful payment transaction. The ability to choose a form of payment in just a few taps using Google Pay enables a broader set of options on where and when to offer users a way to pay in your application. For example, you may add quick checkout options earlier in the process, in areas such as an item's detail view, to allow users to quickly pay for an item they like.

Once you have decided how to arrange your UI and navigation, the next step is to place the button that triggers a payment transaction using Google Pay. The style of this button is expected to conform to a set of guidelines that ensure consistency and familiarity when users see it in your application:

To make this task easier for you, we have bundled all available options, preparing for different resolutions and locales and made it available for download. This bundle includes layout, drawable and values resources that you can paste directly into your project.

You can find the resource that you need to add to your view definition, under the layout directory. To add it to your view, simply place it in the desired position using the include element:

<include
    android:id="@+id/googlePayButton"
    layout="@layout/buy_with_googlepay_button"
    android:layout_width=<your_width_dimension>
    android:layout_height="@dimen/buy_button_height"
    android:visibility="gone"/>

Instantiate the API client

In order to start using the API, it is necessary to instantiate a client object, which you use to make calls to the Google Pay API. You can do that as soon as your activity is created:

private lateinit var paymentsClient: PaymentsClient

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_checkout)

    paymentsClient = createPaymentsClient(this)
}

fun createPaymentsClient(activity: Activity): PaymentsClient {
    val walletOptions = Wallet.WalletOptions.Builder()
            .setEnvironment(WalletConstants.ENVIRONMENT_TEST).build()
    return Wallet.getPaymentsClient(activity, walletOptions)
}

The payment client is initialized with a WalletOptions object. Setting the environment to TEST allows you to experiment with dummy payment information across the entire integration. Once you are ready to create operations that support real transactions, you can update the environment property to PRODUCTION.

The skeleton

Every time you communicate with the Google Pay API, there are a number of configuration parameters that you need to include in your requests, such as the version of the API you are targeting. For the purpose of this codelab, this object also contains information about the payment methods accepted in your application. The final structure looks as follows:

{
    apiVersion: number,
    apiVersionMinor: number,
    allowedPaymentMethods: Array
}

The property allowedPaymentMethods takes a list of payment methods. For every payment method you are required to include the following properties:

{
    type: 'CARD',
    parameters: {
        allowedCardNetworks: Array.<string>,
        allowedAuthMethods: Array.<string>
    }
}

In addition to type and parameters, you later add the tokenizationSpecification property, which is not necessary to determine whether the user in question is able to pay with Google Pay, but it is used by the PaymentDataRequest call to define how the data related to the payment method selected is to be handled. But let's take one step at a time.

The payment method configuration

In this example, you are only going to accept card payments for Mastercard and Visa, both in a tokenized and primary account number (PAN) forms. This is what your payment method looks like:

private val baseCardPaymentMethod = JSONObject().apply {
    put("type", "CARD")
    put("parameters", JSONObject().apply {
        put("allowedCardNetworks", JSONArray(listOf("VISA", "MASTERCARD")))
        put("allowedAuthMethods", JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS")))
    })
}

Putting it all together

Let's recap.

You have defined one payment method to be accepted in your application, and you are going to work with version 2.0 of the API. This is what the resulting configuration looks like:

private val baseCardPaymentMethod = JSONObject().apply {
    put("type", "CARD")
    put("parameters", JSONObject().apply {
        put("allowedCardNetworks", JSONArray(listOf("VISA", "MASTERCARD")))
        put("allowedAuthMethods", JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS")))
    })
}

private val googlePayBaseConfiguration = JSONObject().apply {
    put("apiVersion", 2)
    put("apiVersionMinor", 0)
    put("allowedPaymentMethods",  JSONArray().put(baseCardPaymentMethod))
}

Now that you have your base configuration ready, let's get to the fun part.

One of the main objectives of Google Pay is to provide a faster and more convenient checkout experience for your users. This not only applies to situations in which your users are able to leverage Google Pay to do that, but also in those where they don't. Using the isReadyToPay request allows you to determine readiness to pay with Google Pay, and an opportunity for you to modify the experience in your site accordingly.

Is your user able to pay with Google Pay?

The first thing that you want to do is check whether a specific user who is about to pay in your application, is able to use Google Pay to do so. This request requires you to specify the version of the Google Pay API and allowed payment methods in your site. This is exactly what the base configuration object defined in the previous step contains:

val readyToPayRequest = 
        IsReadyToPayRequest.fromJson(googlePayBaseConfiguration.toString())

val readyToPayTask = paymentsClient.isReadyToPay(readyToPayRequest)
task.addOnCompleteListener { task ->
    try {
        task.getResult(ApiException::class.java)?.let(::setGooglePayAvailable)
    } catch (exception: ApiException) {
        // Error determining readiness to use Google Pay.
        // Inspect the logs for more details.
    }
}

As you can see, if the call returns with an unsuccessful response, there is no further action to be taken in the context of Google Pay. In this situation, the most appropriate next step would be to show additional UI that supports other means of payment.

On the other hand, if the response is successful, you are now ready to allow your users to benefit from using Google Pay, and as a result, you can go ahead and show the Google Pay button to initiate the payment process on user activation (eg.: button click).

Show the button to pay with Google Pay

At this point, you can go ahead and bring the Google Pay button back to the world of visibility:

private fun setGooglePayAvailable(available: Boolean) {
    if (available) {
        googlePayButton.visibility = View.VISIBLE
        googlePayButton.setOnClickListener { requestPayment() }
    } else {
       // Unable to pay using Google Pay. Update your UI accordingly.
    }
}

private fun requestPayment() {
  // TODO: Perform transaction
}

See that you also defined a function to handle button click events. In the next section, you use this function to request a payment method.

Prepare the payment request

At this point, you have loaded the Google Pay API and determined that the user in your application is able to use Google Pay to make a payment. As a result, you have shown the Google Pay payment button in the UI and now your user is ready to initiate the transaction. It is now time to load the payments sheet containing the forms of payment available for the different logged in users.

Just like you did before during the definition of the isReadyToPay request, this call also requires the properties in the base configuration object defined earlier (apiVersion, apiVersionMinor and allowedPaymentMethods) in addition to some new ones. This time, there is a new property called tokenizationSpecification, and additional parameters in your payment methods that are relevant for the purpose of this request only. In addition, transactionInfo and merchantInfo need to be added as well.

Include additional required information in your payment methods

Start by creating a copy of the base card payment method used before. This card payment method now requires a tokenizationSpecification property to define how to handle the data related to the payment method selected, as well as further data requirements needed for the actual transaction: in this example, a full billing address and phone number are required.

The tokenizationSpecification property

The tokenization specification determines how the payment method selected by your users is handled and used to complete a transaction.

There are two different types of handling strategies supported. If you are processing the payment transaction from within your PCI DSS compliant servers, use the DIRECT specification type. In this example, you use a payment gateway to process the payment, hence, you set the PAYMENT_GATEWAY specification type. This is how your tokenization specification shall look like:

private val tokenizationSpecification = JSONObject().apply {
    put("type", "PAYMENT_GATEWAY")
    put("parameters", JSONObject(mapOf(
            "gateway" to "example",
            "gatewayMerchantId" to "exampleGatewayMerchantId")))
}

In the parameters section, you can specify a gateway from the list of providers supported by the Google Pay API, along with additional configuration required by each gateway. For the purpose of this lab, it is sufficient to use the example gateway, which yields test results for the transactions executed.

Additional parameters

Similarly, you can now provide more details about the information that you need to request in order to successfully place the transaction. See how in this example, you need to add the properties billingAddressRequired and billingAddressParameters, to indicate that for this transaction the billing address of the user is required in full format and including a phone number.

private val cardPaymentMethod = JSONObject().apply {
    put("type", "CARD")
    put("tokenizationSpecification", tokenizationSpecification)
    put("parameters", JSONObject().apply {
        put("allowedCardNetworks", JSONArray(listOf("VISA", "MASTERCARD")))
        put("allowedAuthMethods", JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS")))
        put("billingAddressRequired", true)
        put("billingAddressParameters", JSONObject(mapOf("format" to "FULL")))
    })
}

Adding information about the transaction

The transactionInfo property contains an object with financial details about the transaction, namely the price and currency code (ISO 4217 alpha format) along with the status of the price, which can be either final or estimated depending on the nature of the transaction (eg.: the price may vary depending on the shipping address specified):

private val transactionInfo = JSONObject().apply {
    put("totalPrice", "123.45")
    put("totalPriceStatus", "FINAL")
    put("currencyCode", "USD")
}

Adding information about the merchant

The payment request takes information about the merchant performing the request under the merchantInfo property. In this codelab you will focus on two of them:

Once ready, simply add the information about the merchant to the paymentDataRequest object:

private val merchantInfo = JSONObject().apply {
    put("merchantName", "Example Merchant")
    put("merchantId", "01234567890123456789")
}

Request payment information and process the result

Now, merge the previously defined configuration into the final object and pass it on to the loadPaymentData request :

private val paymentDataRequestJson = JSONObject(googlePayBaseConfiguration.toString()).apply {
    put("allowedPaymentMethods", JSONArray().put(cardPaymentMethod))
    put("transactionInfo", transactionInfo)
    put("merchantInfo", merchantInfo)
}

At this point, you have everything you need to ask the Google Pay API for a valid form of payment. To do that, use the loadPaymentData method in the PaymentsClient object, passing in the configuration that you just defined:

val paymentDataRequest = 
        PaymentDataRequest.fromJson(paymentDataRequestJson.toString())

AutoResolveHelper.resolveTask(
        paymentsClient.loadPaymentData(paymentDataRequest),
        this, LOAD_PAYMENT_DATA_REQUEST_CODE)

Calling this method triggers the presentation of the Google Pay payment sheet. If there are no configuration errors, you can see a list of valid payment methods associated with the currently logged in account.

Upon selection, the sheet closes and the result is sent back to your activity and captured through the onActivityResult method:

public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    when (requestCode) {
        LOAD_PAYMENT_DATA_REQUEST_CODE -> {
            when (resultCode) {
                Activity.RESULT_OK ->                    
                    PaymentData.getFromIntent(data)?.let(::handlePaymentSuccess)

                Activity.RESULT_CANCELED -> {
                    // The user cancelled without selecting a payment method.
                }
                
                AutoResolveHelper.RESULT_ERROR -> {
                    AutoResolveHelper.getStatusFromIntent(data)?.let {
                        handleError(it.statusCode)
                    }
                }
            }
        }
    }
}

If the selection is successful, the result is fulfilled with a PaymentData object including relevant information about the payment method selected:

{
  "apiVersionMinor": 0,
  "apiVersion": 2,
  "paymentMethodData": {
    "description": "Visa •••• 1234",
    "tokenizationData": {
      "type": "PAYMENT_GATEWAY",
      "token": "examplePaymentMethodToken"
    },
    "type": "CARD",
    "info": {
      "cardNetwork": "VISA",
      "cardDetails": "1234",
      "billingAddress": {
        "phoneNumber": ...,
        ...
      }
    }
  }
}

You can now use this payment method information to perform the actual transaction.

private fun handlePaymentSuccess(paymentData: PaymentData) {
    val paymentMethodToken = paymentData
            .getJSONObject("tokenizationData")
            .getString("token")

    // Sample TODO: Use this token to perform a payment through your payment gateway
}

You have successfully integrated Google Pay API into your own application.

Now, to take this into production do not forget to take a look at the integration checklist. Once completed and reviewed, you will receive a merchant identifier to add to your client configuration. Similarly, if you are planning to use (or already using) a third party payment processor or gateway, check out the list of supported providers on Google Pay and configure yours. If you are integrating with Google Pay directly, take a look at the documentation section on this topic.

What we've covered

Next steps

Learn more

Did you find this useful?

Very useful! Just enough to meet expectations. Not really.

Would you like to see other codelabs to help you with other types of integration (Direct integration, valuable and passes APIs) ?

Yes please, that'd be grand! I'm happy with what I got.