Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-25 18:48:48 +04:00
parent 8b02a0825a
commit 40da3bbe0f
6 changed files with 74 additions and 51 deletions

View file

@ -51,6 +51,16 @@ internal class DefaultTangemPayStorage @Inject constructor(
private val withdrawStoreDataConverter by lazy { WithdrawStoreDataConverter() } private val withdrawStoreDataConverter by lazy { WithdrawStoreDataConverter() }
private val withdrawStateConverter by lazy { WithdrawStateConverter() } private val withdrawStateConverter by lazy { WithdrawStateConverter() }
private val listType by lazy {
Types.newParameterizedType(List::class.java, WithdrawStoreData::class.java)
}
private val mapType by lazy {
Types.newParameterizedType(Map::class.java, String::class.java, listType)
}
private val adapter: JsonAdapter<Map<String, List<WithdrawStoreData>>> by lazy {
appPreferencesStore.moshi.adapter(mapType)
}
override suspend fun storeCustomerWalletAddress(userWalletId: UserWalletId, customerWalletAddress: String) { override suspend fun storeCustomerWalletAddress(userWalletId: UserWalletId, customerWalletAddress: String) {
withContext(dispatcherProvider.io) { withContext(dispatcherProvider.io) {
appPreferencesStore appPreferencesStore
@ -152,15 +162,19 @@ internal class DefaultTangemPayStorage @Inject constructor(
} }
override suspend fun storeWithdrawOrder(userWalletId: UserWalletId, data: TangemPayWithdrawState) { override suspend fun storeWithdrawOrder(userWalletId: UserWalletId, data: TangemPayWithdrawState) {
val listType = Types.newParameterizedType(List::class.java, WithdrawStoreData::class.java)
val mapType = Types.newParameterizedType(Map::class.java, String::class.java, listType)
val adapter: JsonAdapter<Map<String, List<WithdrawStoreData>>> = appPreferencesStore.moshi.adapter(mapType)
appPreferencesStore.editData { prefs -> appPreferencesStore.editData { prefs ->
val walletKey = createWithdrawOrderIdKey(userWalletId) val walletKey = createWithdrawOrderIdKey(userWalletId)
val currentMap = prefs[PreferencesKeys.TANGEM_PAY_WITHDRAW_ORDERS_KEY]?.let(adapter::fromJson).orEmpty() val currentMap = prefs[PreferencesKeys.TANGEM_PAY_WITHDRAW_ORDERS_KEY]
val updatedList = currentMap[walletKey].orEmpty() + withdrawStoreDataConverter.convert(data) ?.let(adapter::fromJson)
prefs[PreferencesKeys.TANGEM_PAY_WITHDRAW_ORDERS_KEY] = adapter .orEmpty()
.toJson(currentMap + (walletKey to updatedList)) val newItem = withdrawStoreDataConverter.convert(data)
val currentList = currentMap[walletKey].orEmpty()
val updatedList = buildList(currentList.size + 1) {
for (item in currentList) { if (item.orderId != newItem.orderId) add(item) }
add(newItem)
}
prefs[PreferencesKeys.TANGEM_PAY_WITHDRAW_ORDERS_KEY] =
adapter.toJson(currentMap + (walletKey to updatedList))
} }
} }
@ -170,9 +184,6 @@ internal class DefaultTangemPayStorage @Inject constructor(
} }
override suspend fun getWithdrawOrders(userWalletId: UserWalletId): List<TangemPayWithdrawState> { override suspend fun getWithdrawOrders(userWalletId: UserWalletId): List<TangemPayWithdrawState> {
val listType = Types.newParameterizedType(List::class.java, WithdrawStoreData::class.java)
val mapType = Types.newParameterizedType(Map::class.java, String::class.java, listType)
val adapter: JsonAdapter<Map<String, List<WithdrawStoreData>>> = appPreferencesStore.moshi.adapter(mapType)
val map = appPreferencesStore.data.firstOrNull() val map = appPreferencesStore.data.firstOrNull()
?.get(PreferencesKeys.TANGEM_PAY_WITHDRAW_ORDERS_KEY)?.let(adapter::fromJson).orEmpty() ?.get(PreferencesKeys.TANGEM_PAY_WITHDRAW_ORDERS_KEY)?.let(adapter::fromJson).orEmpty()
return map[createWithdrawOrderIdKey(userWalletId)].orEmpty().map(withdrawStateConverter::convert) return map[createWithdrawOrderIdKey(userWalletId)].orEmpty().map(withdrawStateConverter::convert)
@ -190,15 +201,22 @@ internal class DefaultTangemPayStorage @Inject constructor(
} }
override suspend fun deleteWithdrawOrder(userWalletId: UserWalletId, orderId: String) { override suspend fun deleteWithdrawOrder(userWalletId: UserWalletId, orderId: String) {
appPreferencesStore.editData { mutablePreferences -> appPreferencesStore.editData { prefs ->
val orders = mutablePreferences.getObjectMap<WithdrawStoreData>( val walletKey = createWithdrawOrderIdKey(userWalletId)
PreferencesKeys.TANGEM_PAY_WITHDRAW_ORDERS_KEY, val currentMap = prefs[PreferencesKeys.TANGEM_PAY_WITHDRAW_ORDERS_KEY]?.let(adapter::fromJson)
) .orEmpty()
.minus(createWithdrawOrderIdKey(userWalletId)) val currentList = currentMap[walletKey].orEmpty()
mutablePreferences.setObjectMap( val updatedList = buildList(currentList.size) {
key = PreferencesKeys.TANGEM_PAY_WITHDRAW_ORDERS_KEY, for (item in currentList) {
value = orders, if (item.orderId != orderId) add(item)
) }
}
val updatedMap = if (updatedList.isEmpty()) {
currentMap - walletKey
} else {
currentMap + (walletKey to updatedList)
}
prefs[PreferencesKeys.TANGEM_PAY_WITHDRAW_ORDERS_KEY] = adapter.toJson(updatedMap)
} }
} }

View file

@ -7,6 +7,7 @@ import com.squareup.moshi.JsonClass
data class WithdrawStoreData( data class WithdrawStoreData(
@Json(name = "orderId") val orderId: String, @Json(name = "orderId") val orderId: String,
@Json(name = "exchangeData") val exchangeData: ExchangeStoreData?, @Json(name = "exchangeData") val exchangeData: ExchangeStoreData?,
@Json(name = "txHash") val txHash: String?,
) )
@JsonClass(generateAdapter = false) @JsonClass(generateAdapter = false)

View file

@ -110,12 +110,15 @@ internal class DefaultTangemPayWithdrawRepository @Inject constructor(
val storeData = TangemPayWithdrawState( val storeData = TangemPayWithdrawState(
orderId = orderId, orderId = orderId,
exchangeData = exchangeData, exchangeData = exchangeData,
txHash = withdrawTxHash,
) )
if (order != null && !withdrawTxHash.isNullOrEmpty()) { if (order != null && !withdrawTxHash.isNullOrEmpty()) {
finalizeWithdraw(userWallet = userWallet, txHash = withdrawTxHash, exchangeData = exchangeData) finalizeWithdraw(
.onLeft { userWallet = userWallet,
tangemPayStorage.storeWithdrawOrder(userWalletId = userWallet.walletId, data = storeData) txHash = withdrawTxHash,
} exchangeData = exchangeData,
orderId = orderId,
)
} else { } else {
tangemPayStorage.storeWithdrawOrder(userWalletId = userWallet.walletId, data = storeData) tangemPayStorage.storeWithdrawOrder(userWalletId = userWallet.walletId, data = storeData)
} }
@ -124,6 +127,7 @@ internal class DefaultTangemPayWithdrawRepository @Inject constructor(
private suspend fun finalizeWithdraw( private suspend fun finalizeWithdraw(
userWallet: UserWallet, userWallet: UserWallet,
orderId: String,
txHash: String, txHash: String,
exchangeData: TangemPayWithdrawExchangeState, exchangeData: TangemPayWithdrawExchangeState,
): Either<ExpressDataError, Unit> { ): Either<ExpressDataError, Unit> {
@ -135,10 +139,9 @@ internal class DefaultTangemPayWithdrawRepository @Inject constructor(
payInAddress = exchangeData.payInAddress, payInAddress = exchangeData.payInAddress,
txHash = txHash, txHash = txHash,
payInExtraId = exchangeData.payInExtraId, payInExtraId = exchangeData.payInExtraId,
) ).also {
.onLeft { error -> tangemPayStorage.deleteWithdrawOrder(userWallet.walletId, orderId)
Timber.tag(TAG).e(error.toString()) }
}
} }
override suspend fun hasWithdrawOrder(userWallet: UserWallet): Boolean { override suspend fun hasWithdrawOrder(userWallet: UserWallet): Boolean {
@ -168,19 +171,23 @@ internal class DefaultTangemPayWithdrawRepository @Inject constructor(
private suspend fun pollWithdrawOrderIfNeeds(userWallet: UserWallet, data: TangemPayWithdrawState) { private suspend fun pollWithdrawOrderIfNeeds(userWallet: UserWallet, data: TangemPayWithdrawState) {
val exchangeData = data.exchangeData ?: return val exchangeData = data.exchangeData ?: return
val storedHash = data.txHash
val orderId = data.orderId val orderId = data.orderId
val order = orderRepository.getOrderData(userWalletId = userWallet.walletId, orderId = orderId).getOrNull() val txHash = if (storedHash.isNullOrEmpty()) {
?: return val order = orderRepository.getOrderData(userWalletId = userWallet.walletId, orderId = orderId).getOrNull()
val txHash = order.withdrawTxHash ?: return
order.withdrawTxHash.also { fetchedHash ->
tangemPayStorage.storeWithdrawOrder(
userWalletId = userWallet.walletId,
data = data.copy(txHash = fetchedHash),
)
}
} else {
storedHash
}
if (!txHash.isNullOrEmpty()) { if (!txHash.isNullOrEmpty()) {
finalizeWithdraw(userWallet = userWallet, txHash = txHash, exchangeData = exchangeData) finalizeWithdraw(userWallet = userWallet, txHash = txHash, exchangeData = exchangeData, orderId = orderId)
.onRight {
tangemPayStorage.deleteWithdrawOrder(userWalletId = userWallet.walletId, orderId = orderId)
}
.onLeft {
startWithdrawOrderPolling(userWallet = userWallet, orderId = orderId, exchangeData = exchangeData)
}
} else { } else {
startWithdrawOrderPolling(userWallet = userWallet, orderId = orderId, exchangeData = exchangeData) startWithdrawOrderPolling(userWallet = userWallet, orderId = orderId, exchangeData = exchangeData)
} }
@ -204,20 +211,14 @@ internal class DefaultTangemPayWithdrawRepository @Inject constructor(
.onRight { order -> .onRight { order ->
val txHash = order.withdrawTxHash val txHash = order.withdrawTxHash
if (txHash.isNullOrEmpty()) return@onRight if (txHash.isNullOrEmpty()) return@onRight
finalizeWithdraw(userWallet = userWallet, txHash = txHash, exchangeData = exchangeData) finalizeWithdraw(
.onRight { userWallet = userWallet,
tangemPayStorage.deleteWithdrawOrder( txHash = txHash,
userWalletId = userWallet.walletId, exchangeData = exchangeData,
orderId = orderId, orderId = orderId,
) )
withdrawPollingMutex.withLock { withdrawPollingJobs.remove(orderId) } withdrawPollingMutex.withLock { withdrawPollingJobs.remove(orderId) }
return@launch return@launch
}
.onLeft { error ->
Timber.tag(TAG).e("finalizeWithdraw error: $error")
withdrawPollingMutex.withLock { withdrawPollingJobs.remove(orderId) }
return@launch
}
} }
.onLeft { error -> .onLeft { error ->
Timber.tag(TAG).e("getOrderData error ${error.errorCode}") Timber.tag(TAG).e("getOrderData error ${error.errorCode}")

View file

@ -18,5 +18,6 @@ class WithdrawStateConverter : Converter<WithdrawStoreData, TangemPayWithdrawSta
payInExtraId = exchangeData.payInExtraId, payInExtraId = exchangeData.payInExtraId,
) )
}, },
txHash = value.txHash,
) )
} }

View file

@ -18,5 +18,6 @@ class WithdrawStoreDataConverter : Converter<TangemPayWithdrawState, WithdrawSto
payInExtraId = exchangeData.payInExtraId, payInExtraId = exchangeData.payInExtraId,
) )
}, },
txHash = value.txHash,
) )
} }

View file

@ -3,6 +3,7 @@ package com.tangem.domain.pay
data class TangemPayWithdrawState( data class TangemPayWithdrawState(
val orderId: String, val orderId: String,
val exchangeData: TangemPayWithdrawExchangeState?, val exchangeData: TangemPayWithdrawExchangeState?,
val txHash: String? = null,
) )
data class TangemPayWithdrawExchangeState( data class TangemPayWithdrawExchangeState(