# Troubleshooting

Learn how to fix common issues with the PayPal component on Android.

## Common issues and solutions

| Issue | Description | Next steps |
|  --- | --- | --- |
| **PayPal SDK not loading** | Logcat shows `PayPal SDK not loaded`. This could be due to network issues, incorrect configuration, or SSL/TLS errors. | Check your internet connection.Verify PayPal client ID in `gradle.properties`.Ensure WebView has network access.Check for SSL/TLS certificate errors.Enable debug mode: `PayPalQueryParams(debug = true)`. |
| **Button not rendering** | The PayPal button doesn't appear in the container. | Verify component is properly created and rendered.Check Compose modifier provides adequate space.Ensure the component is mounted in Compose hierarchy.Check logcat for WebView initialisation errors.Verify WebView is installed: `WebView.getCurrentWebViewPackage() != null`. |
| **Validation errors** | You receive errors via the `onSubmitError` callback. | Check all required fields are provided.Verify email format for `payeeEmailAddress`.Ensure currency codes match transaction currency.Validate shipping address when using `SetProvidedAddress`.Confirm the `fundingSources` type matches the `renderType`. |
| **Funding source issues** | Certain funding sources not available. | Verify funding sources are supported: `paypal`, `paylater`.Check geographic restrictions.Ensure proper `renderType` configuration for `fundingSources`.Verify PayPal account supports requested funding sources. |
| **WebView crashes or freezes** | WebView becomes unresponsive or crashes app. | Update Android System WebView via Play Store.Check available memory: `Runtime.getRuntime().freeMemory()`.Review conflicting WebView configurations.Check logcat for crash reports.Reduce WebView height: `maxHeightPercent = 0.5f`. |
| **WebView not available** | The button fails to render and there's a WebView initialisation error. | Check WebView availability: `WebView.getCurrentWebViewPackage()`.Prompt user to install/update WebView from Play Store.Open Play Store: `market://details?id=com.google.android.webview`.Show error message if WebView unavailable. |
| **Network connectivity** | The SDK fails to load and there are timeout errors. | Check network: `NetworkCapabilities.NET_CAPABILITY_INTERNET`.Validate connection: `NetworkCapabilities.NET_CAPABILITY_VALIDATED`.Handle network errors in `onError` callback.Show user-friendly message: "No internet connection".Implement retry mechanism. |
| **SSL/TLS certificate errors** | WebView fails to load with SSL errors. This could be due to expired certificates, incorrect device time, or outdated WebView. | **Never ignore SSL errors in production**Ensure device date/time is correct.Update Android System WebView.Check certificate validity.For debug only: Log SSL errors via `onReceivedSslError`. |
| **Memory issues** | The app crashes or becomes unresponsive during payment. | Monitor memory: Check `runtime.totalMemory() - runtime.freeMemory()`.Warn if available memory < 50 MB.Ensure proper component disposal via `DisposableEffect`.Reduce WebView memory footprint with `maxHeightPercent`.Profile memory usage with Android Studio Profiler. |
| **Back button not working** | The Android back button doesn't cancel the PayPal flow. | Implement `BackHandler` in Compose navigation.Override `onBackPressed()` in Activity.Ensure `onCancel` callback is implemented.Show confirmation dialog before cancelling.Handle WebView navigation properly. |
| **Configuration lost on rotation** | The component state is lost on screen rotation. | Use `ViewModel` to preserve payment state.Store state in `MutableStateFlow` or `LiveData`.Use `remember` for component creation in Compose.Implement proper state handling with `collectAsState()`.Consider `rememberSaveable` for critical state. |
| **Missing script parameters** | One-click payment isn't working. | Verify `userIdToken` is provided for vaulted payments.Check vault status before setting `enableOneClickPayment = true`.Ensure `scriptParams` object is properly configured.Validate token with backend before use. |
| **Component not disposing** | There are memory leaks or resource retention. | Component disposal is automatic in Compose.Verify `DisposableEffect` is used correctly.Don't hold strong references to components.Check for circular references.Use Android Studio Profiler to detect leaks. |
| **Event callbacks not firing** | `onSuccess`, `onError`, or other callbacks not called. | Verify callbacks are defined in configuration.Check logcat for error messages.Ensure callbacks are not null.Test with debug mode enabled.Add logging to all callbacks to verify execution. |
| **Shipping address validation fails** | The shipping address was rejected by PayPal. | Validate address format before submission.Ensure all required fields are provided.Check country code is valid ISO 3166-1 alpha-2.Verify postal code format for country.Use `onShippingAddressChange` to validate in real-time. |
| **Payment authorisation fails** | The payment starts but fails to complete. For example, due to insufficient PayPal funds, declined payment method, or fraud detection. | User should check PayPal account status.Verify payment method is valid and active.Check transaction amount is within limits.Review PayPal dashboard for declined reasons.Implement proper error handling in `onError`. |
| **Debug mode issues** | Debug mode isn't working or is causing errors. | Enable via `PayPalQueryParams(debug = true)`.**Only use in development** as it impacts performance.Check logcat for verbose PayPal logs.Disable for production builds.Enable WebView debugging: `WebView.setWebContentsDebuggingEnabled(true)`. |
| **Button styling not applied** | The button doesn't match the expected style. | Verify all `PayPalButtonStyle` parameters are valid.Check `color` values: `gold`, `blue`, `silver`, `white`, `black`.Check `shape` values: `rect`, `pill`.Check `layout` values: `vertical`, `horizontal`.Ensure `height` is between 25-55 pixels. |
| **Multiple buttons rendering incorrectly** | Buttons overlap or don't display properly with `setOfButtons`. | Verify `renderType = "setOfButtons"`.Ensure `fundingSources` is a `List<String>`.Check container has adequate space.Verify all funding sources are valid.Adjust button layout to `vertical` for better spacing. |
| **Locale/language issues** | The button is displayed in the wrong language. | Set locale explicitly: `locale = "en-US"`Verify locale format is correct (`language_COUNTRY`).Check device locale settings.Ensure locale is supported by PayPal.Use `buyerCountry` in `PayPalQueryParams` for country-specific options. |
| **Consent component not working** | The consent checkbox doesn't affect payment. | Verify `consentComponent` is passed to PayPal config.Implement `onGetConsent` callback correctly.Check that the consent component is created before the PayPal component.Ensure `getValue()` returns BooleanTest consent state changes in logcat. |
| **Toggle component issues** | The toggle doesn't switch payment methods. | Verify `toggleComponent` is linked to PayPal config.Check `initialChecked` state is set correctly.Ensure toggle state changes are handled.Verify `userIdToken` is available for vaulted accounts.Test toggle interaction in UI. |
| **Pre-built component issues** | The pre-built component isn't rendering sub-components. | Use `ComponentType.PREBUILT_PAYPAL` type.Verify both `paypalConfig` and `toggleConfig` are provided.Check `PreBuiltPayPalComponentConfig` is properly constructed.Don't manually create sub-components (pre-built handles them).Review logcat for component creation errors. |


## Logging and debugging

To diagnose issues, enable comprehensive logging.

### Enable debug mode


```kotlin
val paypalConfig = PayPalComponentConfig(
    queryParams = PayPalQueryParams(
        debug = true // Only use in development
    )
)
```

### Enable WebView debugging


```kotlin
// In your Application class or before SDK initialisation
if (BuildConfig.DEBUG) {
    WebView.setWebContentsDebuggingEnabled(true)
}
```

### Logcat filters

Use these filters to debug PayPal issues:


```bash
# PXP Checkout logs
adb logcat | grep -E "(PxpCheckout|PXP)"

# PayPal-specific logs
adb logcat | grep -E "(PayPal|paypal)"

# WebView-related logs
adb logcat | grep -E "(WebView|chromium)"

# Network-related logs
adb logcat | grep -E "(OkHttp|Retrofit|Network)"

# Comprehensive debugging
adb logcat | grep -E "(PxpCheckout|PayPal|WebView)" -A 5
```

### Add verbose logging to callbacks


```kotlin
val paypalConfig = PayPalComponentConfig(
    onSuccess = { data ->
        Log.d("PayPal", "✓ Payment successful")
        Log.v("PayPal", "Success data: $data")
        processPayment(data)
    },
    onError = { error ->
        Log.e("PayPal", "✗ Payment error: $error")
        Log.e("PayPal", "Stack trace:", Exception(error))
        handleError(error)
    },
    onCancel = {
        Log.w("PayPal", "⚠ Payment cancelled by user")
        handleCancellation()
    },
    onOrderCreated = { orderData ->
        Log.i("PayPal", "📦 Order created: $orderData")
        trackOrderCreation(orderData)
    },
    onScriptLoaded = {
        Log.d("PayPal", "✓ PayPal SDK script loaded")
        hideLoadingIndicator()
    }
)
```

## Useful code snippets

### Check WebView availability

**When to use**: Before rendering any PayPal component, verify that Android System WebView is installed and functional on the device. This is critical because the PayPal SDK requires WebView to render the payment interface.

**What it does**: Attempts to retrieve the currently installed WebView package. If WebView is not installed, disabled, or corrupted, this function returns `false`. It uses exception handling because `getCurrentWebViewPackage()` may throw exceptions on devices without WebView support.

**Integration**: Call this function in your Activity's `onCreate()` or before creating the PayPal component. If it returns `false`, display an error message and prompt the user to install or update WebView from the Play Store.


```kotlin
fun checkWebViewAvailability(context: Context): Boolean {
    return try {
        val webViewPackage = WebView.getCurrentWebViewPackage()
        webViewPackage != null
    } catch (e: Exception) {
        Log.e("PayPal", "WebView not available", e)
        false
    }
}

// Usage example:
if (!checkWebViewAvailability(context)) {
    // Show error and prompt user to install WebView
    promptWebViewUpdate(context)
    return
}
// Proceed with PayPal component creation
```

### Check network connectivity

**When to use**: Before initiating a payment flow or when diagnosing network-related errors. The PayPal SDK requires an active internet connection to load scripts and process payments.

**What it does**: Uses the modern `ConnectivityManager` API (API 23+) to check not only if a network connection exists, but also if it has validated internet connectivity. `NET_CAPABILITY_INTERNET` checks for a network route to the internet, while `NET_CAPABILITY_VALIDATED` confirms the connection has been tested and verified by the system.

**Integration**: Call this function before creating the PayPal component or in response to network errors. Display a user-friendly message if the network is unavailable and optionally implement a retry mechanism.

**Required permissions**: Add `<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />` to your AndroidManifest.xml.


```kotlin
fun isNetworkAvailable(context: Context): Boolean {
    val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val network = connectivityManager.activeNetwork ?: return false
    val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
    
    return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
           capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
}

// Usage example:
if (!isNetworkAvailable(context)) {
    Toast.makeText(context, "No internet connection. Please check your network.", Toast.LENGTH_LONG).show()
    return
}
// Proceed with PayPal SDK initialisation
```

### Monitor memory usage

**When to use**: During performance testing, when diagnosing crashes or unresponsiveness, or proactively before loading the PayPal WebView. WebView components can consume significant memory, especially during payment flows.

**What it does**: Calculates the app's current memory usage in megabytes by checking the Java runtime heap. It compares used memory against the maximum available heap size and logs a warning if available memory drops below 50 MB, which could lead to OutOfMemoryError exceptions.

**Integration**: Call this function before creating PayPal components, during payment flow, or when users report performance issues. Consider calling it periodically in development builds. In production, only call when diagnosing specific issues to avoid performance overhead.

**Important notes**:

- Memory values are for the Java heap only, not total app memory.
- Low memory situations (<50 MB available) may cause WebView crashes.
- Consider reducing `maxHeightPercent` or optimising other memory-heavy operations if memory is constrained.



```kotlin
fun checkMemoryStatus() {
    val runtime = Runtime.getRuntime()
    val usedMemory = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024
    val maxMemory = runtime.maxMemory() / 1024 / 1024
    val availableMemory = maxMemory - usedMemory
    
    Log.d("PayPal", "Memory: $usedMemory MB used, $availableMemory MB available")
    
    if (availableMemory < 50) {
        Log.w("PayPal", "Low memory warning")
    }
}

// Usage example:
checkMemoryStatus()
if (Runtime.getRuntime().freeMemory() / 1024 / 1024 < 50) {
    // Consider deferring heavy operations or showing a warning
    Log.w("PayPal", "Low memory condition detected before PayPal component creation")
}
```

### Validate configuration

**When to use**: Before creating a PayPal component to catch configuration errors early. This prevents runtime errors and provides clear feedback about what's wrong with the configuration.

**What it does**: Performs validation checks on `PayPalComponentConfig` to ensure all required fields are present and that types match the expected format. Specifically validates that `renderType` and `fundingSources` are correctly configured, as mismatches between these fields are a common source of errors.

**Integration**: Call this function after building your configuration object but before passing it to the component. In development, consider making this a requirement. In production, you can use it to log warnings or provide fallback configurations.

**Important notes**:

- For `standalone` renderType, `fundingSources` must be a String (e.g., `"paypal"`)
- For `setOfButtons` renderType, `fundingSources` must be a List<String> (e.g., `listOf("paypal", "paylater")`)
- You'll need to define a `ValidationResult` data class or replace it with your own error handling mechanism



```kotlin
// Define the ValidationResult data class
data class ValidationResult(
    val isValid: Boolean,
    val errors: List<String>
)

fun validatePayPalConfig(config: PayPalComponentConfig): ValidationResult {
    val errors = mutableListOf<String>()
    
    if (config.renderType.isNullOrEmpty()) {
        errors.add("renderType is required")
    }
    
    if (config.fundingSources == null) {
        errors.add("fundingSources is required")
    }
    
    if (config.renderType == "standalone" && config.fundingSources !is String) {
        errors.add("fundingSources must be a String for standalone renderType")
    }
    
    if (config.renderType == "setOfButtons" && config.fundingSources !is List<*>) {
        errors.add("fundingSources must be a List<String> for setOfButtons renderType")
    }
    
    return ValidationResult(
        isValid = errors.isEmpty(),
        errors = errors
    )
}

// Usage example:
val config = PayPalComponentConfig(
    renderType = "standalone",
    fundingSources = "paypal"
    // ... other config
)

val validation = validatePayPalConfig(config)
if (!validation.isValid) {
    Log.e("PayPal", "Configuration errors: ${validation.errors.joinToString()}")
    // Show error to user or fix configuration
    return
}
// Proceed with component creation
```

### Handle back press in Compose

**When to use**: In Jetpack Compose screens containing PayPal components where you need to handle the Android back button or gesture navigation. This ensures users can exit the payment flow gracefully and that cleanup happens properly.

**What it does**: Uses Compose's `BackHandler` to intercept back button/gesture events. This allows you to log the cancellation, show a confirmation dialog if needed, trigger the PayPal `onCancel` callback, and navigate away from the payment screen cleanly.

**Integration**: Place the `BackHandler` at the top level of your Composable function containing the PayPal component. It will automatically be enabled when the Composable is active and disabled when it leaves the composition.

**Important notes**:

- `BackHandler` is part of `androidx.activity:activity-compose`.
- The callback triggers before the normal back press behaviour.
- You can optionally show a confirmation dialog before cancelling the payment.
- For Activity-based navigation, override `onBackPressed()` in your Activity instead.



```kotlin
@Composable
fun PayPalScreen(navController: NavController) {
    var showExitDialog by remember { mutableStateOf(false) }
    
    BackHandler {
        Log.d("PayPal", "Back pressed during payment flow")
        // Option 1: Exit immediately
        navController.popBackStack()
        
        // Option 2: Show confirmation dialog
        // showExitDialog = true
    }
    
    // Optional: Confirmation dialog
    if (showExitDialog) {
        AlertDialog(
            onDismissRequest = { showExitDialog = false },
            title = { Text("Cancel Payment?") },
            text = { Text("Are you sure you want to cancel this payment?") },
            confirmButton = {
                TextButton(onClick = {
                    showExitDialog = false
                    navController.popBackStack()
                }) {
                    Text("Yes, Cancel")
                }
            },
            dismissButton = {
                TextButton(onClick = { showExitDialog = false }) {
                    Text("Continue Payment")
                }
            }
        )
    }
    
    // PayPal component rendering
}
```

### Prompt WebView update

**When to use**: After detecting that WebView is not available, outdated, or corrupted using the `checkWebViewAvailability()` function. This provides a user-friendly way to resolve WebView issues rather than showing a generic error.

**What it does**: Displays an AlertDialog prompting the user to update Android System WebView. When the user taps "Update", it opens the Google Play Store directly to the WebView app page using a market:// URI scheme. This provides a seamless experience for resolving WebView-related issues.

**Integration**: Call this function when `checkWebViewAvailability()` returns false or when WebView initialisation fails. This is typically in your Activity's `onCreate()` or in a utility function that validates prerequisites before showing the payment UI.

**Important notes**:

- The `market://` scheme will open the Play Store app if installed.
- If Play Store is not available, wrap in try-catch and show alternative instructions.
- Consider adding a "Cancel" button to let users dismiss the dialog.
- Test on devices with WebView disabled to verify the user experience.
- This only works for devices that support WebView (Android 5.0+).



```kotlin
fun promptWebViewUpdate(context: Context) {
    AlertDialog.Builder(context)
        .setTitle("Update required")
        .setMessage("Please update Android System WebView to use PayPal payments")
        .setPositiveButton("Update") { _, _ ->
            try {
                val intent = Intent(Intent.ACTION_VIEW).apply {
                    data = Uri.parse("market://details?id=com.google.android.webview")
                    // Add flag to avoid adding Play Store to back stack
                    flags = Intent.FLAG_ACTIVITY_NEW_TASK
                }
                context.startActivity(intent)
            } catch (e: ActivityNotFoundException) {
                // Fallback to web browser if Play Store not available
                Log.e("PayPal", "Play Store not available", e)
                val webIntent = Intent(Intent.ACTION_VIEW).apply {
                    data = Uri.parse("https://play.google.com/store/apps/details?id=com.google.android.webview")
                }
                context.startActivity(webIntent)
            }
        }
        .setNegativeButton("Cancel", null)
        .setCancelable(false) // Prevent dismissing without action
        .show()
}

// Usage example:
if (!checkWebViewAvailability(context)) {
    Log.e("PayPal", "WebView not available on device")
    promptWebViewUpdate(context)
    return // Don't proceed with PayPal component creation
}
```

## Still having issues?

If you've tried the solutions above and still experience issues:

1. **Check the logs:** Enable debug mode and review logcat output for detailed error messages.
2. **Verify configuration:** Double-check all required parameters are set correctly.
3. **Test on different devices:** Some issues may be device or Android version specific.
4. **Check PayPal dashboard:** Review transaction history and error logs in the Unity Portal.
5. **Contact support:** Provide logcat output, device information, and steps to reproduce the issue.