Open ssitdikov opened 3 years ago
Презентуем ViewController через обычный sheet, используя UIViewControllerRepresentable. Ниже полный код с подтверждением 3дс. Много всяких биндингов, еле привел к работающему варианту. Где-то можно было и проще.
К слову о прямой реализации, пусть лучше хотя бы библиотеку в менеджер пакетов нормально интегрируют. На М1, последнем Xcode и SwiftUI завести все это дело было непросто.
//
// PaymentScreen.swift
// Dbz
//
// Created by Elvin Askyarov on 18.02.2021.
//
import Foundation
import SwiftUI
import YooKassaPayments
import YooKassaPaymentsApi
struct PaymentScreen: View {
var initAmount: Int
@State var amount: Int = 0
@State var showingPaymentSheet = false
@State var status: Int = 0
@State var url = ""
@ObservedObject var textBindingManager = TextBindingManager(limit: 5)
@State var vc: (UIViewController & TokenizationModuleInput)? = nil
@Environment(\.presentationMode) var presentation
init(amount: Int) {
self.initAmount = amount
textBindingManager.text = amount > 0 ? "\(amount)" : "500"
}
var body: some View {
VStack {
if (initAmount>0) {
Text("Пополните баланс на недостающую сумму.")
.multilineTextAlignment(.center)
.font(.system(size: 17))
.foregroundColor(.secondary)
}
HStack(alignment: .center) {
CustomTextField(text: $textBindingManager.text, isFirstResponder: true)
.keyboardType(.numberPad) .frame(width: 90, height: 40)
.defaultTextFieldStyle()
Text("₽")
.font(.system(size: 36))
.foregroundColor(.accentColor)
}.padding(.top, 16)
Text("Минимальная сумма пополнения 150 рублей")
.multilineTextAlignment(.center)
.padding(.horizontal, 32)
.padding(.top, 12)
.foregroundColor(.secondary)
Spacer()
}.sheet(isPresented: $showingPaymentSheet) {
PaymentController(amount: $amount, vc: $vc, url: $url, showingPaymentSheet: $showingPaymentSheet)
}.padding()
.onChange(of: url, perform: { value in
//если 3дс пустое, то закрываем и viewcontroller сдк и view оплаты, иначе запускаем 3дс
if (url.isEmpty) {
showingPaymentSheet = false
dismiss()
} else {
vc?.start3dsProcess(requestUrl: value)
}
})
.navigationBarTitle(Strings.PROFILE_TITLE_PAYMENT, displayMode: .inline)
.navigationBarItems(trailing:
HStack {
if (Int(textBindingManager.text) ?? 0>0) {
Button(Strings.NEXT) {
amount = Int(textBindingManager.text) ?? 0
showingPaymentSheet = true
}.tabBar()
}
})
}
private func dismiss() {
self.presentation.wrappedValue.dismiss()
}
}
class PaymentOutput: TokenizationModuleOutput {
var amount: Int
var url: Binding<String>
var showingPaymentSheet: Binding<Bool>
init(amount: Int, url: Binding<String>, showingPaymentSheet: Binding<Bool>) {
self.amount = amount
self.url = url
self.showingPaymentSheet = showingPaymentSheet
}
func set(amount: Int, url: Binding<String>, showingPaymentSheet: Binding<Bool>) {
self.amount = amount
self.url = url
self.showingPaymentSheet = showingPaymentSheet
}
func tokenizationModule(_ module: TokenizationModuleInput,
didTokenize token: Tokens,
paymentMethodType: PaymentMethodType) {
//отправка в апи токена для получения 3дс
Api.get(
message: .constant(nil),
type: ConfirmPayment.R.self, request: Get3Ds(token: token.paymentToken, amount: amount),
success: {r in
self.url.wrappedValue = r.url
},
failure: {_ in
self.showingPaymentSheet.wrappedValue = false
},
any: {
}
)
}
func didFinish(on module: TokenizationModuleInput,
with error: YooKassaPaymentsError?) {
//print("didFinish Error " + e)
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
//self.dismiss(animated: true)
}
}
func didSuccessfullyPassedCardSec(on module: TokenizationModuleInput) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
print("3DS SUCCESS")
self.url.wrappedValue = ""
// Создать экран успеха после прохождения 3DS
//self.dismiss(animated: true)
// Показать экран успеха
}
}
}
struct PaymentController: UIViewControllerRepresentable {
@Binding var amount: Int
@Binding var vc: (UIViewController & TokenizationModuleInput)?
@Binding var url: String
@Binding var showingPaymentSheet: Bool
@State var output: PaymentOutput = PaymentOutput(amount: 0, url: .constant(""), showingPaymentSheet: .constant(false))
func makeUIViewController(context: Context) -> UIViewController {
let clientApplicationKey = "#"
let a = Amount(value: Decimal(amount), currency: .rub)
let tokenizationSettings = TokenizationSettings(paymentMethodTypes: [.bankCard])
let tokenizationModuleInputData =
TokenizationModuleInputData(clientApplicationKey: clientApplicationKey,
shopName: "#",
purchaseDescription: "\(Main.user.id)",
amount: a,
tokenizationSettings: tokenizationSettings,
savePaymentMethod: .off)
let inputData: TokenizationFlow = .tokenization(tokenizationModuleInputData)
output.set(amount: amount, url: $url, showingPaymentSheet: $showingPaymentSheet)
let vc = TokenizationAssembly.makeModule(inputData: inputData, moduleOutput: output)
self.vc = vc
return vc
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
}
Планируется ли реализация для SwiftUI?