Implement callbacks to customise your PayPal payment flow for Android.
Components emit events based on user interaction or validation. You can use these to implement callback functions, which allow you to inject your own business logic and user experience customisations into the payment flow at critical moments. They ensure that while the SDK handles the complex technical aspects of payment processing, you retain full control over the customer experience and can seamlessly integrate payments into your broader business workflows and systems.
Callbacks enable you to:
- Validate business rules before payments proceed.
- Display custom error, failure, or success messages.
- Tailor user interfaces to match your brand's look and feel.
- Integrate with your own systems for fraud detection or customer management.
- Control exactly how your customers experience both successful and failed transactions.
- Handle PayPal-specific requirements like shipping calculations and address validation.
- Manage different funding sources (PayPal and Pay Later).
- Customise the checkout experience based on customer preferences.
All events are optional and can be mixed and matched based on your business needs.
The following table lists all events supported by the different PayPal components.
| Event | PayPal component | Pre-built component | Consent component | Toggle component |
|---|---|---|---|---|
onSuccess | ||||
onError | ||||
onCancel | ||||
onShippingAddressChange | ||||
onShippingOptionsChange | ||||
onOrderCreated | ||||
onSubmitError | ||||
onScriptLoaded | ||||
onGetConsent | ||||
onToggleChanged | ||||
onLinkClick | ||||
onTriggerFieldValidation |
This callback is triggered when the buyer approves the payment in the PayPal checkout flow. This is where you capture the funds and complete the transaction.
You can use it to:
- Process the approved payment and complete the transaction.
- Update inventory and order status in your system.
- Send order confirmation emails or push notifications to customers.
- Record successful transactions for business intelligence.
- Navigate to a success screen in your app.
| Event data | Description |
|---|---|
dataPaymentData | The payment approval data object from PayPal. |
data.orderIDString | The unique PayPal order identifier for this transaction. |
data.payerIDString | The PayPal payer identifier for the customer who approved the payment. |
data.paymentIDString? | The PayPal payment identifier (optional, may be null). |
data.billingTokenString? | The billing agreement token for vaulting (optional, may be null for one-time payments). |
data.facilitatorAccessTokenString | The access token for the facilitator to complete the transaction. |
This callback must return a string.
import org.json.JSONObject
import android.util.Log
val paypalConfig = PayPalComponentConfig(
onSuccess = { data ->
Log.d("PayPal", "Payment approved: $data")
try {
// Parse the payment data
val jsonObject = JSONObject(data)
val orderID = jsonObject.getString("orderID")
val payerID = jsonObject.getString("payerID")
val paymentID = jsonObject.optString("paymentID", null)
// Update your system with the successful payment
updateOrderStatus(
orderId = orderID,
status = "completed",
paymentMethod = "paypal"
)
// Update inventory
updateInventory(orderID)
// Send confirmation notification
sendPushNotification(
title = "Payment Successful",
body = "Your order $orderID has been confirmed"
)
// Track successful payment
analytics.track("paypal_payment_completed", mapOf(
"orderId" to orderID,
"payerID" to payerID,
"timestamp" to System.currentTimeMillis()
))
// Navigate to success screen
navController.navigate("payment_success/$orderID")
// Return success status
return@PayPalComponentConfig "success"
} catch (e: Exception) {
Log.e("PayPal", "Payment processing error", e)
showErrorMessage("Payment processing failed. Please contact support.")
return@PayPalComponentConfig "error"
}
}
)This callback is triggered when the buyer cancels the payment flow in the PayPal popup or navigates back without completing the payment.
You can use it to:
- Track cancellation rates for conversion optimisation.
- Show helpful messages or alternative payment options.
- Save the customer's cart for later completion.
- Trigger email campaigns for abandoned checkouts.
This callback receives no parameters.
val paypalConfig = PayPalComponentConfig(
onCancel = {
Log.d("PayPal", "Payment cancelled by user")
// Track cancellation for analytics
analytics.track("paypal_payment_cancelled", mapOf(
"timestamp" to System.currentTimeMillis(),
"cartValue" to getCurrentCartValue(),
"stage" to "paypal_checkout"
))
// Preserve cart for later
saveCartForLater()
// Show helpful message
showSnackbar(
message = "No worries! Your items are saved. You can complete your purchase anytime.",
duration = SnackbarDuration.Long
)
// Offer alternatives after a short delay
viewModelScope.launch {
delay(2000)
showAlternativePaymentOptions()
}
// Optional: Schedule abandoned cart notification
scheduleAbandonedCartNotification(30) // 30 minutes delay
}
)This callback is triggered when an error occurs during the PayPal payment process, such as network issues, invalid configuration, or payment processing failures.
You can use it to:
- Log errors for debugging and monitoring.
- Display user-friendly error messages.
- Offer alternative payment methods.
- Implement automatic retry for transient errors.
| Event data | Description |
|---|---|
errorString | The error message or JSON containing details about what went wrong. |
val paypalConfig = PayPalComponentConfig(
onError = { error ->
Log.e("PayPal", "Payment error: $error")
// Log error for debugging
crashlytics.log("PayPal payment error: $error")
crashlytics.recordException(Exception("PayPal Error: $error"))
// Handle different error types
when {
error.contains("INSTRUMENT_DECLINED") -> {
showErrorDialog(
title = "Payment Declined",
message = "Your PayPal payment was declined. Please try a different payment method or contact PayPal support."
)
}
error.contains("PAYER_ACTION_REQUIRED") -> {
showErrorDialog(
title = "Action Required",
message = "Additional action required in PayPal. Please try again or complete any pending actions in your PayPal account."
)
}
error.contains("UNPROCESSABLE_ENTITY") -> {
showErrorDialog(
title = "Processing Error",
message = "There was an issue processing your payment. Please check your payment details and try again."
)
}
error.contains("Network") || error.contains("timeout") -> {
showErrorDialog(
title = "Connection Error",
message = "Network error. Please check your connection and try again.",
action = "Retry" to { retryPayment() }
)
}
else -> {
showErrorDialog(
title = "Payment Error",
message = "Payment failed. Please try again or use a different payment method."
)
}
}
// Show alternative payment methods
showAlternativePaymentMethods()
// Track error metrics
analytics.track("paypal_error", mapOf(
"errorMessage" to error,
"timestamp" to System.currentTimeMillis()
))
}
)This callback is triggered when the PayPal order is successfully created but before the customer approves the payment.
You can use it to:
- Store the order ID for tracking purposes.
- Update order status in your system.
- Log order creation events for analytics.
- Validate order creation success.
| Event data | Description |
|---|---|
dataOrderCreatedData | The order creation response object containing transaction details. |
data.providerTransactionIdString | The PayPal order ID assigned by PayPal for this transaction. |
data.transactionIdString | The PXP transaction ID for tracking within the PXP system. |
data.correlationIdString | The correlation ID for tracking the transaction across systems. |
This callback must return a string.
val paypalConfig = PayPalComponentConfig(
onOrderCreated = { data ->
Log.d("PayPal", "Order created successfully: $data")
try {
val jsonObject = JSONObject(data)
val providerTransactionId = jsonObject.getString("providerTransactionId")
val transactionId = jsonObject.getString("transactionId")
val correlationId = jsonObject.optString("correlationId", "")
// Store order information
storeOrderInformation(
paypalOrderId = providerTransactionId,
pxpTransactionId = transactionId,
correlationId = correlationId,
status = "created",
createdAt = System.currentTimeMillis()
)
// Update UI to show order created state
updateOrderStatus("Order created, awaiting payment approval...")
// Track order creation
analytics.track("paypal_order_created", mapOf(
"paypalOrderId" to providerTransactionId,
"pxpTransactionId" to transactionId,
"timestamp" to System.currentTimeMillis()
))
// Optional: Start order timeout timer
startOrderTimeoutTimer(providerTransactionId, 15) // 15 minute timeout
// Return order created status
return@PayPalComponentConfig "order_created"
} catch (e: Exception) {
Log.e("PayPal", "Failed to parse order creation data", e)
return@PayPalComponentConfig "error"
}
}
)This callback is triggered when the customer changes their shipping address in the PayPal checkout flow. Use this to calculate shipping costs and validate delivery availability.
You can use it to:
- Calculate shipping costs based on the new address.
- Validate if delivery is available to the location.
- Update tax rates based on the shipping destination.
- Check shipping restrictions for specific products.
| Event data | Description |
|---|---|
dataShippingAddressData | The shipping address change data object. |
data.shippingAddressObject | The new shipping address selected by the customer. |
data.shippingAddress.recipientNameString | The name of the person receiving the shipment. |
data.shippingAddress.addressLine1String | The first line of the shipping address. |
data.shippingAddress.addressLine2String? | The second line of the shipping address (optional). |
data.shippingAddress.cityString | The city for the shipping address. |
data.shippingAddress.stateString | The state or region for the shipping address. |
data.shippingAddress.postalCodeString | The postal code or ZIP code for the shipping address. |
data.shippingAddress.countryCodeString | The two-letter ISO country code for the shipping address. |
This callback must return a string. Return an empty string to allow the address change, or return a rejection string (e.g., "reject:COUNTRY_ERROR") to reject it.
val paypalConfig = PayPalComponentConfig(
onShippingAddressChange = { data ->
Log.d("PayPal", "Shipping address changed: $data")
try {
val jsonObject = JSONObject(data)
val shippingAddress = jsonObject.optJSONObject("shippingAddress")
if (shippingAddress != null) {
val countryCode = shippingAddress.optString("countryCode", "")
val state = shippingAddress.optString("state", "")
val city = shippingAddress.optString("city", "")
val postalCode = shippingAddress.optString("postalCode", "")
// Validate if we can ship to this location
if (!canShipToCountry(countryCode)) {
return@PayPalComponentConfig "reject:COUNTRY_ERROR"
}
// Check state restrictions
if (hasStateRestrictions(state)) {
return@PayPalComponentConfig "reject:STATE_RESTRICTED"
}
// Calculate new shipping cost
val shippingCost = calculateShipping(
country = countryCode,
state = state,
city = city,
postalCode = postalCode
)
// Calculate tax
val taxAmount = calculateTax(shippingAddress, baseAmount)
// Update order totals
updateOrderTotals(
shippingCost = shippingCost,
taxAmount = taxAmount
)
Log.d("PayPal", "Shipping validated and costs updated")
return@PayPalComponentConfig "" // Allow the change
} else {
return@PayPalComponentConfig "" // No address provided, allow
}
} catch (e: Exception) {
Log.e("PayPal", "Failed to process address change", e)
return@PayPalComponentConfig "" // Allow the change on error
}
}
)This callback is triggered when the customer selects a different shipping option in the PayPal checkout flow.
You can use it to:
- Update the total cost based on the shipping method selection.
- Show updated delivery dates for the selected method.
- Apply shipping-specific business rules or discounts.
- Track popular shipping method preferences.
| Event data | Description |
|---|---|
dataShippingOptionsData | The shipping option change data object. |
data.selectedShippingOptionObject | The shipping option selected by the customer. |
data.selectedShippingOption.idString | The unique identifier for the selected shipping option. |
data.selectedShippingOption.labelString | The display label for the selected shipping option (e.g., Standard Shipping, Express). |
data.selectedShippingOption.typeString | The type of shipping method (e.g., SHIPPING, PICKUP). |
data.selectedShippingOption.amountObject | The cost details for the selected shipping option. |
data.selectedShippingOption.amount.currencyCodeString | The three-letter ISO currency code (e.g., USD, EUR). |
data.selectedShippingOption.amount.valueDouble | The shipping cost amount in the specified currency. |
This callback must return a string. Return an empty string to allow the shipping option change, or return a rejection string (e.g., "reject:METHOD_UNAVAILABLE") to reject it.
val paypalConfig = PayPalComponentConfig(
onShippingOptionsChange = { data ->
Log.d("PayPal", "Shipping option changed: $data")
try {
val jsonObject = JSONObject(data)
val selectedOption = jsonObject.optJSONObject("selectedShippingOption")
if (selectedOption != null) {
val optionId = selectedOption.optString("id", "")
val optionLabel = selectedOption.optString("label", "")
val amount = selectedOption.optJSONObject("amount")
val shippingCost = amount?.optDouble("value", 0.0) ?: 0.0
// Track shipping method selection
analytics.track("paypal_shipping_method_selected", mapOf(
"shippingMethod" to optionId,
"shippingLabel" to optionLabel,
"shippingCost" to shippingCost,
"orderValue" to baseAmount,
"timestamp" to System.currentTimeMillis()
))
// Add insurance for expedited shipping
val insurance = if (optionId == "expedited" && baseAmount > 50) 2.99 else 0.0
// Calculate carbon offset for standard shipping
val carbonOffset = if (optionId == "standard") 0.50 else 0.0
// Update order totals
updateOrderTotals(
shippingCost = shippingCost,
insurance = insurance,
carbonOffset = carbonOffset
)
Log.d("PayPal", "Shipping option updated: $optionLabel")
}
return@PayPalComponentConfig "" // Allow the change
} catch (e: Exception) {
Log.e("PayPal", "Failed to process shipping option change", e)
return@PayPalComponentConfig "" // Allow the change on error
}
}
)This callback is triggered when there's an error during the order creation or submission process on the PXP side.
You can use it to:
- Handle PXP-specific errors and validation failures.
- Display detailed error messages to users.
- Log submission errors for debugging.
- Implement retry logic for failed submissions.
| Event data | Description |
|---|---|
errorString | The error message describing the submission failure. |
val paypalConfig = PayPalComponentConfig(
onSubmitError = { error ->
Log.e("PayPal", "Submission error: $error")
// Log error for debugging
crashlytics.log("PayPal submission error: $error")
// Handle specific error types
when {
error.contains("VALIDATION_ERROR") -> {
showErrorDialog(
title = "Validation Error",
message = "Please check your payment details and try again."
)
}
error.contains("INSUFFICIENT_FUNDS") -> {
showErrorDialog(
title = "Insufficient Funds",
message = "Insufficient funds in your PayPal account. Please add funds or use a different payment method."
)
}
error.contains("MERCHANT_NOT_ELIGIBLE") -> {
showErrorDialog(
title = "Service Unavailable",
message = "PayPal payments are temporarily unavailable. Please try a different payment method."
)
}
else -> {
showErrorDialog(
title = "Submission Error",
message = "Payment processing failed. Please try again or contact support."
)
}
}
// Offer alternative payment methods
showAlternativePaymentMethods()
// Track submission errors
analytics.track("paypal_submission_error", mapOf(
"error" to error,
"timestamp" to System.currentTimeMillis()
))
}
)This callback is triggered when the PayPal SDK script has been successfully loaded and is ready to use.
You can use it to:
- Initialise PayPal-related features that depend on the SDK.
- Show PayPal buttons that were hidden during loading.
- Track script loading performance.
- Set up PayPal-specific configurations.
This callback receives no parameters.
val paypalConfig = PayPalComponentConfig(
onScriptLoaded = {
Log.d("PayPal", "PayPal SDK script loaded successfully")
// Track script loading
analytics.track("paypal_script_loaded", mapOf(
"timestamp" to System.currentTimeMillis(),
"loadTime" to getPerformanceMetric("paypal_sdk_load")
))
// Hide loading indicator
hidePayPalLoadingIndicator()
// Show PayPal button
showPayPalButton()
// Initialise PayPal messaging if needed
initializePayPalMessaging()
}
)This callback is triggered to get user consent for vaulting (saving) their PayPal account for one-click payments.
You can use it to:
- Check if user agreed to save their PayPal account.
- Integrate with consent components.
- Track consent preferences.
- Comply with payment method storage regulations.
This callback receives no parameters.
val consentComponent = createConsentComponent()
val paypalConfig = PayPalComponentConfig(
enableOneClickPayment = true,
onGetConsent = {
// Get consent from component
val isConsentGranted = consentComponent.getValue() as? Boolean ?: false
// Log consent status
Log.d("PayPal", "Consent status: $isConsentGranted")
// Track consent
analytics.track("paypal_consent_status", mapOf(
"granted" to isConsentGranted,
"timestamp" to System.currentTimeMillis()
))
isConsentGranted
},
consentComponent = consentComponent
)Here's a complete example with all event callbacks:
val paypalConfig = PayPalComponentConfig(
renderType = "standalone",
fundingSources = "paypal",
onSuccess = { data ->
val jsonObject = JSONObject(data)
val orderID = jsonObject.getString("orderID")
processPaymentSuccess(orderID)
navController.navigate("success/$orderID")
return@PayPalComponentConfig "success"
},
onError = { error ->
Log.e("PayPal", "Error: $error")
showErrorDialog(error)
},
onCancel = {
Log.d("PayPal", "Cancelled")
showCancelMessage()
},
onOrderCreated = { data ->
val jsonObject = JSONObject(data)
val orderID = jsonObject.getString("providerTransactionId")
trackOrderCreation(orderID)
return@PayPalComponentConfig "order_created"
},
onShippingAddressChange = { data ->
validateAndUpdateShipping(data)
return@PayPalComponentConfig ""
},
onShippingOptionsChange = { data ->
updateShippingCosts(data)
return@PayPalComponentConfig ""
},
onSubmitError = { error ->
Log.e("PayPal", "Submit error: $error")
handleSubmitError(error)
},
onScriptLoaded = {
Log.d("PayPal", "Script loaded")
hideLoadingIndicator()
},
onGetConsent = {
consentComponent.getValue() as? Boolean ?: false
}
)