Skip to content

Error handling

Understand error types, handle payment failures, and implement proper error recovery.

Overview

Drop-in provides comprehensive error handling through the onError callback. All payment failures, validation errors, and SDK exceptions are caught and delivered through this unified interface, making error handling consistent across all payment methods.

Error callback

All errors are delivered through the onError callback:

import com.pxp.checkout.checkoutdropin.CheckoutDropIn
import com.pxp.checkout.exceptions.BaseSdkException

val checkoutDropIn = CheckoutDropIn.initialize(
    context = context,
    config = CheckoutDropInConfig(
        // ... other config
        onError = { error ->
            Log.e("CheckoutDropIn", "Payment failed: ${error.message}")
            Log.e("CheckoutDropIn", "Error code: ${error.code}")
            
            // Handle the error
            showErrorMessage(error.message)
        }
    )
)

Error object structure

PropertyDescription
message
String
Human-readable error message describing what went wrong.
code
String
SDK error code in format SDK#### (e.g., 'SDK1113' for authentication failed, 'SDK0500' for network error). Use this for programmatic error handling.

Error categories

Errors are organised into four main categories based on their source and nature.

Configuration errors

Errors that occur during SDK initialisation or component rendering due to invalid configuration.

ExceptionError codeDescriptionSolution
CheckoutDropInConfigNotFoundSdkExceptionSDK1100Drop-in configuration not found or invalid.Verify initialisation parameters.
CheckoutDropInFailedToRenderCardSdkExceptionSDK1101Failed to render card component.Check session configuration for card support.
CheckoutDropInFailedToRenderPaypalSdkExceptionSDK1102Failed to render PayPal button.Verify PayPal is enabled in session.
CheckoutDropInFailedToRenderGooglePaySdkExceptionSDK1103Failed to render Google Pay button.Check Google Pay configuration.
CheckoutDropInInvalidPaypalEntryTypeSdkExceptionSDK1118PayPal only supports Ecom entry type.Change entryType to EntryType.Ecom.

Example handling:

onError = { error ->
    if (error.code.startsWith("SDK110")) {
        Log.e("CheckoutDropIn", "Rendering error: ${error.message}")
        
        // Log to monitoring
        logger.error("Rendering error", mapOf(
            "error_code" to error.code,
            "message" to error.message
        ))
        
        // Show generic error to user
        showErrorMessage(
            "We're having trouble loading payment options. " +
            "Please refresh the page or contact support."
        )
    }
}

Payment errors

Errors that occur during payment processing for specific payment methods.

ExceptionError codeDescriptionUser action
CheckoutDropInCardPaymentFailedSdkExceptionSDK1115Card payment failed (see detection patterns below).See card-specific patterns below.
CheckoutDropInPaypalPaymentFailedSdkExceptionSDK1116PayPal transaction failed.Try again or use different method.
CheckoutDropInGooglePayPaymentFailedSdkExceptionSDK1117Google Pay transaction failed.Try again or use different method.

Card-specific error detection patterns

Since card errors come from providers with varying message formats, use message-based detection:

ScenarioDetection approachUser action
Card declinederror.message contains "declined"Try a different card or contact bank.
Insufficient fundserror.message contains "insufficient funds"Use a different payment method.
Expired carderror.message contains "expired"Use a different card.
Invalid CVVerror.message contains "CVV" or "security code"Check security code and retry.
Invalid card numbererror.message contains "card number" or "invalid number"Check card number and retry.
Invalid expiryerror.message contains "expiry" or "expiration"Check expiry date and retry.

Example handling:

onError = { error ->
    // Card-specific errors (message-based detection)
    if (error.code == "SDK1115") {
        // Card payment failed - check message for specifics
        when {
            error.message.contains("declined", ignoreCase = true) -> {
                showErrorMessage(
                    "Your card was declined. Please try a different card or " +
                    "contact your bank for more information."
                )
                offerAlternativePaymentMethods()
            }
            error.message.contains("insufficient funds", ignoreCase = true) -> {
                showErrorMessage(
                    "Insufficient funds. Please use a different payment method."
                )
                offerAlternativePaymentMethods()
            }
            error.message.contains("expired", ignoreCase = true) -> {
                showErrorMessage(
                    "This card has expired. Please use a different card."
                )
            }
            error.message.contains("cvv", ignoreCase = true) || 
            error.message.contains("security code", ignoreCase = true) -> {
                showErrorMessage(
                    "Invalid security code. Please check the CVV on the back of " +
                    "your card and try again."
                )
                // Keep same payment method selected for retry
            }
            else -> {
                showErrorMessage(
                    "Card payment failed. Please check your details and try again."
                )
            }
        }
    }
    
    // Wallet payment errors
    else if (error.code == "SDK1116") {
        showErrorMessage(
            "PayPal payment failed. Please try again or use a different payment method."
        )
        offerAlternativePaymentMethods()
    } else if (error.code == "SDK1117") {
        showErrorMessage(
            "Google Pay payment failed. Please try again or use a different payment method."
        )
        offerAlternativePaymentMethods()
    }
}

Authentication errors

Errors related to 3D Secure (3DS) authentication for card payments.

ExceptionError codeDescriptionUser action
CheckoutDropInAuthenticationFailedSdkExceptionSDK11133DS authentication failed.Try again or use different card.

Authentication timeout and cancellation are also reported through error.message. Check the message content for these scenarios:

  • Timeout: error.message contains "timeout"
  • Cancelled: error.message contains "cancel"

Example handling:

onError = { error ->
    if (error.code == "SDK1113" || 
        error.message.contains("authentication", ignoreCase = true)) {
        when {
            error.message.contains("timeout", ignoreCase = true) -> {
                showErrorMessage(
                    "3D Secure authentication timed out. Please check your " +
                    "internet connection and try again."
                )
                
                // Offer retry
                showRetryButton()
            }
            error.message.contains("cancel", ignoreCase = true) -> {
                showErrorMessage(
                    "Authentication was cancelled. Please try again to complete " +
                    "your payment."
                )
                
                // Don't show error as prominently - user intentionally cancelled
                showInfoMessage("You can retry your payment when ready.")
            }
            else -> {
                showErrorMessage(
                    "3D Secure authentication failed. Please try again or use a " +
                    "different card."
                )
                
                // Track authentication failures
                analytics.track("3ds_authentication_failed", mapOf(
                    "timestamp" to System.currentTimeMillis()
                ))
            }
        }
    }
}

Authorisation errors

Errors that occur during payment authorisation.

ExceptionError codeDescriptionSolution
CheckoutDropInAuthorisationFailedSdkExceptionSDK1114Payment authorisation failed.Check transaction details and retry.

System errors

Errors related to network connectivity, session state, and system availability.

Error codeDetection approachUser action
SDK0500Network error codeCheck connection and retry.
Message-basederror.message contains "session" or "expired"Refresh page and retry.
Message-basederror.message contains "timeout"Check connection and retry.
Message-basederror.message contains "unavailable" or "service"Try again later.
SDK01XX or SDK02XXConfiguration error codes (01XX = SDK, 02XX = Component)Contact support.

Example handling:

onError = { error ->
    // Network and session errors
    when {
        error.code == "SDK0500" -> {
            showErrorMessage(
                "Network connection issue. Please check your internet connection " +
                "and try again."
            )
            showRetryButton()
        }
        error.message.contains("session", ignoreCase = true) || 
        error.message.contains("expired", ignoreCase = true) -> {
            showErrorMessage(
                "Your payment session has expired. Please refresh the page and " +
                "try again."
            )
            showRefreshButton()
        }
        error.message.contains("timeout", ignoreCase = true) -> {
            showErrorMessage(
                "Request timed out. Please check your connection and try again."
            )
            showRetryButton()
        }
        error.message.contains("unavailable", ignoreCase = true) || 
        error.message.contains("service", ignoreCase = true) -> {
            showErrorMessage(
                "Payment service is temporarily unavailable. Please try again in " +
                "a few minutes."
            )
        }
        error.code.startsWith("SDK01") || error.code.startsWith("SDK02") -> {
            showErrorMessage(
                "Payment configuration error. Please contact support for assistance."
            )
            
            // Log critical error
            logger.critical("Configuration error", mapOf(
                "error_code" to error.code,
                "message" to error.message
            ))
        }
    }
}

Error handling patterns

Basic error handling

The simplest error handling pattern shows user-friendly messages and logs errors for debugging.

import com.pxp.checkout.exceptions.BaseSdkException

val checkoutDropIn = CheckoutDropIn.initialize(
    context = context,
    config = CheckoutDropInConfig(
        // ... other config
        onError = { error ->
            // Log error for debugging
            Log.e("CheckoutDropIn", "Payment error: ${error.code} - ${error.message}")
            
            // Show user-friendly message
            showErrorNotification(error.message ?: "Payment failed. Please try again.")
            
            // Re-enable payment button
            enablePaymentButton()
        }
    )
)

Advanced error handling with recovery

Implement retry logic, alternative payment methods, and error categorisation.

var retryCount = 0
const val MAX_RETRIES = 3

val checkoutDropIn = CheckoutDropIn.initialize(
    context = context,
    config = CheckoutDropInConfig(
        // ... other config
        onError = { error ->
            // Log to monitoring service
            logErrorToMonitoring(mapOf(
                "category" to "payment_error",
                "error_code" to error.code,
                "message" to error.message,
                "user_agent" to Build.MODEL,
                "timestamp" to System.currentTimeMillis()
            ))
            
            // Track analytics
            analytics.track("payment_failed", mapOf(
                "error_code" to error.code,
                "error_message" to error.message,
                "retry_count" to retryCount
            ))
            
            // Check if error is retryable (network issues, timeouts)
            val isNetworkError = error.code == "SDK0500"
            val isTimeout = error.message.contains("timeout", ignoreCase = true)
            val isRetryable = isNetworkError || isTimeout || 
                             error.message.contains("unavailable", ignoreCase = true)
            
            // Implement retry logic
            if (isRetryable && retryCount < MAX_RETRIES) {
                retryCount++
                
                showWarningMessage(
                    "Connection issue (attempt $retryCount/$MAX_RETRIES). " +
                    "Please try your payment again."
                ) {
                    // Retry the payment
                    retryPayment()
                }
                return@CheckoutDropInConfig
            }
            
            // Reset retry count for non-retryable errors
            retryCount = 0
            
            // Show user-friendly error messages based on error code and message
            val userMessage = when {
                // Check by SDK error code
                error.code == "SDK0500" ->
                    "Network connection issue. Please check your internet connection and try again."
                error.code == "SDK1113" ->
                    "3D Secure authentication failed. Please try again or use a different card."
                error.code == "SDK1114" ->
                    "Payment authorisation failed. Please check your details and try again."
                error.code == "SDK1115" -> {
                    // Card payment failed - check message for specifics
                    when {
                        error.message.contains("declined", ignoreCase = true) ->
                            "Your card was declined. Please try a different card or contact your bank."
                        error.message.contains("insufficient funds", ignoreCase = true) ->
                            "Insufficient funds. Please use a different payment method."
                        error.message.contains("expired", ignoreCase = true) ->
                            "This card has expired. Please use a different card."
                        error.message.contains("cvv", ignoreCase = true) || 
                        error.message.contains("security code", ignoreCase = true) ->
                            "Invalid security code. Please check the CVV on your card and try again."
                        error.message.contains("card number", ignoreCase = true) ->
                            "Invalid card number. Please check and try again."
                        error.message.contains("expiry", ignoreCase = true) || 
                        error.message.contains("expiration", ignoreCase = true) ->
                            "Invalid expiry date. Please check and try again."
                        else ->
                            "Card payment failed. Please check your details and try again."
                    }
                }
                error.code == "SDK1116" ->
                    "PayPal payment failed. Please try again or use a different payment method."
                error.code == "SDK1117" ->
                    "Google Pay payment failed. Please try again or use a different payment method."
                // Check by message content for scenarios without specific codes
                error.message.contains("timeout", ignoreCase = true) ->
                    "Request timed out. Please check your internet connection and try again."
                error.message.contains("session", ignoreCase = true) || 
                error.message.contains("expired", ignoreCase = true) ->
                    "Your payment session has expired. Please refresh the page."
                error.message.contains("unavailable", ignoreCase = true) ->
                    "Payment service temporarily unavailable. Please try again in a few minutes."
                error.code.startsWith("SDK01") || error.code.startsWith("SDK02") ->
                    "Payment configuration error. Please contact support."
                else -> error.message
            }
            
            showErrorMessage(userMessage)
            
            // Offer alternative payment methods for certain errors
            val shouldOfferAlternatives = 
                error.code == "SDK1115" || // Card payment failed
                error.code == "SDK1116" || // PayPal failed
                error.code == "SDK1117" || // Google Pay failed
                error.message.contains("declined", ignoreCase = true) ||
                error.message.contains("insufficient funds", ignoreCase = true)
            
            if (shouldOfferAlternatives) {
                showAlternativePaymentMethods()
            }
            
            // Show retry button for network/timeout errors
            val shouldShowRetry = 
                error.code == "SDK0500" ||
                error.message.contains("timeout", ignoreCase = true) ||
                error.message.contains("unavailable", ignoreCase = true)
            
            if (shouldShowRetry) {
                showRetryButton()
            }
            
            // Show refresh button for session errors
            if (error.message.contains("session", ignoreCase = true) || 
                error.message.contains("expired", ignoreCase = true)) {
                showRefreshButton()
            }
            
            // Re-enable payment form
            enablePaymentForm()
        }
    )
)

Error logging to monitoring service

Integrate with monitoring services like Firebase Crashlytics or custom logging.

import com.google.firebase.crashlytics.FirebaseCrashlytics

val checkoutDropIn = CheckoutDropIn.initialize(
    context = context,
    config = CheckoutDropInConfig(
        // ... other config
        onError = { error ->
            // Log to Firebase Crashlytics
            FirebaseCrashlytics.getInstance().apply {
                setCustomKey("error_code", error.code)
                setCustomKey("error_message", error.message)
                recordException(Exception("Payment error: ${error.code} - ${error.message}"))
            }
            
            // Log to custom monitoring service
            lifecycleScope.launch {
                try {
                    logErrorToBackend(mapOf(
                        "type" to "payment_error",
                        "error_code" to error.code,
                        "message" to error.message,
                        "url" to currentActivity,
                        "device_model" to Build.MODEL,
                        "os_version" to Build.VERSION.RELEASE,
                        "timestamp" to System.currentTimeMillis()
                    ))
                } catch (e: Exception) {
                    // Silently fail - don't disrupt user experience
                    Log.e("CheckoutDropIn", "Failed to log error", e)
                }
            }
            
            // Show error to user
            showErrorMessage(error.message)
        }
    )
)

Complete error handling example

Here's a production-ready error handling implementation:

import com.pxp.checkout.checkoutdropin.CheckoutDropIn
import com.pxp.checkout.exceptions.BaseSdkException
import com.google.firebase.crashlytics.FirebaseCrashlytics

var retryCount = 0
const val MAX_RETRIES = 3

val checkoutDropIn = CheckoutDropIn.initialize(
    context = context,
    config = CheckoutDropInConfig(
        // ... other config
        onError = { error ->
            // 1. Log error for debugging
            Log.e("CheckoutDropIn", "Payment error: ${error.code} - ${error.message}")
            
            // 2. Send to monitoring service
            FirebaseCrashlytics.getInstance().apply {
                setCustomKey("error_code", error.code)
                setCustomKey("error_message", error.message)
                setCustomKey("retry_count", retryCount)
                recordException(Exception("Payment error: ${error.code}"))
            }
            
            // 3. Track analytics
            analytics.track("payment_failed", mapOf(
                "error_code" to error.code,
                "error_message" to error.message,
                "retry_count" to retryCount,
                "timestamp" to System.currentTimeMillis()
            ))
            
            // 4. Clear any loading state
            hideLoadingOverlay()
            
            // 5. Implement retry logic for transient errors
            val isNetworkError = error.code == "SDK0500"
            val isTimeout = error.message.contains("timeout", ignoreCase = true)
            val isUnavailable = error.message.contains("unavailable", ignoreCase = true)
            
            if ((isNetworkError || isTimeout || isUnavailable) && retryCount < MAX_RETRIES) {
                retryCount++
                
                showNotification(
                    type = NotificationType.WARNING,
                    message = "Connection issue (attempt $retryCount/$MAX_RETRIES). " +
                             "Please try again."
                ) {
                    retryPayment()
                }
                return@CheckoutDropInConfig
            }
            
            // Reset retry count
            retryCount = 0
            
            // 6. Show user-friendly error messages
            val userMessage = when {
                // Check by SDK error code
                error.code == "SDK0500" ->
                    "Network error. Please check your connection and try again."
                error.code == "SDK1113" ->
                    "3D Secure authentication failed. Please try again."
                error.code == "SDK1114" ->
                    "Payment authorisation failed. Please check your details and try again."
                error.code == "SDK1115" -> {
                    // Card payment failed - check message
                    when {
                        error.message.contains("declined", ignoreCase = true) ->
                            "Your card was declined. Please try a different card or contact your bank."
                        error.message.contains("insufficient funds", ignoreCase = true) ->
                            "Insufficient funds. Please use a different payment method."
                        error.message.contains("expired", ignoreCase = true) ->
                            "This card has expired. Please use a different card."
                        error.message.contains("cvv", ignoreCase = true) ->
                            "Invalid security code. Please check and try again."
                        else ->
                            "Card payment failed. Please check your details and try again."
                    }
                }
                error.code == "SDK1116" ->
                    "PayPal payment failed. Please try again or use a different method."
                error.code == "SDK1117" ->
                    "Google Pay payment failed. Please try again or use a different method."
                error.message.contains("session", ignoreCase = true) || 
                error.message.contains("expired", ignoreCase = true) ->
                    "Session expired. Please refresh the page."
                error.code.startsWith("SDK01") || error.code.startsWith("SDK02") ->
                    "Configuration error. Please contact support."
                else -> error.message
            }
            
            // 7. Show error to user
            showNotification(
                type = NotificationType.ERROR,
                title = "Payment failed",
                message = userMessage,
                duration = 8000
            )
            
            // 8. Offer recovery options
            val shouldOfferAlternatives = 
                error.code == "SDK1115" || 
                error.code == "SDK1116" || 
                error.code == "SDK1117" ||
                error.message.contains("declined", ignoreCase = true) ||
                error.message.contains("insufficient funds", ignoreCase = true)
            
            if (shouldOfferAlternatives) {
                showAlternativePaymentMethods()
            }
            
            val shouldShowRetry = 
                error.code == "SDK0500" ||
                error.message.contains("timeout", ignoreCase = true)
            
            if (shouldShowRetry) {
                showRetryButton()
            }
            
            if (error.message.contains("session", ignoreCase = true) || 
                error.message.contains("expired", ignoreCase = true)) {
                showRefreshButton()
            }
            
            // 9. Re-enable UI
            enablePaymentForm()
            enableSubmitButton()
        }
    )
)

For specific error codes and exceptions related to individual payment methods, see the payment method documentation: Card, PayPal, Google Pay.