diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt index ecdc839b23..db011f878c 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt @@ -23,7 +23,9 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import timber.log.Timber import javax.inject.Inject +import javax.inject.Singleton +@Singleton internal class TangemPayRequestPerformer @Inject constructor( @NetworkMoshi moshi: Moshi, private val environmentConfigStorage: EnvironmentConfigStorage, @@ -33,10 +35,7 @@ internal class TangemPayRequestPerformer @Inject constructor( ) { private val customerWalletAddress = MutableStateFlow(null) - - private val refreshTokensMutex = Mutex() - private var refreshTokensJob: Deferred? = null - + private val tokensMutex = Mutex() private val errorConverter = TangemPayErrorConverter(moshi) @Deprecated("Do not use this method") @@ -100,20 +99,9 @@ internal class TangemPayRequestPerformer @Inject constructor( ): Either = withContext(dispatchers.io) { catch( block = { - val tokens = getAccessTokens(userWalletId) - val now = Instant.now() - val accessExpiresAt = Instant.ofEpochSecond(tokens.expiresAt) - val refreshExpiresAt = Instant.ofEpochSecond(tokens.refreshExpiresAt) - val apiResponse: ApiResponse = if (accessExpiresAt.isAfter(now)) { - requestBlock(tokens.getAuthHeader()) - } else if (accessExpiresAt.isBefore(now) && refreshExpiresAt.isAfter(now)) { - val newTokens = refreshOrJoin(refreshTokens = { refreshAuthTokens(userWalletId) }) - requestBlock(newTokens.getAuthHeader()) - } else { - return@catch VisaApiError.RefreshTokenExpired.left() - } + val tokens = getAccessTokens(userWalletId) ?: return@catch VisaApiError.RefreshTokenExpired.left() - when (apiResponse) { + when (val apiResponse: ApiResponse = requestBlock(tokens.getAuthHeader())) { is ApiResponse.Error -> errorConverter.convert(apiResponse.cause).left() is ApiResponse.Success -> apiResponse.data.right() } @@ -122,29 +110,6 @@ internal class TangemPayRequestPerformer @Inject constructor( ).onLeft { visaApiError -> Timber.tag("TangemPayRequestPerformer").e(visaApiError.toString()) } } - private suspend fun refreshOrJoin(refreshTokens: suspend () -> TangemPayAuthTokens): TangemPayAuthTokens { - val jobToAwait: Deferred = refreshTokensMutex.withLock { - val current = refreshTokensJob - if (current == null || current.isCompleted) { - coroutineScope { - async { refreshTokens() }.also { refreshTokensJob = it } - } - } else { - current - } - } - val result = try { - jobToAwait.await() - } finally { - refreshTokensMutex.withLock { - if (refreshTokensJob === jobToAwait && jobToAwait.isCompleted) { - refreshTokensJob = null - } - } - } - return result - } - suspend fun getCustomerWalletAddress(userWalletId: UserWalletId): String { val existingAddress = customerWalletAddress.value if (existingAddress != null) { @@ -158,15 +123,27 @@ internal class TangemPayRequestPerformer @Inject constructor( return storedAddress } - private suspend fun getAccessTokens(userWalletId: UserWalletId): TangemPayAuthTokens { - val walletAddress = getCustomerWalletAddress(userWalletId) - val tokens = tangemPayStorage.getAuthTokens(walletAddress) ?: error("Auth tokens are not stored") - return tokens + private suspend fun getAccessTokens(userWalletId: UserWalletId): TangemPayAuthTokens? { + return tokensMutex.withLock { + val walletAddress = getCustomerWalletAddress(userWalletId) + val tokens = tangemPayStorage.getAuthTokens(walletAddress) ?: error("Auth tokens are not stored") + val now = Instant.now() + val accessExpiresAt = Instant.ofEpochSecond(tokens.expiresAt) + val refreshExpiresAt = Instant.ofEpochSecond(tokens.refreshExpiresAt) + + if (accessExpiresAt.isAfter(now)) { + tokens + } else if (accessExpiresAt.isBefore(now) && refreshExpiresAt.isAfter(now)) { + val newTokens = refreshAuthTokens(userWalletId = userWalletId, refreshToken = tokens.refreshToken) + newTokens + } else { + null + } + } } - private suspend fun refreshAuthTokens(userWalletId: UserWalletId): TangemPayAuthTokens { + private suspend fun refreshAuthTokens(userWalletId: UserWalletId, refreshToken: String): TangemPayAuthTokens { val customerWalletAddress = getCustomerWalletAddress(userWalletId) - val refreshToken = getAccessTokens(userWalletId).refreshToken val tokens = authDataSource.refreshAuthTokens(refreshToken) .fold( ifLeft = { error -> error("Cannot refresh tokens: ${error.message}") },