/* This file is part of GNU Taler (C) 2022-2024 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 { AbsoluteTime, AmountJson, HttpStatusCode, PaytoUri, PaytoUriIBAN, PaytoUriTalerBank, TalerErrorCode, TranslatedString, WithdrawUriResult, assertUnreachable, } from "@gnu-taler/taler-util"; import { Attention, LocalNotificationBanner, notifyInfo, useLocalNotification, useTranslationContext, } from "@gnu-taler/web-util/browser"; import { ComponentChildren, Fragment, VNode, h } from "preact"; import { mutate } from "swr"; import { useBankCoreApiContext } from "@gnu-taler/web-util/browser"; import { useBankState } from "../hooks/bank-state.js"; import { usePreferences } from "../hooks/preferences.js"; import { useSessionState } from "../hooks/session.js"; import { RouteDefinition } from "@gnu-taler/web-util/browser"; import { LoginForm } from "./LoginForm.js"; import { RenderAmount } from "./PaytoWireTransferForm.js"; interface Props { onAborted: () => void; withdrawUri: WithdrawUriResult; routeHere: RouteDefinition<{ wopid: string }>; details: { account: PaytoUri; reserve: string; username: string; amount: AmountJson; }; onAuthorizationRequired: () => void; } /** * Additional authentication required to complete the operation. * Not providing a back button, only abort. */ export function WithdrawalConfirmationQuestion({ onAborted, details, onAuthorizationRequired, routeHere, withdrawUri, }: Props): VNode { const { i18n } = useTranslationContext(); const [settings] = usePreferences(); const { state: credentials } = useSessionState(); const creds = credentials.status !== "loggedIn" ? undefined : credentials; const [, updateBankState] = useBankState(); const [notification, notify, handleError] = useLocalNotification(); const { config, lib: { bank: api }, } = useBankCoreApiContext(); async function doTransfer() { await handleError(async () => { if (!creds) return; const resp = await api.confirmWithdrawalById( creds, withdrawUri.withdrawalOperationId, ); if (resp.type === "ok") { mutate(() => true); // clean any info that we have if (!settings.showWithdrawalSuccess) { notifyInfo(i18n.str`Wire transfer completed!`); } } else { switch (resp.case) { case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: return notify({ type: "error", title: i18n.str`The withdrawal has been aborted previously and can't be confirmed`, description: resp.detail.hint as TranslatedString, debug: resp.detail, when: AbsoluteTime.now(), }); case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return notify({ type: "error", title: i18n.str`The withdrawal operation can't be confirmed before a wallet accepted the transaction.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, when: AbsoluteTime.now(), }); case HttpStatusCode.BadRequest: return notify({ type: "error", title: i18n.str`The operation id is invalid.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, when: AbsoluteTime.now(), }); case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`The operation was not found.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, when: AbsoluteTime.now(), }); case TalerErrorCode.BANK_UNALLOWED_DEBIT: return notify({ type: "error", title: i18n.str`Your balance is not enough for the operation.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, when: AbsoluteTime.now(), }); case HttpStatusCode.Accepted: { updateBankState("currentChallenge", { operation: "confirm-withdrawal", id: String(resp.body.challenge_id), location: routeHere.url({ wopid: withdrawUri.withdrawalOperationId, }), sent: AbsoluteTime.never(), request: withdrawUri.withdrawalOperationId, }); return onAuthorizationRequired(); } default: assertUnreachable(resp); } } }); } async function doCancel() { await handleError(async () => { if (!creds) return; const resp = await api.abortWithdrawalById( creds, withdrawUri.withdrawalOperationId, ); if (resp.type === "ok") { onAborted(); } else { switch (resp.case) { case HttpStatusCode.Conflict: return notify({ type: "error", title: i18n.str`The reserve operation has been confirmed previously and can't be aborted`, description: resp.detail.hint as TranslatedString, debug: resp.detail, when: AbsoluteTime.now(), }); case HttpStatusCode.BadRequest: return notify({ type: "error", title: i18n.str`The operation id is invalid.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, when: AbsoluteTime.now(), }); case HttpStatusCode.NotFound: return notify({ type: "error", title: i18n.str`The operation was not found.`, description: resp.detail.hint as TranslatedString, debug: resp.detail, when: AbsoluteTime.now(), }); default: { assertUnreachable(resp); } } } }); } return (

Confirm the withdrawal operation

{ e.preventDefault(); }} >

Wire transfer details

{((): VNode => { if (!details.account.isKnown) { return (
Payment provider's account
{details.account.targetPath}
); } switch (details.account.targetType) { case "iban": { const name = details.account.params["receiver-name"]; return (
Payment provider's account number
{details.account.iban}
{name && (
Payment provider's name
{name}
)}
); } case "x-taler-bank": { const name = details.account.params["receiver-name"]; return (
Payment provider's account bank hostname
{details.account.host}
Payment provider's account id
{details.account.account}
{name && (
Payment provider's name
{name}
)}
); } case "bitcoin": { const name = details.account.params["receiver-name"]; return (
Payment provider's account address
{details.account.address}
{name && (
Payment provider's name
{name}
)}
); } default: { assertUnreachable(details.account); } } })()}
Amount
); } export function ShouldBeSameUser({ username, children, }: { username: string; children: ComponentChildren; }): VNode { const { state: credentials } = useSessionState(); const { i18n } = useTranslationContext(); if (credentials.status === "loggedOut") { return ( ); } if (credentials.username !== username) { return ( ); } return {children}; }