1. Introduction
What you'll build
At the completion of this codelab, you will have a minimum viable Jetpack Compose app with a working Google Pay integration for Android. This project retrieves a payment token which may be sent to a payment service provider for processing.
What you'll learn
- How to install and configure the Google Pay Jetpack Compose library
- How to display the Google Pay button and handle clicks
- How to request a payment token from Google Pay
What you'll need
- Android Studio (latest stable) installed.
- Android SDK and an emulator or device set up in Android Studio.
- For production, you will need a Google Pay
merchantId. It only takes a minute to register at Google Pay & Wallet Console so might as well take care of it now.
2. Create the Jetpack Compose project
Create project files
- Create a new Jetpack Compose project in Android Studio named
gpay-compose:- Open Android Studio and select
New Project. - Choose the
Empty Activity (Jetpack Compose)template. - Name:
gpay-compose, Package name:com.example.gpay. - Language: Kotlin, Minimum SDK: API 21+.
- Finish to generate the project.
- Open Android Studio and select
- Add the Google Pay Compose Button dependency.In your
app/build.gradle(.kts)file, add: Groovy DSL:dependencies { implementation("com.google.pay.button:compose-pay-button:1.0.0") }dependencies { implementation 'com.google.pay.button:compose-pay-button:1.0.0' } - Add Google Play services Wallet dependency (to open the Google Pay sheet):In your
app/build.gradle(.kts)file, add the following dependency: If your project uses Groovy DSL, use:dependencies { implementation("com.google.android.gms:play-services-wallet:19.3.0") }dependencies { implementation 'com.google.android.gms:play-services-wallet:19.3.0' } - Open
MainActivity.ktin Android Studio and replace the contents with the following minimal Compose app scaffold (we'll wire the button next):package com.example.gpay import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { // Google Pay button added in the next section } } } } }
3. Configure Google Pay
A Google Pay payment request requires a request object. The object defined here as baseGooglePayRequest contains the minimum common settings for all requests. Additional settings will be added depending on the request made which we'll review in this codelab.
Add the Google Pay configuration constants to MainActivity.kt (you'll reuse these in the next step):
private const val merchantId = "12345678901234567890"
// This is the base configuration for all Google Pay payment data requests.
private val baseGooglePayRequest = """
{
"apiVersion": 2,
"apiVersionMinor": 0,
"allowedPaymentMethods": [
{
"type": "CARD",
"parameters": {
"allowedAuthMethods": [
"PAN_ONLY", "CRYPTOGRAM_3DS"
],
"allowedCardNetworks": [
"AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"
]
},
"tokenizationSpecification": {
"type": "PAYMENT_GATEWAY",
"parameters": {
"gateway": "example",
"gatewayMerchantId": "exampleGatewayMerchantId"
}
}
}
],
"merchantInfo": {
"merchantId": "$merchantId"
}
}
""".trimIndent()
Resources
- API Reference: Google Pay API request objects documentation
- API Reference: Refer to
PaymentMethodfor more information about the allowed authorization methods, allowed card networks, and tokenization specifications including the proper gateway value.
4. Add the Google Pay button
Use the Compose Pay Button to render a native Google Pay button, and the Wallet API to open the Google Pay sheet.
Replace the entire contents of MainActivity.kt with the following complete example (includes configuration + button + payment flow):
package com.example.gpay
import android.os.Bundle
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.tasks.Task
import com.google.android.gms.wallet.contract.TaskResultContracts.GetPaymentDataResult
import com.google.android.gms.wallet.*
import com.google.pay.button.ButtonTheme
import com.google.pay.button.ButtonType
import com.google.pay.button.PayButton
import org.json.JSONObject
private const val merchantId = "12345678901234567890"
// Base Google Pay request used for both the button and the Wallet request
private val baseGooglePayRequest = """
{
"apiVersion": 2,
"apiVersionMinor": 0,
"allowedPaymentMethods": [
{
"type": "CARD",
"parameters": {
"allowedAuthMethods": [
"PAN_ONLY", "CRYPTOGRAM_3DS"
],
"allowedCardNetworks": [
"AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"
]
},
"tokenizationSpecification": {
"type": "PAYMENT_GATEWAY",
"parameters": {
"gateway": "example",
"gatewayMerchantId": "exampleGatewayMerchantId"
}
}
}
],
"merchantInfo": {
"merchantId": "$merchantId"
}
}
""".trimIndent()
class MainActivity : ComponentActivity() {
private val paymentDataLauncher = registerForActivityResult(GetPaymentDataResult()) { taskResult ->
when (taskResult.status.statusCode) {
CommonStatusCodes.SUCCESS -> {
handlePaymentData(taskResult.result!!)
}
//CommonStatusCodes.CANCELED -> The user canceled
//CommonStatusCodes.DEVELOPER_ERROR -> The API returned an error (it.status: Status)
//else -> Handle internal and other unexpected errors
}
}
private lateinit var paymentsClient: PaymentsClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Create the PaymentsClient
paymentsClient = Wallet.getPaymentsClient(
this,
Wallet.WalletOptions.Builder()
.setEnvironment(WalletConstants.ENVIRONMENT_TEST) // Switch to PRODUCTION when ready
.build()
)
// Derive allowedPaymentMethods for the button from baseGooglePayRequest
val allowedPaymentMethods = JSONObject(baseGooglePayRequest)
.getJSONArray("allowedPaymentMethods")
.toString()
setContent {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
PayButton(
onClick = { requestPayment(paymentDataLauncher) },
allowedPaymentMethods = allowedPaymentMethods,
type = ButtonType.Pay,
theme = ButtonTheme.Light
)
}
}
}
private fun requestPayment(launcher: ActivityResultLauncher<Task<PaymentData>>) {
// Build a PaymentDataRequest from the base request by adding transaction info
val requestJson = JSONObject(baseGooglePayRequest).apply {
put("transactionInfo", JSONObject().apply {
put("totalPrice", "14.95")
put("totalPriceStatus", "FINAL")
put("countryCode", "US")
put("currencyCode", "USD")
})
}
val request = PaymentDataRequest.fromJson(requestJson.toString())
val task = paymentsClient.loadPaymentData(request)
task.addOnCompleteListener(paymentDataLauncher::launch)
}
private fun handlePaymentData(paymentData: PaymentData?) {
val json = paymentData?.toJson() ?: return
val paymentMethodData = JSONObject(json).getJSONObject("paymentMethodData")
val tokenizationData = paymentMethodData.getJSONObject("tokenizationData")
val token = tokenizationData.getString("token")
// Send 'token' to your payment service provider (PSP)
println("Payment token: $token")
}
}
5. Make a payment request
When the Google Pay button is pressed, requestPayment(...) builds a PaymentDataRequest by adding transactionInfo to your baseGooglePayRequest, opens the Google Pay sheet, and returns a payment token.
Key points
- Button:
PayButtonrenders the native Google Pay button. - Client:
PaymentsClientis configured with TEST or PRODUCTION. - Launch: Use
loadPaymentDataand resolve with anIntentSenderwhen required. - Token: Parse
PaymentData.toJson()to extractpaymentMethodData.tokenizationData.tokenand send to your PSP.
6. Conclusion
Congratulations on completing this Codelab! You have learned how to integrate the Google Pay API into a Jetpack Compose app for Android.
Run the project
Run the project from Android Studio (Run > Run 'app') to start your app.
Where to go from here
Additional resources
- Join the conversation in the #payments channel on Discord
- Follow @GooglePayDevs on X
- Watch Google Pay related videos on YouTube