diff options
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet/Pay.tsx')
-rw-r--r-- | packages/taler-wallet-webextension/src/wallet/Pay.tsx | 287 |
1 files changed, 158 insertions, 129 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/Pay.tsx b/packages/taler-wallet-webextension/src/wallet/Pay.tsx index bd06656c7..a5849bb28 100644 --- a/packages/taler-wallet-webextension/src/wallet/Pay.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Pay.tsx @@ -29,7 +29,7 @@ import * as wxApi from "../wxApi"; import { useState, useEffect } from "preact/hooks"; -import { getJsonI18n, i18n } from "@gnu-taler/taler-util"; +import { ConfirmPayResultDone, getJsonI18n, i18n } from "@gnu-taler/taler-util"; import { PreparePayResult, ConfirmPayResult, @@ -45,13 +45,54 @@ interface Props { talerPayUri?: string } +export function AlreadyPaid({ payStatus }: { payStatus: PreparePayResult }) { + const fulfillmentUrl = payStatus.contractTerms.fulfillment_url; + let message; + if (fulfillmentUrl) { + message = ( + <span> + You have already paid for this article. Click{" "} + <a href={fulfillmentUrl} target="_bank" rel="external">here</a> to view it again. + </span> + ); + } else { + message = <span> + You have already paid for this article:{" "} + <em> + {payStatus.contractTerms.fulfillment_message ?? "no message given"} + </em> + </span>; + } + return <section class="main"> + <h1>GNU Taler Wallet</h1> + <article class="fade"> + {message} + </article> + </section> +} + +const doPayment = async (payStatus: PreparePayResult): Promise<ConfirmPayResultDone> => { + 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 }: Props): JSX.Element { const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>(undefined); const [payResult, setPayResult] = useState<ConfirmPayResult | undefined>(undefined); const [payErrMsg, setPayErrMsg] = useState<string | undefined>(""); - const [numTries, setNumTries] = useState(0); - const [loading, setLoading] = useState(false); - let totalFees: AmountJson | undefined = undefined; useEffect(() => { if (!talerPayUri) return; @@ -60,53 +101,67 @@ export function PayPage({ talerPayUri }: Props): JSX.Element { setPayStatus(p); }; doFetch(); - }, [numTries, talerPayUri]); + }, [talerPayUri]); if (!talerPayUri) { return <span>missing pay uri</span> } - + if (!payStatus) { return <span>Loading payment information ...</span>; } - let insufficientBalance = false; - if (payStatus.status == PreparePayResultType.InsufficientBalance) { - insufficientBalance = true; - } - - if (payStatus.status === PreparePayResultType.PaymentPossible) { - const amountRaw = Amounts.parseOrThrow(payStatus.amountRaw); - const amountEffective: AmountJson = Amounts.parseOrThrow( - payStatus.amountEffective, - ); - totalFees = Amounts.sub(amountEffective, amountRaw).amount; - } - - if ( - payStatus.status === PreparePayResultType.AlreadyConfirmed && - numTries === 0 - ) { - const fulfillmentUrl = payStatus.contractTerms.fulfillment_url; - if (fulfillmentUrl) { + if (payResult && payResult.type === ConfirmPayResultType.Done) { + if (payResult.contractTerms.fulfillment_message) { + const obj = { + fulfillment_message: payResult.contractTerms.fulfillment_message, + fulfillment_message_i18n: + payResult.contractTerms.fulfillment_message_i18n, + }; + const msg = getJsonI18n(obj, "fulfillment_message"); return ( - <span> - You have already paid for this article. Click{" "} - <a href={fulfillmentUrl} target="_bank" rel="external">here</a> to view it again. - </span> + <div> + <p>Payment succeeded.</p> + <p>{msg}</p> + </div> ); } else { - <span> - You have already paid for this article:{" "} - <em> - {payStatus.contractTerms.fulfillment_message ?? "no message given"} - </em> - </span>; + return <span>Redirecting ...</span>; } } + const onClick = async () => { + try { + const res = await doPayment(payStatus) + setPayResult(res); + } catch (e) { + console.error(e); + setPayErrMsg(e.message); + } + + } + + return <PaymentRequestView payStatus={payStatus} onClick={onClick} payErrMsg={payErrMsg} />; +} + +export interface PaymentRequestViewProps { + payStatus: PreparePayResult; + onClick: () => void; + payErrMsg?: string; + +} +export function PaymentRequestView({ payStatus, onClick, payErrMsg }: PaymentRequestViewProps) { + let totalFees: AmountJson | undefined = undefined; + let insufficientBalance = false; + const [loading, setLoading] = useState(false); const contractTerms: ContractTerms = payStatus.contractTerms; + if ( + payStatus.status === PreparePayResultType.AlreadyConfirmed + ) { + return <AlreadyPaid payStatus={payStatus} /> + } + if (!contractTerms) { return ( <span> @@ -115,6 +170,18 @@ export function PayPage({ talerPayUri }: Props): JSX.Element { ); } + if (payStatus.status == PreparePayResultType.InsufficientBalance) { + insufficientBalance = true; + } + + 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 = <strong>{contractTerms.merchant.name}</strong>; @@ -126,99 +193,61 @@ export function PayPage({ talerPayUri }: Props): JSX.Element { <strong>{renderAmount(Amounts.parseOrThrow(contractTerms.amount))}</strong> ); - const doPayment = async (): Promise<void> => { - if (payStatus.status !== "payment-possible") { - throw Error(`invalid state: ${payStatus.status}`); - } - const proposalId = payStatus.proposalId; - setNumTries(numTries + 1); - try { - setLoading(true); - 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; - } - setPayResult(res); - } catch (e) { - console.error(e); - setPayErrMsg(e.message); - } - }; - - if (payResult && payResult.type === ConfirmPayResultType.Done) { - if (payResult.contractTerms.fulfillment_message) { - const obj = { - fulfillment_message: payResult.contractTerms.fulfillment_message, - fulfillment_message_i18n: - payResult.contractTerms.fulfillment_message_i18n, - }; - const msg = getJsonI18n(obj, "fulfillment_message"); - return ( - <div> - <p>Payment succeeded.</p> - <p>{msg}</p> - </div> - ); - } else { - return <span>Redirecting ...</span>; - } - } - - return ( - <div> - <p> - <i18n.Translate> - The merchant <span>{merchantName}</span> offers you to purchase: - </i18n.Translate> - <div style={{ textAlign: "center" }}> - <strong>{contractTerms.summary}</strong> - </div> - {totalFees ? ( + return <section class="main"> + <h1>GNU Taler Wallet</h1> + <article class="fade"> + <div> + <p> <i18n.Translate> - The total price is <span>{amount} </span> - (plus <span>{renderAmount(totalFees)}</span> fees). - </i18n.Translate> + The merchant <span>{merchantName}</span> offers you to purchase: + </i18n.Translate> + <div style={{ textAlign: "center" }}> + <strong>{contractTerms.summary}</strong> + </div> + {totalFees ? ( + <i18n.Translate> + The total price is <span>{amount} </span> + (plus <span>{renderAmount(totalFees)}</span> fees). + </i18n.Translate> + ) : ( + <i18n.Translate> + The total price is <span>{amount}</span>. + </i18n.Translate> + )} + </p> + + {insufficientBalance ? ( + <div> + <p style={{ color: "red", fontWeight: "bold" }}> + Unable to pay: Your balance is insufficient. + </p> + </div> + ) : null} + + {payErrMsg ? ( + <div> + <p>Payment failed: {payErrMsg}</p> + <button + class="pure-button button-success" + onClick={onClick} + > + {i18n.str`Retry`} + </button> + </div> ) : ( - <i18n.Translate> - The total price is <span>{amount}</span>. - </i18n.Translate> - )} - </p> - - {insufficientBalance ? ( - <div> - <p style={{ color: "red", fontWeight: "bold" }}> - Unable to pay: Your balance is insufficient. - </p> - </div> - ) : null} - - {payErrMsg ? ( - <div> - <p>Payment failed: {payErrMsg}</p> - <button - class="pure-button button-success" - onClick={() => doPayment()} - > - {i18n.str`Retry`} - </button> - </div> - ) : ( - <div> - <ProgressButton - isLoading={loading} - disabled={insufficientBalance} - onClick={() => doPayment()} - > - {i18n.str`Confirm payment`} - </ProgressButton> - </div> - )} - </div> - ); -} - + <div> + <ProgressButton + isLoading={loading} + disabled={insufficientBalance} + onClick={onClick} + > + {i18n.str`Confirm payment`} + </ProgressButton> + </div> + )} + </div> + </article> + </section> + + +}
\ No newline at end of file |