# Card-on-file

Learn about customisation options for the card-on-file component.

## Styling

```kotlin
val cardOnFileComponentConfig = CardOnFileComponentConfig(
    style = CardOnFileStyle?,
    deleteModal = DeleteModalConfig,
    cvcComponentConfig = CardCvcComponentConfig?,
    expiryDateComponentConfig = CardExpiryDateComponentConfig?,
    onTokenSelected = ((CardToken) -> Unit)?,
    onPreRenderTokens = ((RetrieveCardTokensResponseSuccess) -> List<CardTokenMapping>)?,
    onRetrieveTokensFailed = ((RetrieveCardTokensResponseFailed) -> Unit)?,
    onDeleteTokenSuccess = ((DeleteCardTokenResponseSuccess) -> Unit)?,
    onDeleteTokenFailed = ((DeleteCardTokenResponseFailed) -> Unit)?,
    onPreDeleteToken = ((BaseTokenData) -> CompletableDeferred<Boolean>)?,
    onUpdateTokenFailed = ((UpdateCardTokenResponseFailed) -> Unit)?,
    onUpdateTokenSuccess = ((UpdateCardTokenResponseSuccess) -> Unit)?,
    onTokensLoaded = ((List<CardToken>) -> Unit)?,
    tokenLabelBuilder = ((CardToken) -> String)?,
    tokenItemBuilder = ((@Composable (TokenBuilderLayoutScope.() -> Unit))?,
    limitTokens = Int?,
    filter = CardTokenFilter,
    order = CardTokenOrder,
    isExpiryDateEditable = Boolean,
    isCvcRequired = Boolean,
    allowDeleteToken = Boolean,
    validThruText = String,
    expiredText = String,
    deleteErrorMessage = String?,
    deleteSuccessMessage = String?,
    updateErrorMessage = String?,
    updateSuccessMessage = String?,
    cardBrandImages = CardBrandImages?,
    useTransparentCardBrandImage = Boolean,
    editCardInformationAriaLabel = String,
    saveCardInformationAriaLabel = String,
    cancelEditCardInformationAriaLabel = String,
    deleteCardButtonAriaLabel = String,
    cardNumberAriaLabel = String,
    cardExpiryDateAriaLabel = String,
    iconDelete = Int,
    iconSave = Int,
    iconCancel = Int,
    iconEdit = Int,
    notificationConfig = NotificationConfig
)
```

| Property | Description |
|  --- | --- |
| `style`CardOnFileStyle? | Component styling configuration. |
| `deleteModal`DeleteModalConfig | Delete modal configuration with dialog text and styling. |
| `cvcComponentConfig`CardCvcComponentConfig? | CVC input component configuration for secure card selection. See [Card CVC](/guides/checkout/components/android/card/card-cvc). |
| `expiryDateComponentConfig`CardExpiryDateComponentConfig? | Expiry date input component configuration for card updates. See [Card expiry date](/guides/checkout/components/android/card/card-expiry-date). |
| `limitTokens`Int? | The maximum number of tokens to display. |
| `filter`CardTokenFilter | Filter options for card tokens. Defaults to default filter. |
| `order`CardTokenOrder | Order/sorting options for card tokens. Defaults to default order. |
| `isExpiryDateEditable`Boolean | Whether the expiry date field can be edited. Defaults to `false`. |
| `isCvcRequired`Boolean | Whether CVC is required when selecting a token. Defaults to `true`. |
| `allowDeleteToken`Boolean | Whether to allow users to delete tokens. Defaults to `false`. |
| `validThruText`String | The "Valid Thru" label text. Defaults to `"Valid Thru"`. |
| `expiredText`String | The expired card text. Defaults to `"Expired"`. |
| `deleteErrorMessage`String? | The card deletion failure message. |
| `deleteSuccessMessage`String? | The card deletion success message. |
| `updateErrorMessage`String? | The card update failure message. |
| `updateSuccessMessage`String? | The card update success message. |
| `cardBrandImages`CardBrandImages? | Custom card brand images configuration. |
| `useTransparentCardBrandImage`Boolean | Whether to use transparent card brand images. Defaults to `true`. |
| `editCardInformationAriaLabel`String | Edit card information aria label for accessibility. |
| `saveCardInformationAriaLabel`String | Save card information aria label for accessibility. |
| `cancelEditCardInformationAriaLabel`String | Cancel edit aria label for accessibility. |
| `deleteCardButtonAriaLabel`String | Delete card button aria label for accessibility. |
| `cardNumberAriaLabel`String | Card number aria label for accessibility. |
| `cardExpiryDateAriaLabel`String | Card expiry date aria label for accessibility. |
| `iconDelete`Int | The delete button icon resource. Defaults to a delete icon. |
| `iconSave`Int | The save button icon resource. Defaults to a save icon. |
| `iconCancel`Int | The cancel button icon resource. Defaults to a cancel icon. |
| `iconEdit`Int | The edit button icon resource. Defaults to an edit icon. |
| `notificationConfig`NotificationConfig | Notification configuration for user feedback. |


## Delete modal configuration

```kotlin
data class DeleteModalConfig(
    var deleteConfirmText: String = "Are you sure you want to delete this card?",
    var deleteConfirmButtonText: String = "Delete",
    var deleteCancelButtonText: String = "Cancel",
    var dialogStyles: CardOnFileStyle.DialogStyles = CardOnFileStyle.DialogStyles()
)
```

| Property | Description |
|  --- | --- |
| `deleteConfirmText`String | The confirmation text displayed in the modal. |
| `deleteConfirmButtonText`String | The confirm button text. |
| `deleteCancelButtonText`String | The cancel button text. |
| `dialogStyles`CardOnFileStyle.DialogStyles | Dialog-related styling configuration. |


## Callbacks

```kotlin
data class CardOnFileComponentConfig(
    val onFocus: (() -> Unit)? = null,
    val onBlur: (() -> Unit)? = null,
    val onFocusChange: ((Boolean) -> Unit)? = null,
    val onValidationPassed: ((List<ValidationResult>) -> Unit)? = null,
    val onValidationFailed: ((List<ValidationResult>) -> Unit)? = null,
    val onTokenSelected: ((CardToken) -> Unit)? = null,
    val onPreRenderTokens: ((RetrieveCardTokensResponseSuccess) -> List<CardTokenMapping>)? = null,
    val onRetrieveTokensFailed: ((RetrieveCardTokensResponseFailed) -> Unit)? = null,
    val onDeleteTokenSuccess: ((DeleteCardTokenResponseSuccess) -> Unit)? = null,
    val onDeleteTokenFailed: ((DeleteCardTokenResponseFailed) -> Unit)? = null,
    val onPreDeleteToken: ((BaseTokenData) -> CompletableDeferred<Boolean>)? = null,
    val onUpdateTokenFailed: ((UpdateCardTokenResponseFailed) -> Unit)? = null,
    val onUpdateTokenSuccess: ((UpdateCardTokenResponseSuccess) -> Unit)? = null,
    val onTokensLoaded: ((List<CardToken>) -> Unit)? = null,
    val tokenLabelBuilder: ((CardToken) -> String)? = null,
    val tokenItemBuilder: ((@Composable (TokenBuilderLayoutScope.() -> Unit))? = null
)
```

| Callback | Description |
|  --- | --- |
| `onFocus: (() -> Unit)?` | Event handler for when the component receives focus. Inherited from BaseFieldComponentConfigImpl. |
| `onBlur: (() -> Unit)?` | Event handler for when the component loses focus. Inherited from BaseFieldComponentConfigImpl. |
| `onFocusChange: ((Boolean) -> Unit)?` | Event handler for when the component's focus state changes. Inherited from BaseFieldComponentConfigImpl. |
| `onValidationPassed: ((List<ValidationResult>) -> Unit)?` | Event handler for when validation passes. Inherited from BaseFieldComponentConfigImpl. |
| `onValidationFailed: ((List<ValidationResult>) -> Unit)?` | Event handler for when validation fails. Inherited from BaseFieldComponentConfigImpl. |
| `onTokenSelected: ((CardToken) -> Unit)?` | Event handler for when a card token is selected. |
| `onPreRenderTokens: ((RetrieveCardTokensResponseSuccess) -> List<CardTokenMapping>)?` | Event handler for custom token processing before rendering. |
| `onRetrieveTokensFailed: ((RetrieveCardTokensResponseFailed) -> Unit)?` | Event handler for token retrieval failure. |
| `onDeleteTokenSuccess: ((DeleteCardTokenResponseSuccess) -> Unit)?` | Event handler for successful token deletion. |
| `onDeleteTokenFailed: ((DeleteCardTokenResponseFailed) -> Unit)?` | Event handler for failed token deletion. |
| `onPreDeleteToken: ((BaseTokenData) -> CompletableDeferred<Boolean>)?` | Event handler for pre-deletion confirmation callback. |
| `onUpdateTokenFailed: ((UpdateCardTokenResponseFailed) -> Unit)?` | Event handler for failed token update. |
| `onUpdateTokenSuccess: ((UpdateCardTokenResponseSuccess) -> Unit)?` | Event handler for successful token update. |
| `onTokensLoaded: ((List<CardToken>) -> Unit)?` | Event handler for when tokens are loaded. |
| `tokenLabelBuilder: ((CardToken) -> String)?` | Custom function for building token labels. |
| `tokenItemBuilder: ((@Composable (TokenBuilderLayoutScope.() -> Unit))?` | Custom Composable for building token item layouts. |


For more information about callbacks, see [Events](/guides/checkout/components/android/card/events).

When integrating card-on-file with card submit for payment processing, you can use the `onCustomValidation` callback to implement custom business rule validation before submission. See [Card submit](/guides/checkout/components/android/card/card-submit) for details.

**Payout support:** The card-on-file component supports card payouts (disbursements) where funds are sent to a cardholder's card. Each saved card token includes `lastSuccessfulPayoutDate`, allowing you to:

- Sort cards by most recent payout activity
- Display cards familiar to payout recipients
- Build streamlined disbursement flows


Common payout use cases include marketplace seller payments, insurance settlements, refunds, and prize distributions. Use `intent = IntentType.Payout` in your transaction data configuration.

## Example

```kotlin
import com.pxp.checkout.components.cardonfile.CardOnFileComponent
import com.pxp.checkout.components.cardonfile.CardOnFileComponentConfig
import com.pxp.checkout.components.cardonfile.DeleteModalConfig
import com.pxp.checkout.components.cardcvc.CardCvcComponentConfig
import com.pxp.checkout.components.cardexpirydate.CardExpiryDateComponentConfig
import com.pxp.checkout.models.CardTokenFilter
import com.pxp.checkout.models.CardTokenOrder
import com.pxp.checkout.styles.CardOnFileStyle

// Optional: Configure CVC component for secure card selection
val cvcConfig = CardCvcComponentConfig(
    label = "CVC",
    placeholder = "123",
    showMaskToggle = false
)

// Optional: Configure expiry date component for card updates
val expiryConfig = CardExpiryDateComponentConfig(
    label = "Expiry Date",
    placeholder = "MM/YY"
)

// Optional: Configure delete modal
val deleteModalConfig = DeleteModalConfig(
    deleteConfirmText = "Are you sure you want to remove this payment method?",
    deleteConfirmButtonText = "Remove Card",
    deleteCancelButtonText = "Keep Card"
)

// Optional: Configure filtering and sorting
val tokenFilter = CardTokenFilter(
    excludeExpired = true,
    includeBrands = listOf(CardBrand.VISA, CardBrand.MASTERCARD)
)

val tokenOrder = CardTokenOrder(
    sortByLastUsed = true,
    sortByExpiryDate = false
)

// Optional: Create custom styling
val customStyle = CardOnFileStyle(
    cardBackground = Color.White,
    cardBorder = Color.Gray,
    cardRadius = 12.dp,
    spacing = 16.dp
)

val config = CardOnFileComponentConfig(
    // Component integrations
    cvcComponentConfig = cvcConfig,
    expiryDateComponentConfig = expiryConfig,
    
    // UI configuration
    deleteModal = deleteModalConfig,
    limitTokens = 5,
    
    // Data management
    filter = tokenFilter,
    order = tokenOrder,
    
    // Feature toggles
    isExpiryDateEditable = true,
    isCvcRequired = true,
    allowDeleteToken = true,
    
    // Custom text labels
    validThruText = "Valid Until",
    expiredText = "Card Expired",
    
    // Styling
    style = customStyle,
    useTransparentCardBrandImage = true,
    
    // Event handlers
    onTokenSelected = { token ->
        println("Card selected: •••• ${token.lastFourDigits}")
    },
    
    onTokensLoaded = { tokens ->
        println("Loaded ${tokens.size} saved cards")
    },
    
    onPreRenderTokens = { response ->
        // Custom processing of tokens before display
        response.data.map { token ->
            CardTokenMapping(
                token = token,
                displayLabel = "${token.scheme} •••• ${token.lastFourDigits}"
            )
        }
    },
    
    onRetrieveTokensFailed = { error ->
        println("Failed to load saved cards: ${error.message}")
    },
    
    onPreDeleteToken = { tokenData ->
        // Show custom confirmation dialog
        val deferred = CompletableDeferred<Boolean>()
        // Simulate user confirmation
        deferred.complete(true)
        deferred
    },
    
    onDeleteTokenSuccess = { response ->
        println("Card successfully deleted")
    },
    
    onDeleteTokenFailed = { error ->
        println("Failed to delete card: ${error.message}")
    },
    
    onUpdateTokenSuccess = { response ->
        println("Card updated successfully")
    },
    
    onUpdateTokenFailed = { error ->
        println("Failed to update card: ${error.message}")
    },
    
    // Custom token label builder
    tokenLabelBuilder = { token ->
        "${token.scheme.uppercase()} ending in ${token.lastFourDigits}"
    },
    
    // Accessibility labels
    editCardInformationAriaLabel = "Edit payment method",
    deleteCardButtonAriaLabel = "Delete payment method",
    cardNumberAriaLabel = "Card number",
    cardExpiryDateAriaLabel = "Expiration date"
)

val cardOnFileComponent = checkout.createComponent<CardOnFileComponent, CardOnFileComponentConfig>(
    type = ComponentType.CARD_ON_FILE,
    config = config
)
```

### Payout/disbursement flow

Display saved cards for recipients to receive funds:

```kotlin
val transactionData = TransactionData(
    amount = 250.00,
    currency = "USD",
    entryType = EntryType.Ecom,
    intent = IntentType.Payout,  // Set to Payout for disbursements
    merchantTransactionId = "payout-${System.currentTimeMillis()}",
    merchantTransactionDate = Date()
)

val config = CardOnFileComponentConfig(
    limitTokens = 5,
    
    // Filter and sort for payout flows
    filter = CardTokenFilter(
        excludeExpired = true
    ),
    
    // Prioritize cards used for recent payouts
    order = CardTokenOrder(
        sortByField = LastUsageField.PAYOUT_DATE,  // Sort by payout activity
        direction = SortDirection.DESC
    ),
    
    isExpiryDateEditable = true,
    isCvcRequired = true,
    allowDeleteToken = false,
    
    onTokenSelected = { token ->
        Log.d("Payout", "Card selected: ${token.lastFourDigits}")
        Log.d("Payout", "Last payout: ${token.lastSuccessfulPayoutDate}")
    },
    
    onTokensLoaded = { tokens ->
        Log.d("Payout", "Loaded ${tokens.size} payout cards")
    },
    
    onRetrieveTokensFailed = { error ->
        Log.e("Payout", "Failed to load cards: ${error.message}")
        showErrorMessage("Unable to load saved cards")
    }
)

val cardOnFileComponent = checkout.createComponent<CardOnFileComponent, CardOnFileComponentConfig>(
    type = ComponentType.CARD_ON_FILE,
    config = config
)

// Continue with card submit component for payout execution...
```

Use `sortByField = LastUsageField.PAYOUT_DATE` to prioritise cards the recipient has successfully used for previous payouts. This builds trust and reduces friction by showing familiar payout destinations first.