taler-typescript-core

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

commit bdc9d168abf0a74c7b491a0804cc8edf9447cb69
parent 367df7aa26633b99343cbb53de381af650f8e549
Author: Sebastian <sebasjm@taler-systems.com>
Date:   Mon,  8 Dec 2025 13:01:26 -0300

fix #10723

Diffstat:
Mpackages/taler-util/src/iban.ts | 15+++++++++++++++
Mpackages/taler-wallet-core/src/wallet.ts | 16++++------------
Mpackages/taler-wallet-webextension/src/components/PendingTransactions.tsx | 2+-
Mpackages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts | 3++-
Mpackages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts | 32++++++++++++++++++--------------
Mpackages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx | 39+++++++++++++++++++++++++++++++--------
6 files changed, 71 insertions(+), 36 deletions(-)

diff --git a/packages/taler-util/src/iban.ts b/packages/taler-util/src/iban.ts @@ -116,6 +116,21 @@ function mod97(digits: number[]): number { return modAccum; } +export function convertHUF_BBANtoIBAN(value: string) { + if (value.length <= 24) { + return opKnownFailure(ParseIbanError.TOO_SHORT); + } + if (value.length >= 16) { + return opKnownFailure(ParseIbanError.TOO_LONG); + } + if (/[0-9+]/.test(value)) { + return opKnownFailure(ParseIbanError.INVALID_CHARSET); + } + + const bban = value.length === 16 ? `${value}00000000` : value; + return opFixedSuccess<IbanString>(constructIban("HU", bban)); +} + /** * Check the IBAN is correct and return canonical form * @param ibanString diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts @@ -257,6 +257,7 @@ import { codecForValidateIbanRequest, codecForWithdrawTestBalance, constructIban, + convertHUF_BBANtoIBAN, encodeCrock, getErrorDetailFromException, getQrCodesForPayto, @@ -1946,20 +1947,11 @@ export async function handleConvertIbanAccountFieldToPayto( ): Promise<ConvertIbanAccountFieldToPaytoResponse> { const strippedInput = req.value.replace(/[- ]/g, ""); if (req.currency === "HUF") { - if ( - strippedInput.length <= 24 && - strippedInput.length >= 16 && - /[0-9+]/.test(strippedInput) - ) { - let bban: string; - if (strippedInput.length === 16) { - bban = `${strippedInput}00000000`; - } else { - bban = strippedInput; - } + const iban = convertHUF_BBANtoIBAN(strippedInput); + if (iban.type === "ok") { return { ok: true, - paytoUri: `payto://iban/${constructIban("HU", bban)}`, + paytoUri: iban.body, type: "iban", }; } else { diff --git a/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx b/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx @@ -48,7 +48,7 @@ export function PendingTransactions({ }: Props): VNode { const api = useBackendContext(); const state = useAsyncAsHook(() => - api.wallet.call(WalletApiOperation.GetTransactions, {}), + api.wallet.call(WalletApiOperation.GetTransactionsV2, {}), ); useEffect(() => { diff --git a/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts b/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { WalletBankAccountInfo, ScopeInfo, PaytoString } from "@gnu-taler/taler-util"; +import { WalletBankAccountInfo, ScopeInfo, PaytoString, WireTypeDetails } from "@gnu-taler/taler-util"; import { ErrorAlertView } from "../../components/CurrentAlerts.js"; import { Loading } from "../../components/Loading.js"; import { ErrorAlert } from "../../context/alert.js"; @@ -55,6 +55,7 @@ export namespace State { currency: string; accountType: SelectFieldHandler; uri: TextFieldHandler; + details: WireTypeDetails; alias: TextFieldHandler; onAccountAdded: ButtonHandler; onCancel: ButtonHandler; diff --git a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts @@ -19,6 +19,7 @@ import { PaytoString, stringifyPaytoUri, WalletBankAccountInfo, + WireTypeDetails, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; @@ -79,7 +80,7 @@ export function useComponentState({ }; } - if (hook2.response.wireTypes.length === 0) { + if (hook2.response.wireTypeDetails.length === 0) { return { status: "error", error: { @@ -94,16 +95,18 @@ export function useComponentState({ const [payto, setPayto] = useState(""); const [label, setLabel] = useState(""); - const [type, setType] = useState(hook2.response.wireTypes[0]); - - const accountType: Record<string, string> = {}; - hook2.response.wireTypes.forEach((t) => { - if (t === "iban") { - accountType[t] = "IBAN"; - } else if (t === "x-taler-bank") { - accountType[t] = "x-taler-bank"; - } else if (t === "bitcoin") { - accountType[t] = "Bitcoin"; + const [type, setType] = useState(hook2.response.wireTypeDetails[0]); + + const detailsByType: Record<string, WireTypeDetails> = {}; + const accountTypeName: Record<string, string> = {}; + hook2.response.wireTypeDetails.forEach((t) => { + detailsByType[t.paymentTargetType] = t; + if (t.paymentTargetType === "iban") { + accountTypeName[t.paymentTargetType] = "IBAN"; + } else if (t.paymentTargetType === "x-taler-bank") { + accountTypeName[t.paymentTargetType] = "x-taler-bank"; + } else if (t.paymentTargetType === "bitcoin") { + accountTypeName[t.paymentTargetType] = "Bitcoin"; } }); @@ -151,11 +154,12 @@ export function useComponentState({ status: "ready", error: undefined, currency: scope.currency, + details: type, accountType: { - list: accountType, - value: type, + list: accountTypeName, + value: type.paymentTargetType, onChange: pushAlertOnError(async (v) => { - setType(v); + setType(detailsByType[v]); }), }, selectAccount: async (w) => { diff --git a/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx b/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx @@ -16,6 +16,7 @@ import { BtAddrString, + convertHUF_BBANtoIBAN, HostPortPath, IbanString, InternationalizationAPI, @@ -28,7 +29,8 @@ import { PaytoUriIBAN, PaytoUriTalerBank, TranslatedString, - WalletBankAccountInfo + WalletBankAccountInfo, + WireTypeDetails, } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { styled } from "@linaria/react"; @@ -44,7 +46,10 @@ import { State } from "./index.js"; type AccountType = "bitcoin" | "x-taler-bank" | "iban"; type ComponentFormByAccountType = { - [type in AccountType]: (props: { field: TextFieldHandler }) => VNode; + [type in AccountType]: (props: { + field: TextFieldHandler; + details: WireTypeDetails; + }) => VNode; }; type ComponentListByAccountType = { @@ -52,6 +57,7 @@ type ComponentListByAccountType = { list: WalletBankAccountInfo[]; onDelete: (a: WalletBankAccountInfo) => Promise<void>; onSelect: (ac: WalletBankAccountInfo) => void; + details: WireTypeDetails; }) => VNode; }; @@ -86,6 +92,7 @@ export function ReadyView({ error, accountType, accountByType, + details, alias, onAccountAdded, deleteAccount, @@ -141,6 +148,7 @@ export function ReadyView({ <CustomFieldByAccountType type={accountType.value as AccountType} field={uri} + details={details} /> </p> </div> @@ -181,6 +189,7 @@ export function ReadyView({ list={list} onDelete={deleteAccount} onSelect={selectAccount} + details={details} /> ); })} @@ -467,8 +476,10 @@ function undefinedIfEmpty<T extends object>(obj: T): T | undefined { function TalerBankAddressAccount({ field, + details, }: { field: TextFieldHandler; + details: WireTypeDetails; }): VNode { const { i18n } = useTranslationContext(); const [host, setHost] = useState<string | undefined>(undefined); @@ -541,12 +552,19 @@ function translateIbanError( // const bicRegex = /^[A-Z]{6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3})?$/; // const ibanRegex = /^[A-Z]{2}[0-9]{2}[a-zA-Z0-9]{1,30}$/; -function IbanAddressAccount({ field }: { field: TextFieldHandler }): VNode { +function IbanAddressAccount({ + field, + details, +}: { + field: TextFieldHandler; + details: WireTypeDetails; +}): VNode { const { i18n } = useTranslationContext(); // const [bic, setBic] = useState<string | undefined>(undefined); const [iban, setIban] = useState<string | undefined>(undefined); const [name, setName] = useState<string | undefined>(undefined); const bic = ""; + const errorsFN = (iban: string | undefined, name: string | undefined) => undefinedIfEmpty({ // bic: !bic @@ -554,9 +572,7 @@ function IbanAddressAccount({ field }: { field: TextFieldHandler }): VNode { // : !bicRegex.test(bic) // ? i18n.str`Invalid bic` // : undefined, - iban: !iban - ? i18n.str`Required` - : validateIBAN(iban, i18n), + iban: !iban ? i18n.str`Required` : validateIBAN(iban, i18n, details), name: !name ? i18n.str`Can't be empty` : undefined, }); const errors = errorsFN(iban, name); @@ -634,9 +650,11 @@ function IbanAddressAccount({ field }: { field: TextFieldHandler }): VNode { function CustomFieldByAccountType({ type, field, + details, }: { type: AccountType; field: TextFieldHandler; + details: WireTypeDetails; }): VNode { // const { i18n } = useTranslationContext(); @@ -644,7 +662,7 @@ function CustomFieldByAccountType({ return ( <div> - <AccountForm field={field} /> + <AccountForm field={field} details={details} /> </div> ); } @@ -652,11 +670,16 @@ function CustomFieldByAccountType({ function validateIBAN( iban: string, i18n: ReturnType<typeof useTranslationContext>["i18n"], + details: WireTypeDetails, ): TranslatedString | undefined { if (!iban) { return i18n.str`Required`; } - const result = parseIban(iban); + + const result = + details.preferredEntryType === "bban" + ? convertHUF_BBANtoIBAN(iban) // FIXME: this only works for HU + : parseIban(iban); if (result.type === "ok") { return undefined; }