Implement callbacks to customise your Aeropay payment flow for Android.
Aeropay callbacks let your application respond to shopper interaction, custom validation, user verification, popup cancellation, transaction submission, and errors.
Use callbacks to:
- Block the flow when checkout isn't ready.
- Save the verified Aeropay user ID for returning-shopper flows.
- Validate business rules before submitting a transaction.
- Verify successful transactions on your backend.
- Restore your checkout interface when the shopper dismisses the popup.
- Log errors and show appropriate recovery options.
Configure onGetShopper on PxpSdkConfig. Configure the other callbacks on AeropayButtonComponentConfig.
The following example implements the main Aeropay callbacks:
val sdkConfig = PxpSdkConfig(
// ...session and transaction configuration
onGetShopper = {
Shopper(
id = "shopper-123",
firstName = "John",
lastName = "Doe",
email = "john.doe@example.com",
phoneNumber = "+14155550123",
)
},
)
val config = AeropayButtonComponentConfig().apply {
onClick = {
clearPaymentErrors()
}
onCustomValidation = {
val checkoutReady = validateCheckoutReady()
if (!checkoutReady) {
showPaymentError("Checkout isn't ready for Aeropay.")
}
checkoutReady
}
onUserVerificationSuccess = { user ->
saveAeropayUserId(user.id)
}
onCancel = {
resetCheckoutState()
}
onPreAuthorisation = {
val validation = validateOrderOnBackend()
if (!validation.approved) {
showPaymentError(validation.message)
}
validation.approved
}
onPostAuthorisation = { result ->
verifyPaymentOnBackend(result)
}
onSubmitError = { error ->
showAlternativePaymentMethods()
}
onError = { error ->
showPaymentError("Unable to complete the Aeropay flow.")
}
}Client callbacks aren't proof of payment. Verify every transaction on your backend before fulfilling an order or confirming a payout.
For a successful new-shopper transaction, callbacks run in this order:
onClickruns when the shopper taps the button.onCustomValidationruns. Returnfalseto stop before the flow starts.- After
onCustomValidationreturnstrue, the SDK callsonGetShopper(if configured) to load shopper details for prefilling and validation. - The component displays the consumer data and OTP screens.
onUserVerificationSuccessruns after successful OTP verification.onPreAuthorisationruns after the shopper selects a bank account and confirms the transaction.- After
onPreAuthorisationreturnstrue, the SDK may callonGetShopperagain when building the transaction request, then submits the transaction. onPostAuthorisationruns after successful transaction submission.
If the shopper dismisses the popup, onCancel runs. Flow errors call onError, while transaction submission failures call onSubmitError.
When you provide userId, the SDK skips consumer data collection and OTP verification. A successful returning-shopper transaction follows this order:
onClickruns after the shopper taps the button.onCustomValidationruns. Returnfalseto stop before user lookup.- The SDK validates
userIdand opens bank selection. onPreAuthorisationruns after the shopper selects a bank account and confirms the transaction.onPostAuthorisationruns after successful transaction submission.
The returning-shopper flow doesn't call onUserVerificationSuccess.
Configure onGetShopper on PxpSdkConfig. The SDK can call it more than once during a successful flow:
- After
onCustomValidationreturnstrue, to prefill and validate consumer data before the popup opens. - At transaction submission, to populate
TransactionRequest.shopper. If the callback isnullor omitted, the SDK falls back totransactionData.shopper.
For a returning shopper with userId, the SDK doesn't need onGetShopper to open the popup, but it can still call the callback when building the transaction request.
Return consistent shopper data on every invocation, or set transactionData.shopper if the callback shouldn't run again at submit.
Use the callback to return the latest shopper data from your application:
onGetShopper = {
val shopper = getCurrentShopper()
Shopper(
id = shopper.id,
firstName = shopper.firstName,
lastName = shopper.lastName,
email = shopper.email,
phoneNumber = shopper.phoneNumber,
)
}The Aeropay flow uses these shopper properties:
| Property | Description |
|---|---|
idString? | Your shopper identifier. Include a stable value so you can associate the transaction with your customer record. |
firstNameString? | The shopper's given name. Used to prefill consumer data and included in the transaction request. |
lastNameString? | The shopper's family name. Used to prefill consumer data and included in the transaction request. |
emailString? | The shopper's email address. Used to prefill consumer data and included in the transaction request. |
phoneNumberString? | The shopper's US phone number, including the +1 country prefix. Used to prefill consumer data, OTP delivery, and the transaction request. |
The new-shopper flow validates any values returned for firstName, lastName, email, and phoneNumber. Invalid data calls onError and prevents the popup from opening.
Configure the following callbacks on AeropayButtonComponentConfig.
The SDK calls onClick when the shopper taps the Aeropay button. Custom validation and popup opening happen after this callback.
Use this callback to clear previous errors, update your checkout interface, or track the interaction:
onClick = {
clearPaymentErrors()
setCheckoutActive(true)
trackEvent("aeropay-button-clicked")
}This callback receives no parameters.
The SDK calls onCustomValidation after onClick and before the Aeropay flow starts. Because the callback is suspend, you can call network APIs directly.
Return true to continue or false to stop:
onCustomValidation = {
val checkoutReady = validateCheckoutReady()
if (!checkoutReady) {
showPaymentError("Checkout isn't ready for Aeropay.")
}
checkoutReady
}If the callback is omitted, the SDK treats the result as true.
| Return value | Behaviour |
|---|---|
true | The SDK continues with returning-user lookup or new-shopper shopper-data checks. |
false | The SDK stops and doesn't open the popup. |
The SDK calls onUserVerificationSuccess after a new shopper enters the correct OTP. The callback isn't called for returning shoppers who enter the flow with userId.
This callback is optional. If you don't configure it, the SDK continues the Aeropay flow after OTP verification.
Store the returned Aeropay user ID securely so you can use the returning-shopper flow:
onUserVerificationSuccess = { user ->
saveAeropayUserId(
shopperId = getCurrentShopperId(),
aeropayUserId = user.id,
)
}The callback receives an AeropayUser object:
| Property | Description |
|---|---|
idString required | The verified Aeropay user ID. |
firstNameString? | The user's given name. |
lastNameString? | The user's family name. |
typeString? | The Aeropay user type. |
emailString? | The user's email address. |
phoneNumberString? | The user's phone number. |
createdDateString? | The date and time when the Aeropay user was created. |
The SDK calls onCancel when the shopper dismisses the Aeropay popup, including the close control and system back or scrim dismiss.
Programmatic dismissal after a successful payment doesn't call onCancel.
Use this callback to restore your checkout interface without treating cancellation as an error:
onCancel = {
setCheckoutActive(false)
setLoading(false)
showAlternativePaymentMethods()
}This callback receives no parameters and has no return value.
The SDK calls onPreAuthorisation after the shopper selects a bank account and confirms the payment or payout. Use it to validate your order or payout before the SDK submits the transaction.
Return true to continue or false to stop submission:
onPreAuthorisation = {
val validation = validateOrderOnBackend()
if (!validation.approved) {
showPaymentError(validation.message)
}
validation.approved
}The callback controls transaction submission as follows:
| Return value | Behaviour |
|---|---|
true | The SDK builds and submits the transaction. |
false | The SDK stops transaction submission and leaves the Aeropay flow open. |
Implement onPreAuthorisation and return true when the transaction can proceed. If the callback is omitted or doesn't return true, the SDK doesn't submit the transaction.
The SDK calls onPostAuthorisation after PXP accepts the transaction submission. The successful payload is a MerchantSubmitResult containing the transaction identifiers.
This callback is optional. If you don't configure it, the SDK continues the Aeropay flow after successful submission and the popup still closes.
Send the identifiers to your backend and verify the final transaction state:
onPostAuthorisation = { submitResult ->
verifyPaymentOnBackend(
merchantTransactionId = submitResult.merchantTransactionId,
systemTransactionId = submitResult.systemTransactionId,
)
}The successful payload contains these properties:
| Property | Description |
|---|---|
merchantTransactionIdString required | Your unique transaction identifier. |
systemTransactionIdString required | PXP's unique transaction identifier. |
The SDK calls onSubmitError when transaction submission fails. It isn't used for consumer data, user verification, bank account, or Aerosync failures. Those errors call onError.
Handle submission failures without exposing technical details to the shopper:
import com.pxp.checkout.models.FailedSubmitResult
onSubmitError = { submitError ->
if (submitError is FailedSubmitResult) {
logPaymentFailure(
errorCode = submitError.errorCode,
correlationId = submitError.correlationId,
)
}
setLoading(false)
showPaymentError("We could not complete the transaction. Try again or use another payment method.")
}A failed PXP response typically passes a FailedSubmitResult:
| Property | Description |
|---|---|
errorCodeString? | The transaction error code. |
errorReasonString? | A description of the failure. |
correlationIdString? | The correlation identifier to include in logs and support requests. |
httpStatusCodeInt? | The HTTP status code returned by PXP. |
detailsList<String>? | Additional error details. |
When a failed Unity transaction response is mapped to FailedSubmitResult, the SDK also emits ComponentError analytics with SDK1326. Network or transport failures still call onSubmitError, but their ComponentError analytics can use a different code, such as SDK0500. Neither path delivers SDK1326 through onError. Handle the response details from onSubmitError.
The SDK calls onError for errors outside transaction submission, including:
- Invalid shopper data.
- Aeropay user creation or verification failure.
- Returning-user lookup:
SDK1307(inactive),SDK1308(recognised API failure). Network errors on the same call useSDK0500orSDK0000, notSDK1308. See Returning-shopper issues. - Bank account retrieval or linking failure.
- Aerosync failure.
- Missing host Activity when launching Aerosync (
SDK1311).
Log recognised SDK errors and show a message that helps the shopper recover:
onError = { error ->
logErrorToMonitoring(
errorCode = error.errorCode,
message = error.message,
details = error.details,
)
setLoading(false)
showPaymentError("Unable to continue with Aeropay. Try again or use another payment method.")
}For prefilled shopper validation failures (SDK1300), inspect error.details for the field keys and localised validation messages.
Recognised SDK failures use BaseSdkException:
| Property | Description |
|---|---|
errorCodeString required | The PXP Android SDK error code (e.g., SDK1300). |
messageString required | A description of the error. |
detailsAny? | Optional structured detail. For SDK1300, a map of field names to validation messages. |
onUserVerificationSuccess receives the following object:
import com.pxp.checkout.components.aeropaybutton.types.AeropayUser
data class AeropayUser(
val id: String,
val firstName: String? = null,
val lastName: String? = null,
val type: String? = null,
val email: String? = null,
val phoneNumber: String? = null,
val createdDate: String? = null,
)Successful transaction submission produces a MerchantSubmitResult with merchantTransactionId and systemTransactionId.
A failed PXP transaction response produces a FailedSubmitResult with errorCode, errorReason, correlationId, httpStatusCode, and details.
Recognised onError failures use BaseSdkException with errorCode, message, and optional details.
Keep error logging separate from the message shown to the shopper. The following pattern records diagnostic information while displaying a neutral recovery message:
val config = AeropayButtonComponentConfig().apply {
onPreAuthorisation = { true }
onPostAuthorisation = { result ->
try {
verifyPaymentOnBackend(result)
showPaymentSuccess()
} catch (exception: Exception) {
showPaymentError("We could not confirm the transaction status. Contact support before trying again.")
}
}
onSubmitError = { error ->
reportErrorToMonitoring(error)
showPaymentError("We could not complete the transaction. Try another payment method.")
}
onError = { error ->
reportErrorToMonitoring(
errorCode = error.errorCode,
message = error.message,
details = error.details,
)
showPaymentError("Unable to continue with Aeropay. Try again or use another payment method.")
}
}