We are excited to bring Express checkout with Google Pay for Android native apps enabling developers to leverage users stored credentials (payment and address) from Google Wallet to streamline their checkout journeys. You will be able to implement familiar callbacks onPaymentDataChanged and onPaymentAuthorized that are already supported on Web now in your Android applications and streamline the checkout funnels. With these callbacks, you can update shipping options, taxes, and total prices dynamically as users interact with the Google Pay sheet, and handle authorization feedback without ever closing the sheet. This is available with play-services-wallet:20.0.0 and onwards.
Why use Dynamic Callbacks?
Dynamic callbacks enable a true "Express Checkout" experience. By moving the Google Pay button upstream to your Product Detail or Cart pages, you can provide the user's shipping address, payment credentials, and contact details all within the Pay sheet.
Getting Started
To get started, update your Google Pay dependency in your build.gradle file:
com.google.android.gms:play-services-wallet:20.0.0
1. Implement the Callback Logic
Extend BasePaymentDataCallbacks to handle the specific events triggered during the checkout process.
import com.google.android.gms.wallet.PaymentData
import com.google.android.gms.wallet.callback.BasePaymentDataCallbacks
import com.google.android.gms.wallet.callback.IntermediatePaymentData
import com.google.android.gms.wallet.callback.OnCompleteListener
import com.google.android.gms.wallet.callback.PaymentAuthorizationResult
import com.google.android.gms.wallet.callback.PaymentDataRequestUpdate
import org.json.JSONObject
class MerchantPaymentDataCallbacks : BasePaymentDataCallbacks() {
/**
* Handles payment data changes in the payment sheet such as shipping address and shipping options.
*/
override fun onPaymentDataChanged(
request: IntermediatePaymentData,
onCompleteListener: OnCompleteListener<PaymentDataRequestUpdate>
) {
// Example: Process the request and return updates
// In a real application, you would likely make a network request to your server
// to get updated shipping options and cart details based on the new address.
val shippingAddress = request.shippingAddress
val shippingOptionData = request.shippingOptionData
// Construct the response object
val responseJson = JSONObject().apply {
// Example: Add new shipping options based on the address
put("newShippingOptionParameters", JSONObject().apply {
put("defaultSelectedOptionId", "shipping-001")
put("shippingOptions", JSONObject().apply {
put("id", "shipping-001")
put("label", "$0.00: Free shipping")
put("description", "Free shipping on all orders")
})
})
// Example: Update transaction info
put("newTransactionInfo", JSONObject().apply {
put("totalPriceStatus", "FINAL")
put("totalPrice", "12.34")
put("currencyCode", "USD")
})
}
val response = PaymentDataRequestUpdate.fromJson(responseJson.toString())
onCompleteListener.complete(response)
}
/**
* Called when a payment is authorized in the payment sheet.
*/
override fun onPaymentAuthorized(
request: PaymentData,
onCompleteListener: OnCompleteListener<PaymentAuthorizationResult>
) {
// Log the payment data for debugging
println("onPaymentAuthorized called with PaymentData: ${request.toJson()}")
// Example: Process the payment authorization
// In a real application, you would send the payment token and other data
// to your server to be processed by your payment service provider.
// Construct the response object
val responseJson = JSONObject().apply {
put("transactionState", "SUCCESS") // Or "ERROR"
// Optionally include an error message
// put("error", JSONObject().apply {
// put("reason", "PAYMENT_DATA_INVALID")
// put("intent", "PAYMENT_AUTHORIZATION")
// put("message", "Invalid payment method.")
// })
}
val response = PaymentAuthorizationResult.fromJson(responseJson.toString())
onCompleteListener.complete(response)
}
}
2. Host the Callback Service
Implement a service that extends BasePaymentDataCallbacksService to provide an instance of your callbacks.
import androidx.annotation.NonNull
import com.google.android.gms.wallet.callback.BasePaymentDataCallbacks
import com.google.android.gms.wallet.callback.BasePaymentDataCallbacksService
/**
* Service class which hosts the payment data callbacks.
*/
class MerchantPaymentDataCallbacksService : BasePaymentDataCallbacksService() {
@NonNull
override fun createPaymentDataCallbacks(): BasePaymentDataCallbacks {
return MerchantPaymentDataCallbacks()
}
}
3. Update the Android Manifest
You must declare your service and protect it with the BIND_PAYMENTS_CALLBACK_SERVICE permission.
<service
android:name=".service.MerchantPaymentDataCallbacksService"
android:permission="com.google.android.gms.permission.BIND_PAYMENTS_CALLBACK_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.wallet.callback.PAYMENT_DATA_CALLBACKS" />
</intent-filter>
</service>
4. Configure the Payment Request
Finally, include the callbackIntents in your PaymentDataRequest JSON object to tell Google Pay which events you want to listen for.
{
"apiVersion": 2,
"apiVersionMinor": 0,
...
"callbackIntents": [
"PAYMENT_AUTHORIZATION",
"SHIPPING_ADDRESS",
"SHIPPING_OPTION"
]
}
Implementing dynamic callbacks on Android allows you to:
Dynamic callbacks bring the Google Pay developer platform on Android to parity with its capabilities on the web. For a full implementation guide, see the updated developer documentation: goo.gle/pay-android-dpu