Implement callbacks to customise your payment flow.
Components emit events and provide callback interfaces 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.
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 pre-built components.
| Event | Billing address | Card-on-file | Click-once | New card |
|---|---|---|---|---|
onAddressChanged | ||||
onBlur | ||||
onCardBrandCannotRecognised | ||||
onCardBrandDetected | ||||
onCardDetected | ||||
onCardFlipped | ||||
onCardInteraction | ||||
onChange | ||||
onClick | ||||
onConsentChanged | ||||
onCountryDeselected | ||||
onCountrySelected | ||||
onCollectEnd | ||||
onCollectStart | ||||
onCvcEntered | ||||
onCustomValidation | ||||
onDeleteTokenFailed | ||||
onDeleteTokenSuccess | ||||
onFocus | ||||
onGetFingerprintResult | ||||
onImageError | ||||
onImageLoaded | ||||
onOnceCardClick | ||||
onPostAuthentication | ||||
onPostAuthorisation | ||||
onPostInitiateAuthentication | ||||
onPostTokenisation | ||||
onPreAuthentication | ||||
onPreAuthorisation | ||||
onPreDeleteToken | ||||
onPreInitiateAuthentication | ||||
onPreRenderTokens | ||||
onPreTokenisation | ||||
onPrivacyClicked | ||||
onRetrieveTokensFailed | ||||
onStateChanged | ||||
onSubmitError | ||||
onSyncComplete | ||||
onTermsClicked | ||||
onTriggerFieldValidation | ||||
onUpdateTokenFailed | ||||
onUpdateTokenSuccess | ||||
onValidation | ||||
onValidationPassed | ||||
onValidationFailed | ||||
tokenItemBuilder | ||||
tokenLabelBuilder |
This callback is triggered when address synchronisation starts.
You can use it to:
- Show loading indicators.
- Track sync performance.
- Update the progress UI.
- Log sync operations.
- Handle concurrent sync requests.
- Display sync progress to users.
| Parameter | Description |
|---|---|
eventAddressSyncEvent | The address synchronisation event object. |
event.sourceAddressBillingAddressData | The shipping address being copied. |
event.targetComponentIdString | The ID of the billing address component. |
event.syncedFieldsList<String> | The list of fields being synchronised. |
event.syncStrategyAddressSyncStrategy | The strategy used for synchronisation. |
event.startTimeLong | When the sync operation started (timestamp). |
val preFillCheckboxConfig = PreFillBillingAddressCheckboxComponentConfig(
onAddressSync = { event ->
Log.d("AddressSync", "Starting address sync")
Log.d("AddressSync", "Source: ${event.sourceAddress}")
Log.d("AddressSync", "Strategy: ${event.syncStrategy}")
Log.d("AddressSync", "Fields: ${event.syncedFields.joinToString()}")
// Show loading UI
showAddressSyncLoading()
// Update progress indicators
updateSyncProgress(0, event.syncedFields.size)
// Track sync start
analytics.track("address_sync_started", mapOf(
"sync_strategy" to event.syncStrategy.name,
"field_count" to event.syncedFields.size,
"target_component" to event.targetComponentId
))
// Log for debugging
logSyncOperation("started", event)
}
)This callback is triggered when the input field loses focus.
You can use it to:
- Hide input assistance UI (keyboards, helpers).
- Trigger field validation.
- Save the field state for recovery.
- Update progress indicators.
- Track user interaction patterns.
- Implement custom focus management.
| Parameter | Description |
|---|---|
focusEventFocusEvent | The focus event object. |
focusEvent.fieldNameString | The name of the field that lost focus. |
focusEvent.valueString | The current value in the field. |
focusEvent.isValidBoolean | Whether the current value is valid. |
focusEvent.timestampLong | When the blur event occurred (timestamp). |
focusEvent.nextFocusTargetString? | The next field that will receive focus, if any. |
val cardNumberConfig = CardNumberComponentConfig(
onBlur = { focusEvent ->
Log.d("Payment", "Card number field blurred: ${focusEvent.value}")
Log.d("Payment", "Field valid: ${focusEvent.isValid}")
// Hide input assistance UI
hideInputHelper()
// Trigger validation if field has content
if (focusEvent.value.isNotEmpty()) {
validateCardNumber(focusEvent.value)
}
// Update UI state
updateFieldAppearance(focusEvent.fieldName, focused = false)
// Track field completion
analytics.track("field_blur", mapOf(
"field" to focusEvent.fieldName,
"has_value" to focusEvent.value.isNotEmpty(),
"is_valid" to focusEvent.isValid
))
}
)This callback is triggered when the card number input changes, but the entered digits don't match any known card brand patterns.
You can use it to:
- Display a generic card icon instead of a brand-specific icon.
- Show warning messages for unsupported card types.
- Provide a fallback UI for unknown card brands.
- Track unrecognised card patterns for analytics.
- Implement custom validation for regional card types.
- Guide users to supported payment methods.
| Parameter | Description |
|---|---|
eventCardBrandRecognitionEvent | The card brand recognition event object. |
event.cardNumberString | The current card number input (masked for security). |
event.detectedLengthInt | The length of the current input. |
event.potentialMatchesList<CardBrand> | The list of possible card brands based on partial input. |
event.inputSourceInputSource | How the number was entered. |
event.timestampLong | The date and time when the detection attempt occurred. |
event.confidenceLevelFloat | The confidence level of the detection attempt (0.0 to 1.0). |
val cardNumberConfig = CardNumberComponentConfig(
onCardBrandCannotRecognised = { event ->
Log.d("Payment", "Card brand could not be recognised")
// Show generic card icon or warning
showUnknownCardBrandWarning()
}
)This callback is triggered when the card number input changes and a recognised card brand (e.g., Visa, MasterCard) is detected from the input value.
You can use it to:
- Update card brand icons and UI styling.
- Enable/disable brand-specific features.
- Show acceptance status messages.
- Track brand detection accuracy.
- Trigger validation rule changes.
| Parameter | Description |
|---|---|
eventCardBrandDetectionEvent | The card brand detection event object. |
event.cardBrandCardBrand | The detected card brand. |
event.isCardBrandAcceptedBoolean | Whether the detected brand is accepted. |
event.confidenceFloat | The detection confidence score (0.0-1.0). |
event.previousBrandCardBrand? | The previously detected brand (if any). |
val cardNumberConfig = CardNumberComponentConfig(
onCardBrandDetected = { event ->
val cardBrand = event.cardBrand
val isAccepted = event.isCardBrandAccepted
Log.d("Payment", "Card brand detected: ${cardBrand.name}, accepted: $isAccepted")
// Update UI to show detected brand
updateCardBrandIcon(cardBrand)
if (!isAccepted) {
showCardNotAcceptedMessage(cardBrand)
}
// Track detection analytics
analytics.track("card_brand_detected", mapOf(
"brand" to cardBrand.name,
"accepted" to isAccepted,
"confidence" to event.confidence
))
}
)This callback is triggered when the dynamic card image component detects or changes the card brand display.
You can use it to:
- Update the card image display.
- Trigger brand-specific validations.
- Update form field behaviours.
- Track detection accuracy.
- Customise the UI based on brand.
- Enable brand-specific features.
| Parameter | Description |
|---|---|
eventCardDetectionEvent | The card detection event object. |
event.detectedBrandCardBrand | The newly detected card brand. |
event.previousBrandCardBrand? | The previously detected brand (if any). |
event.confidenceFloat | The detection confidence score (0.0-1.0). |
event.detectionTimeLong | The time taken for detection, in milliseconds. |
event.sourceDetectionSource | The source of the detection. |
val dynamicCardImageConfig = DynamicCardImageComponentConfig(
onCardDetected = { event ->
Log.d("CardImage", "Card detected: ${event.detectedBrand.displayName}")
Log.d("CardImage", "Confidence: ${event.confidence}")
Log.d("CardImage", "Detection time: ${event.detectionTime}ms")
// Update UI based on detected brand
when (event.detectedBrand) {
CardBrand.VISA -> {
updateCardValidation(visaValidationRules)
enableVisaSpecificFeatures()
}
CardBrand.AMERICAN_EXPRESS -> {
updateCardValidation(amexValidationRules)
showAmexCvcHelper() // 4-digit CVC
}
CardBrand.MASTERCARD -> {
updateCardValidation(mastercardValidationRules)
enableMastercardBenefits()
}
else -> {
updateCardValidation(genericValidationRules)
}
}
// Announce brand change for accessibility
if (announceCardChanges) {
announceToAccessibility("${event.detectedBrand.displayName} card detected")
}
// Track detection analytics
if (trackCardDetection) {
trackAnalyticsEvent("card_brand_detected", mapOf(
"brand" to event.detectedBrand.displayName,
"confidence" to event.confidence.toString(),
"detection_time" to event.detectionTime.toString(),
"source" to event.source.name
))
}
}
)This callback is triggered when the card image is flipped to show the front or back.
You can use it to:
- Coordinate with CVC input field.
- Show relevant card information.
- Track user engagement.
- Provide contextual help.
- Enhance visual feedback.
- Update accessibility announcements.
| Parameter | Description |
|---|---|
eventCardFlipEvent | The card flip event object. |
event.cardBrandCardBrand | The brand of the flipped card. |
event.showingBackBoolean | Whether the back side is now visible. |
event.triggerFlipTrigger | What triggered the flip (tap, swipe, etc.). |
event.animationDurationLong | The duration of the flip animation in milliseconds. |
val dynamicCardImageConfig = DynamicCardImageComponentConfig(
onCardFlipped = { event ->
Log.d("CardImage", "Card flipped: ${event.cardBrand.displayName}")
Log.d("CardImage", "Showing back: ${event.showingBack}")
Log.d("CardImage", "Trigger: ${event.trigger}")
// Update related UI components
if (event.showingBack) {
// Highlight CVC field when back is shown
highlightCvcField()
showCvcHelper()
} else {
// Show card number helper when front is shown
highlightCardNumberField()
showCardNumberHelper()
}
// Provide accessibility feedback
announceToAccessibility(
if (event.showingBack) "Showing card back with security code field"
else "Showing card front with card number"
)
// Track user engagement
analytics.track("card_flip", mapOf(
"showing_back" to event.showingBack,
"trigger" to event.trigger.name,
"brand" to event.cardBrand.displayName
))
}
)This callback is triggered when a user interacts with the card image.
You can use it to:
- Trigger card flip animations.
- Show card details or zoom.
- Provide haptic feedback.
- Track user engagement patterns.
- Enable interactive card features.
- Implement custom gestures.
| Parameter | Description |
|---|---|
eventCardInteractionEvent | The card interaction event object. |
event.cardBrandCardBrand | The brand of the card. |
event.interactionTypeInteractionType | The type of interaction performed. For example, a tap or a swipe. |
event.timestampLong | The date and time when the interaction occurred. |
event.coordinatesTouchCoordinates? | The touch coordinates, if applicable. |
val dynamicCardImageConfig = DynamicCardImageComponentConfig(
onCardInteraction = { event ->
Log.d("CardImage", "Card interaction: ${event.interactionType}")
Log.d("CardImage", "Brand: ${event.cardBrand.displayName}")
when (event.interactionType) {
InteractionType.TAP -> {
// Simple tap - flip card
triggerCardFlip()
provideTapFeedback()
}
InteractionType.LONG_PRESS -> {
// Long press - show card details
showCardDetailsMenu()
provideHapticFeedback()
}
InteractionType.DOUBLE_TAP -> {
// Double tap - zoom card
showCardZoom()
}
InteractionType.SWIPE_LEFT -> {
// Swipe left - previous card style
switchToPreviousCardStyle()
}
InteractionType.SWIPE_RIGHT -> {
// Swipe right - next card style
switchToNextCardStyle()
}
}
// Track engagement metrics
analytics.track("card_interaction", mapOf(
"interaction_type" to event.interactionType.name,
"brand" to event.cardBrand.displayName,
"timestamp" to event.timestamp
))
}
)This callback is triggered when the input value changes in one or more fields. For example, if a character is added or the field is cleared.
You can use it to:
- Real-time validation and formatting
- Auto-advance to next field
- Dynamic UI updates based on content
- Character counting and limits
- Search suggestions
- Progress tracking
| Parameter | Description |
|---|---|
changeEventChangeEvent | The change event object. |
changeEvent.fieldNameString | The name of the field that changed. |
changeEvent.valueString | The new value in the field. |
changeEvent.previousValueString | The previous value before change. |
changeEvent.changeTypeChangeType | The type of change (insertion, deletion, replacement). |
changeEvent.cursorPositionInt | The current cursor position. |
changeEvent.isValidBoolean | Whether the new value is valid. |
changeEvent.timestampLong | The timestamp when the change occurred. |
val cardNumberConfig = CardNumberComponentConfig(
onChange = { changeEvent ->
Log.d("Payment", "Card number changed: ${changeEvent.value}")
Log.d("Payment", "Change type: ${changeEvent.changeType}")
Log.d("Payment", "Cursor position: ${changeEvent.cursorPosition}")
// Real-time validation and formatting
val formattedValue = formatCardNumber(changeEvent.value)
if (formattedValue != changeEvent.value) {
updateFieldValue(changeEvent.fieldName, formattedValue)
}
// Detect card brand as user types
val detectedBrand = detectCardBrand(changeEvent.value)
updateCardBrandIndicator(detectedBrand)
// Auto-advance when complete
if (isCardNumberComplete(changeEvent.value) && changeEvent.isValid) {
advanceToNextField()
}
// Track typing patterns for UX insights
analytics.track("field_change", mapOf(
"field" to changeEvent.fieldName,
"change_type" to changeEvent.changeType.name,
"length" to changeEvent.value.length,
"is_valid" to changeEvent.isValid
))
}
)This callback is triggered when submission begins.
You can use it to:
- Show loading indicators.
- Disable form interactions.
- Track submission attempts.
- Update the UI state to "processing".
- Log submission events for analytics.
| Parameter | Description |
|---|---|
timestampLong | Date and time when the submission started. |
componentStateComponentState | The current state of the submit component. |
validationResultsList<ValidationResult> | The results of pre-submission validation. |
val cardSubmitConfig = CardSubmitComponentConfig(
onSubmitStart = {
Log.d("Payment", "Payment submission started")
// Show loading indicator
showPaymentProcessingUI()
// Disable form interactions
disableAllFormInputs()
// Update UI state
updateSubmitButtonState(loading = true)
// Track submission attempt
analytics.track("payment_submission_started", mapOf(
"timestamp" to System.currentTimeMillis(),
"payment_method" to getCurrentPaymentMethod()
))
}
)This callback is triggered when the device data collection phase starts.
You can use it to:
- Update progress indicators.
- Show "Gathering device information" message.
- Track collection start time.
- Initialise fraud detection.
- Display loading states.
| Parameter | Description |
|---|---|
timestampLong | Date and time when collection started. |
deviceInfoDeviceInfo | Basic device information. |
sessionIdString | The collection session identifier. |
val cardSubmitConfig = CardSubmitComponentConfig(
onCollectStart = {
Log.d("Payment", "Device data collection started")
// Show collection progress
showMessage("Collecting device information...", MessageType.INFO)
// Update progress indicators
updateProgressStep("collect", ProgressStepStatus.IN_PROGRESS)
// Track collection start
analytics.track("device_collection_started", mapOf(
"timestamp" to System.currentTimeMillis(),
"session_id" to getCurrentSessionId()
))
}
)This callback is triggered when the device data collection phase completes.
You can use it to:
- Evaluate the fraud risk score.
- Proceed to authentication if low risk.
- Apply additional security if high risk.
- Track collection performance metrics.
- Update progress indicators.
| Parameter | Description |
|---|---|
collectResultCollectResult | The collection result object. |
collectResult.deviceDataString | Encrypted device fingerprint. |
collectResult.fingerprintString | The unique device identifier. |
collectResult.riskScoreFloat | The fraud risk assessment score (0.0-1.0). |
collectResult.collectDurationLong | The time taken to collect data, in milliseconds. |
collectResult.dataPointsInt | The number of data points collected. |
val cardSubmitConfig = CardSubmitComponentConfig(
onCollectComplete = { collectResult ->
Log.d("Payment", "Device data collection completed")
Log.d("Payment", "Risk score: ${collectResult.riskScore}")
Log.d("Payment", "Collection time: ${collectResult.collectDuration}ms")
// Hide collection progress
hideDataCollectionIndicator()
// Evaluate risk score
when {
collectResult.riskScore > 0.8f -> {
Log.w("Payment", "High risk transaction detected")
handleHighRiskTransaction(collectResult)
}
collectResult.riskScore > 0.5f -> {
Log.i("Payment", "Medium risk transaction")
handleMediumRiskTransaction(collectResult)
}
else -> {
Log.d("Payment", "Low risk transaction")
proceedWithNormalFlow()
}
}
// Track collection performance
analytics.track("device_collection_completed", mapOf(
"risk_score" to collectResult.riskScore,
"duration" to collectResult.collectDuration,
"data_points" to collectResult.dataPoints,
"fingerprint_quality" to getFingerprintQuality(collectResult)
))
}
)This callback is triggered when a customer enters a CVC value in the click-once component's CVC field.
You can use it to:
- Enable the submit button after CVC entry.
- Perform real-time CVC validation.
- Track user progress in the payment flow.
- Update the UI state for form completion.
- Trigger the card authentication flow.
| Parameter | Description |
|---|---|
cvcEventCvcEvent | The CVC entry event object. |
cvcEvent.cvcValueString | The entered CVC value (masked). |
cvcEvent.cardTokenCardToken | The associated card token. |
cvcEvent.isValidBoolean | Whether the CVC is valid. |
cvcEvent.validationErrorsList<ValidationResult>? | Any validation errors. |
val clickOnceConfig = ClickOnceComponentConfig(
onCvcEntered = { cvcEvent ->
Log.d("Payment", "CVC has been entered for card: ${cvcEvent.cardToken.maskedPAN}")
Log.d("Payment", "CVC valid: ${cvcEvent.isValid}")
if (cvcEvent.isValid) {
// Enable submit button
enableSubmitButton()
// Clear any previous errors
clearCvcError()
// Track successful CVC entry
analytics.track("cvc_entered", mapOf(
"card_brand" to cvcEvent.cardToken.cardBrand.name,
"is_valid" to true
))
} else {
// Show validation errors
showCvcErrors(cvcEvent.validationErrors)
// Keep submit button disabled
disableSubmitButton()
}
}
)This callback is triggered after standard component validation passes and before payment submission proceeds. Return true to allow submission, or false to block submission.
You can use it to:
- Validate custom business rules before payment processing.
- Check transaction amount limits or thresholds.
- Verify customer eligibility for specific payment methods.
- Implement fraud prevention rules based on order data.
- Validate inventory availability before charging.
- Enforce payment policies based on customer account status.
This callback receives no parameters and returns a Boolean value.
| Return value | Description |
|---|---|
true | Allows the payment submission to proceed. |
false | Blocks the payment submission and stops the transaction flow. |
This callback is called after all standard SDK component validations (card number, CVV, expiry date, billing address) have passed successfully. It enables you to add an additional validation layer for business-specific requirements.
val cardSubmitConfig = CardSubmitComponentConfig(
onCustomValidation = {
Log.d("Payment", "Running custom validation checks...")
// Example: Validate transaction amount limits
val transactionAmount = checkout.getSdkConfig().transactionData.amount
val maxAllowedAmount = 10000.0
if (transactionAmount > maxAllowedAmount) {
Log.w("Payment", "Transaction amount exceeds maximum allowed: $maxAllowedAmount")
showCustomError("Transaction amount cannot exceed £$maxAllowedAmount")
return@CardSubmitComponentConfig false
}
// Example: Check customer eligibility
val customerTier = getCustomerTier()
if (customerTier == CustomerTier.RESTRICTED) {
Log.w("Payment", "Customer account is restricted from making payments")
showCustomError("Your account is temporarily restricted. Please contact support.")
return@CardSubmitComponentConfig false
}
// Example: Validate inventory availability
val itemsInStock = checkInventoryAvailability()
if (!itemsInStock) {
Log.w("Payment", "One or more items are no longer available")
showCustomError("Some items in your cart are no longer available")
return@CardSubmitComponentConfig false
}
// All custom validations passed
Log.d("Payment", "Custom validation passed successfully")
return@CardSubmitComponentConfig true
},
onSubmitError = { error ->
Log.e("Payment", "Payment submission error: ${error.message}")
},
onPostAuthorisation = { result ->
Log.d("Payment", "Payment authorisation completed")
}
)This callback is triggered when a card token deletion request fails. For example, due to a network error.
You can use it to:
- Show user-friendly error messages.
- Implement retry logic for transient failures.
- Log errors for debugging.
- Track deletion failure rates.
- Provide alternative actions.
| Parameter | Description |
|---|---|
responseDeleteTokenFailureResponse | The failure response object. |
response.tokenIdString | The ID of the token that failed to delete. |
response.errorSdkException | The error details and error code. |
response.errorMessageString | A human-readable error message. |
response.retryableBoolean | Whether the operation can be retried. |
response.requestIdString | The unique request identifier. |
val cardOnFileConfig = CardOnFileComponentConfig(
onDeleteTokenFailed = { response ->
Log.e("Payment", "Token deletion failed: ${response.error}")
Log.e("Payment", "Token ID: ${response.tokenId}")
Log.e("Payment", "Retryable: ${response.retryable}")
// Show appropriate error message
val errorMessage = when (response.error.code) {
"NETWORK_ERROR" -> "Network error. Please check your connection and try again."
"INVALID_TOKEN" -> "This card is no longer valid."
"INSUFFICIENT_PERMISSIONS" -> "You don't have permission to delete this card."
else -> "Failed to delete card. Please try again."
}
showErrorDialog(errorMessage)
// Enable retry if possible
if (response.retryable) {
showRetryOption {
retryTokenDeletion(response.tokenId)
}
}
// Track deletion failures
analytics.track("token_deletion_failed", mapOf(
"token_id" to response.tokenId,
"error_code" to response.error.code,
"retryable" to response.retryable
))
}
)This callback is triggered when a card token is successfully deleted from the vault.
You can use it to:
- Refresh the token list display.
- Show success confirmation messages.
- Update the UI to reflect removal.
- Track token management analytics.
- Clean up local references.
| Parameter | Description |
|---|---|
responseDeleteTokenSuccessResponse | The success response object. |
response.tokenIdString | The ID of the successfully deleted token. |
response.deletedTokenCardToken | Complete token information that was deleted. |
response.remainingTokenCountInt | The number of tokens remaining. |
response.deletionTimeLong | The time taken for the deletion operation. |
val cardOnFileConfig = CardOnFileComponentConfig(
onDeleteTokenSuccess = { response ->
Log.d("Payment", "Token deleted successfully: ${response.tokenId}")
Log.d("Payment", "Remaining tokens: ${response.remainingTokenCount}")
// Show success message
showSuccessMessage("Card deleted successfully")
// Refresh the token list
refreshCardList()
// Update UI if no cards remain
if (response.remainingTokenCount == 0) {
showNoCardsMessage()
hideCardOnFileSection()
}
// Track successful deletion
analytics.track("token_deletion_success", mapOf(
"token_id" to response.tokenId,
"card_brand" to response.deletedToken.cardBrand.name,
"remaining_count" to response.remainingTokenCount,
"deletion_time" to response.deletionTime
))
// Announce to accessibility services
announceToAccessibility("Card deleted successfully")
}
)This callback is triggered when the input field receives focus.
You can use it to:
- Show input hints or help text.
- Display field-specific keyboards.
- Update the UI styling for the focused state.
- Track user interaction patterns.
- Provide contextual assistance.
- Implement custom focus management.
| Parameter | Description |
|---|---|
focusEventFocusEvent | The focus event object. |
focusEvent.fieldNameString | The name of the field that received focus. |
focusEvent.valueString | The current value in the field. |
focusEvent.isValidBoolean | Whether the current value is valid. |
focusEvent.timestampLong | The date and time when the focus event occurred. |
focusEvent.previousFocusTargetString? | The previous field that had focus, if any. |
val cardNumberConfig = CardNumberComponentConfig(
onFocus = { focusEvent ->
Log.d("Payment", "Card number field focused")
Log.d("Payment", "Current value: ${focusEvent.value}")
Log.d("Payment", "Previous focus: ${focusEvent.previousFocusTarget}")
// Show input hints or help text
showCardNumberInputHelper()
// Update field appearance
updateFieldAppearance(focusEvent.fieldName, focused = true)
// Show numeric keyboard for card number
showNumericKeyboard()
// Track field focus for UX analytics
analytics.track("field_focus", mapOf(
"field" to focusEvent.fieldName,
"has_value" to focusEvent.value.isNotEmpty(),
"previous_field" to focusEvent.previousFocusTarget
))
// Announce to accessibility services
announceToAccessibility("Card number field focused. Enter your card number")
}
)This callback is used to retrieve fingerprint data for authentication.
You can use it to:
- Provide device fingerprint for fraud detection.
- Support the 3DS authentication flows.
- Enhance transaction security.
- Comply with payment processor requirements.
| Parameter | Description |
|---|---|
requestIdString | The unique identifier for the fingerprint request. |
timestampLong | The date and time when the fingerprint was requested. |
val cardSubmitConfig = CardSubmitComponentConfig(
onGetFingerprintResult = {
// Return fingerprint data as a string
return@CardSubmitComponentConfig getDeviceFingerprintData()
}
)
private suspend fun getDeviceFingerprintData(): String {
// Implement device fingerprinting logic
return deviceFingerprintService.collectFingerprint()
}This callback is triggered when a card image fails to load. For example, due to an unsupported image format or network issues.
You can use it to:
- Display fallback images.
- Implement custom retry logic.
- Track image availability issues.
- Switch to offline mode.
- Report errors to monitoring systems.
- Show user-friendly error messages.
| Parameter | Description |
|---|---|
eventImageErrorEvent | The image error event object. |
event.cardBrandCardBrand | The brand of the failed image. |
event.errorImageLoadError | The type of error that occurred. |
event.retryAttemptInt | The current retry attempt number. |
event.canRetryBoolean | Whether automatic retry is possible. |
event.errorMessageString | The detailed error description. |
val dynamicCardImageConfig = DynamicCardImageComponentConfig(
onImageError = { event ->
Log.e("CardImage", "Image load failed for ${event.cardBrand.displayName}")
Log.e("CardImage", "Error: ${event.error}")
Log.e("CardImage", "Retry attempt: ${event.retryAttempt}")
when (event.error) {
ImageError.NETWORK_ERROR -> {
if (event.canRetry && event.retryAttempt < 3) {
// Retry with exponential backoff
retryImageLoad(event.cardBrand, event.retryAttempt)
} else {
// Use cached fallback image
loadFallbackImage(event.cardBrand)
}
}
ImageError.NOT_FOUND -> {
// Use generic card image
loadGenericCardImage()
reportMissingImage(event.cardBrand)
}
ImageError.TIMEOUT -> {
// Show loading state and retry
showImageLoadingState()
retryImageLoad(event.cardBrand, event.retryAttempt)
}
else -> {
// Use fallback and log error
loadFallbackImage(event.cardBrand)
logImageError(event)
}
}
// Track error metrics
analytics.track("image_load_error", mapOf(
"brand" to event.cardBrand.displayName,
"error_type" to event.error.name,
"retry_attempt" to event.retryAttempt,
"can_retry" to event.canRetry
))
}
)This callback is triggered when a card image successfully loads.
You can use it to:
- Track image loading performance.
- Monitor cache effectiveness.
- Optimise the image loading strategy.
- Handle slow network conditions.
- Update the loading states.
- Measure user experience metrics.
| Parameter | Description |
|---|---|
eventImageLoadedEvent | The image loaded event object. |
event.cardBrandCardBrand | The brand of the loaded card image. |
event.imageUrlString | The URL or resource identifier of the image. |
event.loadTimeLong | The time taken to load the image, in milliseconds. |
event.fromCacheBoolean | Whether the image was loaded from the cache. |
event.imageSizeLong | The size of the loaded image, in bytes. |
event.resolutionString | The image resolution (width x height). |
val dynamicCardImageConfig = DynamicCardImageComponentConfig(
onImageLoaded = { event ->
Log.d("CardImage", "Image loaded: ${event.cardBrand.displayName}")
Log.d("CardImage", "Load time: ${event.loadTime}ms")
Log.d("CardImage", "From cache: ${event.fromCache}")
Log.d("CardImage", "Image size: ${formatFileSize(event.imageSize)}")
// Hide loading indicators
hideImageLoadingState()
// Update image cache statistics
updateCacheStats(event.fromCache, event.loadTime, event.imageSize)
// Track loading performance
if (trackImageLoading) {
trackPerformanceMetric("image_load_time", event.loadTime)
trackPerformanceMetric("image_cache_hit", if (event.fromCache) 1 else 0)
trackPerformanceMetric("image_size", event.imageSize)
}
// Optimize future loading based on performance
if (event.loadTime > SLOW_LOAD_THRESHOLD) {
considerImageQualityReduction()
}
// Preload related images if performance is good
if (event.loadTime < FAST_LOAD_THRESHOLD && !event.fromCache) {
preloadRelatedBrandImages()
}
// Update accessibility
updateImageAccessibilityDescription(event.cardBrand)
// Analytics tracking
analytics.track("image_loaded", mapOf(
"brand" to event.cardBrand.displayName,
"load_time" to event.loadTime,
"from_cache" to event.fromCache,
"size" to event.imageSize,
"resolution" to "${event.resolution.width}x${event.resolution.height}"
))
}
)This callback is triggered when a customer selects a saved card in the click-once or card-on-file component.
You can use it to:
- Handle card selection logic.
- Update the payment form with the card details.
- Track user preferences.
- Enable/disable the CVC requirement.
- Update the UI to reflect the customer's selection.
| Parameter | Description |
|---|---|
eventCardSelectionEvent | The card selection event object. |
event.tokenIdString | The ID of the selected card token. |
event.cardBrandCardBrand | The brand of the selected card. |
event.maskedNumberString | The masked card number for display. |
event.expiryDateString | The expiry date of the card. |
event.isDefaultBoolean | Whether this is the default card. |
val clickOnceConfig = ClickOnceComponentConfig(
onOnceCardClick = { event ->
Log.d("Payment", "User selected saved card: ${event.maskedNumber}")
Log.d("Payment", "Card brand: ${event.cardBrand.displayName}")
// Update UI to show selected card
updateSelectedCardDisplay(event)
// Handle CVC requirement
if (event.requiresCvc) {
showCvcInput()
focusCvcField()
} else {
hideCvcInput()
enableSubmitButton()
}
// Track user preferences
analytics.track("saved_card_selected", mapOf(
"brand" to event.cardBrand.displayName,
"is_default" to event.isDefault,
"requires_cvc" to event.requiresCvc
))
}
)This callback is triggered when a country selection is cleared or changed.
You can use it to:
- Clear dependent form fields.
- Reset validation states.
- Update the form layout for a new country.
- Track user interaction patterns.
- Trigger field reconfiguration.
| Parameter | Description |
|---|---|
eventCountryDeselectionEvent | The country deselection event object. |
event.deselectedCountryCountry | The country that was deselected. |
event.reasonDeselectionReason | Why the deselection occurred. |
event.timestampLong | The date and time when the deselection occurred. |
event.previousSelectionMethodSelectionMethod? | How the previous selection was made. |
val countrySelectionConfig = CountrySelectionComponentConfig(
onCountryDeselected = { event ->
Log.d("Country", "Country deselected: ${event.deselectedCountry.name}")
Log.d("Country", "Reason: ${event.reason}")
// Clear dependent fields
when (event.deselectedCountry.code) {
"US" -> {
clearStateField()
clearZipCodeField()
hideStateSelector()
}
"CA" -> {
clearProvinceField()
clearPostalCodeField()
hideProvinceSelector()
}
"GB" -> {
clearCountyField()
clearPostcodeField()
}
}
// Reset address validation
resetAddressValidation()
// Update form layout
updateFormLayout(null)
// Track deselection analytics
analytics.track("country_deselected", mapOf(
"country" to event.deselectedCountry.code,
"reason" to event.reason.name,
"previous_method" to event.previousSelectionMethod?.name
))
}
)This callback is triggered when a country is selected by the user or programmatically.
You can use it to:
- Configure country-specific form fields.
- Update address validation rules.
- Show or hide region-specific fields.
- Update the currency and formatting.
- Track user location preferences.
| Parameter | Description |
|---|---|
eventCountrySelectionEvent | The country selection event object. |
event.selectedCountryCountry | The newly selected country. |
event.previousCountryCountry? | The previously selected country, if any. |
event.selectionMethodSelectionMethod | How the selection was made. |
event.timestampLong | The date and time when the selection occurred. |
event.userInitiatedBoolean | Whether the selection was user-initiated. |
val countrySelectionConfig = CountrySelectionComponentConfig(
onCountrySelected = { event ->
Log.d("Country", "Country selected: ${event.selectedCountry.name}")
Log.d("Country", "Selection method: ${event.selectionMethod}")
// Configure country-specific fields
when (event.selectedCountry.code) {
"US" -> {
showStateField()
showZipCodeField()
updatePhoneFormat(PhoneFormat.US)
setAddressValidation(USAddressValidator())
}
"CA" -> {
showProvinceField()
showPostalCodeField()
updatePhoneFormat(PhoneFormat.CA)
setAddressValidation(CanadaAddressValidator())
}
"GB" -> {
showCountyField()
showPostcodeField()
updatePhoneFormat(PhoneFormat.UK)
setAddressValidation(UKAddressValidator())
}
else -> {
configureGenericFields()
setAddressValidation(GenericAddressValidator())
}
}
// Update currency if needed
updateCurrencyForCountry(event.selectedCountry)
// Update form labels and placeholders
updateFormLabelsForCountry(event.selectedCountry)
// Track selection analytics
analytics.track("country_selected", mapOf(
"country" to event.selectedCountry.code,
"country_name" to event.selectedCountry.name,
"selection_method" to event.selectionMethod.name,
"user_initiated" to event.userInitiated,
"previous_country" to event.previousCountry?.code
))
// Announce selection for accessibility
announceToAccessibility("Selected ${event.selectedCountry.name}")
}
)This callback is triggered after 3DS authentication is completed.
You can use it to:
- Navigate to success or failure screens based on the authentication result.
- Display authentication-specific success or error messages.
- Log authentication metrics and performance data.
- Update the UI state after authentication completion.
- Trigger analytics events for authentication outcomes.
- Handle specific error scenarios with custom messaging.
| Parameter | Description |
|---|---|
authResultAuthenticationResult | The authentication result object. |
authResult.authenticationResultAuthenticationResult | The complete authentication result object. |
authResult.statusAuthenticationStatus | The authentication status. |
authResult.transactionIdString | The unique transaction identifier. |
authResult.authenticationValueString | The authentication value. |
authResult.eciString | The Electronic Commerce Indicator. |
authResult.processingTimeLong | The time taken for authentication, in milliseconds. |
authResult.threeDSVersionString | The version of 3DS protocol used. |
authResult.challengeTypeChallengeType | The type of challenge presented. |
authResult.issuerResponseIssuerResponse | The issuer response details. |
authResult.timestampLong | The time and date when authentication completed. |
val cardSubmitConfig = CardSubmitComponentConfig(
onPostAuthentication = { authResult ->
Log.d("Payment", "3DS authentication completed: $authResult")
when (authResult.status) {
AuthenticationStatus.AUTHENTICATED -> {
showMessage("Authentication successful", MessageType.SUCCESS)
}
AuthenticationStatus.FAILED -> {
showMessage("Authentication failed", MessageType.ERROR)
}
}
}
)This callback is triggered after a payment transaction has been processed and authorisation has been completed (successfully or unsuccessfully).
You can use it to:
- Navigate to success or failure screens based on the authorisation result.
- Display transaction-specific success or error messages.
- Store transaction details for order management.
- Trigger fulfillment processes for successful authorisations.
- Handle declined transactions with appropriate retry logic.
- Update inventory or reservation systems.
- Send confirmation emails or notifications.
| Parameter | Description |
|---|---|
resultAuthorisationResult | The authorisation result object. |
result.authorisationResultAuthorisationResult | The complete authorisation result object. |
result.stateAuthorisationState | The authorisation state. |
result.transactionIdString: Unique transaction identifier. | |
result.authCodeString | The authorisation code from the issuer. |
result.responseCodeString | The issuer response code. |
result.responseMessageString | A human-readable response message. |
result.processingTimeLong | The time taken for authorisation, in milliseconds. |
result.amountBigDecimal | The authorised amount. This may differ from the requested amount. |
result.currencyString | The transaction currency. |
result.cardDetailsMaskedCardDetails | The masked card details. |
result.merchantReferenceString | Your transaction reference. |
result.timestampLong | The time and date when authorisation completed. |
result.networkTransactionIdString | The network-specific transaction ID. |
val cardSubmitConfig = CardSubmitComponentConfig(
onPostAuthorisation = { result ->
when (result.state) {
AuthorisationState.AUTHORISED -> {
// Payment successful
showSuccessDialog("Payment successful!")
navigateToSuccessScreen()
}
AuthorisationState.DECLINED -> {
// Payment failed
showErrorDialog("Payment failed. Please try again.")
}
}
}
)This callback is triggered after 3DS authentication is initiated.
You can use it to:
- Display appropriate UI for the challenge type.
- Set up challenge window configuration and sizing.
- Show progress indicators for different authentication flows.
- Handle frictionless authentication completion.
- Implement fallback logic for failed authentication initiation.
- Track authentication performance and success rates.
| Parameter | Description |
|---|---|
authResultInitiationResult | The initiation result object. |
authResult.initiationResultInitiationResult | The authentication initiation result object. |
authResult.statusInitiationStatus | The initiation status. |
authResult.threeDSVersionString | The version of the 3DS protocol being used. |
authResult.transactionIdString | The unique transaction identifier. |
authResult.challengeTypeChallengeType | The type of challenge required. |
authResult.issuerAcsUrlString | The ACS URL for the challenge flow, if applicable. |
authResult.challengeWindowChallengeWindowSize | The preferred challenge window size. |
authResult.processingTimeLong | The time taken to initiate authentication. |
authResult.deviceFingerprintString | The device fingerprinting results. |
authResult.riskAssessmentRiskAssessment | The risk evaluation from the authentication service. |
authResult.timestampLong | The time and date when initiation completed. |
val cardSubmitConfig = CardSubmitComponentConfig(
onPostInitiateAuthentication = { authResult ->
Log.d("Payment", "3DS authentication initiated: $authResult")
when (authResult.status) {
AuthenticationStatus.CHALLENGE_REQUIRED -> {
showMessage("Please complete 3DS verification", MessageType.INFO)
}
}
}
)This callback is triggered after tokenisation completes with the tokenisation result.
You can use it to:
- Store the token reference for future payments.
- Update the customer payment methods list.
- Trigger a payment flow with the newly created token.
- Handle tokenisation errors with appropriate messaging.
- Track tokenisation success rates and performance.
- Implement card-on-file enrollment flows.
| Parameter | Description |
|---|---|
tokenDataTokenisationResult | The tokenisation result object. |
tokenData.tokenisationResultTokenisationResult | The complete tokenisation result object. |
tokenData.successBoolean | Whether tokenisation was successful. |
tokenData.tokenCardToken? | Information about the generated token, if tokenisation was successful. |
tokenData.tokenIdString? | The unique token identifier. |
tokenData.maskedPANString? | The masked Primary Account Number (PAN). |
tokenData.cardBrandCardBrand | The detected card brand. |
tokenData.expiryDateString? | The card expiry date. |
tokenData.binRangeString | The Bank Identification Number (BIN) range. |
tokenData.issuerInfoIssuerInfo | The card issuer details. |
tokenData.processingTimeLong | The time taken for tokenisation, in milliseconds. |
tokenData.vaultProviderString | Token vault provider information. |
tokenData.errorDetailsErrorDetails? | Error information, if tokenisation failed. |
tokenData.timestampLong | The time and date when tokenisation completed. |
val newCardConfig = NewCardComponentConfig(
onPostTokenisation = { tokenData ->
Log.d("Payment", "Card tokenised: $tokenData")
// Store token reference or proceed to payment
handleTokenisation(tokenData)
}
)This callback is triggered before 3DS authentication starts.
You can use it to:
- Configure authentication parameters based on transaction risk.
- Set custom timeout values for different customer segments.
- Add merchant-specific authentication data.
- Implement risk-based authentication logic.
- Customise the authentication flow based on the card type.
- Set up analytics tracking for authentication attempts.
| Parameter | Description |
|---|---|
preAuthDataPreAuthenticationData | The pre-authentication data object. |
preAuthData.transactionAmountBigDecimal | The amount being authenticated. |
preAuthData.currencyString | The transaction currency. |
preAuthData.merchantInfoMerchantInfo | The merchant identification details. |
preAuthData.cardDetailsMaskedCardDetails | The masked card information for authentication. |
preAuthData.customerInfoCustomerData? | The customer data available for authentication. |
preAuthData.deviceFingerprintString | The device fingerprinting data. |
preAuthData.riskDataRiskData | The risk assessment data. |
preAuthData.threeDSMethodString? | The 3DS method data, if available. |
preAuthData.timestampLong | The time and date when pre-authentication started. |
val cardSubmitConfig = CardSubmitComponentConfig(
onPreAuthentication = { preAuthData ->
Log.d("Payment", "Starting 3DS authentication: $preAuthData")
return@CardSubmitComponentConfig AuthenticationConfig(
acquirerProfileId = "your-acquirer-id",
providerId = "your-provider-id",
timeout = 30
)
}
)This callback is triggered before the transaction authorisation. Return the transaction data.
You can use it to:
- Add address verification (AVV) data for reduced interchange.
- Include additional your own specific authorisation data.
- Set custom authorisation parameters based on risk assessment.
- Add Level 2/Level 3 processing data for commercial cards.
- Include customer loyalty or membership information.
- Set specific authorisation modes (e.g., pre-auth vs sale).
| Parameter | Description |
|---|---|
dataPreAuthorisationData | The pre-authorisation data object. |
data.transactionRequestTransactionRequest | The initial transaction request details. |
data.amountBigDecimal | The transaction amount to be authorised. |
data.currencyString | The transaction currency. |
data.cardDetailsMaskedCardDetails | The masked card information for authorisation. |
data.merchantInfoMerchantInfo | Merchant identification and configuration. |
data.customerDataCustomerData? | The available customer information. |
data.billingAddressAddress? | The customer's billing address, if provided. |
data.shippingAddressAddress? | The customer's shipping address, if provided. |
data.orderDetailsOrderDetails? | Order line items and metadata. |
data.riskDataRiskData | The risk assessment and fraud detection data. |
data.authenticationResultAuthenticationResult? | The 3DS authentication result, if performed. |
data.timestampLong | The time and date when pre-authorisation started. |
val cardSubmitConfig = CardSubmitComponentConfig(
onPreAuthorisation = { data ->
Log.d("Payment", "Pre-authorisation data: $data")
return@CardSubmitComponentConfig AuthorisationData(
addressVerification = AddressVerification(
countryCode = "US",
houseNumberOrName = "123 Main St",
postalCode = "12345"
)
)
}
)This callback is triggered before a token deletion attempt, allowing you to show custom confirmation dialogs.
You can use it to:
- Show custom confirmation dialogs with token-specific information.
- Validate business rules before allowing deletion.
- Check for any active subscriptions or recurring payments.
- Implement different confirmation flows based on token usage.
- Prevent accidental deletion of important payment methods.
- Handle the cleanup of related data and preferences.
| Parameter | Description |
|---|---|
tokenCardToken | The card token object. |
token.tokenCardToken | The complete token information to be deleted. |
token.tokenIdString | The unique identifier of the token. |
token.maskedPANString | The masked Primary Account Number (PAN). |
token.cardBrandCardBrand | The card brand/scheme. |
token.expiryDateString | The card expiry date. |
token.isDefaultBoolean | Whether this is the default payment method. |
token.usageCountInt | The number of times this token has been used. |
token.lastUsedLong? | The date and time when the token was last used. |
token.isLinkedToSubscriptionsBoolean | Whether the token is used for recurring payments. |
token.linkedSubscriptionsList<Subscription> | The list of active subscriptions using this token. |
token.customerDataCustomerData? | Customer information for context. |
token.deletionReasonDeletionReason | The reason for deletion. |
token.canRecoverBoolean | Whether deletion can be undone. |
token.timestampLong | The time and date when deletion was requested. |
val cardOnFileConfig = CardOnFileComponentConfig(
onPreDeleteToken = { token ->
val confirmed = showConfirmationDialog(
title = "Delete Card",
message = "Delete card ending in ${token.maskedPrimaryAccountNumber.takeLast(4)}?",
positiveButton = "Delete",
negativeButton = "Cancel"
)
return@CardOnFileComponentConfig confirmed
}
)This callback is triggered before 3DS authentication. Return the authorisation data if you want to proceed with pre-initiating an authentication, null otherwise.
You can use it to:
- Configure 3DS parameters based on the transaction risk and amount.
- Set your own specific authentication data and preferences.
- Determine the appropriate 3DS version based on issuer support.
- Add customer context for an improved authentication experience.
- Implement conditional authentication based on business rules.
- Set challenge preferences based on the customer segment.
| Parameter | Description |
|---|---|
dataPreInitiationData | The pre-initiation data object. |
data.transactionDataTransactionData | The transaction information for authentication. |
data.cardDetailsMaskedCardDetails | The masked card information. |
data.amountBigDecimal | The transaction amount. |
data.currencyString | The transaction currency. |
data.merchantInfoMerchantInfo | Merchant configuration and settings. |
data.customerDataCustomerData? | Available customer information for authentication. |
data.deviceDataDeviceData | The device fingerprinting and browser information. |
data.riskDataRiskData | The initial risk assessment results. |
data.billingAddressAddress? | The customer's billing address. |
data.previousAuthAttemptsList<AuthAttempt> | The history of authentication attempts for this transaction. |
data.timestampLong | The time and date when pre-initiation started. |
val cardSubmitConfig = CardSubmitComponentConfig(
onPreInitiateAuthentication = {
return@CardSubmitComponentConfig AuthenticationInitData(
threeDSecureVersion = "2.1.0",
merchantName = "Your Store"
)
}
)This callback is triggered before card tokenisation. To proceed, return true. To cancel, return false.
You can use it to:
- Validate business rules before allowing tokenisation.
- Check for duplicate cards already on file.
- Implement customer limits on saved payment methods.
- Verify customer eligibility for card-on-file services.
- Apply tokenisation policies based on the card type or issuer.
- Confirm user consent for saving payment information.
| Parameter | Description |
|---|---|
dataPreTokenisationData | The pre-tokenisation data object. |
data.cardDataMaskedCardData | The card information to be tokenised. This is masked for security purposes. |
data.cardBrandCardBrand | The detected card brand. |
data.binRangeString | The Bank Identification Number (BIN) range. |
data.issuerInfoIssuerInfo | The card issuer information. |
data.cardTypeCardType | The type of card. |
data.customerDataCustomerData? | The available customer information. |
data.tokenisationMethodTokenisationMethod | The method of tokenisation. |
data.networkTokenSupportBoolean | Whether network tokenisation is supported. |
data.existingTokensList<CardToken> | The list of existing tokens for this customer. |
data.duplicateCheckDuplicateCheckResult | The results of the duplicate card check. |
data.timestampLong | The time and date when pre-tokenisation started. |
val cardSubmitConfig = CardSubmitComponentConfig(
onPreTokenisation = {
Log.d("Payment", "About to tokenise card")
// Check business rules before tokenisation
return@CardSubmitComponentConfig validateBusinessRules()
}
)This callback is triggered after tokens are retrieved but before they're rendered, allowing filtering and transformation.
You can use it to:
- Filter tokens based on business rules or customer preferences.
- Transform token display labels with custom formatting.
- Apply security policies (e.g., require CVC for certain cards).
- Sort tokens by usage patterns or customer preferences.
- Add custom metadata or styling to specific token types.
- Implement token visibility rules based on customer tier.
| Parameter | Description |
|---|---|
responseTokenRetrievalResponse | The token retrieval response object. |
response.tokenResponseTokenRetrievalResponse | The complete token retrieval response. |
response.gatewayTokensList<GatewayToken> | The list of gateway tokens retrieved. |
response.networkTokensList<NetworkToken>? | The list of network tokens, if available. |
response.retrievalTimeLong | The time taken to retrieve the tokens. |
response.customerDataCustomerData? | Customer information associated with the tokens. |
response.tokenMetadataMap<String, Any>? | Additional metadata for each token. |
response.filterCriteriaFilterCriteria? | Any applied filter criteria. |
response.sortOptionsSortOptions? | The current sorting preferences. |
response.securityContextSecurityContext | The security context for token access. |
response.timestampLong | Time and date when token retrieval was completed. |
val cardOnFileConfig = CardOnFileComponentConfig(
onPreRenderTokens = { response ->
// Filter only Visa cards and mark them as requiring CVC
return@CardOnFileComponentConfig response.gatewayTokens
.filter { token -> token.scheme == CardBrand.VISA }
.map { token ->
CardTokenMapping(
id = token.gatewayTokenId,
isCvcRequired = true,
displayLabel = buildCustomLabel(token)
)
}
}
)This callback is triggered when the initial request to fetch saved card tokens fails.
You can use it to:
- Display appropriate error messages based on the error type.
- Implement retry logic with exponential backoff.
- Fallback to alternative token sources or manual entry.
- Track token retrieval failures for service monitoring.
- Provide an offline mode or cached token access.
- Guide users through error resolution steps.
| Parameter | Description |
|---|---|
responseErrorResponse | The error response object. |
response.errorResponseErrorResponse | The complete error response object. |
response.errorCodeString | The specific error code from the service. |
response.errorMessageString | A human-readable error message. |
response.errorCategoryErrorCategory | The category of error. |
response.retryableBoolean | Whether the operation can be retried. |
response.attemptNumberInt | The number of retry attempts made. |
response.requestTimeLong | The time taken for the failed request. |
response.customerDataCustomerData? | Customer information for context. |
response.requestIdString | The unique identifier for the failed request. |
response.timestampLong | The time and date when the failure occurred. |
val cardOnFileConfig = CardOnFileComponentConfig(
onRetrieveTokensFailed = { response ->
Log.e("Payment", "Failed to retrieve tokens: ${response.error}")
// Show fallback UI or error message
showErrorMessage("Unable to load saved cards. Please try again.")
}
)This callback is triggered when submission fails. For example, due to network issues or a payment processor rejection.
You can use it to:
- Show user-friendly error messages.
- Implement retry logic for transient failures.
- Track submission failure rates.
- Log errors for debugging.
- Provide alternative payment methods.
| Parameter | Description |
|---|---|
errorSdkException | The error object. |
error.errorSdkException | The error details and error code. |
error.errorMessageString | A human-readable error message. |
error.errorTypeErrorType | The type of error (network, validation, server, etc.). |
error.retryableBoolean | Whether the operation can be retried. |
error.submissionDataString? | The data that was being submitted (sanitised). |
val cardSubmitConfig = CardSubmitComponentConfig(
onSubmitError = { error ->
Log.e("Payment", "Submit error: ${error.message}")
Log.e("Payment", "Error type: ${error.errorType}")
Log.e("Payment", "Retryable: ${error.retryable}")
// Show appropriate error message based on error type
val userMessage = when (error.errorType) {
ErrorType.NETWORK -> "Network error. Please check your connection and try again."
ErrorType.SERVER -> "Server error. Please try again in a moment."
ErrorType.VALIDATION -> "Please check your payment information and try again."
ErrorType.PAYMENT_DECLINED -> "Payment was declined. Please try a different card."
ErrorType.AUTHENTICATION -> "Authentication failed. Please verify your details."
else -> "Payment failed. Please try again."
}
showErrorDialog(userMessage)
// Enable retry if appropriate
if (error.retryable) {
showRetryOption {
retryPaymentSubmission()
}
} else {
// Suggest alternative payment methods
showAlternativePaymentOptions()
}
// Track submission errors
analytics.track("payment_submission_failed", mapOf(
"error_code" to error.code,
"error_type" to error.errorType.name,
"retryable" to error.retryable,
"payment_method" to getCurrentPaymentMethod()
))
handlePaymentError(error)
}
)This callback is triggered when updating card token information (e.g., expiry date) fails.
You can use it to:
- Display specific error messages based on the failure type.
- Implement retry logic for transient failures.
- Revert UI changes if the update fails.
- Guide users through correcting invalid data.
- Track update failure patterns for service improvement.
- Provide fallback options for critical updates.
| Parameter | Description |
|---|---|
responseUpdateFailureResponse | The update failure response object. |
response.updateResponseUpdateFailureResponse | The complete update failure response. |
response.tokenIdString | The ID of the token that failed to update. |
response.updateFieldsList<String> | The fields that were being updated. |
response.errorCodeString | The specific error code from the service. |
response.errorMessageString | A human-readable error message. |
response.errorCategoryErrorCategory | The category of error. |
response.retryableBoolean | Whether the update operation can be retried. |
response.originalTokenCardToken | The original token data before the update attempt. |
response.attemptedChangesMap<String, Any> | The changes that were attempted. |
response.timestampLong | The time and date when the update failure occurred. |
val cardOnFileConfig = CardOnFileComponentConfig(
onUpdateTokenFailed = { response ->
Log.e("Payment", "Token update failed: ${response.error}")
// Show error message to user
showErrorMessage("Failed to update card information")
}
)This callback is triggered when card token information is successfully updated.
You can use it to:
- Update the UI display with new token information.
- Show success confirmation to user.
- Trigger token synchronisation across user devices.
- Update the cached token data locally.
- Track successful update patterns for analytics.
- Refresh related payment components.
| Parameter | Description |
|---|---|
responseUpdateSuccessResponse | The update success response object. |
response.updateResponseUpdateSuccessResponse | The complete update success response. |
response.tokenIdString | The ID of the successfully updated token. |
response.updatedTokenCardToken | The updated token with new information. |
response.updatedFieldsList<String> | The list of fields that were successfully updated. |
response.previousValuesMap<String, Any> | The previous values before the update. |
response.updateTimeLong | The time taken to complete the update. |
response.networkTokenBoolean | Whether this token has network tokenisation. |
response.vaultProviderString | The token vault provider information. |
response.synchronizedDevicesList<String> | The list of devices where the token was synchronised. |
response.timestampLong | The time and date when the update completed. |
val cardOnFileConfig = CardOnFileComponentConfig(
onUpdateTokenSuccess = { response ->
Log.d("Payment", "Token updated successfully: $response")
// Refresh display with new information
showSuccessMessage("Card information updated")
refreshCardDisplay()
}
)This callback is triggered when form validation occurs across multiple fields.
You can use it to:
- Display comprehensive validation feedback,
- Enable or disable submit buttons.
- Track form completion progress.
- Implement multi-field validation rules.
- Provide summary error messages.
| Parameter | Description |
|---|---|
validationEventValidationEvent | The validation event object. |
validationEvent.validationResultsList<ValidationResult> | The list of validation results for all fields. |
validationEvent.isFormValidBoolean | Whether the entire form is valid. |
validationEvent.fieldCountInt | The number of fields validated. |
validationEvent.errorCountInt | The number of fields with errors. |
validationEvent.triggerSourceValidationTrigger | What triggered the validation. |
val newCardConfig = NewCardComponentConfig(
onValidation = { validationEvent ->
Log.d("Payment", "Form validation triggered")
Log.d("Payment", "Form valid: ${validationEvent.isFormValid}")
Log.d("Payment", "Error count: ${validationEvent.errorCount}")
// Update submit button state
updateSubmitButtonState(validationEvent.isFormValid)
// Process each field's validation result
validationEvent.validationResults.forEach { result ->
if (!result.isValid && result.errors.isNotEmpty()) {
result.errors.forEach { error ->
Log.e("Validation", "${result.fieldName}: ${error.message} (${error.code})")
showFieldError(result.fieldName, error.message)
}
} else {
clearFieldError(result.fieldName)
}
}
// Track validation metrics
analytics.track("form_validation", mapOf(
"is_valid" to validationEvent.isFormValid,
"error_count" to validationEvent.errorCount,
"field_count" to validationEvent.fieldCount,
"trigger_source" to validationEvent.triggerSource
))
}
)This callback is triggered when validation fails for a single field.
You can use it to:
- Show field-specific error messages.
- Update the field styling to indicate errors.
- Disable form submission.
- Guide the user to correct input.
- Track validation failure patterns.
| Parameter | Description |
|---|---|
validationResultValidationResult | The validation result object. |
validationResult.fieldNameString | The name of the field that failed validation. |
validationResult.valueString | The current field value. |
validationResult.errorsList<ValidationFieldError> | The list of validation errors. |
validationResult.errorCodesList<String> | The specific error codes. |
validationResult.validationTriggerValidationTrigger | What triggered the validation. |
val cardNumberConfig = CardNumberComponentConfig(
onValidationFailed = { validationResult ->
Log.e("Validation", "Card number validation failed")
Log.e("Validation", "Errors: ${validationResult.errors}")
// Show specific error message
val primaryError = validationResult.errors.firstOrNull()
if (primaryError != null) {
showFieldError("card_number", primaryError.message)
}
// Update field appearance
updateFieldAppearance("card_number", hasError = true)
// Disable submit if needed
if (validationResult.blockSubmission) {
disableSubmitButton()
}
// Track validation failures
analytics.track("field_validation_failed", mapOf(
"field" to "card_number",
"error_codes" to validationResult.errors.map { it.code },
"trigger" to validationResult.validationTrigger
))
}
)This callback is triggered when validation is successful for a single field.
You can use it to:
- Clear error messages and styling.
- Enable progression to the next field.
- Update form completion progress.
- Enable the submit button when all fields valid.
- Provide positive feedback.
| Parameter | Description |
|---|---|
validationResultValidationResult | The validation result object. |
validationResult.fieldNameString | The name of the field that passed validation. |
validationResult.valueString | The current valid field value. |
validationResult.validationDurationLong | The time taken for validation in milliseconds. |
validationResult.validationTriggerValidationTrigger | What triggered the validation. |
val cardNumberConfig = CardNumberComponentConfig(
onValidationPassed = { validationResult ->
Log.d("Validation", "Card number validation passed")
Log.d("Validation", "Value: ${validationResult.value}")
// Clear any error states
clearFieldError("card_number")
// Update field appearance to show success
updateFieldAppearance("card_number", hasError = false, isValid = true)
// Check if all fields are now valid
if (isFormValid()) {
enableSubmitButton()
}
// Auto-advance to next field if configured
if (shouldAutoAdvance()) {
focusNextField("expiry_date")
}
// Track successful validation
analytics.track("field_validation_passed", mapOf(
"field" to "card_number",
"validation_duration" to validationResult.validationDuration,
"trigger" to validationResult.validationTrigger
))
}
)This callback is called when each token item's user interface is being constructed, allowing you to customise the layout.
You can use it to:
- Create custom layouts for token display items.
- Implement brand-specific styling and theming.
- Add interactive elements and action buttons.
- Display custom metadata or promotional information.
- Implement accessibility enhancements.
- Add animations and visual feedback.
| Parameter | Description |
|---|---|
elementIdsTokenElementIds | The token element IDs object. |
elementIds.elementIdsTokenElementIds | The UI element identifiers and references for the token. |
elementIds.tokenDataCardToken | The complete token information for customisation. |
elementIds.displayStateDisplayState | The current display state. |
elementIds.interactionStateInteractionState | The user interaction state. |
elementIds.tokenImageIdString | The image resource or URL for the card brand. |
elementIds.tokenLabelIdString | The formatted display label for the token. |
elementIds.expiryDateIdString | The formatted expiry date string. |
elementIds.isDefaultBoolean | Whether this token is the default payment method. |
elementIds.isExpiredBoolean | Whether the token has expired. |
elementIds.metadataMap<String, Any>? | Additional metadata for customisation. |
elementIds.availableActionsList<TokenAction> | The list of actions available for this token. |
val cardOnFileConfig = CardOnFileComponentConfig(
tokenItemBuilder = { elementIds ->
// Return a custom Composable for the token item
@Composable
fun CustomTokenItem() {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Card brand image
AsyncImage(
model = elementIds.tokenImageId,
contentDescription = "Card brand",
modifier = Modifier.size(32.dp)
)
Spacer(modifier = Modifier.width(12.dp))
// Card label
Text(
text = elementIds.tokenLabelId,
modifier = Modifier.weight(1f),
style = MaterialTheme.typography.bodyMedium
)
// Expiry date
Text(
text = elementIds.expiryDateId,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.width(8.dp))
// Action buttons
Row {
IconButton(onClick = { /* Edit action */ }) {
Icon(Icons.Default.Edit, contentDescription = "Edit")
}
IconButton(onClick = { /* Delete action */ }) {
Icon(Icons.Default.Delete, contentDescription = "Delete")
}
}
}
}
return@CardOnFileComponentConfig CustomTokenItem()
}
)This callback is called to generate the display text for each card token. It replaces the default masked card number.
You can use it to:
- Customise the token display format.
- Add issuer-specific information.
- Implement localised display.
- Include expiry information.
- Show token metadata.
| Parameter | Description |
|---|---|
tokenCardToken | The card token object. |
token.tokenCardToken | The card token object. |
token.maskedNumberString | The masked card number. |
token.schemeString | The card scheme/brand. |
token.issuerNameString | The name of the card issuer. |
token.expiryDateString | The card expiry date. |
val cardOnFileConfig = CardOnFileComponentConfig(
tokenLabelBuilder = { token ->
"${token.scheme.name} •••• ${token.maskedPrimaryAccountNumber.takeLast(4)} - ${token.issuerName}"
}
)This callback is triggered when the consent state changes.
You can use it to:
- Update the UI based on consent state.
- Enable or disable related features.
- Track consent compliance.
- Validate form submission.
- Update user preferences.
- Trigger legal compliance checks.
| Parameter | Description |
|---|---|
eventConsentChangedEvent | The consent changed event object. |
event.consentTypeConsentType | The type of consent being changed. |
event.previousStateBoolean | The previous checkbox state. |
event.newStateBoolean | The new checkbox state. |
event.userInitiatedBoolean | Whether the change was user-initiated. |
event.timestampLong | Time and date when the change occurred. |
event.metadataMap<String, Any>? | Additional consent metadata. |
val cardConsentConfig = CardConsentComponentConfig(
onConsentChanged = { event ->
Log.d("Consent", "Consent changed: ${event.consentType}")
Log.d("Consent", "Previous: ${event.previousState}, New: ${event.newState}")
when (event.consentType) {
ConsentType.SAVE_CARD -> {
if (event.newState == ConsentState.CHECKED) {
enableCardSaving()
showCardSavingBenefits()
} else {
disableCardSaving()
hideCardSavingOptions()
}
}
ConsentType.MARKETING_EMAILS -> {
updateMarketingPreferences(event.newState == ConsentState.CHECKED)
}
ConsentType.BIOMETRIC_AUTH -> {
if (event.newState == ConsentState.CHECKED) {
requestBiometricSetup()
} else {
disableBiometricAuth()
}
}
}
// Update form validation
validateFormConsent()
// Track consent analytics
analytics.track("consent_changed", mapOf(
"consent_type" to event.consentType.name,
"previous_state" to event.previousState.name,
"new_state" to event.newState.name,
"user_initiated" to event.userInitiated
))
}
)This callback is triggered when a terms and conditions link is clicked.
You can use it to:
- Track user engagement with legal documents.
- Customise the terms and conditions' presentation.
- Implement custom browser behaviour.
- Log legal compliance events.
- Provide accessibility support.
| Parameter | Description |
|---|---|
eventTermsClickedEvent | The terms clicked event object. |
event.termsUrlString | The URL of the terms document. |
event.clickLocationPoint | The click coordinates on screen. |
event.openModeOpenMode | How the terms should be opened. |
event.timestampLong | When the click occurred. |
event.linkTextString | The text of the clicked link. |
val cardConsentConfig = CardConsentComponentConfig(
onTermsClicked = { event ->
Log.d("Legal", "Terms clicked: ${event.termsUrl}")
Log.d("Legal", "Open mode: ${event.openMode}")
when (event.openMode) {
LinkOpenMode.IN_APP_BROWSER -> {
openInAppBrowser(event.termsUrl)
}
LinkOpenMode.MODAL_DIALOG -> {
showTermsModal(event.termsUrl)
}
LinkOpenMode.BOTTOM_SHEET -> {
showTermsBottomSheet(event.termsUrl)
}
else -> {
openExternalBrowser(event.termsUrl)
}
}
// Track legal document engagement
analytics.track("terms_clicked", mapOf(
"url" to event.termsUrl,
"open_mode" to event.openMode.name,
"link_text" to event.linkText
))
}
)This callback is triggered when a privacy link is clicked.
You can use it to:
- Track privacy policy engagement.
- Customise the privacy policy's presentation.
- Implement custom browser behaviour.
- Log privacy compliance events.
- Support GDPR requirements.
| Parameter | Description |
|---|---|
eventPrivacyClickedEvent | The privacy clicked event object. |
event.privacyUrlString | The URL of the privacy policy. |
event.clickLocationPoint | The click coordinates on screen. |
event.openModeOpenMode | How the privacy policy should be opened. |
event.timestampLong | When the click occurred. |
event.linkTextString | The text of the clicked link. |
val cardConsentConfig = CardConsentComponentConfig(
onPrivacyClicked = { event ->
Log.d("Privacy", "Privacy policy clicked: ${event.privacyUrl}")
Log.d("Privacy", "Open mode: ${event.openMode}")
// Handle privacy policy display
when (event.openMode) {
LinkOpenMode.IN_APP_BROWSER -> {
openInAppBrowser(event.privacyUrl)
}
LinkOpenMode.MODAL_DIALOG -> {
showPrivacyModal(event.privacyUrl)
}
LinkOpenMode.BOTTOM_SHEET -> {
showPrivacyBottomSheet(event.privacyUrl)
}
else -> {
openExternalBrowser(event.privacyUrl)
}
}
// Track privacy engagement for compliance
analytics.track("privacy_clicked", mapOf(
"url" to event.privacyUrl,
"open_mode" to event.openMode.name,
"link_text" to event.linkText,
"compliance_required" to isGDPRRequired()
))
}
)This callback is triggered when the pre-fill billing address checkbox state changes.
You can use it to:
- Update UI based on state.
- Trigger address synchronisation.
- Track user interactions.
- Validate form state.
- Enable/disable related components.
| Parameter | Description |
|---|---|
eventStateChangedEvent | The state changed event object. |
event.previousStateBoolean | The previous checkbox state. |
event.newStateBoolean | The new checkbox state. |
event.userInitiatedBoolean | Whether the change was user-initiated. |
event.timestampLong | When the change occurred. |
event.hasShippingAddressBoolean | Whether the shipping address is available. |
val preFillCheckboxConfig = PreFillBillingAddressCheckboxComponentConfig(
onStateChanged = { event ->
Log.d("PreFill", "Checkbox state changed: ${event.previousState} -> ${event.newState}")
Log.d("PreFill", "User initiated: ${event.userInitiated}")
when (event.newState) {
CheckboxState.CHECKED -> {
if (event.hasShippingAddress) {
// Start address synchronisation
startAddressSync()
showSyncProgress()
} else {
// Show message about needing shipping address first
showMissingShippingAddressMessage()
// Revert checkbox state
revertCheckboxState()
}
}
CheckboxState.UNCHECKED -> {
// Clear billing address form
clearBillingAddressForm()
enableBillingAddressEditing()
hideSyncProgress()
}
}
// Update form validation
validateBillingAddressForm()
// Track interaction analytics
analytics.track("prefill_state_changed", mapOf(
"previous_state" to event.previousState.name,
"new_state" to event.newState.name,
"user_initiated" to event.userInitiated,
"has_shipping_address" to event.hasShippingAddress
))
}
)This callback is triggered when address synchronisation starts.
You can use it to:
- Show loading indicators.
- Track sync performance.
- Update the progress UI.
- Log sync operations.
- Handle concurrent sync requests.
| Parameter | Description |
|---|---|
eventAddressSyncEvent | The address sync event object. |
event.sourceAddressAddress | The shipping address being copied. |
event.targetComponentIdString | The ID of the billing address component. |
event.syncedFieldsList<String> | The list of fields being synchronised. |
event.syncStrategySyncStrategy | The strategy used for synchronisation. |
event.startTimeLong | When the sync operation started. |
val preFillCheckboxConfig = PreFillBillingAddressCheckboxComponentConfig(
onAddressSync = { event ->
Log.d("AddressSync", "Starting address sync")
Log.d("AddressSync", "Source: ${event.sourceAddress}")
Log.d("AddressSync", "Strategy: ${event.syncStrategy}")
Log.d("AddressSync", "Fields: ${event.syncedFields.joinToString()}")
// Show loading UI
showAddressSyncLoading()
// Update progress indicators
updateSyncProgress(0, event.syncedFields.size)
// Track sync start
analytics.track("address_sync_started", mapOf(
"sync_strategy" to event.syncStrategy.name,
"field_count" to event.syncedFields.size,
"target_component" to event.targetComponentId
))
// Log for debugging
logSyncOperation("started", event)
}
)This callback is triggered when address synchronisation completes.
You can use it to:
- Hide loading indicators.
- Update the billing form fields.
- Show success feedback.
- Enable form submission.
- Cache sync results.
- Handle sync errors.
| Parameter | Description |
|---|---|
eventSyncCompleteEvent | The sync complete event object. |
event.sourceAddressAddress | The original shipping address. |
event.targetAddressAddress | The resulting billing address. |
event.syncedFieldsList<String> | Fields that were synchronised. |
event.syncDurationLong | Time taken for sync operation. |
event.successBoolean | Whether sync completed successfully. |
event.errorsList<String>? | Any errors that occurred during sync. |
val preFillCheckboxConfig = PreFillBillingAddressCheckboxComponentConfig(
onSyncComplete = { event ->
Log.d("AddressSync", "Address sync completed")
Log.d("AddressSync", "Success: ${event.success}")
Log.d("AddressSync", "Duration: ${event.syncDuration}ms")
// Hide loading UI
hideAddressSyncLoading()
if (event.success) {
// Update billing form with synced data
updateBillingAddressForm(event.targetAddress)
// Show success feedback
showSyncSuccessMessage()
// Disable billing address editing
disableBillingAddressEditing()
// Enable form submission
enableFormSubmission()
} else {
// Handle sync errors
handleSyncErrors(event.errors)
// Show error message
showSyncErrorMessage()
// Revert checkbox state
revertCheckboxState()
// Re-enable editing
enableBillingAddressEditing()
}
// Track completion analytics
analytics.track("address_sync_completed", mapOf(
"success" to event.success,
"duration" to event.syncDuration,
"field_count" to event.syncedFields.size,
"error_count" to event.errors?.size ?: 0
))
// Log for debugging
logSyncOperation("completed", event)
}
)This callback is triggered when individual field validation is performed.
You can use it to:
- Show real-time validation feedback.
- Implement custom validation logic.
- Update field-specific error messages.
- Track validation performance.
- Provide accessibility announcements.
- Enable progressive form completion.
| Parameter | Description |
|---|---|
validationResultFieldValidationResult | The field validation result object. |
validationResult.fieldNameString | The name of the field being validated. |
validationResult.isValidBoolean | Whether the field passed validation. |
validationResult.errorCodeString? | The specific error code, if validation failed. |
validationResult.errorMessageString? | A human-readable error message. |
validationResult.validationTypeValidationType | The type of validation performed. |
The following snippet is an example of how you might handle a declined transaction when using the card submit component.
val cardSubmitConfig = CardSubmitComponentConfig(
onPostAuthorisation = { submitResult ->
Log.d("Payment", "Transaction result: $submitResult")
when (submitResult.status) {
TransactionStatus.REFUSED -> {
// Transaction was declined by the issuer or payment provider
val refusedResult = submitResult as RefusedSubmitResult
handleDeclinedTransaction(refusedResult)
}
TransactionStatus.AUTHORISED -> {
// Transaction was successful
handleSuccessfulTransaction(submitResult)
}
TransactionStatus.ERROR -> {
// System error occurred
handleSystemError(submitResult)
}
TransactionStatus.PENDING -> {
// Transaction is pending further processing
handlePendingTransaction(submitResult)
}
}
}
)
private fun handleDeclinedTransaction(refusedResult: RefusedSubmitResult) {
// Extract decline information
val stateData = refusedResult.stateData
val providerResponse = refusedResult.providerResponse
val fundingData = refusedResult.fundingData
// Log decline details
Log.w("Payment", "Transaction declined: " +
"stateCode=${stateData?.code}, " +
"stateMessage=${stateData?.message}, " +
"providerCode=${providerResponse?.code}, " +
"providerMessage=${providerResponse?.message}"
)
// Show user-friendly error message based on decline reason
val declineCode = providerResponse?.code
val userMessage = when (declineCode) {
"INSUFFICIENT_FUNDS" -> getString(R.string.error_insufficient_funds)
"EXPIRED_CARD" -> getString(R.string.error_expired_card)
"INVALID_CVV" -> getString(R.string.error_invalid_cvv)
"CARD_BLOCKED" -> getString(R.string.error_card_blocked)
"LIMIT_EXCEEDED" -> getString(R.string.error_limit_exceeded)
else -> {
// Use merchant advice if available
providerResponse?.merchantAdvice?.message
?: getString(R.string.error_payment_declined_generic)
}
}
// Display error to user
showErrorDialog(userMessage)
// Check if retry is recommended
when (providerResponse?.merchantAdvice?.code) {
"RETRY" -> enableRetryOption()
"DO_NOT_RETRY" -> {
disableRetryOption()
suggestAlternativePayment()
}
}
// Log analytics event for declined transaction
analytics.track("payment_declined", mapOf(
"decline_code" to declineCode,
"state_code" to stateData?.code
))
}
private fun showErrorDialog(message: String) {
AlertDialog.Builder(this)
.setTitle(getString(R.string.payment_failed))
.setMessage(message)
.setPositiveButton(getString(R.string.ok)) { dialog, _ ->
dialog.dismiss()
}
.show()
}The following snippet is an example of how you might handle authentication problems when using the card submit component.
val cardSubmitConfig = CardSubmitComponentConfig(
onSubmitError = { error ->
Log.e("Payment", "Submit error occurred: $error")
// Handle different types of authentication and processing errors
when (error.errorCode) {
"SDK0503" -> { // Transaction authentication rejected
showErrorDialog(getString(R.string.error_authentication_rejected))
enableRetryOption()
}
"SDK0505" -> { // Transaction authentication failed
showErrorDialog(getString(R.string.error_authentication_failed))
enableRetryOption()
}
"SDK0504" -> { // SCA exemption required
showErrorDialog(getString(R.string.error_sca_exemption_required))
// Handle SCA exemption flow
handleScaExemption()
}
else -> {
// Generic error handling
showErrorDialog(getString(R.string.error_payment_processing_failed))
logError(error)
}
}
}
)
private fun handleScaExemption() {
// Implement SCA exemption handling logic
// This might involve redirecting to a different flow or
// requesting additional authentication
}
private fun enableRetryOption() {
// Enable retry button or show retry dialog
showRetryDialog()
}
private fun showRetryDialog() {
AlertDialog.Builder(this)
.setTitle(getString(R.string.payment_failed))
.setMessage(getString(R.string.retry_payment_question))
.setPositiveButton(getString(R.string.retry)) { _, _ ->
retryPayment()
}
.setNegativeButton(getString(R.string.cancel)) { dialog, _ ->
dialog.dismiss()
}
.show()
}
private fun retryPayment() {
// Reset form and allow user to retry
clearFormErrors()
enablePaymentForm()
}Here's a complete example showing how to set up a payment flow with comprehensive event handling:
class PaymentActivity : ComponentActivity() {
private lateinit var pxpCheckout: PxpCheckout
private lateinit var newCardComponent: NewCardComponent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupPxpCheckout()
setupNewCardComponent()
setContent {
PaymentScreen()
}
}
private fun setupPxpCheckout() {
val sdkConfig = PxpSdkConfig(
environment = Environment.TEST,
session = SessionConfig(
sessionId = "your_session_id",
sessionData = "your_session_data"
),
transactionData = TransactionData(
amount = 100.0,
currency = CurrencyType.USD,
merchant = "your_merchant_id"
),
clientId = "your_client_id"
)
pxpCheckout = PxpCheckout.builder()
.withConfig(sdkConfig)
.withContext(this)
.withDebugMode(true)
.build()
}
private fun setupNewCardComponent() {
val fields = NewCardComponentConfig.Fields().apply {
cardNumber = CardNumberComponentConfig(
onCardBrandDetected = { event ->
updateDynamicCardImage(event.cardBrand)
},
onValidationPassed = { result ->
enableNextField("expiry")
},
onValidationFailed = { result ->
showFieldError("card_number", result.message)
}
)
expiryDate = CardExpiryDateComponentConfig(
onValidationPassed = { result ->
enableNextField("cvc")
},
onValidationFailed = { result ->
showFieldError("expiry", result.message)
}
)
cvc = CardCvcComponentConfig(
onValidationPassed = { result ->
enableNextField("holder_name")
},
onValidationFailed = { result ->
showFieldError("cvc", result.message)
}
)
holderName = CardHolderNameComponentConfig(
onValidationPassed = { result ->
enableSubmitButton()
},
onValidationFailed = { result ->
showFieldError("holder_name", result.message)
}
)
}
val config = NewCardComponentConfig(
fields = fields,
onValidation = { validationResults ->
handleFormValidation(validationResults)
},
submit = CardSubmitComponentConfig(
onCollectStart = {
showProgressIndicator("Collecting device information...")
},
onCollectEnd = {
hideProgressIndicator()
},
onPreTokenisation = {
showProgressIndicator("Securing card information...")
true
},
onPostTokenisation = { tokenData ->
hideProgressIndicator()
Log.d("Payment", "Card tokenised successfully")
},
onPreAuthorisation = { data ->
showProgressIndicator("Processing payment...")
// Return additional transaction data if needed
null
},
onPostAuthorisation = { result ->
hideProgressIndicator()
handlePaymentResult(result)
},
onSubmitError = { error ->
hideProgressIndicator()
handlePaymentError(error)
}
)
)
newCardComponent = pxpCheckout.createComponent(
ComponentType.NEW_CARD,
config
)
}
private fun handleFormValidation(validationResults: List<ValidationResult>) {
val allValid = validationResults.all { it.isValid }
updateSubmitButtonState(allValid)
validationResults.forEach { result ->
if (!result.isValid) {
showFieldError(result.fieldName, result.message)
} else {
clearFieldError(result.fieldName)
}
}
}
private fun handlePaymentResult(result: PaymentResult) {
when (result.status) {
PaymentStatus.AUTHORISED -> {
showSuccessDialog("Payment successful!")
navigateToSuccessScreen()
}
PaymentStatus.DECLINED -> {
showErrorDialog("Payment was declined. Please try a different card.")
}
PaymentStatus.ERROR -> {
showErrorDialog("Payment failed. Please try again.")
}
}
}
@Composable
private fun PaymentScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
Text(
text = "Payment Information",
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(bottom = 24.dp)
)
pxpCheckout.buildComponentView(
component = newCardComponent,
modifier = Modifier.fillMaxWidth()
)
}
}
// Helper methods
private fun updateDynamicCardImage(cardBrand: CardBrand) {
// Update dynamic card image component
}
private fun enableNextField(fieldName: String) {
// Focus next field in the form
}
private fun showFieldError(fieldName: String, message: String) {
// Show error for specific field
}
private fun clearFieldError(fieldName: String) {
// Clear error for specific field
}
private fun enableSubmitButton() {
// Enable the submit button
}
private fun updateSubmitButtonState(enabled: Boolean) {
// Update submit button state
}
private fun showProgressIndicator(message: String) {
// Show loading indicator with message
}
private fun hideProgressIndicator() {
// Hide loading indicator
}
private fun showSuccessDialog(message: String) {
// Show success dialog
}
private fun showErrorDialog(message: String) {
// Show error dialog
}
private fun navigateToSuccessScreen() {
// Navigate to success screen
}
private fun handlePaymentError(error: PaymentError) {
// Handle payment error
}
}