gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: wallet-core: new states for w


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core: new states for withdrawal, prepare/confirm requests
Date: Tue, 30 Apr 2024 11:51:01 +0200

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 8b5d1276b wallet-core: new states for withdrawal, prepare/confirm 
requests
8b5d1276b is described below

commit 8b5d1276b9d9043e85cba91704c908ff544916e0
Author: Florian Dold <florian@dold.me>
AuthorDate: Tue Apr 30 11:50:59 2024 +0200

    wallet-core: new states for withdrawal, prepare/confirm requests
---
 .../src/integrationtests/test-currency-scope.ts    |   3 +-
 packages/taler-util/src/notifications.ts           |   3 +
 packages/taler-util/src/transactions-types.ts      |   1 +
 packages/taler-util/src/wallet-types.ts            |  30 +++
 packages/taler-wallet-core/src/balance.ts          |   3 +
 packages/taler-wallet-core/src/db.ts               |  20 ++
 packages/taler-wallet-core/src/wallet-api-types.ts |  27 ++
 packages/taler-wallet-core/src/wallet.ts           |  18 ++
 packages/taler-wallet-core/src/withdraw.ts         | 272 +++++++++++++++++----
 9 files changed, 332 insertions(+), 45 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-currency-scope.ts 
b/packages/taler-harness/src/integrationtests/test-currency-scope.ts
index e07a8f47b..058941e16 100644
--- a/packages/taler-harness/src/integrationtests/test-currency-scope.ts
+++ b/packages/taler-harness/src/integrationtests/test-currency-scope.ts
@@ -18,7 +18,7 @@
  * Imports.
  */
 import { Duration, j2s } from "@gnu-taler/taler-util";
-import { Wallet, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { defaultCoinConfig } from "../harness/denomStructures.js";
 import {
   BankService,
@@ -30,7 +30,6 @@ import {
 } from "../harness/harness.js";
 import {
   createWalletDaemonWithClient,
-  makeTestPaymentV2,
   withdrawViaBankV2,
 } from "../harness/helpers.js";
 
diff --git a/packages/taler-util/src/notifications.ts 
b/packages/taler-util/src/notifications.ts
index b60fb267c..d4dfe7589 100644
--- a/packages/taler-util/src/notifications.ts
+++ b/packages/taler-util/src/notifications.ts
@@ -30,6 +30,9 @@ export enum NotificationType {
   BalanceChange = "balance-change",
   BackupOperationError = "backup-error",
   TransactionStateTransition = "transaction-state-transition",
+  /**
+   * @deprecated
+   */
   WithdrawalOperationTransition = "withdrawal-operation-transition",
   ExchangeStateTransition = "exchange-state-transition",
   Idle = "idle",
diff --git a/packages/taler-util/src/transactions-types.ts 
b/packages/taler-util/src/transactions-types.ts
index ac4c3d717..cee3de9fa 100644
--- a/packages/taler-util/src/transactions-types.ts
+++ b/packages/taler-util/src/transactions-types.ts
@@ -151,6 +151,7 @@ export enum TransactionMinorState {
   RefundAvailable = "refund-available",
   AcceptRefund = "accept-refund",
   PaidByOther = "paid-by-other",
+  CompletedByOtherWallet = "completed-by-other-wallet",
 }
 
 export enum TransactionAction {
diff --git a/packages/taler-util/src/wallet-types.ts 
b/packages/taler-util/src/wallet-types.ts
index 9575e6d7d..ccf3c230a 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -1883,6 +1883,36 @@ export interface GetWithdrawalDetailsForAmountRequest {
   clientCancellationId?: string;
 }
 
+export interface PrepareBankIntegratedWithdrawalRequest {
+  talerWithdrawUri: string;
+  exchangeBaseUrl: string;
+  forcedDenomSel?: ForcedDenomSel;
+  restrictAge?: number;
+}
+
+export const codecForPrepareBankIntegratedWithdrawalRequest =
+  (): Codec<PrepareBankIntegratedWithdrawalRequest> =>
+    buildCodecForObject<PrepareBankIntegratedWithdrawalRequest>()
+      .property("exchangeBaseUrl", codecForString())
+      .property("talerWithdrawUri", codecForString())
+      .property("forcedDenomSel", codecForAny())
+      .property("restrictAge", codecOptional(codecForNumber()))
+      .build("PrepareBankIntegratedWithdrawalRequest");
+
+export interface PrepareBankIntegratedWithdrawalResponse {
+  transactionId: string;
+}
+
+export interface ConfirmWithdrawalRequest {
+  transactionId: string;
+}
+
+export const codecForConfirmWithdrawalRequestRequest =
+  (): Codec<ConfirmWithdrawalRequest> =>
+    buildCodecForObject<ConfirmWithdrawalRequest>()
+      .property("transactionId", codecForString())
+      .build("ConfirmWithdrawalRequest");
+
 export interface AcceptBankIntegratedWithdrawalRequest {
   talerWithdrawUri: string;
   exchangeBaseUrl: string;
diff --git a/packages/taler-wallet-core/src/balance.ts 
b/packages/taler-wallet-core/src/balance.ts
index 1fef9876e..5a805b477 100644
--- a/packages/taler-wallet-core/src/balance.ts
+++ b/packages/taler-wallet-core/src/balance.ts
@@ -357,6 +357,9 @@ export async function getBalancesInsideTransaction(
         case WithdrawalGroupStatus.AbortedExchange:
         case WithdrawalGroupStatus.FailedAbortingBank:
         case WithdrawalGroupStatus.FailedBankAborted:
+        case WithdrawalGroupStatus.AbortedOtherWallet:
+        case WithdrawalGroupStatus.AbortedUserRefused:
+        case WithdrawalGroupStatus.DialogProposed:
         case WithdrawalGroupStatus.Done:
           // Does not count as pendingIncoming
           return;
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index 085e909cf..1edafb315 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -297,6 +297,11 @@ export enum WithdrawalGroupStatus {
   PendingReady = 0x0100_0004,
   SuspendedReady = 0x0110_0004,
 
+  /**
+   * Proposed to the user, has can choose to accept/refuse.
+   */
+  DialogProposed = 0x0101_0000,
+
   /**
    * We are telling the bank that we don't want to complete
    * the withdrawal!
@@ -338,6 +343,21 @@ export enum WithdrawalGroupStatus {
   AbortedExchange = 0x0503_0001,
 
   AbortedBank = 0x0503_0002,
+
+  /**
+   * User didn't refused the withdrawal.
+   */
+  AbortedUserRefused = 0x0503_0003,
+
+  /**
+   * Another wallet confirmed the withdrawal
+   * (by POSTing the reseve pub to the bank)
+   * before we had the chance.
+   * 
+   * In this situation, we'll let the other wallet continue
+   * and give up ourselves.
+   */
+  AbortedOtherWallet = 0x0503_0004,
 }
 
 /**
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts 
b/packages/taler-wallet-core/src/wallet-api-types.ts
index e876d8aea..f83db6039 100644
--- a/packages/taler-wallet-core/src/wallet-api-types.ts
+++ b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -47,6 +47,7 @@ import {
   ConfirmPayResult,
   ConfirmPeerPullDebitRequest,
   ConfirmPeerPushCreditRequest,
+  ConfirmWithdrawalRequest,
   ConvertAmountRequest,
   CreateDepositGroupRequest,
   CreateDepositGroupResponse,
@@ -91,6 +92,8 @@ import {
   ListGlobalCurrencyAuditorsResponse,
   ListGlobalCurrencyExchangesResponse,
   ListKnownBankAccountsRequest,
+  PrepareBankIntegratedWithdrawalRequest,
+  PrepareBankIntegratedWithdrawalResponse,
   PrepareDepositRequest,
   PrepareDepositResponse,
   PreparePayRequest,
@@ -195,6 +198,8 @@ export enum WalletApiOperation {
   SetExchangeTosForgotten = "SetExchangeTosForgotten",
   StartRefundQueryForUri = "startRefundQueryForUri",
   StartRefundQuery = "startRefundQuery",
+  PrepareBankIntegratedWithdrawal = "prepareBankIntegratedWithdrawal",
+  ConfirmWithdrawal = "confirmWithdrawal",
   AcceptBankIntegratedWithdrawal = "acceptBankIntegratedWithdrawal",
   GetExchangeTos = "getExchangeTos",
   GetExchangeDetailedInfo = "getExchangeDetailedInfo",
@@ -475,8 +480,28 @@ export type GetWithdrawalDetailsForUriOp = {
   response: WithdrawUriInfoResponse;
 };
 
+/**
+ * Prepare a bank-integrated withdrawal operation.
+ */
+export type PrepareBankIntegratedWithdrawalOp = {
+  op: WalletApiOperation.PrepareBankIntegratedWithdrawal;
+  request: PrepareBankIntegratedWithdrawalRequest;
+  response: PrepareBankIntegratedWithdrawalResponse;
+};
+
+/**
+ * Confirm a withdrawal transaction.
+ */
+export type ConfirmWithdrawalOp = {
+  op: WalletApiOperation.ConfirmWithdrawal;
+  request: ConfirmWithdrawalRequest;
+  response: EmptyObject;
+};
+
 /**
  * Accept a bank-integrated withdrawal.
+ *
+ * @deprecated in favor of prepare/confirm withdrawal.
  */
 export type AcceptBankIntegratedWithdrawalOp = {
   op: WalletApiOperation.AcceptBankIntegratedWithdrawal;
@@ -1292,6 +1317,8 @@ export type WalletOperations = {
   [WalletApiOperation.TestingGetDenomStats]: TestingGetDenomStatsOp;
   [WalletApiOperation.TestingPing]: TestingPingOp;
   [WalletApiOperation.Shutdown]: ShutdownOp;
+  [WalletApiOperation.PrepareBankIntegratedWithdrawal]: 
PrepareBankIntegratedWithdrawalOp;
+  [WalletApiOperation.ConfirmWithdrawal]: ConfirmWithdrawalOp;
 };
 
 export type WalletCoreRequestType<
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index dd6ce96f4..8bfb6772f 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -86,6 +86,7 @@ import {
   codecForCheckPeerPushDebitRequest,
   codecForConfirmPayRequest,
   codecForConfirmPeerPushPaymentRequest,
+  codecForConfirmWithdrawalRequestRequest,
   codecForConvertAmountRequest,
   codecForCreateDepositGroupRequest,
   codecForDeleteExchangeRequest,
@@ -111,6 +112,7 @@ import {
   codecForIntegrationTestV2Args,
   codecForListExchangesForScopedCurrencyRequest,
   codecForListKnownBankAccounts,
+  codecForPrepareBankIntegratedWithdrawalRequest,
   codecForPrepareDepositRequest,
   codecForPreparePayRequest,
   codecForPreparePayTemplateRequest,
@@ -295,9 +297,11 @@ import {
 } from "./wallet-api-types.js";
 import {
   acceptWithdrawalFromUri,
+  confirmWithdrawal,
   createManualWithdrawal,
   getWithdrawalDetailsForAmount,
   getWithdrawalDetailsForUri,
+  prepareBankIntegratedWithdrawal,
 } from "./withdraw.js";
 
 const logger = new Logger("wallet.ts");
@@ -965,6 +969,20 @@ async function dispatchRequestInternal(
         restrictAge: req.restrictAge,
       });
     }
+    case WalletApiOperation.ConfirmWithdrawal: {
+      const req = codecForConfirmWithdrawalRequestRequest().decode(payload);
+      return confirmWithdrawal(wex, req.transactionId);
+    }
+    case WalletApiOperation.PrepareBankIntegratedWithdrawal: {
+      const req =
+        codecForPrepareBankIntegratedWithdrawalRequest().decode(payload);
+      return prepareBankIntegratedWithdrawal(wex, {
+        selectedExchange: req.exchangeBaseUrl,
+        talerWithdrawUri: req.talerWithdrawUri,
+        forcedDenomSel: req.forcedDenomSel,
+        restrictAge: req.restrictAge,
+      });
+    }
     case WalletApiOperation.GetExchangeTos: {
       const req = codecForGetExchangeTosRequest().decode(payload);
       return getExchangeTos(
diff --git a/packages/taler-wallet-core/src/withdraw.ts 
b/packages/taler-wallet-core/src/withdraw.ts
index a55ada796..0597c051f 100644
--- a/packages/taler-wallet-core/src/withdraw.ts
+++ b/packages/taler-wallet-core/src/withdraw.ts
@@ -56,6 +56,7 @@ import {
   Logger,
   NotificationType,
   ObservabilityEventType,
+  PrepareBankIntegratedWithdrawalResponse,
   TalerBankIntegrationHttpClient,
   TalerError,
   TalerErrorCode,
@@ -79,7 +80,9 @@ import {
   assertUnreachable,
   canonicalizeBaseUrl,
   checkDbInvariant,
+  checkLogicInvariant,
   codeForBankWithdrawalOperationPostResponse,
+  codecForBankWithdrawalOperationStatus,
   codecForCashinConversionResponse,
   codecForConversionBankConfig,
   codecForExchangeWithdrawBatchResponse,
@@ -154,6 +157,7 @@ import {
   constructTransactionIdentifier,
   isUnsuccessfulTransaction,
   notifyTransition,
+  parseTransactionIdentifier,
 } from "./transactions.js";
 import {
   WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
@@ -164,7 +168,7 @@ import { WalletExecutionContext, getDenomInfo } from 
"./wallet.js";
 /**
  * Logger for this file.
  */
-const logger = new Logger("operations/withdraw.ts");
+const logger = new Logger("withdraw.ts");
 
 /**
  * Update the materialized withdrawal transaction based
@@ -466,13 +470,18 @@ export class WithdrawTransactionContext implements 
TransactionContext {
             break;
           case WithdrawalGroupStatus.SuspendedAbortingBank:
           case WithdrawalGroupStatus.AbortingBank:
+          case WithdrawalGroupStatus.AbortedUserRefused:
             // No transition needed, but not an error
             return TransitionResult.stay();
+          case WithdrawalGroupStatus.DialogProposed:
+            newStatus = WithdrawalGroupStatus.AbortedUserRefused;
+            break;
           case WithdrawalGroupStatus.Done:
           case WithdrawalGroupStatus.FailedBankAborted:
           case WithdrawalGroupStatus.AbortedExchange:
           case WithdrawalGroupStatus.AbortedBank:
           case WithdrawalGroupStatus.FailedAbortingBank:
+          case WithdrawalGroupStatus.AbortedOtherWallet:
             // Not allowed
             throw Error("abort not allowed in current state");
           default:
@@ -658,6 +667,21 @@ export function computeWithdrawalTransactionStatus(
         major: TransactionMajorState.Aborted,
         minor: TransactionMinorState.Bank,
       };
+    case WithdrawalGroupStatus.AbortedUserRefused:
+      return {
+        major: TransactionMajorState.Aborted,
+        minor: TransactionMinorState.Refused,
+      };
+    case WithdrawalGroupStatus.DialogProposed:
+      return {
+        major: TransactionMajorState.Dialog,
+        minor: TransactionMinorState.Proposed,
+      };
+    case WithdrawalGroupStatus.AbortedOtherWallet:
+      return {
+        major: TransactionMajorState.Aborted,
+        minor: TransactionMinorState.CompletedByOtherWallet,
+      };
   }
 }
 
@@ -702,14 +726,78 @@ export function computeWithdrawalTransactionActions(
     case WithdrawalGroupStatus.SuspendedKyc:
       return [TransactionAction.Resume, TransactionAction.Abort];
     case WithdrawalGroupStatus.FailedAbortingBank:
-      return [TransactionAction.Delete];
     case WithdrawalGroupStatus.AbortedExchange:
-      return [TransactionAction.Delete];
     case WithdrawalGroupStatus.AbortedBank:
+    case WithdrawalGroupStatus.AbortedOtherWallet:
+    case WithdrawalGroupStatus.AbortedUserRefused:
       return [TransactionAction.Delete];
+    case WithdrawalGroupStatus.DialogProposed:
+      return [TransactionAction.Abort];
   }
 }
 
+async function processWithdrawalGroupDialogProposed(
+  ctx: WithdrawTransactionContext,
+  withdrawalGroup: WithdrawalGroupRecord,
+): Promise<TaskRunResult> {
+  if (
+    withdrawalGroup.wgInfo.withdrawalType !==
+    WithdrawalRecordType.BankIntegrated
+  ) {
+    throw new Error(
+      "processWithdrawalGroupDialogProposed called in unexpected state",
+    );
+  }
+
+  const talerWithdrawUri = withdrawalGroup.wgInfo.bankInfo.talerWithdrawUri;
+
+  const parsedUri = parseWithdrawUri(talerWithdrawUri);
+
+  checkLogicInvariant(!!parsedUri);
+
+  const wopid = parsedUri.withdrawalOperationId;
+
+  const url = new URL(
+    `withdrawal-operation/${wopid}`,
+    parsedUri.bankIntegrationApiBaseUrl,
+  );
+
+  url.searchParams.set("old_state", "pending");
+  url.searchParams.set("long_poll_ms", "30000");
+
+  const resp = await ctx.wex.http.fetch(url.href, {
+    method: "GET",
+    cancellationToken: ctx.wex.cancellationToken,
+  });
+
+  // If the bank claims that the withdrawal operation is already
+  // pending, but we're still in DialogProposed, some other wallet
+  // must've completed the withdrawal, we're giving up.
+
+  switch (resp.status) {
+    case HttpStatusCode.Ok: {
+      const body = await readSuccessResponseJsonOrThrow(
+        resp,
+        codecForBankWithdrawalOperationStatus(),
+      );
+      if (body.status !== "pending") {
+        await ctx.transition({}, async (rec) => {
+          switch (rec?.status) {
+            case WithdrawalGroupStatus.DialogProposed: {
+              rec.status = WithdrawalGroupStatus.AbortedOtherWallet;
+              return TransitionResult.transition(rec);
+            }
+          }
+          return TransitionResult.stay();
+        });
+      }
+      break;
+    }
+  }
+
+  return TaskRunResult.longpollReturnedPending();
+}
+
 /**
  * Get information about a withdrawal from
  * a taler://withdraw URI by asking the bank.
@@ -1907,6 +1995,8 @@ export async function processWithdrawalGroup(
     throw Error(`withdrawal group ${withdrawalGroupId} not found`);
   }
 
+  const ctx = new WithdrawTransactionContext(wex, withdrawalGroupId);
+
   switch (withdrawalGroup.status) {
     case WithdrawalGroupStatus.PendingRegisteringBank:
       return await processBankRegisterReserve(wex, withdrawalGroupId);
@@ -1924,6 +2014,8 @@ export async function processWithdrawalGroup(
       return await processWithdrawalGroupPendingReady(wex, withdrawalGroup);
     case WithdrawalGroupStatus.AbortingBank:
       return await processWithdrawalGroupAbortingBank(wex, withdrawalGroup);
+    case WithdrawalGroupStatus.DialogProposed:
+      return await processWithdrawalGroupDialogProposed(ctx, withdrawalGroup);
     case WithdrawalGroupStatus.AbortedBank:
     case WithdrawalGroupStatus.AbortedExchange:
     case WithdrawalGroupStatus.FailedAbortingBank:
@@ -1936,6 +2028,8 @@ export async function processWithdrawalGroup(
     case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
     case WithdrawalGroupStatus.Done:
     case WithdrawalGroupStatus.FailedBankAborted:
+    case WithdrawalGroupStatus.AbortedUserRefused:
+    case WithdrawalGroupStatus.AbortedOtherWallet:
       // Nothing to do.
       return TaskRunResult.finished();
     default:
@@ -2073,12 +2167,6 @@ export interface GetWithdrawalDetailsForUriOpts {
   notifyChangeFromPendingTimeoutMs?: number;
 }
 
-type WithdrawalOperationMemoryMap = {
-  [uri: string]: boolean | undefined;
-};
-
-const ongoingChecks: WithdrawalOperationMemoryMap = {};
-
 /**
  * Get more information about a taler://withdraw URI.
  *
@@ -2119,37 +2207,6 @@ export async function getWithdrawalDetailsForUri(
     );
   });
 
-  // FIXME: this should be removed after the extended version of
-  // withdrawal state machine. issue #8099
-  if (
-    info.status === "pending" &&
-    opts.notifyChangeFromPendingTimeoutMs !== undefined &&
-    !ongoingChecks[talerWithdrawUri]
-  ) {
-    ongoingChecks[talerWithdrawUri] = true;
-    const bankApi = new TalerBankIntegrationHttpClient(
-      info.apiBaseUrl,
-      wex.http,
-    );
-
-    bankApi
-      .getWithdrawalOperationById(info.operationId, {
-        old_state: "pending",
-        timeoutMs: opts.notifyChangeFromPendingTimeoutMs,
-      })
-      .then((resp) => {
-        if (resp.type === "ok" && resp.body.status !== "pending") {
-          wex.ws.notify({
-            type: NotificationType.WithdrawalOperationTransition,
-            uri: talerWithdrawUri,
-          });
-        }
-      })
-      .finally(() => {
-        ongoingChecks[talerWithdrawUri] = false;
-      });
-  }
-
   return {
     operationId: info.operationId,
     confirmTransferUrl: info.confirmTransferUrl,
@@ -2731,6 +2788,135 @@ export async function internalCreateWithdrawalGroup(
   return res.withdrawalGroup;
 }
 
+export async function prepareBankIntegratedWithdrawal(
+  wex: WalletExecutionContext,
+  req: {
+    talerWithdrawUri: string;
+    selectedExchange: string;
+    forcedDenomSel?: ForcedDenomSel;
+    restrictAge?: number;
+  },
+): Promise<PrepareBankIntegratedWithdrawalResponse> {
+  const existingWithdrawalGroup = await wex.db.runReadOnlyTx(
+    { storeNames: ["withdrawalGroups"] },
+    async (tx) => {
+      return await tx.withdrawalGroups.indexes.byTalerWithdrawUri.get(
+        req.talerWithdrawUri,
+      );
+    },
+  );
+
+  if (existingWithdrawalGroup) {
+    let url: string | undefined;
+    if (
+      existingWithdrawalGroup.wgInfo.withdrawalType ===
+      WithdrawalRecordType.BankIntegrated
+    ) {
+      url = existingWithdrawalGroup.wgInfo.bankInfo.confirmUrl;
+    }
+    return {
+      transactionId: constructTransactionIdentifier({
+        tag: TransactionType.Withdrawal,
+        withdrawalGroupId: existingWithdrawalGroup.withdrawalGroupId,
+      }),
+    };
+  }
+
+  const selectedExchange = canonicalizeBaseUrl(req.selectedExchange);
+  const exchange = await fetchFreshExchange(wex, selectedExchange);
+
+  const withdrawInfo = await getBankWithdrawalInfo(
+    wex.http,
+    req.talerWithdrawUri,
+  );
+  const exchangePaytoUri = await getExchangePaytoUri(
+    wex,
+    selectedExchange,
+    withdrawInfo.wireTypes,
+  );
+
+  const withdrawalAccountList = await fetchWithdrawalAccountInfo(
+    wex,
+    {
+      exchange,
+      instructedAmount: withdrawInfo.amount,
+    },
+    wex.cancellationToken,
+  );
+
+  const withdrawalGroup = await internalCreateWithdrawalGroup(wex, {
+    amount: withdrawInfo.amount,
+    exchangeBaseUrl: req.selectedExchange,
+    wgInfo: {
+      withdrawalType: WithdrawalRecordType.BankIntegrated,
+      exchangeCreditAccounts: withdrawalAccountList,
+      bankInfo: {
+        exchangePaytoUri,
+        talerWithdrawUri: req.talerWithdrawUri,
+        confirmUrl: withdrawInfo.confirmTransferUrl,
+        timestampBankConfirmed: undefined,
+        timestampReserveInfoPosted: undefined,
+      },
+    },
+    restrictAge: req.restrictAge,
+    forcedDenomSel: req.forcedDenomSel,
+    reserveStatus: WithdrawalGroupStatus.DialogProposed,
+  });
+
+  const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
+
+  const ctx = new WithdrawTransactionContext(wex, withdrawalGroupId);
+
+  wex.taskScheduler.startShepherdTask(ctx.taskId);
+
+  return {
+    transactionId: ctx.transactionId,
+  };
+}
+
+export async function confirmWithdrawal(
+  wex: WalletExecutionContext,
+  transactionId: string,
+): Promise<void> {
+  const parsedTx = parseTransactionIdentifier(transactionId);
+  if (parsedTx?.tag !== TransactionType.Withdrawal) {
+    throw Error("invalid withdrawal transaction ID");
+  }
+  const withdrawalGroup = await wex.db.runReadOnlyTx(
+    { storeNames: ["withdrawalGroups"] },
+    async (tx) => {
+      return await tx.withdrawalGroups.indexes.byTalerWithdrawUri.get(
+        parsedTx.withdrawalGroupId,
+      );
+    },
+  );
+
+  if (!withdrawalGroup) {
+    throw Error("withdrawal group not found");
+  }
+
+  const ctx = new WithdrawTransactionContext(
+    wex,
+    withdrawalGroup.withdrawalGroupId,
+  );
+  ctx.transition({}, async (rec) => {
+    if (!rec) {
+      return TransitionResult.stay();
+    }
+    switch (rec.status) {
+      case WithdrawalGroupStatus.DialogProposed: {
+        rec.status = WithdrawalGroupStatus.PendingRegisteringBank;
+        return TransitionResult.transition(rec);
+      }
+      default:
+        throw Error("unable to confirm withdrawal in current state");
+    }
+  });
+
+  await wex.taskScheduler.resetTaskRetries(ctx.taskId);
+  wex.taskScheduler.startShepherdTask(ctx.taskId);
+}
+
 /**
  * Accept a bank-integrated withdrawal.
  *
@@ -2738,6 +2924,8 @@ export async function internalCreateWithdrawalGroup(
  *
  * Thus after this call returns, the withdrawal operation can be confirmed
  * with the bank.
+ *
+ * @deprecated in favor of prepare/accept
  */
 export async function acceptWithdrawalFromUri(
   wex: WalletExecutionContext,
@@ -2779,7 +2967,7 @@ export async function acceptWithdrawalFromUri(
     };
   }
 
-  await fetchFreshExchange(wex, selectedExchange);
+  const exchange = await fetchFreshExchange(wex, selectedExchange);
   const withdrawInfo = await getBankWithdrawalInfo(
     wex.http,
     req.talerWithdrawUri,
@@ -2790,8 +2978,6 @@ export async function acceptWithdrawalFromUri(
     withdrawInfo.wireTypes,
   );
 
-  const exchange = await fetchFreshExchange(wex, selectedExchange);
-
   const withdrawalAccountList = await fetchWithdrawalAccountInfo(
     wex,
     {

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