Integration of Google Pay Payment System into Mobile Application
Google Pay on Android works via Google Pay API — not separate SDK, but part of Google Play Services. Integration technically simpler than Apple Pay: no certificates and separate developer portal, only PaymentDataRequest configuration and token handling. But points where can make mistakes sufficient.
PaymentsClient and ENVIRONMENT_TEST / ENVIRONMENT_PRODUCTION
Google Pay works in two environments. In ENVIRONMENT_TEST can get fake tokens without real card — convenient for development. In ENVIRONMENT_PRODUCTION need pre-pass Google verification (fill form in Google Pay Business Console and get approval).
private fun createPaymentsClient(activity: Activity): PaymentsClient {
val walletOptions = Wallet.WalletOptions.Builder()
.setEnvironment(WalletConstants.ENVIRONMENT_PRODUCTION)
.build()
return Wallet.getPaymentsClient(activity, walletOptions)
}
PaymentDataRequest configuration
private fun createPaymentDataRequest(price: String): PaymentDataRequest {
val tokenizationSpec = JSONObject().apply {
put("type", "PAYMENT_GATEWAY")
put("parameters", JSONObject().apply {
put("gateway", "stripe") // or "cloudpayments", "yookassa" etc
put("stripe:version", "2023-10-16")
put("stripe:publishableKey", "pk_live_...")
})
}
val cardPaymentMethod = JSONObject().apply {
put("type", "CARD")
put("parameters", JSONObject().apply {
put("allowedAuthMethods", JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS")))
put("allowedCardNetworks", JSONArray(listOf("MASTERCARD", "VISA", "MIR")))
})
put("tokenizationSpecification", tokenizationSpec)
}
val request = JSONObject().apply {
put("apiVersion", 2)
put("apiVersionMinor", 0)
put("allowedPaymentMethods", JSONArray(listOf(cardPaymentMethod)))
put("transactionInfo", JSONObject().apply {
put("totalPrice", price)
put("totalPriceStatus", "FINAL")
put("currencyCode", "RUB")
put("countryCode", "RU")
})
put("merchantInfo", JSONObject().apply {
put("merchantName", "Your Company Name")
put("merchantId", "YOUR_MERCHANT_ID") // from Business Console
})
}
return PaymentDataRequest.fromJson(request.toString())
}
PAN_ONLY vs CRYPTOGRAM_3DS
Often causes questions. PAN_ONLY — card added to Google Pay via browser or manually, without 3DS token. CRYPTOGRAM_3DS — device with hardware protection (SE or StrongBox), card tokenized in Trusted Execution Environment. For Russian acquirers both methods supported, but clarify with provider — some accept only CRYPTOGRAM_3DS to reduce fraud.
Launch payment interface
private val paymentLauncher = registerForActivityResult(
ActivityResultContracts.StartIntentSenderForResult()
) { result ->
when (result.resultCode) {
Activity.RESULT_OK -> {
val data = result.data ?: return@registerForActivityResult
val paymentData = PaymentData.getFromIntent(data)
val token = paymentData
?.paymentMethodToken
?.token // JSON-string with provider token
// Send token to backend
}
Activity.RESULT_CANCELED -> { /* user closed */ }
AutoResolveHelper.RESULT_ERROR -> {
val status = AutoResolveHelper.getStatusFromIntent(result.data)
Log.e("GPay", "Error: ${status?.statusMessage}")
}
}
}
// Launch
val task = paymentsClient.loadPaymentData(createPaymentDataRequest("1500.00"))
task.addOnCompleteListener { completedTask ->
if (completedTask.isSuccessful) {
paymentLauncher.launch(
IntentSenderRequest.Builder(
completedTask.result.resolutionPendingIntent!!.intentSender
).build()
)
}
}
Google Pay button: design requirements
Google strictly regulates button appearance. Can't change color, font, aspect ratio of Google Pay button. Google checks this during review before ENVIRONMENT_PRODUCTION release.
Correctly use ready widget:
val button = PayButton(context).apply {
initialize(
ButtonOptions.newBuilder()
.setButtonType(ButtonType.BUY)
.setCornerRadius(8)
.build()
)
}
isReadyToPay before showing button
Don't show Google Pay button without check:
val isReadyToPayRequest = IsReadyToPayRequest.fromJson(
JSONObject().apply {
put("apiVersion", 2)
put("apiVersionMinor", 0)
put("allowedPaymentMethods", JSONArray(listOf(cardPaymentMethod)))
}.toString()
)
paymentsClient.isReadyToPay(isReadyToPayRequest)
.addOnSuccessListener { result ->
googlePayButton.isVisible = result
}
If card not added to Google Pay or device incompatible — don't show button.
What's included
- PaymentsClient setup with correct environment
- tokenizationSpecification configuration for payment provider
- isReadyToPay implementation and correct button hiding
- PaymentData handling and token passing to backend
- Passing Google Pay Business Console review
Timeline
2–3 days. Cost calculated individually.







