> ## Documentation Index
> Fetch the complete documentation index at: https://primer.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# PrimerCheckoutState

> Checkout lifecycle and outcome states, with the payment result model

> [!WARNING]
>
> Primer Checkout iOS SDK is currently in **beta** (v3.0.0-beta.8).
> 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 **outcome** (success, failure, dismissed).

Your `onCompletion` closure on [`PrimerCheckout`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout) or on the `.primerCheckoutSession(_:theme:onCompletion:)` modifier receives the **outcome** states `.success`, `.failure` and `.dismissed`. To follow the **lifecycle** states `.initializing` and `.ready`, read [`PrimerCheckoutSession.phase`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session).

`.success` and `.dismissed` end the session and are delivered at most once. `.failure` depends on when it happens: **once the checkout is ready, a failed payment attempt does not end the session** — the SDK shows its error screen, offers a retry, and your closure is called again with the outcome of each further attempt. An initialization failure (before the checkout becomes ready) ends an inline `PrimerCheckoutSession`. `PrimerCheckout` keeps its initialization error view open with a Retry button, so a later outcome can still arrive. With `uiOptions.isErrorScreenEnabled` set to `false`, the SDK shows no error view and your app decides what to show. `PrimerCheckoutPresenter` closes its sheet on the failure instead.

> [!WARNING]
>
> Do not treat a `.failure` after the checkout is ready as the end of the session. If you tear down your checkout UI on it, a customer who retries after a decline and then succeeds will never reach your success handling. Tear down on `.success` and `.dismissed`. On an inline `PrimerCheckoutSession`, also tear down on a `.failure` that arrives before the session first becomes ready.

> [!NOTE]
>
> 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

```swift theme={"dark"}
@available(iOS 15.0, *)
public enum PrimerCheckoutState: Equatable {
    case initializing
    case ready(clientSession: PrimerClientSession)
    case success(PaymentResult)
    case dismissed
    case failure(PrimerError, checkoutData: PrimerCheckoutData? = nil)
}
```

## States

| State | Associated values | Description |
| - | - | - |
| `.initializing` | — | Initial state while loading configuration and payment methods. The SDK is fetching the client session and preparing available payment methods. |
| `.ready` | [`PrimerClientSession`](#primerclientsession) | Checkout is fully initialized and payment methods are loaded. Carries the client session — order total, currency, line items, customer, fees. |
| `.success` | [`PaymentResult`](#paymentresult) | Payment completed successfully. Carries the full payment result with payment ID, status, and other details. |
| `.dismissed` | — | Terminal state. The shopper closed the checkout without completing a payment. |
| `.failure` | [`PrimerError`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-error), [`checkoutData`](#failure-associated-values) | Payment or checkout failed. Carries the specific error with diagnostics information for debugging, and the payment when one was created before the failure. |

### `.ready` associated values

| Parameter | Type | Description |
| - | - | - |
| `clientSession` | [`PrimerClientSession`](#primerclientsession) | The resolved client session: order total, currency, line items, order and customer details, allowed card networks, and fees. |

To read the client session, use [`PrimerCheckoutSession.clientSession`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session). It is `@Published` and becomes non-`nil` as soon as the checkout is ready:

```swift theme={"dark"}
@StateObject private var session: PrimerCheckoutSession

init(clientToken: String) {
    _session = StateObject(wrappedValue: PrimerCheckoutSession(clientToken: clientToken))
}
...
if let clientSession = session.clientSession {
    Text(formatted(clientSession.totalAmount, clientSession.currencyCode))
}
```

`refresh()` renews `clientSession`, and `cancel()` sets it back to `nil`. The value is a snapshot. A client-session update during checkout, such as a surcharge or a billing address, does not update it.

> [!NOTE]
>
> This requires the inline integration. `PrimerCheckout` — the managed modal — renders the SDK's own screens and exposes no session object, so the client session is not reachable from it.

### `.failure` associated values

| Parameter | Type | Description |
| - | - | - |
| (first) | [`PrimerError`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-error) | Error details including description, error ID, and diagnostics ID |
| `checkoutData` | `PrimerCheckoutData?` | Set when the payment was created before failing, for example on a decline. `checkoutData?.payment` has the payment `id`, `orderId`, and the `status` from the last Payments API response. `nil` when the failure happened before a payment existed (initialization, tokenization, connectivity). |

## Lifecycle

A checkout session progresses from the lifecycle states to an outcome. `.success` and `.dismissed` end it. A `.failure` ends it only when initialization fails on an inline `PrimerCheckoutSession`, or when `PrimerCheckoutPresenter` closes its sheet with the error screen off:

```
initializing → ready → success | dismissed        (session over)
                 ↑ ↓
               failure                            (retryable — session continues)
```

| Transition | When | Ends the session |
| - | - | - |
| `.initializing` → `.ready` | Configuration and payment methods have loaded. | — |
| `.initializing` → `.failure` | Initialization fails (bad client token, network, configuration). | Inline `PrimerCheckoutSession`: yes. `PrimerCheckout`: no, its error view offers Retry (shown while `uiOptions.isErrorScreenEnabled` is `true`, the default). With the error screen off, `PrimerCheckout` shows nothing and offers no retry, and `PrimerCheckoutPresenter` closes its sheet. |
| `.ready` → `.success` | Payment is confirmed by the processor (automatic payment handling). | Yes |
| `.ready` → `.failure` | A payment attempt fails. With the SDK error screen on (`uiOptions.isErrorScreenEnabled`, the default), the shopper can retry the failed payment method. When another payment method is available, the shopper can choose it instead. A further attempt delivers another outcome. With the error screen off, the SDK shows no error view and only reports `.failure`, so you decide what to show. `PrimerCheckoutPresenter` also closes its sheet. | No, except `PrimerCheckoutPresenter` with the error screen off, which closes the sheet |
| `→ .dismissed` | The shopper closes the checkout without completing a payment, or you call `PrimerSelectionSession.cancel()`. `PrimerCheckoutSession.cancel()` tears the session down without delivering `.dismissed`. | Yes |

> [!NOTE]
>
> `.success` and `.dismissed` are delivered at most once per session. `.failure` is delivered once per failed attempt. A failure before `.ready` ends an inline `PrimerCheckoutSession`. With the error screen off, any failure ends a `PrimerCheckoutPresenter` checkout. The lifecycle states `.initializing` and `.ready` are never passed to `onCompletion`.

## Handling completion

Pass an `onCompletion` closure when you present checkout. It receives the outcome states — `.success`, `.failure`, or `.dismissed` — and may be called more than once if a payment attempt fails and the customer retries.

```swift theme={"dark"}
import SwiftUI
import PrimerSDK

struct CheckoutView: View {
    @StateObject private var session: PrimerCheckoutSession

    init(clientToken: String) {
        _session = StateObject(wrappedValue: 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:
            break
        }
    }
}
```

> [!TIP]
>
> `onCompletion` only delivers outcome states, but a `switch` on `PrimerCheckoutState` must still be exhaustive. Cover the lifecycle cases `.initializing` and `.ready` with a `default:` case. `@unknown default:` does not cover them: it matches only cases added in a future version, so the compiler still warns that the switch is not exhaustive.

## PaymentResult

`PaymentResult` is the value carried by `.success`. It is a public, `Sendable`, `Equatable` struct describing the completed payment.

```swift theme={"dark"}
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?
}
```

| Property | Type | Description |
| - | - | - |
| `paymentId` | `String` | Unique payment identifier assigned by Primer. |
| `status` | [`PaymentStatus`](#paymentstatus) | The final status of the payment. |
| `token` | `String?` | A token that depends on the payment method, or `nil`. For card and saved payment methods, it equals `paymentId`. To identify the payment, use `paymentId`. |
| `redirectUrl` | `String?` | Always `nil`. The SDK does not set it. |
| `errorMessage` | `String?` | Always `nil`. The SDK does not set it. Read failure details from the `PrimerError` in `.failure`. |
| `amount` | `Int?` | The payment amount in minor units, when available. |
| `currencyCode` | `String?` | The ISO 4217 currency code, when available. |
| `paymentMethodType` | `String?` | 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.

```swift theme={"dark"}
public enum PaymentStatus: Sendable {
    case pending
    case success
    case failed
}
```

| Case | Description |
| - | - |
| `.pending` | The payment is pending and awaiting further processing or confirmation. |
| `.success` | The payment completed successfully. |
| `.failed` | The payment failed. |

> [!NOTE]
>
> 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:

```swift theme={"dark"}
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)
        default:
            break
        }
    case let .failure(error, _):
        // Not the end of the session: the customer can retry, and this
        // closure will be called again with the next attempt's outcome.
        showErrorScreen(message: error.localizedDescription)
    case .dismissed:
        dismiss()
    default:
        break
    }
}
```

For a full integration walkthrough, see [Handle payment result](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/guides/handle-payment-result.md).

## PrimerClientSession

The resolved client session, carried by `.ready` and published on [`PrimerCheckoutSession.clientSession`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session).

```swift theme={"dark"}
@objc public final class PrimerClientSession: NSObject, Codable {
    public let customerId: String?
    public let orderId: String?
    public let currencyCode: String?
    public let totalAmount: Int?
    public let lineItems: [PrimerLineItem]?
    public let orderDetails: PrimerOrder?
    public let customer: PrimerCustomer?
    public let paymentMethod: PrimerClientSession.PaymentMethod?
    public let fees: [PrimerFee]?
}
```

| Property | Type | Description |
| - | - | - |
| `customerId` | `String?` | Customer identifier from your system. |
| `orderId` | `String?` | Order identifier from your system. |
| `currencyCode` | `String?` | ISO 4217 currency code (e.g., `"USD"`, `"EUR"`). |
| `totalAmount` | `Int?` | Total amount in minor currency units (e.g., `1000` = \$10.00). |
| `lineItems` | `[PrimerLineItem]?` | The order's line items. |
| `orderDetails` | `PrimerOrder?` | Country code and shipping details. |
| `customer` | `PrimerCustomer?` | Customer contact details, billing and shipping address. |
| `paymentMethod` | [`PrimerClientSession.PaymentMethod?`](#primerclientsessionpaymentmethod) | Payment-method options, a nested struct. Currently the allowed card networks. |
| `fees` | `[PrimerFee]?` | Fees applied to the order, such as surcharges. |

> [!NOTE]
>
> Every property is optional because the client session itself is merchant-configured — a session created without line items has none. Read them defensively rather than assuming a shape.

### PrimerLineItem

| Property | Type | Description |
| - | - | - |
| `itemId` | `String?` | Your identifier for the item. |
| `itemDescription` | `String?` | Human-readable description. |
| `amount` | `Int?` | Unit amount in minor units. |
| `discountAmount` | `Int?` | Discount applied to this item, in minor units. |
| `quantity` | `Int?` | Number of units. |
| `taxCode` | `String?` | Tax code for this item. |
| `taxAmount` | `Int?` | Tax charged on this item, in minor units. |

> [!WARNING]
>
> `taxCode` and `taxAmount` are per line item. Do not read the order's total tax from them — a three-item order reports each item's own tax, and the values will not sum to a field you can find elsewhere on the session unless your client session was configured that way.

### PrimerOrder

| Property | Type | Description |
| - | - | - |
| `countryCode` | `String?` | The order's ISO 3166-1 alpha-2 country code. |
| `shipping` | `PrimerShipping?` | Shipping `amount`, plus the selected method's `methodId`, `methodName`, and `methodDescription`. |

### PrimerCustomer

| Property | Type | Description |
| - | - | - |
| `emailAddress` | `String?` | Customer email. |
| `mobileNumber` | `String?` | Customer phone number. |
| `firstName` / `lastName` | `String?` | Customer name. |
| `billingAddress` | [`PrimerAddress?`](#primeraddress) | Billing address. |
| `shippingAddress` | [`PrimerAddress?`](#primeraddress) | Shipping address. |

### PrimerAddress

| Property | Type | Description |
| - | - | - |
| `firstName` / `lastName` | `String?` | Name on the address. |
| `addressLine1` / `addressLine2` | `String?` | Street address. |
| `city` | `String?` | City. |
| `state` | `String?` | State, province, or region. |
| `countryCode` | `String?` | ISO 3166-1 alpha-2 country code. |
| `postalCode` | `String?` | Postal or ZIP code. |

### PrimerClientSession.PaymentMethod

A `Codable`, `Equatable` struct nested in `PrimerClientSession`, so name it as `PrimerClientSession.PaymentMethod`.

| Property | Type | Description |
| - | - | - |
| `orderedAllowedCardNetworks` | [`[CardNetwork]`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-card-network) | Card networks the client session allows, in the configured order. A network the SDK does not recognize becomes `.unknown` rather than being dropped, so the list always matches what the session declared. |

### PrimerFee

| Property | Type | Description |
| - | - | - |
| `type` | `String` | The fee type, currently always `"SURCHARGE"`. If the client session contains a fee of a type the SDK does not recognize, `fees` is `nil`. |
| `amount` | `Int` | The fee amount in minor units. |

## See also

> [card] [PrimerCheckout](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout.md)
>
> Present checkout and receive the final state through `onCompletion`.

> [card] [PrimerError](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-error.md)
>
> Error details carried by the `.failure` state.

> [card] [Handle payment result](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/guides/handle-payment-result.md)
>
> Route your UI based on the checkout outcome.

> [card] [PrimerCheckoutSession](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session.md)
>
> Drive the inline checkout lifecycle from your own layout.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.