gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: handle withdrawals aborted by


From: gnunet
Subject: [taler-wallet-core] branch master updated: handle withdrawals aborted by the bank, add test
Date: Thu, 20 Aug 2020 11:05: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 a8fb1602 handle withdrawals aborted by the bank, add test
a8fb1602 is described below

commit a8fb16021d6f71e6d0c7fae6b440e5c3197b8867
Author: Florian Dold <florian.dold@gmail.com>
AuthorDate: Thu Aug 20 14:34:56 2020 +0530

    handle withdrawals aborted by the bank, add test
---
 packages/taler-integrationtests/src/harness.ts     | 19 ++++++
 .../src/test-withdrawal-abort-bank.ts              | 67 ++++++++++++++++++++++
 packages/taler-wallet-core/src/TalerErrorCode.ts   | 49 ++++++++++++++++
 .../taler-wallet-core/src/operations/reserves.ts   | 32 ++++++++++-
 .../src/operations/transactions.ts                 |  4 ++
 packages/taler-wallet-core/src/types/dbTypes.ts    |  5 ++
 packages/taler-wallet-core/src/types/talerTypes.ts |  3 +
 7 files changed, 178 insertions(+), 1 deletion(-)

diff --git a/packages/taler-integrationtests/src/harness.ts 
b/packages/taler-integrationtests/src/harness.ts
index 6897b4b5..545ea309 100644
--- a/packages/taler-integrationtests/src/harness.ts
+++ b/packages/taler-integrationtests/src/harness.ts
@@ -618,6 +618,25 @@ export namespace BankApi {
       },
     );
   }
+
+  export async function abortWithdrawalOperation(
+    bank: BankServiceInterface,
+    bankUser: BankUser,
+    wopi: WithdrawalOperationInfo,
+  ): Promise<void> {
+    const url = new URL(
+      `accounts/${bankUser.username}/withdrawals/${wopi.withdrawal_id}/abort`,
+      bank.baseUrl,
+    );
+    await axios.post(
+      url.href,
+      {},
+      {
+        auth: bankUser,
+      },
+    );
+  }
+
 }
 
 export class BankService implements BankServiceInterface {
diff --git a/packages/taler-integrationtests/src/test-withdrawal-abort-bank.ts 
b/packages/taler-integrationtests/src/test-withdrawal-abort-bank.ts
new file mode 100644
index 00000000..3c1e6292
--- /dev/null
+++ b/packages/taler-integrationtests/src/test-withdrawal-abort-bank.ts
@@ -0,0 +1,67 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020 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/>
+ */
+
+/**
+ * Imports.
+ */
+import { runTest, GlobalTestState, BankApi, BankAccessApi } from "./harness";
+import { createSimpleTestkudosEnvironment } from "./helpers";
+import { codecForBalancesResponse, TalerErrorCode } from "taler-wallet-core";
+
+/**
+ * Run test for basic, bank-integrated withdrawal.
+ */
+runTest(async (t: GlobalTestState) => {
+  // Set up test environment
+
+  const { wallet, bank, exchange } = await createSimpleTestkudosEnvironment(t);
+
+  // Create a withdrawal operation
+
+  const user = await BankApi.createRandomBankUser(bank);
+  const wop = await BankAccessApi.createWithdrawalOperation(
+    bank,
+    user,
+    "TESTKUDOS:10",
+  );
+
+  // Hand it to the wallet
+
+  const r1 = await wallet.apiRequest("getWithdrawalDetailsForUri", {
+    talerWithdrawUri: wop.taler_withdraw_uri,
+  });
+  t.assertTrue(r1.type === "response");
+
+  await wallet.runPending();
+
+  // Confirm it
+
+  await BankApi.abortWithdrawalOperation(bank, user, wop);
+
+  // Withdraw
+
+  const r2 = await wallet.apiRequest("acceptBankIntegratedWithdrawal", {
+    exchangeBaseUrl: exchange.baseUrl,
+    talerWithdrawUri: wop.taler_withdraw_uri,
+  });
+  t.assertTrue(r2.type === "error");
+  t.assertTrue(
+    r2.error.talerErrorCode ===
+      TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK,
+  );
+
+  await t.shutdown();
+});
diff --git a/packages/taler-wallet-core/src/TalerErrorCode.ts 
b/packages/taler-wallet-core/src/TalerErrorCode.ts
index fd3ca1fc..412f3ef8 100644
--- a/packages/taler-wallet-core/src/TalerErrorCode.ts
+++ b/packages/taler-wallet-core/src/TalerErrorCode.ts
@@ -1767,6 +1767,13 @@ export enum TalerErrorCode {
    */
   POST_TRANSFERS_DB_LOOKUP_ERROR = 2413,
 
+  /**
+   * The merchant backend cannot create an instance with the given default max 
deposit fee or default max wire fee because the fee currencies are incompatible 
with the merchant's currency in the config.
+   * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  POST_INSTANCES_BAD_CURRENCY = 2449,
+
   /**
    * The merchant backend cannot create an instance under the given identifier 
as one already exists. Use PATCH to modify the existing entry.
    * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
@@ -2733,6 +2740,41 @@ export enum TalerErrorCode {
    */
   MERCHANT_GET_ORDER_INVALID_TOKEN = 2923,
 
+  /**
+   * The merchant backup failed to lookup the order status in the database.
+   * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  MERCHANT_PRIVATE_GET_ORDERS_STATUS_DB_LOOKUP_ERROR = 2924,
+
+  /**
+   * The merchant backup failed to lookup the contract terms in the database.
+   * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  MERCHANT_PRIVATE_GET_ORDERS_CONTRACT_DB_LOOKUP_ERROR = 2925,
+
+  /**
+   * The merchant backup failed to parse the order contract terms.
+   * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  MERCHANT_PRIVATE_GET_ORDERS_PARSE_CONTRACT_ERROR = 2926,
+
+  /**
+   * The merchant backup failed to lookup the refunds in the database.
+   * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  MERCHANT_PRIVATE_GET_ORDERS_REFUND_DB_LOOKUP_ERROR = 2927,
+
+  /**
+   * The merchant backup failed to lookup filtered orders in the database.
+   * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  MERCHANT_PRIVATE_GET_ORDERS_BY_FILTER_DB_LOOKUP_ERROR = 2928,
+
   /**
    * The signature from the exchange on the deposit confirmation is invalid.  
Returned with a "400 Bad Request" status code.
    * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
@@ -3153,6 +3195,13 @@ export enum TalerErrorCode {
    */
   WALLET_CORE_NOT_AVAILABLE = 7011,
 
+  /**
+   * The bank has aborted a withdrawal operation, and thus a withdrawal can't 
complete.
+   * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK = 7012,
+
   /**
    * End of error code range.
    * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
diff --git a/packages/taler-wallet-core/src/operations/reserves.ts 
b/packages/taler-wallet-core/src/operations/reserves.ts
index fb525da4..8adaeea8 100644
--- a/packages/taler-wallet-core/src/operations/reserves.ts
+++ b/packages/taler-wallet-core/src/operations/reserves.ts
@@ -60,6 +60,7 @@ import {
   guardOperationException,
   OperationFailedAndReportedError,
   makeErrorDetails,
+  OperationFailedError,
 } from "./errors";
 import { NotificationType } from "../types/notifications";
 import { codecForReserveStatus } from "../types/ReserveStatus";
@@ -358,7 +359,7 @@ async function registerReserveWithBank(
   return processReserveBankStatus(ws, reservePub);
 }
 
-export async function processReserveBankStatus(
+async function processReserveBankStatus(
   ws: InternalWalletState,
   reservePub: string,
 ): Promise<void> {
@@ -393,6 +394,25 @@ async function processReserveBankStatusImpl(
     codecForWithdrawOperationStatusResponse(),
   );
 
+  if (status.aborted) {
+    logger.trace("bank aborted the withdrawal");
+    await ws.db.mutate(Stores.reserves, reservePub, (r) => {
+      switch (r.reserveStatus) {
+        case ReserveRecordStatus.REGISTERING_BANK:
+        case ReserveRecordStatus.WAIT_CONFIRM_BANK:
+          break;
+        default:
+          return;
+      }
+      const now = getTimestampNow();
+      r.timestampBankConfirmed = now;
+      r.reserveStatus = ReserveRecordStatus.BANK_ABORTED;
+      r.retryInfo = initRetryInfo();
+      return r;
+    });
+    return;
+  }
+
   if (status.selection_done) {
     if (reserve.reserveStatus === ReserveRecordStatus.REGISTERING_BANK) {
       await registerReserveWithBank(ws, reservePub);
@@ -612,6 +632,8 @@ async function processReserveImpl(
     case ReserveRecordStatus.WAIT_CONFIRM_BANK:
       await processReserveBankStatus(ws, reservePub);
       break;
+    case ReserveRecordStatus.BANK_ABORTED:
+      break;
     default:
       console.warn("unknown reserve record status:", reserve.reserveStatus);
       assertUnreachable(reserve.reserveStatus);
@@ -802,6 +824,14 @@ export async function createTalerWithdrawReserve(
   // We do this here, as the reserve should be registered before we return,
   // so that we can redirect the user to the bank's status page.
   await processReserveBankStatus(ws, reserve.reservePub);
+  const processedReserve = await ws.db.get(Stores.reserves, 
reserve.reservePub);
+  if (processedReserve?.reserveStatus === ReserveRecordStatus.BANK_ABORTED) {
+    throw OperationFailedError.fromCode(
+      TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK,
+      "withdrawal aborted by bank",
+      {},
+    );
+  }
   return {
     reservePub: reserve.reservePub,
     confirmTransferUrl: withdrawInfo.confirmTransferUrl,
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index 8d0558db..b79ac3b2 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -23,6 +23,7 @@ import {
   WithdrawalSourceType,
   WalletRefundItem,
   RefundState,
+  ReserveRecordStatus,
 } from "../types/dbTypes";
 import { Amounts, AmountJson } from "../util/amounts";
 import { timestampCmp, Timestamp } from "../util/time";
@@ -186,6 +187,9 @@ export async function getTransactions(
         if (r.initialWithdrawalStarted) {
           return;
         }
+        if (r.reserveStatus === ReserveRecordStatus.BANK_ABORTED) {
+          return;
+        }
         let withdrawalDetails: WithdrawalDetails;
         if (r.bankInfo) {
           withdrawalDetails = {
diff --git a/packages/taler-wallet-core/src/types/dbTypes.ts 
b/packages/taler-wallet-core/src/types/dbTypes.ts
index 42192dd9..82260963 100644
--- a/packages/taler-wallet-core/src/types/dbTypes.ts
+++ b/packages/taler-wallet-core/src/types/dbTypes.ts
@@ -76,6 +76,11 @@ export enum ReserveRecordStatus {
    * by the user.
    */
   DORMANT = "dormant",
+
+  /**
+   * The bank aborted the withdrawal.
+   */
+  BANK_ABORTED = "bank-aborted",
 }
 
 export interface RetryInfo {
diff --git a/packages/taler-wallet-core/src/types/talerTypes.ts 
b/packages/taler-wallet-core/src/types/talerTypes.ts
index f251b47d..f14e2a2a 100644
--- a/packages/taler-wallet-core/src/types/talerTypes.ts
+++ b/packages/taler-wallet-core/src/types/talerTypes.ts
@@ -707,6 +707,8 @@ export class WithdrawOperationStatusResponse {
 
   transfer_done: boolean;
 
+  aborted: boolean;
+
   amount: string;
 
   sender_wire?: string;
@@ -1178,6 +1180,7 @@ export const codecForWithdrawOperationStatusResponse = 
(): Codec<
   buildCodecForObject<WithdrawOperationStatusResponse>()
     .property("selection_done", codecForBoolean)
     .property("transfer_done", codecForBoolean)
+    .property("aborted", codecForBoolean)
     .property("amount", codecForString())
     .property("sender_wire", codecOptional(codecForString()))
     .property("suggested_exchange", codecOptional(codecForString()))

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