/*
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,
Translate,
} from "@gnu-taler/taler-util";
import { OperationFailedError } from "@gnu-taler/taler-wallet-core";
import { Fragment, h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks";
import { ErrorMessage } from "../components/ErrorMessage";
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,
LightText,
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 (
Could not load pay status}
error={hook}
/>
);
}
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;
useEffect(() => {
if (
payStatus.status === PreparePayResultType.AlreadyConfirmed &&
payStatus.paid
) {
const fu = payStatus.contractTerms.fulfillment_url;
if (fu) {
setTimeout(() => {
document.location.href = fu;
}, 3000);
}
}
});
if (!contractTerms) {
return (
Could not load 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;
}
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 ? (
Pay with a mobile phone
) : (
Hide QR
)}
{showQR && (
)}
);
}
function ButtonsSection(): VNode {
if (payResult) {
if (payResult.type === ConfirmPayResultType.Pending) {
return (
Processing...
);
}
return ;
}
if (payStatus.status === PreparePayResultType.PaymentPossible) {
return (
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.
)}
Withdraw digital cash
);
}
if (payStatus.status === PreparePayResultType.AlreadyConfirmed) {
return (
{payStatus.paid && contractTerms.fulfillment_message && (
Merchant message}
text={contractTerms.fulfillment_message}
kind="neutral"
/>
)}
{!payStatus.paid && }
);
}
return ;
}
return (
Digital cash payment
{payStatus.status === PreparePayResultType.AlreadyConfirmed &&
(payStatus.paid ? (
payStatus.contractTerms.fulfillment_url ? (
Already paid, you are going to be redirected to{" "}
{payStatus.contractTerms.fulfillment_url}
) : (
Already paid
)
) : (
Already claimed
))}
{payResult && payResult.type === ConfirmPayResultType.Done && (
Payment complete
{!payResult.contractTerms.fulfillment_message ? (
payResult.contractTerms.fulfillment_url ? (
You are going to be redirected to $
{payResult.contractTerms.fulfillment_url}
) : (
You can close this page.
)
) : (
payResult.contractTerms.fulfillment_message
)}