💳 Google Pay Integration Guide for Android Developers

💳 Google Pay Integration Guide for Android Developers

Integrating Google Pay into your Android app can dramatically improve user experience by offering a seamless, secure, and fast checkout process. In this guide, I'll walk you through the complete integration process — from setup to implementation — to help you get Google Pay running in your app smoothly.

🚀 Why Google Pay?

Google Pay offers:

  • Fast Checkout with saved payment methods
  • Improved Security with tokenization
  • Increased Conversions with reduced friction
  • Wide Adoption — built-in on most Android phones

🧰 Prerequisites

Before diving into code, ensure:

  1. You have an Android app project using Kotlin or Java.
  2. Your app is targeting Android 5.0 (API level 21) or higher.
  3. You have a Google Developer account.
  4. You are using a test payment processor (e.g., "TEST") for development.

⚙️ Step 1: Add Required Dependencies

Update your build.gradle file:

   //google pay
    implementation(libs.play.services.wallet)
    implementation(libs.compose.pay.button)
    implementation(libs.kotlinx.coroutines.play.services)        

in the libs.versions.toml

composePayButton = "1.0.0"
playServicesWallet = "19.4.0"
kotlinxCoroutinesPlayServices = "1.10.2"        
Article content

🏗️ Step 2: Configure Your App Manifest

Add the Wallet permissions and required services:

<uses-permission android:name="android.permission.INTERNET"/>

<application>
    <meta-data
        android:name="com.google.android.gms.wallet.api.enabled"
        android:value="true" />
</application>
        

🔐 Step 3: Create Your Payment Configuration

The payment configuration defines what payment methods your app supports, merchant information, and transaction parameters. To keep the integration clean and maintainable, it’s best to create a reusable utility — like PaymentsUtil — that handles this setup.

object Constants {
    const val PAYMENTS_ENVIRONMENT = WalletConstants.ENVIRONMENT_PRODUCTION // Use TEST for dev

    val SUPPORTED_NETWORKS = listOf("AMEX", "DISCOVER", "JCB", "MASTERCARD", "VISA")

    val SUPPORTED_METHODS = listOf("PAN_ONLY", "CRYPTOGRAM_3DS")

    const val COUNTRY_CODE = "EG"
    const val CURRENCY_CODE = "EGP"

    val SHIPPING_SUPPORTED_COUNTRIES = listOf("US", "GB")

    private const val PAYMENT_GATEWAY_TOKENIZATION_NAME = "example" // Replace with actual gateway

    val PAYMENT_GATEWAY_TOKENIZATION_PARAMETERS = mapOf(
        "gateway" to PAYMENT_GATEWAY_TOKENIZATION_NAME,
        "gatewayMerchantId" to "gatewayMerchantId"
    )

    const val DIRECT_TOKENIZATION_PUBLIC_KEY = "REPLACE_ME"

    val DIRECT_TOKENIZATION_PARAMETERS = mapOf(
        "protocolVersion" to "ECv1",
        "publicKey" to DIRECT_TOKENIZATION_PUBLIC_KEY
    )
}
        

🧠 Understanding apiVersion and apiVersionMinor

These fields define the version of the Google Pay API protocol your app uses:

val baseRequest = JSONObject()
    .put("apiVersion", 2)
    .put("apiVersionMinor", 0)
        

  • apiVersion: Major version — set by Google. (Always use the latest stable)
  • apiVersionMinor: Backward-compatible changes.

No need to change these unless specifically instructed in the documentation.

🧰 2. Create the PaymentsUtil Helper

A Kotlin object simplifies managing all Google Pay setup logic.

a. Define Allowed Card Methods

val allowedCardNetworks = JSONArray(Constants.SUPPORTED_NETWORKS)
val allowedCardAuthMethods = JSONArray(Constants.SUPPORTED_METHODS)        

b. Set Up Tokenization (Gateway or Direct)

val gatewayTokenizationSpecification = JSONObject()
    .put("type", "PAYMENT_GATEWAY") 
    .put("parameters", JSONObject(Constants.PAYMENT_GATEWAY_TOKENIZATION_PARAMETERS))        
💡 Replace with DIRECT if you're doing direct integration with your own processor.

c. Describe the Card Payment Method

private fun baseCardPaymentMethod(): JSONObject = JSONObject()
    .put("type", "CARD")
    .put("parameters", JSONObject()
        .put("allowedAuthMethods", allowedCardAuthMethods)
        .put("allowedCardNetworks", allowedCardNetworks)
        .put("billingAddressRequired", true)
        .put("billingAddressParameters", JSONObject()
            .put("format", "FULL")
        )
    )
        

🛍️ 3. Define Merchant and Transaction Info

private val merchantInfo = JSONObject().put("merchantName", "merchantName")

private fun getTransactionInfo(price: String): JSONObject = JSONObject()
    .put("totalPrice", price)
    .put("totalPriceStatus", "FINAL")
    .put("countryCode", Constants.COUNTRY_CODE)
    .put("currencyCode", Constants.CURRENCY_CODE)
        


🧾 4. Construct the Payment Data Request

This builds the final PaymentDataRequest object Google Pay uses:

fun getPaymentDataRequest(priceLabel: String): JSONObject =
    baseRequest
        .put("allowedPaymentMethods", JSONArray().put(
            baseCardPaymentMethod().put("tokenizationSpecification", gatewayTokenizationSpecification)
        ))
        .put("transactionInfo", getTransactionInfo(priceLabel))
        .put("merchantInfo", merchantInfo)
        .put("shippingAddressRequired", true)
        .put("shippingAddressParameters", JSONObject()
            .put("phoneNumberRequired", false)
            .put("allowedCountryCodes", JSONArray(Constants.SHIPPING_SUPPORTED_COUNTRIES))
        )        


💻 Create a PaymentsClient instance:

val walletOptions = Wallet.WalletOptions.Builder()
    .setEnvironment(WalletConstants.ENVIRONMENT_TEST) // Use PRODUCTION for release
    .build()

val paymentsClient = Wallet.getPaymentsClient(context, walletOptions)
        

Check if Google Pay is available:

val isReadyToPayJson = JSONObject().apply {
    put("apiVersion", 2)
    put("apiVersionMinor", 0)
    put("allowedPaymentMethods", JSONArray().apply {
        put(JSONObject().apply {
            put("type", "CARD")
            put("parameters", JSONObject().apply {
                put("allowedAuthMethods", JSONArray(listOf("PAN_ONLY", "CRYPTOGRAM_3DS")))
                put("allowedCardNetworks", JSONArray(listOf("VISA", "MASTERCARD")))
            })
        })
    })
}

val request = IsReadyToPayRequest.fromJson(isReadyToPayJson.toString())

paymentsClient.isReadyToPay(request)
    .addOnCompleteListener { task ->
        if (task.isSuccessful) {
            // Show Google Pay button
        } else {
            // Fallback UI
        }
    }
        

🛒 Launch the Google Pay Payment Sheet

val paymentDataRequestJson = loadPaymentDataRequest() // Load JSON from payments.json
val paymentDataRequest = PaymentDataRequest.fromJson(paymentDataRequestJson.toString())

AutoResolveHelper.resolveTask(
    paymentsClient.loadPaymentData(paymentDataRequest),
    activity,
    LOAD_PAYMENT_DATA_REQUEST_CODE
)
        

🧾 Handle the Result in onActivityResult

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    when (requestCode) {
        LOAD_PAYMENT_DATA_REQUEST_CODE -> {
            when (resultCode) {
                Activity.RESULT_OK -> {
                    val paymentData = PaymentData.getFromIntent(data!!)
                    val json = paymentData?.toJson()
                    // Send payment info to your backend to complete transaction
                }
                Activity.RESULT_CANCELED -> {
                    // User canceled
                }
                AutoResolveHelper.RESULT_ERROR -> {
                    val status = AutoResolveHelper.getStatusFromIntent(data)
                    Log.e("GooglePay", "Error: ${status?.statusMessage}")
                }
            }
        }
    }
}
        

✅ Go Live

Before publishing:

  • Switch environment to WalletConstants.ENVIRONMENT_PRODUCTION
  • Register your merchant ID in the Google Pay API Console
  • Make sure your app is signed with the production key
  • Pass Google’s review process

🧪 Testing Tips

  • Use test card info provided by your gateway.
  • Always test on real Android devices with Google Play Services.

🔍 Debugging Common Issues

  • OR_BIBED_13 Error: Usually means your app's signing key does not match the registered one. Ensure your release key is correctly uploaded to the Google Pay console.
  • Google Pay not showing: Check if the test user has an eligible card and account setup.


📦 Conclusion

Adding Google Pay support to your Android app can improve checkout speed and trust. The process is straightforward with good planning and testing. If you run into issues, the Google Pay Developer Documentation is a great reference.



Great work ya youssef,thanks for sharing

كلام متعوب عليه بجد ❤️

To view or add a comment, sign in

More articles by Youssef badway

  • Network security configuration

    The Network Security Configuration in Android is used to customize the behavior of your app's network stack. It allows…

    1 Comment
  • Writing Swift-Friendly Kotlin Multiplatform APIs — Part 1

    *This list contains some important notes and solutions for common issues in KMP projects So I will sum up these…

  • Unit Tests

    “The Testing Pyramid,” unit tests verify how isolated parts of your application work. Before checking how things work…

    12 Comments
  • Unit tests in TDD part 1

    Unit tests Unit tests are the quickest, easiest to write and cheapest to run. They generally test one outcome of one…

    8 Comments
  • Practicing Red-Green-Refactor

    In this article, you will learn the basics of the TDD process while walking through the Red-Green-Refactor steps…

    2 Comments
  • Compose and the View system can work together side by side.

    Introduction Compose and the View system can work together side by side. By the end of this article, you'll be able to…

    4 Comments
  • Understand Recomposition

    jetpack Compose is the newest thing in town. It is still in Alpha, but it shows promising signs.

    4 Comments
  • Dependency Injection pattern

    Most of the classes have some dependency that is needed for the proper functioning of the class. In the general case…

    5 Comments

Others also viewed

Explore content categories