38

Tutorial  |  Google Pay API for Android  |  Google Developers

 3 years ago
source link: https://developers.google.com/pay/api/android/guides/tutorial?linkId=98806031
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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.

    private val baseRequest = JSONObject().apply {
        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.

private fun gatewayTokenizationSpecification(): JSONObject {
    return JSONObject().apply {
        put("type", "PAYMENT_GATEWAY")
        put("parameters", JSONObject(mapOf(
                "gateway" to "example",
                "gatewayMerchantId" to "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

AllPayments
  "gateway": "allpayments"
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"

Developer docs

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

Developer docs

123456789101112131415161718192021222324252627282930

Tip: example is a valid gateway name in the test environment. If you want to try out the API, you can leave the code sample above as-is. When you use the example gateway, your site doesn't receive card data capable of a transaction, but the user flow looks the same.

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.

private val allowedCardNetworks = JSONArray(listOf(
        "AMEX",
        "DISCOVER",
        "INTERAC",
        "JCB",
        "MASTERCARD",
        "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).

private val allowedCardAuthMethods = JSONArray(listOf(
        "PAN_ONLY",
        "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.

Important: Ensure that your existing risk checks and controls for payment transactions are also applied to Google Pay transactions. Google Pay validation and fraud checks aren't intended to replace your risk management processes.

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.

    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)
        }
    }

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

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

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.

    fun createPaymentsClient(activity: Activity): PaymentsClient {
        val walletOptions = 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:

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

} catch (e: JSONException) {
            null
        }
    }

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.

    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)
            }
        }
    }

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.

Important: Merchants that process transactions in the European Economic Area (EEA) or any other countries that are subject to Strong Customer Authentication (SCA) must include the countryCode, totalPrice, and merchantName parameters to meet SCA requirements.

    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)
        }
    }

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:

private val merchantInfo: JSONObject =
        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:

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
    }
}

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.

        googlePayButton.setOnClickListener { requestPayment() }

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.

    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
            }
        }
    }

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.

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)
    }
}
    Objective: To validate your integration and request production access, proceed to our Integration checklist.

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK