gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: issue #5860


From: gnunet
Subject: [taler-wallet-core] branch master updated: issue #5860
Date: Fri, 17 Sep 2021 20:49:15 +0200

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 315b167b issue #5860
315b167b is described below

commit 315b167bee240e625beea731df6472a971b46cb2
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Fri Sep 17 15:48:33 2021 -0300

    issue #5860
---
 packages/taler-util/src/taleruri.ts                |   3 +
 packages/taler-util/src/walletTypes.ts             |   6 +
 .../src/crypto/workers/cryptoApi.ts                |   6 +-
 .../src/crypto/workers/cryptoImplementation.ts     |  11 +-
 packages/taler-wallet-core/src/operations/pay.ts   |  20 ++-
 packages/taler-wallet-webextension/package.json    |   1 +
 .../src/components/QR.tsx                          |  37 ++++++
 .../src/components/styled/index.tsx                |   8 +-
 .../src/cta/Pay.stories.tsx                        |   7 ++
 packages/taler-wallet-webextension/src/cta/Pay.tsx | 136 +++++++++++----------
 .../taler-wallet-webextension/src/cta/Withdraw.tsx | 133 ++++++++++----------
 pnpm-lock.yaml                                     |   6 +
 12 files changed, 233 insertions(+), 141 deletions(-)

diff --git a/packages/taler-util/src/taleruri.ts 
b/packages/taler-util/src/taleruri.ts
index 6c0dc7b8..09c70682 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 79403ac6..2b35423b 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 da92e83c..6bace01a 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 e1580a7d..7112964d 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 9a7b0d06..970aa46f 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 028a5c66..4023e4eb 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 00000000..8e3f6929
--- /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 f7945569..a46f38ee 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 38e3d0f3..9a997687 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 758bc4b5..e85cd60a 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 ac25bcd1..304313a9 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 2eb62d7f..bea05dec 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'}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]