Skip to main content
Current version: 3.0.0
Minimum deployment target: iOS 14.0
Swift version: 5.0+
Distribution: Swift Package Manager (signed XCFramework)

Installation

Swift Package Manager, using the package URL:
The package resolves a prebuilt, signed 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.
The 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.
If the closure is omitted, the SDK logs a warning at init time and cannot recover from a poisoned execution — the next payment attempt against that 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 a PayrailsQueryResult enum.
  • 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(_:)).
Rule of thumb: query(_:) reads data; session methods do things, check the device, or return typed values.

Payment types


Factory methods

All factory methods are static methods on Payrails 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. Use session.deleteInstrument(instrumentId:) and session.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(_:)).
Rule of thumb: 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 via Payrails.createSession(with:)). All queries return nil when no session is active.

Calling Payrails.query

The return type is 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.
ImportantThe SDK amount and the amount recorded in the Payrails execution must match. A mismatch causes the payment to be rejected with a 401 error.

How amount updates work

Updating the amount is a two-step process:
Both steps must complete before the user initiates payment.

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 calling Payrails.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 your CardPaymentButton / 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. .validationFailed is reserved for an onRequestStart handler 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):
Typically conformed to by a UIViewController:

Delegate protocols

onPaymentButtonClicked is a notification, not a gate. It tells you the customer tapped, for analytics, observability or showing a spinner. It returns Void and the SDK does not wait for it, so it cannot stop or defer a payment. To make the payment conditional on your own check, use onRequestStart — the SDK awaits that one and honours its answer.

PayrailsCardPaymentButtonDelegate

Breaking change in ONB-739The pre-ONB-739 signature was onAuthorizeFailed(_ button: Payrails.CardPaymentButton) with no payload. Merchants migrating from earlier versions must update their delegate conformance to take failure: AuthorizationFailure and (if they relied on session-expiry signaling) supply the onSessionExpired closure to createSession. See the card-payment-flow documentation for a migration example.

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. Requires featureConfig.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

DeprecatedonAuthorizeFailed(_ button: Payrails.ApplePayButton) with no payload. A default implementation forwards to it, so existing integrations keep receiving failures, but only the failure: variant carries the discriminating code — the sole way to distinguish an onRequestStart block from an issuer decline.

PayrailsPayPalButtonDelegate

DeprecatedonAuthorizeFailed(_ button: Payrails.PayPalButton) with no payload. Same forwarding default and same reasoning as Apple Pay above.

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.

Instrument management

Call the typed Session methods to manage instruments:

Card form configuration

See Styling Guide for full details.

Debug

Last modified on September 30, 2026