Skip to content

Events

Implement callbacks to customise your payment flow.

Overview

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.

Supported events

The following table lists all events supported by the different pre-built components.

EventBilling addressCard-on-fileClick-onceNew card
onAddressChanged
onBlur
onCardBrandCannotRecognised
onCardBrandDetected
onCardDetected
onCardFlipped
onCardInteraction
onChange
onClick
onConsentChanged
onCountryDeselected
onCountrySelected
onCollectEnd
onCollectStart
onCvcEntered
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

Callbacks

onAddressChanged

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.

Event data

ParameterDescription
event
AddressSyncEvent
The address synchronisation event object.
event.sourceAddress
BillingAddressData
The shipping address being copied.
event.targetComponentId
String
The ID of the billing address component.
event.syncedFields
List<String>
The list of fields being synchronised.
event.syncStrategy
AddressSyncStrategy
The strategy used for synchronisation.
event.startTime
Long
When the sync operation started (timestamp).

Example implementation

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)
    }
)

onBlur

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.

Event data

ParameterDescription
focusEvent
FocusEvent
The focus event object.
focusEvent.fieldName
String
The name of the field that lost focus.
focusEvent.value
String
The current value in the field.
focusEvent.isValid
Boolean
Whether the current value is valid.
focusEvent.timestamp
Long
When the blur event occurred (timestamp).
focusEvent.nextFocusTarget
String?
The next field that will receive focus, if any.

Example implementation

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
        ))
    }
)

onCardBrandCannotRecognised

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.

Event data

ParameterDescription
event
CardBrandRecognitionEvent
The card brand recognition event object.
event.cardNumber
String
The current card number input (masked for security).
event.detectedLength
Int
The length of the current input.
event.potentialMatches
List<CardBrand>
The list of possible card brands based on partial input.
event.inputSource
InputSource
How the number was entered.
event.timestamp
Long
The date and time when the detection attempt occurred.
event.confidenceLevel
Float
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()
    }
)

onCardBrandDetected

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.

Event data

ParameterDescription
event
CardBrandDetectionEvent
The card brand detection event object.
event.cardBrand
CardBrand
The detected card brand.
event.isCardBrandAccepted
Boolean
Whether the detected brand is accepted.
event.confidence
Float
The detection confidence score (0.0-1.0).
event.previousBrand
CardBrand?
The previously detected brand (if any).

Example implementation

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
        ))
    }
)

onCardDetected

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.

Event data

ParameterDescription
event
CardDetectionEvent
The card detection event object.
event.detectedBrand
CardBrand
The newly detected card brand.
event.previousBrand
CardBrand?
The previously detected brand (if any).
event.confidence
Float
The detection confidence score (0.0-1.0).
event.detectionTime
Long
The time taken for detection, in milliseconds.
event.source
DetectionSource
The source of the detection.

Example implementation

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
            ))
        }
    }
)

onCardFlipped

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.

Event data

ParameterDescription
event
CardFlipEvent
The card flip event object.
event.cardBrand
CardBrand
The brand of the flipped card.
event.showingBack
Boolean
Whether the back side is now visible.
event.trigger
FlipTrigger
What triggered the flip (tap, swipe, etc.).
event.animationDuration
Long
The duration of the flip animation in milliseconds.

Example implementation

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
        ))
    }
)

onCardInteraction

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.

Event data

ParameterDescription
event
CardInteractionEvent
The card interaction event object.
event.cardBrand
CardBrand
The brand of the card.
event.interactionType
InteractionType
The type of interaction performed. For example, a tap or a swipe.
event.timestamp
Long
The date and time when the interaction occurred.
event.coordinates
TouchCoordinates?
The touch coordinates, if applicable.

Example implementation

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
        ))
    }
)

onChange

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

Event data

ParameterDescription
changeEvent
ChangeEvent
The change event object.
changeEvent.fieldName
String
The name of the field that changed.
changeEvent.value
String
The new value in the field.
changeEvent.previousValue
String
The previous value before change.
changeEvent.changeType
ChangeType
The type of change (insertion, deletion, replacement).
changeEvent.cursorPosition
Int
The current cursor position.
changeEvent.isValid
Boolean
Whether the new value is valid.
changeEvent.timestamp
Long
The timestamp when the change occurred.

Example implementation

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
        ))
    }
)

onSubmitStart

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.

Event data

ParameterDescription
timestamp
Long
Date and time when the submission started.
componentState
ComponentState
The current state of the submit component.
validationResults
List<ValidationResult>
The results of pre-submission validation.

Example implementation

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()
        ))
    }
)

onCollectStart

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.

Event data

ParameterDescription
timestamp
Long
Date and time when collection started.
deviceInfo
DeviceInfo
Basic device information.
sessionId
String
The collection session identifier.

Example implementation

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()
        ))
    }
)

onCollectComplete

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.

Event data

ParameterDescription
collectResult
CollectResult
The collection result object.
collectResult.deviceData
String
Encrypted device fingerprint.
collectResult.fingerprint
String
The unique device identifier.
collectResult.riskScore
Float
The fraud risk assessment score (0.0-1.0).
collectResult.collectDuration
Long
The time taken to collect data, in milliseconds.
collectResult.dataPoints
Int
The number of data points collected.

Example implementation

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)
        ))
    }
)

onCvcEntered

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.

Event data

ParameterDescription
cvcEvent
CvcEvent
The CVC entry event object.
cvcEvent.cvcValue
String
The entered CVC value (masked).
cvcEvent.cardToken
CardToken
The associated card token.
cvcEvent.isValid
Boolean
Whether the CVC is valid.
cvcEvent.validationErrors
List<ValidationResult>?
Any validation errors.

Example implementation

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()
        }
    }
)

onDeleteTokenFailed

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.

Event data

ParameterDescription
response
DeleteTokenFailureResponse
The failure response object.
response.tokenId
String
The ID of the token that failed to delete.
response.error
SdkException
The error details and error code.
response.errorMessage
String
A human-readable error message.
response.retryable
Boolean
Whether the operation can be retried.
response.requestId
String
The unique request identifier.

Example implementation

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
        ))
    }
)

onDeleteTokenSuccess

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.

Event data

ParameterDescription
response
DeleteTokenSuccessResponse
The success response object.
response.tokenId
String
The ID of the successfully deleted token.
response.deletedToken
CardToken
Complete token information that was deleted.
response.remainingTokenCount
Int
The number of tokens remaining.
response.deletionTime
Long
The time taken for the deletion operation.

Example implementation

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")
    }
)

onFocus

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.

Event data

ParameterDescription
focusEvent
FocusEvent
The focus event object.
focusEvent.fieldName
String
The name of the field that received focus.
focusEvent.value
String
The current value in the field.
focusEvent.isValid
Boolean
Whether the current value is valid.
focusEvent.timestamp
Long
The date and time when the focus event occurred.
focusEvent.previousFocusTarget
String?
The previous field that had focus, if any.

Example implementation

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")
    }
)

onGetFingerprintResult

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.

Event data

ParameterDescription
requestId
String
The unique identifier for the fingerprint request.
timestamp
Long
The date and time when the fingerprint was requested.

Example implementation

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()
}

onImageError

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.

Event data

ParameterDescription
event
ImageErrorEvent
The image error event object.
event.cardBrand
CardBrand
The brand of the failed image.
event.error
ImageLoadError
The type of error that occurred.
event.retryAttempt
Int
The current retry attempt number.
event.canRetry
Boolean
Whether automatic retry is possible.
event.errorMessage
String
The detailed error description.

Example implementation

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
        ))
    }
)

onImageLoaded

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.

Event data

ParameterDescription
event
ImageLoadedEvent
The image loaded event object.
event.cardBrand
CardBrand
The brand of the loaded card image.
event.imageUrl
String
The URL or resource identifier of the image.
event.loadTime
Long
The time taken to load the image, in milliseconds.
event.fromCache
Boolean
Whether the image was loaded from the cache.
event.imageSize
Long
The size of the loaded image, in bytes.
event.resolution
String
The image resolution (width x height).

Example implementation

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}"
        ))
    }
)

onOnceCardClick

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.

Event data

ParameterDescription
event
CardSelectionEvent
The card selection event object.
event.tokenId
String
The ID of the selected card token.
event.cardBrand
CardBrand
The brand of the selected card.
event.maskedNumber
String
The masked card number for display.
event.expiryDate
String
The expiry date of the card.
event.isDefault
Boolean
Whether this is the default card.

Example implementation

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
        ))
    }
)

onCountryDeselected

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.

Event data

ParameterDescription
event
CountryDeselectionEvent
The country deselection event object.
event.deselectedCountry
Country
The country that was deselected.
event.reason
DeselectionReason
Why the deselection occurred.
event.timestamp
Long
The date and time when the deselection occurred.
event.previousSelectionMethod
SelectionMethod?
How the previous selection was made.

Example implementation

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
        ))
    }
)

onCountrySelected

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.

Event data

ParameterDescription
event
CountrySelectionEvent
The country selection event object.
event.selectedCountry
Country
The newly selected country.
event.previousCountry
Country?
The previously selected country, if any.
event.selectionMethod
SelectionMethod
How the selection was made.
event.timestamp
Long
The date and time when the selection occurred.
event.userInitiated
Boolean
Whether the selection was user-initiated.

Example implementation

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}")
    }
)

onPostAuthentication

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.

Event data

ParameterDescription
authResult
AuthenticationResult
The authentication result object.
authResult.authenticationResult
AuthenticationResult
The complete authentication result object.
authResult.status
AuthenticationStatus
The authentication status.
authResult.transactionId
String
The unique transaction identifier.
authResult.authenticationValue
String
The authentication value.
authResult.eci
String
The Electronic Commerce Indicator.
authResult.processingTime
Long
The time taken for authentication, in milliseconds.
authResult.threeDSVersion
String
The version of 3DS protocol used.
authResult.challengeType
ChallengeType
The type of challenge presented.
authResult.issuerResponse
IssuerResponse
The issuer response details.
authResult.timestamp
Long
The time and date when authentication completed.

Example implementation

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)
            }
        }
    }
)

onPostAuthorisation

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.

Event data

ParameterDescription
result
AuthorisationResult
The authorisation result object.
result.authorisationResult
AuthorisationResult
The complete authorisation result object.
result.state
AuthorisationState
The authorisation state.
result.transactionId
String: Unique transaction identifier.
result.authCode
String
The authorisation code from the issuer.
result.responseCode
String
The issuer response code.
result.responseMessage
String
A human-readable response message.
result.processingTime
Long
The time taken for authorisation, in milliseconds.
result.amount
BigDecimal
The authorised amount. This may differ from the requested amount.
result.currency
String
The transaction currency.
result.cardDetails
MaskedCardDetails
The masked card details.
result.merchantReference
String
Your transaction reference.
result.timestamp
Long
The time and date when authorisation completed.
result.networkTransactionId
String
The network-specific transaction ID.

Example implementation

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.")
            }
        }
    }
)

onPostInitiateAuthentication

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.

Event data

ParameterDescription
authResult
InitiationResult
The initiation result object.
authResult.initiationResult
InitiationResult
The authentication initiation result object.
authResult.status
InitiationStatus
The initiation status.
authResult.threeDSVersion
String
The version of the 3DS protocol being used.
authResult.transactionId
String
The unique transaction identifier.
authResult.challengeType
ChallengeType
The type of challenge required.
authResult.issuerAcsUrl
String
The ACS URL for the challenge flow, if applicable.
authResult.challengeWindow
ChallengeWindowSize
The preferred challenge window size.
authResult.processingTime
Long
The time taken to initiate authentication.
authResult.deviceFingerprint
String
The device fingerprinting results.
authResult.riskAssessment
RiskAssessment
The risk evaluation from the authentication service.
authResult.timestamp
Long
The time and date when initiation completed.

Example implementation

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)
            }
        }
    }
)

onPostTokenisation

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.

Event data

ParameterDescription
tokenData
TokenisationResult
The tokenisation result object.
tokenData.tokenisationResult
TokenisationResult
The complete tokenisation result object.
tokenData.success
Boolean
Whether tokenisation was successful.
tokenData.token
CardToken?
Information about the generated token, if tokenisation was successful.
tokenData.tokenId
String?
The unique token identifier.
tokenData.maskedPAN
String?
The masked Primary Account Number (PAN).
tokenData.cardBrand
CardBrand
The detected card brand.
tokenData.expiryDate
String?
The card expiry date.
tokenData.binRange
String
The Bank Identification Number (BIN) range.
tokenData.issuerInfo
IssuerInfo
The card issuer details.
tokenData.processingTime
Long
The time taken for tokenisation, in milliseconds.
tokenData.vaultProvider
String
Token vault provider information.
tokenData.errorDetails
ErrorDetails?
Error information, if tokenisation failed.
tokenData.timestamp
Long
The time and date when tokenisation completed.

Example implementation

val newCardConfig = NewCardComponentConfig(
    onPostTokenisation = { tokenData ->
        Log.d("Payment", "Card tokenised: $tokenData")
        // Store token reference or proceed to payment
        handleTokenisation(tokenData)
    }
)

onPreAuthentication

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.

Event data

ParameterDescription
preAuthData
PreAuthenticationData
The pre-authentication data object.
preAuthData.transactionAmount
BigDecimal
The amount being authenticated.
preAuthData.currency
String
The transaction currency.
preAuthData.merchantInfo
MerchantInfo
The merchant identification details.
preAuthData.cardDetails
MaskedCardDetails
The masked card information for authentication.
preAuthData.customerInfo
CustomerData?
The customer data available for authentication.
preAuthData.deviceFingerprint
String
The device fingerprinting data.
preAuthData.riskData
RiskData
The risk assessment data.
preAuthData.threeDSMethod
String?
The 3DS method data, if available.
preAuthData.timestamp
Long
The time and date when pre-authentication started.

Example implementation

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
        )
    }
)

onPreAuthorisation

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).

Event data

ParameterDescription
data
PreAuthorisationData
The pre-authorisation data object.
data.transactionRequest
TransactionRequest
The initial transaction request details.
data.amount
BigDecimal
The transaction amount to be authorised.
data.currency
String
The transaction currency.
data.cardDetails
MaskedCardDetails
The masked card information for authorisation.
data.merchantInfo
MerchantInfo
Merchant identification and configuration.
data.customerData
CustomerData?
The available customer information.
data.billingAddress
Address?
The customer's billing address, if provided.
data.shippingAddress
Address?
The customer's shipping address, if provided.
data.orderDetails
OrderDetails?
Order line items and metadata.
data.riskData
RiskData
The risk assessment and fraud detection data.
data.authenticationResult
AuthenticationResult?
The 3DS authentication result, if performed.
data.timestamp
Long
The time and date when pre-authorization started.

Example implementation

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"
            )
        )
    }
)

onPreDeleteToken

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.

Event data

ParameterDescription
token
CardToken
The card token object.
token.token
CardToken
The complete token information to be deleted.
token.tokenId
String
The unique identifier of the token.
token.maskedPAN
String
The masked Primary Account Number (PAN).
token.cardBrand
CardBrand
The card brand/scheme.
token.expiryDate
String
The card expiry date.
token.isDefault
Boolean
Whether this is the default payment method.
token.usageCount
Int
The number of times this token has been used.
token.lastUsed
Long?
The date and time when the token was last used.
token.isLinkedToSubscriptions
Boolean
Whether the token is used for recurring payments.
token.linkedSubscriptions
List<Subscription>
The list of active subscriptions using this token.
token.customerData
CustomerData?
Customer information for context.
token.deletionReason
DeletionReason
The reason for deletion.
token.canRecover
Boolean
Whether deletion can be undone.
token.timestamp
Long
The time and date when deletion was requested.

Example implementation

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
    }
)

onPreInitiateAuthentication

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.

Event data

ParameterDescription
data
PreInitiationData
The pre-initiation data object.
data.transactionData
TransactionData
The transaction information for authentication.
data.cardDetails
MaskedCardDetails
The masked card information.
data.amount
BigDecimal
The transaction amount.
data.currency
String
The transaction currency.
data.merchantInfo
MerchantInfo
Merchant configuration and settings.
data.customerData
CustomerData?
Available customer information for authentication.
data.deviceData
DeviceData
The device fingerprinting and browser information.
data.riskData
RiskData
The initial risk assessment results.
data.billingAddress
Address?
The customer's billing address.
data.previousAuthAttempts
List<AuthAttempt>
The history of authentication attempts for this transaction.
data.timestamp
Long
The time and date when pre-initiation started.

Example implementation

val cardSubmitConfig = CardSubmitComponentConfig(
    onPreInitiateAuthentication = {
        return@CardSubmitComponentConfig AuthenticationInitData(
            threeDSecureVersion = "2.1.0",
            merchantName = "Your Store"
        )
    }
)

onPreTokenisation

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.

Event data

ParameterDescription
data
PreTokenisationData
The pre-tokenisation data object.
data.cardData
MaskedCardData
The card information to be tokenised. This is masked for security purposes.
data.cardBrand
CardBrand
The detected card brand.
data.binRange
String
The Bank Identification Number (BIN) range.
data.issuerInfo
IssuerInfo
The card issuer information.
data.cardType
CardType
The type of card.
data.customerData
CustomerData?
The available customer information.
data.tokenisationMethod
TokenisationMethod
The method of tokenisation.
data.networkTokenSupport
Boolean
Whether network tokenisation is supported.
data.existingTokens
List<CardToken>
The list of existing tokens for this customer.
data.duplicateCheck
DuplicateCheckResult
The results of the duplicate card check.
data.timestamp
Long
The time and date when pre-tokenisation started.

Example implementation

val cardSubmitConfig = CardSubmitComponentConfig(
    onPreTokenisation = {
        Log.d("Payment", "About to tokenise card")
        // Check business rules before tokenisation
        return@CardSubmitComponentConfig validateBusinessRules()
    }
)

onPreRenderTokens

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.

Event data

ParameterDescription
response
TokenRetrievalResponse
The token retrieval response object.
response.tokenResponse
TokenRetrievalResponse
The complete token retrieval response.
response.gatewayTokens
List<GatewayToken>
The list of gateway tokens retrieved.
response.networkTokens
List<NetworkToken>?
The list of network tokens, if available.
response.retrievalTime
Long
The time taken to retrieve the tokens.
response.customerData
CustomerData?
Customer information associated with the tokens.
response.tokenMetadata
Map<String, Any>?
Additional metadata for each token.
response.filterCriteria
FilterCriteria?
Any applied filter criteria.
response.sortOptions
SortOptions?
The current sorting preferences.
response.securityContext
SecurityContext
The security context for token access.
response.timestamp
Long
Time and date when token retrieval was completed.

Example implementation

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)
                )
            }
    }
)

onRetrieveTokensFailed

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.

Event data

ParameterDescription
response
ErrorResponse
The error response object.
response.errorResponse
ErrorResponse
The complete error response object.
response.errorCode
String
The specific error code from the service.
response.errorMessage
String
A human-readable error message.
response.errorCategory
ErrorCategory
The category of error.
response.retryable
Boolean
Whether the operation can be retried.
response.attemptNumber
Int
The number of retry attempts made.
response.requestTime
Long
The time taken for the failed request.
response.customerData
CustomerData?
Customer information for context.
response.requestId
String
The unique identifier for the failed request.
response.timestamp
Long
The time and date when the failure occurred.

Example implementation

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.")
    }
)

onSubmitError

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.

Event data

ParameterDescription
error
SdkException
The error object.
error.error
SdkException
The error details and error code.
error.errorMessage
String
A human-readable error message.
error.errorType
ErrorType
The type of error (network, validation, server, etc.).
error.retryable
Boolean
Whether the operation can be retried.
error.submissionData
String?
The data that was being submitted (sanitised).

Example implementation

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)
    }
)

onUpdateTokenFailed

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.

Event data

ParameterDescription
response
UpdateFailureResponse
The update failure response object.
response.updateResponse
UpdateFailureResponse
The complete update failure response.
response.tokenId
String
The ID of the token that failed to update.
response.updateFields
List<String>
The fields that were being updated.
response.errorCode
String
The specific error code from the service.
response.errorMessage
String
A human-readable error message.
response.errorCategory
ErrorCategory
The category of error.
response.retryable
Boolean
Whether the update operation can be retried.
response.originalToken
CardToken
The original token data before the update attempt.
response.attemptedChanges
Map<String, Any>
The changes that were attempted.
response.timestamp
Long
The time and date when the update failure occurred.

Example implementation

val cardOnFileConfig = CardOnFileComponentConfig(
    onUpdateTokenFailed = { response ->
        Log.e("Payment", "Token update failed: ${response.error}")
        // Show error message to user
        showErrorMessage("Failed to update card information")
    }
)

onUpdateTokenSuccess

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.

Event data

ParameterDescription
response
UpdateSuccessResponse
The update success response object.
response.updateResponse
UpdateSuccessResponse
The complete update success response.
response.tokenId
String
The ID of the successfully updated token.
response.updatedToken
CardToken
The updated token with new information.
response.updatedFields
List<String>
The list of fields that were successfully updated.
response.previousValues
Map<String, Any>
The previous values before the update.
response.updateTime
Long
The time taken to complete the update.
response.networkToken
Boolean
Whether this token has network tokenisation.
response.vaultProvider
String
The token vault provider information.
response.synchronizedDevices
List<String>
The list of devices where the token was synchronised.
response.timestamp
Long
The time and date when the update completed.

Example implementation

val cardOnFileConfig = CardOnFileComponentConfig(
    onUpdateTokenSuccess = { response ->
        Log.d("Payment", "Token updated successfully: $response")
        // Refresh display with new information
        showSuccessMessage("Card information updated")
        refreshCardDisplay()
    }
)

onValidation

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.

Event data

ParameterDescription
validationEvent
ValidationEvent
The validation event object.
validationEvent.validationResults
List<ValidationResult>
The list of validation results for all fields.
validationEvent.isFormValid
Boolean
Whether the entire form is valid.
validationEvent.fieldCount
Int
The number of fields validated.
validationEvent.errorCount
Int
The number of fields with errors.
validationEvent.triggerSource
ValidationTrigger
What triggered the validation.

Example implementation

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
        ))
    }
)

onValidationFailed

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.

Event data

ParameterDescription
validationResult
ValidationResult
The validation result object.
validationResult.fieldName
String
The name of the field that failed validation.
validationResult.value
String
The current field value.
validationResult.errors
List<ValidationFieldError>
The list of validation errors.
validationResult.errorCodes
List<String>
The specific error codes.
validationResult.validationTrigger
ValidationTrigger
What triggered the validation.

Example implementation

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
        ))
    }
)

onValidationPassed

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.

Event data

ParameterDescription
validationResult
ValidationResult
The validation result object.
validationResult.fieldName
String
The name of the field that passed validation.
validationResult.value
String
The current valid field value.
validationResult.validationDuration
Long
The time taken for validation in milliseconds.
validationResult.validationTrigger
ValidationTrigger
What triggered the validation.

Example implementation

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
        ))
    }
)

tokenItemBuilder

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.

Event data

ParameterDescription
elementIds
TokenElementIds
The token element IDs object.
elementIds.elementIds
TokenElementIds
The UI element identifiers and references for the token.
elementIds.tokenData
CardToken
The complete token information for customisation.
elementIds.displayState
DisplayState
The current display state.
elementIds.interactionState
InteractionState
The user interaction state.
elementIds.tokenImageId
String
The image resource or URL for the card brand.
elementIds.tokenLabelId
String
The formatted display label for the token.
elementIds.expiryDateId
String
The formatted expiry date string.
elementIds.isDefault
Boolean
Whether this token is the default payment method.
elementIds.isExpired
Boolean
Whether the token has expired.
elementIds.metadata
Map<String, Any>?
Additional metadata for customisation.
elementIds.availableActions
List<TokenAction>
The list of actions available for this token.

Example implementation

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()
    }
)

tokenLabelBuilder

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.

Event data

ParameterDescription
token
CardToken
The card token object.
token.token
CardToken
The card token object.
token.maskedNumber
String
The masked card number.
token.scheme
String
The card scheme/brand.
token.issuerName
String
The name of the card issuer.
token.expiryDate
String
The card expiry date.

Example implementation

val cardOnFileConfig = CardOnFileComponentConfig(
    tokenLabelBuilder = { token ->
        "${token.scheme.name} •••• ${token.maskedPrimaryAccountNumber.takeLast(4)} - ${token.issuerName}"
    }
)

onConsentChanged

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.

Event data

ParameterDescription
event
ConsentChangedEvent
The consent changed event object.
event.consentType
ConsentType
The type of consent being changed.
event.previousState
Boolean
The previous checkbox state.
event.newState
Boolean
The new checkbox state.
event.userInitiated
Boolean
Whether the change was user-initiated.
event.timestamp
Long
Time and date when the change occurred.
event.metadata
Map<String, Any>?
Additional consent metadata.

Example implementation

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
        ))
    }
)

onTermsClicked

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.

Event data

ParameterDescription
event
TermsClickedEvent
The terms clicked event object.
event.termsUrl
String
The URL of the terms document.
event.clickLocation
Point
The click coordinates on screen.
event.openMode
OpenMode
How the terms should be opened.
event.timestamp
Long
When the click occurred.
event.linkText
String
The text of the clicked link.

Example implementation

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
        ))
    }
)

onPrivacyClicked

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.

Event data

ParameterDescription
event
PrivacyClickedEvent
The privacy clicked event object.
event.privacyUrl
String
The URL of the privacy policy.
event.clickLocation
Point
The click coordinates on screen.
event.openMode
OpenMode
How the privacy policy should be opened.
event.timestamp
Long
When the click occurred.
event.linkText
String
The text of the clicked link.

Example implementation

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()
        ))
    }
)

onStateChanged

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 synchronization
  • Track user interactions
  • Validate form state
  • Enable/disable related components

Event data

ParameterDescription
event
StateChangedEvent
The state changed event object.
event.previousState
Boolean
The previous checkbox state.
event.newState
Boolean
The new checkbox state.
event.userInitiated
Boolean
Whether the change was user-initiated.
event.timestamp
Long
When the change occurred.
event.hasShippingAddress
Boolean
Whether the shipping address is available.

Example implementation

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 synchronization
                    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
        ))
    }
)

onAddressSync

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.

Event data

ParameterDescription
event
AddressSyncEvent
The address sync event object.
event.sourceAddress
Address
The shipping address being copied.
event.targetComponentId
String
The ID of the billing address component.
event.syncedFields
List<String>
The list of fields being synchronised.
event.syncStrategy
SyncStrategy
The strategy used for synchronisation.
event.startTime
Long
When the sync operation started.

Example implementation

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)
    }
)

onSyncComplete

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.

Event data

ParameterDescription
event
SyncCompleteEvent
The sync complete event object.
event.sourceAddress
Address
The original shipping address.
event.targetAddress
Address
The resulting billing address.
event.syncedFields
List<String>
Fields that were synchronised.
event.syncDuration
Long
Time taken for sync operation.
event.success
Boolean
Whether sync completed successfully.
event.errors
List<String>?
Any errors that occurred during sync.

Example implementation

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)
    }
)

onTriggerFieldValidation

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.

Event data

ParameterDescription
validationResult
FieldValidationResult
The field validation result object.
validationResult.fieldName
String
The name of the field being validated.
validationResult.isValid
Boolean
Whether the field passed validation.
validationResult.errorCode
String?
The specific error code, if validation failed.
validationResult.errorMessage
String?
A human-readable error message.
validationResult.validationType
ValidationType
The type of validation performed.

Common scenarios

Handle a declined transaction

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()
}

Handle an authentication failure

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()
}

Complete payment flow example

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
    }
}