/* This file is part of GNU Taler (C) 2022 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ import { Logger } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import { StateUpdater, useEffect, useRef } from "preact/hooks"; import { useBackendContext } from "../context/backend.js"; import { PageStateType, usePageContext } from "../context/pageState.js"; import { InternationalizationAPI, useTranslationContext, } from "@gnu-taler/web-util/lib/index.browser"; import { BackendState } from "../hooks/backend.js"; import { prepareHeaders, validateAmount } from "../utils.js"; const logger = new Logger("WalletWithdrawForm"); export function WalletWithdrawForm({ focus, currency, }: { currency?: string; focus?: boolean; }): VNode { const backend = useBackendContext(); const { pageState, pageStateSetter } = usePageContext(); const { i18n } = useTranslationContext(); let submitAmount: string | undefined = "5.00"; const ref = useRef(null); useEffect(() => { if (focus) ref.current?.focus(); }, [focus]); return (

 

  { // FIXME: validate using 'parseAmount()', // deactivate submit button as long as // amount is not valid submitAmount = e.currentTarget.value; }} />

{ submitAmount = validateAmount(submitAmount); /** * By invalid amounts, the validator prints error messages * on the console, and the browser colourizes the amount input * box to indicate a error. */ if (!submitAmount && currency) return; createWithdrawalCall( `${currency}:${submitAmount}`, backend.state, pageStateSetter, i18n, ); }} />

); } /** * This function creates a withdrawal operation via the Access API. * * After having successfully created the withdrawal operation, the * user should receive a QR code of the "taler://withdraw/" type and * supposed to scan it with their phone. * * TODO: (1) after the scan, the page should refresh itself and inform * the user about the operation's outcome. (2) use POST helper. */ async function createWithdrawalCall( amount: string, backendState: BackendState, pageStateSetter: StateUpdater, i18n: InternationalizationAPI, ): Promise { if (backendState?.status === "loggedOut") { logger.error("Page has a problem: no credentials found in the state."); pageStateSetter((prevState) => ({ ...prevState, error: { title: i18n.str`No credentials given.`, }, })); return; } let res: Response; try { const { username, password } = backendState; const headers = prepareHeaders(username, password); // Let bank generate withdraw URI: const url = new URL( `access-api/accounts/${backendState.username}/withdrawals`, backendState.url, ); res = await fetch(url.href, { method: "POST", headers, body: JSON.stringify({ amount }), }); } catch (error) { logger.trace("Could not POST withdrawal request to the bank", error); pageStateSetter((prevState) => ({ ...prevState, error: { title: i18n.str`Could not create withdrawal operation`, description: (error as any).error.description, debug: JSON.stringify(error), }, })); return; } if (!res.ok) { const response = await res.json(); logger.error( `Withdrawal creation gave response error: ${response} (${res.status})`, ); pageStateSetter((prevState) => ({ ...prevState, error: { title: i18n.str`Withdrawal creation gave response error`, description: response.error.description, debug: JSON.stringify(response), }, })); return; } logger.trace("Withdrawal operation created!"); const resp = await res.json(); pageStateSetter((prevState: PageStateType) => ({ ...prevState, withdrawalInProgress: true, talerWithdrawUri: resp.taler_withdraw_uri, withdrawalId: resp.withdrawal_id, })); }