Skip to main content
Primer Checkout iOS SDK is currently in beta (v3.0.0-beta.1). The API is subject to change before the stable release.
PrimerCheckoutState is a public, Equatable enum that represents the current state of the checkout flow. It captures both the lifecycle of the session (initializing, ready) and its terminal outcome (success, failure, dismissed). Your onCompletion closure on PrimerCheckout or on the .primerCheckoutSession(_:onCompletion:) modifier fires exactly once per session, and only with a terminal state: .success, .failure, or .dismissed. The lifecycle states .initializing and .ready are part of the enum’s definition but are never delivered to onCompletion; they describe internal progress the SDK drives on your behalf.
On iOS, a single PrimerCheckoutState enum covers what the Android SDK splits across PrimerCheckoutState (lifecycle) and PrimerCheckoutEvent (outcomes). The .initializing and .ready cases mirror the lifecycle; .success, .failure, and .dismissed mirror the outcome events delivered to onCompletion.

Definition

@available(iOS 15.0, *)
public enum PrimerCheckoutState: Equatable {
    case initializing
    case ready(totalAmount: Int, currencyCode: String)
    case success(PaymentResult)
    case dismissed
    case failure(PrimerError)
}

States

StateAssociated valuesDescription
.initializingInitial state while loading configuration and payment methods. The SDK is fetching the client session and preparing available payment methods.
.readytotalAmount: Int, currencyCode: StringCheckout is fully initialized and payment methods are loaded. The session is ready for payment.
.successPaymentResultPayment completed successfully. Carries the full payment result with payment ID, status, and other details.
.dismissedTerminal state. Checkout was dismissed by user action or programmatically without completing a payment.
.failurePrimerErrorPayment or checkout failed. Carries the specific error with diagnostics information for debugging.

.ready associated values

ParameterTypeDescription
totalAmountIntThe total payment amount in minor units (e.g., cents for USD — 1000 = $10.00).
currencyCodeStringThe ISO 4217 currency code (e.g., "USD", "EUR", "GBP").

Lifecycle

A checkout session progresses from the lifecycle states to one of three mutually exclusive terminal outcomes:
initializing → ready → success | failure | dismissed
.success, .failure, and .dismissed are parallel terminal branches — a session ends in exactly one of them.
TransitionWhen
.initializing.readyConfiguration and payment methods have loaded.
.ready.successPayment is confirmed by the processor (automatic payment handling).
.ready.failureA payment error or SDK error occurs.
→ .dismissedThe user closes checkout, or you dismiss it programmatically, without completing a payment.
.success, .failure, and .dismissed are terminal. The onCompletion closure receives exactly one of them per session. The lifecycle states .initializing and .ready are never passed to onCompletion.

Handling completion

Pass an onCompletion closure when you present checkout. The closure fires once, with one of the terminal states (.success, .failure, or .dismissed).
import SwiftUI
import PrimerSDK

struct CheckoutView: View {
    @StateObject private var session = PrimerCheckoutSession(clientToken: clientToken)

    var body: some View {
        ScrollView {
            PrimerCardForm()
        }
        .primerCheckoutSession(session) { state in
            handle(state)
        }
    }

    private func handle(_ state: PrimerCheckoutState) {
        switch state {
        case let .success(result):
            print("Payment \(result.paymentId) succeeded")
        case let .failure(error):
            print("Checkout failed: \(error.localizedDescription)")
        case .dismissed:
            print("Checkout dismissed")
        default:
            // .initializing and .ready are lifecycle states and are
            // never delivered here.
            break
        }
    }
}
onCompletion only delivers terminal states, but a switch on PrimerCheckoutState must still be exhaustive. Cover the lifecycle cases with a default: (or @unknown default:) so future state additions don’t break compilation.

PaymentResult

PaymentResult is the value carried by .success. It is a public, Sendable, Equatable struct describing the completed payment.
public struct PaymentResult: Sendable, Equatable {
    public let paymentId: String
    public let status: PaymentStatus
    public let token: String?
    public let redirectUrl: String?
    public let errorMessage: String?
    public let amount: Int?
    public let currencyCode: String?
    public let paymentMethodType: String?
}
PropertyTypeDescription
paymentIdStringUnique payment identifier assigned by Primer.
statusPaymentStatusThe final status of the payment.
tokenString?The payment method token, when available.
redirectUrlString?A redirect URL associated with the payment, when applicable.
errorMessageString?A human-readable error message, when the payment did not complete cleanly.
amountInt?The payment amount in minor units, when available.
currencyCodeString?The ISO 4217 currency code, when available.
paymentMethodTypeString?The payment method type identifier used for the payment (e.g., "PAYMENT_CARD").

PaymentStatus

PaymentStatus is a public, Sendable enum describing the final status of a payment.
public enum PaymentStatus: Sendable {
    case pending
    case success
    case failed
}
CaseDescription
.pendingThe payment is pending and awaiting further processing or confirmation.
.successThe payment completed successfully.
.failedThe payment failed.
Always include a default: (or @unknown default:) case when switching on PaymentStatus to handle future additions.

Handling the result

Use the .success payload to read the payment details and route your UI:
private func handleCompletion(_ state: PrimerCheckoutState) {
    switch state {
    case let .success(result):
        switch result.status {
        case .success:
            showReceipt(paymentId: result.paymentId)
        case .pending:
            showPendingScreen(paymentId: result.paymentId)
        case .failed:
            showErrorScreen(message: result.errorMessage)
        @unknown default:
            break
        }
    case let .failure(error):
        showErrorScreen(message: error.localizedDescription)
    case .dismissed:
        dismiss()
    default:
        // .initializing and .ready are lifecycle states and are
        // never delivered to onCompletion.
        break
    }
}
For a full integration walkthrough, including manual payment handling, see Handle payment result.

See also

PrimerCheckout

Present checkout and receive the final state through onCompletion.

PrimerError

Error details carried by the .failure state.

Handle payment result

Route your UI based on the checkout outcome.

PrimerCheckoutSession

Drive the inline checkout lifecycle from your own layout.