/* This file is part of TALER (C) 2015-2016 GNUnet e.V. 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. 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 TALER; see the file COPYING. If not, see */ /** * Page shown to the user to confirm creation * of a reserve, usually requested by the bank. * * @author Florian Dold */ import { AmountLike, Amounts, i18n, WithdrawUriInfoResponse } from '@gnu-taler/taler-util'; import { ExchangeWithdrawDetails } from '@gnu-taler/taler-wallet-core/src/operations/withdraw'; import { useEffect, useState } from "preact/hooks"; import { CheckboxOutlined } from '../components/CheckboxOutlined'; import { ExchangeXmlTos } from '../components/ExchangeToS'; import { LogoHeader } from '../components/LogoHeader'; import { Part } from '../components/Part'; import { ButtonDestructive, ButtonSuccess, ButtonWarning, LinkSuccess, LinkWarning, TermsOfService, WalletAction } from '../components/styled'; import { acceptWithdrawal, getExchangeWithdrawalInfo, getWithdrawalDetailsForUri, onUpdateNotification, setExchangeTosAccepted } from "../wxApi"; import { h } from 'preact'; interface Props { talerWithdrawUri?: string; } export interface ViewProps { details: ExchangeWithdrawDetails; amount: string; onWithdraw: () => Promise; onReview: (b: boolean) => void; onAccept: (b: boolean) => void; reviewing: boolean; accepted: boolean; confirmed: boolean; terms: { value?: TermsDocument; status: TermsStatus; } }; type TermsStatus = 'new' | 'accepted' | 'changed' | 'notfound'; type TermsDocument = TermsDocumentXml | TermsDocumentHtml; interface TermsDocumentXml { type: 'xml', document: Document, } interface TermsDocumentHtml { type: 'html', href: string, } function amountToString(text: AmountLike) { const aj = Amounts.jsonifyAmount(text) const amount = Amounts.stringifyValue(aj) return `${amount} ${aj.currency}` } export function View({ details, amount, onWithdraw, terms, reviewing, onReview, onAccept, accepted, confirmed }: ViewProps) { const needsReview = terms.status === 'changed' || terms.status === 'new' return (

{i18n.str`Digital cash withdrawal`}

{Amounts.isNonZero(details.withdrawFee) && }
{!reviewing &&
{i18n.str`Edit exchange`}
} {!reviewing && accepted &&
onReview(true)} > {i18n.str`Show terms of service`}
} {reviewing &&
{terms.status !== 'accepted' && terms.value && terms.value.type === 'xml' && }
} {reviewing && accepted &&
onReview(false)} > {i18n.str`Hide terms of service`}
} {(reviewing || accepted) &&
{ onAccept(!accepted) onReview(false) }} />
}
{terms.status === 'new' && !accepted && !reviewing && onReview(true)} > {i18n.str`Review exchange terms of service`} } {terms.status === 'changed' && !accepted && onReview(true)} > {i18n.str`Review new version of terms of service`} } {(terms.status === 'accepted' || (needsReview && accepted)) && {i18n.str`Confirm withdrawal`} } {terms.status === 'notfound' && {i18n.str`Exchange doesn't have terms of service`} }
) } export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element { const [uriInfo, setUriInfo] = useState(undefined); const [details, setDetails] = useState(undefined); const [cancelled, setCancelled] = useState(false); const [selecting, setSelecting] = useState(false); const [error, setError] = useState(false); const [updateCounter, setUpdateCounter] = useState(1); const [reviewing, setReviewing] = useState(false) const [accepted, setAccepted] = useState(false) const [confirmed, setConfirmed] = useState(false) useEffect(() => { return onUpdateNotification(() => { console.log('updating...') setUpdateCounter(updateCounter + 1); }); }, []); useEffect(() => { console.log('on effect yes', talerWithdrawUri) if (!talerWithdrawUri) return const fetchData = async (): Promise => { try { const res = await getWithdrawalDetailsForUri({ talerWithdrawUri }); setUriInfo(res); } catch (e) { console.error('error', JSON.stringify(e, undefined, 2)) setError(true) } }; fetchData(); }, [selecting, talerWithdrawUri, updateCounter]); useEffect(() => { async function fetchData() { if (!uriInfo || !uriInfo.defaultExchangeBaseUrl) return try { const res = await getExchangeWithdrawalInfo({ exchangeBaseUrl: uriInfo.defaultExchangeBaseUrl, amount: Amounts.parseOrThrow(uriInfo.amount), tosAcceptedFormat: ['text/json', 'text/xml', 'text/pdf'] }) setDetails(res) } catch (e) { setError(true) } } fetchData() }, [uriInfo]) if (!talerWithdrawUri) { return missing withdraw uri; } const onAccept = async (): Promise => { if (!details) { throw Error("can't accept, no exchange selected"); } try { await setExchangeTosAccepted(details.exchangeDetails.exchangeBaseUrl, details.tosRequested?.tosEtag) setAccepted(true) } catch (e) { setError(true) } } const onWithdraw = async (): Promise => { if (!details) { throw Error("can't accept, no exchange selected"); } setConfirmed(true) console.log("accepting exchange", details.exchangeInfo.baseUrl); try { const res = await acceptWithdrawal(talerWithdrawUri, details.exchangeInfo.baseUrl); console.log("accept withdrawal response", res); if (res.confirmTransferUrl) { document.location.href = res.confirmTransferUrl; } } catch (e) { setConfirmed(false) } }; if (cancelled) { return Withdraw operation has been cancelled.; } if (error) { return This URI is not valid anymore.; } if (!uriInfo) { return Loading...; } if (!details) { return Getting withdrawal details.; } let termsContent: TermsDocument | undefined = undefined; if (details.tosRequested) { if (details.tosRequested.tosContentType === 'text/xml') { try { const document = new DOMParser().parseFromString(details.tosRequested.tosText, "text/xml") termsContent = { type: 'xml', document } } catch (e) { console.log(e) debugger; } } } const status: TermsStatus = !termsContent ? 'notfound' : ( !details.exchangeDetails.termsOfServiceAcceptedEtag ? 'new' : ( details.tosRequested?.tosEtag !== details.exchangeDetails.termsOfServiceAcceptedEtag ? 'changed' : 'accepted' )) return }