Minimum deployment target: iOS 14.0
Swift version: 5.0+
Distribution: Swift Package Manager (signed XCFramework)
Installation
Swift Package Manager, using the package URL:Payrails.xcframework and verifies it against the checksum in the package manifest. PayrailsCSE and PayPalCheckout resolve as separate package dependencies.
Getting started
Payrails.InitData
Holds the init payload returned by your backend after calling the Payrails initialization endpoint.
Payrails.Options
Runtime options passed to Configuration.
Payrails.Configuration
Wraps InitData and Options as the input to createSession.
Payrails.createSession(with:onSessionExpired:onRequestStart:)
Creates and stores a session. All factory methods use the most recently created session.
onSessionExpired closure lets the SDK self-heal when the underlying Payrails execution becomes unreusable (typically: user abandoned a 3DS challenge and the backend execution stayed in authorizePending). The merchant supplies a closure that fetches fresh InitData from their backend; the SDK swaps its internal config in place — the merchant’s Session reference and cached buttons / forms keep working unchanged.
Session will fail naturally.
onRequestStart
Optional gate invoked once per payment attempt, before the authorization request is sent and before any provider UI (wallet sheet, PayPal sheet, redirect) is presented. Calling completion(.proceed) lets the attempt continue; completion(.refuse()) stops it.
The gate fires for every payment method configured on the session. A handler that only gates one method must call completion(.proceed) on the other branches.
Action.tokenize is reserved. The tokenization flow is not gated in this version, so action is always .authorize today.
refuse(message:)’s message, when supplied, becomes AuthorizationFailure.message on the delivered .validationFailed. It is passed through verbatim and is not displayed by the SDK.
The timeout case does not carry the SDK’s diagnostic into
failure.message: it describes an integration fault rather than something phrased for a customer. Only a deliberate .refuse(message:) travels outward.
When the attempt is stopped, no authorization request is sent, isPaymentInProgress returns to false, and the initiating element’s delegate receives onAuthorizeFailed(_:failure:) with failure.code == .validationFailed.
Omitting the handler leaves the payment path fully synchronous — the SDK skips the gate rather than taking an asynchronous detour.
Session
Payrails.Session is returned from createSession and is the single typed API surface for headless integrations.
When to use query(_:) vs session methods directly:
query(_:)— stateless reads of session metadata (holder reference, amount, execution ID, API links, payment method config, stored instruments). Single unified accessor returning aPayrailsQueryResultenum.- Session methods — actions and mutations (
executePayment,tokenize,deleteInstrument,updateInstrument,update), device-capability checks (isApplePayAvailable), or typed reads where merchants prefer concrete return types over an enum (getPaymentMethodConfig(_:)).
query(_:) reads data; session methods do things, check the device, or return typed values.
Payment types
Factory methods
All factory methods are static methods onPayrails and require an active session.
Card form
Payrails.CardForm is a UIStackView subclass. Add it to your view hierarchy and constrain with Auto Layout.
Card payment button
Apple Pay button
PayPal button
Generic redirect button
Stored instruments
Static helpers
Instrument management (delete/update) moved to typed Session methods in 1.28.0. Usesession.deleteInstrument(instrumentId:)andsession.updateInstrument(instrumentId:body:)directly — see the Session block above.
Query API
PayrailsQueryKey
PaymentMethodFilter
PayrailsQueryResult
Supporting types
How to Query Session Data
Payrails.query(_:) provides read-only access to the current session’s configuration and state. Use it to retrieve the execution ID, payment amount, stored instruments, API links, and more — without reaching into internal session state.
When to use query(_:) vs Session methods
- Use
query(_:)for stateless reads of session metadata:.holderReference,.amount,.executionId,.binLookup,.paymentMethodConfig(...),.paymentMethodInstruments(...). - Use Session methods directly for actions (
executePayment,deleteInstrument,updateInstrument,update), device-capability checks (isApplePayAvailable), or typed reads where merchants prefer concrete return types over an enum (getPaymentMethodConfig(_:)).
query(_:) reads data. Session methods do things, check the device, or return typed values where an enum would add friction.
Prerequisites
An active session must exist (created viaPayrails.createSession(with:)). All queries return nil when no session is active.
Calling Payrails.query
PayrailsQueryResult?, a Swift enum. Switch on it to extract the typed value:
Available query keys
.executionId
The execution ID for the current checkout. Pass this to your backend for order correlation.
.holderReference
The holder reference (customer identifier) associated with this session.
.amount
The payment amount and currency for the current execution.
.binLookup
The API link for BIN lookup. Use this to call the lookup endpoint and determine card network, country, and 3DS requirements before payment.
.instrumentDelete
The API link for deleting a stored instrument.
.instrumentUpdate
The API link for updating a stored instrument (e.g. setting as default).
.paymentMethodConfig(filter:)
Configuration for available payment methods, filtered by a PaymentMethodFilter.
.paymentMethodInstruments(type:)
The stored instruments for a given payment type.
Summary table
Updating session state
How to Update the Checkout Amount
The checkout amount is set when the session is initialized from the init payload. If the amount changes after initialization — for example, the user adds a tip, chooses express shipping, or applies a discount code — you must update both your backend and the SDK in lockstep.How amount updates work
Updating the amount is a two-step process:Step 1: Recalculate the amount
Step 2: Update the amount on your backend
Call your backend, which calls the Payrails API to update the execution amount. The exact endpoint and request shape are defined by your backend implementation.Step 3: Update the SDK amount
After the backend confirms the update, sync the SDK:Complete example: tip selection
After a redirect session recovery
If the user’s payment involved a redirect (e.g. PayPal, generic redirect) and the app returned from the background, the session may be restored from the original init payload. In this case:- Any in-memory amount updates made via
Payrails.update()are reset to the original init payload amount. - If you need to preserve the updated amount after a redirect, you must re-apply
Payrails.update()once the session is restored.
Troubleshooting
Payment rejected with 401 / authorization error The SDK amount does not match the Payrails execution amount. Verify that your backend update completed successfully before callingPayrails.update().
Payrails.update() appears to have no effect
If there is no active session, the call is silently dropped. Ensure Payrails.createSession() has completed successfully before calling update(). Check for any No active Payrails session log messages.
Amount label does not update
Payrails.update() updates the internal SDK state; it does not automatically refresh any UI element. Update your amount label independently after recalculating.
Callbacks and results
Payment outcomes are delivered to yourCardPaymentButton / CardPaymentForm / StoredInstrumentPaymentButton / GenericRedirectButton delegate. Failures arrive as an AuthorizationFailure struct — a flat { code, message, rawError } shape that mirrors the Web SDK’s onFailed payload:
Client-side input validation never reaches this path: an element early-returns on an invalid form rather than emitting a failure..validationFailedis reserved for anonRequestStarthandler blocking the attempt.
Delegate callbacks fired
When the user dismisses the 3DS sheet (or any other path that leaves the Payrails execution in
authorizePending), the SDK additionally invokes the merchant’s onSessionExpired closure (supplied at createSession) in the background to rebuild its internal config in place — the merchant’s Session reference keeps working. If the closure was not supplied, the SDK logs a warning at createSession time and the next payment attempt against the Session will fail naturally against the dead execution.
Payment presenter protocol
Required to present view controllers during payment (e.g. 3DS challenges):UIViewController:
Delegate protocols
onPaymentButtonClickedis a notification, not a gate. It tells you the customer tapped, for analytics, observability or showing a spinner. It returnsVoidand the SDK does not wait for it, so it cannot stop or defer a payment. To make the payment conditional on your own check, useonRequestStart— the SDK awaits that one and honours its answer.
PayrailsCardPaymentButtonDelegate
PayrailsCardFormDelegate
didChangePreferredScheme is optional — a protocol-extension default provides a no-op, so implementations that predate co-branded support continue to compile. See Co-branded cards.
Co-branded cards
Emitted by the card form when a card carries two payment networks. RequiresfeatureConfig.coBrandedCardsRollout and a links.binLookup entry in the session init response; absent either, none of this is produced.
cardForm(_:didChangePreferredScheme:) fires when a co-branded BIN resolves, when the shopper selects a different brand, and when the card number changes such that co-branded state clears. The clearing case delivers an empty cardSchemes and a nil preferredScheme.
The selected scheme is carried into the authorization request by the SDK. Callers do not pass it to any payment method.
Styling is CardFormStyle.cardBrandSelector: CardBrandSelectorStyle?; nil keeps the SDK default. The selector’s heading and subheading are CardTranslations.Labels.cardBrandSelectorTitle and .cardBrandSelectorSubtitle; each falls back to an SDK default when nil.
PayrailsApplePayButtonDelegate
PayrailsPayPalButtonDelegate
PayrailsStoredInstrumentsDelegate
Error handling
PayrailsError
PayrailsError conforms to LocalizedError; use error.errorDescription for a human-readable message.
Tokenization
Saves a payment method as a reusable instrument without charging the customer.tokenize is unified across methods: the TokenizationRequest case selects which instrument to tokenize and carries what that method needs. Both overloads live on Payrails.Session (see Session), alongside executePayment.