简体   繁体   中英

Confirm Stripe payment in SwiftUI (iOS 16)

I am using Stripe API to accept card payment in SwiftUI.

I managed to make successful payment using STPPaymentCardTextField wrapped in UIViewRepresentable and confirm it using the provided sheet modifier .paymentConfirmationSheet(....) .

The problem appeared when I adopted the new NavigationStack in iOS 16. It appears that the .paymentConfirmationSheet(....) doesn't work properly if it is presented inside the new NavigationStack .

Is there any other way I can confirm card payment in SwiftUI? How can I fix this?

If I switch back to NavigationView it works as expected but I would want to use the new features of NavigationStack .

My demo checkout view and its viewmodel

struct TestCheckout: View {
    
    @ObservedObject var model = TestCheckoutViewModel()
    
    var body: some View {
        
        VStack {
            CardField(paymentMethodParams: $model.paymentMethodParams)
            
            Button("Pay") {
                model.pay()
            }
            .buttonStyle(LargeButtonStyle())
            .paymentConfirmationSheet(isConfirmingPayment: $model.confirmPayment,
                                      paymentIntentParams: model.paymentIntentParams,
                                      onCompletion: model.onPaymentComplete)
        }
    }
}

class TestCheckoutViewModel: ObservableObject {
    @Published var paymentMethodParams: STPPaymentMethodParams?
    @Published var confirmPayment = false
    @Published var paymentIntentParams = STPPaymentIntentParams(clientSecret: "")
    
    func pay() {
        
        Task {
            
            do {
                // Create dummy payment intent
                let paymentIntent = try await StripeManager.shared.getPaymentIntent(orderId: "", amount: 1000)
                
                // Collect card details
                let paymentIntentParams = STPPaymentIntentParams(clientSecret: paymentIntent.secret)
                paymentIntentParams.paymentMethodParams = paymentMethodParams
                
                // Submit the payment
                DispatchQueue.main.async {
                    self.paymentIntentParams = paymentIntentParams
                    self.confirmPayment = true
                }
            } catch {
                print(error)
            }
        }
    }

    func onPaymentComplete(status: STPPaymentHandlerActionStatus, paymentIntent: STPPaymentIntent?, error: Error?) {
        print("Payment completed: \(error)")
    }
}

Now this doesn't work

struct TestView: View {
    var body: some View {
        NavigationStack {
            NavigationLink("Checkout", value: "checkout")
                .navigationDestination(for: String.self) { string in
                    if string == "checkout" {
                        TestCheckout()
                    }
                }
        }
    }
}

But this does

struct TestView: View {
    var body: some View {
        TestCheckout()
    }
}

The error I get is:

Error Domain=com.stripe.lib Code=50 "There was an unexpected error -- try again in a few seconds" UserInfo={NSLocalizedDescription=There was an unexpected error -- try again in a few seconds, com.stripe.lib:StripeErrorTypeKey=invalid_request_error, com.stripe.lib:StripeErrorCodeKey=payment_intent_unexpected_state, com.stripe.lib:ErrorMessageKey=Nemůžete potvrdit tento Platební záměr, protože v něm chybí platební metoda. Můžete buď aktualizovat Platební záměr s platební metodou a pak jej znovu potvrdit, nebo jej znovu potvrďte přímo s platební metodou.}

Finally I found the problem.

The problem is the @ObservedObject var model = TestCheckoutViewModel() . For some reason it doesn't work with @ObservedObject anymore and it has to be @StateObject .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM