/*
This file is part of TALER
(C) 2015 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 entering
* a contract.
*/
/**
* Imports.
*/
// import * as i18n from "../i18n";
import {
AmountJson,
AmountLike,
Amounts,
ConfirmPayResult,
ConfirmPayResultDone,
ConfirmPayResultType,
ContractTerms,
i18n,
NotificationType,
PreparePayResult,
PreparePayResultType,
Product,
} from "@gnu-taler/taler-util";
import { OperationFailedError } from "@gnu-taler/taler-wallet-core";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Loading } from "../components/Loading";
import { LoadingError } from "../components/LoadingError";
import { LogoHeader } from "../components/LogoHeader";
import { Part } from "../components/Part";
import { QR } from "../components/QR";
import {
ButtonSuccess,
LinkSuccess,
SmallLightText,
SuccessBox,
WalletAction,
WarningBox,
} from "../components/styled";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
import * as wxApi from "../wxApi";
interface Props {
talerPayUri?: string;
goToWalletManualWithdraw: (currency?: string) => void;
goBack: () => void;
}
const doPayment = async (
payStatus: PreparePayResult,
): Promise => {
if (payStatus.status !== "payment-possible") {
throw Error(`invalid state: ${payStatus.status}`);
}
const proposalId = payStatus.proposalId;
const res = await wxApi.confirmPay(proposalId, undefined);
if (res.type !== ConfirmPayResultType.Done) {
throw Error("payment pending");
}
const fu = res.contractTerms.fulfillment_url;
if (fu) {
document.location.href = fu;
}
return res;
};
export function PayPage({
talerPayUri,
goToWalletManualWithdraw,
goBack,
}: Props): VNode {
const [payResult, setPayResult] = useState(
undefined,
);
const [payErrMsg, setPayErrMsg] = useState<
OperationFailedError | string | undefined
>(undefined);
const hook = useAsyncAsHook(async () => {
if (!talerPayUri) throw Error("Missing pay uri");
const payStatus = await wxApi.preparePay(talerPayUri);
const balance = await wxApi.getBalance();
return { payStatus, balance };
}, [NotificationType.CoinWithdrawn]);
if (!hook) {
return ;
}
if (hook.hasError) {
return ;
}
const foundBalance = hook.response.balance.balances.find(
(b) =>
Amounts.parseOrThrow(b.available).currency ===
Amounts.parseOrThrow(hook.response.payStatus.amountRaw).currency,
);
const foundAmount = foundBalance
? Amounts.parseOrThrow(foundBalance.available)
: undefined;
const onClick = async (): Promise => {
try {
const res = await doPayment(hook.response.payStatus);
setPayResult(res);
} catch (e) {
console.error(e);
if (e instanceof Error) {
setPayErrMsg(e.message);
}
}
};
return (
);
}
export interface PaymentRequestViewProps {
payStatus: PreparePayResult;
payResult?: ConfirmPayResult;
onClick: () => void;
payErrMsg?: string;
uri: string;
goToWalletManualWithdraw: () => void;
balance: AmountJson | undefined;
}
export function PaymentRequestView({
uri,
payStatus,
payResult,
onClick,
goToWalletManualWithdraw,
balance,
}: PaymentRequestViewProps): VNode {
let totalFees: AmountJson = Amounts.getZero(payStatus.amountRaw);
const contractTerms: ContractTerms = payStatus.contractTerms;
if (!contractTerms) {
return (
Error: did not get contract terms from merchant or wallet backend.
);
}
if (payStatus.status === PreparePayResultType.PaymentPossible) {
const amountRaw = Amounts.parseOrThrow(payStatus.amountRaw);
const amountEffective: AmountJson = Amounts.parseOrThrow(
payStatus.amountEffective,
);
totalFees = Amounts.sub(amountEffective, amountRaw).amount;
}
// let merchantName: VNode;
// if (contractTerms.merchant && contractTerms.merchant.name) {
// merchantName = {contractTerms.merchant.name};
// } else {
// merchantName = (pub: {contractTerms.merchant_pub});
// }
function Alternative(): VNode {
const [showQR, setShowQR] = useState(false);
const privateUri =
payStatus.status !== PreparePayResultType.AlreadyConfirmed
? `${uri}&n=${payStatus.noncePriv}`
: uri;
if (!uri) return ;
return (
setShowQR((qr) => !qr)}>
{!showQR ? i18n.str`Pay with a mobile phone` : i18n.str`Hide QR`}
{showQR && (
)}
);
}
function ButtonsSection(): VNode {
if (payResult) {
if (payResult.type === ConfirmPayResultType.Pending) {
return (
Processing...
);
}
return ;
}
if (payStatus.status === PreparePayResultType.PaymentPossible) {
return (
{i18n.str`Pay`} {amountToString(payStatus.amountEffective)}
);
}
if (payStatus.status === PreparePayResultType.InsufficientBalance) {
return (
{balance ? (
Your balance of {amountToString(balance)} is not enough to pay
for this purchase
) : (
Your balance is not enough to pay for this purchase.
)}
{i18n.str`Withdraw digital cash`}
);
}
if (payStatus.status === PreparePayResultType.AlreadyConfirmed) {
return (
{payStatus.paid && contractTerms.fulfillment_message && (
)}
{!payStatus.paid && }
);
}
return ;
}
return (
{!payResult.contractTerms.fulfillment_message
? "You will now be sent back to the merchant you came from."
: payResult.contractTerms.fulfillment_message}