Skip to main content

Customization

You own every pixel outside the payment element. Inside it, the card form is the only form the SDK renders, and it is the only surface a theme reaches; wallet buttons and Klarna's widget keep their partners' branding.

The theme​

PurseTheme takes native values: UIColor, UIFont, CGFloat. There is no CSS-string parser here, unlike Android's ThemeValues: no cross-platform merchant configuration exists to share, so a string layer would be overhead with no reader.

let theme = PurseTheme(
global: .init(color: UIColor(white: 0.12, alpha: 1), font: .systemFont(ofSize: 14)),
globalInvalid: .init(color: .systemRed),
input: .init(placeholderColor: .placeholderText, backgroundColor: .systemBackground, font: .systemFont(ofSize: 16)),
inputFocus: .init(color: .tintColor),
label: .init(font: .systemFont(ofSize: 13, weight: .semibold)),
helperText: .init(color: .secondaryLabel, font: .systemFont(ofSize: 12)),
gap: 12
)
ScopeApplies toVariants
globalevery text of the formglobalInvalid, globalValid
inputthe secure fieldsinputFocus, inputInvalid, inputValid
labelfield labelslabelInvalid, labelValid
helperTexthelper and error textshelperTextInvalid
gapspacing between fields

resolve() applies web's chain: scope variant, then scope, then global variant, then global, over PurseTheme.default.

What the theme actually paints today

The input scopes map onto Secure Fields' own SecureFieldsStyle (font, text colour, placeholder colour, background, and the focus, valid and invalid overrides). The card form (CardFormView) draws, per field, a label above the secure field and a helper or error line below it: label and labelInvalid style the label, helperText and helperTextInvalid the line. Two values resolve but paint nothing: labelValid, and gap (the spacing between fields is fixed).

Texts​

CardFormTexts covers the card number, expiry, CVV and holder fields, each with label, placeholder, helperText, requiredError and formatError. placeholder goes into the secure field, label is drawn above it and is also its accessibility label, and the line below shows helperText, or requiredError once an empty field has lost focus, or formatError once a filled field is invalid. A text you leave out falls back to the SDK's English or French copy, chosen from the locale.

Where the theme goes​

PluginOptions(locale:theme:cardFormTexts:) carries a theme, a BCP-47 locale and those texts down to every plugin. Pass it when you create the checkout:

let checkout = try PurseHeadlessCheckout(
widgetData: widgetData,
lockedEnvironment: .production,
options: PluginOptions(locale: "fr-FR", theme: theme)
)

The card form an element hands you is then themed, and locale picks the card form's default copy and is the locale sent in a 3-D Secure step's browser data; without it, the device's Locale.preferredLanguages is used. options is not part of a saved state: pass it again to restore(_:options:).

To render the fields yourself, drive the card form directly, through the same public seams the built-in element uses:

let host = try checkout.host(for: method)
let context = host.buildContext()
let coordinator = CardCoordinator(
host: host,
tenantId: context.vaultTenantId ?? "",
supportedBrands: context.configuration?.vault?.supportedBrands ?? [],
options: PluginOptions(locale: "fr-FR", theme: theme)
)

// Mount coordinator.fieldViews, observe coordinator.isFormValid, then on Pay:
_ = try await coordinator.submit() // tokenizes and reports fulfilment to the checkout
let result = try await checkout.submitPayment()
try await checkout.presentRedirectIfNeeded(for: result, using: presenter)

This is the low-level path: you render the fields, and you own the submit and the redirect that PaymentElement would otherwise own. Use it when your brand demands it, not by default.

Icons​

checkout.iconURL(for: item) returns the CDN URL of a method's or token's badge for the environment in use, and checkout.schemeAssets(for: method) the accepted card schemes with icon and label. The SDK ships no images and loads none: you fetch and render them, so you never host partner logos yourself.