Listen for the primer:ready event to access the Primer SDK instance
Set the onPaymentSuccess callback
Access payment details and paymentMethodType from the callback parameters
Display your custom UI (modal, toast, inline message, etc.)
Observe checkout.state with collectAsStateWithLifecycle() and react in a LaunchedEffect
Handle PrimerCheckoutState.Success to access PrimerCheckoutData with payment details
Navigate to a confirmation screen or update your order UI
Property
Type
Description
payment.id
String
The Primer payment ID
payment.orderId
String?
Your order ID
When using PrimerCheckoutSheet, the SDK shows a default success screen that auto-dismisses after 3 seconds. The Success state emits immediately — you do not need to wait for the dismiss. Navigate as soon as you observe the state.
Turn off the SDK success screen with PrimerUIOptions(isSuccessScreenEnabled: false)
Use the onCompletion callback on PrimerCheckout
Match on .success to detect payment completion (bind it with case .success(let result) to read result.paymentId)
Update your SwiftUI state to show a custom success view
By default, PrimerCheckout shows its own success screen for 3 seconds and delivers .success to onCompletion when that screen closes. With isSuccessScreenEnabled: false, the SDK shows nothing and delivers .success as soon as the payment completes.
primer.onPaymentSuccess = ({ payment }) => { document.getElementById('success-container').innerHTML = ` <h2>Thank you for your order!</h2> <p>Order ID: ${payment.orderId}</p> <p>Amount: ${formatCurrency(payment.amount, payment.currencyCode)}</p> `;};
Display payment details from the Success event:
val state by checkout.state.collectAsStateWithLifecycle()LaunchedEffect(state) { when (val s = state) { is PrimerCheckoutState.Success -> { val payment = s.checkoutData.payment showSuccessScreen( orderId = payment.orderId, paymentId = payment.id, ) } is PrimerCheckoutState.Failure -> { Log.e("Checkout", "Failed: ${s.primerError.diagnosticsId}") } else -> Unit }}PrimerCheckoutSheet(checkout = checkout)
Display payment details from the success result:
PrimerCheckout( clientToken: clientToken, primerSettings: PrimerSettings(uiOptions: PrimerUIOptions(isSuccessScreenEnabled: false)), onCompletion: { state in switch state { case .success(let result): showSuccessScreen(paymentId: result.paymentId) case let .failure(error, _): print("Failed [\(error.errorId)]: \(error.diagnosticsId)") default: break } })
onDismiss fires after the sheet closes. If you already navigated away when observing PrimerCheckoutState.Success, guard against double navigation.
Handle the .dismissed case to detect when the shopper closes the checkout. The payment method list shows a close button when dismissalMechanism includes .closeButton. Tapping it ends the checkout with .dismissed. When other methods are available, the close button on a payment method screen, such as the card form, returns the shopper to the list. In that case, .dismissed does not arrive. With a single method, that close button ends the checkout with .dismissed. If you present PrimerCheckout in your own sheet, a swipe-down does not reach onCompletion, so use your sheet’s onDismiss for that.
PrimerCheckout( clientToken: clientToken, primerSettings: PrimerSettings( uiOptions: PrimerUIOptions( dismissalMechanism: [.closeButton] ) ), onCompletion: { state in switch state { case .success: withAnimation { showSuccess = true } case .dismissed: // The shopper closed the checkout break default: break } })