Tutorial | Google Pay API for Android | Google Developers
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": "aciworldwide"Adyen
"gatewayMerchantId": "YOUR_ENTITY_ID"
"gateway": "adyen"Alfa-Bank
"gatewayMerchantId": "YOUR_MERCHANT_ACCOUNT_NAME"
"gateway": "alfabank"AllPayments
"gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
"gateway": "allpayments"APPEX
"gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
"gateway": "epos"
"gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
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.
/*Objective: To validate your integration and request production access, proceed to our Integration checklist.
* 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)
}
}
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK