Tutorial

If your Android application is distributed through the Google Play store, you can integrate it with the Google Pay API. You can also configure it to accept payment cards. To integrate your application and configure it to accept payment cards, follow the steps in this tutorial.

Step 1: Define your Google Pay API version

Declare the version of the Google Pay API that your application uses. The major and minor versions affect the fields expected in each passed object and are included in the response.

Create a base request object that contains properties that are present in all other request objects.

Kotlin

    private val baseRequest = JSONObject().apply {
        put("apiVersion", 2)
        put("apiVersionMinor", 0)
    }

Java

  private static JSONObject getBaseRequest() throws JSONException {
    return new JSONObject().put("apiVersion", 2).put("apiVersionMinor", 0);
  }

Step 2: Request a payment token for your payment provider

Google encrypts information about a payer's selected card for secure processing by a payment provider.

Kotlin

private fun gatewayTokenizationSpecification(): JSONObject {
    return JSONObject().apply {
        put("type", "PAYMENT_GATEWAY")
        put("parameters", JSONObject(mapOf(
                "gateway" to "example",
                "gatewayMerchantId" to "exampleGatewayMerchantId")))
    }
}

Java

  private static JSONObject getGatewayTokenizationSpecification() throws JSONException {
    return new JSONObject() {{
      put("type", "PAYMENT_GATEWAY");
      put("parameters", new JSONObject() {{
        put("gateway", "example");
        put("gatewayMerchantId", "exampleGatewayMerchantId");
      }});
    }};
  }

Replace example and exampleGatewayMerchantId with the appropriate values for your payment provider. Use the following table to find the specific gateway and gatewayMerchantId values for your payment provider:

Gateway Parameters and documents
ACI
  "gateway": "aciworldwide"
  "gatewayMerchantId": "YOUR_ENTITY_ID"

Developer docs

Adyen
  "gateway": "adyen"
  "gatewayMerchantId": "YOUR_MERCHANT_ACCOUNT_NAME"

Developer docs

Alfa-Bank
  "gateway": "alfabank"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

APPEX
  "gateway": "epos"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Assist
  "gateway": "assist"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Billing Systems
  "gateway": "billingsystems"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Blue Media
  "gateway": "bluemedia"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

BlueSnap
  "gateway": "bluesnap"
  "gatewayMerchantId": "YOUR_shopToken"

Developer docs

Braintree
  "gateway": "braintree"
  "braintree:apiVersion": "v1"
  "braintree:sdkVersion": "braintree.client.VERSION"
  "braintree:merchantId": "YOUR_BRAINTREE_MERCHANT_ID"
  "braintree:clientKey": "YOUR_BRAINTREE_TOKENIZATION_KEY"

Developer docs

Braspag
  "gateway": "cielo"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

CardConnect
  "gateway": "cardconnect"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Cathay United Bank
  "gateway": "cathaybk"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Chase Merchant Services
  "gateway": "chase"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Checkout.com
  "gateway": "checkoutltd"
  "gatewayMerchantId": "YOUR_PUBLIC_KEY"

Developer docs

CloudPayments
  "gateway": "cloudpayments"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Computop
  "gateway": "computop"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

ConcordPay
  "gateway": "concordpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Credorax
  "gateway": "credorax"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Cybersource
  "gateway": "cybersource"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Datatrans
  "gateway": "datatrans"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

dLocal
  "gateway": "dlocal"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Dotpay
  "gateway": "dotpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

e-SiTef - Software Express
  "gateway": "softwareexpress"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

EasyPay
  "gateway": "easypay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

EBANX
  "gateway": "ebanx"
  "gatewayMerchantId": "YOUR_PUBLIC_INTEGRATION_KEY"

Developer docs

EBANX Pay
  "gateway": "ebanxpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

eCard
  "gateway": "ecard"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

ECPay
  "gateway": "ecpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

No developer docs available

eGHL
  "gateway": "eghl"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

eSafe
  "gateway": "esafe"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

No developer docs available

Evo Payment Gateway
  "gateway": "evopaymentgateway"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

No developer docs available

Fat Zebra
  "gateway": "fatzebra"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

First Data (Payeezy)
  "gateway": "firstdata"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

FreedomPay
  "gateway": "freedompay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Gestpay
  "gateway": "gestpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Global One Pay
  "gateway": "globalonepay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

No developer docs available

Global Payments
  "gateway": "globalpayments"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

GMO Payment Gateway
  "gateway": "gmopg"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

GoPay
  "gateway": "gopay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

GP Webpay
  "gateway": "gpwebpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

No developer docs available

HiTrust
  "gateway": "hitrustpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

iPay88
  "gateway": "ipay88"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

iQmetrix
  "gateway": "iqmetrixpaymentservicesgateway"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

IXOPAY
  "gateway": "ixopay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

JudoPay
  "gateway": "judopay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

No developer docs available

Kassa
  "gateway": "kassacom"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Kineox
  "gateway": "kineox"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

LiqPay
  "gateway": "liqpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

LogPay
  "gateway": "logpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Lyra
  "gateway": "lyra"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Mastercard Payment Gateway Services
  "gateway": "mpgs"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

MOBI.Money
  "gateway": "mobimoney"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Molpay
  "gateway": "molpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

No developer docs available

Moneris
  "gateway": "moneris"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Moneta
  "gateway": "moneta"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

No developer docs available

Monext
  "gateway": "monext"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Money.Mail.Ru
  "gateway": "moneymailru"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Multicarta
  "gateway": "mulitcarta"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Mundipagg
  "gateway": "mundipagg"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

MyCheck
  "gateway": "mycheck"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

MyPay
  "gateway": "mypay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

No developer docs available

Newebpay (formerly STPath, Pay2Go)
  "gateway": "newebpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Nexi
  "gateway": "nexi"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

NMI
  "gateway": "creditcall"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Nuvei
  "gateway": "nuvei"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Payfacto
  "gateway": "payfacto"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

paygent
  "gateway": "paygent"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

PayLane
  "gateway": "paylane"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Payler
  "gateway": "payler"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Payload
  "gateway": "payload"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Paymark
  "gateway": "paymark"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Paymentwall
  "gateway": "paymentwall"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Paymo
  "gateway": "paymo"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

PayOnline
  "gateway": "payonline"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Paysafe
  "gateway": "paysafe"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Payture
  "gateway": "payture"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

PayU
  "gateway": "payu"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Payway
  "gateway": "payway"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Platon
  "gateway": "platon"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Portmone
  "gateway": "portmonecom"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Przelewy24
  "gateway": "przelewy24"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Radial
  "gateway": "radial"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

RBK.money
  "gateway": "rbkmoney"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Redsys
  "gateway": "redsys"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Sberbank
  "gateway": "sberbank"
  "gatewayMerchantId": "YOUR_ORGANIZATION_NAME"

Developer docs

Sipay
  "gateway": "sipay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Softbank Payment Service
  "gateway": "sbps"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Solid
  "gateway": "solid"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Sony Payment Services
  "gateway": "sonypaymentservices"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Spreedly
  "gateway": "spreedly"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Square
  "gateway": "square"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Stripe
  "gateway": "stripe"
  "stripe:version": "2018-10-31"
  "stripe:publishableKey": "YOUR_PUBLIC_STRIPE_KEY"

Developer docs

TapPay (Cherri Tech)
  "gateway": "tappay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Tatra banka (CardPay)
  "gateway": "tatrabanka"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

No developer docs available

Tinkoff
  "gateway": "tinkoff"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

theMAP
  "gateway": "themap"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

TPay.com
  "gateway": "tpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Tranzzo
  "gateway": "tranzzo"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Uniteller
  "gateway": "uniteller"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

UPC
  "gateway": "upc"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Vantiv
  "gateway": "vantiv"
  "vantiv:merchantPayPageId": "YOUR_PAY_PAGE_ID"
  "vantiv:merchantOrderId": "YOUR_ORDER_ID"
  "vantiv:merchantTransactionId": "YOUR_TRANSACTION_ID"
  "vantiv:merchantReportGroup": "*web"

Developer docs

Veritrans
  "gateway": "veritrans"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Vindicia
  "gateway": "vindicia"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

WayForPay
  "gateway": "wayforpay"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Windcave
  "gateway": "windcave"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Wirecard
  "gateway": "wirecard"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Worldnet
  "gateway": "worldnet"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

Worldpay
  "gateway": "worldpay"
  "gatewayMerchantId": "YOUR_WORLDPAY_MERCHANT_ID"

Developer docs

Yandex.Checkout
  "gateway": "yandexcheckout"
  "gatewayMerchantId": "YOUR_SHOP_ID"

Developer docs

The PAYMENT_GATEWAY tokenization type is the most common merchant implementation of the card payment method in the Google Pay API. If your payment provider isn't supported, you might be able to accept Google Pay by a DIRECT integration. For more information, see the Direct tokenization documentation.

Step 3: Define supported payment card networks

Define the card networks that your application accepts.

Kotlin

private val allowedCardNetworks = JSONArray(listOf(
        "AMEX",
        "DISCOVER",
        "INTERAC",
        "JCB",
        "MASTERCARD",
        "VISA"))

Java

private static JSONArray getAllowedCardNetworks() {
  return new JSONArray()
      .put("AMEX")
      .put("DISCOVER")
      .put("INTERAC")
      .put("JCB")
      .put("MASTERCARD")
      .put("VISA");
}

The Google Pay API might return cards on file on Google.com (PAN_ONLY) or a device token on an Android device authenticated with a 3-D Secure cryptogram (CRYPTOGRAM_3DS).

Kotlin

private val allowedCardAuthMethods = JSONArray(listOf(
        "PAN_ONLY",
        "CRYPTOGRAM_3DS"))

Java

private static JSONArray getAllowedCardAuthMethods() {
  return new JSONArray()
      .put("PAN_ONLY")
      .put("CRYPTOGRAM_3DS");
}

For more information, see CardParameters in our JSON object reference. For Android device tokens support, check with your gateway or processor for the card networks supported.

Step 4: Describe your allowed payment methods

In order to describe your application's support for the CARD payment method, combine your supported authentication methods and supported card networks.

Kotlin

    private fun baseCardPaymentMethod(): JSONObject {
        return JSONObject().apply {

            val parameters = JSONObject().apply {
                put("allowedAuthMethods", allowedCardAuthMethods)
                put("allowedCardNetworks", allowedCardNetworks)
                put("billingAddressRequired", true)
                put("billingAddressParameters", JSONObject().apply {
                    put("format", "FULL")
                })
            }

            put("type", "CARD")
            put("parameters", parameters)
        }
    }

Java

  private static JSONObject getBaseCardPaymentMethod() throws JSONException {
    JSONObject cardPaymentMethod = new JSONObject();
    cardPaymentMethod.put("type", "CARD");

    JSONObject parameters = new JSONObject();
    parameters.put("allowedAuthMethods", getAllowedCardAuthMethods());
    parameters.put("allowedCardNetworks", getAllowedCardNetworks());
    // Optionally, you can add billing address/phone number associated with a CARD payment method.
    parameters.put("billingAddressRequired", true);

    JSONObject billingAddressParameters = new JSONObject();
    billingAddressParameters.put("format", "FULL");

    parameters.put("billingAddressParameters", billingAddressParameters);

    cardPaymentMethod.put("parameters", parameters);

    return cardPaymentMethod;
  }

Extend the base card payment method object to describe information expected to be returned to your application, which must include tokenized payment data.

Kotlin

    private fun cardPaymentMethod(): JSONObject {
        val cardPaymentMethod = baseCardPaymentMethod()
        cardPaymentMethod.put("tokenizationSpecification", gatewayTokenizationSpecification())

        return cardPaymentMethod
    }

Java

  private static JSONObject getCardPaymentMethod() throws JSONException {
    JSONObject cardPaymentMethod = getBaseCardPaymentMethod();
    cardPaymentMethod.put("tokenizationSpecification", getGatewayTokenizationSpecification());

    return cardPaymentMethod;
  }

For more information about supported parameters, see CardParameters in our JSON object reference.

In addition to CARD, Google Pay also supports the PAYPAL payment method. For more details about how to add PayPal as a payment method to Google Pay, see PayPal’s developer documentation.

Step 5: Create a PaymentsClient instance

Create a PaymentsClient instance in the onCreate method in your Activity. The PaymentsClient is used for interaction with the Google Pay API.

Kotlin

    fun createPaymentsClient(activity: Activity): PaymentsClient {
        val walletOptions = Wallet.WalletOptions.Builder()
                .setEnvironment(Constants.PAYMENTS_ENVIRONMENT)
                .build()

        return Wallet.getPaymentsClient(activity, walletOptions)
    }

Java

  public static PaymentsClient createPaymentsClient(Activity activity) {
    Wallet.WalletOptions walletOptions =
        new Wallet.WalletOptions.Builder().setEnvironment(Constants.PAYMENTS_ENVIRONMENT).build();
    return Wallet.getPaymentsClient(activity, walletOptions);
  }

Step 6: Determine readiness to pay with the Google Pay API

Add your allowed payment methods to your base request object with the following code snippet:

Kotlin

    fun isReadyToPayRequest(): JSONObject? {
        return try {
            baseRequest.apply {
                put("allowedPaymentMethods", JSONArray().put(baseCardPaymentMethod()))
            }

        } catch (e: JSONException) {
            null
        }
    }

Java

  public static Optional<JSONObject> getIsReadyToPayRequest() {
    try {
      JSONObject isReadyToPayRequest = getBaseRequest();
      isReadyToPayRequest.put(
          "allowedPaymentMethods", new JSONArray().put(getBaseCardPaymentMethod()));

      return Optional.of(isReadyToPayRequest);

    } catch (JSONException e) {
      return Optional.empty();
    }
  }

Before you display the Google Pay button, call the isReadyToPay API to determine if the user can make payments with the Google Pay API. For a full list of configuration properties, see the IsReadyToPayRequest JSON object documentation.

Kotlin

    private fun possiblyShowGooglePayButton() {

        val isReadyToPayJson = PaymentsUtil.isReadyToPayRequest() ?: return
        val request = IsReadyToPayRequest.fromJson(isReadyToPayJson.toString()) ?: return

        // The call to isReadyToPay is asynchronous and returns a Task. We need to provide an
        // OnCompleteListener to be triggered when the result of the call is known.
        val task = paymentsClient.isReadyToPay(request)
        task.addOnCompleteListener { completedTask ->
            try {
                completedTask.getResult(ApiException::class.java)?.let(::setGooglePayAvailable)
            } catch (exception: ApiException) {
                // Process error
                Log.w("isReadyToPay failed", exception)
            }
        }
    }

Java

  private void possiblyShowGooglePayButton() {

    final Optional<JSONObject> isReadyToPayJson = PaymentsUtil.getIsReadyToPayRequest();
    if (!isReadyToPayJson.isPresent()) {
      return;
    }

    // The call to isReadyToPay is asynchronous and returns a Task. We need to provide an
    // OnCompleteListener to be triggered when the result of the call is known.
    IsReadyToPayRequest request = IsReadyToPayRequest.fromJson(isReadyToPayJson.get().toString());
    Task<Boolean> task = paymentsClient.isReadyToPay(request);
    task.addOnCompleteListener(this,
        new OnCompleteListener<Boolean>() {
          @Override
          public void onComplete(@NonNull Task<Boolean> task) {
            if (task.isSuccessful()) {
              setGooglePayAvailable(task.getResult());
            } else {
              Log.w("isReadyToPay failed", task.getException());
            }
          }
        });
  }

As the example shows, it's best to show Google Pay as a payment option only after you receive a successful result from the isReadyToPay function. It's most common to display a Google Pay payment button through a layout include when you implement a payment option. For more information about Google Pay payment buttons, logos, and marks available for use in your application, see Brand guidelines.

Step 7: Create a PaymentDataRequest object

A PaymentDataRequest JSON object describes the information that you request from a payer in a Google Pay payment sheet.

Provide information about the transaction price and the status of the provided price. For more information, see TransactionInfo JSON object documentation.

The following example shows how to get price, price status, and currency transaction information.

Kotlin

    private fun getTransactionInfo(price: String): JSONObject {
        return JSONObject().apply {
            put("totalPrice", price)
            put("totalPriceStatus", "FINAL")
            put("countryCode", Constants.COUNTRY_CODE)
            put("currencyCode", Constants.CURRENCY_CODE)
        }
    }

Java

  private static JSONObject getTransactionInfo(String price) throws JSONException {
    JSONObject transactionInfo = new JSONObject();
    transactionInfo.put("totalPrice", price);
    transactionInfo.put("totalPriceStatus", "FINAL");
    transactionInfo.put("countryCode", Constants.COUNTRY_CODE);
    transactionInfo.put("currencyCode", Constants.CURRENCY_CODE);
    transactionInfo.put("checkoutOption", "COMPLETE_IMMEDIATE_PURCHASE");

    return transactionInfo;
  }

Provide a user-visible merchant name. For more information, see the MerchantInfo JSON object documentation.

The following example shows how to get the merchant's name:

Kotlin

private val merchantInfo: JSONObject =
        JSONObject().put("merchantName", "Example Merchant")

Java

  private static JSONObject getMerchantInfo() throws JSONException {
    return new JSONObject().put("merchantName", "Example Merchant");
  }

Assign your base request object to a new PaymentDataRequest JSON object. Then, add the payment methods supported by your application, such as any configuration of additional data expected in the response. Finally, add information about the transaction and the merchant who makes the request.

The following example shows how to request payment data:

Kotlin

fun getPaymentDataRequest(price: String): JSONObject? {
    try {
        return baseRequest.apply {
            put("allowedPaymentMethods", JSONArray().put(cardPaymentMethod()))
            put("transactionInfo", getTransactionInfo(price))
            put("merchantInfo", merchantInfo)

            // An optional shipping address requirement is a top-level property of the
            // PaymentDataRequest JSON object.
            val shippingAddressParameters = JSONObject().apply {
                put("phoneNumberRequired", false)
                put("allowedCountryCodes", JSONArray(listOf("US", "GB")))
            }
            put("shippingAddressParameters", shippingAddressParameters)
            put("shippingAddressRequired", true)
        }
    } catch (e: JSONException) {
        return null
    }
}

Java


  public static Optional<JSONObject> getPaymentDataRequest(long priceCents) {

    final String price = PaymentsUtil.centsToString(priceCents);

    try {
      JSONObject paymentDataRequest = PaymentsUtil.getBaseRequest();
      paymentDataRequest.put(
          "allowedPaymentMethods", new JSONArray().put(PaymentsUtil.getCardPaymentMethod()));
      paymentDataRequest.put("transactionInfo", PaymentsUtil.getTransactionInfo(price));
      paymentDataRequest.put("merchantInfo", PaymentsUtil.getMerchantInfo());

      /* An optional shipping address requirement is a top-level property of the PaymentDataRequest
      JSON object. */
      paymentDataRequest.put("shippingAddressRequired", true);

      JSONObject shippingAddressParameters = new JSONObject();
      shippingAddressParameters.put("phoneNumberRequired", false);

      JSONArray allowedCountryCodes = new JSONArray(Constants.SHIPPING_SUPPORTED_COUNTRIES);

      shippingAddressParameters.put("allowedCountryCodes", allowedCountryCodes);
      paymentDataRequest.put("shippingAddressParameters", shippingAddressParameters);
      return Optional.of(paymentDataRequest);

    } catch (JSONException e) {
      return Optional.empty();
    }
  }

For more information, see the PaymentDataRequest JSON object documentation.

Step 8: Register event handler for user gesture

To request display of a Google Pay payment sheet after user activation of a Google Pay payment button, define an OnClickListener.

Kotlin

        googlePayButton.setOnClickListener { requestPayment() }

Java

    googlePayButton.setOnClickListener(
        new View.OnClickListener() {
          @Override
          public void onClick(View view) {
            requestPayment(view);
          }
        });

The PaymentDataRequest object is a Parcelable that represents a payment data request. The PaymentDataRequest provides information that's necessary in order to support a payment. Use the AutoResolveHelper class to auto resolve the Task, then handle the result in the onActivityResult method of your Activity class.

Step 9: Handle the response object

After a payer successfully provides the requested information in the Google Pay payment sheet, a PaymentData object is returned to onActivityResult.

To pass payment information to your processor and to present the user with a confirmation of their purchase, convert a successful response to JSON. If the transactionInfo.totalPriceStatus passed to PaymentDataRequest is ESTIMATED, you must show a final price before you charge the returned payment method.

Extract the payment token from the paymentData response. If you implement a gateway integration, pass this token to your gateway without any modifications.

Kotlin

    public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        when (requestCode) {
            // Value passed in AutoResolveHelper
            LOAD_PAYMENT_DATA_REQUEST_CODE -> {
                when (resultCode) {
                    RESULT_OK ->
                        data?.let { intent ->
                            PaymentData.getFromIntent(intent)?.let(::handlePaymentSuccess)
                        }

                    RESULT_CANCELED -> {
                        // The user cancelled the payment attempt
                    }

                    AutoResolveHelper.RESULT_ERROR -> {
                        AutoResolveHelper.getStatusFromIntent(data)?.let {
                            handleError(it.statusCode)
                        }
                    }
                }

                // Re-enables the Google Pay payment button.
                googlePayButton.isClickable = true
            }
        }
    }

Java

  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
      // value passed in AutoResolveHelper
      case LOAD_PAYMENT_DATA_REQUEST_CODE:
        switch (resultCode) {

          case Activity.RESULT_OK:
            PaymentData paymentData = PaymentData.getFromIntent(data);
            handlePaymentSuccess(paymentData);
            break;

          case Activity.RESULT_CANCELED:
            // The user cancelled the payment attempt
            break;

          case AutoResolveHelper.RESULT_ERROR:
            Status status = AutoResolveHelper.getStatusFromIntent(data);
            handleError(status.getStatusCode());
            break;
        }

        // Re-enables the Google Pay payment button.
        googlePayButton.setClickable(true);
    }
  }

For more information about the content and structure of the response, see the PaymentData JSON object reference.

Put it all together

The following snippet shows a complete example for a properly configured project. For the project-level setup steps, see Configure your project.

CheckoutActivity.kt (Kotlin)

This example Activity assumes a Google Pay payment button with an id attribute of googlepay_button exists in your layout.

/*
 * Copyright 2018 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.gms.samples.wallet.activity

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.text.Html
import android.util.Log
import android.view.View
import android.widget.Toast
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.samples.wallet.util.PaymentsUtil
import com.google.android.gms.samples.wallet.R
import com.google.android.gms.samples.wallet.util.Json
import com.google.android.gms.wallet.*
import kotlinx.android.synthetic.main.activity_checkout.*
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject


/**
 * Checkout implementation for the app
 */
class CheckoutActivity : Activity() {

    private val SHIPPING_COST_CENTS = 9 * PaymentsUtil.CENTS.toLong()

    /**
     * A client for interacting with the Google Pay API.
     *
     * @see [PaymentsClient](https://developers.google.com/android/reference/com/google/android/gms/wallet/PaymentsClient)
     */
    private lateinit var paymentsClient: PaymentsClient

    private lateinit var garmentList: JSONArray
    private lateinit var selectedGarment: JSONObject

    /**
     * Arbitrarily-picked constant integer you define to track a request for payment data activity.
     *
     * @value #LOAD_PAYMENT_DATA_REQUEST_CODE
     */
    private val LOAD_PAYMENT_DATA_REQUEST_CODE = 991

    /**
     * Initialize the Google Pay API on creation of the activity
     *
     * @see Activity.onCreate
     */
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_checkout)

        // Set up the mock information for our item in the UI.
        selectedGarment = fetchRandomGarment()
        displayGarment(selectedGarment)

        // Initialize a Google Pay API client for an environment suitable for testing.
        // It's recommended to create the PaymentsClient object inside of the onCreate method.
        paymentsClient = PaymentsUtil.createPaymentsClient(this)
        possiblyShowGooglePayButton()

        googlePayButton.setOnClickListener { requestPayment() }
    }

    /**
     * Determine the viewer's ability to pay with a payment method supported by your app and display a
     * Google Pay payment button.
     *
     * @see [](https://developers.google.com/android/reference/com/google/android/gms/wallet/PaymentsClient.html.isReadyToPay
    ) */
    private fun possiblyShowGooglePayButton() {

        val isReadyToPayJson = PaymentsUtil.isReadyToPayRequest() ?: return
        val request = IsReadyToPayRequest.fromJson(isReadyToPayJson.toString()) ?: return

        // The call to isReadyToPay is asynchronous and returns a Task. We need to provide an
        // OnCompleteListener to be triggered when the result of the call is known.
        val task = paymentsClient.isReadyToPay(request)
        task.addOnCompleteListener { completedTask ->
            try {
                completedTask.getResult(ApiException::class.java)?.let(::setGooglePayAvailable)
            } catch (exception: ApiException) {
                // Process error
                Log.w("isReadyToPay failed", exception)
            }
        }
    }

    /**
     * If isReadyToPay returned `true`, show the button and hide the "checking" text. Otherwise,
     * notify the user that Google Pay is not available. Please adjust to fit in with your current
     * user flow. You are not required to explicitly let the user know if isReadyToPay returns `false`.
     *
     * @param available isReadyToPay API response.
     */
    private fun setGooglePayAvailable(available: Boolean) {
        if (available) {
            googlePayButton.visibility = View.VISIBLE
        } else {
            Toast.makeText(
                    this,
                    "Unfortunately, Google Pay is not available on this device",
                    Toast.LENGTH_LONG).show();
        }
    }
    
    private fun requestPayment() {

        // Disables the button to prevent multiple clicks.
        googlePayButton.isClickable = false

        // The price provided to the API should include taxes and shipping.
        // This price is not displayed to the user.
        val garmentPrice = selectedGarment.getDouble("price")
        val priceCents = Math.round(garmentPrice * PaymentsUtil.CENTS.toLong()) + SHIPPING_COST_CENTS

        val paymentDataRequestJson = PaymentsUtil.getPaymentDataRequest(priceCents)
        if (paymentDataRequestJson == null) {
            Log.e("RequestPayment", "Can't fetch payment data request")
            return
        }
        val request = PaymentDataRequest.fromJson(paymentDataRequestJson.toString())

        // Since loadPaymentData may show the UI asking the user to select a payment method, we use
        // AutoResolveHelper to wait for the user interacting with it. Once completed,
        // onActivityResult will be called with the result.
        if (request != null) {
            AutoResolveHelper.resolveTask(
                    paymentsClient.loadPaymentData(request), this, LOAD_PAYMENT_DATA_REQUEST_CODE)
        }
    }

    /**
     * Handle a resolved activity from the Google Pay payment sheet.
     *
     * @param requestCode Request code originally supplied to AutoResolveHelper in requestPayment().
     * @param resultCode Result code returned by the Google Pay API.
     * @param data Intent from the Google Pay API containing payment or error data.
     * @see [Getting a result
     * from an Activity](https://developer.android.com/training/basics/intents/result)
     */
    public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        when (requestCode) {
            // Value passed in AutoResolveHelper
            LOAD_PAYMENT_DATA_REQUEST_CODE -> {
                when (resultCode) {
                    RESULT_OK ->
                        data?.let { intent ->
                            PaymentData.getFromIntent(intent)?.let(::handlePaymentSuccess)
                        }

                    RESULT_CANCELED -> {
                        // The user cancelled the payment attempt
                    }

                    AutoResolveHelper.RESULT_ERROR -> {
                        AutoResolveHelper.getStatusFromIntent(data)?.let {
                            handleError(it.statusCode)
                        }
                    }
                }

                // Re-enables the Google Pay payment button.
                googlePayButton.isClickable = true
            }
        }
    }

    /**
     * PaymentData response object contains the payment information, as well as any additional
     * requested information, such as billing and shipping address.
     *
     * @param paymentData A response object returned by Google after a payer approves payment.
     * @see [Payment
     * Data](https://developers.google.com/pay/api/android/reference/object.PaymentData)
     */
    private fun handlePaymentSuccess(paymentData: PaymentData) {
        val paymentInformation = paymentData.toJson() ?: return

        try {
            // Token will be null if PaymentDataRequest was not constructed using fromJson(String).
            val paymentMethodData = JSONObject(paymentInformation).getJSONObject("paymentMethodData")
            val billingName = paymentMethodData.getJSONObject("info")
                    .getJSONObject("billingAddress").getString("name")
            Log.d("BillingName", billingName)

            Toast.makeText(this, getString(R.string.payments_show_name, billingName), Toast.LENGTH_LONG).show()

            // Logging token string.
            Log.d("GooglePaymentToken", paymentMethodData
                    .getJSONObject("tokenizationData")
                    .getString("token"))

        } catch (e: JSONException) {
            Log.e("handlePaymentSuccess", "Error: " + e.toString())
        }

    }

    /**
     * At this stage, the user has already seen a popup informing them an error occurred. Normally,
     * only logging is required.
     *
     * @param statusCode will hold the value of any constant from CommonStatusCode or one of the
     * WalletConstants.ERROR_CODE_* constants.
     * @see [
     * Wallet Constants Library](https://developers.google.com/android/reference/com/google/android/gms/wallet/WalletConstants.constant-summary)
     */
    private fun handleError(statusCode: Int) {
        Log.w("loadPaymentData failed", String.format("Error code: %d", statusCode))
    }

    private fun fetchRandomGarment() : JSONObject {
        if (!::garmentList.isInitialized) {
            garmentList = Json.readFromResources(this, R.raw.tshirts)
        }

        val randomIndex:Int = Math.round(Math.random() * (garmentList.length() - 1)).toInt()
        return garmentList.getJSONObject(randomIndex)
    }

    private fun displayGarment(garment:JSONObject) {
        detailTitle.setText(garment.getString("title"))
        detailPrice.setText("\$${garment.getString("price")}")

        val escapedHtmlText:String = Html.fromHtml(garment.getString("description")).toString()
        detailDescription.setText(Html.fromHtml(escapedHtmlText))

        val imageUri = "@drawable/${garment.getString("image")}"
        val imageResource = resources.getIdentifier(imageUri, null, packageName)
        detailImage.setImageResource(imageResource)
    }
}

    

PaymentsUtil.kt (Kotlin)

This example file builds JSON objects suitable to build an IsReadyToPayRequest or a PaymentDataRequest.

/*
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.gms.samples.wallet.util

import android.app.Activity
import com.google.android.gms.samples.wallet.Constants
import com.google.android.gms.wallet.PaymentsClient
import com.google.android.gms.wallet.Wallet
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.math.BigDecimal
import java.math.RoundingMode

/**
 * Contains helper static methods for dealing with the Payments API.
 *
 * Many of the parameters used in the code are optional and are set here merely to call out their
 * existence. Please consult the documentation to learn more and feel free to remove ones not
 * relevant to your implementation.
 */
object PaymentsUtil {
    val CENTS = BigDecimal(100)

    /**
     * Create a Google Pay API base request object with properties used in all requests.
     *
     * @return Google Pay API base request object.
     * @throws JSONException
     */
    private val baseRequest = JSONObject().apply {
        put("apiVersion", 2)
        put("apiVersionMinor", 0)
    }

    /**
     * Gateway Integration: Identify your gateway and your app's gateway merchant identifier.
     *
     *
     * The Google Pay API response will return an encrypted payment method capable of being charged
     * by a supported gateway after payer authorization.
     *
     *
     * TODO: Check with your gateway on the parameters to pass and modify them in Constants.java.
     *
     * @return Payment data tokenization for the CARD payment method.
     * @throws JSONException
     * @see [PaymentMethodTokenizationSpecification](https://developers.google.com/pay/api/android/reference/object.PaymentMethodTokenizationSpecification)
     */
    private fun gatewayTokenizationSpecification(): JSONObject {
        return JSONObject().apply {
            put("type", "PAYMENT_GATEWAY")
            put("parameters", JSONObject(Constants.PAYMENT_GATEWAY_TOKENIZATION_PARAMETERS))
        }
    }

    /**
     * `DIRECT` Integration: Decrypt a response directly on your servers. This configuration has
     * additional data security requirements from Google and additional PCI DSS compliance complexity.
     *
     *
     * Please refer to the documentation for more information about `DIRECT` integration. The
     * type of integration you use depends on your payment processor.
     *
     * @return Payment data tokenization for the CARD payment method.
     * @throws JSONException
     * @see [PaymentMethodTokenizationSpecification](https://developers.google.com/pay/api/android/reference/object.PaymentMethodTokenizationSpecification)
     */
    private fun directTokenizationSpecification(): JSONObject {
        if (Constants.DIRECT_TOKENIZATION_PUBLIC_KEY == "REPLACE_ME" ||
                (Constants.DIRECT_TOKENIZATION_PARAMETERS.isEmpty() ||
                 Constants.DIRECT_TOKENIZATION_PUBLIC_KEY.isEmpty())) {

            throw RuntimeException(
                    "Please edit the Constants.java file to add protocol version & public key.")
        }

        return JSONObject().apply {
            put("type", "DIRECT")
            put("parameters", JSONObject(Constants.DIRECT_TOKENIZATION_PARAMETERS))
        }
    }

    /**
     * Card networks supported by your app and your gateway.
     *
     *
     * TODO: Confirm card networks supported by your app and gateway & update in Constants.java.
     *
     * @return Allowed card networks
     * @see [CardParameters](https://developers.google.com/pay/api/android/reference/object.CardParameters)
     */
    private val allowedCardNetworks = JSONArray(Constants.SUPPORTED_NETWORKS)

    /**
     * Card authentication methods supported by your app and your gateway.
     *
     *
     * TODO: Confirm your processor supports Android device tokens on your supported card networks
     * and make updates in Constants.java.
     *
     * @return Allowed card authentication methods.
     * @see [CardParameters](https://developers.google.com/pay/api/android/reference/object.CardParameters)
     */
    private val allowedCardAuthMethods = JSONArray(Constants.SUPPORTED_METHODS)

    /**
     * Describe your app's support for the CARD payment method.
     *
     *
     * The provided properties are applicable to both an IsReadyToPayRequest and a
     * PaymentDataRequest.
     *
     * @return A CARD PaymentMethod object describing accepted cards.
     * @throws JSONException
     * @see [PaymentMethod](https://developers.google.com/pay/api/android/reference/object.PaymentMethod)
     */
    // Optionally, you can add billing address/phone number associated with a CARD payment method.
    private fun baseCardPaymentMethod(): JSONObject {
        return JSONObject().apply {

            val parameters = JSONObject().apply {
                put("allowedAuthMethods", allowedCardAuthMethods)
                put("allowedCardNetworks", allowedCardNetworks)
                put("billingAddressRequired", true)
                put("billingAddressParameters", JSONObject().apply {
                    put("format", "FULL")
                })
            }

            put("type", "CARD")
            put("parameters", parameters)
        }
    }

    /**
     * Describe the expected returned payment data for the CARD payment method
     *
     * @return A CARD PaymentMethod describing accepted cards and optional fields.
     * @throws JSONException
     * @see [PaymentMethod](https://developers.google.com/pay/api/android/reference/object.PaymentMethod)
     */
    private fun cardPaymentMethod(): JSONObject {
        val cardPaymentMethod = baseCardPaymentMethod()
        cardPaymentMethod.put("tokenizationSpecification", gatewayTokenizationSpecification())

        return cardPaymentMethod
    }

    /**
     * An object describing accepted forms of payment by your app, used to determine a viewer's
     * readiness to pay.
     *
     * @return API version and payment methods supported by the app.
     * @see [IsReadyToPayRequest](https://developers.google.com/pay/api/android/reference/object.IsReadyToPayRequest)
     */
    fun isReadyToPayRequest(): JSONObject? {
        return try {
            baseRequest.apply {
                put("allowedPaymentMethods", JSONArray().put(baseCardPaymentMethod()))
            }

        } catch (e: JSONException) {
            null
        }
    }

    /**
     * Information about the merchant requesting payment information
     *
     * @return Information about the merchant.
     * @throws JSONException
     * @see [MerchantInfo](https://developers.google.com/pay/api/android/reference/object.MerchantInfo)
     */
    private val merchantInfo: JSONObject =
            JSONObject().put("merchantName", "Example Merchant")

    /**
     * Creates an instance of [PaymentsClient] for use in an [Activity] using the
     * environment and theme set in [Constants].
     *
     * @param activity is the caller's activity.
     */
    fun createPaymentsClient(activity: Activity): PaymentsClient {
        val walletOptions = Wallet.WalletOptions.Builder()
                .setEnvironment(Constants.PAYMENTS_ENVIRONMENT)
                .build()

        return Wallet.getPaymentsClient(activity, walletOptions)
    }

    /**
     * Provide Google Pay API with a payment amount, currency, and amount status.
     *
     * @return information about the requested payment.
     * @throws JSONException
     * @see [TransactionInfo](https://developers.google.com/pay/api/android/reference/object.TransactionInfo)
     */
    @Throws(JSONException::class)
    private fun getTransactionInfo(price: String): JSONObject {
        return JSONObject().apply {
            put("totalPrice", price)
            put("totalPriceStatus", "FINAL")
            put("countryCode", Constants.COUNTRY_CODE)
            put("currencyCode", Constants.CURRENCY_CODE)
        }
    }

    /**
     * An object describing information requested in a Google Pay payment sheet
     *
     * @return Payment data expected by your app.
     * @see [PaymentDataRequest](https://developers.google.com/pay/api/android/reference/object.PaymentDataRequest)
     */
    fun getPaymentDataRequest(priceCemts: Long): JSONObject? {
        return try {
            baseRequest.apply {
                put("allowedPaymentMethods", JSONArray().put(cardPaymentMethod()))
                put("transactionInfo", getTransactionInfo(priceCemts.centsToString()))
                put("merchantInfo", merchantInfo)

                // An optional shipping address requirement is a top-level property of the
                // PaymentDataRequest JSON object.
                val shippingAddressParameters = JSONObject().apply {
                    put("phoneNumberRequired", false)
                    put("allowedCountryCodes", JSONArray(listOf("US", "GB")))
                }
                put("shippingAddressParameters", shippingAddressParameters)
                put("shippingAddressRequired", true)
            }
        } catch (e: JSONException) {
            null
        }
    }
}

/**
 * Converts cents to a string format accepted by [PaymentsUtil.getPaymentDataRequest].
 *
 * @param cents value of the price.
 */
fun Long.centsToString() = BigDecimal(this)
        .divide(PaymentsUtil.CENTS)
        .setScale(2, RoundingMode.HALF_EVEN)
        .toString()

    

CheckoutActivity.java (Java)

This example Activity assumes a Google Pay payment button with an id attribute of googlepay_button exists in your layout.

/*
 * Copyright 2020 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.gms.samples.wallet.activity;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.Html;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.google.android.gms.common.api.Status;
import com.google.android.gms.samples.wallet.databinding.ActivityCheckoutBinding;
import com.google.android.gms.samples.wallet.util.PaymentsUtil;
import com.google.android.gms.samples.wallet.R;
import com.google.android.gms.samples.wallet.util.Json;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.wallet.AutoResolveHelper;
import com.google.android.gms.wallet.IsReadyToPayRequest;
import com.google.android.gms.wallet.PaymentData;
import com.google.android.gms.wallet.PaymentDataRequest;
import com.google.android.gms.wallet.PaymentsClient;

import java.util.Locale;
import java.util.Optional;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

/**
 * Checkout implementation for the app
 */
public class CheckoutActivity extends AppCompatActivity {

  // Arbitrarily-picked constant integer you define to track a request for payment data activity.
  private static final int LOAD_PAYMENT_DATA_REQUEST_CODE = 991;

  private static final long SHIPPING_COST_CENTS = 90 * PaymentsUtil.CENTS_IN_A_UNIT.longValue();

  // A client for interacting with the Google Pay API.
  private PaymentsClient paymentsClient;

  private ActivityCheckoutBinding layoutBinding;
  private View googlePayButton;

  private JSONArray garmentList;
  private JSONObject selectedGarment;

  /**
   * Initialize the Google Pay API on creation of the activity
   *
   * @see Activity#onCreate(android.os.Bundle)
   */
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    initializeUi();

    // Set up the mock information for our item in the UI.
    try {
      selectedGarment = fetchRandomGarment();
      displayGarment(selectedGarment);
    } catch (JSONException e) {
      throw new RuntimeException("The list of garments cannot be loaded");
    }

    // Initialize a Google Pay API client for an environment suitable for testing.
    // It's recommended to create the PaymentsClient object inside of the onCreate method.
    paymentsClient = PaymentsUtil.createPaymentsClient(this);
    possiblyShowGooglePayButton();
  }

  /**
   * Handle a resolved activity from the Google Pay payment sheet.
   *
   * @param requestCode Request code originally supplied to AutoResolveHelper in requestPayment().
   * @param resultCode  Result code returned by the Google Pay API.
   * @param data        Intent from the Google Pay API containing payment or error data.
   * @see <a href="https://developer.android.com/training/basics/intents/result">Getting a result
   * from an Activity</a>
   */
  @Override
  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
      // value passed in AutoResolveHelper
      case LOAD_PAYMENT_DATA_REQUEST_CODE:
        switch (resultCode) {

          case Activity.RESULT_OK:
            PaymentData paymentData = PaymentData.getFromIntent(data);
            handlePaymentSuccess(paymentData);
            break;

          case Activity.RESULT_CANCELED:
            // The user cancelled the payment attempt
            break;

          case AutoResolveHelper.RESULT_ERROR:
            Status status = AutoResolveHelper.getStatusFromIntent(data);
            handleError(status.getStatusCode());
            break;
        }

        // Re-enables the Google Pay payment button.
        googlePayButton.setClickable(true);
    }
  }

  private void initializeUi() {

    // Use view binding to access the UI elements
    layoutBinding = ActivityCheckoutBinding.inflate(getLayoutInflater());
    setContentView(layoutBinding.getRoot());

    // The Google Pay button is a layout file – take the root view
    googlePayButton = layoutBinding.googlePayButton.getRoot();
    googlePayButton.setOnClickListener(
        new View.OnClickListener() {
          @Override
          public void onClick(View view) {
            requestPayment(view);
          }
        });
  }

  private void displayGarment(JSONObject garment) throws JSONException {
    layoutBinding.detailTitle.setText(garment.getString("title"));
    layoutBinding.detailPrice.setText(
        String.format(Locale.getDefault(), "$%.2f", garment.getDouble("price")));

    final String escapedHtmlText = Html.fromHtml(
        garment.getString("description"), Html.FROM_HTML_MODE_COMPACT).toString();
    layoutBinding.detailDescription.setText(Html.fromHtml(
        escapedHtmlText, Html.FROM_HTML_MODE_COMPACT));

    final String imageUri = String.format("@drawable/%s", garment.getString("image"));
    final int imageResource = getResources().getIdentifier(imageUri, null, getPackageName());
    layoutBinding.detailImage.setImageResource(imageResource);
  }

  /**
   * Determine the viewer's ability to pay with a payment method supported by your app and display a
   * Google Pay payment button.
   *
   * @see <a href="https://developers.google.com/android/reference/com/google/android/gms/wallet/
   * PaymentsClient.html#isReadyToPay(com.google.android.gms.wallet.
   * IsReadyToPayRequest)">PaymentsClient#IsReadyToPay</a>
   */
  private void possiblyShowGooglePayButton() {

    final Optional<JSONObject> isReadyToPayJson = PaymentsUtil.getIsReadyToPayRequest();
    if (!isReadyToPayJson.isPresent()) {
      return;
    }

    // The call to isReadyToPay is asynchronous and returns a Task. We need to provide an
    // OnCompleteListener to be triggered when the result of the call is known.
    IsReadyToPayRequest request = IsReadyToPayRequest.fromJson(isReadyToPayJson.get().toString());
    Task<Boolean> task = paymentsClient.isReadyToPay(request);
    task.addOnCompleteListener(this,
        new OnCompleteListener<Boolean>() {
          @Override
          public void onComplete(@NonNull Task<Boolean> task) {
            if (task.isSuccessful()) {
              setGooglePayAvailable(task.getResult());
            } else {
              Log.w("isReadyToPay failed", task.getException());
            }
          }
        });
  }

  /**
   * If isReadyToPay returned {@code true}, show the button and hide the "checking" text. Otherwise,
   * notify the user that Google Pay is not available. Please adjust to fit in with your current
   * user flow. You are not required to explicitly let the user know if isReadyToPay returns {@code
   * false}.
   *
   * @param available isReadyToPay API response.
   */
  private void setGooglePayAvailable(boolean available) {
    if (available) {
      googlePayButton.setVisibility(View.VISIBLE);
    } else {
      Toast.makeText(this, R.string.googlepay_status_unavailable, Toast.LENGTH_LONG).show();
    }
  }

  /**
   * PaymentData response object contains the payment information, as well as any additional
   * requested information, such as billing and shipping address.
   *
   * @param paymentData A response object returned by Google after a payer approves payment.
   * @see <a href="https://developers.google.com/pay/api/android/reference/
   * object#PaymentData">PaymentData</a>
   */
  private void handlePaymentSuccess(PaymentData paymentData) {

    // Token will be null if PaymentDataRequest was not constructed using fromJson(String).
    final String paymentInfo = paymentData.toJson();
    if (paymentInfo == null) {
      return;
    }

    try {
      JSONObject paymentMethodData = new JSONObject(paymentInfo).getJSONObject("paymentMethodData");
      // If the gateway is set to "example", no payment information is returned - instead, the
      // token will only consist of "examplePaymentMethodToken".

      final JSONObject tokenizationData = paymentMethodData.getJSONObject("tokenizationData");
      final String token = tokenizationData.getString("token");
      final JSONObject info = paymentMethodData.getJSONObject("info");
      final String billingName = info.getJSONObject("billingAddress").getString("name");
      Toast.makeText(
          this, getString(R.string.payments_show_name, billingName),
          Toast.LENGTH_LONG).show();

      // Logging token string.
      Log.d("Google Pay token: ", token);

    } catch (JSONException e) {
      throw new RuntimeException("The selected garment cannot be parsed from the list of elements");
    }
  }

  /**
   * At this stage, the user has already seen a popup informing them an error occurred. Normally,
   * only logging is required.
   *
   * @param statusCode will hold the value of any constant from CommonStatusCode or one of the
   *                   WalletConstants.ERROR_CODE_* constants.
   * @see <a href="https://developers.google.com/android/reference/com/google/android/gms/wallet/
   * WalletConstants#constant-summary">Wallet Constants Library</a>
   */
  private void handleError(int statusCode) {
    Log.e("loadPaymentData failed", String.format("Error code: %d", statusCode));
  }

  public void requestPayment(View view) {

    // Disables the button to prevent multiple clicks.
    googlePayButton.setClickable(false);

    // The price provided to the API should include taxes and shipping.
    // This price is not displayed to the user.
    try {
      double garmentPrice = selectedGarment.getDouble("price");
      long garmentPriceCents = Math.round(garmentPrice * PaymentsUtil.CENTS_IN_A_UNIT.longValue());
      long priceCents = garmentPriceCents + SHIPPING_COST_CENTS;

      Optional<JSONObject> paymentDataRequestJson = PaymentsUtil.getPaymentDataRequest(priceCents);
      if (!paymentDataRequestJson.isPresent()) {
        return;
      }

      PaymentDataRequest request =
          PaymentDataRequest.fromJson(paymentDataRequestJson.get().toString());

      // Since loadPaymentData may show the UI asking the user to select a payment method, we use
      // AutoResolveHelper to wait for the user interacting with it. Once completed,
      // onActivityResult will be called with the result.
      if (request != null) {
        AutoResolveHelper.resolveTask(
            paymentsClient.loadPaymentData(request),
            this, LOAD_PAYMENT_DATA_REQUEST_CODE);
      }

    } catch (JSONException e) {
      throw new RuntimeException("The price cannot be deserialized from the JSON object.");
    }
  }

  private JSONObject fetchRandomGarment() {

    // Only load the list of items if it has not been loaded before
    if (garmentList == null) {
      garmentList = Json.readFromResources(this, R.raw.tshirts);
    }

    // Take a random element from the list
    int randomIndex = Math.toIntExact(Math.round(Math.random() * (garmentList.length() - 1)));
    try {
      return garmentList.getJSONObject(randomIndex);
    } catch (JSONException e) {
      throw new RuntimeException("The index specified is out of bounds.");
    }
  }
}

PaymentsUtil.java (Java)

This example file builds JSON objects suitable to build an IsReadyToPayRequest or a PaymentDataRequest.

/*
 * Copyright 2020 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.gms.samples.wallet.util;

import android.app.Activity;

import com.google.android.gms.samples.wallet.Constants;
import com.google.android.gms.wallet.PaymentsClient;
import com.google.android.gms.wallet.Wallet;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Optional;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Contains helper static methods for dealing with the Payments API.
 *
 * <p>Many of the parameters used in the code are optional and are set here merely to call out their
 * existence. Please consult the documentation to learn more and feel free to remove ones not
 * relevant to your implementation.
 */
public class PaymentsUtil {

  public static final BigDecimal CENTS_IN_A_UNIT = new BigDecimal(100d);

  /**
   * Create a Google Pay API base request object with properties used in all requests.
   *
   * @return Google Pay API base request object.
   * @throws JSONException
   */
  private static JSONObject getBaseRequest() throws JSONException {
    return new JSONObject().put("apiVersion", 2).put("apiVersionMinor", 0);
  }

  /**
   * Creates an instance of {@link PaymentsClient} for use in an {@link Activity} using the
   * environment and theme set in {@link Constants}.
   *
   * @param activity is the caller's activity.
   */
  public static PaymentsClient createPaymentsClient(Activity activity) {
    Wallet.WalletOptions walletOptions =
        new Wallet.WalletOptions.Builder().setEnvironment(Constants.PAYMENTS_ENVIRONMENT).build();
    return Wallet.getPaymentsClient(activity, walletOptions);
  }

  /**
   * Gateway Integration: Identify your gateway and your app's gateway merchant identifier.
   *
   * <p>The Google Pay API response will return an encrypted payment method capable of being charged
   * by a supported gateway after payer authorization.
   *
   * <p>TODO: Check with your gateway on the parameters to pass and modify them in Constants.java.
   *
   * @return Payment data tokenization for the CARD payment method.
   * @throws JSONException
   * @see <a href=
   * "https://developers.google.com/pay/api/android/reference/object#PaymentMethodTokenizationSpecification">PaymentMethodTokenizationSpecification</a>
   */
  private static JSONObject getGatewayTokenizationSpecification() throws JSONException {
    return new JSONObject() {{
      put("type", "PAYMENT_GATEWAY");
      put("parameters", new JSONObject() {{
        put("gateway", "example");
        put("gatewayMerchantId", "exampleGatewayMerchantId");
      }});
    }};
  }

  /**
   * {@code DIRECT} Integration: Decrypt a response directly on your servers. This configuration has
   * additional data security requirements from Google and additional PCI DSS compliance complexity.
   *
   * <p>Please refer to the documentation for more information about {@code DIRECT} integration. The
   * type of integration you use depends on your payment processor.
   *
   * @return Payment data tokenization for the CARD payment method.
   * @throws JSONException
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#PaymentMethodTokenizationSpecification">PaymentMethodTokenizationSpecification</a>
   */
  private static JSONObject getDirectTokenizationSpecification()
      throws JSONException, RuntimeException {
    if (Constants.DIRECT_TOKENIZATION_PARAMETERS.isEmpty()
        || Constants.DIRECT_TOKENIZATION_PUBLIC_KEY.isEmpty()
        || Constants.DIRECT_TOKENIZATION_PUBLIC_KEY == null
        || Constants.DIRECT_TOKENIZATION_PUBLIC_KEY == "REPLACE_ME") {
      throw new RuntimeException(
          "Please edit the Constants.java file to add protocol version & public key.");
    }
    JSONObject tokenizationSpecification = new JSONObject();

    tokenizationSpecification.put("type", "DIRECT");
    JSONObject parameters = new JSONObject(Constants.DIRECT_TOKENIZATION_PARAMETERS);
    tokenizationSpecification.put("parameters", parameters);

    return tokenizationSpecification;
  }

  /**
   * Card networks supported by your app and your gateway.
   *
   * <p>TODO: Confirm card networks supported by your app and gateway & update in Constants.java.
   *
   * @return Allowed card networks
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#CardParameters">CardParameters</a>
   */
  private static JSONArray getAllowedCardNetworks() {
    return new JSONArray(Constants.SUPPORTED_NETWORKS);
  }

  /**
   * Card authentication methods supported by your app and your gateway.
   *
   * <p>TODO: Confirm your processor supports Android device tokens on your supported card networks
   * and make updates in Constants.java.
   *
   * @return Allowed card authentication methods.
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#CardParameters">CardParameters</a>
   */
  private static JSONArray getAllowedCardAuthMethods() {
    return new JSONArray(Constants.SUPPORTED_METHODS);
  }

  /**
   * Describe your app's support for the CARD payment method.
   *
   * <p>The provided properties are applicable to both an IsReadyToPayRequest and a
   * PaymentDataRequest.
   *
   * @return A CARD PaymentMethod object describing accepted cards.
   * @throws JSONException
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#PaymentMethod">PaymentMethod</a>
   */
  private static JSONObject getBaseCardPaymentMethod() throws JSONException {
    JSONObject cardPaymentMethod = new JSONObject();
    cardPaymentMethod.put("type", "CARD");

    JSONObject parameters = new JSONObject();
    parameters.put("allowedAuthMethods", getAllowedCardAuthMethods());
    parameters.put("allowedCardNetworks", getAllowedCardNetworks());
    // Optionally, you can add billing address/phone number associated with a CARD payment method.
    parameters.put("billingAddressRequired", true);

    JSONObject billingAddressParameters = new JSONObject();
    billingAddressParameters.put("format", "FULL");

    parameters.put("billingAddressParameters", billingAddressParameters);

    cardPaymentMethod.put("parameters", parameters);

    return cardPaymentMethod;
  }

  /**
   * Describe the expected returned payment data for the CARD payment method
   *
   * @return A CARD PaymentMethod describing accepted cards and optional fields.
   * @throws JSONException
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#PaymentMethod">PaymentMethod</a>
   */
  private static JSONObject getCardPaymentMethod() throws JSONException {
    JSONObject cardPaymentMethod = getBaseCardPaymentMethod();
    cardPaymentMethod.put("tokenizationSpecification", getGatewayTokenizationSpecification());

    return cardPaymentMethod;
  }

  /**
   * An object describing accepted forms of payment by your app, used to determine a viewer's
   * readiness to pay.
   *
   * @return API version and payment methods supported by the app.
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#IsReadyToPayRequest">IsReadyToPayRequest</a>
   */
  public static Optional<JSONObject> getIsReadyToPayRequest() {
    try {
      JSONObject isReadyToPayRequest = getBaseRequest();
      isReadyToPayRequest.put(
          "allowedPaymentMethods", new JSONArray().put(getBaseCardPaymentMethod()));

      return Optional.of(isReadyToPayRequest);

    } catch (JSONException e) {
      return Optional.empty();
    }
  }

  /**
   * Provide Google Pay API with a payment amount, currency, and amount status.
   *
   * @return information about the requested payment.
   * @throws JSONException
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#TransactionInfo">TransactionInfo</a>
   */
  private static JSONObject getTransactionInfo(String price) throws JSONException {
    JSONObject transactionInfo = new JSONObject();
    transactionInfo.put("totalPrice", price);
    transactionInfo.put("totalPriceStatus", "FINAL");
    transactionInfo.put("countryCode", Constants.COUNTRY_CODE);
    transactionInfo.put("currencyCode", Constants.CURRENCY_CODE);
    transactionInfo.put("checkoutOption", "COMPLETE_IMMEDIATE_PURCHASE");

    return transactionInfo;
  }

  /**
   * Information about the merchant requesting payment information
   *
   * @return Information about the merchant.
   * @throws JSONException
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#MerchantInfo">MerchantInfo</a>
   */
  private static JSONObject getMerchantInfo() throws JSONException {
    return new JSONObject().put("merchantName", "Example Merchant");
  }

  /**
   * An object describing information requested in a Google Pay payment sheet
   *
   * @return Payment data expected by your app.
   * @see <a
   * href="https://developers.google.com/pay/api/android/reference/object#PaymentDataRequest">PaymentDataRequest</a>
   */
  public static Optional<JSONObject> getPaymentDataRequest(long priceCents) {

    final String price = PaymentsUtil.centsToString(priceCents);

    try {
      JSONObject paymentDataRequest = PaymentsUtil.getBaseRequest();
      paymentDataRequest.put(
          "allowedPaymentMethods", new JSONArray().put(PaymentsUtil.getCardPaymentMethod()));
      paymentDataRequest.put("transactionInfo", PaymentsUtil.getTransactionInfo(price));
      paymentDataRequest.put("merchantInfo", PaymentsUtil.getMerchantInfo());

      /* An optional shipping address requirement is a top-level property of the PaymentDataRequest
      JSON object. */
      paymentDataRequest.put("shippingAddressRequired", true);

      JSONObject shippingAddressParameters = new JSONObject();
      shippingAddressParameters.put("phoneNumberRequired", false);

      JSONArray allowedCountryCodes = new JSONArray(Constants.SHIPPING_SUPPORTED_COUNTRIES);

      shippingAddressParameters.put("allowedCountryCodes", allowedCountryCodes);
      paymentDataRequest.put("shippingAddressParameters", shippingAddressParameters);
      return Optional.of(paymentDataRequest);

    } catch (JSONException e) {
      return Optional.empty();
    }
  }

  /**
   * Converts cents to a string format accepted by {@link PaymentsUtil#getPaymentDataRequest}.
   *
   * @param cents value of the price in cents.
   */
  public static String centsToString(long cents) {
    return new BigDecimal(cents)
        .divide(CENTS_IN_A_UNIT, RoundingMode.HALF_EVEN)
        .setScale(2, RoundingMode.HALF_EVEN)
        .toString();
  }
}