summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-09-17 15:48:33 -0300
committerSebastian <sebasjm@gmail.com>2021-09-17 15:49:07 -0300
commit315b167bee240e625beea731df6472a971b46cb2 (patch)
tree098557be6106622844ad5d59dce7e0c64bb22bcc
parent490620ad04a677fa220cbe77dc0bea29b6e80c12 (diff)
downloadwallet-core-315b167bee240e625beea731df6472a971b46cb2.tar.gz
wallet-core-315b167bee240e625beea731df6472a971b46cb2.tar.bz2
wallet-core-315b167bee240e625beea731df6472a971b46cb2.zip
issue #5860
-rw-r--r--packages/taler-util/src/taleruri.ts3
-rw-r--r--packages/taler-util/src/walletTypes.ts6
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts6
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts11
-rw-r--r--packages/taler-wallet-core/src/operations/pay.ts20
-rw-r--r--packages/taler-wallet-webextension/package.json1
-rw-r--r--packages/taler-wallet-webextension/src/components/QR.tsx37
-rw-r--r--packages/taler-wallet-webextension/src/components/styled/index.tsx8
-rw-r--r--packages/taler-wallet-webextension/src/cta/Pay.stories.tsx7
-rw-r--r--packages/taler-wallet-webextension/src/cta/Pay.tsx136
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw.tsx133
-rw-r--r--pnpm-lock.yaml6
12 files changed, 233 insertions, 141 deletions
diff --git a/packages/taler-util/src/taleruri.ts b/packages/taler-util/src/taleruri.ts
index 6c0dc7b86..09c70682a 100644
--- a/packages/taler-util/src/taleruri.ts
+++ b/packages/taler-util/src/taleruri.ts
@@ -22,6 +22,7 @@ export interface PayUriResult {
orderId: string;
sessionId: string;
claimToken: string | undefined;
+ noncePriv: string | undefined;
}
export interface WithdrawUriResult {
@@ -147,6 +148,7 @@ export function parsePayUri(s: string): PayUriResult | undefined {
const c = pi?.rest.split("?");
const q = new URLSearchParams(c[1] ?? "");
const claimToken = q.get("c") ?? undefined;
+ const noncePriv = q.get("n") ?? undefined;
const parts = c[0].split("/");
if (parts.length < 3) {
return undefined;
@@ -163,6 +165,7 @@ export function parsePayUri(s: string): PayUriResult | undefined {
orderId,
sessionId: sessionId,
claimToken,
+ noncePriv,
};
}
diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts
index 79403ac69..2b35423bc 100644
--- a/packages/taler-util/src/walletTypes.ts
+++ b/packages/taler-util/src/walletTypes.ts
@@ -325,6 +325,7 @@ export const codecForPreparePayResultPaymentPossible = (): Codec<PreparePayResul
.property("contractTerms", codecForContractTerms())
.property("proposalId", codecForString())
.property("contractTermsHash", codecForString())
+ .property("noncePriv", codecForString())
.property(
"status",
codecForConstString(PreparePayResultType.PaymentPossible),
@@ -336,6 +337,7 @@ export const codecForPreparePayResultInsufficientBalance = (): Codec<PreparePayR
.property("amountRaw", codecForAmountString())
.property("contractTerms", codecForAny())
.property("proposalId", codecForString())
+ .property("noncePriv", codecForString())
.property(
"status",
codecForConstString(PreparePayResultType.InsufficientBalance),
@@ -354,6 +356,7 @@ export const codecForPreparePayResultAlreadyConfirmed = (): Codec<PreparePayResu
.property("contractTerms", codecForAny())
.property("contractTermsHash", codecForString())
.property("proposalId", codecForString())
+ .property("noncePriv", codecForString())
.build("PreparePayResultAlreadyConfirmed");
export const codecForPreparePayResult = (): Codec<PreparePayResult> =>
@@ -385,6 +388,7 @@ export interface PreparePayResultPaymentPossible {
contractTermsHash: string;
amountRaw: string;
amountEffective: string;
+ noncePriv: string;
}
export interface PreparePayResultInsufficientBalance {
@@ -392,6 +396,7 @@ export interface PreparePayResultInsufficientBalance {
proposalId: string;
contractTerms: ContractTerms;
amountRaw: string;
+ noncePriv: string;
}
export interface PreparePayResultAlreadyConfirmed {
@@ -402,6 +407,7 @@ export interface PreparePayResultAlreadyConfirmed {
amountEffective: string;
contractTermsHash: string;
proposalId: string;
+ noncePriv: string;
}
export interface BankWithdrawDetails {
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
index da92e83c6..6bace01a3 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
@@ -213,7 +213,7 @@ export class CryptoApi {
ws.w = null;
}
} catch (e) {
- logger.error(e);
+ logger.error(e as string);
}
if (ws.currentWorkItem !== null) {
ws.currentWorkItem.reject(e);
@@ -379,6 +379,10 @@ export class CryptoApi {
return this.doRpc<{ priv: string; pub: string }>("createEddsaKeypair", 1);
}
+ eddsaGetPublic(key: string): Promise<{ priv: string; pub: string }> {
+ return this.doRpc<{ priv: string; pub: string }>("eddsaGetPublic", 1, key);
+ }
+
rsaUnblind(sig: string, bk: string, pk: string): Promise<string> {
return this.doRpc<string>("rsaUnblind", 4, sig, bk, pk);
}
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
index e1580a7d1..7112964db 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
@@ -62,6 +62,7 @@ import {
setupRefreshTransferPub,
setupTipPlanchet,
setupWithdrawPlanchet,
+ eddsaGetPublic,
} from "../talerCrypto.js";
import { randomBytes } from "../primitives/nacl-fast.js";
import { kdf } from "../primitives/kdf.js";
@@ -141,7 +142,7 @@ function timestampRoundedToBuffer(ts: Timestamp): Uint8Array {
class SignaturePurposeBuilder {
private chunks: Uint8Array[] = [];
- constructor(private purposeNum: number) {}
+ constructor(private purposeNum: number) { }
put(bytes: Uint8Array): SignaturePurposeBuilder {
this.chunks.push(Uint8Array.from(bytes));
@@ -170,7 +171,6 @@ class SignaturePurposeBuilder {
function buildSigPS(purposeNum: number): SignaturePurposeBuilder {
return new SignaturePurposeBuilder(purposeNum);
}
-
export class CryptoImplementation {
static enableTracing = false;
@@ -361,6 +361,13 @@ export class CryptoImplementation {
};
}
+ eddsaGetPublic(key: string): { priv: string; pub: string } {
+ return {
+ priv: key,
+ pub: encodeCrock(eddsaGetPublic(decodeCrock(key)))
+ }
+ }
+
/**
* Unblind a blindly signed value.
*/
diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts
index 9a7b0d069..970aa46ff 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -875,7 +875,9 @@ async function startDownloadProposal(
orderId: string,
sessionId: string | undefined,
claimToken: string | undefined,
+ noncePriv: string | undefined,
): Promise<string> {
+
const oldProposal = await ws.db
.mktx((x) => ({ proposals: x.proposals }))
.runReadOnly(async (tx) => {
@@ -884,12 +886,20 @@ async function startDownloadProposal(
orderId,
]);
});
- if (oldProposal) {
+
+ /**
+ * If we have already claimed this proposal with the same sessionId
+ * nonce and claim token, reuse it.
+ */
+ if (oldProposal &&
+ oldProposal.downloadSessionId === sessionId &&
+ oldProposal.noncePriv === noncePriv &&
+ oldProposal.claimToken === claimToken) {
await processDownloadProposal(ws, oldProposal.proposalId);
return oldProposal.proposalId;
}
- const { priv, pub } = await ws.cryptoApi.createEddsaKeypair();
+ const { priv, pub } = await (noncePriv ? ws.cryptoApi.eddsaGetPublic(noncePriv) : ws.cryptoApi.createEddsaKeypair());
const proposalId = encodeCrock(getRandomBytes(32));
const proposalRecord: ProposalRecord = {
@@ -1405,6 +1415,7 @@ export async function checkPaymentByProposalId(
status: PreparePayResultType.InsufficientBalance,
contractTerms: d.contractTermsRaw,
proposalId: proposal.proposalId,
+ noncePriv: proposal.noncePriv,
amountRaw: Amounts.stringify(d.contractData.amount),
};
}
@@ -1417,6 +1428,7 @@ export async function checkPaymentByProposalId(
status: PreparePayResultType.PaymentPossible,
contractTerms: d.contractTermsRaw,
proposalId: proposal.proposalId,
+ noncePriv: proposal.noncePriv,
amountEffective: Amounts.stringify(totalCost),
amountRaw: Amounts.stringify(res.paymentAmount),
contractTermsHash: d.contractData.contractTermsHash,
@@ -1453,6 +1465,7 @@ export async function checkPaymentByProposalId(
amountRaw: Amounts.stringify(purchase.download.contractData.amount),
amountEffective: Amounts.stringify(purchase.totalPayCost),
proposalId,
+ noncePriv: proposal.noncePriv,
};
} else if (!purchase.timestampFirstSuccessfulPay) {
return {
@@ -1463,6 +1476,7 @@ export async function checkPaymentByProposalId(
amountRaw: Amounts.stringify(purchase.download.contractData.amount),
amountEffective: Amounts.stringify(purchase.totalPayCost),
proposalId,
+ noncePriv: proposal.noncePriv,
};
} else {
const paid = !purchase.paymentSubmitPending;
@@ -1475,6 +1489,7 @@ export async function checkPaymentByProposalId(
amountEffective: Amounts.stringify(purchase.totalPayCost),
...(paid ? { nextUrl: purchase.download.contractData.orderId } : {}),
proposalId,
+ noncePriv: proposal.noncePriv,
};
}
}
@@ -1507,6 +1522,7 @@ export async function preparePayForUri(
uriResult.orderId,
uriResult.sessionId,
uriResult.claimToken,
+ uriResult.noncePriv,
);
return checkPaymentByProposalId(ws, proposalId, uriResult.sessionId);
diff --git a/packages/taler-wallet-webextension/package.json b/packages/taler-wallet-webextension/package.json
index 028a5c660..4023e4ebd 100644
--- a/packages/taler-wallet-webextension/package.json
+++ b/packages/taler-wallet-webextension/package.json
@@ -22,6 +22,7 @@
"history": "4.10.1",
"preact": "^10.5.13",
"preact-router": "^3.2.1",
+ "qrcode-generator": "^1.4.4",
"tslib": "^2.1.0"
},
"devDependencies": {
diff --git a/packages/taler-wallet-webextension/src/components/QR.tsx b/packages/taler-wallet-webextension/src/components/QR.tsx
new file mode 100644
index 000000000..8e3f69295
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/components/QR.tsx
@@ -0,0 +1,37 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 <http://www.gnu.org/licenses/>
+ */
+
+ import { h, VNode } from "preact";
+ import { useEffect, useRef } from "preact/hooks";
+ import qrcode from "qrcode-generator";
+
+ export function QR({ text }: { text: string; }):VNode {
+ const divRef = useRef<HTMLDivElement>(null);
+ useEffect(() => {
+ if (!divRef.current) return
+ const qr = qrcode(0, 'L');
+ qr.addData(text);
+ qr.make();
+ divRef.current.innerHTML = qr.createSvgTag({
+ scalable: true,
+ });
+ });
+
+ return <div style={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
+ <div style={{ width: '50%', minWidth: 200, maxWidth: 300 }} ref={divRef} />
+ </div>;
+ }
+ \ No newline at end of file
diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx
index f7945569e..a46f38ee9 100644
--- a/packages/taler-wallet-webextension/src/components/styled/index.tsx
+++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx
@@ -29,10 +29,11 @@ export const PaymentStatus = styled.div<{ color: string }>`
export const WalletAction = styled.section`
display: flex;
+ text-align: center;
flex-direction: column;
justify-content: space-between;
align-items: center;
- max-width: 50%;
+ /* max-width: 50%; */
margin: auto;
height: 100%;
@@ -42,6 +43,10 @@ export const WalletAction = styled.section`
}
section {
margin-bottom: 2em;
+ & button {
+ margin-right: 8px;
+ margin-left: 8px;
+ }
}
`
export const WalletActionOld = styled.section`
@@ -628,6 +633,7 @@ export const TermsOfService = styled.div`
display: flex;
flex-direction: column;
text-align: left;
+ max-width: 500px;
& > header {
text-align: center;
diff --git a/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx b/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx
index 38e3d0f35..9a997687f 100644
--- a/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx
@@ -33,6 +33,7 @@ export default {
export const InsufficientBalance = createExample(TestedComponent, {
payStatus: {
status: PreparePayResultType.InsufficientBalance,
+ noncePriv: '',
proposalId: "proposal1234",
contractTerms: {
merchant: {
@@ -45,15 +46,19 @@ export const InsufficientBalance = createExample(TestedComponent, {
});
export const PaymentPossible = createExample(TestedComponent, {
+ uri: 'taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0',
payStatus: {
status: PreparePayResultType.PaymentPossible,
amountEffective: 'USD:10',
amountRaw: 'USD:10',
+ noncePriv: '',
contractTerms: {
+ nonce: '123213123',
merchant: {
name: 'someone'
},
amount: 'USD:10',
+ summary: 'some beers',
} as Partial<ContractTerms> as any,
contractTermsHash: '123456',
proposalId: 'proposal1234'
@@ -65,6 +70,7 @@ export const AlreadyConfirmedWithFullfilment = createExample(TestedComponent, {
status: PreparePayResultType.AlreadyConfirmed,
amountEffective: 'USD:10',
amountRaw: 'USD:10',
+ noncePriv: '',
contractTerms: {
merchant: {
name: 'someone'
@@ -82,6 +88,7 @@ export const AlreadyConfirmedWithoutFullfilment = createExample(TestedComponent,
payStatus: {
status: PreparePayResultType.AlreadyConfirmed,
amountEffective: 'USD:10',
+ noncePriv: '',
amountRaw: 'USD:10',
contractTerms: {
merchant: {
diff --git a/packages/taler-wallet-webextension/src/cta/Pay.tsx b/packages/taler-wallet-webextension/src/cta/Pay.tsx
index 758bc4b54..e85cd60a1 100644
--- a/packages/taler-wallet-webextension/src/cta/Pay.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Pay.tsx
@@ -29,7 +29,7 @@ import * as wxApi from "../wxApi";
import { useState, useEffect } from "preact/hooks";
-import { ConfirmPayResultDone, getJsonI18n, i18n } from "@gnu-taler/taler-util";
+import { AmountLike, ConfirmPayResultDone, getJsonI18n, i18n } from "@gnu-taler/taler-util";
import {
PreparePayResult,
ConfirmPayResult,
@@ -39,7 +39,11 @@ import {
ContractTerms,
ConfirmPayResultType,
} from "@gnu-taler/taler-util";
-import { JSX, VNode, h } from "preact";
+import { JSX, VNode, h, Fragment } from "preact";
+import { ButtonSuccess, LinkSuccess, WalletAction } from "../components/styled";
+import { LogoHeader } from "../components/LogoHeader";
+import { Part } from "../components/Part";
+import { QR } from "../components/QR";
interface Props {
talerPayUri?: string
@@ -143,17 +147,17 @@ export function PayPage({ talerPayUri }: Props): JSX.Element {
}
- return <PaymentRequestView payStatus={payStatus} onClick={onClick} payErrMsg={payErrMsg} />;
+ return <PaymentRequestView uri={talerPayUri} payStatus={payStatus} onClick={onClick} payErrMsg={payErrMsg} />;
}
export interface PaymentRequestViewProps {
payStatus: PreparePayResult;
onClick: () => void;
payErrMsg?: string;
-
+ uri: string;
}
-export function PaymentRequestView({ payStatus, onClick, payErrMsg }: PaymentRequestViewProps) {
- let totalFees: AmountJson | undefined = undefined;
+export function PaymentRequestView({ uri, payStatus, onClick, payErrMsg }: PaymentRequestViewProps) {
+ let totalFees: AmountJson = Amounts.getZero(payStatus.amountRaw);
let insufficientBalance = false;
const [loading, setLoading] = useState(false);
const contractTerms: ContractTerms = payStatus.contractTerms;
@@ -174,6 +178,7 @@ export function PaymentRequestView({ payStatus, onClick, payErrMsg }: PaymentReq
if (payStatus.status == PreparePayResultType.InsufficientBalance) {
insufficientBalance = true;
+ return <div>no te alcanza</div>
}
if (payStatus.status === PreparePayResultType.PaymentPossible) {
@@ -191,65 +196,62 @@ export function PaymentRequestView({ payStatus, onClick, payErrMsg }: PaymentReq
merchantName = <strong>(pub: {contractTerms.merchant_pub})</strong>;
}
- const amount = (
- <strong>{renderAmount(Amounts.parseOrThrow(contractTerms.amount))}</strong>
- );
-
- return <section class="main">
- <h1>GNU Taler Wallet</h1>
- <article class="fade">
- <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 ? (
- <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>
- ) : (
- <div>
- <ProgressButton
- isLoading={loading}
- disabled={insufficientBalance}
- onClick={onClick}
- >
- {i18n.str`Confirm payment`}
- </ProgressButton>
- </div>
- )}
- </div>
- </article>
- </section>
-
+ const [showQR, setShowQR] = useState<boolean>(false)
+ const privateUri = `${uri}&n=${payStatus.noncePriv}`
+ return <WalletAction>
+ <LogoHeader />
+ <h2>
+ {i18n.str`Digital cash payment`}
+ </h2>
+ <section>
+ <Part big title="Total paid" text={amountToString(payStatus.amountEffective)} kind='negative' />
+ <Part big title="Purchase amount" text={amountToString(payStatus.amountRaw)} kind='neutral' />
+ {Amounts.isNonZero(totalFees) && <Part big title="Fee" text={amountToString(totalFees)} kind='negative' />}
+ <Part title="Merchant" text={contractTerms.merchant.name} kind='neutral' />
+ <Part title="Purchase" text={contractTerms.summary} kind='neutral' />
+ {contractTerms.order_id && <Part title="Receipt" text={`#${contractTerms.order_id}`} kind='neutral' />}
+ </section>
+ {showQR && <section>
+ <QR text={privateUri} />
+ <a href={privateUri}>or click here to pay with a installed wallet</a>
+ </section>}
+ <section>
+ {payErrMsg ? (
+ <div>
+ <p>Payment failed: {payErrMsg}</p>
+ <button
+ class="pure-button button-success"
+ onClick={onClick}
+ >
+ {i18n.str`Retry`}
+ </button>
+ </div>
+ ) : (
+ <Fragment>
+
+ <LinkSuccess
+ upperCased
+ // disabled={!details.exchangeInfo.baseUrl}
+ onClick={() => setShowQR(qr => !qr)}
+ >
+ {!showQR ? i18n.str`Complete with mobile wallet` : i18n.str`Hide QR`}
+ </LinkSuccess>
+ <ButtonSuccess
+ upperCased
+ // disabled={!details.exchangeInfo.baseUrl}
+ // onClick={() => onReview(true)}
+ >
+ {i18n.str`Confirm payment`}
+ </ButtonSuccess>
+ </Fragment>
+ )}
+
+ </section>
+ </WalletAction>
+}
-} \ No newline at end of file
+function amountToString(text: AmountLike) {
+ const aj = Amounts.jsonifyAmount(text)
+ const amount = Amounts.stringifyValue(aj)
+ return `${amount} ${aj.currency}`
+}
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx
index ac25bcd15..304313a9e 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx
@@ -80,20 +80,18 @@ export function View({ details, amount, onWithdraw, terms, reviewing, onReview,
const needsReview = terms.status === 'changed' || terms.status === 'new'
return (
- <WalletAction style={{ textAlign: 'center' }}>
+ <WalletAction>
<LogoHeader />
<h2>
{i18n.str`Digital cash withdrawal`}
</h2>
<section>
- <div>
- <Part title="Total to withdraw" text={amountToString(Amounts.sub(Amounts.parseOrThrow(amount), details.withdrawFee).amount)} kind='positive' />
- <Part title="Chosen amount" text={amountToString(amount)} kind='neutral' />
- {Amounts.isNonZero(details.withdrawFee) &&
- <Part title="Exchange fee" text={amountToString(details.withdrawFee)} kind='negative' />
- }
- <Part title="Exchange" text={details.exchangeInfo.baseUrl} kind='neutral' big />
- </div>
+ <Part title="Total to withdraw" text={amountToString(Amounts.sub(Amounts.parseOrThrow(amount), details.withdrawFee).amount)} kind='positive' />
+ <Part title="Chosen amount" text={amountToString(amount)} kind='neutral' />
+ {Amounts.isNonZero(details.withdrawFee) &&
+ <Part title="Exchange fee" text={amountToString(details.withdrawFee)} kind='negative' />
+ }
+ <Part title="Exchange" text={details.exchangeInfo.baseUrl} kind='neutral' big />
</section>
{!reviewing &&
<section>
@@ -132,63 +130,50 @@ export function View({ details, amount, onWithdraw, terms, reviewing, onReview,
}
{(reviewing || accepted) &&
<section>
- <div>
- <CheckboxOutlined
- name="terms"
- enabled={accepted}
- label={i18n.str`I accept the exchange terms of service`}
- onToggle={() => {
- onAccept(!accepted)
- onReview(false)
- }}
- />
- </div>
+ <CheckboxOutlined
+ name="terms"
+ enabled={accepted}
+ label={i18n.str`I accept the exchange terms of service`}
+ onToggle={() => {
+ onAccept(!accepted)
+ onReview(false)
+ }}
+ />
</section>
}
<section>
{terms.status === 'new' && !accepted &&
- <div>
- <ButtonSuccess
- upperCased
- disabled={!details.exchangeInfo.baseUrl}
- onClick={() => onReview(true)}
- >
- {i18n.str`Review exchange terms of service`}
- </ButtonSuccess>
- </div>
+ <ButtonSuccess
+ upperCased
+ disabled={!details.exchangeInfo.baseUrl}
+ onClick={() => onReview(true)}
+ >
+ {i18n.str`Review exchange terms of service`}
+ </ButtonSuccess>
}
{terms.status === 'changed' && !accepted &&
- <div>
- <ButtonWarning
- upperCased
- disabled={!details.exchangeInfo.baseUrl}
- onClick={() => onReview(true)}
- >
- {i18n.str`Review new version of terms of service`}
- </ButtonWarning>
- </div>
+ <ButtonWarning
+ upperCased
+ disabled={!details.exchangeInfo.baseUrl}
+ onClick={() => onReview(true)}
+ >
+ {i18n.str`Review new version of terms of service`}
+ </ButtonWarning>
}
{(terms.status === 'accepted' || (needsReview && accepted)) &&
- <div>
- <ButtonSuccess
- upperCased
- disabled={!details.exchangeInfo.baseUrl || confirmed}
- onClick={onWithdraw}
- >
- {i18n.str`Confirm withdrawal`}
- </ButtonSuccess>
- </div>
+ <ButtonSuccess
+ upperCased
+ disabled={!details.exchangeInfo.baseUrl || confirmed}
+ onClick={onWithdraw}
+ >
+ {i18n.str`Confirm withdrawal`}
+ </ButtonSuccess>
}
{terms.status === 'notfound' &&
- <div>
- <ButtonDestructive
- upperCased
- disabled={true}
- >
- {i18n.str`Exchange doesn't have terms of service`}
- </ButtonDestructive>
- </div>
+ <ButtonDestructive upperCased disabled>
+ {i18n.str`Exchange doesn't have terms of service`}
+ </ButtonDestructive>
}
</section>
</WalletAction>
@@ -231,12 +216,16 @@ export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element
useEffect(() => {
async function fetchData() {
if (!uriInfo || !uriInfo.defaultExchangeBaseUrl) return
- const res = await getExchangeWithdrawalInfo({
- exchangeBaseUrl: uriInfo.defaultExchangeBaseUrl,
- amount: Amounts.parseOrThrow(uriInfo.amount),
- tosAcceptedFormat: ['text/json', 'text/xml', 'text/pdf']
- })
- setDetails(res)
+ 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])
@@ -249,8 +238,12 @@ export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element
if (!details) {
throw Error("can't accept, no exchange selected");
}
- await setExchangeTosAccepted(details.exchangeDetails.exchangeBaseUrl, details.tosRequested?.tosEtag)
- setAccepted(true)
+ try {
+ await setExchangeTosAccepted(details.exchangeDetails.exchangeBaseUrl, details.tosRequested?.tosEtag)
+ setAccepted(true)
+ } catch (e) {
+ setError(true)
+ }
}
const onWithdraw = async (): Promise<void> => {
@@ -259,10 +252,14 @@ export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element
}
setConfirmed(true)
console.log("accepting exchange", details.exchangeInfo.baseUrl);
- const res = await acceptWithdrawal(talerWithdrawUri, details.exchangeInfo.baseUrl);
- console.log("accept withdrawal response", res);
- if (res.confirmTransferUrl) {
- document.location.href = res.confirmTransferUrl;
+ 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)
}
};
@@ -288,7 +285,7 @@ export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element
} catch (e) {
console.log(e)
debugger;
- }
+ }
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2eb62d7f1..bea05decd 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -248,6 +248,7 @@ importers:
preact-cli: ^3.0.5
preact-render-to-string: ^5.1.19
preact-router: ^3.2.1
+ qrcode-generator: ^1.4.4
rimraf: ^3.0.2
rollup: ^2.37.1
rollup-plugin-css-only: ^3.1.0
@@ -264,6 +265,7 @@ importers:
history: 4.10.1
preact: 10.5.14
preact-router: 3.2.1_preact@10.5.14
+ qrcode-generator: 1.4.4
tslib: 2.3.1
devDependencies:
'@babel/core': 7.13.16
@@ -16698,6 +16700,10 @@ packages:
engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
dev: true
+ /qrcode-generator/1.4.4:
+ resolution: {integrity: sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==}
+ dev: false
+
/qs/6.10.1:
resolution: {integrity: sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==}
engines: {node: '>=0.6'}