View Issue Details

IDProjectCategoryView StatusLast Update
0012083Talerwallet (iOS App)public2026-11-11 01:40
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 
Summary0012083: iOS: an order that needs a discount/subscription token the wallet does not hold is "Payment not possible" without any reason -- token availability from wallet-core is ignored (patch attached)
DescriptionA v1 order whose payment option needs a merchant-issued token (a discount or subscription pass) that the wallet does not hold is shown on iOS as "Payment not possible" with no reason at all. wallet-core reports the token shortfall per choice (ChoiceSelectionDetail.tokenDetails: tokensRequested, tokensAvailable, perTokenFamily[slug].causeHint), and iOS decodes it, but no view reads it.

What the user sees (origin/master builds of 2026-11-11; single-choice order "KUDOS:1 + one wc-discount token", wallet holds KUDOS 19.50 and no token):
- iOS: title "Payment not possible", the option "member price, uses one discount token 1 KUDOS", "Spend now: wallet-compare discount pass x1", button "Cancel". Nothing says the token is missing; the amounts area is empty (before-ios-1.png).
- Android: the token card has a red error icon (tooltip "Insufficient tokens") and the option's button reads "Insufficient tokens" (before-android-1.png).
- WebExtension: "A required token is not available in this wallet.", under the token "0 of 1 available" and "Not enough of this token is available." (before-web-1.png).

Code (origin/master 674b7915):
- TalerWallet1/Model/Model+Payment.swift:399-428: TokenAvailabilityHint, TokenFamily, TokenDetails and ChoiceSelectionDetail.tokenDetails are decoded; grep tokenDetails/perTokenFamily in TalerWallet1/Views finds nothing.
- TalerWallet1/Views/Sheets/Payment/ChoicesView.swift:139-153: token chips without availability; :155-163 tapping an unpayable option does nothing.
- TalerWallet1/Views/Sheets/Payment/PaymentScan.swift:207-237: a reason is only derived from balanceDetails (coins); a token-only shortfall has no balanceDetails and lands in the empty "TODO: Error - neither effective nor balanceDetails" branch.
- wallet-core: pay-merchant.ts:3355-3366 (a token failure yields InsufficientBalance with tokenDetails and no balanceDetails when the coins suffice).
References: Android PromptPaymentComposable.kt:579-584, 718-777; web routes/payment-model.ts:173-198, 291-314 and screens/PaymentScreen.tsx:91-103.

Patch (ios-missing-token-reason.patch): when an option is not payable and tokenDetails.tokensAvailable < tokensRequested, ChoicesView shows "A required token is not available in this wallet." (WebExtension wording) under the "Spend now" tokens, in the error colour. One new English entry in Localizable.xcstrings.
Verified: GNU_Taler Release builds for the simulator; after-ios-1.png is the same order with the patch.

Possible follow-up, not in the patch: an unpayable option that consists of tokens only (amount 0) is hidden entirely (ChoicesView.swift:123); if it is the only option, the screen shows no option at all.

Assisted by LLM
Steps To Reproduce1. Merchant with a discount token family (e.g. slug wc-discount); create a v1 order with one choice: amount 1 plus input {type: token, token_family_slug: wc-discount}.
2. Open its taler://pay/ link in a wallet that has money but no such token.
3. iOS: "Payment not possible" without a reason.
(Automated: wc-stack pay-uri --order-json '{"version":1,"choices":[{"amount":"KUDOS:1","inputs":[{"type":"token","token_family_slug":"wc-discount","count":1}]}]}'.)
TagsNo tags attached.
Attached Files
before-ios-1.png (198,847 bytes)   
before-ios-1.png (198,847 bytes)   
before-android-1.png (88,474 bytes)   
before-android-1.png (88,474 bytes)   
before-web-1.png (124,478 bytes)   
before-web-1.png (124,478 bytes)   
after-ios-1.png (217,062 bytes)   
after-ios-1.png (217,062 bytes)   
ios-missing-token-reason.patch (1,709 bytes)   
diff --git a/TalerWallet1/Resources/Localizable.xcstrings b/TalerWallet1/Resources/Localizable.xcstrings
index e283ca45..83c20cc0 100755
--- a/TalerWallet1/Resources/Localizable.xcstrings
+++ b/TalerWallet1/Resources/Localizable.xcstrings
@@ -1880,6 +1880,9 @@
         }
       }
     },
+    "A required token is not available in this wallet." : {
+      "comment" : "payment option needs a merchant-issued discount or subscription token the wallet does not hold"
+    },
     "Abort" : {
       "comment" : "deposit",
       "localizations" : {
diff --git a/TalerWallet1/Views/Sheets/Payment/ChoicesView.swift b/TalerWallet1/Views/Sheets/Payment/ChoicesView.swift
index dbeb0dca..dcd94b73 100644
--- a/TalerWallet1/Views/Sheets/Payment/ChoicesView.swift
+++ b/TalerWallet1/Views/Sheets/Payment/ChoicesView.swift
@@ -143,6 +143,12 @@ struct ChoicesView: View, Sendable {
                                     .talerFont(.body)
                                     .padding(.bottom, -4)
                                 InputTokenView(inputs: inputs, tokenFamilies: tokenFamilies)
+                                if !isPaymentPossible, let tokenDetails = selectionDetail.tokenDetails,
+                                   tokenDetails.tokensAvailable < tokenDetails.tokensRequested {
+                                    Text("A required token is not available in this wallet.")
+                                        .talerFont(.body)
+                                        .foregroundColor(WalletColors().errorColor)
+                                }
                             }
                             // Receive output tokens
                             let outputs = contractChoice.outputs
ios-missing-token-reason.patch (1,709 bytes)   

Required notifications

Address or linkRemarkAdded byNotifiedActions

Activities

Issue History

Date ModifiedUsernameFieldChange
2026-11-11 01:40bot-cgNew Issue
2026-11-11 01:40bot-cgFile Added: before-ios-1.png
2026-11-11 01:40bot-cgFile Added: before-android-1.png
2026-11-11 01:40bot-cgFile Added: before-web-1.png
2026-11-11 01:40bot-cgFile Added: after-ios-1.png
2026-11-11 01:40bot-cgFile Added: ios-missing-token-reason.patch