View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0012098 | Taler | wallet (Android App) | public | 2026-11-11 01:56 | 2026-11-11 02:00 |
| 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 | 0012098: Android: during the exchange's AML review a KYC-blocked transaction still says "follow the instructions" / "Complete KYC" -- wallet-core's kycAmlReview is ignored (patch attached) | ||||
| Description | When a KYC-blocked transaction is under review by the exchange (the user already submitted the KYC data and the exchange put the account into AML review), wallet-core sets TransactionCommon.kycAmlReview = true. Android does not decode the field and keeps telling the user to act: "This transaction would exceed a limit set by the payment service. To increase the limit, follow the instructions linked below." and "Complete KYC", although the only thing the user can do is wait. The WebExtension says "Waiting for review / The exchange is reviewing your account. Please wait." with "View verification details". What the user sees (a KUDOS 5 withdrawal from the tripwire KUDOS test exchange, blocked on KYC; the exchange's AML review was simulated by replaying wallet-core's own transaction JSON with "kycAmlReview": true through the dev experiment api-response, because a test exchange cannot be put into AML review from the client side): - Android: limit text + "Complete KYC" (before-android-1.png); identical to the screen before the KYC data was submitted. - WebExtension, same JSON: state "Waiting for review", card "Waiting for review - The exchange is reviewing your account. Please wait." (before-web-1.png); without the flag the same transaction shows "Identity verification required - Complete verification" (before-web-0-no-review.png). - iOS ignores the field as well (separate, related report). Code (origin/master 4c483527): - wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt: TransactionWithdrawal (:165), TransactionPeerPullCredit (:620), TransactionPeerPushCredit (:690) have kycUrl but no kycAmlReview. - transactions/TransactionStateComposable.kt:67-69 (limit text), transactions/TransactionActionsComposable.kt:59-63, 132-136, 154 (ConfirmKycButton "Complete KYC"). - wallet-core: taler-util types-taler-wallet-transactions.ts:362-363 (kycAmlReview), taler-wallet-core kyc.ts:615-636, withdraw.ts:455/494, pay-peer-push-credit.ts:220-358, pay-peer-pull-credit.ts:282-421. - Reference: taler-typescript-core packages/wallet-webui/src/routes/transaction-model.ts:264-268, 743-756, 883-886. The patch (android-F406-kyc-aml-review.patch): decodes kycAmlReview for withdrawals and peer credits (Transaction.kycUnderReview helper); while it is set and the transaction is pending, the state banner says "The payment service is reviewing your information. Please wait." and the KYC button reads "View verification status" (it still opens the KYC page, where the status can be checked). Two new strings. The peer-transaction detail now passes the transaction to TransactionStateComposable as the withdrawal detail already does. Verified: :wallet:assembleFdroidSim and :wallet:testFdroidSimUnitTest pass; after-android-1.png is the same injected state with the patched build. Assisted by LLM | ||||
| Steps To Reproduce | 1. Withdraw more than the KYC threshold (e.g. KUDOS 5 from https://tripwire.taler.net:8445/), complete the KYC forms. 2. While the exchange reviews the account (kycAmlReview: true in getTransactionById), open the transaction: it still asks to complete KYC. (Without an exchange operator: developer mode, taler://dev-experiment/api-response?operation=getTransactionsV2&response=<the transaction list with kycAmlReview set>.) | ||||
| Tags | No tags attached. | ||||
| Attached Files | android-F406-kyc-aml-review.patch (7,293 bytes)
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionActionsComposable.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionActionsComposable.kt
index ec26567f..4aaa2a8d 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionActionsComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionActionsComposable.kt
@@ -57,7 +57,7 @@ fun ColumnScope.WithdrawalTransfers(
}
KycRequired -> if (tx.kycUrl != null) {
- ConfirmKycButton {
+ ConfirmKycButton(underReview = tx.kycUnderReview) {
onConfirmKyc(tx.kycUrl)
}
}
@@ -130,7 +130,7 @@ fun ColumnScope.DepositActions(
}
KycRequired -> if (tx.kycUrl != null) {
- ConfirmKycButton {
+ ConfirmKycButton(underReview = tx.kycUnderReview) {
onConfirmKyc(tx.kycUrl)
}
}
@@ -151,7 +151,7 @@ fun ColumnScope.PeerActions(
} ?: return
if (tx.txState.minor == KycRequired) {
- ConfirmKycButton { onConfirmKyc(kycUrl) }
+ ConfirmKycButton(underReview = tx.kycUnderReview) { onConfirmKyc(kycUrl) }
}
}
@@ -196,7 +196,7 @@ private fun ShowQrCodesButton(onClick: () -> Unit) {
}
@Composable
-private fun ConfirmKycButton(onClick: () -> Unit) {
+private fun ConfirmKycButton(underReview: Boolean = false, onClick: () -> Unit) {
Button(onClick) {
Icon(
Icons.Default.Link,
@@ -204,6 +204,8 @@ private fun ConfirmKycButton(onClick: () -> Unit) {
modifier = Modifier.size(ButtonDefaults.IconSize)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
- Text(stringResource(R.string.transaction_action_kyc))
+ Text(stringResource(
+ if (underReview) R.string.transaction_action_kyc_status else R.string.transaction_action_kyc
+ ))
}
}
\ No newline at end of file
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailScreen.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailScreen.kt
index c658eab3..4ba5ca73 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailScreen.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailScreen.kt
@@ -426,7 +426,7 @@ fun TransactionPeerComposable(
.verticalScroll(scrollState),
horizontalAlignment = Alignment.CenterHorizontally,
) {
- TransactionStateComposable(state = t.txState)
+ TransactionStateComposable(state = t.txState, tx = t)
Text(
modifier = Modifier.padding(16.dp),
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionStateComposable.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionStateComposable.kt
index 972e1911..66b8de29 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionStateComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionStateComposable.kt
@@ -61,7 +61,10 @@ fun TransactionStateComposable(
state: TransactionState,
tx: Transaction? = null,
) {
- val message = when (state) {
+ val underReview = tx?.kycUnderReview == true && state.major == Pending
+ val message = if (underReview) {
+ stringResource(R.string.transaction_state_pending_kyc_review)
+ } else when (state) {
TransactionState(Done) -> stringResource(R.string.transaction_state_done)
TransactionState(Pending, BankConfirmTransfer) -> stringResource(R.string.transaction_state_pending_bank)
TransactionState(Pending, KycInit) -> stringResource(R.string.transaction_preparing_kyc)
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
index d5d397c5..7f26ae7c 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
@@ -133,6 +133,18 @@ sealed class Transaction {
}
@Serializable
+/**
+ * The exchange reports that it is reviewing the user's KYC information,
+ * so there is nothing the user can do but wait.
+ */
+val Transaction.kycUnderReview: Boolean
+ get() = when (this) {
+ is TransactionWithdrawal -> kycAmlReview == true
+ is TransactionPeerPullCredit -> kycAmlReview == true
+ is TransactionPeerPushCredit -> kycAmlReview == true
+ else -> false
+ }
+
enum class TransactionAction {
// Common States
@SerialName("delete")
@@ -168,6 +180,7 @@ data class TransactionWithdrawal(
override val txState: TransactionState,
override val txActions: List<TransactionAction>,
val kycUrl: String? = null,
+ val kycAmlReview: Boolean? = null,
val exchangeBaseUrl: String? = null,
val withdrawalDetails: WithdrawalDetails,
override val error: TalerErrorInfo? = null,
@@ -625,6 +638,7 @@ data class TransactionPeerPullCredit(
override val txState: TransactionState,
override val txActions: List<TransactionAction>,
val kycUrl: String? = null,
+ val kycAmlReview: Boolean? = null,
val exchangeBaseUrl: String,
override val error: TalerErrorInfo? = null,
override val amountRaw: Amount,
@@ -693,6 +707,7 @@ data class TransactionPeerPushCredit(
override val txState: TransactionState,
override val txActions: List<TransactionAction>,
val kycUrl: String? = null,
+ val kycAmlReview: Boolean? = null,
val exchangeBaseUrl: String,
override val error: TalerErrorInfo? = null,
override val amountRaw: Amount,
diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml
index 89750709..8f11e499 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -183,6 +183,7 @@ GNU Taler is immune to many types of fraud such as credit card data theft, phish
<!-- Transactions -->
<string name="transaction_action_kyc">Complete KYC</string>
+ <string name="transaction_action_kyc_status">View verification status</string>
<string name="transaction_denom_loss">Loss of funds</string>
<string name="transaction_deposit">Deposit</string>
<string name="transaction_deposit_to">Deposit to %1$s</string>
@@ -212,6 +213,7 @@ GNU Taler is immune to many types of fraud such as credit card data theft, phish
<string name="transaction_state_pending">This transaction is pending</string>
<string name="transaction_state_pending_bank">Waiting for authorization in the bank</string>
<string name="transaction_state_pending_kyc_bank">This transaction would exceed a limit set by the payment service. To increase the limit, follow the instructions linked below.</string>
+ <string name="transaction_state_pending_kyc_review">The payment service is reviewing your information. Please wait.</string>
<string name="transaction_state_pending_kyc_auth">You need to prove having control over the bank account for the deposit. To continue, follow the instructions linked below.</string>
<string name="transaction_state_suspended">This transaction is suspended</string>
<string name="transactions_abort">Abort</string>
| ||||
| related to | 0012103 | new | iOS: during the exchange's AML review a KYC-blocked transaction still says "You need to pass a legitimization procedure" -- wallet-core's kycAmlReview is ignored (patch attached) |
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2026-11-11 01:56 | bot-cg | New Issue | |
| 2026-11-11 01:56 | bot-cg | File Added: before-android-1.png | |
| 2026-11-11 01:56 | bot-cg | File Added: before-web-1.png | |
| 2026-11-11 01:56 | bot-cg | File Added: before-web-0-no-review.png | |
| 2026-11-11 01:56 | bot-cg | File Added: after-android-1.png | |
| 2026-11-11 01:56 | bot-cg | File Added: android-F406-kyc-aml-review.patch |