Skip to content

Click-once

Learn about how to configure the click-once component.

Basic usage

Minimal configuration

The click-once component displays saved cards and allows customers to pay with a single tap. At minimum, you need to configure it with a card submit configuration:

import SwiftUI
import PXPCheckoutSDK

var config = ClickOnceComponentConfig()

var submitConfig = CardSubmitComponentConfig()
submitConfig.onPreAuthorisation = { _ async in
    TransactionInitiationData()
}
submitConfig.onPostAuthorisation = { _ in }
config.cardSubmitComponentConfig = submitConfig

let clickOnce = try pxpCheckout.create(.clickOnce, componentConfig: config) as! ClickOnceComponent

clickOnce.buildContent()

The click-once component requires ownerType, ownerId, and shopper information to retrieve saved tokens. Configure these in your CheckoutConfig.

Payout support: The click-once component supports card payouts (also called disbursements or withdrawals) where funds are sent from your merchant account to a cardholder's card. This is commonly used for:

  • Marketplace seller payments
  • Insurance claim settlements
  • Refunds and reimbursements
  • Competition prizes and rewards

When using intent: TransactionIntentData(card: .payout), configure isRenderLastPayoutCard = true to display cards previously used for successful payouts, providing a familiar payout experience for returning recipients.

Advanced configuration

You can customise token filtering, sorting, CVC requirements, and button styling:

var config = ClickOnceComponentConfig()

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

config.isCvcRequired = true
config.cvcComponentConfig = CardCvcComponentConfig(label: "Security code", isRequired: true)

config.cardBrandImages = CardBrandImages(
    visaSrc: Image("CustomVisa"),
    mastercardSrc: Image("CustomMC")
)
config.useTransparentCardBrandImage = true
config.hideCardBrandLogo = false

config.submitText = "Pay now"
config.label = "Saved card"
config.transactionInitiatorType = .CIT

config.onOnceCardClick = {
    // Runs before payment submission
}

config.onPreRenderTokens = { response in
    // Custom token filtering and ordering
    response.gatewayTokens.map { CardTokenMapping(id: $0.gatewayTokenId, isCvcRequired: true) }
}

var submitConfig = CardSubmitComponentConfig()
submitConfig.onPreAuthorisation = { _ async in TransactionInitiationData() }
submitConfig.onPostAuthorisation = { _ in }
config.cardSubmitComponentConfig = submitConfig

let clickOnce = try pxpCheckout.create(.clickOnce, componentConfig: config) as! ClickOnceComponent
PropertyDescription
cardSubmitComponentConfig
CardSubmitComponentConfig?
The card submit configuration for payment processing. Required for handling the payment flow. See Card submit. Defaults to nil.
limitTokens
Int?
The maximum number of tokens to display. Limits how many saved cards are available for selection. Defaults to nil.
filterBy
FilterByConfig?
The token filtering options. Allows you to narrow down which saved cards are displayed based on various criteria. Defaults to nil.
filterBy.excludeExpiredTokens
Bool?
Whether to exclude expired cards. When true, cards past their expiry date are hidden from the list. Defaults to true.
filterBy.schemes
[TokenScheme]?
The list of accepted card schemes. For example, [.visa, .mastercard] shows only Visa and Mastercard tokens. Defaults to nil.
filterBy.fundingSource
String?
The funding source filter. For example "Credit" or "Debit" filters to show only cards of that type. Defaults to nil.
filterBy.issuerCountryCode
String?
The issuer country code filter. Filters tokens by the country where the card was issued. Defaults to nil.
filterBy.ownerType
String?
The owner type filter. For example "Consumer" or "Commercial" filters by card ownership type. Defaults to nil.
orderBy
OrderByConfig?
The token ordering configuration. 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 sorting by expiry date with direction ("asc" or "desc") and priority. Defaults to nil.
orderBy.scheme
OrderByValueConfig<TokenScheme>?
The configuration for sorting by card scheme with custom order and priority. Defaults to nil.
orderBy.fundingSource
OrderByValueConfig<String>?
The configuration for sorting by funding source with custom order and priority. Defaults to nil.
orderBy.ownerType
OrderByValueConfig<String>?
The configuration for sorting by owner type with custom order and priority. Defaults to nil.
orderBy.issuerCountryCode
OrderByDirectionConfig?
The configuration for sorting by issuer country with direction and priority. Defaults to nil.
orderBy.lastUsageDate
OrderByLastUsageDateConfig?
The configuration for sorting by last usage date. Allows sorting by purchase or payout activity. Defaults to nil.
isCvcRequired
Bool?
Whether CVC is required for payment. When true, users must enter the security code before submitting. Defaults to nil.
cvcComponentConfig
CardCvcComponentConfig?
The CVC field configuration. Used when isCvcRequired is true. See Card CVC. Defaults to nil.
hideCardBrandLogo
Bool?
Whether to hide the brand logo. When true, the card brand image isn't displayed. Defaults to nil.
useTransparentCardBrandImage
Bool?
Whether to use transparent brand images. When true, card brand images render without background colours. Defaults to true.
cardBrandImages
CardBrandImages?
The custom card brand images. Properties: visaSrc, mastercardSrc, amexSrc, cupSrc, dinersSrc, discoverSrc, jcbSrc. Defaults to nil.
disableCardSelection
Bool?
Whether to disable changing the selected card. When true, users can't switch between saved cards. Defaults to nil.
isRenderLastPurchaseCard
Bool?
Whether to display the last purchase card. When true, the most recently used card for purchases is displayed. Defaults to nil.
isRenderLastPayoutCard
Bool?
Whether to display cards previously used for successful payouts. When true, cards with a lastSuccessfulPayoutDate will be shown even if they haven't been used for purchases. Recommended for payout flows (.payout intent) to show recipients their familiar payout cards. Defaults to nil.
transactionInitiatorType
TransactionInitiatorType?
The transaction initiator type. Indicates whether the transaction is customer-initiated (CIT) or merchant-initiated (MIT). Defaults to nil.
externalTokenId
String?
The specific external token to display. When set, only this token is shown. Defaults to nil.
expiredText
String?
The text displayed for expired cards. Overrides SDK default. Defaults to nil.
validThruText
String?
The "Valid thru" label text. Overrides SDK default. Defaults to nil.
submitText
String?
The submit button text. Overrides SDK default. Defaults to nil.
label
String?
The section label text. Displayed above the component. Defaults to nil.
labelAccessibilityLabel
String?
The accessibility label for the section label. Defaults to nil.
submitAccessibilityLabel
String?
The accessibility label for the submit button. Defaults to nil.
selectCardButtonAccessibilityLabel
String?
The accessibility label for the card picker button. Defaults to nil.
cardNumberAccessibilityLabel
String?
The accessibility label for the card number display. Defaults to nil.
cardExpiryDateAccessibilityLabel
String?
The accessibility label for the expiry date display. Defaults to nil.
styles
ClickOnceStandaloneStyles?
The container, button, and component styling configuration. Controls the overall appearance of the click-once component. Defaults to nil.
inputStyles
FieldInputStyle?
The shared input styling for CVC field when required. Defaults to nil.

Event handling

The click-once component provides event handlers for token management and payment:

config.onPreRenderTokens = { response in
    // Custom token filtering and ordering
    response.gatewayTokens
        .filter { $0.issuerCountryCode == "USA" }
        .map { CardTokenMapping(id: $0.gatewayTokenId, isCvcRequired: true) }
}

config.onRetrieveTokensSuccess = { success in
    // Token retrieval succeeded
}

config.onRetrieveTokensFailed = { error in
    // Handle retrieval error
}

config.onValidationError = { error in
    // Handle validation error
}

config.onOnceCardClick = {
    // Payment button tapped
}

config.buttonBuilder = { elements in
    AnyView(
        HStack {
            elements.tokenImageView
            elements.cardNumberView
            elements.cvcComponentView
            elements.payButtonView
        }
    )
}

config.selectTokenItemBuilder = { elements in
    AnyView(
        HStack {
            elements.tokenImageView
            elements.cardNumberView
            elements.expiryDateView
        }
    )
}
CallbackDescription
onPreRenderTokens: ((RetrieveCardTokensResponseSuccess) -> [CardTokenMapping])?Called to pre-process tokens before rendering. Returns an array of CardTokenMapping where each can set id and per-token isCvcRequired. Allows custom filtering and ordering logic.
onRetrieveTokensSuccess: ((ClickOnceRetrieveTokensSuccess) -> Void)?Called after successful token retrieval. Receives the response with available tokens.
onRetrieveTokensFailed: ((ClickOnceRetrieveTokensError) -> Void)?Called when token retrieval fails. Receives the error details.
onValidationError: ((BaseSdkException) -> Void)?Called when validation errors occur. Receives the validation exception details.
onOnceCardClick: (() -> Void)?Called when the payment button is tapped, before submission begins.
buttonBuilder: ((ClickOnceButtonBuilderElements) -> AnyView)?Custom SwiftUI builder for the payment button layout. Receives pre-built views and returns your custom layout.
selectTokenItemBuilder: ((ClickOnceSelectTokenBuilderElements) -> AnyView)?Custom SwiftUI builder for token picker rows. Receives pre-built views and returns your custom layout.

For more information about event patterns and filtering options, see Events and Card-on-file.

Methods

buildContent()

Returns SwiftUI content for the click-once component:

clickOnce.buildContent()

Examples

Basic click-once payment

A simple click-once payment form:

var config = ClickOnceComponentConfig()
config.submitText = "Pay now"
config.isCvcRequired = true

var submitConfig = CardSubmitComponentConfig()
submitConfig.onPreAuthorisation = { _ async in TransactionInitiationData() }
submitConfig.onPostAuthorisation = { result in
    // Handle payment result
}
config.cardSubmitComponentConfig = submitConfig

let clickOnce = try pxpCheckout.create(.clickOnce, componentConfig: config) as! ClickOnceComponent

Filtered and sorted tokens

Display only specific card types in a custom order:

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

var submitConfig = CardSubmitComponentConfig()
submitConfig.onPreAuthorisation = { _ async in TransactionInitiationData() }
submitConfig.onPostAuthorisation = { _ in }
config.cardSubmitComponentConfig = submitConfig

let clickOnce = try pxpCheckout.create(.clickOnce, componentConfig: config) as! ClickOnceComponent

With 3DS authentication

Enable 3DS authentication for click-once payments:

var config = ClickOnceComponentConfig()
config.isCvcRequired = true

var submitConfig = CardSubmitComponentConfig()
submitConfig.useUnityAuthenticationStrategy = true
submitConfig.onPreInitiateAuthentication = {
    PreInitiateIntegratedAuthenticationData(providerId: "pxpfinancial", timeout: 120)
}
submitConfig.onPreAuthentication = {
    InitiateIntegratedAuthenticationData(
        merchantCountryNumericCode: "826",
        merchantLegalName: "Example Ltd",
        challengeWindowSize: .size1,
        requestorChallengeIndicator: .challengeRequestedMandate,
        timeout: 180
    )
}
submitConfig.onPreAuthorisation = { _ async in TransactionInitiationData() }
submitConfig.onPostAuthorisation = { _ in }
config.cardSubmitComponentConfig = submitConfig

let clickOnce = try pxpCheckout.create(.clickOnce, componentConfig: config) as! ClickOnceComponent

Payout/disbursement flow

Use the click-once component to send funds to a cardholder's card (common for marketplace payouts, refunds, or prize disbursements):

let transactionData = TransactionData(
    amount: Decimal(string: "150.00")!,
    currency: "USD",
    entryType: .ecom,
    intent: TransactionIntentData(card: .payout),  // Critical: 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 = ClickOnceComponentConfig()
config.submitText = "Send funds"

// Enable payout-specific features
config.isRenderLastPayoutCard = true  // Show cards used for previous payouts
config.isRenderLastPurchaseCard = false  // Optionally hide purchase-only cards

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

config.isCvcRequired = true

var submitConfig = CardSubmitComponentConfig()
submitConfig.onPreAuthorisation = { _ async in
    TransactionInitiationData(
        riskScreeningData: RiskScreeningData(
            performRiskScreening: true,
            excludeDeviceData: false,
            deviceSessionId: generateDeviceSessionId(),
            userIp: "192.168.1.100",
            account: RiskScreeningAccount(
                id: "recipient_12345",
                creationDateTime: "2024-01-15T10:30:00.000Z"
            ),
            fulfillments: [
                RiskScreeningFulfillment(
                    type: .shipped,
                    recipientPerson: RiskScreeningRecipientPerson(
                        phoneNumber: "+1234567890"
                    )
                )
            ]
        )
    )
}
submitConfig.onPostAuthorisation = { result in
    if result is MerchantSubmitResult {
        print("Payout completed")
        // Navigate to payout confirmation screen
    }
}
config.cardSubmitComponentConfig = submitConfig

let clickOnce = try pxpCheckout.create(.clickOnce, componentConfig: config) as! ClickOnceComponent

private func generateDeviceSessionId() -> String {
    return UUID().uuidString.replacingOccurrences(of: "-", with: "")
}

When using .payout intent, funds move from your merchant account to the cardholder's card. Ensure you:

  • Have sufficient balance in your gateway account.
  • Use appropriate messaging ("withdraw", "receive", "send to your card").
  • Verify recipient card eligibility for disbursements (not all cards support payouts).

For 3DS details, see 3DS transactions. For validation behaviour, see Data validation.