diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractor.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractor.kt index 421a6908..35752614 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractor.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractor.kt @@ -965,7 +965,8 @@ internal class NativeAlternativePaymentInteractor( ) _state.update { Pending(pendingStateValue) } enablePendingSecondaryAction() - if (pendingStateValue.elements.isNullOrEmpty() || + if (configuration.redirect?.enableHeadlessMode == true || + pendingStateValue.elements.isNullOrEmpty() || configuration.paymentConfirmation.confirmButton == null ) { capture() diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt index aaabe67c..25aceadd 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt @@ -392,8 +392,9 @@ data class PONativeAlternativePaymentConfiguration( * * @param[returnUrl] Deep link return URL. Required for the flows that include web redirect. * @param[enableHeadlessMode] Enables headless mode. - * The web redirect will be handled directly when it's the first step in the flow, - * and if it's the only required step it will complete the flow without starting the bottom sheet. + * The redirect (web or deep link) will be handled directly when it's the first step in the flow, without starting the bottom sheet. + * It will also capture the payment in the background when it's required by the flow. + * __Note:__ use only with flows that do not require user input or instructions in the native UI. */ @Parcelize data class RedirectConfiguration( diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentLauncher.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentLauncher.kt index e8e4d57f..280aee0c 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentLauncher.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentLauncher.kt @@ -1,13 +1,15 @@ package com.processout.sdk.ui.napm import android.app.Application -import android.content.Context import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResultLauncher +import androidx.activity.viewModels import androidx.core.app.ActivityOptionsCompat import androidx.core.net.toUri import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.processout.sdk.R import com.processout.sdk.api.ProcessOut import com.processout.sdk.api.dispatcher.POEventDispatcher @@ -41,10 +43,8 @@ import kotlinx.coroutines.launch * Launcher that starts [NativeAlternativePaymentActivity] and provides the result. */ class PONativeAlternativePaymentLauncher private constructor( - private val app: Application, - private val scope: CoroutineScope, + private val hostActivity: ComponentActivity, private val launcher: ActivityResultLauncher, - private val activityOptions: ActivityOptionsCompat, private val delegate: PONativeAlternativePaymentDelegate, private val callback: (ProcessOutActivityResult) -> Unit, private val eventDispatcher: POEventDispatcher = POEventDispatcher.instance, @@ -52,6 +52,26 @@ class PONativeAlternativePaymentLauncher private constructor( private val customerTokensService: POCustomerTokensService = ProcessOut.instance.customerTokens ) { + private val app: Application = hostActivity.application + private val scope: CoroutineScope = hostActivity.lifecycleScope + + private val activityOptions = ActivityOptionsCompat.makeCustomAnimation( + hostActivity, R.anim.po_slide_in_vertical, 0 + ) + + private val viewModel: NativeAlternativePaymentViewModel by hostActivity.viewModels { + NativeAlternativePaymentViewModel.Factory( + app = app, + configuration = PONativeAlternativePaymentConfiguration( + flow = Authorization( + invoiceId = String(), + gatewayConfigurationId = String() + ), + header = null + ) + ) + } + private lateinit var customTabLauncher: POAlternativePaymentMethodCustomTabLauncher private object LocalCache { @@ -68,13 +88,11 @@ class PONativeAlternativePaymentLauncher private constructor( delegate: PONativeAlternativePaymentDelegate, callback: (ProcessOutActivityResult) -> Unit ) = PONativeAlternativePaymentLauncher( - app = from.requireActivity().application, - scope = from.lifecycleScope, + hostActivity = from.requireActivity(), launcher = from.registerForActivityResult( NativeAlternativePaymentActivityContract(), callback ), - activityOptions = createActivityOptions(from.requireContext()), delegate = delegate, callback = callback ).apply { @@ -96,13 +114,11 @@ class PONativeAlternativePaymentLauncher private constructor( delegate: com.processout.sdk.ui.napm.delegate.PONativeAlternativePaymentDelegate, callback: (ProcessOutActivityResult) -> Unit ) = PONativeAlternativePaymentLauncher( - app = from.requireActivity().application, - scope = from.lifecycleScope, + hostActivity = from.requireActivity(), launcher = from.registerForActivityResult( NativeAlternativePaymentActivityContract(), callback ), - activityOptions = createActivityOptions(from.requireContext()), delegate = object : PONativeAlternativePaymentDelegate {}, callback = callback ).apply { @@ -121,13 +137,11 @@ class PONativeAlternativePaymentLauncher private constructor( from: Fragment, callback: (ProcessOutActivityResult) -> Unit ) = PONativeAlternativePaymentLauncher( - app = from.requireActivity().application, - scope = from.lifecycleScope, + hostActivity = from.requireActivity(), launcher = from.registerForActivityResult( NativeAlternativePaymentActivityContract(), callback ), - activityOptions = createActivityOptions(from.requireContext()), delegate = object : PONativeAlternativePaymentDelegate {}, callback = callback ).apply { @@ -146,14 +160,12 @@ class PONativeAlternativePaymentLauncher private constructor( delegate: PONativeAlternativePaymentDelegate, callback: (ProcessOutActivityResult) -> Unit ) = PONativeAlternativePaymentLauncher( - app = from.application, - scope = from.lifecycleScope, + hostActivity = from, launcher = from.registerForActivityResult( NativeAlternativePaymentActivityContract(), from.activityResultRegistry, callback ), - activityOptions = createActivityOptions(from), delegate = delegate, callback = callback ).apply { @@ -175,14 +187,12 @@ class PONativeAlternativePaymentLauncher private constructor( delegate: com.processout.sdk.ui.napm.delegate.PONativeAlternativePaymentDelegate, callback: (ProcessOutActivityResult) -> Unit ) = PONativeAlternativePaymentLauncher( - app = from.application, - scope = from.lifecycleScope, + hostActivity = from, launcher = from.registerForActivityResult( NativeAlternativePaymentActivityContract(), from.activityResultRegistry, callback ), - activityOptions = createActivityOptions(from), delegate = object : PONativeAlternativePaymentDelegate {}, callback = callback ).apply { @@ -201,14 +211,12 @@ class PONativeAlternativePaymentLauncher private constructor( from: ComponentActivity, callback: (ProcessOutActivityResult) -> Unit ) = PONativeAlternativePaymentLauncher( - app = from.application, - scope = from.lifecycleScope, + hostActivity = from, launcher = from.registerForActivityResult( NativeAlternativePaymentActivityContract(), from.activityResultRegistry, callback ), - activityOptions = createActivityOptions(from), delegate = object : PONativeAlternativePaymentDelegate {}, callback = callback ).apply { @@ -217,18 +225,30 @@ class PONativeAlternativePaymentLauncher private constructor( callback = ::handleWebRedirect ) } - - private fun createActivityOptions(context: Context) = - ActivityOptionsCompat.makeCustomAnimation( - context, R.anim.po_slide_in_vertical, 0 - ) } init { + collectViewModelCompletion() dispatchEvents() dispatchDefaultValues() } + private fun collectViewModelCompletion() { + hostActivity.lifecycleScope.launch { + hostActivity.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.completion.collect { completion -> + when (completion) { + NativeAlternativePaymentCompletion.Success -> + completeHeadlessMode(result = ProcessOutResult.Success(value = POUnit)) + is NativeAlternativePaymentCompletion.Failure -> + completeHeadlessMode(result = completion.failure) + else -> {} + } + } + } + } + } + private fun dispatchEvents() { eventDispatcher.subscribe( coroutineScope = scope @@ -348,14 +368,11 @@ class PONativeAlternativePaymentLauncher private constructor( ) { when (state) { NEXT_STEP_REQUIRED -> handleNextStep(redirect, configuration) - PENDING -> launchActivity(configuration) - SUCCESS -> - if (configuration.success != null) { - launchActivity(configuration) - } else { - POLogger.info("Success: payment completed.") - completeHeadlessMode(result = ProcessOutResult.Success(value = POUnit)) - } + PENDING -> viewModel.start(configuration) + SUCCESS -> { + POLogger.info("Success: payment completed.") + completeHeadlessMode(result = ProcessOutResult.Success(value = POUnit)) + } UNKNOWN -> { val failure = ProcessOutResult.Failure( code = Internal(), @@ -482,6 +499,7 @@ class PONativeAlternativePaymentLauncher private constructor( } } LocalCache.configuration = null + viewModel.reset() callback(result.toActivityResult()) } }