55// Created by Nick Porter on 4/29/26.
66//
77
8+ import Combine
89import Foundation
910@_spi ( CheckoutSessionsPreview) import StripePaymentSheet
1011
@@ -13,6 +14,28 @@ extension StripeSdkImpl {
1314 Mappers . mapFromCheckoutState ( checkout. state)
1415 }
1516
17+ /// Forwards every `Checkout` state change to JS. We listen on `$state`
18+ /// instead of `CheckoutDelegate` since the delegate misses the loading
19+ /// transitions, but `$state` fires on every assignment.
20+ @MainActor
21+ internal func observeCheckoutState(
22+ _ checkout: Checkout ,
23+ sessionKey: String
24+ ) {
25+ checkoutStateCancellables [ sessionKey] ? . cancel ( )
26+
27+ checkoutStateCancellables [ sessionKey] = checkout. $state
28+ // JS already has the initial state from `initCheckoutSession`.
29+ . dropFirst ( )
30+ . receive ( on: DispatchQueue . main)
31+ . sink { [ weak self] state in
32+ self ? . emitter? . emitCheckoutSessionDidChangeState ( [
33+ " sessionKey " : sessionKey,
34+ " state " : Mappers . mapFromCheckoutState ( state) ,
35+ ] )
36+ }
37+ }
38+
1639 @objc ( initCheckoutSession: configuration: resolver: rejecter: )
1740 public func initCheckoutSession(
1841 clientSecret: String ,
@@ -36,6 +59,7 @@ extension StripeSdkImpl {
3659 let sessionKey = UUID ( ) . uuidString
3760
3861 self . checkoutInstances [ sessionKey] = checkout
62+ self . observeCheckoutState ( checkout, sessionKey: sessionKey)
3963
4064 resolve ( [
4165 " sessionKey " : sessionKey,
0 commit comments