Skip to content

Troubleshooting

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

Common issues and solutions

IssueDescriptionNext steps
PayPal SDK not loadingLogcat 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 renderingThe 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 errorsYou 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 issuesCertain 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 freezesWebView 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 availableThe 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 connectivityThe 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 errorsWebView 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 issuesThe 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 workingThe 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 rotationThe 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 parametersOne-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 disposingThere 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 firingonSuccess, 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 failsThe 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 failsThe 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 issuesDebug 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 appliedThe 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 incorrectlyButtons 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 issuesThe 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 workingThe 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 Boolean
  • Test consent state changes in logcat.
Toggle component issuesThe 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 issuesThe 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

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

Enable WebView debugging

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

Logcat filters

Use these filters to debug PayPal issues:

# 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

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.

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.

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.
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
// 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.
@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+).
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.