Support native provider

This commit is contained in:
Ali 2021-04-09 02:07:57 +04:00
parent 97f7004a1e
commit 5a31f90acf
3 changed files with 266 additions and 89 deletions

View File

@ -561,7 +561,75 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
var dismissImpl: (() -> Void)?
let canSave = paymentForm.canSaveCredentials || paymentForm.passwordMissing
let controller = BotCheckoutNativeCardEntryController(context: strongSelf.context, additionalFields: additionalFields, publishableKey: publishableKey, completion: { method in
let controller = BotCheckoutNativeCardEntryController(context: strongSelf.context, provider: .stripe(additionalFields: additionalFields, publishableKey: publishableKey), completion: { method in
guard let strongSelf = self else {
return
}
if canSave && paymentForm.passwordMissing {
switch method {
case let .webToken(webToken) where webToken.saveOnServer:
var text = strongSelf.presentationData.strings.Checkout_NewCard_SaveInfoEnableHelp
text = text.replacingOccurrences(of: "[", with: "")
text = text.replacingOccurrences(of: "]", with: "")
present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_NotNow, action: {
var updatedToken = webToken
updatedToken.saveOnServer = false
applyPaymentMethod(.webToken(updatedToken))
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Yes, action: {
guard let strongSelf = self else {
return
}
if paymentForm.passwordMissing {
var updatedToken = webToken
updatedToken.saveOnServer = false
applyPaymentMethod(.webToken(updatedToken))
let controller = SetupTwoStepVerificationController(context: strongSelf.context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in
if shouldDismiss {
controller.dismiss()
}
switch update {
case .noPassword, .awaitingEmailConfirmation:
break
case .passwordSet:
var updatedToken = webToken
updatedToken.saveOnServer = true
applyPaymentMethod(.webToken(updatedToken))
}
})
strongSelf.present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
} else {
var updatedToken = webToken
updatedToken.saveOnServer = true
applyPaymentMethod(.webToken(updatedToken))
}
})]), nil)
default:
applyPaymentMethod(method)
}
} else {
applyPaymentMethod(method)
}
dismissImpl?()
})
dismissImpl = { [weak controller] in
controller?.dismiss()
}
strongSelf.present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
} else if let nativeProvider = paymentForm.nativeProvider, nativeProvider.name == "smartglocal" {
guard let paramsData = nativeProvider.params.data(using: .utf8) else {
return
}
guard let nativeParams = (try? JSONSerialization.jsonObject(with: paramsData)) as? [String: Any] else {
return
}
guard let publicToken = nativeParams["public_token"] as? String else {
return
}
var dismissImpl: (() -> Void)?
let canSave = paymentForm.canSaveCredentials || paymentForm.passwordMissing
let controller = BotCheckoutNativeCardEntryController(context: strongSelf.context, provider: .smartglobal(isTesting: paymentForm.invoice.isTest, publicToken: publicToken), completion: { method in
guard let strongSelf = self else {
return
}

View File

@ -30,13 +30,17 @@ struct BotCheckoutNativeCardEntryAdditionalFields: OptionSet {
}
final class BotCheckoutNativeCardEntryController: ViewController {
enum Provider {
case stripe(additionalFields: BotCheckoutNativeCardEntryAdditionalFields, publishableKey: String)
case smartglobal(isTesting: Bool, publicToken: String)
}
private var controllerNode: BotCheckoutNativeCardEntryControllerNode {
return super.displayNode as! BotCheckoutNativeCardEntryControllerNode
}
private let context: AccountContext
private let additionalFields: BotCheckoutNativeCardEntryAdditionalFields
private let publishableKey: String
private let provider: Provider
private let completion: (BotCheckoutPaymentMethod) -> Void
private var presentationData: PresentationData
@ -46,10 +50,9 @@ final class BotCheckoutNativeCardEntryController: ViewController {
private var doneItem: UIBarButtonItem?
private var activityItem: UIBarButtonItem?
public init(context: AccountContext, additionalFields: BotCheckoutNativeCardEntryAdditionalFields, publishableKey: String, completion: @escaping (BotCheckoutPaymentMethod) -> Void) {
public init(context: AccountContext, provider: Provider, completion: @escaping (BotCheckoutPaymentMethod) -> Void) {
self.context = context
self.additionalFields = additionalFields
self.publishableKey = publishableKey
self.provider = provider
self.completion = completion
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
@ -71,7 +74,7 @@ final class BotCheckoutNativeCardEntryController: ViewController {
}
override public func loadDisplayNode() {
self.displayNode = BotCheckoutNativeCardEntryControllerNode(additionalFields: self.additionalFields, publishableKey: self.publishableKey, theme: self.presentationData.theme, strings: self.presentationData.strings, present: { [weak self] c, a in
self.displayNode = BotCheckoutNativeCardEntryControllerNode(provider: self.provider, theme: self.presentationData.theme, strings: self.presentationData.strings, present: { [weak self] c, a in
self?.present(c, in: .window(.root), with: a)
}, dismiss: { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)

View File

@ -42,7 +42,7 @@ private final class BotCheckoutNativeCardEntryScrollerNode: ASDisplayNode {
}
final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
private let publishableKey: String
private let provider: BotCheckoutNativeCardEntryController.Provider
private let present: (ViewController, Any?) -> Void
private let dismiss: () -> Void
@ -71,8 +71,10 @@ final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode,
private var currentCardData: BotPaymentCardInputData?
private var currentCountryIso2: String?
init(additionalFields: BotCheckoutNativeCardEntryAdditionalFields, publishableKey: String, theme: PresentationTheme, strings: PresentationStrings, present: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void, openCountrySelection: @escaping () -> Void, updateStatus: @escaping (BotCheckoutNativeCardEntryStatus) -> Void, completion: @escaping (BotCheckoutPaymentMethod) -> Void) {
self.publishableKey = publishableKey
private var dataTask: URLSessionDataTask?
init(provider: BotCheckoutNativeCardEntryController.Provider, theme: PresentationTheme, strings: PresentationStrings, present: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void, openCountrySelection: @escaping () -> Void, updateStatus: @escaping (BotCheckoutNativeCardEntryStatus) -> Void, completion: @escaping (BotCheckoutPaymentMethod) -> Void) {
self.provider = provider
self.present = present
self.dismiss = dismiss
@ -96,6 +98,8 @@ final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode,
}
itemNodes.append([BotPaymentHeaderItemNode(text: strings.Checkout_NewCard_PaymentCard), self.cardItem])
switch provider {
case let .stripe(additionalFields, _):
if additionalFields.contains(.cardholderName) {
var sectionItems: [BotPaymentItemNode] = []
@ -138,6 +142,11 @@ final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode,
self.countryItem = nil
self.zipCodeItem = nil
}
case .smartglobal:
self.cardholderItem = nil
self.countryItem = nil
self.zipCodeItem = nil
}
self.saveInfoItem = BotPaymentSwitchItemNode(title: strings.Checkout_NewCard_SaveInfo, isOn: false)
itemNodes.append([self.saveInfoItem, BotPaymentTextItemNode(text: strings.Checkout_NewCard_SaveInfoHelp)])
@ -214,6 +223,7 @@ final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode,
deinit {
self.verifyDisposable.dispose()
self.dataTask?.cancel()
}
func updateCountry(_ iso2: String) {
@ -233,9 +243,11 @@ final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode,
return
}
switch self.provider {
case let .stripe(_, publishableKey):
let configuration = STPPaymentConfiguration.shared().copy() as! STPPaymentConfiguration
configuration.smsAutofillDisabled = true
configuration.publishableKey = self.publishableKey
configuration.publishableKey = publishableKey
configuration.appleMerchantIdentifier = "merchant.ph.telegra.Telegraph"
let apiClient = STPAPIClient(configuration: configuration)
@ -279,6 +291,100 @@ final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode,
}))
self.updateDone()
case let .smartglobal(isTesting, publicToken):
let url: String
if isTesting {
url = "https://tgb-playground.smart-glocal.com/cds/v1/tokenize/card"
} else {
url = "https://tgb.smart-glocal.com/cds/v1/tokenize/card"
}
let jsonPayload: [String: Any] = [
"card": [
"number": cardData.number,
"expiration_month": "\(cardData.month)",
"expiration_year": "\(cardData.year)",
"security_code": "\(cardData.code)"
] as [String: Any]
]
guard let parsedUrl = URL(string: url) else {
return
}
var request = URLRequest(url: parsedUrl)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(publicToken, forHTTPHeaderField: "X-PUBLIC-TOKEN")
guard let requestBody = try? JSONSerialization.data(withJSONObject: jsonPayload, options: []) else {
return
}
request.httpBody = requestBody
let session = URLSession.shared
let dataTask = session.dataTask(with: request, completionHandler: { [weak self] data, response, error in
Queue.mainQueue().async {
guard let strongSelf = self else {
return
}
enum ReponseError: Error {
case generic
}
do {
guard let data = data else {
throw ReponseError.generic
}
let jsonRaw = try JSONSerialization.jsonObject(with: data, options: [])
guard let json = jsonRaw as? [String: Any] else {
throw ReponseError.generic
}
guard let resultData = json["data"] as? [String: Any] else {
throw ReponseError.generic
}
guard let resultInfo = resultData["info"] as? [String: Any] else {
throw ReponseError.generic
}
guard let token = resultData["token"] as? String else {
throw ReponseError.generic
}
guard let maskedCardNumber = resultInfo["masked_card_number"] as? String else {
throw ReponseError.generic
}
let responseJson: [String: Any] = [
"type": "card",
"id": "\(token)"
]
let serializedResponseJson = try JSONSerialization.data(withJSONObject: responseJson, options: [])
guard let serializedResponseString = String(data: serializedResponseJson, encoding: .utf8) else {
throw ReponseError.generic
}
strongSelf.completion(.webToken(BotCheckoutPaymentWebToken(
title: maskedCardNumber,
data: serializedResponseString,
saveOnServer: strongSelf.saveInfoItem.isOn
)))
} catch {
strongSelf.isVerifying = false
strongSelf.updateDone()
}
}
})
self.dataTask = dataTask
self.isVerifying = true
self.updateDone()
dataTask.resume()
break
}
}
private func updateDone() {