# 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

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

| Event | Billing address | Card-on-file | Click-once | New card |
|  --- | --- | --- | --- | --- |
| `onAddressChanged` |  |  |  |  |
| `onBlur` |  |  |  |  |
| `onCardBrandCannotRecognised` |  |  |  |  |
| `onCardBrandDetected` |  |  |  |  |
| `onCardDetected` |  |  |  |  |
| `onCardFlipped` |  |  |  |  |
| `onCardInteraction` |  |  |  |  |
| `onChange` |  |  |  |  |
| `onClick` |  |  |  |  |
| `onConsentChanged` |  |  |  |  |
| `onCountryDeselected` |  |  |  |  |
| `onCountrySelected` |  |  |  |  |
| `onCollectEnd` |  |  |  |  |
| `onCollectStart` |  |  |  |  |
| `onCvcEntered` |  |  |  |  |
| `onCustomValidation` |  |  |  |  |
| `onDeleteTokenFailed` |  |  |  |  |
| `onDeleteTokenSuccess` |  |  |  |  |
| `onFocus` |  |  |  |  |
| `onGetFingerprintResult` |  |  |  |  |
| `onImageError` |  |  |  |  |
| `onImageLoaded` |  |  |  |  |
| `onOnceCardClick` |  |  |  |  |
| `onPostAuthentication` |  |  |  |  |
| `onPostAuthorisation` |  |  |  |  |
| `onPostInitiateAuthentication` |  |  |  |  |
| `onPostTokenisation` |  |  |  |  |
| `onPreAuthentication` |  |  |  |  |
| `onPreAuthorisation` |  |  |  |  |
| `onPreDeleteToken` |  |  |  |  |
| `onPreInitiateAuthentication` |  |  |  |  |
| `onPreRenderTokens` |  |  |  |  |
| `onPreTokenisation` |  |  |  |  |
| `onPrivacyClicked` |  |  |  |  |
| `onRetrieveTokensFailed` |  |  |  |  |
| `onStateChanged` |  |  |  |  |
| `onSubmitError` |  |  |  |  |
| `onSyncComplete` |  |  |  |  |
| `onTermsClicked` |  |  |  |  |
| `onTriggerFieldValidation` |  |  |  |  |
| `onUpdateTokenFailed` |  |  |  |  |
| `onUpdateTokenSuccess` |  |  |  |  |
| `onValidation` |  |  |  |  |
| `onValidationPassed` |  |  |  |  |
| `onValidationFailed` |  |  |  |  |
| `tokenItemBuilder` |  |  |  |  |
| `tokenLabelBuilder` |  |  |  |  |


Standalone components
The following table lists all events supported by the different standalone components.

| Event | Billing address input fields | Card input fields | Card submit | Country selection | Dynamic card image | Pre-fill billing address checkbox |
|  --- | --- | --- | --- | --- | --- | --- |
| `onAddressChanged` |  |  |  |  |  |  |
| `onBlur` |  |  |  |  |  |  |
| `onCardBrandCannotRecognised` |  | Card number only. |  |  |  |  |
| `onCardBrandDetected` |  | Card number only. |  |  |  |  |
| `onCardDetected` |  |  |  |  |  |  |
| `onCardFlipped` |  |  |  |  |  |  |
| `onCardInteraction` |  |  |  |  |  |  |
| `onChange` |  |  |  |  |  |  |
| `onClick` |  |  |  |  |  |  |
| `onCollectEnd` |  |  |  |  |  |  |
| `onCollectStart` |  |  |  |  |  |  |
| `onCountrySelected` |  |  |  |  |  |  |
| `onCountryDeselected` |  |  |  |  |  |  |
| `onCvcEntered` |  |  |  |  |  |  |
| `onCustomValidation` |  |  |  |  |  |  |
| `onDeleteTokenFailed` |  |  |  |  |  |  |
| `onDeleteTokenSuccess` |  |  |  |  |  |  |
| `onFocus` |  |  |  |  |  |  |
| `onGetFingerprintResult` |  |  |  |  |  |  |
| `onImageError` |  |  |  |  |  |  |
| `onImageLoaded` |  |  |  |  |  |  |
| `onOnceCardClick` |  |  |  |  |  |  |
| `onPostAuthentication` |  |  |  |  |  |  |
| `onPostAuthorisation` |  |  |  |  |  |  |
| `onPostInitiateAuthentication` |  |  |  |  |  |  |
| `onPostTokenisation` |  |  |  |  |  |  |
| `onPreAuthentication` |  |  |  |  |  |  |
| `onPreAuthorisation` |  |  |  |  |  |  |
| `onPreDeleteToken` |  |  |  |  |  |  |
| `onPreInitiateAuthentication` |  |  |  |  |  |  |
| `onPreRenderTokens` |  |  |  |  |  |  |
| `onPreTokenisation` |  |  |  |  |  |  |
| `onPrivacyClicked` |  |  |  |  |  |  |
| `onRetrieveTokensFailed` |  |  |  |  |  |  |
| `onStateChanged` |  |  |  |  |  |  |
| `onSubmitError` |  |  |  |  |  |  |
| `onSyncComplete` |  |  |  |  |  |  |
| `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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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). |



```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `timestamp`Long | Date and time when collection started. |
| `deviceInfo`DeviceInfo | Basic device information. |
| `sessionId`String | The collection session identifier. |


#### Example implementation


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


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

### onCustomValidation

This callback is triggered after standard component validation passes and before payment submission proceeds. Return `true` to allow submission, or `false` to block submission.

You can use it to:

- Validate custom business rules before payment processing.
- Check transaction amount limits or thresholds.
- Verify customer eligibility for specific payment methods.
- Implement fraud prevention rules based on order data.
- Validate inventory availability before charging.
- Enforce payment policies based on customer account status.


#### Event data

This callback receives no parameters and returns a Boolean value.

| Return value | Description |
|  --- | --- |
| `true` | Allows the payment submission to proceed. |
| `false` | Blocks the payment submission and stops the transaction flow. |


This callback is called after all standard SDK component validations (card number, CVV, expiry date, billing address) have passed successfully. It enables you to add an additional validation layer for business-specific requirements.

#### Example implementation


```kotlin
val cardSubmitConfig = CardSubmitComponentConfig(
    onCustomValidation = {
        Log.d("Payment", "Running custom validation checks...")
        
        // Example: Validate transaction amount limits
        val transactionAmount = checkout.getSdkConfig().transactionData.amount
        val maxAllowedAmount = 10000.0
        
        if (transactionAmount > maxAllowedAmount) {
            Log.w("Payment", "Transaction amount exceeds maximum allowed: $maxAllowedAmount")
            showCustomError("Transaction amount cannot exceed £$maxAllowedAmount")
            return@CardSubmitComponentConfig false
        }
        
        // Example: Check customer eligibility
        val customerTier = getCustomerTier()
        if (customerTier == CustomerTier.RESTRICTED) {
            Log.w("Payment", "Customer account is restricted from making payments")
            showCustomError("Your account is temporarily restricted. Please contact support.")
            return@CardSubmitComponentConfig false
        }
        
        // Example: Validate inventory availability
        val itemsInStock = checkInventoryAvailability()
        if (!itemsInStock) {
            Log.w("Payment", "One or more items are no longer available")
            showCustomError("Some items in your cart are no longer available")
            return@CardSubmitComponentConfig false
        }
        
        // All custom validations passed
        Log.d("Payment", "Custom validation passed successfully")
        return@CardSubmitComponentConfig true
    },
    
    onSubmitError = { error ->
        Log.e("Payment", "Payment submission error: ${error.message}")
    },
    
    onPostAuthorisation = { result ->
        Log.d("Payment", "Payment authorisation completed")
    }
)
```

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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `requestId`String | The unique identifier for the fingerprint request. |
| `timestamp`Long | The date and time when the fingerprint was requested. |


#### Example implementation


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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-authorisation started. |


#### Example implementation


```kotlin
val cardSubmitConfig = CardSubmitComponentConfig(
    onPreAuthorisation = { data ->
        Log.d("Payment", "Pre-authorisation data: $data")
        
        TransactionInitiationData(
            psd2Data = null, // Set to Psd2Data(scaExemption = ...) if needed
            riskScreeningData = RiskScreeningData(
                performRiskScreening = true,
                userIp = "192.168.1.100",
                account = RiskScreeningAccount(
                    id = "user_12345678",
                    creationDateTime = "2024-01-15T10:30:00.000Z"
                ),
                items = listOf(
                    RiskScreeningItem(
                        price = 99.99,
                        quantity = 1,
                        category = "Electronics"
                    )
                ),
                fulfillments = listOf(
                    RiskScreeningFulfillment(
                        type = FulfillmentType.SHIPPED,
                        shipping = RiskScreeningShipping(
                            shippingMethod = ShippingMethod.EXPRESS
                        ),
                        recipientPerson = RiskScreeningRecipientPerson(
                            phoneNumber = "+1234567890",
                            email = "customer@example.com"
                        )
                    )
                )
            )
        )
    }
)
```

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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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 synchronisation.
- Track user interactions.
- Validate form state.
- Enable/disable related components.


#### Event data

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
val preFillCheckboxConfig = PreFillBillingAddressCheckboxComponentConfig(
    onStateChanged = { event ->
        Log.d("PreFill", "Checkbox state changed: ${event.previousState} -> ${event.newState}")
        Log.d("PreFill", "User initiated: ${event.userInitiated}")
        
        when (event.newState) {
            CheckboxState.CHECKED -> {
                if (event.hasShippingAddress) {
                    // Start address synchronisation
                    startAddressSync()
                    showSyncProgress()
                } else {
                    // Show message about needing shipping address first
                    showMissingShippingAddressMessage()
                    // Revert checkbox state
                    revertCheckboxState()
                }
            }
            CheckboxState.UNCHECKED -> {
                // Clear billing address form
                clearBillingAddressForm()
                enableBillingAddressEditing()
                hideSyncProgress()
            }
        }
        
        // Update form validation
        validateBillingAddressForm()
        
        // Track interaction analytics
        analytics.track("prefill_state_changed", mapOf(
            "previous_state" to event.previousState.name,
            "new_state" to event.newState.name,
            "user_initiated" to event.userInitiated,
            "has_shipping_address" to event.hasShippingAddress
        ))
    }
)
```

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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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


```kotlin
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

| Parameter | Description |
|  --- | --- |
| `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.


```kotlin
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.


```kotlin
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:


```kotlin
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 transaction data with risk screening
                    TransactionInitiationData(
                        psd2Data = null, // Set to Psd2Data(scaExemption = ...) if needed
                        riskScreeningData = RiskScreeningData(
                            performRiskScreening = true,
                            userIp = "192.168.1.100",
                            account = RiskScreeningAccount(
                                id = "user_12345678",
                                creationDateTime = "2024-01-15T10:30:00.000Z"
                            ),
                            fulfillments = listOf(
                                RiskScreeningFulfillment(
                                    type = FulfillmentType.SHIPPED,
                                    recipientPerson = RiskScreeningRecipientPerson(
                                        phoneNumber = "+1234567890"
                                    )
                                )
                            )
                        )
                    )
                },
                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
    }
}
```