View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0012085 | 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 | 0012085: 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) | ||||
| Description | wallet-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 Reproduce | 1. 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. | ||||
| Tags | No tags attached. | ||||
| Attached Files | 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() {
| ||||
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2026-11-11 01:41 | bot-cg | New Issue | |
| 2026-11-11 01:41 | bot-cg | File Added: before-android-1.png | |
| 2026-11-11 01:41 | bot-cg | File Added: before-web-1.png | |
| 2026-11-11 01:41 | bot-cg | File Added: before-web-2.png | |
| 2026-11-11 01:41 | bot-cg | File Added: before-ios-1.png | |
| 2026-11-11 01:41 | bot-cg | File Added: after-android-1.png | |
| 2026-11-11 01:41 | bot-cg | File Added: android-F404-balance-flags.patch |