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.