Understand error types, handle payment failures, and implement proper error recovery.
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.
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)
}
)
)| Property | Description |
|---|---|
messageString | Human-readable error message describing what went wrong. |
codeString | SDK error code in format SDK#### (e.g., 'SDK1113' for authentication failed, 'SDK0500' for network error). Use this for programmatic error handling. |
Errors are organised into four main categories based on their source and nature.
Errors that occur during SDK initialisation or component rendering due to invalid configuration.
| Exception | Error code | Description | Solution |
|---|---|---|---|
CheckoutDropInConfigNotFoundSdkException | SDK1100 | Drop-in configuration not found or invalid. | Verify initialisation parameters. |
CheckoutDropInFailedToRenderCardSdkException | SDK1101 | Failed to render card component. | Check session configuration for card support. |
CheckoutDropInFailedToRenderPaypalSdkException | SDK1102 | Failed to render PayPal button. | Verify PayPal is enabled in session. |
CheckoutDropInFailedToRenderGooglePaySdkException | SDK1103 | Failed to render Google Pay button. | Check Google Pay configuration. |
CheckoutDropInInvalidPaypalEntryTypeSdkException | SDK1118 | PayPal 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."
)
}
}Errors that occur during payment processing for specific payment methods.
| Exception | Error code | Description | User action |
|---|---|---|---|
CheckoutDropInCardPaymentFailedSdkException | SDK1115 | Card payment failed (see detection patterns below). | See card-specific patterns below. |
CheckoutDropInPaypalPaymentFailedSdkException | SDK1116 | PayPal transaction failed. | Try again or use different method. |
CheckoutDropInGooglePayPaymentFailedSdkException | SDK1117 | Google Pay transaction failed. | Try again or use different method. |
Since card errors come from providers with varying message formats, use message-based detection:
| Scenario | Detection approach | User action |
|---|---|---|
| Card declined | error.message contains "declined" | Try a different card or contact bank. |
| Insufficient funds | error.message contains "insufficient funds" | Use a different payment method. |
| Expired card | error.message contains "expired" | Use a different card. |
| Invalid CVV | error.message contains "CVV" or "security code" | Check security code and retry. |
| Invalid card number | error.message contains "card number" or "invalid number" | Check card number and retry. |
| Invalid expiry | error.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()
}
}Errors related to 3D Secure (3DS) authentication for card payments.
| Exception | Error code | Description | User action |
|---|---|---|---|
CheckoutDropInAuthenticationFailedSdkException | SDK1113 | 3DS 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.messagecontains "timeout" - Cancelled:
error.messagecontains "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()
))
}
}
}
}Errors that occur during payment authorisation.
| Exception | Error code | Description | Solution |
|---|---|---|---|
CheckoutDropInAuthorisationFailedSdkException | SDK1114 | Payment authorisation failed. | Check transaction details and retry. |
Errors related to network connectivity, session state, and system availability.
| Error code | Detection approach | User action |
|---|---|---|
SDK0500 | Network error code | Check connection and retry. |
| Message-based | error.message contains "session" or "expired" | Refresh page and retry. |
| Message-based | error.message contains "timeout" | Check connection and retry. |
| Message-based | error.message contains "unavailable" or "service" | Try again later. |
SDK01XX or SDK02XX | Configuration 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
))
}
}
}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()
}
)
)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()
}
)
)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)
}
)
)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.