commit 3276c8fb44f1203938f44c054c0332bb865e3744
parent 56e6cc26196381c56f4e9d96ed0b2104e2096c55
Author: Iván Ávalos <avalos@disroot.org>
Date: Sat, 29 Aug 2026 00:07:37 +0200
wallet: use bank-selected exchange for withdrawals
Diffstat:
3 files changed, 57 insertions(+), 13 deletions(-)
diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt
@@ -144,7 +144,7 @@ class ExchangeManager(
}
fun findExchangeForCurrency(currency: String): Flow<ExchangeItem?> = flow {
- emit(findExchange(currency))
+ emit(findExchangeByCurrency(currency))
}
fun findExchangeForBaseUrl(url: String): Flow<ExchangeItem?> = flow {
@@ -152,7 +152,7 @@ class ExchangeManager(
}
@WorkerThread
- suspend fun findExchange(currency: String): ExchangeItem? {
+ suspend fun findExchangeByCurrency(currency: String): ExchangeItem? {
var exchange: ExchangeItem? = null
api.request(
operation = "listExchanges",
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
@@ -184,6 +184,9 @@ data class WithdrawalDetailsForUri(
val status: WithdrawalOperationStatusFlag,
)
+internal fun WithdrawalDetailsForUri.initialExchangeBaseUrl(): String? =
+ defaultExchangeBaseUrl ?: possibleExchanges.firstOrNull()?.exchangeBaseUrl
+
@Serializable
data class WithdrawalDetailsForAmount(
/**
@@ -313,8 +316,7 @@ class WithdrawManager(
val tx = transactionManager.getTransactionById(details.transactionId)
?: error("transaction ${details.transactionId} not found")
- val exchangeBaseUrl = details.info.defaultExchangeBaseUrl
- ?: details.info.possibleExchanges.firstOrNull()?.exchangeBaseUrl
+ val exchangeBaseUrl = details.info.initialExchangeBaseUrl()
// Handle no exchanges configured by bank.
if (exchangeBaseUrl == null) {
@@ -336,8 +338,7 @@ class WithdrawManager(
status = if (alreadyConfirmed) AlreadyConfirmed else InfoReceived,
transactionId = details.transactionId,
uriInfo = details.info,
- exchangeBaseUrl = details.info.defaultExchangeBaseUrl
- ?: details.info.possibleExchanges.firstOrNull()?.exchangeBaseUrl
+ exchangeBaseUrl = exchangeBaseUrl,
)
)
}
@@ -348,10 +349,10 @@ class WithdrawManager(
// then extend with amount details (not for cash acceptor)
if (!status.isCashAcceptor) {
- getWithdrawalDetailsForAmount(
+ getWithdrawalDetailsForExchange(
+ exchangeBaseUrl = exchangeBaseUrl,
amount = details.info.amount
?: Amount.zero(details.info.currency),
- defaultExchangeBaseUrl = details.info.defaultExchangeBaseUrl,
loading = loading,
)
}
@@ -362,13 +363,11 @@ class WithdrawManager(
fun getWithdrawalDetailsForAmount(
amount: Amount,
scopeInfo: ScopeInfo? = null,
- defaultExchangeBaseUrl: String? = null,
loading: Boolean = true,
) = scope.launch {
// complete exchangeBaseUrl if missing
val exchange = scopeInfo?.let { exchangeManager.findExchange(it) }
- ?: defaultExchangeBaseUrl?.let { exchangeManager.findExchange(it) }
- ?: exchangeManager.findExchange(amount.currency)
+ ?: exchangeManager.findExchangeByCurrency(amount.currency)
if (exchange != null) {
getWithdrawalDetails(
amount = amount,
@@ -579,4 +578,4 @@ class WithdrawManager(
)
},
)
-}
-\ No newline at end of file
+}
diff --git a/wallet/src/test/java/net/taler/wallet/withdraw/WithdrawManagerKtTest.kt b/wallet/src/test/java/net/taler/wallet/withdraw/WithdrawManagerKtTest.kt
@@ -17,11 +17,57 @@
package net.taler.wallet.withdraw
import net.taler.common.Bech32.Companion.generateFakeSegwitAddress
+import net.taler.wallet.exchanges.ExchangeItem
+import net.taler.wallet.exchanges.ExchangeTosStatus
import org.junit.Assert
import org.junit.Test
class WithdrawManagerKtTest {
+ private fun exchange(baseUrl: String) = ExchangeItem(
+ exchangeBaseUrl = baseUrl,
+ currency = "CHF",
+ paytoUris = emptyList(),
+ tosStatus = ExchangeTosStatus.Accepted,
+ )
+
+ @Test
+ fun initialExchangePrefersBankDefault() {
+ val tops = exchange("https://exchange.taler-ops.ch/")
+ val bfh = exchange("https://exchange.taler.ti.bfh.ch/")
+ val info = WithdrawalDetailsForUri(
+ currency = "CHF",
+ defaultExchangeBaseUrl = bfh.exchangeBaseUrl,
+ possibleExchanges = listOf(tops, bfh),
+ status = WithdrawalOperationStatusFlag.Pending,
+ )
+
+ Assert.assertEquals(bfh.exchangeBaseUrl, info.initialExchangeBaseUrl())
+ }
+
+ @Test
+ fun initialExchangeFallsBackToFirstPossibleExchange() {
+ val tops = exchange("https://exchange.taler-ops.ch/")
+ val bfh = exchange("https://exchange.taler.ti.bfh.ch/")
+ val info = WithdrawalDetailsForUri(
+ currency = "CHF",
+ possibleExchanges = listOf(bfh, tops),
+ status = WithdrawalOperationStatusFlag.Pending,
+ )
+
+ Assert.assertEquals(bfh.exchangeBaseUrl, info.initialExchangeBaseUrl())
+ }
+
+ @Test
+ fun initialExchangeIsMissingWithoutDefaultOrPossibilities() {
+ val info = WithdrawalDetailsForUri(
+ currency = "CHF",
+ status = WithdrawalOperationStatusFlag.Pending,
+ )
+
+ Assert.assertNull(info.initialExchangeBaseUrl())
+ }
+
@Test
fun generateMainnet() {
val (addr1, addr2) = generateFakeSegwitAddress("54ZN9AMVN1R0YZ68ZPVHHQA4KZE1V037M05FNMYH4JQ596YAKJEG",