From 3d65f04189a01b6ffbd9c89accc3e231784b1db8 Mon Sep 17 00:00:00 2001 From: Andrii Vysotskyi Date: Wed, 2 Oct 2024 12:22:38 +0200 Subject: [PATCH] feat(ad-hoc): support apm pay and tokenize in example (#360) --- .../Example/Resources/Localizable.xcstrings | 20 +++++++-------- .../AlternativePaymentsInteractor.swift | 6 +++-- ...edStringResource+AlternativePayments.swift | 4 +-- .../View/AlternativePaymentsView.swift | 9 ++++--- .../AlternativePaymentsViewModel.swift | 25 +++++++++++++++---- .../AlternativePaymentsViewModelState.swift | 16 ++++++++++-- 6 files changed, 55 insertions(+), 25 deletions(-) diff --git a/Example/Example/Resources/Localizable.xcstrings b/Example/Example/Resources/Localizable.xcstrings index 221bddedb..1d39a7b7e 100644 --- a/Example/Example/Resources/Localizable.xcstrings +++ b/Example/Example/Resources/Localizable.xcstrings @@ -51,6 +51,16 @@ } } }, + "alternative-payments.flow" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flow" + } + } + } + }, "alternative-payments.gateway" : { "localizations" : { "en" : { @@ -111,16 +121,6 @@ } } }, - "alternative-payments.tokenization-preference" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Should Tokenize" - } - } - } - }, "apple-pay.error-message" : { "localizations" : { "en" : { diff --git a/Example/Example/Sources/UI/Modules/AlternativePayments/Interactor/AlternativePaymentsInteractor.swift b/Example/Example/Sources/UI/Modules/AlternativePayments/Interactor/AlternativePaymentsInteractor.swift index fe512b6c7..19056d786 100644 --- a/Example/Example/Sources/UI/Modules/AlternativePayments/Interactor/AlternativePaymentsInteractor.swift +++ b/Example/Example/Sources/UI/Modules/AlternativePayments/Interactor/AlternativePaymentsInteractor.swift @@ -101,7 +101,7 @@ final class AlternativePaymentsInteractor { return try await invoicesService.createInvoice(request: request) } - func authorize(invoice: POInvoice, gatewayConfigurationId: String) async throws { + func authorize(invoice: POInvoice, gatewayConfigurationId: String, saveSource: Bool) async throws { let request = POAlternativePaymentAuthorizationRequest( invoiceId: invoice.id, gatewayConfigurationId: gatewayConfigurationId ) @@ -109,7 +109,9 @@ final class AlternativePaymentsInteractor { let authorizationRequest = POInvoiceAuthorizationRequest( invoiceId: invoice.id, source: response.gatewayToken, - allowFallbackToSale: true + saveSource: saveSource, + allowFallbackToSale: true, + clientSecret: invoice.clientSecret ) let threeDSService = POTest3DSService() try await invoicesService.authorizeInvoice(request: authorizationRequest, threeDSService: threeDSService) diff --git a/Example/Example/Sources/UI/Modules/AlternativePayments/Symbols/LocalizedStringResource+AlternativePayments.swift b/Example/Example/Sources/UI/Modules/AlternativePayments/Symbols/LocalizedStringResource+AlternativePayments.swift index f2877abbb..af5f589f6 100644 --- a/Example/Example/Sources/UI/Modules/AlternativePayments/Symbols/LocalizedStringResource+AlternativePayments.swift +++ b/Example/Example/Sources/UI/Modules/AlternativePayments/Symbols/LocalizedStringResource+AlternativePayments.swift @@ -40,8 +40,8 @@ extension LocalizedStringResource { /// Native payment preference. static let nativePreference = LocalizedStringResource("alternative-payments.native-preference") - /// Tokenization preference. - static let tokenizationPreference = LocalizedStringResource("alternative-payments.tokenization-preference") + /// Payment flow. + static let flow = LocalizedStringResource("alternative-payments.flow") /// Generic error message. static let errorMessage = LocalizedStringResource("alternative-payments.error-message") diff --git a/Example/Example/Sources/UI/Modules/AlternativePayments/View/AlternativePaymentsView.swift b/Example/Example/Sources/UI/Modules/AlternativePayments/View/AlternativePaymentsView.swift index 54f26dd63..9d4c7baeb 100644 --- a/Example/Example/Sources/UI/Modules/AlternativePayments/View/AlternativePaymentsView.swift +++ b/Example/Example/Sources/UI/Modules/AlternativePayments/View/AlternativePaymentsView.swift @@ -32,14 +32,15 @@ struct AlternativePaymentsView: View { Text(.AlternativePayments.gatewayConfiguration) } } + Picker(data: $viewModel.state.flow) { flow in + Text(flow.rawValue.capitalized) + } label: { + Text(.AlternativePayments.flow) + } Toggle( String(localized: .AlternativePayments.nativePreference), isOn: $viewModel.state.preferNative ) - Toggle( - String(localized: .AlternativePayments.tokenizationPreference), - isOn: $viewModel.state.shouldTokenize - ) } header: { Text(.AlternativePayments.gateway) } diff --git a/Example/Example/Sources/UI/Modules/AlternativePayments/ViewModel/AlternativePaymentsViewModel.swift b/Example/Example/Sources/UI/Modules/AlternativePayments/ViewModel/AlternativePaymentsViewModel.swift index 733f11abe..67f682af1 100644 --- a/Example/Example/Sources/UI/Modules/AlternativePayments/ViewModel/AlternativePaymentsViewModel.swift +++ b/Example/Example/Sources/UI/Modules/AlternativePayments/ViewModel/AlternativePaymentsViewModel.swift @@ -18,6 +18,7 @@ final class AlternativePaymentsViewModel: ObservableObject { self.interactor = interactor cancellables = [] observeInteractorStateChanges() + updateStateFlows() } // MARK: - AlternativePaymentsViewModel @@ -127,6 +128,13 @@ final class AlternativePaymentsViewModel: ObservableObject { state.message = .init(text: errorMessage, severity: .error) } + private func updateStateFlows() { + let flows: [AlternativePaymentsViewModelState.Flow] = [ + .payment, .tokenization, .combined + ] + state.flow = .init(sources: flows, id: \.self, selection: .payment) + } + // MARK: - private func startPayment() async { @@ -142,14 +150,21 @@ final class AlternativePaymentsViewModel: ObservableObject { try await interactor.invoice(id: state.invoice.id) } var authorizationSource = gatewayConfigurationId - if state.shouldTokenize { + switch state.flow.selection { + case .payment where state.preferNative: + try await authorizeNatively(invoice: invoice, gatewayConfigurationId: gatewayConfigurationId) + case .payment: + try await interactor.authorize( + invoice: invoice, gatewayConfigurationId: gatewayConfigurationId, saveSource: false + ) + case .tokenization: let token = try await interactor.tokenize(gatewayConfigurationId: gatewayConfigurationId) try await interactor.authorize(invoice: invoice, customerToken: token) authorizationSource = token.id - } else if state.preferNative { - try await authorizeNatively(invoice: invoice, gatewayConfigurationId: gatewayConfigurationId) - } else { - try await interactor.authorize(invoice: invoice, gatewayConfigurationId: gatewayConfigurationId) + case .combined: + try await interactor.authorize( + invoice: invoice, gatewayConfigurationId: gatewayConfigurationId, saveSource: true + ) } let successMessage = String( localized: .AlternativePayments.successMessage, replacements: invoice.id, authorizationSource diff --git a/Example/Example/Sources/UI/Modules/AlternativePayments/ViewModel/AlternativePaymentsViewModelState.swift b/Example/Example/Sources/UI/Modules/AlternativePayments/ViewModel/AlternativePaymentsViewModelState.swift index 9de68c393..2ef666209 100644 --- a/Example/Example/Sources/UI/Modules/AlternativePayments/ViewModel/AlternativePaymentsViewModelState.swift +++ b/Example/Example/Sources/UI/Modules/AlternativePayments/ViewModel/AlternativePaymentsViewModelState.swift @@ -12,6 +12,18 @@ import ProcessOutUI struct AlternativePaymentsViewModelState { + enum Flow: String, Hashable { + + /// One time payment. + case payment + + /// Payment method should be tokenized. + case tokenization + + /// Combined payment and tokenization. + case combined + } + struct GatewayConfiguration: Identifiable { /// Item identifier. @@ -53,8 +65,8 @@ struct AlternativePaymentsViewModelState { /// Boolean value indicating whether native flow should be preferred if available. var preferNative = false - /// Boolean value indicating whether payment method should be tokenized before authorizing invoice. - var shouldTokenize = false + /// Payment flow. + var flow: PickerData! // swiftlint:disable:this implicitly_unwrapped_optional /// Currently presented native alternative payment. var nativePayment: NativePayment?