/*
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 { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useMemo, useState } from "preact/hooks";
import { useBackendContext } from "../context/backend.js";
import { usePageContext } from "../context/pageState.js";
import { useAccessAPI } from "../hooks/access.js";
import { undefinedIfEmpty } from "../utils.js";
import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js";
const logger = new Logger("WithdrawalConfirmationQuestion");
interface Props {
account: string;
withdrawalId: string;
}
/**
* Additional authentication required to complete the operation.
* Not providing a back button, only abort.
*/
export function WithdrawalConfirmationQuestion({
account,
withdrawalId,
}: Props): VNode {
const { pageState, pageStateSetter } = usePageContext();
const backend = useBackendContext();
const { i18n } = useTranslationContext();
const captchaNumbers = useMemo(() => {
return {
a: Math.floor(Math.random() * 10),
b: Math.floor(Math.random() * 10),
};
}, []);
const { confirmWithdrawal, abortWithdrawal } = useAccessAPI();
const [captchaAnswer, setCaptchaAnswer] = useState();
const answer = parseInt(captchaAnswer ?? "", 10);
const errors = undefinedIfEmpty({
answer: !captchaAnswer
? i18n.str`Answer the question before continue`
: Number.isNaN(answer)
? i18n.str`The answer should be a number`
: answer !== captchaNumbers.a + captchaNumbers.b
? i18n.str`The answer "${answer}" to "${captchaNumbers.a} + ${captchaNumbers.b}" is wrong.`
: undefined,
});
return (
{i18n.str`Confirm Withdrawal`}
A this point, a real bank would ask for an additional
authentication proof (PIN/TAN, one time password, ..), instead
of a simple calculation.
);
}
/**
* This function confirms a withdrawal operation AFTER
* the wallet has given the exchange's payment details
* to the bank (via the Integration API). Such details
* can be given by scanning a QR code or by passing the
* raw taler://withdraw-URI to the CLI wallet.
*
* This function will set the confirmation status in the
* 'page state' and let the related components refresh.
*/
// async function confirmWithdrawalCall(
// backendState: BackendState,
// withdrawalId: string | undefined,
// pageStateSetter: StateUpdater,
// i18n: InternationalizationAPI,
// ): Promise {
// if (backendState.status === "loggedOut") {
// logger.error("No credentials found.");
// pageStateSetter((prevState) => ({
// ...prevState,
// error: {
// title: i18n.str`No credentials found.`,
// },
// }));
// return;
// }
// if (typeof withdrawalId === "undefined") {
// logger.error("No withdrawal ID found.");
// pageStateSetter((prevState) => ({
// ...prevState,
// error: {
// title: i18n.str`No withdrawal ID found.`,
// },
// }));
// return;
// }
// let res: Response;
// try {
// const { username, password } = backendState;
// const headers = prepareHeaders(username, password);
// /**
// * NOTE: tests show that when a same object is being
// * POSTed, caching might prevent same requests from being
// * made. Hence, trying to POST twice the same amount might
// * get silently ignored.
// *
// * headers.append("cache-control", "no-store");
// * headers.append("cache-control", "no-cache");
// * headers.append("pragma", "no-cache");
// * */
// // Backend URL must have been stored _with_ a final slash.
// const url = new URL(
// `access-api/accounts/${backendState.username}/withdrawals/${withdrawalId}/confirm`,
// backendState.url,
// );
// res = await fetch(url.href, {
// method: "POST",
// headers,
// });
// } catch (error) {
// logger.error("Could not POST withdrawal confirmation to the bank", error);
// pageStateSetter((prevState) => ({
// ...prevState,
// error: {
// title: i18n.str`Could not confirm the withdrawal`,
// description: (error as any).error.description,
// debug: JSON.stringify(error),
// },
// }));
// return;
// }
// if (!res || !res.ok) {
// const response = await res.json();
// // assume not ok if res is null
// logger.error(
// `Withdrawal confirmation gave response error (${res.status})`,
// res.statusText,
// );
// pageStateSetter((prevState) => ({
// ...prevState,
// error: {
// title: i18n.str`Withdrawal confirmation gave response error`,
// debug: JSON.stringify(response),
// },
// }));
// return;
// }
// logger.trace("Withdrawal operation confirmed!");
// pageStateSetter((prevState) => {
// const { talerWithdrawUri, ...rest } = prevState;
// return {
// ...rest,
// info: i18n.str`Withdrawal confirmed!`,
// };
// });
// }
// /**
// * Abort a withdrawal operation via the Access API's /abort.
// */
// async function abortWithdrawalCall(
// backendState: BackendState,
// withdrawalId: string | undefined,
// pageStateSetter: StateUpdater,
// i18n: InternationalizationAPI,
// ): Promise {
// if (backendState.status === "loggedOut") {
// logger.error("No credentials found.");
// pageStateSetter((prevState) => ({
// ...prevState,
// error: {
// title: i18n.str`No credentials found.`,
// },
// }));
// return;
// }
// if (typeof withdrawalId === "undefined") {
// logger.error("No withdrawal ID found.");
// pageStateSetter((prevState) => ({
// ...prevState,
// error: {
// title: i18n.str`No withdrawal ID found.`,
// },
// }));
// return;
// }
// let res: Response;
// try {
// const { username, password } = backendState;
// const headers = prepareHeaders(username, password);
// /**
// * NOTE: tests show that when a same object is being
// * POSTed, caching might prevent same requests from being
// * made. Hence, trying to POST twice the same amount might
// * get silently ignored. Needs more observation!
// *
// * headers.append("cache-control", "no-store");
// * headers.append("cache-control", "no-cache");
// * headers.append("pragma", "no-cache");
// * */
// // Backend URL must have been stored _with_ a final slash.
// const url = new URL(
// `access-api/accounts/${backendState.username}/withdrawals/${withdrawalId}/abort`,
// backendState.url,
// );
// res = await fetch(url.href, { method: "POST", headers });
// } catch (error) {
// logger.error("Could not abort the withdrawal", error);
// pageStateSetter((prevState) => ({
// ...prevState,
// error: {
// title: i18n.str`Could not abort the withdrawal.`,
// description: (error as any).error.description,
// debug: JSON.stringify(error),
// },
// }));
// return;
// }
// if (!res.ok) {
// const response = await res.json();
// logger.error(
// `Withdrawal abort gave response error (${res.status})`,
// res.statusText,
// );
// pageStateSetter((prevState) => ({
// ...prevState,
// error: {
// title: i18n.str`Withdrawal abortion failed.`,
// description: response.error.description,
// debug: JSON.stringify(response),
// },
// }));
// return;
// }
// logger.trace("Withdrawal operation aborted!");
// pageStateSetter((prevState) => {
// const { ...rest } = prevState;
// return {
// ...rest,
// info: i18n.str`Withdrawal aborted!`,
// };
// });
// }