import { Logger } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact";
import { StateUpdater } from "preact/hooks";
import { useBackendContext } from "../../context/backend.js";
import { PageStateType, usePageContext } from "../../context/pageState.js";
import { useTranslationContext } from "../../context/translation.js";
import { BackendState } from "../../hooks/backend.js";
import { prepareHeaders } from "../../utils.js";
const logger = new Logger("WithdrawalConfirmationQuestion");
/**
* Additional authentication required to complete the operation.
* Not providing a back button, only abort.
*/
export function WithdrawalConfirmationQuestion(): VNode {
const { pageState, pageStateSetter } = usePageContext();
const backend = useBackendContext();
const { i18n } = useTranslationContext();
const captchaNumbers = {
a: Math.floor(Math.random() * 10),
b: Math.floor(Math.random() * 10),
};
let captchaAnswer = "";
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,
): Promise {
if (backendState.status === "loggedOut") {
logger.error("No credentials found.");
pageStateSetter((prevState) => ({
...prevState,
error: {
title: "No credentials found.",
},
}));
return;
}
if (typeof withdrawalId === "undefined") {
logger.error("No withdrawal ID found.");
pageStateSetter((prevState) => ({
...prevState,
error: {
title: "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: `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: `Withdrawal confirmation gave response error`,
debug: JSON.stringify(response),
},
}));
return;
}
logger.trace("Withdrawal operation confirmed!");
pageStateSetter((prevState) => {
const { talerWithdrawUri, ...rest } = prevState;
return {
...rest,
info: "Withdrawal confirmed!",
};
});
}
/**
* Abort a withdrawal operation via the Access API's /abort.
*/
async function abortWithdrawalCall(
backendState: BackendState,
withdrawalId: string | undefined,
pageStateSetter: StateUpdater,
): Promise {
if (backendState.status === "loggedOut") {
logger.error("No credentials found.");
pageStateSetter((prevState) => ({
...prevState,
error: {
title: `No credentials found.`,
},
}));
return;
}
if (typeof withdrawalId === "undefined") {
logger.error("No withdrawal ID found.");
pageStateSetter((prevState) => ({
...prevState,
error: {
title: `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: `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: `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: "Withdrawal aborted!",
};
});
}