taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

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:
Mwallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt | 4++--
Mwallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt | 20+++++++++-----------
Mwallet/src/test/java/net/taler/wallet/withdraw/WithdrawManagerKtTest.kt | 46++++++++++++++++++++++++++++++++++++++++++++++
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",