如何解决条纹错误:“不应一次处理多笔付款”
我正在尝试确认由我的后端 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 举报,一经查实,本站将立刻删除。