# Troubleshooting

Learn how to diagnose and fix common issues with the Google Pay Android component.

## Exception types and error codes

### Component exceptions

The Google Pay Android component throws specific exceptions for different error scenarios:

| Exception  | Error code | Description |
|  --- | --- | --- |
| `GooglePayLoadFailedException` | SDK0704 | The Google Pay API failed to load. For example, due to network issues, Google Play Services unavailable, or device incompatibility. |
| `GooglePayNotReadyException` | SDK0701 | Google Pay isn't ready on this device. For example, Google Play Services not installed, no Google account, or no payment methods configured. |
| `GooglePayClientNotInitializedException` | SDK0703 | The Google Pay client was not properly initialised before attempting a payment. |
| `GooglePayConfigurationValidationFailedException` | SDK0705 | Configuration validation failed. For example, missing required fields, invalid payment method configuration, or incorrect format. |
| `GooglePayPaymentFailedException` | SDK0702 | Payment processing failed. For example, due to network issues, declined payment, or server errors. |
| `GooglePayDecryptAndTokenVaultFailedException` | SDK0706 | Payment token decryption or vault storage failed. Check gateway configuration and token handling. |
| `UnsupportedFundingTypeGooglePaySdkException` | SDK0108 | Google Pay is not included in the allowed funding types for this session. |


Payment cancellation by the user is not thrown as an exception. Instead, use the `onCancel` callback to handle user cancellations gracefully.

## Error handling best practices

### Comprehensive error handler


```kotlin
import android.util.Log
import com.pxp.checkout.components.googlepay.*
import com.pxp.checkout.services.kount.*
import com.pxp.checkout.models.*

val config = GooglePayButtonComponentConfig().apply {
    onError = { error ->
        // Log error for debugging
        Log.e("GooglePay", "Error: ${error::class.simpleName} - ${error.message}", error)
        
        // Handle specific error types
        val userMessage = when (error) {
            is GooglePayNotReadyException -> {
                showAlternativePaymentMethods()
                "Google Pay is not available on this device. Please use an alternative payment method."
            }
            is GooglePayConfigurationValidationFailedException -> {
                logCriticalError("Configuration validation error", error)
                "Payment system temporarily unavailable. Please try again in a few moments."
            }
            is GooglePayClientNotInitializedException -> {
                logCriticalError("Client not initialised", error)
                "Payment system not ready. Please refresh and try again."
            }
            is GooglePayLoadFailedException -> {
                "Unable to load payment system. Please check your connection and try again."
            }
            is GooglePayPaymentFailedException -> {
                "Payment failed. Please check your payment information and try again."
            }
            is GooglePayDecryptAndTokenVaultFailedException -> {
                logCriticalError("Token vault error", error)
                "Payment processing error. Please try again."
            }
            is UnsupportedFundingTypeGooglePaySdkException -> {
                showAlternativePaymentMethods()
                "Google Pay is not available for this session. Please use an alternative payment method."
            }
            else -> {
                logCriticalError("Unknown error", error)
                "An unexpected error occurred. Please try again or contact support."
            }
        }
        
        showErrorMessage(userMessage)
    }
    
    // Handle user cancellation separately (not an exception)
    onCancel = {
        Log.d("GooglePay", "User cancelled Google Pay")
    }
}
```

## Troubleshooting common issues

### Google Pay button isn't showing

The symptoms of this are:

- The button is not rendered in the UI.
- Empty space where the button should appear.
- No Google Pay option is visible to customers.


#### Diagnostic steps


```kotlin
import android.accounts.AccountManager
import android.os.Build
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability

// Check Google Pay availability
fun checkGooglePayAvailability(context: Context): Boolean {
    Log.d("GooglePay", "Checking availability...")
    
    // Check Google Play Services
    val googleApiAvailability = GoogleApiAvailability.getInstance()
    val resultCode = googleApiAvailability.isGooglePlayServicesAvailable(context)
    
    if (resultCode != ConnectionResult.SUCCESS) {
        Log.e("GooglePay", "Google Play Services unavailable: $resultCode")
        return false
    }
    
    // Check Android version
    if (Build.VERSION.SDK_INT < 24) {
        Log.e("GooglePay", "Android version too low: ${Build.VERSION.SDK_INT}")
        return false
    }
    
    // Check for Google account
    val accountManager = AccountManager.get(context)
    val accounts = accountManager.getAccountsByType("com.google")
    
    if (accounts.isEmpty()) {
        Log.w("GooglePay", "No Google account found")
        return false
    }
    
    // Verify payment method configuration
    val config = getGooglePayConfig()
    val paymentMethods = config.paymentDataRequest.allowedPaymentMethods
    
    if (paymentMethods.isEmpty() || 
        paymentMethods[0].parameters.allowedCardNetworks.isEmpty()) {
        Log.e("GooglePay", "Invalid payment method configuration")
        return false
    }
    
    Log.d("GooglePay", "Google Pay is available")
    return true
}
```

#### Solutions


```kotlin
import androidx.compose.runtime.*
import com.pxp.checkout.PxpCheckout
import com.pxp.checkout.components.googlepay.*

// Solution 1: Graceful fallback
@Composable
fun AdaptivePaymentButtons(pxpCheckout: PxpCheckout) {
    val googlePayAvailable = remember {
        checkGooglePayAvailability(context)
    }
    
    if (googlePayAvailable) {
        GooglePayButton(pxpCheckout)
    } else {
        AlternativePaymentMethods()
    }
}

// Solution 2: Progressive loading with timeout
@Composable
fun GooglePayWithFallback(pxpCheckout: PxpCheckout) {
    var showGooglePay by remember { mutableStateOf(true) }
    var isLoading by remember { mutableStateOf(true) }
    
    LaunchedEffect(Unit) {
        withTimeoutOrNull(5000) {
            delay(100)
            isLoading = false
        }
        
        if (isLoading) {
            showGooglePay = false
            isLoading = false
        }
    }
    
    when {
        isLoading -> CircularProgressIndicator()
        showGooglePay -> GooglePayButton(pxpCheckout)
        else -> AlternativePaymentMethods()
    }
}
```

### Payment sheet not opening

The symptoms of this are:

- Button is visible and clickable but nothing happens.
- No error messages are shown.
- Payment sheet doesn't appear after button click.


#### Diagnostic steps


```kotlin
import android.util.Log
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.pxp.checkout.components.googlepay.*
import kotlinx.coroutines.CompletableDeferred

val config = GooglePayButtonComponentConfig().apply {
    onGooglePaymentButtonClicked = {
        Log.d("GooglePay", "Button clicked")
        Log.d("GooglePay", "Config: $paymentDataRequest")
        
        // Verify Google Play Services
        val googlePlayAvailable = GoogleApiAvailability.getInstance()
            .isGooglePlayServicesAvailable(context)
        
        if (googlePlayAvailable != ConnectionResult.SUCCESS) {
            Log.e("GooglePay", "Google Play Services unavailable: $googlePlayAvailable")
        }
        
        // Verify amount
        val amount = paymentDataRequest.transactionInfo.totalPrice.toDoubleOrNull()
        if (amount == null || amount <= 0) {
            Log.e("GooglePay", "Invalid amount: $amount")
        }
        
        CompletableDeferred(Unit)
    }
}
```

#### Solutions


```kotlin
import com.pxp.checkout.PxpCheckout
import com.pxp.checkout.components.googlepay.*
import kotlinx.coroutines.CompletableDeferred

// Solution 1: Add validation before opening sheet
val config = GooglePayButtonComponentConfig().apply {
    onGooglePaymentButtonClicked = {
        // Validate synchronously
        val email = getCustomerEmail()
        if (email.isEmpty() || !email.contains("@")) {
            showError("Please enter a valid email address")
            return@onGooglePaymentButtonClicked null
        }
        
        // Validate terms
        if (!areTermsAccepted()) {
            showError("Please accept terms and conditions")
            return@onGooglePaymentButtonClicked null
        }
        
        // Validate amount
        val amount = paymentDataRequest.transactionInfo.totalPrice.toDoubleOrNull()
        if (amount == null || amount <= 0) {
            showError("Invalid payment amount")
            return@onGooglePaymentButtonClicked null
        }
        
        // All validation passed
        CompletableDeferred(Unit)
    }
}

// Solution 2: Handle configuration errors
import android.util.Log
import com.pxp.checkout.PxpCheckout
import com.pxp.checkout.components.googlepay.*
import com.pxp.checkout.types.ComponentType

fun createGooglePayButton(pxpCheckout: PxpCheckout): GooglePayButtonComponent? {
    return try {
        validateGooglePayConfiguration(config)
        
        pxpCheckout.createComponent<GooglePayButtonComponent, GooglePayButtonComponentConfig>(
            type = ComponentType.GOOGLE_PAY_BUTTON,
            config = config
        )
    } catch (e: GooglePayConfigurationValidationFailedException) {
        Log.e("GooglePay", "Configuration validation failed", e)
        showError("Payment configuration error. Please contact support.")
        showAlternativePaymentMethods()
        null
    } catch (e: Exception) {
        Log.e("GooglePay", "Failed to create button", e)
        showError("Unable to initialise Google Pay. Please try again.")
        showAlternativePaymentMethods()
        null
    }
}
```

### Payment authorisation failures

The symptoms of this are:

- The Google Pay sheet appears but payment fails.
- Customer sees a generic error message.
- Transaction doesn't complete successfully.


#### Diagnostic steps


```kotlin
import android.util.Log
import com.pxp.checkout.PxpCheckout
import com.pxp.checkout.components.googlepay.*
import com.pxp.checkout.services.kount.*
import com.pxp.checkout.models.*
import kotlinx.coroutines.async

val config = GooglePayButtonComponentConfig().apply {
    onPreAuthorisation = suspend { data ->
        Log.d("GooglePay", "Pre-authorisation data: $data")
        Log.d("GooglePay", "Gateway token ID: ${data?.gatewayTokenId}")
        
        try {
            // Validate amount
            val amount = getOrderTotal()
            if (amount <= 0) {
                Log.e("GooglePay", "Invalid amount: $amount")
                throw IllegalStateException("Invalid transaction amount")
            }
            
            // Check inventory
            val inventoryAvailable = checkInventory()
            if (!inventoryAvailable) {
                Log.e("GooglePay", "Inventory not available")
                throw IllegalStateException("Items no longer available")
            }
            
            GooglePayTransactionInitData(
                riskScreeningData = RiskScreeningData(
                    performRiskScreening = true,
                    excludeDeviceData = false,
                    userIp = "192.168.1.100",
                    account = RiskScreeningAccount(
                        id = "user_12345678",
                        creationDateTime = "2024-01-15T10:30:00.000Z"
                    ),
                    fulfillments = listOf(
                        RiskScreeningFulfillment(
                            type = FulfillmentType.SHIPPED,
                            recipientPerson = RiskScreeningRecipientPerson(
                                phoneNumber = "+1234567890"
                            )
                        )
                    )
                )
            )
        } catch (e: Exception) {
            Log.e("GooglePay", "Pre-authorisation failed", e)
            null // Cancel payment
        }
    }
    
    onPostAuthorisation = { result, paymentData ->
        when (result) {
            is MerchantSubmitResult -> {
                Log.d("GooglePay", "Payment authorised: ${result.merchantTransactionId}")
            }
            is FailedSubmitResult -> {
                Log.e("GooglePay", "Payment failed: ${result.errorCode} - ${result.errorReason}")
            }
        }
    }
}
```

#### Solutions


```kotlin
import android.util.Log
import kotlinx.coroutines.delay

// Solution 1: Enhanced validation
fun validatePaymentData(paymentData: Any?): Boolean {
    require(paymentData != null) { "Payment data is missing" }
    
    val amount = getOrderTotal()
    require(amount > 0 && amount < 1000000) { "Invalid payment amount" }
    
    require(getCurrentCustomerEmail().isNotEmpty()) { "Customer email is required" }
    
    return true
}

// Solution 2: Implement retry logic
suspend fun processPaymentWithRetry(
    paymentData: Any,
    maxRetries: Int = 2
): PaymentResult {
    repeat(maxRetries) { attempt ->
        try {
            Log.d("Payment", "Payment attempt ${attempt + 1} of $maxRetries")
            return processPayment(paymentData)
        } catch (e: Exception) {
            Log.w("Payment", "Attempt ${attempt + 1} failed: ${e.message}")
            
            if (isRetryableError(e) && attempt < maxRetries - 1) {
                delay(1000L * (attempt + 1))
                continue
            }
            throw e
        }
    }
    throw IllegalStateException("Payment retry logic failed")
}

fun isRetryableError(error: Exception): Boolean {
    val retryableErrors = listOf("network error", "timeout", "temporary failure")
    return retryableErrors.any { error.message?.lowercase()?.contains(it) == true }
}
```

## Debugging tools and techniques

### Logging setup


```kotlin
import android.content.Context
import android.util.Log
import android.os.Build
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.pxp.checkout.components.googlepay.*

// Log environment information
fun logGooglePayEnvironment(context: Context) {
    Log.d("GooglePay", "=== Environment ===")
    Log.d("GooglePay", "Device: ${Build.MANUFACTURER} ${Build.MODEL}")
    Log.d("GooglePay", "Android: ${Build.VERSION.RELEASE} (SDK ${Build.VERSION.SDK_INT})")
    
    val googlePlayAvailable = GoogleApiAvailability.getInstance()
        .isGooglePlayServicesAvailable(context)
    Log.d("GooglePay", "Google Play Services: $googlePlayAvailable")
}

// Log configuration
fun logGooglePayConfig(config: GooglePayButtonComponentConfig) {
    Log.d("GooglePay", "=== Configuration ===")
    val method = config.paymentDataRequest.allowedPaymentMethods.firstOrNull()
    Log.d("GooglePay", "Card networks: ${method?.parameters?.allowedCardNetworks}")
    Log.d("GooglePay", "Auth methods: ${method?.parameters?.allowedAuthMethods}")
    Log.d("GooglePay", "Amount: ${config.paymentDataRequest.transactionInfo.totalPrice}")
    Log.d("GooglePay", "Currency: ${config.paymentDataRequest.transactionInfo.currencyCode}")
}
```

### Logcat filtering

Filter Logcat for Google Pay related logs:


```bash
adb logcat | grep -E "GooglePay|PxpCheckout"
```

### Android Studio Profiler

Use Android Studio's built-in profilers to monitor performance, memory usage, and network calls during payment processing.

## Prevention and best practices

### Pre-flight checks

Perform checks before initialising Google Pay to catch issues early:


```kotlin
import android.accounts.AccountManager
import android.content.Context
import android.os.Build
import android.util.Log
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability

fun performPreflightChecks(context: Context): Boolean {
    // Check Google Play Services
    val googlePlayResult = GoogleApiAvailability.getInstance()
        .isGooglePlayServicesAvailable(context)
    if (googlePlayResult != ConnectionResult.SUCCESS) {
        Log.w("GooglePay", "Google Play Services unavailable")
        return false
    }
    
    // Check Android version
    if (Build.VERSION.SDK_INT < 24) {
        Log.w("GooglePay", "Android version too low")
        return false
    }
    
    // Check Google account
    val accountManager = AccountManager.get(context)
    if (accountManager.getAccountsByType("com.google").isEmpty()) {
        Log.w("GooglePay", "No Google account")
        return false
    }
    
    // Check payment configuration
    val config = getGooglePayConfig()
    if (config.paymentDataRequest.allowedPaymentMethods.isEmpty()) {
        Log.w("GooglePay", "No payment methods configured")
        return false
    }
    
    return true
}

// Use before initialisation
import androidx.compose.runtime.*
import com.pxp.checkout.PxpCheckout

@Composable
fun SafeGooglePayButton(pxpCheckout: PxpCheckout) {
    val isAvailable = remember {
        performPreflightChecks(context)
    }
    
    if (isAvailable) {
        GooglePayButton(pxpCheckout)
    } else {
        AlternativePaymentMethods()
    }
}
```

### Error monitoring

Track errors using your preferred monitoring service:


```kotlin
import android.util.Log
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.pxp.checkout.components.googlepay.*
import com.pxp.checkout.models.*

val config = GooglePayButtonComponentConfig().apply {
    onError = { error ->
        // Log to your monitoring service (e.g., Firebase Crashlytics, Sentry)
        FirebaseCrashlytics.getInstance().apply {
            setCustomKey("component", "GooglePay")
            setCustomKey("error_type", error::class.simpleName ?: "Unknown")
            recordException(error)
        }
        
        // Show user-friendly message
        showErrorMessage(error)
    }
    
    onPostAuthorisation = { result, _ ->
        when (result) {
            is MerchantSubmitResult -> {
                Log.d("GooglePay", "Payment successful: ${result.merchantTransactionId}")
            }
            is FailedSubmitResult -> {
                Log.e("GooglePay", "Payment failed: ${result.errorCode}")
            }
        }
    }
}
```