Skip to content

Google Pay

Accept Google Pay payments with automatic wallet handling, one-click checkout, and seamless Android integration.

Overview

Google Pay is automatically included in the drop-in when enabled in your session. The drop-in handles all Google Pay setup, button rendering, wallet interaction, and payment processing automatically.

Key benefits

  • Google Pay appears automatically in the drop-in when it's enabled in your session configuration.
  • The drop-in handles all Google Pay setup, so you don't need Google Pay-specific code.
  • Google Pay uses the same onSuccess and onError callbacks as other payment methods for a unified integration.
  • Customers can pay using any card or payment method saved in their Google account.
  • Native Android Google Pay UI for familiar and trusted checkout experience.
  • One-click checkout for fast and frictionless payments.
  • Enhanced security with tokenised payment credentials.

How it works

When a customer selects Google Pay:

  1. The customer taps "Google Pay" in the payment options list.
  2. The native Android Google Pay sheet opens.
  3. The customer selects their preferred payment method from Google Wallet.
  4. The customer confirms the payment in the Google Pay interface.
  5. The Google Pay sheet closes automatically.
  6. The payment is processed through Unity.
  7. Your onSuccess callback fires.

Configuration

Configure Google Pay-specific settings for the payment button, merchant information, and transaction parameters.

Configuration properties

The following properties are available for Google Pay configuration:

Property Description
collectCvc
CollectCvcMode?
Controls when to collect CVC during Google Pay transactions.

Possible values:
  • CollectCvcMode.ALWAYS: Always collect CVC.
  • CollectCvcMode.NEVER: Never collect CVC.
  • CollectCvcMode.DEFAULT: Collect CVC only when 3DS isn't performed (recommended).
billingAddressRequired
Boolean?
Requests a billing address from Google Pay. When enabled, use billingAddressParameters to specify phone number requirements.
billingAddressParameters
BillingAddressParameters?
Billing address options object.
billingAddressParameters.phoneNumberRequired
Boolean?
Whether to require a phone number in the billing address.
shippingAddressRequired
Boolean?
Requests a full shipping address from Google Pay. When enabled, use shippingAddressParameters to specify address restrictions.
shippingAddressParameters
ShippingAddressParameters?
Shipping address options object.
shippingAddressParameters.allowedCountryCodes
List<String>?
Allowed country codes for shipping addresses (e.g., listOf("GB", "US", "FR")).
shippingAddressParameters.phoneNumberRequired
Boolean?
Whether to require a phone number in the shipping address.
blockedIssuerCountryCodes
List<String>?
Blocks cards issued in the listed ISO country codes. Don't combine with methodConfig.global.allowedIssuerCountryCodes.

Settings used from global configuration

Google Pay inherits the following settings from methodConfig.global:

Property Description
acceptedCardNetworks
List<CardNetworks>?
Which card brands to accept through Google Pay (e.g., listOf(CardNetworks.VISA, CardNetworks.MASTERCARD)).
allowedIssuerCountryCodes
List<String>?
Allowed card issuer countries as ISO country codes (e.g., listOf("GB", "US", "FR")).
onCancel
(paymentMethod: PaymentMethod, data: Any?) -> Unit
Called when user cancels Google Pay payment (closes sheet).

Complete example

This example shows a full Google Pay configuration with global callbacks and all Google Pay-specific options:

methodConfig = DropInMethodConfig(
    // Global callbacks that apply to Google Pay
    global = DropInGlobalConfig(
        // Handle Google Pay cancellation
        onCancel = { paymentMethod, data ->
            if (paymentMethod == PaymentMethod.GOOGLE_PAY) {
                Log.d("Checkout", "Google Pay payment cancelled: $data")
                
                // Track analytics
                analytics.trackEvent("google_pay_cancelled", mapOf(
                    "timestamp" to System.currentTimeMillis()
                ))
                
                // Update UI
                Toast.makeText(
                    context,
                    "Google Pay payment was cancelled. Please try again.",
                    Toast.LENGTH_SHORT
                ).show()
            }
        }
    ),
    
    googlePay = DropInGooglePayConfig(
        // CVC collection mode
        collectCvc = CollectCvcMode.DEFAULT,
        
        // Billing address collection
        billingAddressRequired = true,
        billingAddressParameters = BillingAddressParameters(
            phoneNumberRequired = true
        ),
        
        // Shipping address collection
        shippingAddressRequired = true,
        shippingAddressParameters = ShippingAddressParameters(
            allowedCountryCodes = listOf("GB", "US", "FR", "DE"),
            phoneNumberRequired = true
        ),
        
        // Block specific issuer countries
        blockedIssuerCountryCodes = null // Don't use with global.allowedIssuerCountryCodes
    )
)

Google Pay requirements

Google Pay requires the following to function correctly:

  • Android compatibility: Android 7.0 (API level 24) or higher.
  • Google Play Services: Google Play Services must be installed and up to date.
  • Google account: The customer must be signed in to a Google account with Google Wallet set up.
  • HTTPS: Your backend endpoints must be served over HTTPS.
  • Unity Portal configuration: Google Pay must be enabled and configured in the Unity Portal.
  • Entry type: Google Pay only supports entryType: Ecom.

Implementation

Google Pay works through the standard implementation, with no Google Pay-specific code needed:

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.pxp.checkout.checkoutdropin.CheckoutDropIn
import com.pxp.checkout.checkoutdropin.types.CheckoutDropInConfig
import com.pxp.checkout.components.checkoutdropincomponent.CheckoutDropInComponent
import com.pxp.checkout.models.DropInTransactionData
import com.pxp.checkout.models.DropInTransactionIntentData
import com.pxp.checkout.models.Environment
import com.pxp.checkout.models.EntryType
import com.pxp.checkout.models.IntentType
import com.pxp.checkout.checkoutdropin.types.DropInSubmitResult
import com.pxp.checkout.exceptions.BaseSdkException

@Composable
fun CheckoutScreen() {
    val context = LocalContext.current
    var sessionData by remember { mutableStateOf<SessionData?>(null) }
    var checkoutDropInComponent by remember { mutableStateOf<CheckoutDropInComponent?>(null) }
    var isLoading by remember { mutableStateOf(true) }
    
    // Fetch session from backend (with Google Pay enabled)
    LaunchedEffect(Unit) {
        try {
            val response = apiClient.post("/api/create-session") {
                contentType(ContentType.Application.Json)
            }
            
            if (response.status.value == 200) {
                val result = response.body<SessionResponse>()
                if (result.success && result.data != null) {
                    sessionData = result.data
                } else {
                    Log.e("Checkout", "Failed to create session: ${result.error}")
                }
            }
        } catch (e: Exception) {
            Log.e("Checkout", "Error creating session", e)
        } finally {
            isLoading = false
        }
    }
    
    // Initialize Drop-in once session is loaded
    LaunchedEffect(sessionData) {
        sessionData?.let { session ->
            val checkoutDropIn = CheckoutDropIn.initialize(
                context = context,
                config = CheckoutDropInConfig(
                    environment = Environment.TEST,
                    session = session,
                    ownerType = "MerchantGroup",
                    ownerId = "MERCHANT-1",
                    transactionData = DropInTransactionData(
                        currency = "GBP",
                        amount = 99.99,
                        entryType = EntryType.Ecom,
                        intent = DropInTransactionIntentData(
                            card = IntentType.Purchase  // Google Pay uses card intent
                        ),
                        merchant = "Demo Store",
                        merchantTransactionId = { UUID.randomUUID().toString() },
                        merchantTransactionDate = { Instant.now().toString() }
                    ),
                    onGetShopper = {
                        Shopper(id = "shopper-123")
                    },
                    onSuccess = { result: DropInSubmitResult ->
                        Log.d("Checkout", "Google Pay payment successful!")
                        Log.d("Checkout", "System transaction ID: ${result.systemTransactionId}")
                        Log.d("Checkout", "Payment method: ${result.paymentMethod}") // Will be "GooglePay"
                        
                        // CRITICAL: Verify on backend
                        coroutineScope.launch {
                            verifyPaymentOnBackend(result)
                        }
                    },
                    onError = { error: BaseSdkException ->
                        Log.e("Checkout", "Google Pay payment failed", error)
                        Toast.makeText(
                            context,
                            "Payment failed: ${error.message}",
                            Toast.LENGTH_LONG
                        ).show()
                    }
                )
            )
            checkoutDropInComponent = checkoutDropIn.create()
        }
    }
    
    // Render Drop-in
    if (isLoading) {
        CircularProgressIndicator()
    } else {
        checkoutDropInComponent?.Content(modifier = Modifier.fillMaxWidth())
    }
}

Session configuration (backend)

Enable Google Pay in your session request:

// BACKEND: Create a session with Google Pay enabled
const sessionRequest = {
  merchant: "MERCHANT-1",
  site: "SITE-1",
  sessionTimeout: 120,
  merchantTransactionId: crypto.randomUUID(),
  transactionMethod: {
    intent: {
      card: "Purchase"  // Google Pay uses card intent
    }
  },
  amounts: {
    currencyCode: "GBP",
    transactionValue: 99.99
  },
  allowedFundingTypes: {
    wallets: {
      googlePay: {
        // Google Pay configuration from the Unity Portal will be used
        // You can optionally specify merchant information here
        merchantName: "Demo Store",
        merchantId: "BCR2DN4TZXY2MLBU"
      }
    }
  },
  allowTransaction: true,
  serviceType: "CheckoutDropIn"
};

Payment flows

Drop-in supports two Google Pay payment flows, configured via the intent parameter:

Immediate, single-step payment where funds are captured right away.

import com.pxp.checkout.models.DropInTransactionData
import com.pxp.checkout.models.DropInTransactionIntentData
import com.pxp.checkout.models.EntryType
import com.pxp.checkout.models.IntentType

transactionData = DropInTransactionData(
    currency = "GBP",
    amount = 99.99,
    entryType = EntryType.Ecom,
    intent = DropInTransactionIntentData(
        card = IntentType.Purchase  // Pay Now flow - Google Pay uses card intent
    ),
    merchant = "Demo Store",
    merchantTransactionId = { UUID.randomUUID().toString() },
    merchantTransactionDate = { Instant.now().toString() }
)

Use this flow for:

  • Digital products
  • Simple orders with immediate fulfillment
  • Subscriptions
  • Donations

Handling responses

Google Pay callback data

When a Google Pay payment succeeds, your onSuccess callback receives the same standard result as other payment methods:

onSuccess = { result: DropInSubmitResult ->
    Log.d("Checkout", "Payment details:")
    Log.d("Checkout", "- System transaction ID: ${result.systemTransactionId}")
    Log.d("Checkout", "- Merchant transaction ID: ${result.merchantTransactionId}")
    Log.d("Checkout", "- Payment method: ${result.paymentMethod}") // "GooglePay"
    
    // Note: Amount, currency, and other transaction details must be retrieved from backend
    // Google Pay tokenisation is handled internally
}

Error handling

Handle Google Pay-specific errors:

import com.pxp.checkout.exceptions.BaseSdkException

onError = { error: BaseSdkException ->
    Log.e("Checkout", "Error code: ${error.code}")
    Log.e("Checkout", "Error message: ${error.message}")
    
    // Handle user cancellation
    if (error.message?.contains("cancelled", ignoreCase = true) == true ||
        error.message?.contains("closed", ignoreCase = true) == true) {
        Log.d("Checkout", "User cancelled Google Pay payment")
        // Don't show error - user intentionally cancelled
        return@CheckoutDropInConfig
    }
    
    // Handle Google Pay-specific errors based on message content
    when {
        error.message?.contains("not available", ignoreCase = true) == true -> {
            Toast.makeText(
                context,
                "Google Pay isn't available on this device. Please use a different payment method.",
                Toast.LENGTH_LONG
            ).show()
        }
        error.message?.contains("not supported", ignoreCase = true) == true -> {
            Toast.makeText(
                context,
                "Google Pay isn't supported for this transaction. Please use a different payment method.",
                Toast.LENGTH_LONG
            ).show()
        }
        error.message?.contains("declined", ignoreCase = true) == true -> {
            Toast.makeText(
                context,
                "Payment declined. Please try a different payment method.",
                Toast.LENGTH_LONG
            ).show()
        }
        error.message?.contains("timeout", ignoreCase = true) == true -> {
            Toast.makeText(
                context,
                "Google Pay timed out. Please try again.",
                Toast.LENGTH_SHORT
            ).show()
        }
        else -> {
            Toast.makeText(
                context,
                "Google Pay payment failed: ${error.message}",
                Toast.LENGTH_LONG
            ).show()
        }
    }
}

Common error scenarios

The following table describes common Google Pay error scenarios:

ScenarioHow to detectRecommended action
User cancellederror.message contains "cancelled" or "closed"No alert needed - user action was intentional
Google Pay unavailableerror.message contains "not available"Suggest using different payment method
Not supportederror.message contains "not supported"Direct user to alternative payment options
Payment declinederror.message contains "declined"Suggest trying different payment method
Timeouterror.message contains "timeout"Suggest retrying the payment

Google Pay errors return descriptive messages rather than specific error code constants. Check the error.message property for error details. You can also use the onCancel callback in methodConfig.global to specifically handle user cancellations.

Backend verification

Always verify Google Pay payments on your backend to ensure payment success before fulfilling orders:

import com.pxp.checkout.checkoutdropin.types.DropInSubmitResult

onSuccess = { result: DropInSubmitResult ->
    // Send to backend for verification
    coroutineScope.launch {
        try {
            val response = apiClient.post("/api/verify-payment") {
                contentType(ContentType.Application.Json)
                setBody(mapOf(
                    "systemTransactionId" to result.systemTransactionId,
                    "merchantTransactionId" to result.merchantTransactionId
                ))
            }
            
            if (response.status.value == 200) {
                val verified = response.body<VerificationResponse>()
                if (verified.success) {
                    // Navigate to success screen
                    navController.navigate("success?orderId=${verified.orderId}")
                } else {
                    Toast.makeText(
                        context,
                        "Payment verification failed",
                        Toast.LENGTH_LONG
                    ).show()
                }
            }
        } catch (e: Exception) {
            Log.e("Checkout", "Verification error", e)
            Toast.makeText(
                context,
                "Failed to verify payment",
                Toast.LENGTH_LONG
            ).show()
        }
    }
}

Backend verification code

Use the following backend code to verify Google Pay transactions via the PXP API:

// BACKEND: Verify Google Pay payment
app.post('/api/verify-payment', async (req, res) => {
  const { systemTransactionId, merchantTransactionId } = req.body;
  
  try {
    // Query the PXP API to get transaction details
    const txnPath = `api/v1/transactions/${systemTransactionId}`;
    const { authHeader, requestId } = createAuthHeader(
      txnPath,
      '',
      process.env.PXP_TOKEN_ID,
      process.env.PXP_TOKEN_VALUE
    );
    
    const transaction = await fetch(
      `https://api-services.pxp.io/${txnPath}`,
      {
        headers: {
          'X-Client-Id': process.env.PXP_CLIENT_ID,
          'X-Request-Id': requestId,
          'Authorization': authHeader
        }
      }
    ).then(r => r.json());
    
    // Verify transaction state
    if (transaction.state !== 'Authorised' && transaction.state !== 'Captured') {
      return res.json({ success: false, error: 'Transaction not successful' });
    }
    
    // Verify merchant transaction ID matches
    if (transaction.merchantTransactionId !== merchantTransactionId) {
      return res.json({ success: false, error: 'Transaction ID mismatch' });
    }
    
    // Verify amount matches expected amount from your order records
    const order = await getOrderByMerchantTransactionId(merchantTransactionId);
    const txnAmount = transaction.amounts?.transactionValue || transaction.amount || 0;
    if (Math.abs(txnAmount - order.amount) > 0.01) {
      return res.json({ success: false, error: 'Amount mismatch' });
    }
    
    // Google Pay payments show as Card funding type (processed as card transactions)
    const fundingType = transaction.fundingData?.fundingType || 
                       transaction.fundingType || 
                       'Unknown';
    if (fundingType !== 'Card') {
      return res.json({ success: false, error: 'Invalid funding type' });
    }
    
    // Fulfill order
    const orderId = await fulfillOrder(transaction);
    
    return res.json({ success: true, orderId });
    
  } catch (error) {
    console.error('Verification error:', error);
    return res.json({ success: false, error: 'Verification failed' });
  }
});

Testing Google Pay

Test cards

Use the following test cards in the Google Pay test environment:

Card networkTest card numberExpiryCVC
Visa4111 1111 1111 1111Any future dateAny 3 digits
Mastercard5555 5555 5555 4444Any future dateAny 3 digits
Amex3782 822463 10005Any future dateAny 4 digits

To test Google Pay, you must add these test cards to your Google account in the test environment. Open Google Wallet on your test device and add the test cards manually.

Test environment

Configure your app for Google Pay testing:

// Use test environment for Google Pay testing
CheckoutDropInConfig(
    environment = Environment.TEST,  // Test environment
    // ... other config
    methodConfig = DropInMethodConfig(
        googlePay = DropInGooglePayConfig(
            collectCvc = CollectCvcMode.DEFAULT,
            billingAddressRequired = false,
            shippingAddressRequired = false
        )
    )
)