From b7b16dde44381d17b4e1ac1fbc7ada0aa774320f Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 30 Apr 2025 16:40:34 +0300 Subject: [PATCH 01/13] Refactor nAPM default values to propagate 'paymentMethod' instead of 'gatewayConfigurationId' --- ...tAlternativePaymentDefaultValuesRequest.kt | 15 ++++++++ ...ativePaymentMethodDefaultValuesResponse.kt | 8 +++++ .../ui/checkout/DynamicCheckoutInteractor.kt | 34 ++++++++++++++----- .../DynamicCheckoutInteractorState.kt | 8 ++--- .../ui/checkout/PODynamicCheckoutDelegate.kt | 2 +- .../ui/checkout/PODynamicCheckoutLauncher.kt | 9 ++--- 6 files changed, 56 insertions(+), 20 deletions(-) create mode 100644 sdk/src/main/kotlin/com/processout/sdk/api/model/request/PODynamicCheckoutAlternativePaymentDefaultValuesRequest.kt diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/model/request/PODynamicCheckoutAlternativePaymentDefaultValuesRequest.kt b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/PODynamicCheckoutAlternativePaymentDefaultValuesRequest.kt new file mode 100644 index 000000000..5792d4600 --- /dev/null +++ b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/PODynamicCheckoutAlternativePaymentDefaultValuesRequest.kt @@ -0,0 +1,15 @@ +package com.processout.sdk.api.model.request + +import com.processout.sdk.api.dispatcher.POEventDispatcher +import com.processout.sdk.api.model.response.PODynamicCheckoutPaymentMethod +import com.processout.sdk.api.model.response.PONativeAlternativePaymentMethodParameter +import com.processout.sdk.core.annotation.ProcessOutInternalApi +import java.util.UUID + +/** @suppress */ +@ProcessOutInternalApi +data class PODynamicCheckoutAlternativePaymentDefaultValuesRequest( + override val uuid: UUID, + val paymentMethod: PODynamicCheckoutPaymentMethod.AlternativePayment, + val parameters: List +) : POEventDispatcher.Request diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/model/response/PONativeAlternativePaymentMethodDefaultValuesResponse.kt b/sdk/src/main/kotlin/com/processout/sdk/api/model/response/PONativeAlternativePaymentMethodDefaultValuesResponse.kt index e572545bb..bfa67e51c 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/model/response/PONativeAlternativePaymentMethodDefaultValuesResponse.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/model/response/PONativeAlternativePaymentMethodDefaultValuesResponse.kt @@ -1,7 +1,9 @@ package com.processout.sdk.api.model.response import com.processout.sdk.api.dispatcher.POEventDispatcher +import com.processout.sdk.api.model.request.PODynamicCheckoutAlternativePaymentDefaultValuesRequest import com.processout.sdk.api.model.request.PONativeAlternativePaymentMethodDefaultValuesRequest +import com.processout.sdk.core.annotation.ProcessOutInternalApi import java.util.UUID /** @@ -24,3 +26,9 @@ data class PONativeAlternativePaymentMethodDefaultValuesResponse internal constr fun PONativeAlternativePaymentMethodDefaultValuesRequest.toResponse( defaultValues: Map ) = PONativeAlternativePaymentMethodDefaultValuesResponse(uuid, defaultValues) + +/** @suppress */ +@ProcessOutInternalApi +fun PODynamicCheckoutAlternativePaymentDefaultValuesRequest.toResponse( + defaultValues: Map +) = PONativeAlternativePaymentMethodDefaultValuesResponse(uuid, defaultValues) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt index 53c315336..4e0dab7d0 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt @@ -122,8 +122,8 @@ internal class DynamicCheckoutInteractor( private suspend fun start() { handleCompletions() + dispatchEvents() dispatchSideEffects() - collectEvents() collectInvoice() collectInvoiceAuthorizationRequest() collectTokenizedCard() @@ -993,7 +993,14 @@ internal class DynamicCheckoutInteractor( } } - private fun collectEvents() { + private fun dispatch(event: PODynamicCheckoutEvent) { + interactorScope.launch { + eventDispatcher.send(event) + POLogger.debug("Event has been sent: %s", event) + } + } + + private fun dispatchEvents() { eventDispatcher.subscribeForRequest( coroutineScope = interactorScope ) { request -> @@ -1001,6 +1008,22 @@ internal class DynamicCheckoutInteractor( eventDispatcher.send(request.toResponse(shouldContinue = false)) } } + eventDispatcher.subscribeForRequest( + coroutineScope = interactorScope + ) { request -> + activePaymentMethod()?.let { paymentMethod -> + if (paymentMethod is NativeAlternativePayment) { + interactorScope.launch { + val defaultValuesRequest = PODynamicCheckoutAlternativePaymentDefaultValuesRequest( + uuid = request.uuid, + paymentMethod = paymentMethod.original, + parameters = request.parameters + ) + eventDispatcher.send(defaultValuesRequest) + } + } + } + } eventDispatcher.subscribe( coroutineScope = interactorScope ) { event -> @@ -1010,13 +1033,6 @@ internal class DynamicCheckoutInteractor( } } - private fun dispatch(event: PODynamicCheckoutEvent) { - interactorScope.launch { - eventDispatcher.send(event) - POLogger.debug("Event has been sent: %s", event) - } - } - private fun dispatchSideEffects() { interactorScope.launch(Dispatchers.Main.immediate) { cardTokenization.sideEffects.collect { sideEffect -> diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractorState.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractorState.kt index 16f552017..bbb71d0d4 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractorState.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractorState.kt @@ -25,21 +25,21 @@ internal data class DynamicCheckoutInteractorState( data class Card( override val id: String, - override val original: PODynamicCheckoutPaymentMethod, + override val original: PODynamicCheckoutPaymentMethod.Card, val configuration: CardConfiguration, val display: Display ) : PaymentMethod data class GooglePay( override val id: String, - override val original: PODynamicCheckoutPaymentMethod, + override val original: PODynamicCheckoutPaymentMethod.GooglePay, val allowedPaymentMethods: String, val paymentDataRequest: JSONObject ) : PaymentMethod data class AlternativePayment( override val id: String, - override val original: PODynamicCheckoutPaymentMethod, + override val original: PODynamicCheckoutPaymentMethod.AlternativePayment, val gatewayConfigurationId: String, val redirectUrl: String, val savePaymentMethodField: Field?, @@ -49,7 +49,7 @@ internal data class DynamicCheckoutInteractorState( data class NativeAlternativePayment( override val id: String, - override val original: PODynamicCheckoutPaymentMethod, + override val original: PODynamicCheckoutPaymentMethod.AlternativePayment, val gatewayConfigurationId: String, val display: Display ) : PaymentMethod diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt index d91777f5b..bd52df692 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt @@ -75,7 +75,7 @@ interface PODynamicCheckoutDelegate { * It's not mandatory to provide default values for all parameters. */ suspend fun defaultValues( - gatewayConfigurationId: String, + paymentMethod: PODynamicCheckoutPaymentMethod.AlternativePayment, parameters: List ): Map = emptyMap() diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutLauncher.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutLauncher.kt index 99d75b636..ff36e3539 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutLauncher.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutLauncher.kt @@ -12,10 +12,7 @@ import com.processout.sdk.api.model.event.POCardTokenizationEvent import com.processout.sdk.api.model.event.PODynamicCheckoutEvent import com.processout.sdk.api.model.event.PONativeAlternativePaymentMethodEvent import com.processout.sdk.api.model.event.POSavedPaymentMethodsEvent -import com.processout.sdk.api.model.request.POCardTokenizationPreferredSchemeRequest -import com.processout.sdk.api.model.request.PODynamicCheckoutInvoiceAuthorizationRequest -import com.processout.sdk.api.model.request.PODynamicCheckoutInvoiceRequest -import com.processout.sdk.api.model.request.PONativeAlternativePaymentMethodDefaultValuesRequest +import com.processout.sdk.api.model.request.* import com.processout.sdk.api.model.response.toResponse import com.processout.sdk.api.service.PO3DSService import com.processout.sdk.api.service.proxy3ds.POProxy3DSServiceRequest @@ -153,12 +150,12 @@ class PODynamicCheckoutLauncher private constructor( } private fun dispatchDefaultValues() { - eventDispatcher.subscribeForRequest( + eventDispatcher.subscribeForRequest( coroutineScope = scope ) { request -> scope.launch { val defaultValues = delegate.defaultValues( - gatewayConfigurationId = request.gatewayConfigurationId, + paymentMethod = request.paymentMethod, parameters = request.parameters ) eventDispatcher.send(request.toResponse(defaultValues)) From 38dcbddac0d694b9cbc67a0105a73de578e4c2fd Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 30 Apr 2025 17:24:19 +0300 Subject: [PATCH 02/13] Move DynamicCheckoutSavedPaymentMethods.kt (request/response) to '.dispatcher' package --- .../com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt | 2 ++ .../com/processout/sdk/ui/checkout/PODynamicCheckoutLauncher.kt | 2 ++ .../{ => dispatcher}/DynamicCheckoutSavedPaymentMethods.kt | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) rename ui/src/main/kotlin/com/processout/sdk/ui/checkout/{ => dispatcher}/DynamicCheckoutSavedPaymentMethods.kt (93%) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt index 4e0dab7d0..18614f4a5 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt @@ -54,6 +54,8 @@ import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.GooglePayCo import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.GooglePayConfiguration.CheckoutOption.DEFAULT import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.GooglePayConfiguration.TotalPriceStatus.ESTIMATED import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.GooglePayConfiguration.TotalPriceStatus.FINAL +import com.processout.sdk.ui.checkout.dispatcher.DynamicCheckoutSavedPaymentMethodsRequest +import com.processout.sdk.ui.checkout.dispatcher.DynamicCheckoutSavedPaymentMethodsResponse import com.processout.sdk.ui.napm.NativeAlternativePaymentCompletion import com.processout.sdk.ui.napm.NativeAlternativePaymentEvent import com.processout.sdk.ui.napm.NativeAlternativePaymentSideEffect diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutLauncher.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutLauncher.kt index ff36e3539..e215a12b3 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutLauncher.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutLauncher.kt @@ -20,6 +20,8 @@ import com.processout.sdk.api.service.proxy3ds.POProxy3DSServiceRequest.* import com.processout.sdk.api.service.proxy3ds.POProxy3DSServiceResponse import com.processout.sdk.core.POUnit import com.processout.sdk.core.ProcessOutActivityResult +import com.processout.sdk.ui.checkout.dispatcher.DynamicCheckoutSavedPaymentMethodsRequest +import com.processout.sdk.ui.checkout.dispatcher.toResponse import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi import kotlinx.coroutines.* diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutSavedPaymentMethods.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/dispatcher/DynamicCheckoutSavedPaymentMethods.kt similarity index 93% rename from ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutSavedPaymentMethods.kt rename to ui/src/main/kotlin/com/processout/sdk/ui/checkout/dispatcher/DynamicCheckoutSavedPaymentMethods.kt index 4e60d1883..e58096d9f 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutSavedPaymentMethods.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/dispatcher/DynamicCheckoutSavedPaymentMethods.kt @@ -1,4 +1,4 @@ -package com.processout.sdk.ui.checkout +package com.processout.sdk.ui.checkout.dispatcher import com.processout.sdk.api.dispatcher.POEventDispatcher import com.processout.sdk.ui.savedpaymentmethods.POSavedPaymentMethodsConfiguration From d078af85adfea4d0ceb3ffe8b38d7df243c15946 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 30 Apr 2025 17:25:50 +0300 Subject: [PATCH 03/13] alternativePayment() function in delegate --- .../sdk/ui/checkout/PODynamicCheckoutDelegate.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt index bd52df692..4abc00f87 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt @@ -79,6 +79,15 @@ interface PODynamicCheckoutDelegate { parameters: List ): Map = emptyMap() + /** + * Allows to override default alternative payment configuration. + * Invoked when payment method is about to start. + */ + suspend fun alternativePayment( + paymentMethod: PODynamicCheckoutPaymentMethod.AlternativePayment, + configuration: PODynamicCheckoutConfiguration.AlternativePaymentConfiguration + ): PODynamicCheckoutConfiguration.AlternativePaymentConfiguration = configuration + /** * Allows to customize saved payment methods configuration. */ From 95b5d3369c262d446dfe38d19a1fd8c4eeaaa5f5 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 30 Apr 2025 17:31:51 +0300 Subject: [PATCH 04/13] DynamicCheckoutAlternativePaymentConfiguration.kt (request/response) --- ...CheckoutAlternativePaymentConfiguration.kt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 ui/src/main/kotlin/com/processout/sdk/ui/checkout/dispatcher/DynamicCheckoutAlternativePaymentConfiguration.kt diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/dispatcher/DynamicCheckoutAlternativePaymentConfiguration.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/dispatcher/DynamicCheckoutAlternativePaymentConfiguration.kt new file mode 100644 index 000000000..c560ff96b --- /dev/null +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/dispatcher/DynamicCheckoutAlternativePaymentConfiguration.kt @@ -0,0 +1,21 @@ +package com.processout.sdk.ui.checkout.dispatcher + +import com.processout.sdk.api.dispatcher.POEventDispatcher +import com.processout.sdk.api.model.response.PODynamicCheckoutPaymentMethod +import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration +import java.util.UUID + +internal data class DynamicCheckoutAlternativePaymentConfigurationRequest( + override val uuid: UUID = UUID.randomUUID(), + val paymentMethod: PODynamicCheckoutPaymentMethod.AlternativePayment, + val configuration: PODynamicCheckoutConfiguration.AlternativePaymentConfiguration +) : POEventDispatcher.Request + +internal data class DynamicCheckoutAlternativePaymentConfigurationResponse( + override val uuid: UUID, + val configuration: PODynamicCheckoutConfiguration.AlternativePaymentConfiguration +) : POEventDispatcher.Response + +internal fun DynamicCheckoutAlternativePaymentConfigurationRequest.toResponse( + configuration: PODynamicCheckoutConfiguration.AlternativePaymentConfiguration +) = DynamicCheckoutAlternativePaymentConfigurationResponse(uuid, configuration) From c507aa132034cf288504ccd8fe1c06548d394186 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 30 Apr 2025 17:37:35 +0300 Subject: [PATCH 05/13] dispatchAlternativePaymentConfiguration() in launcher --- .../sdk/ui/checkout/PODynamicCheckoutLauncher.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutLauncher.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutLauncher.kt index e215a12b3..ceb26f304 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutLauncher.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutLauncher.kt @@ -20,6 +20,7 @@ import com.processout.sdk.api.service.proxy3ds.POProxy3DSServiceRequest.* import com.processout.sdk.api.service.proxy3ds.POProxy3DSServiceResponse import com.processout.sdk.core.POUnit import com.processout.sdk.core.ProcessOutActivityResult +import com.processout.sdk.ui.checkout.dispatcher.DynamicCheckoutAlternativePaymentConfigurationRequest import com.processout.sdk.ui.checkout.dispatcher.DynamicCheckoutSavedPaymentMethodsRequest import com.processout.sdk.ui.checkout.dispatcher.toResponse import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi @@ -93,6 +94,7 @@ class PODynamicCheckoutLauncher private constructor( dispatchInvoiceAuthorizationRequest() dispatchPreferredScheme() dispatchDefaultValues() + dispatchAlternativePaymentConfiguration() dispatchSavedPaymentMethodsConfiguration() dispatch3DSService() } @@ -165,6 +167,20 @@ class PODynamicCheckoutLauncher private constructor( } } + private fun dispatchAlternativePaymentConfiguration() { + eventDispatcher.subscribeForRequest( + coroutineScope = scope + ) { request -> + scope.launch { + val configuration = delegate.alternativePayment( + paymentMethod = request.paymentMethod, + configuration = request.configuration + ) + eventDispatcher.send(request.toResponse(configuration)) + } + } + } + private fun dispatchSavedPaymentMethodsConfiguration() { eventDispatcher.subscribeForRequest( coroutineScope = scope From 78cd9c59d283a3bb117a00f881a7b8728018e21a Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 1 May 2025 18:46:57 +0300 Subject: [PATCH 06/13] Updated DynamicCheckoutAlternativePaymentConfigurationResponse --- .../DynamicCheckoutAlternativePaymentConfiguration.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/dispatcher/DynamicCheckoutAlternativePaymentConfiguration.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/dispatcher/DynamicCheckoutAlternativePaymentConfiguration.kt index c560ff96b..c743c7dde 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/dispatcher/DynamicCheckoutAlternativePaymentConfiguration.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/dispatcher/DynamicCheckoutAlternativePaymentConfiguration.kt @@ -13,9 +13,10 @@ internal data class DynamicCheckoutAlternativePaymentConfigurationRequest( internal data class DynamicCheckoutAlternativePaymentConfigurationResponse( override val uuid: UUID, + val paymentMethod: PODynamicCheckoutPaymentMethod.AlternativePayment, val configuration: PODynamicCheckoutConfiguration.AlternativePaymentConfiguration ) : POEventDispatcher.Response internal fun DynamicCheckoutAlternativePaymentConfigurationRequest.toResponse( configuration: PODynamicCheckoutConfiguration.AlternativePaymentConfiguration -) = DynamicCheckoutAlternativePaymentConfigurationResponse(uuid, configuration) +) = DynamicCheckoutAlternativePaymentConfigurationResponse(uuid, paymentMethod, configuration) From 0f7bed6d0668b492666548a29c96006c4898c151 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 1 May 2025 18:53:23 +0300 Subject: [PATCH 07/13] Request and collect nAPM config per payment method --- .../ui/checkout/DynamicCheckoutInteractor.kt | 99 +++++++++++++++---- 1 file changed, 81 insertions(+), 18 deletions(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt index 18614f4a5..0fc94a418 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt @@ -47,6 +47,7 @@ import com.processout.sdk.ui.checkout.DynamicCheckoutInteractorState.PaymentMeth import com.processout.sdk.ui.checkout.DynamicCheckoutInteractorState.PaymentMethod.AlternativePayment import com.processout.sdk.ui.checkout.DynamicCheckoutInteractorState.PaymentMethod.Card import com.processout.sdk.ui.checkout.DynamicCheckoutInteractorState.PaymentMethod.GooglePay +import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.AlternativePaymentConfiguration import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.GooglePayConfiguration import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.GooglePayConfiguration.BillingAddressConfiguration.Format.FULL import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.GooglePayConfiguration.BillingAddressConfiguration.Format.MIN @@ -54,12 +55,12 @@ import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.GooglePayCo import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.GooglePayConfiguration.CheckoutOption.DEFAULT import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.GooglePayConfiguration.TotalPriceStatus.ESTIMATED import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.GooglePayConfiguration.TotalPriceStatus.FINAL +import com.processout.sdk.ui.checkout.dispatcher.DynamicCheckoutAlternativePaymentConfigurationRequest +import com.processout.sdk.ui.checkout.dispatcher.DynamicCheckoutAlternativePaymentConfigurationResponse import com.processout.sdk.ui.checkout.dispatcher.DynamicCheckoutSavedPaymentMethodsRequest import com.processout.sdk.ui.checkout.dispatcher.DynamicCheckoutSavedPaymentMethodsResponse -import com.processout.sdk.ui.napm.NativeAlternativePaymentCompletion -import com.processout.sdk.ui.napm.NativeAlternativePaymentEvent -import com.processout.sdk.ui.napm.NativeAlternativePaymentSideEffect -import com.processout.sdk.ui.napm.NativeAlternativePaymentViewModel +import com.processout.sdk.ui.napm.* +import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.* import com.processout.sdk.ui.savedpaymentmethods.POSavedPaymentMethodsConfiguration import com.processout.sdk.ui.shared.extension.orElse import kotlinx.coroutines.* @@ -129,6 +130,7 @@ internal class DynamicCheckoutInteractor( collectInvoice() collectInvoiceAuthorizationRequest() collectTokenizedCard() + collectNativeAlternativePaymentConfiguration() collectSavedPaymentMethodsConfiguration() fetchConfiguration() } @@ -440,13 +442,6 @@ internal class DynamicCheckoutInteractor( POLogger.info("Selected payment method: %s", paymentMethod.original) dispatch(DidSelectPaymentMethod(paymentMethod = paymentMethod.original)) resetPaymentMethods() - if (state.processingPaymentMethod != null) { - invalidateInvoice( - reason = PODynamicCheckoutInvoiceInvalidationReason.PaymentMethodChanged - ) - } else if (state.invoice != null) { - start(paymentMethod) - } _state.update { it.copy( selectedPaymentMethod = paymentMethod, @@ -454,6 +449,14 @@ internal class DynamicCheckoutInteractor( errorMessage = null ) } + if (state.processingPaymentMethod != null) { + invalidateInvoice( + reason = PODynamicCheckoutInvoiceInvalidationReason.PaymentMethodChanged, + selectedPaymentMethod = paymentMethod + ) + } else if (state.invoice != null) { + start(paymentMethod) + } } } @@ -471,12 +474,14 @@ internal class DynamicCheckoutInteractor( configuration = cardTokenization.configuration .apply(paymentMethod.configuration) ) - is NativeAlternativePayment -> nativeAlternativePayment.start( - configuration = nativeAlternativePayment.configuration.copy( - invoiceId = configuration.invoiceRequest.invoiceId, - gatewayConfigurationId = paymentMethod.gatewayConfigurationId + is NativeAlternativePayment -> interactorScope.launch { + eventDispatcher.send( + DynamicCheckoutAlternativePaymentConfigurationRequest( + paymentMethod = paymentMethod.original, + configuration = configuration.alternativePayment + ) ) - ) + } else -> {} } } @@ -500,6 +505,61 @@ internal class DynamicCheckoutInteractor( never -> CollectionMode.Never } + private fun collectNativeAlternativePaymentConfiguration() { + eventDispatcher.subscribeForResponse( + coroutineScope = interactorScope + ) { response -> + _state.value.selectedPaymentMethod?.let { paymentMethod -> + if (paymentMethod.original != response.paymentMethod) { + return@let + } + if (paymentMethod is NativeAlternativePayment) { + nativeAlternativePayment.start( + configuration = nativeAlternativePayment.configuration + .apply( + invoiceId = configuration.invoiceRequest.invoiceId, + gatewayConfigurationId = paymentMethod.gatewayConfigurationId, + configuration = response.configuration + ) + ) + } + } + } + } + + private fun PONativeAlternativePaymentConfiguration.apply( + invoiceId: String, + gatewayConfigurationId: String, + configuration: AlternativePaymentConfiguration + ) = copy( + invoiceId = invoiceId, + gatewayConfigurationId = gatewayConfigurationId, + paymentConfirmation = paymentConfirmation.apply(configuration.paymentConfirmation), + barcode = configuration.barcode, + inlineSingleSelectValuesLimit = configuration.inlineSingleSelectValuesLimit + ) + + private fun PaymentConfirmationConfiguration.apply( + configuration: AlternativePaymentConfiguration.PaymentConfirmationConfiguration + ) = copy( + timeoutSeconds = configuration.timeoutSeconds, + showProgressIndicatorAfterSeconds = configuration.showProgressIndicatorAfterSeconds, + confirmButton = configuration.confirmButton?.let { + Button( + text = it.text, + icon = it.icon + ) + }, + cancelButton = configuration.cancelButton?.let { + CancelButton( + text = it.text, + icon = it.icon, + disabledForSeconds = it.disabledForSeconds, + confirmation = it.confirmation + ) + } + ) + private fun onFieldValueChanged(event: FieldValueChanged) { when (val paymentMethod = paymentMethod(event.paymentMethodId)) { is Card -> cardTokenization.onEvent( @@ -787,7 +847,10 @@ internal class DynamicCheckoutInteractor( } } - private fun invalidateInvoice(reason: PODynamicCheckoutInvoiceInvalidationReason) { + private fun invalidateInvoice( + reason: PODynamicCheckoutInvoiceInvalidationReason, + selectedPaymentMethod: PaymentMethod? = null + ) { POLogger.info("Invalidating invoice. Reason: %s", reason, attributes = logAttributes) var errorMessage: String? = null if (reason is PODynamicCheckoutInvoiceInvalidationReason.Failure) { @@ -811,7 +874,7 @@ internal class DynamicCheckoutInteractor( _state.update { it.copy( invoice = null, - selectedPaymentMethod = null, + selectedPaymentMethod = selectedPaymentMethod, processingPaymentMethod = null, errorMessage = errorMessage ) From d9c05aa408270511e9d2a9ff252d7ec72425d938 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 1 May 2025 19:19:23 +0300 Subject: [PATCH 08/13] Throw IllegalStateException on attempt to change returnUrl via delegate --- .../processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt | 3 +++ .../processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt | 1 + 2 files changed, 4 insertions(+) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt index 0fc94a418..e0edb8d8b 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutInteractor.kt @@ -514,6 +514,9 @@ internal class DynamicCheckoutInteractor( return@let } if (paymentMethod is NativeAlternativePayment) { + if (response.configuration.returnUrl != configuration.alternativePayment.returnUrl) { + error("Changing alternative payment 'returnUrl' is not supported via delegate.") + } nativeAlternativePayment.start( configuration = nativeAlternativePayment.configuration .apply( diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt index 4abc00f87..4f26f4abc 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt @@ -82,6 +82,7 @@ interface PODynamicCheckoutDelegate { /** * Allows to override default alternative payment configuration. * Invoked when payment method is about to start. + * __Note:__ Changing _returnUrl_ is not supported and will throw _IllegalStateException_. */ suspend fun alternativePayment( paymentMethod: PODynamicCheckoutPaymentMethod.AlternativePayment, From f57270871268738afe1d9abcf96b1114cc91e16a Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 1 May 2025 19:32:52 +0300 Subject: [PATCH 09/13] Remove unnecessary 'suspend' modifier from functions in DC delegate --- .../processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt index 4f26f4abc..a15c0a0e0 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutDelegate.kt @@ -65,7 +65,7 @@ interface PODynamicCheckoutDelegate { * Allows to choose default preferred card scheme based on issuer information. * Primary card scheme is used by default. */ - suspend fun preferredScheme( + fun preferredScheme( issuerInformation: POCardIssuerInformation ): String? = issuerInformation.scheme @@ -84,7 +84,7 @@ interface PODynamicCheckoutDelegate { * Invoked when payment method is about to start. * __Note:__ Changing _returnUrl_ is not supported and will throw _IllegalStateException_. */ - suspend fun alternativePayment( + fun alternativePayment( paymentMethod: PODynamicCheckoutPaymentMethod.AlternativePayment, configuration: PODynamicCheckoutConfiguration.AlternativePaymentConfiguration ): PODynamicCheckoutConfiguration.AlternativePaymentConfiguration = configuration @@ -92,7 +92,7 @@ interface PODynamicCheckoutDelegate { /** * Allows to customize saved payment methods configuration. */ - suspend fun savedPaymentMethods( + fun savedPaymentMethods( configuration: POSavedPaymentMethodsConfiguration ): POSavedPaymentMethodsConfiguration = configuration } From 691b87fd734d65a4edb14360d0fc777a19ae4e9b Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 1 May 2025 20:10:05 +0300 Subject: [PATCH 10/13] kotlinxCoroutinesPlayServicesVersion = '1.10.2' --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 59a4f823b..06b1d475c 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ ext { gmsWalletVersion = '19.4.0' mlkitTextRecognitionVersion = '19.0.1' - kotlinxCoroutinesPlayServicesVersion = '1.9.0' + kotlinxCoroutinesPlayServicesVersion = '1.10.2' retrofitVersion = '2.11.0' moshiVersion = '1.15.2' From b2c880297217337d1183548891db622f5acc15ff Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 1 May 2025 20:11:19 +0300 Subject: [PATCH 11/13] Remove unnecessary 'suspend' modifier from functions in delegates --- .../sdk/ui/card/tokenization/POCardTokenizationDelegate.kt | 4 ++-- .../com/processout/sdk/ui/card/update/POCardUpdateDelegate.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/POCardTokenizationDelegate.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/POCardTokenizationDelegate.kt index 535a553aa..fe3bbf554 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/POCardTokenizationDelegate.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/POCardTokenizationDelegate.kt @@ -36,7 +36,7 @@ interface POCardTokenizationDelegate { * Allows to choose default preferred card scheme based on issuer information. * Primary card scheme is used by default. */ - suspend fun preferredScheme( + fun preferredScheme( issuerInformation: POCardIssuerInformation ): String? = issuerInformation.scheme @@ -44,7 +44,7 @@ interface POCardTokenizationDelegate { * Allows to decide whether the flow should continue or complete after the failure. * Returns _true_ by default. */ - suspend fun shouldContinue( + fun shouldContinue( failure: ProcessOutResult.Failure ): Boolean = true } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/update/POCardUpdateDelegate.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/update/POCardUpdateDelegate.kt index b3b4f7b13..17f69fc70 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/update/POCardUpdateDelegate.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/update/POCardUpdateDelegate.kt @@ -17,7 +17,7 @@ interface POCardUpdateDelegate { * Allows to decide whether the card update should continue or complete after the failure. * Returns _true_ by default. */ - suspend fun shouldContinue( + fun shouldContinue( failure: ProcessOutResult.Failure ): Boolean = true } From b50e7b3e20749f41c4ef0c485348e3c435a5ad46 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 1 May 2025 21:05:57 +0300 Subject: [PATCH 12/13] Explicit dependecy: org.jetbrains.kotlinx:kotlinx-coroutines-android --- build.gradle | 3 ++- sdk/build.gradle | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 06b1d475c..ac6fa2f7d 100644 --- a/build.gradle +++ b/build.gradle @@ -36,6 +36,8 @@ ext { publishGroupId = 'com.processout' publishVersion = file('version.resolved').getText().trim() + kotlinxCoroutinesVersion = '1.10.2' + androidxCoreVersion = '1.15.0' androidxAppCompatVersion = '1.7.0' androidxConstraintLayoutVersion = '2.2.1' @@ -54,7 +56,6 @@ ext { gmsWalletVersion = '19.4.0' mlkitTextRecognitionVersion = '19.0.1' - kotlinxCoroutinesPlayServicesVersion = '1.10.2' retrofitVersion = '2.11.0' moshiVersion = '1.15.2' diff --git a/sdk/build.gradle b/sdk/build.gradle index fc2ef0382..ea5974309 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -100,6 +100,9 @@ def setBuildConfig(buildType) { } dependencies { + api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinxCoroutinesVersion" + api "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$kotlinxCoroutinesVersion" + api "androidx.appcompat:appcompat:$androidxAppCompatVersion" api "androidx.core:core-ktx:$androidxCoreVersion" api "androidx.activity:activity-ktx:$androidxActivityVersion" @@ -110,7 +113,6 @@ dependencies { api "com.google.android.material:material:$materialVersion" api "com.google.android.gms:play-services-wallet:$gmsWalletVersion" - api "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$kotlinxCoroutinesPlayServicesVersion" implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" implementation "com.squareup.retrofit2:converter-moshi:$retrofitVersion" From a9f936027e479e9623afd9ba96ba39a19c22e10e Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Thu, 1 May 2025 21:07:49 +0300 Subject: [PATCH 13/13] androidxCoreVersion = '1.16.0' --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ac6fa2f7d..3a42ea54e 100644 --- a/build.gradle +++ b/build.gradle @@ -38,7 +38,7 @@ ext { kotlinxCoroutinesVersion = '1.10.2' - androidxCoreVersion = '1.15.0' + androidxCoreVersion = '1.16.0' androidxAppCompatVersion = '1.7.0' androidxConstraintLayoutVersion = '2.2.1' androidxActivityVersion = '1.10.1'