gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: show wire details when the de


From: gnunet
Subject: [taler-wallet-core] branch master updated: show wire details when the deposit has been wired
Date: Wed, 29 Mar 2023 20:15:29 +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 74dba9506 show wire details when the deposit has been wired
74dba9506 is described below

commit 74dba9506dba104d918c5386e67146f71f07436c
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Wed Mar 29 15:14:02 2023 -0300

    show wire details when the deposit has been wired
---
 packages/taler-util/src/transactions-types.ts      | 13 ++-
 packages/taler-wallet-core/src/db.ts               | 14 ++++
 .../taler-wallet-core/src/operations/deposits.ts   | 93 +++++++++++++++++++++-
 .../src/operations/transactions.ts                 |  2 +
 .../src/components/TransactionItem.tsx             | 54 ++++++++++---
 .../src/wallet/Transaction.tsx                     | 68 ++++++++++++++--
 6 files changed, 226 insertions(+), 18 deletions(-)

diff --git a/packages/taler-util/src/transactions-types.ts 
b/packages/taler-util/src/transactions-types.ts
index 9623e25a9..1d7e6ef30 100644
--- a/packages/taler-util/src/transactions-types.ts
+++ b/packages/taler-util/src/transactions-types.ts
@@ -535,7 +535,7 @@ export interface TransactionRefresh extends 
TransactionCommon {
   /**
    * Fees, i.e. the effective, negative effect of the refresh
    * on the balance.
-   * 
+   *
    * Only applicable for stand-alone refreshes, and zero for
    * other refreshes where the transaction itself accounts for the
    * refresh fee.
@@ -578,6 +578,17 @@ export interface TransactionDeposit extends 
TransactionCommon {
    * Did all the deposit requests succeed?
    */
   deposited: boolean;
+
+  trackingState: Array<{
+    // Raw wire transfer identifier of the deposit.
+    wireTransferId: string;
+    // When was the wire transfer given to the bank.
+    timestampExecuted: TalerProtocolTimestamp;
+    // Total amount transfer for this wtid (including fees)
+    amountRaw: AmountString;
+    // Total amount received for this wtid (without fees)
+    amountEffective: AmountString;
+  }>;
 }
 
 export interface TransactionByIdRequest {
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index cd676b7ca..fb5ea025a 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -1671,6 +1671,20 @@ export interface DepositGroupRecord {
   operationStatus: OperationStatus;
 
   transactionPerCoin: TransactionStatus[];
+
+  trackingState?: {
+    [signature: string]: {
+      // Raw wire transfer identifier of the deposit.
+      wireTransferId: string;
+      // When was the wire transfer given to the bank.
+      timestampExecuted: TalerProtocolTimestamp;
+      // Total amount transfer for this wtid (including fees)
+      amountRaw: AmountString;
+      // Total amount received for this wtid (without fees)
+      amountEffective: AmountString;
+      exchangePub: string;
+    };
+  };
 }
 
 /**
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts 
b/packages/taler-wallet-core/src/operations/deposits.ts
index 22283b7a8..c6cd4732c 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -53,12 +53,15 @@ import {
   TrackDepositGroupRequest,
   TrackDepositGroupResponse,
   TrackTransaction,
+  TrackTransactionWired,
   TransactionType,
   URL,
+  WireFee,
 } from "@gnu-taler/taler-util";
 import {
   DenominationRecord,
   DepositGroupRecord,
+  ExchangeDetailsRecord,
   OperationStatus,
   TransactionStatus,
 } from "../db.js";
@@ -157,7 +160,6 @@ export async function processDepositGroup(
     const perm = depositPermissions[i];
 
     let updatedDeposit: boolean | undefined = undefined;
-    let updatedTxStatus: TransactionStatus | undefined = undefined;
 
     if (!depositGroup.depositedPerCoin[i]) {
       const requestBody: ExchangeDepositRequest = {
@@ -186,6 +188,17 @@ export async function processDepositGroup(
       updatedDeposit = true;
     }
 
+    let updatedTxStatus: TransactionStatus | undefined = undefined;
+    type ValueOf<T> = T[keyof T];
+
+    let newWiredTransaction:
+      | {
+          id: string;
+          value: ValueOf<NonNullable<DepositGroupRecord["trackingState"]>>;
+        }
+      | undefined;
+
+    let signature: string | undefined;
     if (depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired) {
       const track = await trackDepositPermission(ws, depositGroup, perm);
 
@@ -207,6 +220,32 @@ export async function processDepositGroup(
         }
       } else if (track.type === "wired") {
         updatedTxStatus = TransactionStatus.Wired;
+
+        const payto = parsePaytoUri(depositGroup.wire.payto_uri);
+        if (!payto) {
+          throw Error(`unparsable payto: ${depositGroup.wire.payto_uri}`);
+        }
+
+        const fee = await getExchangeWireFee(
+          ws,
+          payto.targetType,
+          perm.exchange_url,
+          track.execution_time,
+        );
+        const raw = Amounts.parseOrThrow(track.coin_contribution);
+        const wireFee = Amounts.parseOrThrow(fee.wireFee);
+        const effective = Amounts.sub(raw, wireFee).amount;
+
+        newWiredTransaction = {
+          value: {
+            amountRaw: Amounts.stringify(raw),
+            amountEffective: Amounts.stringify(effective),
+            exchangePub: track.exchange_pub,
+            timestampExecuted: track.execution_time,
+            wireTransferId: track.wtid,
+          },
+          id: track.exchange_sig,
+        };
       } else {
         updatedTxStatus = TransactionStatus.Unknown;
       }
@@ -226,6 +265,14 @@ export async function processDepositGroup(
           if (updatedTxStatus !== undefined) {
             dg.transactionPerCoin[i] = updatedTxStatus;
           }
+          if (newWiredTransaction) {
+            if (!dg.trackingState) {
+              dg.trackingState = {};
+            }
+
+            dg.trackingState[newWiredTransaction.id] =
+              newWiredTransaction.value;
+          }
           await tx.depositGroups.put(dg);
         });
     }
@@ -257,6 +304,50 @@ export async function processDepositGroup(
   return OperationAttemptResult.finishedEmpty();
 }
 
+async function getExchangeWireFee(
+  ws: InternalWalletState,
+  wireType: string,
+  baseUrl: string,
+  time: TalerProtocolTimestamp,
+): Promise<WireFee> {
+  const exchangeDetails = await ws.db
+    .mktx((x) => [x.exchanges, x.exchangeDetails])
+    .runReadOnly(async (tx) => {
+      const ex = await tx.exchanges.get(baseUrl);
+      if (!ex || !ex.detailsPointer) return undefined;
+      return await tx.exchangeDetails.indexes.byPointer.get([
+        baseUrl,
+        ex.detailsPointer.currency,
+        ex.detailsPointer.masterPublicKey,
+      ]);
+    });
+
+  if (!exchangeDetails) {
+    throw Error(`exchange missing: ${baseUrl}`);
+  }
+
+  const fees = exchangeDetails.wireInfo.feesForType[wireType];
+  if (!fees || fees.length === 0) {
+    throw Error(
+      `exchange ${baseUrl} doesn't have fees for wire type ${wireType}`,
+    );
+  }
+  const fee = fees.find((x) => {
+    return AbsoluteTime.isBetween(
+      AbsoluteTime.fromTimestamp(time),
+      AbsoluteTime.fromTimestamp(x.startStamp),
+      AbsoluteTime.fromTimestamp(x.endStamp),
+    );
+  });
+  if (!fee) {
+    throw Error(
+      `exchange ${exchangeDetails.exchangeBaseUrl} doesn't have fees for wire 
type ${wireType} at ${time.t_s}`,
+    );
+  }
+
+  return fee;
+}
+
 export async function trackDepositGroup(
   ws: InternalWalletState,
   req: TrackDepositGroupRequest,
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index 3e396098e..c0045aced 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -764,6 +764,7 @@ function buildTransactionForDeposit(
       deposited = false;
     }
   }
+
   return {
     type: TransactionType.Deposit,
     amountRaw: Amounts.stringify(dg.effectiveDepositAmount),
@@ -788,6 +789,7 @@ function buildTransactionForDeposit(
         )) /
       dg.transactionPerCoin.length,
     depositGroupId: dg.depositGroupId,
+    trackingState: Object.values(dg.trackingState ?? {}),
     deposited,
     ...(ort?.lastError ? { error: ort.lastError } : {}),
   };
diff --git 
a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx 
b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
index 934a0fe52..7ddaee9f3 100644
--- a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
+++ b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
@@ -76,7 +76,11 @@ export function TransactionItem(props: { tx: Transaction }): 
VNode {
           subtitle={tx.info.summary}
           timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"P"}
-          // pending={tx.pending}
+          pending={
+            tx.extendedStatus === ExtendedStatus.Pending
+              ? i18n.str`Payment in progress`
+              : undefined
+          }
         />
       );
     case TransactionType.Refund:
@@ -89,7 +93,11 @@ export function TransactionItem(props: { tx: Transaction }): 
VNode {
           title={tx.info.merchant.name}
           timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"R"}
-          // pending={tx.pending}
+          pending={
+            tx.extendedStatus === ExtendedStatus.Pending
+              ? i18n.str`Executing refund...`
+              : undefined
+          }
         />
       );
     case TransactionType.Tip:
@@ -101,7 +109,11 @@ export function TransactionItem(props: { tx: Transaction 
}): VNode {
           title={new URL(tx.merchantBaseUrl).hostname}
           timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"T"}
-          // pending={tx.pending}
+          pending={
+            tx.extendedStatus === ExtendedStatus.Pending
+              ? i18n.str`Grabbing the tipping...`
+              : undefined
+          }
         />
       );
     case TransactionType.Refresh:
@@ -113,7 +125,11 @@ export function TransactionItem(props: { tx: Transaction 
}): VNode {
           title={"Refresh"}
           timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"R"}
-          // pending={tx.pending}
+          pending={
+            tx.extendedStatus === ExtendedStatus.Pending
+              ? i18n.str`Refreshing coins...`
+              : undefined
+          }
         />
       );
     case TransactionType.Deposit:
@@ -125,7 +141,11 @@ export function TransactionItem(props: { tx: Transaction 
}): VNode {
           title={tx.targetPaytoUri}
           timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"D"}
-          // pending={tx.pending}
+          pending={
+            tx.extendedStatus === ExtendedStatus.Pending
+              ? i18n.str`Deposit in progress`
+              : undefined
+          }
         />
       );
     case TransactionType.PeerPullCredit:
@@ -137,7 +157,11 @@ export function TransactionItem(props: { tx: Transaction 
}): VNode {
           title={tx.info.summary || "Invoice"}
           timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"I"}
-          // pending={tx.pending}
+          pending={
+            tx.extendedStatus === ExtendedStatus.Pending
+              ? i18n.str`Waiting to be paid`
+              : undefined
+          }
         />
       );
     case TransactionType.PeerPullDebit:
@@ -149,7 +173,11 @@ export function TransactionItem(props: { tx: Transaction 
}): VNode {
           title={tx.info.summary || "Invoice"}
           timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"I"}
-          // pending={tx.pending}
+          pending={
+            tx.extendedStatus === ExtendedStatus.Pending
+              ? i18n.str`Payment in progress`
+              : undefined
+          }
         />
       );
     case TransactionType.PeerPushCredit:
@@ -161,7 +189,11 @@ export function TransactionItem(props: { tx: Transaction 
}): VNode {
           title={tx.info.summary || "Transfer"}
           timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"T"}
-          // pending={tx.pending}
+          pending={
+            tx.extendedStatus === ExtendedStatus.Pending
+              ? i18n.str`Receiving the transfer`
+              : undefined
+          }
         />
       );
     case TransactionType.PeerPushDebit:
@@ -173,7 +205,11 @@ export function TransactionItem(props: { tx: Transaction 
}): VNode {
           title={tx.info.summary || "Transfer"}
           timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"T"}
-          // pending={tx.pending}
+          pending={
+            tx.extendedStatus === ExtendedStatus.Pending
+              ? i18n.str`Waiting to be received`
+              : undefined
+          }
         />
       );
     default: {
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx 
b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index 217a77575..a9683f680 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -28,6 +28,7 @@ import {
   stringifyPaytoUri,
   TalerProtocolTimestamp,
   Transaction,
+  TransactionDeposit,
   TransactionType,
   TranslatedString,
   WithdrawalType,
@@ -714,13 +715,24 @@ export function TransactionView({
             }}
           />
         ) : transaction.wireTransferProgress === 100 ? (
-          <AlertView
-            alert={{
-              type: "success",
-              message: i18n.str`Wire transfer completed`,
-              description: i18n.str` `,
-            }}
-          />
+          <Fragment>
+            <AlertView
+              alert={{
+                type: "success",
+                message: i18n.str`Wire transfer completed`,
+                description: i18n.str` `,
+              }}
+            />
+            <Part
+              title={i18n.str`Transfer details`}
+              text={
+                <TrackingDepositDetails
+                  trackingState={transaction.trackingState}
+                />
+              }
+              kind="neutral"
+            />
+          </Fragment>
         ) : (
           <AlertView
             alert={{
@@ -1559,6 +1571,48 @@ function RefundDetails({ amount }: { amount: 
AmountWithFee }): VNode {
   );
 }
 
+function TrackingDepositDetails({
+  trackingState,
+}: {
+  trackingState: TransactionDeposit["trackingState"];
+}): VNode {
+  const { i18n } = useTranslationContext();
+
+  const trackByWtid = Object.values(trackingState ?? {}).reduce((prev, cur) => 
{
+    const am = Amounts.parseOrThrow(cur.amountEffective);
+    const sum = !prev[cur.wireTransferId]
+      ? am
+      : Amounts.add(prev[cur.wireTransferId], am).amount;
+    prev[cur.wireTransferId] = sum;
+    return prev;
+  }, {} as Record<string, AmountJson>);
+  const wireTransfers = Object.entries(trackByWtid).map(([id, amountJson]) => 
({
+    id,
+    amount: Amounts.stringify(amountJson),
+  }));
+
+  return (
+    <PurchaseDetailsTable>
+      <tr>
+        <td>
+          <i18n.Translate>Transfer identification</i18n.Translate>
+        </td>
+        <td>
+          <i18n.Translate>Amount</i18n.Translate>
+        </td>
+      </tr>
+
+      {wireTransfers.map((wire) => (
+        <tr>
+          <td>{wire.id}</td>
+          <td>
+            <Amount value={wire.amount} />
+          </td>
+        </tr>
+      ))}
+    </PurchaseDetailsTable>
+  );
+}
 function DepositDetails({ amount }: { amount: AmountWithFee }): VNode {
   const { i18n } = useTranslationContext();
 

-- 
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]