View Issue Details

IDProjectCategoryView StatusLast Update
0012087Talerwallet (WebExtension)public2026-11-11 01:41
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 
Summary0012087: WebExtension: editing a saved bank account only offers the raw payto:// URI -- Android and iOS reopen the structured form (patch attached)
DescriptionSettings -> Bank accounts -> "Edit" in the web wallet does not reopen the account in the form used to add it. It always switches to the "Advanced payto URI" method and shows a single monospace text box with the stored URI (e.g. payto://x-taler-bank/sandcastle/alice?receiver-name=Alice%20Example) plus Label and Currencies; the account-type chooser is hidden. To change the holder or the account number the user has to edit a percent-encoded URI; removing "?receiver-name=…" is accepted and the account silently loses its holder, and a typo only yields "Enter a valid supported payto URI." Android and iOS open the same account in their structured form, pre-filled.

Screenshots (demo backend, account added through the web form as "Taler-enabled bank", holder Alice Example, bank sandcastle, account alice):
- before-web-1-list.png: the list; note also that accounts the wallet learned from withdrawals have no label, so their title is the raw payto URI.
- before-web-2-edit.png: Edit -> "Payto URI" box only.
- before-web-3-holder-removed.png: after deleting receiver-name and saving, the account has no holder.
- before-android-1-edit.png, before-ios-1-edit.png (reference): holder, bank/IBAN and account fields pre-filled.
- after-web-2-edit.png: with the patch, Edit shows Account holder name / Bank / Account name / Label pre-filled.
- after-web-3-saved.png: holder changed to "Alice Example-Smith" and saved through the guided form.

Code (taler-typescript-core origin/master bf2e571a2, packages/wallet-webui/src/):
- screens/BankAccountsScreen.tsx:75-77 and 107-116: method forced to "advanced" whenever props.editing is set
- screens/BankAccountsScreen.tsx:311-329: account-type select only when not editing
- screens/BankAccountsScreen.tsx:477-492: the "Payto URI" input
- routes/management-model.ts:240-241: "advanced" passes the URI through; routes/ManagementRoutes.tsx:580-632: only the guided IBAN path validates and converts
For comparison: Android accounts/AddAccountComposable.kt:93-118 (pre-fills from the parsed payto), iOS Views/Settings/Bank/BankEditView.swift:81-101.

Patch (attached, web-bank-account-structured-edit.patch, BankAccountsScreen.tsx only): a helper parses the saved URI with Paytos.fromString() and pre-fills the guided form when editing -- IBAN: holder, IBAN, town, postal code; x-taler-bank: holder, bank, account; bitcoin: address. It falls back to the advanced payto field when the type is not among the offered methods, when there is no holder, or when the bank host is not one of the exchange's hosts (the bank field is a select over those). Saving goes through the existing guided paths (IBAN conversion/validation, constructPayto). No new strings.

Verification: Chrome extension build; after-web-2-edit.png / after-web-3-saved.png. pnpm typecheck: same 29 pre-existing errors in test/*.test.tsx with and without the patch, none in the touched file; prettier clean.

Related: 0012053 (focus on Edit; the form it opens is the one changed here).

Assisted by LLM
Steps To Reproduce1. Settings -> Bank accounts -> Add bank account: "Taler-enabled bank", holder, bank, account name, label -> Add account.
2. Press "Edit" on that account: only a "Payto URI" field is offered.
TagsNo tags attached.
Attached Files
before-web-1-list.png (185,666 bytes)   
before-web-1-list.png (185,666 bytes)   
before-web-2-edit.png (119,104 bytes)   
before-web-2-edit.png (119,104 bytes)   
before-web-3-holder-removed.png (148,744 bytes)   
before-web-3-holder-removed.png (148,744 bytes)   
before-android-1-edit.png (81,802 bytes)   
before-android-1-edit.png (81,802 bytes)   
before-ios-1-edit.png (176,567 bytes)   
before-ios-1-edit.png (176,567 bytes)   
after-web-2-edit.png (110,869 bytes)   
after-web-2-edit.png (110,869 bytes)   
after-web-3-saved.png (157,288 bytes)   
after-web-3-saved.png (157,288 bytes)   
web-bank-account-structured-edit.patch (4,078 bytes)   
diff --git a/packages/wallet-webui/src/screens/BankAccountsScreen.tsx b/packages/wallet-webui/src/screens/BankAccountsScreen.tsx
index 051179b24..923aa87d1 100644
--- a/packages/wallet-webui/src/screens/BankAccountsScreen.tsx
+++ b/packages/wallet-webui/src/screens/BankAccountsScreen.tsx
@@ -1,4 +1,5 @@
 import { useEffect, useMemo, useRef, useState } from "preact/hooks";
+import { Paytos, PaytoType, Result } from "@gnu-taler/taler-util";
 import { Button } from "../ui/Button.js";
 import { Card } from "../ui/Card.js";
 import { ErrorCard } from "../ui/ErrorCard.js";
@@ -47,6 +48,79 @@ function supportedMethod(
   return value === "iban" || value === "x-taler-bank" || value === "bitcoin";
 }
 
+interface BankAccountFields {
+  method: GuidedMethod;
+  receiverName: string;
+  receiverTown: string;
+  receiverPostalCode: string;
+  accountField: string;
+  bankHost: string;
+  bankAccount: string;
+  bitcoinAddress: string;
+}
+
+/**
+ * The form fields for a saved account, so that editing uses the same guided
+ * form as adding; a URI that the guided methods cannot represent stays raw.
+ */
+function bankAccountFields(
+  account: BankAccountView | undefined,
+  guidedMethods: GuidedMethod[],
+  wireTypes: BankAccountWireTypeView[] | undefined,
+): BankAccountFields {
+  const fields: BankAccountFields = {
+    method: account ? "advanced" : (guidedMethods[0] ?? "advanced"),
+    receiverName: "",
+    receiverTown: "",
+    receiverPostalCode: "",
+    accountField: "",
+    bankHost: "",
+    bankAccount: "",
+    bitcoinAddress: "",
+  };
+  if (!account) return fields;
+  const parsed = Paytos.fromString(account.paytoUri);
+  if (Result.isError(parsed)) return fields;
+  const payto = parsed.value;
+  const holder = Paytos.getAccountHolder(payto) ?? "";
+  switch (payto.targetType) {
+    case PaytoType.IBAN:
+      if (!guidedMethods.includes("iban") || !holder) return fields;
+      return {
+        ...fields,
+        method: "iban",
+        receiverName: holder,
+        accountField: payto.iban,
+        receiverTown: payto.params["receiver-town"] ?? "",
+        receiverPostalCode: payto.params["receiver-postal-code"] ?? "",
+      };
+    case PaytoType.TalerBank: {
+      const hosts =
+        wireTypes?.find((entry) => entry.paymentTargetType === "x-taler-bank")
+          ?.talerBankHostnames ?? [];
+      // the bank field is a choice among these hosts when there are any
+      if (
+        !guidedMethods.includes("x-taler-bank") ||
+        !holder ||
+        (hosts.length > 0 && !hosts.includes(payto.host))
+      )
+        return fields;
+      return {
+        ...fields,
+        method: "x-taler-bank",
+        receiverName: holder,
+        bankHost: payto.host,
+        bankAccount: payto.account,
+      };
+    }
+    case PaytoType.Bitcoin:
+      if (!guidedMethods.includes("bitcoin")) return fields;
+      return { ...fields, method: "bitcoin", bitcoinAddress: payto.address };
+    default:
+      return fields;
+  }
+}
+
 export function BankAccountsScreen(props: {
   accounts?: BankAccountView[];
   wireTypes?: BankAccountWireTypeView[];
@@ -112,8 +186,20 @@ export function BankAccountsScreen(props: {
     setLabel(props.editing?.label ?? "");
     setCurrencies(props.editing?.currencies.join(", ") ?? "");
     setPaytoUri(props.editing?.paytoUri ?? "");
-    setMethod(props.editing ? "advanced" : (guidedMethods[0] ?? "advanced"));
-  }, [guidedMethods, props.editing]);
+    const fields = bankAccountFields(
+      props.editing,
+      guidedMethods,
+      props.wireTypes,
+    );
+    setMethod(fields.method);
+    setReceiverName(fields.receiverName);
+    setReceiverTown(fields.receiverTown);
+    setReceiverPostalCode(fields.receiverPostalCode);
+    setAccountField(fields.accountField);
+    setBankHost(fields.bankHost);
+    setBankAccount(fields.bankAccount);
+    setBitcoinAddress(fields.bitcoinAddress);
+  }, [guidedMethods, props.editing, props.wireTypes]);
   const wireType = props.wireTypes?.find(
     (entry) => entry.paymentTargetType === method,
   );

Required notifications

Address or linkRemarkAdded byNotifiedActions

Relationships

related to 0012053 new Wallet: Bank accounts 'Edit' / 'Add bank account' leave focus on the button; the opened form can be off-screen 

Activities

Issue History

Date ModifiedUsernameFieldChange
2026-11-11 01:41bot-cgNew Issue
2026-11-11 01:41bot-cgRelationship addedrelated to 0012053
2026-11-11 01:41bot-cgFile Added: before-web-1-list.png
2026-11-11 01:41bot-cgFile Added: before-web-2-edit.png
2026-11-11 01:41bot-cgFile Added: before-web-3-holder-removed.png
2026-11-11 01:41bot-cgFile Added: before-android-1-edit.png
2026-11-11 01:41bot-cgFile Added: before-ios-1-edit.png
2026-11-11 01:41bot-cgFile Added: after-web-2-edit.png
2026-11-11 01:41bot-cgFile Added: after-web-3-saved.png
2026-11-11 01:41bot-cgFile Added: web-bank-account-structured-edit.patch