View Issue Details

IDProjectCategoryView StatusLast Update
0012079Talerwallet (iOS App)public2026-11-11 01:37
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 
Summary0012079: iOS: an order already claimed by another wallet (or deleted by the merchant) shows only "Abandoned" -- the reason is never displayed (patch attached)
DescriptionOpening the pay link of an order that another wallet has already claimed (a QR code scanned with a second phone, a link opened twice), or of an order the merchant has deleted or that never existed, ends in a failed claim. wallet-core keeps it as a FailedClaim transaction (state failed/claim-proposal, actions [delete]) with failReason = WALLET_ORDER_ALREADY_CLAIMED (7014) or WALLET_MERCHANT_ORDER_NOT_FOUND (7049) (pay-merchant.ts:1576-1606, failProposalClaimPermanently :1236-1266). The WebExtension explains it; Android and iOS only say that the transaction failed.

What the user sees (origin/master builds of 2026-11-11; the order is claimed first by a taler-wallet-cli wallet, resp. deleted at the merchant before the link is opened):
- WebExtension: "Payment could not continue" -- "This order was already claimed by another wallet." -- "Return to the merchant and request a new order." (before-web-1.png); deleted order: "The merchant order no longer exists or has expired." -- "Return to the merchant and create a new order." (before-web-2.png).
- Android: "This transaction has failed or been abandoned", date, "Total UNKNOWN 0.00", "Paid UNKNOWN 0.00", Delete (before-android-1.png, before-android-2.png). The placeholder amounts come from wallet-core's contract-less transaction.
- iOS: "Payment", date, "Abandoned", Done -- nothing else (before-ios-1.png, before-ios-2.png).

On iOS (origin/master 674b7915):
- TalerWallet1/Views/Transactions/PaymentTransactionView.swift:150-152, 172, 242: the view renders the dialog branch or, if !isClaiming, the finished payment. A failed claim keeps minor state claim-proposal, so neither branch runs and nothing is shown.
- TalerWallet1/Views/Transactions/PaidTransactionDetailV.swift:96-101 would print failReason.hint, but is unreachable here.
- TalerWallet1/Views/Transactions/TransactionSummaryList.swift:170-187: only a *deleted* transition raises an error.
Reference: web routes/App.tsx:4416-4432, routes/transaction-model.ts:383-394, ui/error.ts:24-29, 83-88.

Patch (ios-claimed-or-deleted-order-no-reason.patch): PaymentTransactionView gets a branch for a failed claim (ClaimFailureView): 7014 -> "This order was already claimed by another wallet." / "Return to the merchant and request a new order."; 7049 -> "The merchant order no longer exists or has expired." / "Return to the merchant and create a new order."; otherwise failReason.hint. Four new English entries in Localizable.xcstrings.
Verified: GNU_Taler Release builds for the simulator; after-ios-1.png and after-ios-2.png are the same two scenarios with the patch.

Related: 0012068 (generic: transaction error never shown), 0012078 (Android counterpart).

Assisted by LLM
Steps To Reproduce1. Create an order (any merchant) and open its taler://pay/ link in one wallet (or claim it with taler-wallet-cli), without paying.
2. Open the same link in the wallet under test: failed/Abandoned without a reason.
3. Variant: delete the order at the merchant (or change the order id in the link) and open it.
(Automated: U=$(wc-stack pay-uri --amount KUDOS:1); wc-stack claim-elsewhere "$U"; wc-android uri "$U".)
TagsNo tags attached.
Attached Files
before-android-1.png (61,442 bytes)   
before-android-1.png (61,442 bytes)   
before-ios-1.png (121,529 bytes)   
before-ios-1.png (121,529 bytes)   
before-web-1.png (62,560 bytes)   
before-web-1.png (62,560 bytes)   
before-android-2.png (61,059 bytes)   
before-android-2.png (61,059 bytes)   
before-ios-2.png (121,544 bytes)   
before-ios-2.png (121,544 bytes)   
before-web-2.png (62,248 bytes)   
before-web-2.png (62,248 bytes)   
after-ios-1.png (157,838 bytes)   
after-ios-1.png (157,838 bytes)   
after-ios-2.png (157,649 bytes)   
after-ios-2.png (157,649 bytes)   
ios-claimed-or-deleted-order-no-reason.patch (3,505 bytes)   
diff --git a/TalerWallet1/Resources/Localizable.xcstrings b/TalerWallet1/Resources/Localizable.xcstrings
index e283ca45..1aba8779 100755
--- a/TalerWallet1/Resources/Localizable.xcstrings
+++ b/TalerWallet1/Resources/Localizable.xcstrings
@@ -16830,6 +16830,12 @@
         }
       }
     },
+    "Return to the merchant and create a new order." : {
+      "comment" : "below 'The merchant order no longer exists or has expired.'"
+    },
+    "Return to the merchant and request a new order." : {
+      "comment" : "below 'This order was already claimed by another wallet.'"
+    },
     "Right now" : {
       "localizations" : {
         "de" : {
@@ -19138,6 +19144,9 @@
         }
       }
     },
+    "The merchant order no longer exists or has expired." : {
+      "comment" : "payment: the merchant does not know the order (anymore)"
+    },
     "The payment service is waiting for your wire-transfer." : {
       "localizations" : {
         "de" : {
@@ -19986,6 +19995,9 @@
         }
       }
     },
+    "This order was already claimed by another wallet." : {
+      "comment" : "payment: the merchant order was claimed by another wallet first"
+    },
     "This payment service has not published any terms of service." : {
       "comment" : "A message displayed when a payment service has not published any terms of service.",
       "isCommentAutoGenerated" : true,
diff --git a/TalerWallet1/Views/Transactions/PaymentTransactionView.swift b/TalerWallet1/Views/Transactions/PaymentTransactionView.swift
index 1f23d124..c1520836 100755
--- a/TalerWallet1/Views/Transactions/PaymentTransactionView.swift
+++ b/TalerWallet1/Views/Transactions/PaymentTransactionView.swift
@@ -90,6 +90,34 @@ struct MerchantHeader: View {
     }
 }
 // MARK: -
+/// Why the wallet could not claim an order, e.g. because another wallet already claimed it
+struct ClaimFailureView: View {
+    let failReason: TalerErrorDetail?
+
+    var body: some View {
+        let (message, hint): (String?, String?) = switch failReason?.code {
+            case 7014:      // WALLET_ORDER_ALREADY_CLAIMED
+                (String(localized: "This order was already claimed by another wallet."),
+                 String(localized: "Return to the merchant and request a new order."))
+            case 7049:      // WALLET_MERCHANT_ORDER_NOT_FOUND
+                (String(localized: "The merchant order no longer exists or has expired."),
+                 String(localized: "Return to the merchant and create a new order."))
+            default:
+                (failReason?.hint, nil)
+        }
+        if let message {
+            Section {
+                Text(message)
+                    .talerFont(.title3)
+                if let hint {
+                    Text(hint)
+                        .talerFont(.body)
+                }
+            }
+        }
+    }
+}
+// MARK: -
 struct PaymentTransactionView: View {
     private let symLog = SymLogV(0)
     let stack: CallStack
@@ -239,6 +267,8 @@ struct PaymentTransactionView: View {
                     // TODO: purge and reload
                 }
             }
+        } else if common.isFailed {  // the claim failed permanently: no contract, but a reason
+            ClaimFailureView(failReason: common.failReason)
         } else if isNotClaiming { // show finished payment
             PaidTransactionDetailV(stack: stack.push(),
                                paymentTx: paymentTransaction)           // TODO: details.info.merchant.name

Required notifications

Address or linkRemarkAdded byNotifiedActions

Relationships

related to 0012078 new Android: an order already claimed by another wallet (or deleted by the merchant) shows only "This transaction has failed or been abandoned" and "UNKNOWN 0.00" -- the reason is dropped (regression of 0009946, patch attached) 

Activities

Issue History

Date ModifiedUsernameFieldChange
2026-11-11 01:36bot-cgNew Issue
2026-11-11 01:36bot-cgRelationship addedrelated to 0012078
2026-11-11 01:37bot-cgFile Added: before-android-1.png
2026-11-11 01:37bot-cgFile Added: before-ios-1.png
2026-11-11 01:37bot-cgFile Added: before-web-1.png
2026-11-11 01:37bot-cgFile Added: before-android-2.png
2026-11-11 01:37bot-cgFile Added: before-ios-2.png
2026-11-11 01:37bot-cgFile Added: before-web-2.png
2026-11-11 01:37bot-cgFile Added: after-ios-1.png
2026-11-11 01:37bot-cgFile Added: after-ios-2.png
2026-11-11 01:37bot-cgFile Added: ios-claimed-or-deleted-order-no-reason.patch