Skip to content

Troubleshooting

Debug common issues with PayPal payouts in your Android app.

Overview

This guide helps you diagnose and resolve common issues when implementing PayPal payouts in your Android application.

Common issues

SDK initialisation errors

Issue: SDK fails to initialise

Symptoms:

  • Exception thrown during PxpCheckout.builder().build().
  • App crashes on initialisation.

Causes:

  • Missing or invalid session data.
  • Invalid environment configuration.
  • Missing required transaction data.

Solutions:

try {
    val pxpCheckout = PxpCheckout.builder()
        .withConfig(
            PxpSdkConfig(
                environment = Environment.TEST, // Ensure valid environment
                session = sessionData,          // Verify session data is not null
                ownerId = "Unity",
                ownerType = "MerchantGroup",
                clientId = "your-client-id",
                transactionData = TransactionData(
                    amount = 100.0,             // Must be > 0
                    currency = "USD",           // Must be valid ISO code
                    merchant = "your-merchant-id",
                    entryType = EntryType.Ecom,
                    intent = TransactionIntentData(paypal = IntentType.Payout),
                    merchantTransactionId = UUID.randomUUID().toString(),
                    merchantTransactionDate = { System.currentTimeMillis() }
                ),
                paypalConfig = PaypalConfig(
                    payout = PayoutConfig(
                        proceedPayoutWithSdk = true
                    )
                )
            )
        )
        .withContext(context)  // Ensure context is valid
        .build()
} catch (e: Exception) {
    Log.e("Payout", "SDK initialisation failed", e)
    showError("Failed to initialise payout SDK: ${e.message}")
}

Component creation errors

Issue: Component creation fails

Symptoms:

  • Exception when calling createComponent().
  • Component not rendering.

Causes:

  • Incorrect component type.
  • Invalid configuration.
  • SDK not properly initialised.

Solutions:

// Verify component type
val component = try {
    pxpCheckout.createComponent(
        type = ComponentType.PAYOUT_AMOUNT,  // Ensure correct enum value
        config = PayoutAmountComponentConfig(
            label = "Withdrawal Amount"
        )
    )
} catch (e: Exception) {
    Log.e("Payout", "Component creation failed", e)
    null
}

if (component == null) {
    showError("Failed to create payout component")
    return
}

Payout submission errors

PO04: Invalid recipient information

Cause: PayPal wallet email or payer ID not provided or invalid for withdrawal flow.

Solution:

// Ensure both email and payerId are provided
paypalConfig = PaypalConfig(
    payout = PayoutConfig(
        proceedPayoutWithSdk = true,
        paypalWallet = PayPalPayOutWalletConfig(
            email = "customer@example.com",  // Required
            payerId = "PAYER_ID_XXX"         // Required
        )
    )
)

PO02/PO03/PO07: Invalid transaction data

Causes:

  • Amount is invalid (PO02).
  • Currency is invalid (PO03).
  • Transaction date is invalid (PO07).

Solution:

// Validate amount before initialisation
fun validateAmount(amount: Double?): Boolean {
    return when {
        amount == null -> {
            showError("Amount is required")
            false
        }
        amount <= 0 -> {
            showError("Amount must be greater than zero")
            false
        }
        amount.isNaN() || amount.isInfinite() -> {
            showError("Amount must be a valid number")
            false
        }
        else -> true
    }
}

if (!validateAmount(payoutAmount)) {
    return  // Don't proceed with invalid amount
}

transactionData = TransactionData(
    amount = payoutAmount,  // Validated amount
    // ... other properties
)

Network errors

Issue: Network timeout or connection error

Symptoms:

  • Payout fails with network-related error.
  • Long delay before error callback.

Solutions:

  1. Check internet connectivity:
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)
}

// Check before payout
if (!isNetworkAvailable(context)) {
    showError("No internet connection. Please check your network and try again.")
    return
}
  1. Implement retry logic:
var retryCount = 0
val MAX_RETRIES = 3

onError = { error ->
    val isNetworkError = error.errorCode == "NETWORK_ERROR" || 
                         error.errorReason?.contains("network", ignoreCase = true) == true
    
    if (isNetworkError && retryCount < MAX_RETRIES) {
        retryCount++
        Handler(Looper.getMainLooper()).postDelayed({
            showMessage("Retrying... (Attempt $retryCount of $MAX_RETRIES)")
        }, 2000L * retryCount)
    } else {
        retryCount = 0
        showError("Network error. Please try again later.")
    }
}

UI rendering issues

Issue: Component not visible

Causes:

  • Component not properly rendered in Compose.
  • Modifier constraints hiding component.
  • Component not created successfully.

Solutions:

  1. Verify component creation:
val component = remember {
    pxpCheckout.createComponent(type, config)
}

// Check if component is not null
if (component != null) {
    pxpCheckout.buildComponentView(
        component = component,
        modifier = Modifier.fillMaxWidth()
    )
} else {
    Text("Failed to load component")
}
  1. Check Compose hierarchy:
@Composable
fun PayoutScreen() {
    Surface(modifier = Modifier.fillMaxSize()) {  // Ensure parent has size
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp)
        ) {
            pxpCheckout.buildComponentView(
                component = component,
                modifier = Modifier
                    .fillMaxWidth()
                    .wrapContentHeight()  // Allow component to determine height
            )
        }
    }
}
  1. Debug with visibility logging:
DisposableEffect(component) {
    Log.d("Payout", "Component mounted: ${component.componentType}")
    onDispose {
        Log.d("Payout", "Component disposed: ${component.componentType}")
    }
}

Callback not triggered

Issue: Callbacks not being called

Causes:

  • Callback not properly defined in config.
  • Component not properly initialised.
  • Error occurred before callback could be triggered.

Solutions:

  1. Verify callback syntax:
// Correct callback syntax
val config = PayoutSubmissionComponentConfig(
    recipientWallet = "Paypal",
    onPrePayoutSubmit = {  // Lambda with no parameters
        PrePayoutSubmitResult(isApproved = true)
    },
    onPostPayout = { result ->  // Lambda with parameter
        handleSuccess(result)
    }
)
  1. Add logging to all callbacks:
val config = PayoutSubmissionComponentConfig(
    recipientWallet = "Paypal",
    
    onClick = {
        Log.d("Payout", "onClick triggered")
    },
    
    onPrePayoutSubmit = {
        Log.d("Payout", "onPrePayoutSubmit triggered")
        PrePayoutSubmitResult(isApproved = true)
    },
    
    onPostPayout = { result ->
        Log.d("Payout", "onPostPayout triggered: ${result.merchantTransactionId}")
    },
    
    onError = { error ->
        Log.e("Payout", "onError triggered: ${error.errorCode}")
    }
)

Debugging tips

Enable verbose logging

// Add logging throughout your payout flow
class PayoutActivity : ComponentActivity() {
    
    private val TAG = "PayoutDebug"
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(TAG, "Activity created")
        
        try {
            Log.d(TAG, "Initialising SDK")
            val pxpCheckout = initializeSDK()
            Log.d(TAG, "SDK initialised successfully")
            
            Log.d(TAG, "Creating components")
            val components = createComponents(pxpCheckout)
            Log.d(TAG, "Components created: ${components.size}")
            
            setContent {
                MaterialTheme {
                    PayoutScreen(components)
                }
            }
        } catch (e: Exception) {
            Log.e(TAG, "Error during initialisation", e)
            showError("Initialisation failed: ${e.message}")
        }
    }
}

Inspect error details

onError = { error ->
    // Log all available error information
    Log.e("Payout", """
        ====== Payout Error ======
        Error Code: ${error.errorCode}
        Error Reason: ${error.errorReason}
        HTTP Status: ${error.httpStatusCode}
        Correlation ID: ${error.correlationId}
        Details: ${error.details}
        Stack Trace: ${Log.getStackTraceString(Exception())}
        ==========================
    """.trimIndent())
    
    // Send to crash reporting service
    reportError(error)
}

Test with different scenarios

// Test with various amounts
testPayouts(
    amounts = listOf(0.01, 1.0, 100.0, 9999.99),
    currencies = listOf("USD", "EUR", "GBP")
)

// Test error scenarios
testErrorScenarios(
    scenarios = listOf(
        "zero_amount",
        "invalid_currency",
        "missing_wallet",
        "network_failure"
    )
)