Skip to content

Commit

Permalink
Add exponential back off for api verification of purchases
Browse files Browse the repository at this point in the history
  • Loading branch information
Pururun committed Dec 6, 2023
1 parent bc20099 commit fdfd71b
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package net.mullvad.mullvadvpn.constant

const val VERIFICATION_MAX_ATTEMPTS = 4
const val VERIFICATION_INITIAL_BACK_OFF_MILLISECONDS = 3000L
const val VERIFICATION_BACK_OFF_FACTOR = 3L
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import android.app.Activity
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import net.mullvad.mullvadvpn.constant.VERIFICATION_BACK_OFF_FACTOR
import net.mullvad.mullvadvpn.constant.VERIFICATION_INITIAL_BACK_OFF_MILLISECONDS
import net.mullvad.mullvadvpn.constant.VERIFICATION_MAX_ATTEMPTS
import net.mullvad.mullvadvpn.lib.payment.PaymentRepository
import net.mullvad.mullvadvpn.lib.payment.model.PaymentAvailability
import net.mullvad.mullvadvpn.lib.payment.model.ProductId
import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult
import net.mullvad.mullvadvpn.lib.payment.model.VerificationResult
import net.mullvad.mullvadvpn.util.retryWithExponentialBackOff

interface PaymentUseCase {
val paymentAvailability: Flow<PaymentAvailability?>
Expand Down Expand Up @@ -43,13 +47,22 @@ class PlayPaymentUseCase(private val paymentRepository: PaymentRepository) : Pay
}

override suspend fun verifyPurchases(onSuccessfulVerification: () -> Unit) {
paymentRepository.verifyPurchases().collect {
if (it == VerificationResult.Success) {
// Update the payment availability after a successful verification.
queryPaymentAvailability()
onSuccessfulVerification()
paymentRepository
.verifyPurchases()
.retryWithExponentialBackOff(
maxAttempts = VERIFICATION_MAX_ATTEMPTS,
initialBackOffDelay = VERIFICATION_INITIAL_BACK_OFF_MILLISECONDS,
backOffDelayFactor = VERIFICATION_BACK_OFF_FACTOR
) {
it is VerificationResult.Error
}
.collect {
if (it == VerificationResult.Success) {
// Update the payment availability after a successful verification.
queryPaymentAvailability()
onSuccessfulVerification()
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.retryWhen
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.withTimeoutOrNull
import net.mullvad.mullvadvpn.lib.common.util.safeOffer
Expand Down Expand Up @@ -129,3 +133,34 @@ inline fun <T1, T2, T3, T4, T5, T6, T7, T8, R> combine(

suspend inline fun <T> Deferred<T>.awaitWithTimeoutOrNull(timeout: Long) =
withTimeoutOrNull(timeout) { await() }

@Suppress("UNCHECKED_CAST")
suspend inline fun <T> Flow<T>.retryWithExponentialBackOff(
maxAttempts: Int,
initialBackOffDelay: Long,
backOffDelayFactor: Long,
crossinline predicate: (T) -> Boolean,
): Flow<T> =
map {
if (predicate(it)) {
throw ExceptionWrapper(it as Any)
}
it
}
.retryWhen { _, attempt ->
if (attempt >= maxAttempts) {
return@retryWhen false
}
val backOffDelay = initialBackOffDelay * backOffDelayFactor.pow(attempt.toInt())
delay(backOffDelay)
true
}
.catch {
if (it is ExceptionWrapper) {
this.emit(it.item as T)
} else {
throw it
}
}

class ExceptionWrapper(val item: Any) : Throwable()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package net.mullvad.mullvadvpn.util

import kotlin.math.pow

fun Long.pow(exponent: Int): Long = toDouble().pow(exponent).toLong()

0 comments on commit fdfd71b

Please sign in to comment.