Updated on 2026-08-14
This commit is contained in:
parent
19fb8f2bcd
commit
ecdb72e55c
58 changed files with 1082 additions and 346 deletions
|
|
@ -15,7 +15,7 @@ import retrofit2.converter.moshi.MoshiConverterFactory
|
|||
object MoshiConverter {
|
||||
|
||||
val networkMoshi: Moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.addLast(KotlinJsonAdapterFactory())
|
||||
.add(BigDecimalAdapter())
|
||||
.add(TangemSdkAdapter.ByteArrayAdapter())
|
||||
.build()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
package com.tangem.datasource.api.common.config
|
||||
|
||||
import com.tangem.utils.ProviderSuspend
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
|
||||
internal class TangemVisa : ApiConfig() {
|
||||
internal class TangemVisa(
|
||||
private val appVersionProvider: AppVersionProvider,
|
||||
) : ApiConfig() {
|
||||
|
||||
override val defaultEnvironment: ApiEnvironment = ApiEnvironment.PROD
|
||||
|
||||
|
|
@ -16,5 +19,8 @@ internal class TangemVisa : ApiConfig() {
|
|||
headers = createHeaders(),
|
||||
)
|
||||
|
||||
private fun createHeaders() = mapOf<String, ProviderSuspend<String>>()
|
||||
private fun createHeaders() = mapOf(
|
||||
"version" to ProviderSuspend { appVersionProvider.versionName },
|
||||
"platform" to ProviderSuspend { "Android" },
|
||||
)
|
||||
}
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
package com.tangem.datasource.api.common.config
|
||||
|
||||
import com.tangem.utils.ProviderSuspend
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
|
||||
internal class TangemVisaAuth : ApiConfig() {
|
||||
internal class TangemVisaAuth(
|
||||
private val appVersionProvider: AppVersionProvider,
|
||||
) : ApiConfig() {
|
||||
|
||||
override val defaultEnvironment: ApiEnvironment = ApiEnvironment.STAGE
|
||||
|
||||
|
|
@ -16,5 +19,8 @@ internal class TangemVisaAuth : ApiConfig() {
|
|||
headers = createHeaders(),
|
||||
)
|
||||
|
||||
private fun createHeaders() = mapOf<String, ProviderSuspend<String>>()
|
||||
private fun createHeaders() = mapOf(
|
||||
"version" to ProviderSuspend { appVersionProvider.versionName },
|
||||
"platform" to ProviderSuspend { "Android" },
|
||||
)
|
||||
}
|
||||
|
|
@ -1,21 +1,67 @@
|
|||
package com.tangem.datasource.api.visa
|
||||
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.utils.ReadTimeout
|
||||
import com.tangem.datasource.api.visa.models.request.ActivationByCardWalletRequest
|
||||
import com.tangem.datasource.api.visa.models.request.ActivationByCustomerWalletRequest
|
||||
import com.tangem.datasource.api.visa.models.response.CardActivationRemoteStateResponse
|
||||
import com.tangem.datasource.api.visa.models.response.CardWalletDataToSignResponse
|
||||
import com.tangem.datasource.api.visa.models.response.CustomerWalletDataToSignResponse
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Query
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
interface TangemVisaApi {
|
||||
|
||||
@GET("activation-status")
|
||||
@GET("product_instance/activation_status")
|
||||
suspend fun getRemoteActivationStatus(
|
||||
@Header("Authorization") authHeader: String,
|
||||
): CardActivationRemoteStateResponse
|
||||
@Query("customer_id") customerId: String,
|
||||
@Query("product_instance_id") productInstanceId: String,
|
||||
@Query("card_id") cardId: String,
|
||||
@Query("card_public_key") cardPublicKey: String,
|
||||
): ApiResponse<CardActivationRemoteStateResponse>
|
||||
|
||||
@ReadTimeout(duration = 20, TimeUnit.MINUTES)
|
||||
@GET("activation-status")
|
||||
@GET("product_instance/activation_status")
|
||||
suspend fun getRemoteActivationStatusLongPoll(
|
||||
@Header("Authorization") authHeader: String,
|
||||
): CardActivationRemoteStateResponse
|
||||
@Query("customer_id") customerId: String,
|
||||
@Query("product_instance_id") productInstanceId: String,
|
||||
@Query("card_id") cardId: String,
|
||||
@Query("card_public_key") cardPublicKey: String,
|
||||
): ApiResponse<CardActivationRemoteStateResponse>
|
||||
|
||||
@GET("product_instance/card_wallet_acceptance")
|
||||
suspend fun getCardWalletAcceptance(
|
||||
@Header("Authorization") authHeader: String,
|
||||
@Query("customer_id") customerId: String,
|
||||
@Query("product_instance_id") productInstanceId: String,
|
||||
@Query("activation_order") activationOrderId: String,
|
||||
@Query("customer_wallet_address") customerWalletAddress: String,
|
||||
): ApiResponse<CardWalletDataToSignResponse>
|
||||
|
||||
@GET("product_instance/customer_wallet_acceptance")
|
||||
suspend fun getCustomerWalletAcceptance(
|
||||
@Header("Authorization") authHeader: String,
|
||||
@Query("customer_id") customerId: String,
|
||||
@Query("product_instance_id") productInstanceId: String,
|
||||
@Query("activation_order") activationOrderId: String,
|
||||
@Query("card_wallet_address") cardWalletAddress: String,
|
||||
): ApiResponse<CustomerWalletDataToSignResponse>
|
||||
|
||||
@POST("product_instance/activation_by_card_wallet")
|
||||
suspend fun activateByCardWallet(
|
||||
@Header("Authorization") authHeader: String,
|
||||
@Body body: ActivationByCardWalletRequest,
|
||||
): ApiResponse<Unit>
|
||||
|
||||
@POST("product_instance/activation_by_customer_wallet")
|
||||
suspend fun activateByCustomerWallet(
|
||||
@Header("Authorization") authHeader: String,
|
||||
@Body body: ActivationByCustomerWalletRequest,
|
||||
): ApiResponse<Unit>
|
||||
}
|
||||
|
|
@ -3,45 +3,29 @@ package com.tangem.datasource.api.visa
|
|||
import com.tangem.datasource.api.visa.models.response.GenerateNonceResponse
|
||||
import com.tangem.datasource.api.visa.models.response.JWTResponse
|
||||
import retrofit2.http.Field
|
||||
import retrofit2.http.FormUrlEncoded
|
||||
import retrofit2.http.Headers
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface TangemVisaAuthApi {
|
||||
|
||||
@FormUrlEncoded
|
||||
@Headers("Content-Type: application/x-www-form-urlencoded")
|
||||
@POST("auth/clients/mobile-app-android/nonce-challenge")
|
||||
@POST("auth/card_wallet")
|
||||
suspend fun generateNonceByWalletAddress(
|
||||
@Field("customer_id") customerId: String? = null,
|
||||
@Field("customer_wallet_address") customerWalletAddress: String,
|
||||
@Query("card_wallet_address") cardWalletAddress: String,
|
||||
): GenerateNonceResponse
|
||||
|
||||
@FormUrlEncoded
|
||||
@Headers("Content-Type: application/x-www-form-urlencoded")
|
||||
@POST("auth/clients/mobile-app-android/nonce-challenge")
|
||||
@POST("auth/card_id")
|
||||
suspend fun generateNonceByCard(
|
||||
@Field("card_id") cardId: String,
|
||||
@Field("card_public_key") cardPublicKey: String,
|
||||
@Query("card_id") cardId: String,
|
||||
@Query("card_public_key") cardPublicKey: String,
|
||||
): GenerateNonceResponse
|
||||
|
||||
@FormUrlEncoded
|
||||
@Headers("Content-Type: application/x-www-form-urlencoded")
|
||||
@POST("auth/protocol/openid-connect/token")
|
||||
@POST("auth/get_token")
|
||||
suspend fun getAccessToken(
|
||||
@Field("client_id") clientId: String = "mobile-app-android",
|
||||
@Field("grant_type") grantType: String = "password",
|
||||
@Field("session_id") sessionId: String,
|
||||
@Field("signature") signature: String,
|
||||
@Field("salt") salt: String?,
|
||||
@Query("session_id") sessionId: String,
|
||||
@Query("signature") signature: String,
|
||||
@Query("salt") salt: String?,
|
||||
): JWTResponse
|
||||
|
||||
@FormUrlEncoded
|
||||
@Headers("Content-Type: application/x-www-form-urlencoded")
|
||||
@POST("auth/protocol/openid-connect/token")
|
||||
suspend fun refreshAccessToken(
|
||||
@Field("client_id") clientId: String = "mobile-app-android",
|
||||
@Field("grant_type") grantType: String = "refresh_token",
|
||||
@Field("refresh_token") refreshToken: String,
|
||||
): JWTResponse
|
||||
@POST("auth/refresh_token")
|
||||
suspend fun refreshAccessToken(@Field("refresh_token") refreshToken: String): JWTResponse
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.datasource.api.visa.models.request
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ActivationByCardWalletRequest(
|
||||
@Json(name = "customer_id") val customerId: String,
|
||||
@Json(name = "product_instance_id") val productInstanceId: String,
|
||||
@Json(name = "activation_order_id") val activationOrderId: String,
|
||||
@Json(name = "data") val data: Data,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Data(
|
||||
@Json(name = "card_wallet") val cardWallet: CardWallet,
|
||||
@Json(name = "otp") val otp: Otp,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CardWallet(
|
||||
@Json(name = "address") val address: String,
|
||||
@Json(name = "card_wallet_confirmation") val cardWalletConfirmation: CardWalletConfirmation?,
|
||||
@Json(name = "deploy_acceptance_signature") val deployAcceptanceSignature: String,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CardWalletConfirmation(
|
||||
@Json(name = "challenge") val challenge: String,
|
||||
@Json(name = "wallet_salt") val walletSalt: String,
|
||||
@Json(name = "wallet_signature") val walletSignature: String,
|
||||
@Json(name = "card_salt") val cardSalt: String,
|
||||
@Json(name = "card_signature") val cardSignature: String,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Otp(
|
||||
@Json(name = "root_otp") val rootOtp: String,
|
||||
@Json(name = "counter") val counter: Int,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.datasource.api.visa.models.request
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ActivationByCustomerWalletRequest(
|
||||
@Json(name = "customer_id") val customerId: String,
|
||||
@Json(name = "product_instance_id") val productInstanceId: String,
|
||||
@Json(name = "activation_order_id") val activationOrderId: String,
|
||||
@Json(name = "data") val data: Data,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Data(
|
||||
@Json(name = "customer_wallet") val customerWallet: CustomerWallet,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CustomerWallet(
|
||||
@Json(name = "address") val address: String,
|
||||
@Json(name = "deploy_acceptance_signature") val deployAcceptanceSignature: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -5,5 +5,12 @@ import com.squareup.moshi.JsonClass
|
|||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CardActivationRemoteStateResponse(
|
||||
@Json(name = "state") val state: String,
|
||||
)
|
||||
@Json(name = "activation_status") val status: String,
|
||||
@Json(name = "activation_order") val activationOrder: ActivationOrder?,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ActivationOrder(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "customer_wallet_address") val customerWalletAddress: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.datasource.api.visa.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CardWalletDataToSignResponse(
|
||||
@Json(name = "dataForCardWallet") val dataForCardWallet: Data,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Data(
|
||||
@Json(name = "hash") val hash: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.datasource.api.visa.models.response
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CustomerWalletDataToSignResponse(
|
||||
@Json(name = "data_for_customer_wallet") val dataForCardWallet: Data,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Data(
|
||||
@Json(name = "hash") val hash: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -42,9 +42,10 @@ internal object ApiConfigsModule {
|
|||
|
||||
@Provides
|
||||
@IntoSet
|
||||
fun provideTangemAuthVisaConfig(): ApiConfig = TangemVisaAuth()
|
||||
fun provideTangemAuthVisaConfig(appVersionProvider: AppVersionProvider): ApiConfig =
|
||||
TangemVisaAuth(appVersionProvider)
|
||||
|
||||
@Provides
|
||||
@IntoSet
|
||||
fun provideTangemVisaConfig(): ApiConfig = TangemVisa()
|
||||
fun provideTangemVisaConfig(appVersionProvider: AppVersionProvider): ApiConfig = TangemVisa(appVersionProvider)
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import com.tangem.datasource.api.common.adapter.LocalDateAdapter
|
|||
import com.tangem.datasource.api.common.adapter.addStakeKitEnumFallbackAdapters
|
||||
import com.tangem.datasource.local.config.providers.models.ProviderModel
|
||||
import com.tangem.domain.models.scan.serialization.*
|
||||
import com.tangem.domain.visa.model.VisaActivationRemoteState
|
||||
import com.tangem.domain.visa.model.VisaCardActivationStatus
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -35,8 +36,9 @@ class MoshiModule {
|
|||
.add(BigDecimalAdapter())
|
||||
.add(LocalDateAdapter())
|
||||
.add(DateTimeAdapter())
|
||||
.add(VisaActivationRemoteState.jsonAdapter)
|
||||
.add(VisaCardActivationStatus.jsonAdapter)
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.addLast(KotlinJsonAdapterFactory())
|
||||
.addStakeKitEnumFallbackAdapters()
|
||||
.build()
|
||||
}
|
||||
|
|
@ -59,10 +61,11 @@ class MoshiModule {
|
|||
val typedAdapters = MoshiJsonConverter.getTangemSdkTypedAdapters()
|
||||
|
||||
return Moshi.Builder().apply {
|
||||
add(VisaActivationRemoteState.jsonAdapter)
|
||||
add(VisaCardActivationStatus.jsonAdapter)
|
||||
adapters.forEach { this.add(it) }
|
||||
typedAdapters.forEach { add(it.key, it.value) }
|
||||
add(KotlinJsonAdapterFactory())
|
||||
addLast(KotlinJsonAdapterFactory())
|
||||
}.build()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,16 @@ package com.tangem.datasource.local.visa
|
|||
|
||||
interface VisaOTPStorage {
|
||||
|
||||
suspend fun saveOTP(cardId: String, otp: ByteArray)
|
||||
suspend fun saveOTP(cardId: String, data: VisaOtpData)
|
||||
|
||||
suspend fun getOTP(cardId: String): ByteArray?
|
||||
suspend fun getOTP(cardId: String): VisaOtpData?
|
||||
|
||||
suspend fun removeOTP(cardId: String)
|
||||
}
|
||||
|
||||
class VisaOtpData(
|
||||
val rootOTP: ByteArray,
|
||||
val counter: Int,
|
||||
)
|
||||
|
||||
suspend fun VisaOTPStorage.hasSavedOTP(cardId: String): Boolean = getOTP(cardId) != null
|
||||
|
|
@ -36,8 +36,8 @@ private val API_CONFIGS = setOf(
|
|||
Express(configManager, expressAuthProvider, appVersionProvider),
|
||||
TangemTech(appVersionProvider, appAuthProvider),
|
||||
StakeKit(stakeKitAuthProvider),
|
||||
TangemVisaAuth(),
|
||||
TangemVisa(),
|
||||
TangemVisaAuth(appVersionProvider),
|
||||
TangemVisa(appVersionProvider),
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
@ -188,6 +188,10 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
|
|||
expected = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.STAGE,
|
||||
baseUrl = "https://api-s.tangem.org/",
|
||||
headers = mapOf(
|
||||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"platform" to ProviderSuspend { "Android" },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -198,7 +202,10 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
|
|||
expected = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://bff.tangem.com/",
|
||||
headers = mapOf(),
|
||||
headers = mapOf(
|
||||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"platform" to ProviderSuspend { "Android" },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue