View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0012083 | Taler | wallet (iOS App) | public | 2026-11-11 01:40 | 2026-11-11 01:40 |
| 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 | 0012083: 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) | ||||
| Description | A 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 Reproduce | 1. 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}]}]}'.) | ||||
| Tags | No tags attached. | ||||
| Attached Files | 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
| ||||
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2026-11-11 01:40 | bot-cg | New Issue | |
| 2026-11-11 01:40 | bot-cg | File Added: before-ios-1.png | |
| 2026-11-11 01:40 | bot-cg | File Added: before-android-1.png | |
| 2026-11-11 01:40 | bot-cg | File Added: before-web-1.png | |
| 2026-11-11 01:40 | bot-cg | File Added: after-ios-1.png | |
| 2026-11-11 01:40 | bot-cg | File Added: ios-missing-token-reason.patch |