Skip to content

Card-on-file

Learn how to configure the card-on-file component.

Basic usage

Minimal configuration

At minimum, the card-on-file component can be created with default settings to display all saved card tokens:

import SwiftUI
import PXPCheckoutSDK

let pxpCheckout = try PxpCheckout.initialize(config: CheckoutConfig(
    environment: .test,
    session: SessionData(
        sessionId: "your-session-id",
        hmacKey: "your-hmac-key",
        encryptionKey: "your-encryption-key"
    ),
    transactionData: TransactionData(
        amount: Decimal(10),
        currency: "USD",
        entryType: .ecom,
        intent: TransactionIntentData(card: .authorisation),
        merchantTransactionId: "unique-transaction-id",
        merchantTransactionDate: { Date() }
    ),
    merchantShopperId: "shopper-id",
    ownerType: "MerchantGroup",
    ownerId: "your-owner-id"
))

// Create card-on-file component
let cardOnFileComponent = try pxpCheckout.create(.cardOnFile, componentConfig: CardOnFileComponentConfig()) as! CardOnFileComponent

// Render component in SwiftUI
cardOnFileComponent.buildContent()

Advanced configuration

For more complex implementations, you can configure filtering, sorting, and display options:

// Create additional components for card-on-file
var cvcConfig = CardCvcComponentConfig(label: "Security code")
cvcConfig.isRequired = true

var expiryConfig = CardExpiryDateComponentConfig(label: "Expiry date")

// Create card-on-file with advanced configuration
var config = CardOnFileComponentConfig()

// Filtering options
config.limitTokens = 5
config.filterBy = FilterByConfig(
    excludeExpiredTokens: true,
    schemes: [.visa, .mastercard],
    fundingSource: "Credit",
    issuerCountryCode: "USA",
    ownerType: "Consumer"
)

// Sorting options
config.orderBy = OrderByConfig(
    expiryDate: OrderByDirectionConfig(
        direction: "asc",
        priority: 1
    ),
    scheme: OrderByValueConfig(
        valuesOrder: [.visa, .mastercard, .americanExpress],
        priority: 2
    ),
    fundingSource: OrderByValueConfig(
        valuesOrder: ["Credit", "Debit"],
        priority: 3
    ),
    lastUsageDate: OrderByLastUsageDateConfig(
        direction: "desc",
        orderByField: "lastSuccessfulPurchaseDate",
        priority: 4
    )
)

// UI configuration
config.isExpiryDateEditable = true
config.isCvcRequired = true
config.allowDeleteToken = true

// Nested components
config.cvcComponentConfig = cvcConfig
config.expiryDateComponentConfig = expiryConfig

let cardOnFileComponent = try pxpCheckout.create(.cardOnFile, componentConfig: config) as! CardOnFileComponent

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: TransactionIntentData(card: .payout) in your transaction data configuration.

PropertyDescription
limitTokens
Int?
The maximum number of tokens to display in the component. For example, if set to 5 then only the first five card tokens are displayed. Defaults to nil.
filterBy
FilterByConfig?
The filtering options for saved card tokens. Allows you to narrow down which saved cards are displayed based on various criteria. Defaults to nil.
filterBy.excludeExpiredTokens
Bool?
Whether to exclude tokens associated with expired cards. When true, cards past their expiry date are hidden from the list. Defaults to false.
filterBy.schemes
[TokenScheme]?
The list of card schemes to include. For example, [.visa, .mastercard] shows only Visa and Mastercard tokens. Defaults to nil.
filterBy.fundingSource
String?
The funding source type to include. For example "Credit" or "Debit" filters to show only cards of that type. Defaults to nil.
filterBy.issuerCountryCode
String?
The issuer country code to include. Filters tokens by the country where the card was issued. Defaults to nil.
filterBy.ownerType
String?
The owner type to include. For example "Consumer" or "Commercial" filters by card ownership type. Defaults to nil.
orderBy
OrderByConfig?
The ordering options for saved card tokens. Controls how tokens are sorted and displayed. Multiple ordering rules can be applied with priority values. Defaults to nil.
orderBy.expiryDate
OrderByDirectionConfig?
The configuration for ordering by expiry date. Defaults to nil.
orderBy.expiryDate.direction
String?
The direction to order by expiry date.

Possible values:
  • "desc" - newest expiry dates first
  • "asc" - oldest expiry dates first
Defaults to nil.
orderBy.expiryDate.priority
Int?
The priority of this ordering rule. Lower numbers are applied first. Defaults to nil.
orderBy.scheme
OrderByValueConfig<TokenScheme>?
The configuration for ordering by card scheme. Allows custom ordering like Visa first, then Mastercard. Defaults to nil.
orderBy.scheme.valuesOrder
[TokenScheme]?
The card scheme order. Tokens matching earlier schemes in the array appear first. Defaults to nil.
orderBy.scheme.priority
Int?
The priority of this ordering rule. Lower numbers are applied first. Defaults to nil.
orderBy.fundingSource
OrderByValueConfig<String>?
The configuration for ordering by funding source. Allows prioritising credit vs debit cards. Defaults to nil.
orderBy.fundingSource.valuesOrder
[String]?
The funding source order. Tokens matching earlier values appear first.

Possible values:
  • "Credit"
  • "Debit"
Defaults to nil.
orderBy.fundingSource.priority
Int?
The priority of this ordering rule. Lower numbers are applied first. Defaults to nil.
orderBy.ownerType
OrderByValueConfig<String>?
The configuration for ordering by owner type. Allows prioritising consumer vs commercial cards. Defaults to nil.
orderBy.ownerType.valuesOrder
[String]?
The owner type order. Tokens matching earlier values appear first.

Possible values:
  • "Consumer"
  • "Commercial"
Defaults to nil.
orderBy.ownerType.priority
Int?
The priority of this ordering rule. Lower numbers are applied first. Defaults to nil.
orderBy.issuerCountryCode
OrderByDirectionConfig?
The configuration for ordering by issuer country code. Allows alphabetical sorting by country. Defaults to nil.
orderBy.issuerCountryCode.direction
String?
The direction to order by country code.

Possible values:
  • "desc" - reverse alphabetical
  • "asc" - alphabetical
Defaults to nil.
orderBy.issuerCountryCode.priority
Int?
The priority of this ordering rule. Lower numbers are applied first. Defaults to nil.
orderBy.lastUsageDate
OrderByLastUsageDateConfig?
The configuration for ordering by last usage date. Shows most/least recently used cards first. Defaults to nil.
orderBy.lastUsageDate.direction
String?
The direction to order by last usage.

Possible values:
  • "desc" - most recent first
  • "asc" - oldest first
Defaults to nil.
orderBy.lastUsageDate.orderByField
String?
The field to order by when sorting by last usage. Choose based on your transaction type:

"lastSuccessfulPurchaseDate": Sort by most recent purchase/authorisation (default for payment flows)

"lastSuccessfulPayoutDate": Sort by most recent payout/disbursement (recommended for .payout intent flows)

For mixed flows or when unspecified, the SDK sorts by the most recent activity across both fields. Defaults to nil.
orderBy.lastUsageDate.priority
Int?
The priority of this ordering rule. Lower numbers are applied first. Defaults to nil.
transactionInitiatorType
TransactionInitiatorType?
The transaction initiator type. Indicates whether the transaction is customer-initiated or merchant-initiated. Defaults to nil.
isExpiryDateEditable
Bool?
Whether the expiry date field can be edited by the cardholder. When true, users can update the expiry date if their card has been renewed. Defaults to false.
isCvcRequired
Bool?
Whether CVC is required when selecting a token. When true, users must enter the security code even for saved cards. This applies to all tokens. Defaults to false.
allowDeleteToken
Bool?
Whether to allow cardholders to delete a token (previously saved card). When true, a delete button appears for each saved card. Defaults to false.
cvcComponentConfig
CardCvcComponentConfig?
The configuration for the standalone card CVC component. Used when isCvcRequired is true. See Card CVC. Defaults to nil.
expiryDateComponentConfig
CardExpiryDateComponentConfig?
The configuration for the standalone card expiry date component. Used when isExpiryDateEditable is true. See Card expiry date. Defaults to nil.

Styling

Default styling

The card-on-file component renders saved cards with built-in SwiftUI styling.

Custom styling

You can override the default appearance by providing custom styles, images, and text labels:

// Custom text labels
config.expiredText = "Card expired"
config.validThruText = "Valid until"
config.deleteErrorMessage = "Unable to delete card. Please try again."
config.deleteSuccessMessage = "Card has been removed successfully."
config.updateErrorMessage = "Unable to update card information. Please try again."
config.updateSuccessMessage = "Card information updated successfully."

// Custom styling
config.styles = CardOnFileStyles(
    backgroundColor: Color(.systemGray6),
    borderColor: Color(.systemGray3),
    borderRadius: 8,
    padding: 20
)

config.inputStyles = FieldInputStyle(
    cornerRadius: 5,
    backgroundColor: Color(.secondarySystemBackground)
)

// Custom icons
config.iconDeleteSrc = Image(systemName: "trash")
config.iconSaveSrc = Image(systemName: "checkmark")
config.iconCancelSrc = Image(systemName: "xmark")
config.iconEditSrc = Image(systemName: "pencil")

// Custom card brand images
config.cardBrandImages = CardBrandImages(
    visaSrc: Image("BrandVisa"),
    mastercardSrc: Image("BrandMastercard"),
    amexSrc: Image("BrandAmex")
)

config.useTransparentCardBrandImage = true

// Accessibility labels
config.editCardInformationAccessibilityLabel = "Edit card information button"
config.saveCardInformationAccessibilityLabel = "Save card information button"
config.cancelEditCardInformationAccessibilityLabel = "Cancel edit card information button"
config.deleteCardButtonAccessibilityLabel = "Delete card button"
config.cardNumberAccessibilityLabel = "Card number"
config.cardExpiryDateAccessibilityLabel = "Card expiry date"

// Delete modal configuration
config.deleteModal = DeleteModalConfig(
    dialogAccessibilityLabel: "Delete card dialog",
    bodyText: "Are you sure you want to delete this payment method?",
    cancelButtonText: "No, keep it",
    deleteButtonText: "Yes, delete it"
)
PropertyDescription
styles
CardOnFileStyles?
List container, token rows, typography, and message styles.
styles.backgroundColor
Color?
List background.
styles.borderColor
Color?
List border colour.
styles.borderRadius
CGFloat?
List corner radius.
styles.padding
CGFloat?
List inner padding.
styles.gap
CGFloat?
Spacing between token rows.
styles.tokenContainerStyle
CardTokenContainerStyle?
Normal, selected, and expired row styling.
styles.cardNumberStyle
CardTokenTextStyle?
Masked PAN text.
styles.expiredTextStyle
CardTokenTextStyle?
Expired label.
styles.validThruLabelStyle
CardTokenTextStyle?
“Valid thru” label.
styles.expiryDateValueStyle
CardTokenTextStyle?
Expiry value.
styles.successMessageStyle
CardOnFileMessageStyle?
Success banner.
styles.errorMessageStyle
CardOnFileMessageStyle?
Error banner.
inputStyles
FieldInputStyle?
Shared base for CVC and expiry inputs; per-component configs override when set.
cardBrandImages
CardBrandImages?
SwiftUI Image per brand (visaSrc, mastercardSrc, amexSrc, cupSrc, dinersSrc, discoverSrc, jcbSrc).
iconDeleteSrc
Image?
Delete control icon.
iconSaveSrc
Image?
Save control icon.
iconCancelSrc
Image?
Cancel control icon.
iconEditSrc
Image?
Edit control icon.
useTransparentCardBrandImage
Bool?
Prefer transparent brand artwork. Defaults to true.
expiredText
String?
Copy for expired cards.
validThruText
String?
“Valid thru” label override.
deleteErrorMessage
String?
Delete failure message.
deleteSuccessMessage
String?
Delete success message.
updateErrorMessage
String?
Update failure message.
updateSuccessMessage
String?
Update success message.
deleteModal
DeleteModalConfig?
Delete confirmation content and optional CardOnFileModalStyle, button styles, iconWarning.
editCardInformationAccessibilityLabel
String?
VoiceOver label for edit.
saveCardInformationAccessibilityLabel
String?
VoiceOver label for save.
cancelEditCardInformationAccessibilityLabel
String?
VoiceOver label for cancel.
deleteCardButtonAccessibilityLabel
String?
VoiceOver label for delete.
cardNumberAccessibilityLabel
String?
VoiceOver label for masked number.
cardExpiryDateAccessibilityLabel
String?
VoiceOver label for expiry.

Event handling

The card-on-file component provides event handlers to manage token retrieval, rendering, updates, and deletions:

config.onPreRenderTokens = { response in
    response.gatewayTokens.map {
        CardTokenMapping(id: $0.gatewayTokenId, isCvcRequired: true)
    }
    + response.schemeTokens.map {
        CardTokenMapping(id: $0.schemeTokenId, isCvcRequired: true)
    }
}

config.onRetrieveTokensSuccess = { response in
    // Optional: analytics or UI state
}

config.onRetrieveTokensFailed = { failure in
    // Show error UI
}

config.onSelectToken = { token in
    // token.maskedPrimaryAccountNumber, token.cardScheme, etc.
}

config.onPreDeleteToken = { token in
    // Return true to proceed (after your own confirmation if needed)
    true
}

config.onDeleteTokenSuccess = { _ in }
config.onDeleteTokenFailed = { _ in }

config.onUpdateTokenSuccess = { _ in }
config.onUpdateTokenFailed = { _ in }

config.onValidationError = { error in
    // e.g. invalid expiry while editing
}

config.tokenLabelBuilder = { token in
    "\(token.cardScheme.displayName) \(token.maskedPrimaryAccountNumber)"
}

config.tokenItemBuilder = { elements in
    AnyView(
        HStack {
            elements.tokenImageView
            VStack(alignment: .leading) {
                elements.tokenLabelView
                elements.expiryDateView
            }
            Spacer()
            elements.editButtonView
            elements.deleteButtonView
        }
    )
}
CallbackDescription
onPreRenderTokens: ((RetrieveCardTokensResponseSuccess) -> [CardTokenMapping])?Called to map and order tokens before UI builds. Returns an array of CardTokenMapping where each can set id and per-token isCvcRequired. Allows custom filtering and ordering logic.
onRetrieveTokensSuccess: ((RetrieveCardTokensResponseSuccess) -> Void)?Called when the token list is successfully retrieved from the API. Receives the response with all available tokens.
onRetrieveTokensFailed: ((RetrieveCardTokensResponseFailed) -> Void)?Called when token list retrieval fails. Receives the failure response with error details.
onSelectToken: ((BaseCardToken) -> Void)?Called when user selects a token. This includes initial auto-selection and re-selection after token deletion. Receives the selected token details.
onPreDeleteToken: ((BaseCardToken) async -> Bool)?Called before a token is deleted. Return false to cancel deletion, true to proceed. Use for custom confirmation flows.
onDeleteTokenSuccess: ((DeleteCardTokenResponseSuccess) -> Void)?Called when a token is successfully removed. Receives the deletion response.
onDeleteTokenFailed: ((DeleteCardTokenResponseFailed) -> Void)?Called when token deletion fails. Receives the API error details.
onUpdateTokenSuccess: ((UpdateCardTokenResponseSuccess) -> Void)?Called when an expiry date update succeeds. Receives the update response.
onUpdateTokenFailed: ((UpdateCardTokenResponseFailed) -> Void)?Called when an expiry date update fails. Receives the API error details.
onValidationError: ((BaseSdkException) -> Void)?Called when validation issues occur (for example, invalid expiry format during editing). Receives the validation exception.
tokenItemBuilder: ((CardTokenBuilderElements) -> AnyView)?Custom SwiftUI layout builder for each token row. Receives pre-built views: tokenImageView, tokenLabelView, expiryDateView, editButtonView, deleteButtonView. Return your custom AnyView layout.
tokenLabelBuilder: ((BaseCardToken) -> String)?Custom label text builder for the default layout. Receives the token details and returns a single-line string.

BaseCardToken exposes fields such as maskedPrimaryAccountNumber, cardExpiryMonth, cardExpiryYear, fundingSource, ownerType, issuerName, issuerCountryCode, lastSuccessfulPurchaseDate, lastSuccessfulPayoutDate, and cardScheme.

For shared event patterns and types, see Events.

Methods

buildContent()

Returns SwiftUI content for the card-on-file list:

cardOnFileComponent.buildContent()

Checkout-level unmount()

Releases components created from the checkout instance:

PxpCheckout.unmount()

Payment integration

To charge a saved card, link this component to Card submit: set cardSubmitConfig.cardOnFileComponent and cardSubmitConfig.useCardOnFile = true. The submit component performs validation and reads the selected token and CVC internally — you don't need to call getValue or validate from your app code.

Examples

Basic saved cards

A straightforward implementation showing saved payment methods:

var config = CardOnFileComponentConfig()
config.limitTokens = 5
config.filterBy = FilterByConfig(excludeExpiredTokens: true)

config.onSelectToken = { token in
    print("Selected:", token.maskedPrimaryAccountNumber)
}

config.onRetrieveTokensFailed = { _ in
    // Show error
}

let cardOnFile = try pxpCheckout.create(.cardOnFile, componentConfig: config) as! CardOnFileComponent

Filtered and sorted list

Display only specific card types in a custom order:

var config = CardOnFileComponentConfig()
config.filterBy = FilterByConfig(
    excludeExpiredTokens: true,
    schemes: [.visa, .mastercard],
    fundingSource: "Credit",
    issuerCountryCode: "USA"
)
config.orderBy = OrderByConfig(
    lastUsageDate: OrderByLastUsageDateConfig(
        orderByField: "lastSuccessfulPurchaseDate",
        direction: "desc",
        priority: 1
    ),
    scheme: OrderByValueConfig(valuesOrder: [.visa, .mastercard], priority: 2)
)
config.limitTokens = 3

Editable expiry, CVC required, delete enabled

Allow users to edit and delete their saved cards:

var config = CardOnFileComponentConfig()
config.isExpiryDateEditable = true
config.isCvcRequired = true
config.allowDeleteToken = true

var cvcConfig = CardCvcComponentConfig(label: "Security code")
cvcConfig.applyMask = true
cvcConfig.showMaskToggle = true
config.cvcComponentConfig = cvcConfig

config.expiryDateComponentConfig = CardExpiryDateComponentConfig(label: "Expiry date")

config.onPreDeleteToken = { token in
    // Present UIAlertController or SwiftUI confirmation; return whether to delete
    true
}

Custom delete modal

Configure a custom deletion confirmation modal:

config.deleteModal = DeleteModalConfig(
    dialogAccessibilityLabel: "Confirm deletion",
    bodyText: "Remove this payment method?",
    cancelButtonText: "Cancel",
    deleteButtonText: "Delete",
    modalStyle: CardOnFileModalStyle(
        backgroundColor: .white,
        borderRadius: 12,
        bodyPadding: 16,
        shadowRadius: 16
    ),
    deleteButtonStyle: CardOnFileDeleteButtonStyle(
        backgroundColor: .red,
        textColor: .white,
        height: 44,
        borderRadius: 8
    ),
    cancelButtonStyle: CardOnFileCancelButtonStyle(
        backgroundColor: .white,
        textColor: .primary,
        height: 44,
        borderRadius: 8,
        borderColor: .gray,
        borderWidth: 1
    )
)

Custom token row (SwiftUI)

Use a custom SwiftUI layout for token display:

config.tokenItemBuilder = { elements in
    AnyView(
        VStack(alignment: .leading, spacing: 8) {
            HStack {
                elements.tokenImageView
                elements.tokenLabelView
                Spacer()
                elements.editButtonView
                elements.deleteButtonView
            }
            elements.expiryDateView
        }
        .padding()
        .background(RoundedRectangle(cornerRadius: 10).stroke(Color.gray.opacity(0.3)))
    )
}

With card submit

Integrate card-on-file with the card submit component for payment processing:

let cofConfig = CardOnFileComponentConfig()
cofConfig.isCvcRequired = true
let cof = try pxpCheckout.create(.cardOnFile, componentConfig: cofConfig) as! CardOnFileComponent

var submitConfig = CardSubmitComponentConfig()
submitConfig.cardOnFileComponent = cof
submitConfig.useCardOnFile = true
submitConfig.submitText = "Pay with saved card"
submitConfig.onPreAuthorisation = { _ async in TransactionInitiationData() }
submitConfig.onPostAuthorisation = { _ in }

let submit = try pxpCheckout.create(.cardSubmit, componentConfig: submitConfig) as! CardSubmitComponent

VStack {
    cof.buildContent()
    submit.buildContent()
}

Payout/disbursement flow

Display saved cards for recipients to receive funds:

let transactionData = TransactionData(
    amount: Decimal(string: "250.00")!,
    currency: "USD",
    entryType: .ecom,
    intent: TransactionIntentData(card: .payout),  // Set to payout for disbursements
    merchantTransactionId: "payout-\(UUID().uuidString)",
    merchantTransactionDate: { Date() }
)

let checkoutConfig = CheckoutConfig(
    environment: .test,
    session: sessionData,
    transactionData: transactionData,
    merchantShopperId: "recipient-shopper-id",
    ownerType: "MerchantGroup",
    ownerId: "your-owner-id",
    onGetShopper: { async in TransactionShopper(id: "recipient-shopper-id") }
)

let pxpCheckout = try PxpCheckout.initialize(config: checkoutConfig)

var config = CardOnFileComponentConfig()
config.limitTokens = 5

// Filter and sort for payout flows
config.filterBy = FilterByConfig(excludeExpiredTokens: true)

// Prioritise cards used for recent payouts
config.orderBy = OrderByConfig(
    lastUsageDate: OrderByLastUsageDateConfig(
        orderByField: "lastSuccessfulPayoutDate",  // Sort by payout activity
        direction: "desc",
        priority: 1
    )
)

config.isExpiryDateEditable = true
config.isCvcRequired = true

config.onSelectToken = { token in
    print("Payout card selected:", token.maskedPrimaryAccountNumber)
    print("Last payout:", token.lastSuccessfulPayoutDate.isEmpty ? "None" : token.lastSuccessfulPayoutDate)
}

config.onRetrieveTokensFailed = { error in
    print("Failed to load payout cards:", error.message)
    // Show error message to user
}

let cardOnFileComponent = try pxpCheckout.create(.cardOnFile, componentConfig: config) as! CardOnFileComponent

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

Use orderByField: "lastSuccessfulPayoutDate" to prioritise cards the recipient has successfully used for previous payouts. This builds trust and reduces friction by showing familiar payout destinations first.

For setup and lifecycle, see Implementation. For validation behaviour on linked fields, see Data validation.