diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/network/interceptor/RetryInterceptor.kt b/sdk/src/main/kotlin/com/processout/sdk/api/network/interceptor/RetryInterceptor.kt index 4096b1acd..58327b3f7 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/network/interceptor/RetryInterceptor.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/network/interceptor/RetryInterceptor.kt @@ -11,7 +11,7 @@ import java.util.UUID internal class RetryInterceptor( private val retryStrategy: PORetryStrategy = Exponential( maxRetries = 4, - initialDelay = 100, + seedDelay = 100, maxDelay = 1000, factor = 3.0 ) @@ -19,7 +19,7 @@ internal class RetryInterceptor( override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request().addIdempotencyKey() - val iterator = retryStrategy.iterator + val backoffIterator = retryStrategy.newBackoffIterator() repeat(retryStrategy.maxRetries - 1) { var response: Response? = null try { @@ -34,7 +34,7 @@ internal class RetryInterceptor( // network issue, retry } response?.body?.close() - Thread.sleep(iterator.next()) + Thread.sleep(backoffIterator.next()) } return chain.proceed(request) } diff --git a/sdk/src/main/kotlin/com/processout/sdk/core/retry/PORetryStrategy.kt b/sdk/src/main/kotlin/com/processout/sdk/core/retry/PORetryStrategy.kt index eb1043dad..7e4502633 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/core/retry/PORetryStrategy.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/core/retry/PORetryStrategy.kt @@ -7,7 +7,7 @@ import kotlin.math.roundToLong @ProcessOutInternalApi sealed class PORetryStrategy( val maxRetries: Int, - private val initialDelay: Long, + private val seedDelay: Long, private val minDelay: Long, private val maxDelay: Long, private val factor: Double @@ -18,7 +18,7 @@ sealed class PORetryStrategy( delay: Long ) : PORetryStrategy( maxRetries = maxRetries, - initialDelay = delay, + seedDelay = delay, minDelay = delay, maxDelay = delay, factor = 1.0 @@ -26,23 +26,23 @@ sealed class PORetryStrategy( class Exponential( maxRetries: Int, - initialDelay: Long, - minDelay: Long = initialDelay, + seedDelay: Long, + minDelay: Long = seedDelay, maxDelay: Long, factor: Double ) : PORetryStrategy( maxRetries = maxRetries, - initialDelay = initialDelay, + seedDelay = seedDelay, minDelay = minDelay, maxDelay = maxDelay, factor = factor ) - class Iterator( - private val iterator: kotlin.collections.Iterator, + class BackoffIterator( + private val iterator: Iterator, private val minDelay: Long, private val maxDelay: Long - ) : kotlin.collections.Iterator { + ) : Iterator { override fun hasNext(): Boolean = iterator.hasNext() @@ -53,12 +53,11 @@ sealed class PORetryStrategy( } } - val iterator: Iterator - get() = Iterator( - iterator = generateSequence(initialDelay.toDouble()) { previous -> - previous * factor - }.iterator(), - minDelay = minDelay, - maxDelay = maxDelay - ) + fun newBackoffIterator() = BackoffIterator( + iterator = generateSequence(seed = seedDelay.toDouble()) { previous -> + previous * factor + }.iterator(), + minDelay = minDelay, + maxDelay = maxDelay + ) } diff --git a/sdk/src/main/kotlin/com/processout/sdk/ui/nativeapm/NativeAlternativePaymentMethodViewModel.kt b/sdk/src/main/kotlin/com/processout/sdk/ui/nativeapm/NativeAlternativePaymentMethodViewModel.kt index 2b9e374a6..d599d950c 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/ui/nativeapm/NativeAlternativePaymentMethodViewModel.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/ui/nativeapm/NativeAlternativePaymentMethodViewModel.kt @@ -3,6 +3,7 @@ package com.processout.sdk.ui.nativeapm import android.app.Application import android.os.Handler import android.os.Looper +import android.os.SystemClock import android.util.Patterns import android.view.View import android.view.inputmethod.EditorInfo @@ -79,7 +80,7 @@ internal class NativeAlternativePaymentMethodViewModel private constructor( eventDispatcher = PODefaultEventDispatchers.defaultNativeAlternativePaymentMethod, captureRetryStrategy = Exponential( maxRetries = Int.MAX_VALUE, - initialDelay = 150, + seedDelay = 150, minDelay = 3 * 1000, maxDelay = 90 * 1000, factor = 1.45 @@ -104,8 +105,8 @@ internal class NativeAlternativePaymentMethodViewModel private constructor( var animateViewTransition = true - private var captureStartTimestamp = 0L - private var capturePassedTimestamp = 0L + private var captureStartTime = 0L + private var captureElapsedTime = 0L private val handler by lazy { Handler(Looper.getMainLooper()) } @@ -521,26 +522,26 @@ internal class NativeAlternativePaymentMethodViewModel private constructor( } private fun capture() { - if (captureStartTimestamp != 0L) { + if (captureStartTime != 0L) { return } - captureStartTimestamp = System.currentTimeMillis() + captureStartTime = SystemClock.elapsedRealtime() options.showPaymentConfirmationProgressIndicatorAfterSeconds?.let { afterSeconds -> showPaymentConfirmationProgressIndicator( afterMillis = TimeUnit.SECONDS.toMillis(afterSeconds.toLong()) ) } viewModelScope.launch { - val iterator = captureRetryStrategy.iterator - while (capturePassedTimestamp <= options.paymentConfirmationTimeoutSeconds * 1000) { + val backoffIterator = captureRetryStrategy.newBackoffIterator() + while (captureElapsedTime <= options.paymentConfirmationTimeoutSeconds * 1000L) { val result = invoicesService.captureNativeAlternativePayment(invoiceId, gatewayConfigurationId) POLogger.debug("Attempted to capture invoice.") if (isCaptureRetryable(result)) { - delay(iterator.next()) - capturePassedTimestamp = System.currentTimeMillis() - captureStartTimestamp + delay(timeMillis = backoffIterator.next()) + captureElapsedTime = SystemClock.elapsedRealtime() - captureStartTime } else { - captureStartTimestamp = 0L - capturePassedTimestamp = 0L + captureStartTime = 0L + captureElapsedTime = 0L when (result) { is ProcessOutResult.Success -> _uiState.value.doWhenCapture { uiModel -> @@ -552,8 +553,8 @@ internal class NativeAlternativePaymentMethodViewModel private constructor( return@launch } } - captureStartTimestamp = 0L - capturePassedTimestamp = 0L + captureStartTime = 0L + captureElapsedTime = 0L _uiState.value = Failure( ProcessOutResult.Failure( Timeout(), "Payment confirmation timed out." diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCountdownTimerText.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCountdownTimerText.kt index 56893a086..6fdbb74e5 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCountdownTimerText.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POCountdownTimerText.kt @@ -2,6 +2,7 @@ package com.processout.sdk.ui.core.component +import android.os.SystemClock import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight @@ -16,23 +17,25 @@ import kotlinx.coroutines.delay fun POCountdownTimerText( textFormat: String, timeoutSeconds: Int, + initialElapsedRealtime: Long, modifier: Modifier = Modifier, style: POText.Style = POText.Style( color = colors.text.primary, textStyle = typography.s15(FontWeight.Medium) ) ) { - var secondsLeft by remember { mutableIntStateOf(timeoutSeconds) } - val formattedText = remember(secondsLeft) { - val minutes = secondsLeft / 60 - val seconds = secondsLeft % 60 + var remainingSeconds by remember { mutableIntStateOf(timeoutSeconds) } + val formattedText = remember(remainingSeconds) { + val minutes = remainingSeconds / 60 + val seconds = remainingSeconds % 60 val formattedTime = String.format("%02d:%02d", minutes, seconds) String.format(textFormat, formattedTime) } - LaunchedEffect(secondsLeft) { - if (secondsLeft > 0) { + LaunchedEffect(Unit) { + while (remainingSeconds > 0) { + val elapsedSeconds = ((SystemClock.elapsedRealtime() - initialElapsedRealtime) / 1000L).toInt() + remainingSeconds = (timeoutSeconds - elapsedSeconds).coerceAtLeast(minimumValue = 0) delay(timeMillis = 1000) - secondsLeft -= 1 } } POText( diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepper.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepper.kt index 0a83ed1c9..04a0e937d 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepper.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POStepper.kt @@ -1,5 +1,6 @@ package com.processout.sdk.ui.core.component.stepper +import android.os.SystemClock import androidx.compose.runtime.Composable import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -22,7 +23,8 @@ object POStepper { data class CountdownTimerText( val textFormat: String, - val timeoutSeconds: Int + val timeoutSeconds: Int, + val initialElapsedRealtime: Long = SystemClock.elapsedRealtime() ) } diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POVerticalStepper.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POVerticalStepper.kt index 86f5d572d..ee62d5f03 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POVerticalStepper.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/stepper/POVerticalStepper.kt @@ -104,6 +104,7 @@ fun POVerticalStepper( POCountdownTimerText( textFormat = description.textFormat, timeoutSeconds = description.timeoutSeconds, + initialElapsedRealtime = description.initialElapsedRealtime, modifier = Modifier .fillMaxWidth() .padding(vertical = spacing.space4), diff --git a/ui/build.gradle b/ui/build.gradle index f30cdb053..19fb93816 100644 --- a/ui/build.gradle +++ b/ui/build.gradle @@ -92,6 +92,7 @@ dependencies { api "androidx.activity:activity-compose:$androidxActivityVersion" api "androidx.lifecycle:lifecycle-viewmodel-compose:$androidxLifecycleVersion" + implementation "androidx.lifecycle:lifecycle-process:$androidxLifecycleVersion" implementation "androidx.camera:camera-camera2:$androidxCameraVersion" implementation "androidx.camera:camera-lifecycle:$androidxCameraVersion" diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/recognition/CardRecognitionSession.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/recognition/CardRecognitionSession.kt index 4c580267a..ecb2c5f57 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/recognition/CardRecognitionSession.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/recognition/CardRecognitionSession.kt @@ -2,6 +2,7 @@ package com.processout.sdk.ui.card.scanner.recognition import android.app.Application import android.graphics.Bitmap +import android.os.SystemClock import androidx.camera.core.ImageProxy import com.google.android.gms.common.moduleinstall.InstallStatusListener import com.google.android.gms.common.moduleinstall.ModuleInstall @@ -50,7 +51,7 @@ internal class CardRecognitionSession( private val moduleInstallClient = ModuleInstall.getClient(app) private val textRecognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS) - private var startTimestamp = 0L + private var startTime = 0L private val recognizedCards = mutableListOf() init { @@ -163,8 +164,8 @@ internal class CardRecognitionSession( val candidates = text.candidates(MIN_CONFIDENCE) val number = numberDetector.firstMatch(candidates) if (number != null) { - if (startTimestamp == 0L) { - startTimestamp = System.currentTimeMillis() + if (startTime == 0L) { + startTime = SystemClock.elapsedRealtime() } val card = POScannedCard( number = number, @@ -178,12 +179,12 @@ internal class CardRecognitionSession( _currentCard.send(card) } } - if (System.currentTimeMillis() - startTimestamp > RECOGNITION_DURATION_MS) { + if (SystemClock.elapsedRealtime() - startTime > RECOGNITION_DURATION_MS) { if (recognizedCards.isNotEmpty()) { sendMostFrequentCard() recognizedCards.clear() } - startTimestamp = 0L + startTime = 0L } imageProxy.close() } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentCapturePoller.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentCapturePoller.kt new file mode 100644 index 000000000..82e879f53 --- /dev/null +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentCapturePoller.kt @@ -0,0 +1,147 @@ +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.processout.sdk.ui.napm + +import android.os.SystemClock +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentAuthorizationRequest +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentTokenizationRequest +import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentAuthorizationResponse +import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentElement +import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentState +import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentState.SUCCESS +import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentTokenizationResponse +import com.processout.sdk.api.service.POCustomerTokensService +import com.processout.sdk.api.service.POInvoicesService +import com.processout.sdk.core.POFailure.Code.* +import com.processout.sdk.core.ProcessOutResult +import com.processout.sdk.core.fold +import com.processout.sdk.core.logger.POLogger +import com.processout.sdk.core.retry.PORetryStrategy +import com.processout.sdk.core.retry.PORetryStrategy.Exponential +import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.Flow.Authorization +import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.Flow.Tokenization +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.selects.onTimeout +import kotlinx.coroutines.selects.select +import kotlin.math.min + +internal class NativeAlternativePaymentCapturePoller( + private val configuration: PONativeAlternativePaymentConfiguration, + private val invoicesService: POInvoicesService, + private val customerTokensService: POCustomerTokensService, + private val retryStrategy: PORetryStrategy = Exponential( + maxRetries = Int.MAX_VALUE, + seedDelay = 150, + minDelay = 3 * 1000, + maxDelay = 90 * 1000, + factor = 1.45 + ) +) { + + data class CaptureResponse( + val state: PONativeAlternativePaymentState, + val elements: List? + ) + + private var backoffIterator = retryStrategy.newBackoffIterator() + private val backoffResetSignal = Channel(capacity = Channel.CONFLATED) + + suspend fun poll(): ProcessOutResult { + val startTime = SystemClock.elapsedRealtime() + val timeout = configuration.paymentConfirmation.timeoutSeconds * 1000L + backoffIterator = retryStrategy.newBackoffIterator() + while (backoffResetSignal.tryReceive().isSuccess) { + // Discard stale signals. + } + while (true) { + val result = call() + POLogger.debug("Attempted to capture the payment.") + if (!isRetryable(result)) { + return result + } + val elapsedTime = SystemClock.elapsedRealtime() - startTime + val remainingTime = timeout - elapsedTime + if (remainingTime <= 0) { + break + } + val waitTime = min(backoffIterator.next(), remainingTime) + select { + onTimeout(timeMillis = waitTime) {} + backoffResetSignal.onReceive { + backoffIterator = retryStrategy.newBackoffIterator() + POLogger.debug("Capture polling backoff has been reset.") + } + } + } + return ProcessOutResult.Failure( + code = Timeout(), + message = "Payment confirmation has timed out." + ) + } + + fun resetBackoff() { + backoffResetSignal.trySend(Unit) + } + + private suspend fun call(): ProcessOutResult = + when (val flow = configuration.flow) { + is Authorization -> invoicesService.authorize( + request = PONativeAlternativePaymentAuthorizationRequest( + invoiceId = flow.invoiceId, + gatewayConfigurationId = flow.gatewayConfigurationId, + configuration = flow.configuration + ) + ).map() + is Tokenization -> customerTokensService.tokenize( + request = PONativeAlternativePaymentTokenizationRequest( + customerId = flow.customerId, + customerTokenId = flow.customerTokenId, + gatewayConfigurationId = flow.gatewayConfigurationId, + configuration = flow.configuration + ) + ).map() + } + + private fun isRetryable( + result: ProcessOutResult + ): Boolean = result.fold( + onSuccess = { it.state != SUCCESS }, + onFailure = { failure -> + val retryableCodes = listOf( + NetworkUnreachable, + Timeout(), + Internal() + ) + retryableCodes.contains(failure.code) + } + ) + + @JvmName(name = "mapFromAuthorizationResult") + private fun ProcessOutResult.map() = + fold( + onSuccess = { + ProcessOutResult.Success( + CaptureResponse( + state = it.state, + elements = it.elements + ) + ) + }, + onFailure = { it } + ) + + @JvmName(name = "mapFromTokenizationResult") + private fun ProcessOutResult.map() = + fold( + onSuccess = { + ProcessOutResult.Success( + CaptureResponse( + state = it.state, + elements = it.elements + ) + ) + }, + onFailure = { it } + ) +} 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 d1625e916..c2ae4338f 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 @@ -14,6 +14,9 @@ import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.core.os.postDelayed import androidx.core.text.isDigitsOnly +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ProcessLifecycleOwner import coil.imageLoader import coil.request.CachePolicy import coil.request.ImageRequest @@ -43,7 +46,6 @@ import com.processout.sdk.core.fold import com.processout.sdk.core.logger.POLogger import com.processout.sdk.core.onFailure import com.processout.sdk.core.onSuccess -import com.processout.sdk.core.retry.PORetryStrategy import com.processout.sdk.ui.base.BaseInteractor import com.processout.sdk.ui.core.component.stepper.POStepper import com.processout.sdk.ui.core.state.POImmutableList @@ -82,9 +84,14 @@ internal class NativeAlternativePaymentInteractor( private val customerTokensService: POCustomerTokensService, private val barcodeBitmapProvider: BarcodeBitmapProvider, private val mediaStorageProvider: MediaStorageProvider, - private val captureRetryStrategy: PORetryStrategy, - private val eventDispatcher: POEventDispatcher = POEventDispatcher.instance -) : BaseInteractor() { + private val eventDispatcher: POEventDispatcher = POEventDispatcher.instance, + private var capturePoller: NativeAlternativePaymentCapturePoller = + NativeAlternativePaymentCapturePoller( + configuration = configuration, + invoicesService = invoicesService, + customerTokensService = customerTokensService + ) +) : BaseInteractor(), DefaultLifecycleObserver { private val _completion = MutableStateFlow(Awaiting) val completion = _completion.asStateFlow() @@ -100,9 +107,11 @@ internal class NativeAlternativePaymentInteractor( private var paymentState: PONativeAlternativePaymentState = UNKNOWN private var latestDefaultValuesRequest: NativeAlternativePaymentDefaultValuesRequest? = null private var latestWillSubmitParametersEvent: WillSubmitParameters? = null + private var isCapturePolling = false - private var captureStartTimestamp = 0L - private var capturePassedTimestamp = 0L + init { + ProcessLifecycleOwner.get().lifecycle.addObserver(this) + } fun start() { if (_state.value !is Idle) { @@ -122,15 +131,20 @@ internal class NativeAlternativePaymentInteractor( return } this.configuration = configuration + capturePoller = NativeAlternativePaymentCapturePoller( + configuration = configuration, + invoicesService = invoicesService, + customerTokensService = customerTokensService + ) start() } fun reset() { interactorScope.coroutineContext.cancelChildren() handler.removeCallbacksAndMessages(null) + paymentState = UNKNOWN latestDefaultValuesRequest = null - captureStartTimestamp = 0L - capturePassedTimestamp = 0L + latestWillSubmitParametersEvent = null _completion.update { Awaiting } _state.update { Idle } } @@ -1061,7 +1075,6 @@ internal class NativeAlternativePaymentInteractor( uuid = uuid, paymentMethod = paymentMethod, invoice = invoice, - redirect = redirect, stepper = null, elements = elements, primaryActionId = ActionId.CONFIRM_PAYMENT, @@ -1078,66 +1091,40 @@ internal class NativeAlternativePaymentInteractor( } private fun capture() { - if (captureStartTimestamp != 0L) { + if (isCapturePolling) { return } + isCapturePolling = true updateStepper(activeStepIndex = 1) - captureStartTimestamp = System.currentTimeMillis() interactorScope.launch { - val iterator = captureRetryStrategy.iterator - while (capturePassedTimestamp <= configuration.paymentConfirmation.timeoutSeconds * 1000) { - val result = when (val flow = configuration.flow) { - is Authorization -> invoicesService.authorize( - request = PONativeAlternativePaymentAuthorizationRequest( - invoiceId = flow.invoiceId, - gatewayConfigurationId = flow.gatewayConfigurationId, - configuration = flow.configuration - ) - ).map() - is Tokenization -> customerTokensService.tokenize( - request = PONativeAlternativePaymentTokenizationRequest( - customerId = flow.customerId, - customerTokenId = flow.customerTokenId, - gatewayConfigurationId = flow.gatewayConfigurationId, - configuration = flow.configuration - ) - ).map() - } - POLogger.debug("Attempted to confirm the payment.") - if (isCaptureRetryable(result)) { - delay(iterator.next()) - capturePassedTimestamp = System.currentTimeMillis() - captureStartTimestamp - } else { - captureStartTimestamp = 0L - capturePassedTimestamp = 0L - result.onSuccess { + try { + capturePoller.poll() + .onSuccess { response -> + val elements = response.elements?.map() _state.whenPending { stateValue -> handleSuccess( stateValue.copy( uuid = UUID.randomUUID().toString(), - elements = it.elements + elements = elements ) ) } }.onFailure { failure -> _completion.update { Failure(failure) } } - return@launch - } - } - captureStartTimestamp = 0L - capturePassedTimestamp = 0L - _completion.update { - Failure( - ProcessOutResult.Failure( - code = Timeout(), - message = "Payment confirmation timed out." - ) - ) + } finally { + isCapturePolling = false } } } + override fun onStart(owner: LifecycleOwner) { + if (isCapturePolling) { + POLogger.debug("App returned to foreground: resetting capture polling backoff.") + capturePoller.resetBackoff() + } + } + private fun updateStepper(activeStepIndex: Int) { _state.whenPending { stateValue -> _state.update { @@ -1167,48 +1154,6 @@ internal class NativeAlternativePaymentInteractor( } } - @JvmName(name = "mapFromAuthorizationResult") - private suspend fun ProcessOutResult.map() = - fold( - onSuccess = { - ProcessOutResult.Success( - ProcessingResponse( - state = it.state, - elements = it.elements?.map() - ) - ) - }, - onFailure = { it } - ) - - @JvmName(name = "mapFromTokenizationResult") - private suspend fun ProcessOutResult.map() = - fold( - onSuccess = { - ProcessOutResult.Success( - ProcessingResponse( - state = it.state, - elements = it.elements?.map() - ) - ) - }, - onFailure = { it } - ) - - private fun isCaptureRetryable( - result: ProcessOutResult - ): Boolean = result.fold( - onSuccess = { it.state != SUCCESS }, - onFailure = { - val retryableCodes = listOf( - NetworkUnreachable, - Timeout(), - Internal() - ) - retryableCodes.contains(it.code) - } - ) - private fun handleSuccess(stateValue: PendingStateValue) { POLogger.info("Success: payment completed.") dispatch(DidCompletePayment) @@ -1467,11 +1412,7 @@ internal class NativeAlternativePaymentInteractor( } override fun clear() { + ProcessLifecycleOwner.get().lifecycle.removeObserver(this) handler.removeCallbacksAndMessages(null) } - - private data class ProcessingResponse( - val state: PONativeAlternativePaymentState, - val elements: List? - ) } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractorState.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractorState.kt index 86514c14a..f3e0e8d63 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractorState.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractorState.kt @@ -63,7 +63,6 @@ internal sealed interface NativeAlternativePaymentInteractorState { val uuid: String, val paymentMethod: PONativeAlternativePaymentMethodDetails, val invoice: Invoice?, - val redirect: Redirect?, val stepper: Stepper?, val elements: List?, val primaryActionId: String?, diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt index 71e900ff0..a2de55e3e 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt @@ -16,7 +16,6 @@ import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentA import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentElement.Form.Parameter import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentElement.Form.Parameter.* import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentElement.Form.Parameter.Otp.Subtype -import com.processout.sdk.core.retry.PORetryStrategy.Exponential import com.processout.sdk.ui.core.state.* import com.processout.sdk.ui.core.state.POActionState.Confirmation import com.processout.sdk.ui.core.transformation.POPhoneNumberVisualTransformation @@ -61,14 +60,7 @@ internal class NativeAlternativePaymentViewModel private constructor( invoicesService = ProcessOut.instance.invoices, customerTokensService = ProcessOut.instance.customerTokens, barcodeBitmapProvider = BarcodeBitmapProvider(), - mediaStorageProvider = MediaStorageProvider(app), - captureRetryStrategy = Exponential( - maxRetries = Int.MAX_VALUE, - initialDelay = 150, - minDelay = 3 * 1000, - maxDelay = 90 * 1000, - factor = 1.45 - ) + mediaStorageProvider = MediaStorageProvider(app) ) ) as T }