Updated on 2026-08-14

This commit is contained in:
Tangem 2025-01-14 13:45:57 +03:00
parent 113c9cb46e
commit 4192ccab33
34 changed files with 444 additions and 81 deletions

View file

@ -25,6 +25,7 @@ sealed class ApiConfig {
TangemTech,
StakeKit,
TangemVisaAuth,
TangemVisa,
}
private fun initializeId(): ID {
@ -33,6 +34,7 @@ sealed class ApiConfig {
is TangemTech -> ID.TangemTech
is StakeKit -> ID.StakeKit
is TangemVisaAuth -> ID.TangemVisaAuth
is TangemVisa -> ID.TangemVisa
}
}

View file

@ -0,0 +1,30 @@
package com.tangem.datasource.api.common.config
import com.tangem.datasource.api.common.visa.TangemVisaAuthProvider
import com.tangem.utils.Provider
import kotlinx.coroutines.runBlocking
internal class TangemVisa(
private val authProvider: TangemVisaAuthProvider,
) : ApiConfig() {
override val defaultEnvironment: ApiEnvironment = ApiEnvironment.PROD
override val environmentConfigs = listOf(
createProdEnvironment(),
)
private fun createProdEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig(
environment = ApiEnvironment.PROD,
baseUrl = "https://bff.tangem.com/",
headers = createHeaders(),
)
private fun createHeaders() = mapOf(
"Authorization" to Provider {
// This is safe because it's used by interceptor which runs on the IO thread
// (maybe change to ProviderSuspend implementation)
runBlocking { authProvider.getAuthHeader() }
},
)
}

View file

@ -0,0 +1,6 @@
package com.tangem.datasource.api.common.visa
interface TangemVisaAuthProvider {
suspend fun getAuthHeader(): String
}

View file

@ -1,5 +1,10 @@
package com.tangem.datasource.api.visa
import com.tangem.datasource.api.visa.models.response.CardActivationRemoteStateResponse
import retrofit2.http.GET
interface TangemVisaApi {
// TODO
@GET("activation-status")
suspend fun getRemoteActivationStatus(): CardActivationRemoteStateResponse
}

View file

@ -0,0 +1,9 @@
package com.tangem.datasource.api.visa.models.response
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class CardActivationRemoteStateResponse(
@Json(name = "state") val state: String,
)

View file

@ -5,6 +5,7 @@ import com.tangem.datasource.api.common.config.*
import com.tangem.datasource.api.common.config.Express
import com.tangem.datasource.api.common.config.StakeKit
import com.tangem.datasource.api.common.config.TangemTech
import com.tangem.datasource.api.common.visa.TangemVisaAuthProvider
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
import com.tangem.lib.auth.ExpressAuthProvider
import com.tangem.lib.auth.StakeKitAuthProvider
@ -42,5 +43,10 @@ internal object ApiConfigsModule {
@Provides
@IntoSet
fun provideTangemVisaConfig(): ApiConfig = TangemVisaAuth()
fun provideTangemAuthVisaConfig(): ApiConfig = TangemVisaAuth()
@Provides
@IntoSet
fun provideTangemVisaConfig(tangemVisaAuthProvider: TangemVisaAuthProvider): ApiConfig =
TangemVisa(tangemVisaAuthProvider)
}

View file

@ -15,6 +15,7 @@ import com.tangem.datasource.api.onramp.OnrampApi
import com.tangem.datasource.api.stakekit.StakeKitApi
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.TangemTechApiV2
import com.tangem.datasource.api.visa.TangemVisaApi
import com.tangem.datasource.api.visa.TangemVisaAuthApi
import com.tangem.datasource.local.logs.AppLogsStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
@ -172,16 +173,43 @@ internal object NetworkModule {
@Provides
@Singleton
fun provideTangemVisaApi(
fun provideTangemVisaAuthApi(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
apiConfigsManager: ApiConfigsManager,
appLogsStore: AppLogsStore,
): TangemVisaAuthApi {
return createApi<TangemVisaAuthApi>(
id = ApiConfig.ID.TangemVisaAuth,
moshi = moshi,
context = context,
apiConfigsManager = apiConfigsManager,
clientBuilder = {
addInterceptor(
NetworkLogsSaveInterceptor(appLogsStore),
)
},
)
}
@Provides
@Singleton
fun provideTangemVisaApi(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
apiConfigsManager: ApiConfigsManager,
appLogsStore: AppLogsStore,
): TangemVisaApi {
return createApi<TangemVisaApi>(
id = ApiConfig.ID.TangemVisa,
moshi = moshi,
context = context,
apiConfigsManager = apiConfigsManager,
clientBuilder = {
addInterceptor(
NetworkLogsSaveInterceptor(appLogsStore),
)
},
)
}

View file

@ -0,0 +1,10 @@
package com.tangem.datasource.local.visa
import com.tangem.domain.visa.model.VisaAuthTokens
interface VisaAuthTokenStorage {
suspend fun store(tokens: VisaAuthTokens)
suspend fun get(): VisaAuthTokens?
}

View file

@ -10,12 +10,14 @@ import com.tangem.datasource.api.common.config.ApiConfig.Companion.EXTERNAL_BUIL
import com.tangem.datasource.api.common.config.ApiConfig.Companion.INTERNAL_BUILD_TYPE
import com.tangem.datasource.api.common.config.ApiConfig.Companion.MOCKED_BUILD_TYPE
import com.tangem.datasource.api.common.config.ApiConfig.Companion.RELEASE_BUILD_TYPE
import com.tangem.datasource.api.common.visa.TangemVisaAuthProvider
import com.tangem.lib.auth.ExpressAuthProvider
import com.tangem.lib.auth.StakeKitAuthProvider
import com.tangem.utils.Provider
import com.tangem.utils.version.AppVersionProvider
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@ -28,6 +30,7 @@ private val appVersionProvider = mockk<AppVersionProvider>()
private val expressAuthProvider = mockk<ExpressAuthProvider>()
private val stakeKitAuthProvider = mockk<StakeKitAuthProvider>()
private val appAuthProvider = mockk<AuthProvider>()
private val visaAuthProvider = mockk<TangemVisaAuthProvider>()
// Don't forget to add new config !!!
private val API_CONFIGS = setOf(
@ -53,6 +56,7 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
every { stakeKitAuthProvider.getApiKey() } returns STAKE_KIT_API_KEY
every { appAuthProvider.getCardId() } returns APP_CARD_ID
every { appAuthProvider.getCardPublicKey() } returns APP_CARD_PUBLIC_KEY
every { runBlocking { visaAuthProvider.getAuthHeader() } } returns VISA_AUTH_HEADER // TODO
}
@Test
@ -77,6 +81,7 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
const val STAKE_KIT_API_KEY = "stake_kit_api_key"
const val APP_CARD_ID = "app_card_id"
const val APP_CARD_PUBLIC_KEY = "app_public_key"
const val VISA_AUTH_HEADER = "Bearer visa_auth_header"
@JvmStatic
@Parameterized.Parameters
@ -86,6 +91,7 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
is TangemTech -> createTangemTechModel()
is StakeKit -> createStakeKitModel()
is TangemVisaAuth -> createVisaAuthModel()
is TangemVisa -> createVisaModel()
}
}
@ -183,6 +189,19 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
)
}
private fun createVisaModel(): Model {
return Model(
id = ApiConfig.ID.TangemVisa,
expected = ApiEnvironmentConfig(
environment = ApiEnvironment.PROD,
baseUrl = "https://bff.tangem.com/",
headers = mapOf(
"Authorization" to Provider { VISA_AUTH_HEADER },
),
),
)
}
private fun String.checkHeaderValueOrEmpty(): String {
for (i in this.indices) {
val c = this[i]