PrimerCheckoutState value:
The same
PrimerCheckoutState also has lifecycle cases (.initializing, .ready). onCompletion never receives them. With PrimerCheckoutSession, observe its phase for the lifecycle instead. Always include a default case when switching, so future enum additions stay source-compatible.SwiftUI
Using PrimerCheckout
The prebuiltPrimerCheckout view accepts an onCompletion closure. It receives .failure once per failed attempt, including a failed initialization. While the SDK error screen is enabled (uiOptions.isErrorScreenEnabled, the default), the checkout stays open so the customer can retry. It then receives .success or .dismissed exactly once, and nothing after that.
Using the .primerCheckoutSession modifier
When you embed the composable views (for examplePrimerCardForm) inline in your own layout, hold a PrimerCheckoutSession as a @StateObject and receive the outcome through the onCompletion parameter of the .primerCheckoutSession(_:theme:onCompletion:) modifier.
.success and .dismissed are delivered at most once — the session latches the first state that ends it, so a later .dismissed produced by a view-lifecycle teardown is ignored..failure is different. Once checkout is ready, a failed payment attempt does not end the session: the SDK shows its error screen, the customer can retry, and onCompletion is called again with the next attempt’s outcome. Only a failure before the checkout becomes ready is terminal. Keep your checkout UI alive on .failure — otherwise a customer who retries after a decline and then succeeds never reaches your success handling.Navigating on success
Use thePaymentResult to drive navigation. PaymentResult is Equatable but not Hashable, so it cannot be appended to a NavigationPath or used with navigationDestination(for:) directly. Instead, store the result in @State and present the confirmation screen with navigationDestination(isPresented:). NavigationStack and navigationDestination(isPresented:) require iOS 16. On iOS 15, use NavigationView with a NavigationLink(isActive:) instead.
UIKit
In UIKit, present the checkout withPrimerCheckoutPresenter and adopt PrimerCheckoutPresenterDelegate to receive the outcome. The presenter closes its own sheet, so you don’t dismiss it yourself. It finishes dismissing the sheet before it calls primerCheckoutPresenterDidCompleteWithSuccess(_:). primerCheckoutPresenterDidDismiss() is called when the customer closes the checkout without completing a payment. primerCheckoutPresenterDidFailWithError(_:checkoutData:) is called once per failed attempt, including a failed initialization. The sheet stays open on the SDK error screen so the customer can retry. It closes on failure only when uiOptions.isErrorScreenEnabled is false.
PrimerCheckoutState cases:
Reading the PaymentResult
On success you receive aPaymentResult. Read its properties to build your confirmation UI, send analytics, or reconcile against your backend.
amount and currencyCode can be nil, and they are always nil for card, saved and Stripe ACH payment methods. If you need them, read them from your own order or from your backend, using paymentId. The Charged line in the sample below only shows when both values are present.
status is always .success for card, saved and Stripe ACH payment methods in a .success(PaymentResult) outcome. Other payment methods report the status from the Payments API response. It can be .pending until the processor confirms the payment, as with some Klarna, PayPal, web redirect or QR code payments. Check status before you fulfill an order. Primer Checkout creates the payment for you, so PrimerSettings(paymentHandling: .manual) is not supported.Handling the PrimerError
On failure you receive aPrimerError. The SDK already surfaces failures inside the checkout UI, so you typically use the error for logging, analytics, and support workflows rather than for displaying a message yourself.
Together with the error, you receive
checkoutData: PrimerCheckoutData?. In SwiftUI, bind it with case let .failure(error, checkoutData). In UIKit, it is the checkoutData parameter of primerCheckoutPresenterDidFailWithError(_:checkoutData:). If a payment was created before the failure, for example on a decline, use checkoutData?.payment?.id and checkoutData?.payment?.orderId to find it on your backend. checkoutData is nil if the failure happened before a payment existed, for example during initialization or tokenization, or after a connectivity error.
See also
PrimerCheckoutState
The full state enum and the PaymentResult model.
PrimerError
Error categories, IDs, and diagnostics.
PrimerCheckoutPresenter
The UIKit entry point and delegate callbacks.
PrimerCheckout
The prebuilt SwiftUI checkout view.