微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

条纹错误:“不应一次处理多笔付款”

如何解决条纹错误:“不应一次处理多笔付款”

我正在尝试确认由我的后端 api 生成的 setupintent,但我认为它尝试多次提交 setupintent。下面的方法是通过使用Stripe iOS SDK(不幸的是为uikit制作的)向用户添加新卡。我修改了示例方法并将它们包装在一些 uiviewrepresentables 中,这样我就可以在需要时使用 3DS 弹出窗口。问题是它在 Stripe 中得到验证。 SetupIntent 出现在日志中,但应用程序崩溃。我将在下面将我的代码分享给其他在 Swiftui 上实施 Stripe SDK 时遇到困难的人。

这是我使用 STPPaymentMethod 参数检索并确认 setupintent 的一段代码

struct FakeStripeView: UIViewControllerRepresentable {
    @EnvironmentObject var navView : NavView
    @Binding var cardParams : STPPaymentMethodCardParams
    @Binding var isActive : Bool
    @Binding var billing : STPPaymentMethodBillingDetails
    
    public typealias UIViewControllerType = FakeStripeViewController
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<FakeStripeView>) -> FakeStripeViewController {
        let viewController = FakeStripeViewController(navView: navView)
        return viewController
    }
    
    func updateUIViewController(_ uiViewController: FakeStripeViewController,context _: UIViewControllerRepresentableContext<FakeStripeView>) {
        if navView.isActive{
            uiViewController.createSetupIntent(cardParams: cardParams,billing: billing,completion: { state in
                if state != false{
                    self.navView.isActive = false
                    self.navView.addNewCard = false
                }
            })
        }
    }
}

class FakeStripeViewController: UIViewController {
    
    var navView: NavView?
        
        convenience init() {
            self.init(navView: nil)
        }
        
        init(navView: NavView?) {
            self.navView = navView
            super.init(nibName: nil,bundle: nil)
        }
        
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
    }
    
    func createSetupIntent(cardParams: STPPaymentMethodCardParams,billing: STPPaymentMethodBillingDetails,completion: @escaping (Bool) -> ()) {
        Stripeapiclient.shared.createSetupIntent(completion: { (setupIntent) in
            if(setupIntent.status != "not completed"){
                if(setupIntent.status.contains("error:")){
                    print(setupIntent.status)
                }
                else{
                    let paymentMethodParams = STPPaymentMethodParams(card: cardParams,billingDetails: billing,Metadata: nil)
                    let setupIntentParams = STPSetupIntentConfirmParams(clientSecret: setupIntent.client_secret)
                    setupIntentParams.paymentMethodParams = paymentMethodParams
                    
                    let paymentHandler = STPPaymentHandler.shared()
                    paymentHandler.confirmSetupIntent(setupIntentParams,with: self) { status,setupIntent,error in
                                switch (status) {
                                case .Failed:
                                    // Setup Failed
                                    break
                                case .canceled:
                                    // Setup canceled
                                    break
                                case .succeeded:
                                    print("successful")
                                    Stripeapiclient.shared.retrievePaymentMethods()
                                    completion(true)
                                    self.navView?.isActive = false
                                    self.navView?.addNewCard = false
                                    break
                                @unkNown default:
                                    fatalError()
                                    break
                                }
                            }
                        }
                    }
                })
            }
        }

extension FakeStripeViewController: STPAuthenticationContext {
    func authenticationPresentingViewController() -> UIViewController {
        self
    }
}

这是卡片字段的包装器:

struct StripePaymentCardTextField: UIViewRepresentable {
    
    @Binding var cardParams: STPPaymentMethodCardParams
    @Binding var isValid: Bool
    @Binding var billing : STPPaymentMethodBillingDetails
    
    func makeUIView(context: Context) -> STPPaymentCardTextField {
        let input = STPPaymentCardTextField()
        input.borderColor = getColorFromHex(hex: 0x7653DB,alpha: 1.0)
        input.borderWidth = 5
        input.cornerRadius = 15
        input.delegate = context.coordinator
        return input
    }
    
    func makeCoordinator() -> StripePaymentCardTextField.Coordinator { Coordinator(self) }

    func updateUIView(_ view: STPPaymentCardTextField,context: Context) { }
    
    class Coordinator: NSObject,STPPaymentCardTextFieldDelegate {

        var parent: StripePaymentCardTextField
        
        init(_ textField: StripePaymentCardTextField) {
            parent = textField
            parent.billing.email = UserData.shared.user?.email
            parent.billing.name = UserData.shared.user?.name
            parent.billing.phone = UserData.shared.user?.phone
        }
        
        func paymentCardTextFieldDidChange(_ textField: STPPaymentCardTextField) {
            parent.cardParams = textField.cardParams
            parent.isValid = textField.isValid
            parent.billing.address?.postalCode = textField.postalCode
        }
    }
}

这是添加付款方式的视图。在这个视图中实现了 fakestripeview,因此我们可以在需要时弹出 3ds。

struct AddPaymentMethod: View {
    @State var cardParams : STPPaymentMethodCardParams = STPPaymentMethodCardParams()
    @State var billingParams : STPPaymentMethodBillingDetails = STPPaymentMethodBillingDetails()
    @State var isValid = false
    @EnvironmentObject var navView : NavView
    
    @State var showAlert = false
    var body: some View {
        vstack(alignment: .center){
            CCView(cardParams: $cardParams)
                .padding(.leading,10)
                .frame(maxWidth: .infinity,alignment: .center)
                .frame(height: UIScreen.main.bounds.width/2)
                .padding(.bottom)
            
            
                
            
            StripePaymentCardTextField(cardParams: $cardParams,isValid: $isValid,billing: $billingParams)
                .frame(height:70)
                .padding()

            Button(action: {
                if(isValid){
                    navView.isActive = true
                }
                else{
                    self.showAlert = true
                }
                
            },label: {
                Text("Submit")
                    .foregroundColor(.white)
                    .frame(height: 50)
                    .frame(maxWidth: .infinity)
                    .background(beatactViolet)
                    .modifier(EventCardModifier())
                    .padding()
                    
            })
            Spacer()
            FakeStripeView(cardParams: $cardParams,isActive: $navView.isActive,billing: $billingParams)
                .frame(width: 0,height: 0).environmentObject(NavView.shared)
        }.alert(isPresented: $showAlert,content: {
            Alert(title: Text("Card/Adress Invalid"),message: Text("Please try again."),dismissButton: .default(Text("OK!")))
        }).navigationTitle("Add card")
    }
}

我认为这种方法的主要问题是 fakestripeview 和 AddPaymentMethod 视图的方法间的接口。如果它对您有任何帮助,请随意使用任何代码。我知道我花了一段时间才想出一个可行的方法来做到这一点。这种方法有效,但在 setupintent 确认后它会使应用程序崩溃(但至少它以条带形式显示)。关于我如何能够改进这一点并使其发挥作用的任何线索?

解决方法

我对 SwiftUI 不够精通,无法帮助您处理现有代码,但是 the Stripe iOS SDK was updated to support SwiftUI in version 21.3.0,在 the latest release (21.4.0 at time of writing) 中修复了 SwiftUI 错误,现在包含 some SwiftUI sample code。>

也许更新到支持 SwiftUI 的 SDK 版本并重构您的代码以利用该支持是前进的最佳途径?

,

对于任何对此感兴趣的人:

struct FakeStripeView: UIViewControllerRepresentable {
    @EnvironmentObject var navView : NavView
    @Binding var cardParams : STPPaymentMethodCardParams
    @Binding var isActive : Bool
    @Binding var billing : STPPaymentMethodBillingDetails
    
    public typealias UIViewControllerType = FakeStripeViewController
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<FakeStripeView>) -> FakeStripeViewController {
        let viewController = FakeStripeViewController.shared
        return viewController
    }
    
    func updateUIViewController(_ uiViewController: FakeStripeViewController,context _: UIViewControllerRepresentableContext<FakeStripeView>) {}
}

class FakeStripeViewController: UIViewController {
    
    static let shared = FakeStripeViewController()
    var navView: NavView?
        
        convenience init() {
            self.init(navView: nil)
        }
        
        init(navView: NavView?) {
            self.navView = navView
            super.init(nibName: nil,bundle: nil)
        }
        
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
    }
    
    func createSetupIntent(cardParams: STPPaymentMethodCardParams,billing: STPPaymentMethodBillingDetails,completion: @escaping (Bool) -> ()) {
        StripeAPIClient.shared.createSetupIntent(completion: { (setupIntent) in
            if(setupIntent.status != "not completed"){
                if(setupIntent.status.contains("error:")){
                    print(setupIntent.status)
                }
                else{
                    let paymentMethodParams = STPPaymentMethodParams(card: cardParams,billingDetails: billing,metadata: nil)
                    let setupIntentParams = STPSetupIntentConfirmParams(clientSecret: setupIntent.client_secret)
                    setupIntentParams.paymentMethodParams = paymentMethodParams
                    
                    let paymentHandler = STPPaymentHandler.shared()
                    paymentHandler.confirmSetupIntent(setupIntentParams,with: self) { status,setupIntent,error in
                                switch (status) {
                                case .failed:
                                    // Setup failed
                                    break
                                case .canceled:
                                    // Setup canceled
                                    break
                                case .succeeded:
                                    print("successful")
                                    StripeAPIClient.shared.retrievePaymentMethods()
                                    completion(true)
                                    self.navView?.isActive = false
                                    self.navView?.addNewCard = false
                                    break
                                @unknown default:
                                    fatalError()
                                    break
                                }
                            }
                        }
                    }
                })
            }
        }

extension FakeStripeViewController: STPAuthenticationContext {
    func authenticationPresentingViewController() -> UIViewController {
        self
    }
}

我添加了一个可以在 swiftui 视图中调用的类的共享实例。比如这个:


struct AddPaymentMethod: View {
    @State var cardParams : STPPaymentMethodCardParams = STPPaymentMethodCardParams()
    @State var billingParams : STPPaymentMethodBillingDetails = STPPaymentMethodBillingDetails()
    @State var isValid = false
    @EnvironmentObject var navView : NavView
    
    
    @State var showAlert = false
    var body: some View {
        VStack(alignment: .center){
            CCView(cardParams: $cardParams)
                .padding(.leading,10)
                .frame(maxWidth: .infinity,alignment: .center)
                .frame(height: UIScreen.main.bounds.width/2)
                .padding(.bottom)
            
            
                
            
            StripePaymentCardTextField(cardParams: $cardParams,isValid: $isValid,billing: $billingParams)
                .frame(height:70)
                .padding()

            Button(action: {
                if(isValid){
                    FakeStripeViewController.shared.createSetupIntent(cardParams: cardParams,billing: billingParams,completion: { state in
                        if(state){
                            navView.addNewCard = false
                        }
                    })
                }
                else{
                    self.showAlert = true
                }
                
            },label: {
                Text("Submit")
                    .foregroundColor(.white)
                    .frame(height: 50)
                    .frame(maxWidth: .infinity)
                    .background(beatactViolet)
                    .modifier(EventCardModifier())
                    .padding()
                    
            })
            Spacer()
            FakeStripeView(cardParams: $cardParams,isActive: $navView.isActive,billing: $billingParams)
                .frame(width: 0,height: 0).environmentObject(NavView.shared)
        }.alert(isPresented: $showAlert,content: {
            Alert(title: Text("Card/Adress Invalid"),message: Text("Please try again."),dismissButton: .default(Text("OK!")))
        }).navigationTitle("Add card")
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?