commit cd74fa6d5a6849eed5f1949eadc9e791678349a2
parent ee1abcd74821be2241a2d8505756a494a8256855
Author: Marc Stibane <marc@taler.net>
Date: Thu, 13 Aug 2026 16:18:24 +0200
AI: x-taler-bank payto
Diffstat:
2 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/TalerWallet1/Helper/URL+id+iban.swift b/TalerWallet1/Helper/URL+id+iban.swift
@@ -48,9 +48,13 @@ extension URL {
var xTaler: String? {
/// https://datatracker.ietf.org/doc/rfc8905/
- /// payto://iban/DE75512108001245126199?amount=EUR:200.0&message=hello
+ /// payto://x-taler-bank/BANK-HOST/ACCOUNT?receiver-name=…
+ /// The bank host is part of the target, so return "BANK-HOST/ACCOUNT" —
+ /// returning only lastPathComponent silently dropped the bank and made
+ /// the account unaddressable when it was written back out.
if scheme == "payto" && host == "x-taler-bank" {
- return lastPathComponent
+ let target = path.hasPrefix("/") ? String(path.dropFirst()) : path
+ return target.isEmpty ? nil : target
}
return nil
}
diff --git a/TalerWallet1/Views/Settings/Bank/BankEditView.swift b/TalerWallet1/Views/Settings/Bank/BankEditView.swift
@@ -106,9 +106,26 @@ struct BankEditView: View {
if !accountHolder.isEmpty && !accountLabel.isEmpty {
if isIBAN ? !iban.isEmpty
: !xTaler.isEmpty {
- let payto = isIBAN ? "payto://iban/\(iban)?receiver-name=\(accountHolder)" // TODO: convert BBAN to IBAN
- : "payto://xTaler/\(xTaler)?receiver-name=\(accountHolder)"
- let paytoUri = payto.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
+ // Build the payto URI with URLComponents so each component is escaped as a
+ // component. The previous code percent-encoded the ALREADY-ASSEMBLED string
+ // with .urlQueryAllowed, which leaves "?", "&" and "=" untouched — an
+ // account holder called "a&foo=bar" injected extra query parameters.
+
+ // TODO: for x-taler-bank the target must be BANK-HOST/ACCOUNT. Accounts read
+ // from an existing payto now round-trip with their host, but there is still
+ // no separate input field for it, so a hand-typed account without a host
+ // produces an incomplete target. See bugs.txt [14.2].
+ var components = URLComponents()
+ components.scheme = "payto"
+ components.host = isIBAN ? "iban" : "x-taler-bank"
+ let target = isIBAN ? iban.filter { !$0.isWhitespace }.uppercased() : xTaler
+ components.path = "/" + target
+ components.queryItems = [URLQueryItem(name: "receiver-name", value: accountHolder)]
+ guard let paytoUri = components.string else {
+ symLog.log("could not build a payto URI from the entered account")
+ showAlert = true
+ return false
+ }
symLog.log(paytoUri)
if let account = try? await model.addBankAccount(paytoUri,
label: accountLabel,