Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-22 11:55:20 +04:00
parent a96fb4b2f4
commit f43fb6a8ac
23 changed files with 122 additions and 177 deletions

View file

@ -6,20 +6,17 @@ import com.tangem.datasource.utils.RequestHeader
import com.tangem.lib.auth.ExpressAuthProvider
import com.tangem.utils.ProviderSuspend
import com.tangem.utils.info.AppInfoProvider
import com.tangem.utils.version.AppVersionProvider
/**
* Express [ApiConfig]
*
* @property environmentConfig environment config
* @property expressAuthProvider express auth provider
* @property appVersionProvider app version provider
* @property appInfoProvider app info provider
*/
internal class Express(
private val environmentConfig: EnvironmentConfig,
private val expressAuthProvider: ExpressAuthProvider,
private val appVersionProvider: AppVersionProvider,
private val appInfoProvider: AppInfoProvider,
) : ApiConfig() {
@ -102,7 +99,7 @@ internal class Express(
private fun createHeaders(isProd: Boolean) = buildMap {
put(key = "api-key", value = ProviderSuspend { getApiKey(isProd) })
put(key = "session-id", value = ProviderSuspend(expressAuthProvider::getSessionId))
putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values)
putAll(from = RequestHeader.AppVersionPlatformHeaders(appInfoProvider).values)
}
private fun getApiKey(isProd: Boolean): String {

View file

@ -6,14 +6,12 @@ import com.tangem.datasource.utils.RequestHeader
import com.tangem.utils.Provider
import com.tangem.utils.ProviderSuspend
import com.tangem.utils.info.AppInfoProvider
import com.tangem.utils.version.AppVersionProvider
/**
* Gasless transactions [ApiConfig]
*/
internal class GaslessTxService(
private val authProvider: AuthProvider,
private val appVersionProvider: AppVersionProvider,
private val appInfoProvider: AppInfoProvider,
) : ApiConfig() {
@ -50,7 +48,7 @@ internal class GaslessTxService(
)
private fun createHeaders(environment: ApiEnvironment) = buildMap {
putAll(RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values)
putAll(RequestHeader.AppVersionPlatformHeaders(appInfoProvider).values)
put(
key = "Authorization",
value = ProviderSuspend {

View file

@ -5,14 +5,12 @@ import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.utils.RequestHeader
import com.tangem.utils.Provider
import com.tangem.utils.info.AppInfoProvider
import com.tangem.utils.version.AppVersionProvider
/**
* News [ApiConfig]
[REDACTED_AUTHOR]
*/
internal class News(
private val appVersionProvider: AppVersionProvider,
private val appInfoProvider: AppInfoProvider,
private val authProvider: AuthProvider,
) : ApiConfig() {
@ -64,7 +62,7 @@ internal class News(
apiEnvironment = Provider { environment },
).values,
)
putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values)
putAll(from = RequestHeader.AppVersionPlatformHeaders(appInfoProvider).values)
}
private companion object {

View file

@ -3,11 +3,11 @@ package com.tangem.datasource.api.common.config
import com.tangem.datasource.BuildConfig
import com.tangem.datasource.local.config.environment.EnvironmentConfig
import com.tangem.utils.ProviderSuspend
import com.tangem.utils.version.AppVersionProvider
import com.tangem.utils.info.AppInfoProvider
internal sealed class TangemPay(
private val environmentConfig: EnvironmentConfig,
private val appVersionProvider: AppVersionProvider,
private val appInfoProvider: AppInfoProvider,
) : ApiConfig() {
override val defaultEnvironment: ApiEnvironment = getInitialEnvironment()
@ -52,7 +52,7 @@ internal sealed class TangemPay(
)
private fun createHeaders(apiEnvironment: ApiEnvironment) = mapOf(
"version" to ProviderSuspend { appVersionProvider.versionName },
"version" to ProviderSuspend { appInfoProvider.appVersion },
"platform" to ProviderSuspend { "Android" },
"X-API-KEY" to ProviderSuspend { getBffStaticToken(apiEnvironment) },
)
@ -74,8 +74,8 @@ internal sealed class TangemPay(
class Bff(
environmentConfig: EnvironmentConfig,
appVersionProvider: AppVersionProvider,
) : TangemPay(environmentConfig, appVersionProvider) {
appInfoProvider: AppInfoProvider,
) : TangemPay(environmentConfig, appInfoProvider) {
override fun getBaseUrl(apiEnvironment: ApiEnvironment): String {
return when (apiEnvironment) {
ApiEnvironment.DEV -> "https://api.dev.us.paera.com/bff-v2/"
@ -93,8 +93,8 @@ internal sealed class TangemPay(
class Auth(
environmentConfig: EnvironmentConfig,
appVersionProvider: AppVersionProvider,
) : TangemPay(environmentConfig, appVersionProvider) {
appInfoProvider: AppInfoProvider,
) : TangemPay(environmentConfig, appInfoProvider) {
override fun getBaseUrl(apiEnvironment: ApiEnvironment): String {
return when (apiEnvironment) {
ApiEnvironment.DEV -> "https://api.dev.us.paera.com/"

View file

@ -5,11 +5,9 @@ import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.utils.RequestHeader
import com.tangem.utils.Provider
import com.tangem.utils.info.AppInfoProvider
import com.tangem.utils.version.AppVersionProvider
/** TangemTech [ApiConfig] */
internal class TangemTech(
private val appVersionProvider: AppVersionProvider,
private val authProvider: AuthProvider,
private val appInfoProvider: AppInfoProvider,
) : ApiConfig() {
@ -62,7 +60,7 @@ internal class TangemTech(
private fun createHeaders(apiEnvironment: ApiEnvironment) = buildMap {
putAll(from = RequestHeader.TangemApiKeyHeader(authProvider, Provider { apiEnvironment }).values)
putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values)
putAll(from = RequestHeader.AppVersionPlatformHeaders(appInfoProvider).values)
putAll(from = RequestHeader.AuthenticationHeader(authProvider).values)
}
}

View file

@ -6,12 +6,10 @@ import com.tangem.datasource.local.config.environment.EnvironmentConfig
import com.tangem.datasource.utils.RequestHeader
import com.tangem.utils.ProviderSuspend
import com.tangem.utils.info.AppInfoProvider
import com.tangem.utils.version.AppVersionProvider
/** YieldSupply [ApiConfig] */
internal class YieldSupply(
private val environmentConfig: EnvironmentConfig,
private val appVersionProvider: AppVersionProvider,
private val authProvider: AuthProvider,
private val appInfoProvider: AppInfoProvider,
) : ApiConfig() {
@ -66,7 +64,7 @@ internal class YieldSupply(
put(key = "api-key", value = ProviderSuspend {
getApiKey(apiEnvironment)
})
putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values)
putAll(from = RequestHeader.AppVersionPlatformHeaders(appInfoProvider).values)
putAll(from = RequestHeader.AuthenticationHeader(authProvider).values)
}

View file

@ -7,7 +7,6 @@ import com.tangem.lib.auth.ExpressAuthProvider
import com.tangem.lib.auth.P2PEthPoolAuthProvider
import com.tangem.lib.auth.StakeKitAuthProvider
import com.tangem.utils.info.AppInfoProvider
import com.tangem.utils.version.AppVersionProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -23,13 +22,11 @@ internal object ApiConfigsModule {
fun provideExpressConfig(
environmentConfig: EnvironmentConfig,
expressAuthProvider: ExpressAuthProvider,
appVersionProvider: AppVersionProvider,
appInfoProvider: AppInfoProvider,
): ApiConfig {
return Express(
environmentConfig = environmentConfig,
expressAuthProvider = expressAuthProvider,
appVersionProvider = appVersionProvider,
appInfoProvider = appInfoProvider,
)
}
@ -48,55 +45,47 @@ internal object ApiConfigsModule {
@Provides
@IntoSet
fun provideTangemTechConfig(
appVersionProvider: AppVersionProvider,
authProvider: AuthProvider,
appInfoProvider: AppInfoProvider,
): ApiConfig = TangemTech(
appVersionProvider = appVersionProvider,
authProvider = authProvider,
appInfoProvider = appInfoProvider,
)
fun provideTangemTechConfig(authProvider: AuthProvider, appInfoProvider: AppInfoProvider): ApiConfig {
return TangemTech(
authProvider = authProvider,
appInfoProvider = appInfoProvider,
)
}
@Provides
@IntoSet
fun provideNewsConfig(
appVersionProvider: AppVersionProvider,
authProvider: AuthProvider,
appInfoProvider: AppInfoProvider,
): ApiConfig = News(
appVersionProvider = appVersionProvider,
appInfoProvider = appInfoProvider,
authProvider = authProvider,
)
fun provideNewsConfig(authProvider: AuthProvider, appInfoProvider: AppInfoProvider): ApiConfig {
return News(
appInfoProvider = appInfoProvider,
authProvider = authProvider,
)
}
@Provides
@IntoSet
fun provideYieldSupplyConfig(
environmentConfig: EnvironmentConfig,
appVersionProvider: AppVersionProvider,
authProvider: AuthProvider,
appInfoProvider: AppInfoProvider,
): ApiConfig = YieldSupply(
environmentConfig = environmentConfig,
appVersionProvider = appVersionProvider,
authProvider = authProvider,
appInfoProvider = appInfoProvider,
)
): ApiConfig {
return YieldSupply(
environmentConfig = environmentConfig,
authProvider = authProvider,
appInfoProvider = appInfoProvider,
)
}
@Provides
@IntoSet
fun provideTangemPayBffConfig(
environmentConfig: EnvironmentConfig,
appVersionProvider: AppVersionProvider,
): ApiConfig = TangemPay.Bff(environmentConfig, appVersionProvider)
fun provideTangemPayBffConfig(environmentConfig: EnvironmentConfig, appInfoProvider: AppInfoProvider): ApiConfig {
return TangemPay.Bff(environmentConfig, appInfoProvider)
}
@Provides
@IntoSet
fun provideTangemPayAuthConfig(
environmentConfig: EnvironmentConfig,
appVersionProvider: AppVersionProvider,
): ApiConfig = TangemPay.Auth(environmentConfig, appVersionProvider)
fun provideTangemPayAuthConfig(environmentConfig: EnvironmentConfig, appInfoProvider: AppInfoProvider): ApiConfig {
return TangemPay.Auth(environmentConfig, appInfoProvider)
}
@Provides
@IntoSet
@ -112,14 +101,9 @@ internal object ApiConfigsModule {
@Provides
@IntoSet
fun provideGaslessServiceConfig(
appVersionProvider: AppVersionProvider,
authProvider: AuthProvider,
appInfoProvider: AppInfoProvider,
): ApiConfig {
fun provideGaslessServiceConfig(authProvider: AuthProvider, appInfoProvider: AppInfoProvider): ApiConfig {
return GaslessTxService(
authProvider = authProvider,
appVersionProvider = appVersionProvider,
appInfoProvider = appInfoProvider,
)
}

View file

@ -1,21 +0,0 @@
package com.tangem.datasource.di
import com.tangem.datasource.info.AndroidAppInfoProvider
import com.tangem.utils.info.AppInfoProvider
import com.tangem.utils.version.AppVersionProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object AppInfoModule {
@Singleton
@Provides
fun provideAppInfoProvider(appVersionProvider: AppVersionProvider): AppInfoProvider {
return AndroidAppInfoProvider(appVersionProvider)
}
}

View file

@ -1,27 +0,0 @@
package com.tangem.datasource.info
import android.os.Build
import com.tangem.utils.info.AppInfoProvider
import com.tangem.utils.version.AppVersionProvider
import java.util.*
import javax.inject.Inject
internal class AndroidAppInfoProvider @Inject constructor(
private val appVersionProvider: AppVersionProvider,
) : AppInfoProvider {
override val platform: String
get() = "Android"
override val device: String
get() = "${Build.MANUFACTURER} ${Build.MODEL}"
override val osVersion: String
get() = Build.VERSION.RELEASE
override val language: String
get() = Locale.getDefault().language
override val timezone: String
get() = TimeZone.getDefault().id
override val appVersion: String
get() = appVersionProvider.versionName
override val isHuaweiDevice: Boolean
get() = Build.MANUFACTURER.equals("HUAWEI", ignoreCase = true) ||
Build.BRAND.equals("HUAWEI", ignoreCase = true)
}

View file

@ -1,14 +1,11 @@
package com.tangem.datasource.utils
import android.os.Build
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.api.common.config.ApiEnvironment
import com.tangem.datasource.utils.RequestHeader.CacheControlHeader.checkHeaderValueOrEmpty
import com.tangem.utils.Provider
import com.tangem.utils.ProviderSuspend
import com.tangem.utils.info.AppInfoProvider
import com.tangem.utils.version.AppVersionProvider
import java.util.Locale
import java.util.TimeZone
/**
@ -29,17 +26,16 @@ sealed class RequestHeader(vararg pairs: Pair<String, ProviderSuspend<String>>)
)
class AppVersionPlatformHeaders(
appVersionProvider: AppVersionProvider,
appInfoProvider: AppInfoProvider,
) : RequestHeader(
"system_version" to ProviderSuspend { appInfoProvider.osVersion },
"version" to ProviderSuspend { appVersionProvider.versionName },
"version" to ProviderSuspend { appInfoProvider.appVersion },
"platform" to ProviderSuspend { "android" },
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
"language" to ProviderSuspend { appInfoProvider.language.checkHeaderValueOrEmpty() },
"timezone" to ProviderSuspend {
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
},
"device" to ProviderSuspend { "${Build.MANUFACTURER} ${Build.MODEL}".checkHeaderValueOrEmpty() },
"device" to ProviderSuspend { appInfoProvider.device.checkHeaderValueOrEmpty() },
)
/**

View file

@ -51,21 +51,18 @@ class ApiConfigTest {
Express(
environmentConfig = environmentConfig,
expressAuthProvider = mockk(),
appVersionProvider = mockk(),
appInfoProvider = mockk(),
)
}
ApiConfig.ID.YieldSupply -> {
YieldSupply(
environmentConfig = environmentConfig,
appVersionProvider = mockk(),
authProvider = appAuthProvider,
appInfoProvider = mockk(),
)
}
ApiConfig.ID.TangemTech -> {
TangemTech(
appVersionProvider = mockk(),
authProvider = appAuthProvider,
appInfoProvider = mockk(),
)
@ -73,23 +70,21 @@ class ApiConfigTest {
ApiConfig.ID.StakeKit -> StakeKit(stakeKitAuthProvider = mockk())
ApiConfig.ID.TangemPay -> TangemPay.Bff(
environmentConfig = environmentConfig,
appVersionProvider = mockk(),
appInfoProvider = mockk(),
)
ApiConfig.ID.TangemPayAuth -> TangemPay.Auth(
environmentConfig = environmentConfig,
appVersionProvider = mockk(),
appInfoProvider = mockk(),
)
ApiConfig.ID.BlockAid -> BlockAid(environmentConfig = environmentConfig)
ApiConfig.ID.MoonPay -> MoonPay()
ApiConfig.ID.P2PEthPool -> P2PEthPool(p2pAuthProvider = mockk())
ApiConfig.ID.News -> News(
appVersionProvider = mockk(),
authProvider = appAuthProvider,
appInfoProvider = mockk(),
)
ApiConfig.ID.GaslessTxService -> GaslessTxService(
authProvider = appAuthProvider,
appVersionProvider = mockk(),
appInfoProvider = mockk(),
)
}

View file

@ -19,7 +19,6 @@ import com.tangem.lib.auth.StakeKitAuthProvider
import com.tangem.test.core.ProvideTestModels
import com.tangem.utils.ProviderSuspend
import com.tangem.utils.info.AppInfoProvider
import com.tangem.utils.version.AppVersionProvider
import io.mockk.clearMocks
import io.mockk.coEvery
import io.mockk.every
@ -38,7 +37,6 @@ import java.util.TimeZone
internal class ProdApiConfigsManagerTest {
private val environmentConfig = createMockEnvironmentConfig()
private val appVersionProvider = mockk<AppVersionProvider>()
private val expressAuthProvider = mockk<ExpressAuthProvider>()
private val stakeKitAuthProvider = mockk<StakeKitAuthProvider>()
private val p2pEthPoolAuthProvider = mockk<P2PEthPoolAuthProvider>()
@ -52,14 +50,13 @@ internal class ProdApiConfigsManagerTest {
@BeforeEach
fun setup() {
clearMocks(
appVersionProvider,
expressAuthProvider,
stakeKitAuthProvider,
appAuthProvider,
appInfoProvider,
)
every { appVersionProvider.versionName } returns VERSION_NAME
every { appInfoProvider.appVersion } returns VERSION_NAME
every { expressAuthProvider.getSessionId() } returns EXPRESS_SESSION_ID
every { stakeKitAuthProvider.getApiKey() } returns STAKE_KIT_API_KEY
every { p2pEthPoolAuthProvider.getApiKey() } returns P2P_API_KEY
@ -71,6 +68,8 @@ internal class ProdApiConfigsManagerTest {
coEvery { appAuthProvider.getCardPublicKey() } returns APP_CARD_PUBLIC_KEY
every { appInfoProvider.osVersion } returns "Android 16"
every { appInfoProvider.language } returns Locale.getDefault().toLanguageTag()
every { appInfoProvider.device } returns "${Build.MANUFACTURER} ${Build.MODEL}"
manager = ProdApiConfigsManager(apiConfigs = createApiConfigs())
}
@ -94,21 +93,18 @@ internal class ProdApiConfigsManagerTest {
Express(
environmentConfig = environmentConfig,
expressAuthProvider = expressAuthProvider,
appVersionProvider = appVersionProvider,
appInfoProvider = appInfoProvider,
)
}
ApiConfig.ID.YieldSupply -> {
YieldSupply(
environmentConfig = environmentConfig,
appVersionProvider = appVersionProvider,
authProvider = appAuthProvider,
appInfoProvider = appInfoProvider,
)
}
ApiConfig.ID.TangemTech -> {
TangemTech(
appVersionProvider = appVersionProvider,
authProvider = appAuthProvider,
appInfoProvider = appInfoProvider,
)
@ -116,23 +112,21 @@ internal class ProdApiConfigsManagerTest {
ApiConfig.ID.StakeKit -> StakeKit(stakeKitAuthProvider = stakeKitAuthProvider)
ApiConfig.ID.TangemPay -> TangemPay.Bff(
environmentConfig = environmentConfig,
appVersionProvider = appVersionProvider,
appInfoProvider = appInfoProvider,
)
ApiConfig.ID.TangemPayAuth -> TangemPay.Auth(
environmentConfig = environmentConfig,
appVersionProvider = appVersionProvider,
appInfoProvider = appInfoProvider,
)
ApiConfig.ID.BlockAid -> BlockAid(environmentConfig = environmentConfig)
ApiConfig.ID.MoonPay -> MoonPay()
ApiConfig.ID.P2PEthPool -> P2PEthPool(p2pAuthProvider = p2pEthPoolAuthProvider)
ApiConfig.ID.News -> News(
appVersionProvider = appVersionProvider,
authProvider = appAuthProvider,
appInfoProvider = appInfoProvider,
)
ApiConfig.ID.GaslessTxService -> GaslessTxService(
authProvider = appAuthProvider,
appVersionProvider = appVersionProvider,
appInfoProvider = appInfoProvider,
)
}
@ -195,7 +189,7 @@ internal class ProdApiConfigsManagerTest {
"version" to ProviderSuspend { VERSION_NAME },
"system_version" to ProviderSuspend { "Android 16" },
"platform" to ProviderSuspend { "android" },
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
"language" to ProviderSuspend { Locale.getDefault().toLanguageTag().checkHeaderValueOrEmpty() },
"timezone" to ProviderSuspend {
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
},
@ -218,7 +212,7 @@ internal class ProdApiConfigsManagerTest {
"version" to ProviderSuspend { VERSION_NAME },
"platform" to ProviderSuspend { "android" },
"system_version" to ProviderSuspend { "Android 16" },
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
"language" to ProviderSuspend { Locale.getDefault().toLanguageTag().checkHeaderValueOrEmpty() },
"timezone" to ProviderSuspend {
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
},
@ -241,7 +235,7 @@ internal class ProdApiConfigsManagerTest {
"version" to ProviderSuspend { VERSION_NAME },
"platform" to ProviderSuspend { "android" },
"system_version" to ProviderSuspend { "Android 16" },
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
"language" to ProviderSuspend { Locale.getDefault().toLanguageTag().checkHeaderValueOrEmpty() },
"timezone" to ProviderSuspend {
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
},
@ -316,7 +310,7 @@ internal class ProdApiConfigsManagerTest {
"version" to ProviderSuspend { VERSION_NAME },
"platform" to ProviderSuspend { "android" },
"system_version" to ProviderSuspend { "Android 16" },
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
"language" to ProviderSuspend { Locale.getDefault().toLanguageTag().checkHeaderValueOrEmpty() },
"timezone" to ProviderSuspend {
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
},
@ -394,7 +388,7 @@ internal class ProdApiConfigsManagerTest {
"version" to ProviderSuspend { VERSION_NAME },
"platform" to ProviderSuspend { "android" },
"system_version" to ProviderSuspend { "Android 16" },
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
"language" to ProviderSuspend { Locale.getDefault().toLanguageTag().checkHeaderValueOrEmpty() },
"timezone" to ProviderSuspend {
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
},