> ## 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.

# PrimerCheckout

> SwiftUI managed-modal checkout view that renders Primer’s default screens

> [!WARNING]
>
> Primer Checkout iOS SDK is currently in **beta** (v3.0.0-beta.8).
> The API is subject to change before the stable release.

`PrimerCheckout` is the prebuilt, fully managed SwiftUI flow. Drop it into your view hierarchy with a client token and it renders Primer's default screens end to end — splash, payment method selection, card form, processing, and the success and error results — including 3DS and redirect handling.

This is the fastest way to integrate. You do not compose any screens or wire up sessions yourself; the view manages its own lifecycle and reports the outcome through `onCompletion`.

When the client session offers only one payment method and the shopper has no saved payment methods, `PrimerCheckout` skips the selection screen and starts that method directly. A card opens the card form, and PayPal launches at once. If the shopper has saved payment methods, the selection screen opens so they can pay with one.

> [!NOTE]
>
> `PrimerCheckout` renders the SDK's default screens and does not expose the composable views or their `@ViewBuilder` slots. For full UI customization, embed views such as [`PrimerCardForm`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/primer-card-form) and [`PrimerPaymentMethods`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/primer-payment-methods) in your own layout and wire them up with [`PrimerCheckoutSession`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session).

## Declaration

```swift theme={"dark"}
@available(iOS 15.0, *)
@MainActor
public struct PrimerCheckout: View
```

## Initializer

```swift theme={"dark"}
public init(
  clientToken: String,
  primerSettings: PrimerSettings = PrimerSettings(),
  primerTheme: PrimerCheckoutTheme = PrimerCheckoutTheme(),
  onCompletion: ((PrimerCheckoutState) -> Void)? = nil,
  onShippingAddressChange: ShippingAddressChangeHandler? = nil,
  onShippingOptionChange: ShippingOptionChangeHandler? = nil
)
```

### Parameters

| Parameter | Type | Default | Description |
| - | - | - | - |
| `clientToken` | `String` | Required | The client token obtained from your backend. See [Client session](https://primer.io/docs/checkout/client-session.md). |
| `primerSettings` | [`PrimerSettings`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/common/primer-settings) | `PrimerSettings()` | Configuration settings including payment options and UI preferences. |
| `primerTheme` | [`PrimerCheckoutTheme`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/theming/primer-theme) | `PrimerCheckoutTheme()` | Theme configuration for design tokens applied across all default screens. |
| `onCompletion` | `((PrimerCheckoutState) -> Void)?` | `nil` | Optional callback for the checkout outcome. It receives `.failure` once for each failed attempt, including a failed initialization. `.success` or `.dismissed` arrives exactly once and ends the flow. The lifecycle states (`.initializing`, `.ready`) are not delivered here. See [`PrimerCheckoutState`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-state). |
| `onShippingAddressChange` | `ShippingAddressChangeHandler?` | `nil` | Express Checkout: supplies the shipping options for the address the shopper entered in the wallet sheet. See [Express Checkout shipping](https://primer.io/docs/checkout/primer-checkout/guides-and-recipes/express-checkout-shipping.md). |
| `onShippingOptionChange` | `ShippingOptionChangeHandler?` | `nil` | Express Checkout: commits the option the shopper selected, through your backend. |

> [!NOTE]
>
> `PrimerCheckout` requires iOS 15.0 or later and must be used on the main actor, like any SwiftUI `View`.

## Usage

### Minimal

Present the managed flow with just a client token. All other parameters use their defaults.

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

struct CheckoutView: View {
  let clientToken: String

  var body: some View {
    PrimerCheckout(clientToken: clientToken)
  }
}
```

### With settings and theme

Customize payment options and design tokens by passing `primerSettings` and `primerTheme`.

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

struct CheckoutView: View {
  let clientToken: String

  var body: some View {
    PrimerCheckout(
      clientToken: clientToken,
      primerSettings: PrimerSettings(),
      primerTheme: PrimerCheckoutTheme()
    )
  }
}
```

### Handling the result

Pass an `onCompletion` closure and switch over the [`PrimerCheckoutState`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-state) it delivers. `.failure` arrives once for each failed attempt. With the default error screen, the shopper can retry, so do not close the checkout on it. `.success` or `.dismissed` ends the flow and arrives exactly once.

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

struct CheckoutView: View {
  let clientToken: String
  @Environment(\.dismiss) private var dismiss

  var body: some View {
    PrimerCheckout(clientToken: clientToken) { state in
      switch state {
      case let .success(result):
        print("Payment succeeded: \(result.paymentId)")
        dismiss()
      case let .failure(error, _):
        print("Payment failed: \(error.localizedDescription)")
      case .dismissed:
        dismiss()
      default:
        break
      }
    }
  }
}
```

> [!NOTE]
>
> By default, a failure shows the SDK error screen, and the shopper can retry from there. If you turn it off with `primerSettings: PrimerSettings(uiOptions: PrimerUIOptions(isErrorScreenEnabled: false))`, `PrimerCheckout` shows nothing after a failed initialization or a failed payment. Your app only receives `.failure` and must close the checkout or show its own UI.

> [!TIP]
>
> See [Handle the payment result](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/guides/handle-payment-result.md) for a full walkthrough of consuming each `PrimerCheckoutState` case.

## Presenting modally

`PrimerCheckout` is an ordinary SwiftUI `View`, so present it however your navigation pattern prefers — for example, in a `.sheet`.

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

struct StorefrontView: View {
  @State private var isCheckoutPresented = false
  let clientToken: String

  var body: some View {
    Button("Pay") { isCheckoutPresented = true }
      .sheet(isPresented: $isCheckoutPresented) {
        PrimerCheckout(clientToken: clientToken) { state in
          if case .dismissed = state { isCheckoutPresented = false }
          if case .success = state { isCheckoutPresented = false }
        }
      }
  }
}
```

While a payment is processing, the shopper cannot swipe the sheet away. At other times the shopper can swipe it away, and SwiftUI resets `isCheckoutPresented`. A swipe does not report `.dismissed`. `.dismissed` is delivered when the shopper closes the checkout from the SDK's own screens, such as the close button. To show the close button, pass `primerSettings: PrimerSettings(uiOptions: PrimerUIOptions(dismissalMechanism: [.closeButton]))`. The default is `[.gestures]`, which shows no close button. Apple Pay, Stripe ACH and QR code methods such as PromptPay do not show the processing screen, so the sheet can still be swiped away during those payments.

## Customizing the UI

`PrimerCheckout` does not accept screen slots. When you need to control layout, styling, or which fields appear, build your own UI from the composable views and drive them with a session instead.

| Goal | Use |
| - | - |
| Fastest integration, default screens | `PrimerCheckout` (this page) |
| Custom SwiftUI layout and slots | [`PrimerCheckoutSession`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session) + [`PrimerCardForm`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/primer-card-form) / [`PrimerPaymentMethods`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/primer-payment-methods) |
| UIKit apps | [`PrimerCheckoutPresenter`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-presenter) |

For the composable approach, create a `PrimerCheckoutSession` as a `@StateObject`, place the composable views in your layout, and attach the `.primerCheckoutSession(_:theme:onCompletion:)` modifier. The modifier injects the session that those views resolve from the environment.

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

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

  init(clientToken: String) {
    _session = StateObject(wrappedValue: PrimerCheckoutSession(clientToken: clientToken))
  }

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

  private func handle(_ state: PrimerCheckoutState) {
    // .failure after .ready arrives once per failed payment attempt, then .success or .dismissed arrives once.
    // A failed initialization is final: .failure arrives and nothing follows.
  }
}
```

## See also

> [card] [PrimerCheckoutSession](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session.md)
>
> Compose custom UI from the SwiftUI views.

> [card] [PrimerCheckoutPresenter](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-presenter.md)
>
> Present the managed flow from UIKit.

> [card] [PrimerCheckoutState](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-state.md)
>
> The states delivered to onCompletion.

> [card] [PrimerCheckoutTheme](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/theming/primer-theme.md)
>
> Style the default screens with design tokens.


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