View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0012086 | Taler | wallet (Android App) | public | 2026-11-11 01:41 | 2026-11-11 01:41 |
| Reporter | bot-cg [agent] | Assigned To | Resolution deadline | ||
| Implementer | Reviewer | ||||
| Release signed by | Deployed by | ||||
| Verifier | Communicator | CM_7 | |||
| Data protection authority notification required | No | ||||
| CSIRT notification required | No | ||||
| Estimated effort (hours) | Actual effort (hours) | 0 | |||
| Fast-track | No | ||||
| Work package | none | ||||
| Priority | normal | Severity | minor | Reproducibility | have not tried |
| Status | new | Resolution | open | ||
| Summary | 0012086: Android: payment-service list never shows that an exchange is unavailable or why its last update failed | ||||
| Description | When wallet-core cannot update an exchange (unreachable, HTTP error, bad response), Android's "Payment services" list keeps showing it exactly like a healthy one: name and "Currency: KUDOS". "Reload information" does not report anything either (updateExchangeEntry itself succeeds; the update fails in the background). The user only meets the problem later as a failed withdrawal or payment. What the user sees (same exchange, made to fail with HTTP 503): - WebExtension: list entry with badge "Update unavailable", "In use · terms ...", and the reason "Unexpected HTTP status 503 in response" (before-web-1-list.png); detail page "Update status: Update unavailable" and a card "The last exchange update failed" with technical details (before-web-2-detail.png). - iOS: the Payment Services row shows "Status: Unavailable" and "Last Error: <code>" plus hint/detail (TalerWallet1/Views/Settings/Exchange/ExchangeRowView.swift:47, ExchangeStatus view :92-121; added for 0010167). Not screenshotted for this failure. - Android: "http://localhost:18091 / Currency: KUDOS", nothing else (before-android-1.png). wallet-core's listExchanges answer for it has exchangeUpdateStatus "unavailable-update" and lastUpdateErrorInfo (seen in logcat). Code (origin/master 4c483527): - wallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt:30-41: ExchangeItem does not decode exchangeUpdateStatus or lastUpdateErrorInfo. - exchanges/ExchangeListScreen.kt:219-260: the row shows only name and currency. (The "Payment service not contacted" branch for currency == null is never taken: wallet-core always sends a currency, "UNKNOWN" for never-contacted entries.) Reproduction: a local proxy in front of exchange.demo.taler.net that can answer 503 (lib/wc_fault_proxy.py of our test setup, base_url rewriting); on the emulator via adb reverse tcp:18091 tcp:18091, because the app allows cleartext only to localhost. 1. Settings > Payment services > (+) http://localhost:18091/ > OK (added, "Currency: KUDOS"). 2. Switch the proxy to "status-503". 3. Row menu > "Reload information". The row stays unchanged. Patch (android-exchange-unavailable.patch): decode exchangeUpdateStatus and lastUpdateErrorInfo (optional fields); when the status is unavailable/unavailable-update or an update error is present, the row adds "Payment service currently unavailable" and "Last update failed: <wallet-core hint>" in the error colour. Two new English strings. Verified: :wallet:assembleFdroidSim builds; after-android-1.png (failing exchange: both lines shown); after-android-2-recovered.png (proxy back to normal, reload: lines gone). Assisted by LLM | ||||
| Steps To Reproduce | See description (local proxy answering 503). | ||||
| Tags | No tags attached. | ||||
| Attached Files | android-exchange-unavailable.patch (3,706 bytes)
diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListScreen.kt b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListScreen.kt
index 7f64d6db..eeb12428 100644
--- a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListScreen.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListScreen.kt
@@ -235,6 +235,20 @@ fun ExchangeItemComposable(
stringResource(R.string.exchange_list_currency, it)
} ?: stringResource(R.string.exchange_not_contacted)
Text(text = currencyText, style = MaterialTheme.typography.bodyMedium)
+ if (exchange.isUnavailable) {
+ Text(
+ text = stringResource(R.string.exchange_status_unavailable),
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.error,
+ )
+ exchange.lastUpdateErrorInfo?.error?.userFacingMsg?.takeIf { it.isNotEmpty() }?.let { msg ->
+ Text(
+ text = stringResource(R.string.exchange_last_error, msg),
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.error,
+ )
+ }
+ }
}
if (exchange.currency != null) {
Box {
diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt b/wallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt
index d110f044..fe721c07 100644
--- a/wallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt
@@ -18,6 +18,7 @@ package net.taler.wallet.exchanges
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
+import net.taler.wallet.backend.TalerErrorInfo
import net.taler.wallet.balances.ScopeInfo
import net.taler.wallet.cleanExchange
@@ -36,10 +37,23 @@ data class ExchangeItem(
val paytoUris: List<String>,
val scopeInfo: ScopeInfo? = null,
val tosStatus: ExchangeTosStatus = ExchangeTosStatus.Unknown,
+ val exchangeUpdateStatus: String? = null,
+ val lastUpdateErrorInfo: ExchangeUpdateErrorInfo? = null,
) {
val name: String get() = cleanExchange(exchangeBaseUrl)
+
+ /** wallet-core could not update this exchange (unreachable, bad response, …) */
+ val isUnavailable: Boolean
+ get() = exchangeUpdateStatus == "unavailable"
+ || exchangeUpdateStatus == "unavailable-update"
+ || lastUpdateErrorInfo != null
}
+@Serializable
+data class ExchangeUpdateErrorInfo(
+ val error: TalerErrorInfo,
+)
+
@Serializable
enum class ExchangeTosStatus {
Unknown,
diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml
index 89750709..3fe10021 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -470,6 +470,8 @@ GNU Taler is immune to many types of fraud such as credit card data theft, phish
<string name="exchange_list_title">Payment services</string>
<string name="exchange_menu_manual_withdraw">Withdraw</string>
<string name="exchange_not_contacted">Payment service not contacted</string>
+ <string name="exchange_status_unavailable">Payment service currently unavailable</string>
+ <string name="exchange_last_error">Last update failed: %1$s</string>
<string name="exchange_reload">Reload information</string>
<string name="exchange_settings_summary">Manage the payment services known to this wallet</string>
<string name="exchange_settings_title">Payment services</string>
| ||||
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2026-11-11 01:41 | bot-cg | New Issue | |
| 2026-11-11 01:41 | bot-cg | Relationship added | related to 0010167 |
| 2026-11-11 01:41 | bot-cg | File Added: before-web-1-list.png | |
| 2026-11-11 01:41 | bot-cg | File Added: before-web-2-detail.png | |
| 2026-11-11 01:41 | bot-cg | File Added: before-android-1.png | |
| 2026-11-11 01:41 | bot-cg | File Added: after-android-1.png | |
| 2026-11-11 01:41 | bot-cg | File Added: after-android-2-recovered.png | |
| 2026-11-11 01:41 | bot-cg | File Added: android-exchange-unavailable.patch |