taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit f31e901f43e61b16ac1ce31753917f3c42cbbed0
parent 139b03f9afb5bb8b511c9dd6d09680c467560ba2
Author: Florian Dold <florian@dold.me>
Date:   Thu,  9 Jul 2026 19:34:34 +0200

fix subject/message confusion in payto URIs

Diffstat:
Mpackages/libeufin-bank-webui/src/pages/PaytoWireTransferForm.tsx | 1+
Mpackages/taler-util/src/payto.ts | 9++++++++-
Mpackages/taler-wallet-core/src/common.ts | 2+-
Mpackages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx | 32+++++++++++++++++++-------------
4 files changed, 29 insertions(+), 15 deletions(-)

diff --git a/packages/libeufin-bank-webui/src/pages/PaytoWireTransferForm.tsx b/packages/libeufin-bank-webui/src/pages/PaytoWireTransferForm.tsx @@ -297,6 +297,7 @@ export function PaytoWireTransferForm({ setAmount(Amounts.stringifyValue(amount)); } } + // FIXME: Why? Do we still need this fallback? const subject = !parsed.value.params["message"] ? parsed.value.params["subject"] : parsed.value.params["message"]; diff --git a/packages/taler-util/src/payto.ts b/packages/taler-util/src/payto.ts @@ -19,6 +19,7 @@ import { generateFakeSegwitAddress } from "./bitcoin.js"; import { Codec, Context, DecodingError, renderContext } from "./codec.js"; import { assertUnreachable } from "./errors.js"; import { IbanString, parseIban, ParseIbanError } from "./iban.js"; +import { AmountString } from "./index.node.js"; import { Result, ResultError, ResultOk } from "./result.js"; import { decodeCrock, @@ -965,6 +966,12 @@ export interface PaytoUriCyclos extends PaytoUriGeneric { account: string; } +export interface WellKnownPaytoParams { + amount?: AmountString; + message?: string; + ["ch-qrr"]?: string; +} + /** * Add query parameters to a payto URI. * @@ -972,7 +979,7 @@ export interface PaytoUriCyclos extends PaytoUriGeneric { */ export function addPaytoQueryParams( s: string, - params: { [name: string]: string }, + params: WellKnownPaytoParams, ): string { const [acct, search] = s.slice(PAYTO_PREFIX.length).split("?"); const searchParams = new URLSearchParams(search || ""); diff --git a/packages/taler-wallet-core/src/common.ts b/packages/taler-wallet-core/src/common.ts @@ -1181,7 +1181,7 @@ export async function prepareTransferOptionsRaw( }); if (reservePub != null) { paytoUri = addPaytoQueryParams(paytoUri, { - subject: `Taler ${reservePub}`, + message: `Taler ${reservePub}`, }); } return { diff --git a/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx b/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx @@ -96,7 +96,8 @@ export function BankDetailsForWithdrawal({ const accounts = unsortedAccounts.sort(accountOrder); const options = accounts.flatMap((account) => - account.transferOptions.sort(optionOrder)) + account.transferOptions.sort(optionOrder), + ); const selectedOption = options[index]; // const altCurrency = selectedOption.account.currencySpecification?.name; @@ -105,8 +106,8 @@ export function BankDetailsForWithdrawal({ case "ch-qr-bill": case "payto": { const payto = Result.unpack(Paytos.fromString(selectedOption.paytoUri)); - const amount = Amounts.parseOrThrow(payto.params["amount"]) - const subject = payto.params["subject"] + const amount = Amounts.parseOrThrow(payto.params["amount"]); + const subject = payto.params["message"]; // make sure the payto has the right params // payto.params["amount"] = altCurrency // ? selectedOption.account.transferAmount! @@ -250,18 +251,20 @@ export function BankDetailsForWithdrawal({ } export function BankDetailsForKycAuth({ - info -}: { info: KycAuthTransferInfo }): VNode { + info, +}: { + info: KycAuthTransferInfo; +}): VNode { const { i18n } = useTranslationContext(); const [index, setIndex] = useState(0); - const { transferOptions: unsortedOptions } = info + const { transferOptions: unsortedOptions } = info; if (!unsortedOptions.length) { return <div>the exchange account list is empty</div>; } - const options = unsortedOptions.sort(optionOrder) + const options = unsortedOptions.sort(optionOrder); const selectedOption = options[index]; @@ -269,8 +272,8 @@ export function BankDetailsForKycAuth({ case "ch-qr-bill": case "payto": { const payto = Result.unpack(Paytos.fromString(selectedOption.paytoUri)); - const amount = Amounts.parseOrThrow(payto.params["amount"]) - const subject = payto.params["subject"] + const amount = Amounts.parseOrThrow(payto.params["amount"]); + const subject = payto.params["message"]; // make sure the payto has the right params // payto.params["amount"] = altCurrency // ? selectedOption.account.transferAmount! @@ -422,7 +425,7 @@ export function IBANAccountInfoTable({ subject: string; qrCodes: QrCodeSpec[]; refCode?: string; - payto: Paytos.URI + payto: Paytos.URI; }) { const { i18n } = useTranslationContext(); const api = useBackendContext(); @@ -560,7 +563,7 @@ export function IBANAccountInfoTable({ ); case PaytoType.Bitcoin: case PaytoType.Ethereum: - return <Fragment /> + return <Fragment />; default: assertUnreachable(payto); @@ -809,7 +812,10 @@ function Frame({ {accounts.length > 1 ? ( <Fragment> {accounts.map((ac, acIdx) => { - const desc = ac.type === "ch-qr-bill" ? i18n.str`Swiss Bill` : i18n.str`Wire transfer` + const desc = + ac.type === "ch-qr-bill" + ? i18n.str`Swiss Bill` + : i18n.str`Wire transfer`; return ( <Button key={acIdx} @@ -818,7 +824,7 @@ function Frame({ updateIndex(acIdx); }} > - {acIdx+1}) {desc} + {acIdx + 1}) {desc} </Button> ); })}