Skip to main content

SDK reference

This page is the map of the SDK's public surface; names are the Swift ones. The public declarations carry DocC comments your IDE shows on Option-click, but the SDK does not yet publish a generated DocC reference the way the Android SDK publishes KDoc.

Products​

ProductHolds
PurseHeadlessCheckoutPurseHeadlessCheckout, PaymentElement and its listener, the payment model, the card and redirection plugins, the gift card configs, PluginRegistry, PurseTheme, PurseAssets, the error types
PurseCheckoutApplePayApplePayModule, ApplePayPlugin, ApplePayCoordinator, ApplePayConfiguration
PurseCheckoutPayPalPayPalModule, PayPalPlugin, PayPalCoordinator
PurseCheckoutKlarnaKlarnaModule, KlarnaPlugin, KlarnaCoordinator, KlarnaAuthorizationDeclined

Since 1.1.0, a release tag resolves PurseHeadlessCheckout, PurseCheckoutApplePay and PurseCheckoutKlarna to signed XCFrameworks; PurseCheckoutPayPal is built from source. Because the SDK is binary, its public enums are not frozen: an exhaustive switch over one needs an @unknown default. The three partner products are optional: a card-only merchant links no PassKit, no PayPal SDK and no Klarna SDK. Requirements are iOS 16+, Swift 6 and Xcode 16+.

Creation​

public convenience init(
widgetData: String,
lockedEnvironment: PurseEnvironment? = nil,
disabledMethods: Set<MethodKey> = [],
hooks: PurseCheckoutHooks? = nil,
registry: PluginRegistry = .shared,
secondaryConfigs: SecondaryConfigs = .shared,
instanceId: String = UUID().uuidString,
urlSession: URLSession = .shared,
walletAPIRootOverride: String? = nil,
monitoringAPIRootOverride: String? = nil,
restored: Bool = false,
options: PluginOptions = PluginOptions()
) throws

public static func restore(
_ state: CheckoutSavedState,
hooks: PurseCheckoutHooks? = nil,
registry: PluginRegistry = .shared,
secondaryConfigs: SecondaryConfigs = .shared,
urlSession: URLSession = .shared,
options: PluginOptions = PluginOptions()
) throws -> PurseHeadlessCheckout
ParameterMeaning
widgetDataThe base64 widget.data string of the client session
lockedEnvironmentOverrides the environment the session declares; release builds should pass .production
disabledMethodsMethodKey pairs this build never lists, whatever the session offers; scoped to the instance
hooksonBeforeValidate, onAfterValidate, onRedirection
registry, secondaryConfigsSubstitute a registry or issuer table; both default to the shared singleton
optionsPluginOptions(locale:theme:cardFormTexts:), handed to every plugin the checkout builds; not part of a saved state, so pass it to restore again
stateA CheckoutSavedState from saveState(), or CheckoutSavedState.fromJSON(_:)

SDKVersion.value is the SDK version stamped on every monitoring event; checkout.instanceId identifies one checkout in Purse's monitoring. The class is @MainActor and an ObservableObject.

Published state​

PropertyTypeChanges when
sessionPaymentSessionThe session in force; emits again after setSession(_:)
sessionStateSessionState.ready → .submitting → .submitted / .error; .expired, .terminated
paymentMethods[PaymentItem]Primaries and gift card methods, each with its live disabled state
paymentTokens[PaymentToken]Saved cards and saved PayPal accounts
savedSecondaryTokens[SavedSecondaryToken]Saved gift cards, balances resolving in the background
remainingAmountToPayDecimalAfter setPrimary, applySecondary, removeSecondary
amountSplit[PaymentSplit]The slices submitPayment() will send
isPaymentFulfilledBoolThe split covers the amount and every form in it is valid
itemGenerations[String: Int]Bumped by setSession(_:) for items that were rebuilt; key your PaymentElementView on it
isTornDownBoolAfter teardown()

Plain properties: instanceId, params, disabledMethods, options, onPluginEvent, paymentAPIRoot, currency, isInformationRequest, currentSessionId, secondaryMethods, appliedSecondaries, unfulfilledItemId.

Functions​

GroupFunctions
SplitsetPrimary(_:), setPrimaryToken(_:), clearPrimarySplit(), applySecondary(_:amount:), removeSecondary(_:) (both for SecondaryToken and SavedSecondaryToken)
Gift cardssecondaryConfig(for:), getSecondaryToken(method:pan:cvv:) (async), registerSecondary(_:registered:name:), registrationOf(_:)
Savingregister(_:registered:name:), registrationOf(_:)
Wallet (async)editToken(_:name:), setTokenFavorite(_:), deleteToken(_:), deleteAllTokens(); editToken also takes a SavedSecondaryToken
SubmitsubmitPayment(skipOnBeforeValidate:) (async), pendingRedirect(for:), presentRedirectIfNeeded(…) (async)
BNPL (async)simulateLoan(_:product:), simulateLoans(_:product:)
SessionsetSession(_:) (synchronous, throws), saveState(), teardown(), syncServerClock() (async)
BadgesiconURL(for:), schemeAssets(for:)
ElementspaymentElement(for:listener:redirect:appReturnURL:), host(for:) for a coordinator you drive yourself, setCollected(_:_:), setFulfilled(_:_:)

Badges​

public func iconURL(for item: PaymentItem) -> String
public func schemeAssets(for method: PrimaryMethod) -> [PurseAssets.CardSchemeAsset]

CDN URL builders resolved for the session's environment, ported from web's @upstreampay/assets. The SDK ships no images: loading and rendering the SVGs is yours. A card brand with no badge is left out rather than pointed at a URL that would 404. PurseAssets also exposes cdnBase(_:), badgeURL(_:badgeName:), resolveIconURL(partner:method:isSecondary:environment:) and buildCardSchemeAssets(brands:environment:) directly.

Payment element​

public func paymentElement(
for item: PaymentItem,
listener: any PaymentElementListener,
redirect: (any RedirectPresenter)? = nil,
appReturnURL: URL? = nil
) -> PaymentElement

Build an element when the shopper selects an item, not for every row: building runs the partner's prepare, which for Klarna and Apple Pay is a POST /init. A redirection method's /init runs later, at submit.

PaymentElement exposes item, hasUI, selfSubmitting, the published isReady, makeView() -> UIView?, prepare() (idempotent, async) and submit(), which returns the Task it runs on. PaymentElementView is the UIViewRepresentable that renders it in SwiftUI; give it an .id that changes with the item's generation.

PaymentElementListener is a @MainActor protocol with onReady(_:), onOutcome(_:), onRequiresAction() and onPaymentId(_:), every member defaulted.

public enum PaymentOutcome {
case completed(ValidationResult) // status: PaymentStatus, isSuccess
case returnedFromRedirect(url: URL?) // not an outcome: confirm the status on your backend
case canceled
case failed(any PurseError) // error.code: PurseErrorCode
}

Coordinators, for an app that draws its own wallet button or drives a native step itself: CardCoordinator and the redirect presenters in the core product, ApplePayCoordinator, PayPalCoordinator and KlarnaCoordinator in their own. Each takes a PluginHost from checkout.host(for:).

Plugins and partner modules​

PluginRegistry.shared.install(ApplePayModule(configuration: applePayConfiguration))
PluginRegistry.shared.install(PayPalModule())
PluginRegistry.shared.install(KlarnaModule())

Swift has no ServiceLoader, so a partner product is installed explicitly, never discovered. PluginRegistry resolves a plugin in the same order as web and Android (vault vendor, template, redirection integration mode, exact (partner, method), method fallback) through registerVault(vendor:builder:), registerGeneric(template:builder:), register(partner:method:builder:) and registerFallback(method:builder:). modules lists what is installed, in order; installing the same module twice is a no-op. Card and redirection are built in.

PartnerModule is the registration entry point (id, register(into:), renders(_:), surface(for:request:)); PartnerUIPlugin is the payment contract (preValidation, postValidation, onSessionSet, onAmountUpdate, validateUI, ensureClientCompatibility, destroyClient), and PluginHost is what a coordinator asks of the checkout. SecondaryConfigs.register adds a gift card issuer through the SecondaryConfig protocol.

Plugin events​

public var onPluginEvent: ((CheckoutPluginEvent) -> Void)?

public struct CheckoutPluginEvent {
public let itemId: String
public let event: PluginEvent
}

public enum PluginEvent {
case ready
case redirect
case initFailed(message: String?)
case clientError(message: String?)
case partnerError(message: String?, recoverable: Bool)
case cancelled
case endOfSession
}

A coordinator reports its partner's native step through PluginHost.dispatch(_:). The checkout reacts first, then calls onPluginEvent: initFailed and clientError disable the method with PARTNER_UI_FATAL_ERROR, partnerError with PARTNER_UI_RECOVERABLE_ERROR or PARTNER_UI_FATAL_ERROR depending on recoverable, ready clears a recoverable state, and endOfSession sets sessionState to .terminated. PluginEvent.code is the wire name ("InitFailed", …) shared with Android, sent on HEADLESS_CHECKOUT_PARTNER_UI_EVENT. See Method states.

Types​

TypeNotes
PaymentItem (enum)primaryMethod, secondaryMethod, paymentToken, savedSecondaryToken; id, partner, method, configuration, disabled, isSecondary, minAmount, maxAmount
PrimaryMethodcanBeRegistered, saveTokenMode, simulable, integrationType, supportedBrands
PaymentTokenisVaultedCard, requiresCVV, paysWithoutCVV, label, brand, maskedPAN, holderName, favorite, tokenSource, methodId
SecondaryTokenpan, cvv, balance, takePolicy, isSaved, canBeRegistered
SavedSecondaryTokenpan, name, balance, totalBalance, balanceResolved, favorite, disabled
DisabledState (enum)code, message, isFiltered, isTechnicallyDisabled, requiresUIClear, isRecoverable, isRecomputable; see Method states
PaymentSplit, SplitSource, PrimarySourceamount, source; what submitPayment() sends
MethodKeypartner + method; id is the item id ("paypal-wallet")
Partner (enum).purse, .aci, .adyen, .paypal, .illicado, .klarna, and .other(wireValue) for everything else; wireValue, fromWire(_:)
PaymentMethod (enum).creditCard, .applePay, .wallet, .giftCard, .bnpl, .installments, and .other(wireValue); wireValue, fromWire(_:)
PaymentSession, SessionStateid, amount, currency, expirationDate, meansOfPayment; .ready, .expired, .submitting, .submitted, .terminated, .error
CheckoutSavedStateSnapshot from saveState(); toJSON(), fromJSON(_:), savedAt. Holds no card data
LoanProduct, LoanSimulationInput and output of BNPL simulation
ValidationResultglobalStatus, status: PaymentStatus, isSuccess, redirection, raw
PaymentStatus (enum).success, .failed, .error, .uncertain, .waiting, .unknown: the API's global_status
RedirectAction (enum).get, .postForm, .postHTML, each with a returnURLPrefix; what onRedirection receives
RedirectPresenter, WebAuthRedirectPresenter, RedirectOutcome, RedirectPolicyPresenting a 3-D Secure or PSP page, and what came back
PurseEnvironment (enum).development, .test, .sandbox, .production, each with its API roots
PurseError (protocol)code: PurseErrorCode, message, underlying, documentationLink; conformers in Error codes
PurseCheckoutHooksonBeforeValidate, onAfterValidate, onRedirection
CheckoutEventCode, EventLevel, CheckoutMonitoringEventThe monitoring vocabulary reported to Purse

Presentation​

PurseTheme maps native UIColor and UIFont onto Secure Fields' own style: input, inputFocus, inputInvalid, inputValid, label, labelInvalid, labelValid, helperText, helperTextInvalid, gap, plus global* fallbacks, with PurseTheme.default and resolve(). It travels with CardFormTexts inside PluginOptions(locale:theme:cardFormTexts:).

Pass PluginOptions to PurseHeadlessCheckout(widgetData:…, options:) (and to restore(_:…, options:)): every plugin the checkout builds receives it, so a card form from paymentElement(for:) is themed, and its locale is also the one sent in a 3-D Secure step's browser data. A CardCoordinator(host:tenantId:supportedBrands:token:options:) you drive yourself takes its own.