View Issue Details

IDProjectCategoryView StatusLast Update
0012085Talerwallet (Android App)public2026-11-11 01:41
Reporterbot-cg [agent] Assigned ToResolution deadline
ImplementerReviewer 
Release signed byDeployed by 
VerifierCommunicatorCM_7  
Data protection authority notification requiredNo  
CSIRT notification requiredNo  
Estimated effort (hours)Actual effort (hours)0 
Fast-trackNo
Work packagenone
PrioritynormalSeverityminorReproducibilityhave not tried
Status newResolutionopen 
Summary0012085: Android: the balances screen ignores wallet-core's balance flags -- money waiting for KYC or bank confirmation is shown as plain "+X incoming" (patch attached)
Descriptionwallet-core's getBalances returns flags per balance that say when pending money waits for the user: incoming-kyc (a withdrawal needs KYC), incoming-confirmation (a withdrawal still has to be confirmed at the bank) and outgoing-kyc (a deposit needs KYC). The Android wallet does not even decode them: the balances screen shows "+5.00 incoming" for money that will never arrive until the user completes KYC, exactly like money that is just on its way.

What the user sees (fresh wallets; withdrawal of KUDOS 5 from the tripwire KUDOS test exchange, which requires KYC above KUDOS 4, confirmed at the bank -> pending(kyc); and a withdrawal of KUDOS 3 from bank.demo.taler.net accepted in the wallet but not yet confirmed at the bank):
- Android: "+3.00 incoming" and "+5.00 incoming", nothing else (before-android-1.png). getBalances returned "flags":["incoming-confirmation"] and "flags":["incoming-kyc"] for these two balances (logcat).
- WebExtension: "Incoming funds need bank confirmation" / "Incoming funds need identity verification" next to the balance (before-web-1.png, before-web-2.png).
- iOS: the pending-incoming row carries a badge and VoiceOver reads ". Needs bank confirmation" / ". Legitimization required" (before-ios-1.png, bank confirmation case).

Code (origin/master 4c483527):
- wallet/src/main/java/net/taler/wallet/balances/Balances.kt:25-35: BalanceItem has no flags.
- wallet/src/main/java/net/taler/wallet/balances/LegacyBalancesComposable.kt:346-366 (default main screen) and BalancesPagerComposable.kt:544-617 (new main screen, developer option): pending amounts only.
- wallet-core: taler-util types-taler-wallet.ts:399-404 (BalanceFlag), taler-wallet-core balance.ts:493-515, 556-564, 731, 738, 769, 976.
- Reference: taler-typescript-core packages/wallet-webui/src/routes/balance-model.ts:98-115; taler-ios TalerWallet1/Views/Balances/BalancesPendingRowV.swift:28-58.

The patch (android-F404-balance-flags.patch):
- Balances.kt: BalanceItem.flags as a list of strings (a flag added later cannot break decoding) with incomingKyc / incomingConfirmation / outgoingKyc helpers.
- LegacyBalancesComposable.kt: under "+X incoming" / "-X outgoing" a warning line with existing translated strings: "KYC required to proceed" (transactions_required_kyc) and "Waiting for authorization in the bank" (transaction_state_pending_bank). No new strings. The developer-only new main screen is not changed.

Verified: :wallet:assembleFdroidSim and :wallet:testFdroidSimUnitTest pass; after-android-1.png is the same wallet with the patched build.

Related: 0011978 (wallet-core does not raise incoming-kyc for peer-to-peer credits yet; once it does, this covers those too).

Assisted by LLM
Steps To Reproduce1. Withdraw KUDOS 5 from the KUDOS test exchange https://tripwire.taler.net:8445/ (bank :8446), confirm at the bank: the withdrawal waits for KYC.
2. Accept a bank-integrated withdrawal from https://bank.demo.taler.net/ in the wallet, do not confirm it at the bank.
3. Balances: "+5.00 incoming" and "+3.00 incoming", no hint that the user has to act.
TagsNo tags attached.
Attached Files
before-android-1.png (114,394 bytes)   
before-android-1.png (114,394 bytes)   
before-web-1.png (97,541 bytes)   
before-web-1.png (97,541 bytes)   
before-web-2.png (40,677 bytes)   
before-web-2.png (40,677 bytes)   
before-ios-1.png (250,780 bytes)   
before-ios-1.png (250,780 bytes)   
after-android-1.png (131,988 bytes)   
after-android-1.png (131,988 bytes)   
android-F404-balance-flags.patch (5,890 bytes)   
diff --git a/wallet/src/main/java/net/taler/wallet/balances/Balances.kt b/wallet/src/main/java/net/taler/wallet/balances/Balances.kt
index d2a57099..01595f47 100644
--- a/wallet/src/main/java/net/taler/wallet/balances/Balances.kt
+++ b/wallet/src/main/java/net/taler/wallet/balances/Balances.kt
@@ -30,8 +30,22 @@ data class BalanceItem(
     val pendingOutgoing: Amount,
     val disablePeerPayments: Boolean,
     val shoppingUrls: List<String> = emptyList(),
+    /**
+     * wallet-core's BalanceFlag values, kept as strings so that a flag
+     * added later cannot break decoding the whole balance list.
+     */
+    val flags: List<String> = emptyList(),
 ) {
     val currency: String get() = available.currency
+
+    /** Pending incoming money waits for the user to pass KYC. */
+    val incomingKyc: Boolean get() = "incoming-kyc" in flags
+
+    /** Pending incoming money waits for the user to confirm at the bank. */
+    val incomingConfirmation: Boolean get() = "incoming-confirmation" in flags
+
+    /** Pending outgoing money waits for the user to pass KYC. */
+    val outgoingKyc: Boolean get() = "outgoing-kyc" in flags
 }
 
 @Serializable
diff --git a/wallet/src/main/java/net/taler/wallet/balances/LegacyBalancesComposable.kt b/wallet/src/main/java/net/taler/wallet/balances/LegacyBalancesComposable.kt
index 06bdfa07..fdc7b36d 100644
--- a/wallet/src/main/java/net/taler/wallet/balances/LegacyBalancesComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/balances/LegacyBalancesComposable.kt
@@ -16,6 +16,7 @@
 
 package net.taler.wallet.balances
 
+import androidx.annotation.StringRes
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.animateContentSize
 import androidx.compose.foundation.background
@@ -23,10 +24,12 @@ import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.consumeWindowInsets
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.items
 import androidx.compose.material.icons.Icons
@@ -344,23 +347,37 @@ fun LegacyPendingComposable(
             Column(modifier = Modifier.padding(vertical = 5.dp)) {
                 ProvideTextStyle(MaterialTheme.typography.bodyLarge) {
                     AnimatedVisibility(!balance.pendingIncoming.isZero()) {
-                        Text(
-                            stringResource(
-                                R.string.balances_inbound_amount,
-                                balance.pendingIncoming.toString(showSymbol = false),
-                            ),
-                            color = TalerTheme.extraColors.success,
-                        )
+                        Column {
+                            Text(
+                                stringResource(
+                                    R.string.balances_inbound_amount,
+                                    balance.pendingIncoming.toString(showSymbol = false),
+                                ),
+                                color = TalerTheme.extraColors.success,
+                            )
+                            // the money only arrives once the user acts
+                            if (balance.incomingKyc) {
+                                PendingActionRequired(R.string.transactions_required_kyc)
+                            }
+                            if (balance.incomingConfirmation) {
+                                PendingActionRequired(R.string.transaction_state_pending_bank)
+                            }
+                        }
                     }
 
                     AnimatedVisibility(!balance.pendingOutgoing.isZero()) {
-                        Text(
-                            stringResource(
-                                R.string.balances_outbound_amount,
-                                balance.pendingOutgoing.toString(showSymbol = false)
-                            ),
-                            color = MaterialTheme.colorScheme.error,
-                        )
+                        Column {
+                            Text(
+                                stringResource(
+                                    R.string.balances_outbound_amount,
+                                    balance.pendingOutgoing.toString(showSymbol = false)
+                                ),
+                                color = MaterialTheme.colorScheme.error,
+                            )
+                            if (balance.outgoingKyc) {
+                                PendingActionRequired(R.string.transactions_required_kyc)
+                            }
+                        }
                     }
                 }
             }
@@ -374,6 +391,27 @@ fun LegacyPendingComposable(
     )
 }
 
+@Composable
+private fun PendingActionRequired(@StringRes text: Int) {
+    Row(
+        modifier = Modifier.padding(top = 2.dp),
+        verticalAlignment = Alignment.CenterVertically,
+    ) {
+        Icon(
+            Icons.Default.Warning,
+            contentDescription = null,
+            modifier = Modifier.size(16.dp),
+            tint = MaterialTheme.colorScheme.error,
+        )
+        Text(
+            text = stringResource(text),
+            modifier = Modifier.padding(start = 4.dp),
+            style = MaterialTheme.typography.bodyMedium,
+            color = MaterialTheme.colorScheme.error,
+        )
+    }
+}
+
 @Preview
 @Composable
 fun LegacyBalancesComposablePreview() {

Required notifications

Address or linkRemarkAdded byNotifiedActions

Activities

Issue History

Date ModifiedUsernameFieldChange
2026-11-11 01:41bot-cgNew Issue
2026-11-11 01:41bot-cgFile Added: before-android-1.png
2026-11-11 01:41bot-cgFile Added: before-web-1.png
2026-11-11 01:41bot-cgFile Added: before-web-2.png
2026-11-11 01:41bot-cgFile Added: before-ios-1.png
2026-11-11 01:41bot-cgFile Added: after-android-1.png
2026-11-11 01:41bot-cgFile Added: android-F404-balance-flags.patch