diff options
author | Florian Dold <florian.dold@gmail.com> | 2020-08-20 14:34:56 +0530 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2020-08-20 14:34:56 +0530 |
commit | a8fb16021d6f71e6d0c7fae6b440e5c3197b8867 (patch) | |
tree | ff893a899e34d7f94dad420b0a6907e95f2942cb /packages/taler-wallet-core | |
parent | 786976e5a8e10f6a3eab50cacbefe98d8b2364f5 (diff) |
handle withdrawals aborted by the bank, add test
Diffstat (limited to 'packages/taler-wallet-core')
5 files changed, 92 insertions, 1 deletions
diff --git a/packages/taler-wallet-core/src/TalerErrorCode.ts b/packages/taler-wallet-core/src/TalerErrorCode.ts index fd3ca1fc8..412f3ef8a 100644 --- a/packages/taler-wallet-core/src/TalerErrorCode.ts +++ b/packages/taler-wallet-core/src/TalerErrorCode.ts @@ -1768,6 +1768,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). * (A value of 0 indicates that the error is generated client-side). @@ -2734,6 +2741,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). * (A value of 0 indicates that the error is generated client-side). @@ -3154,6 +3196,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). * (A value of 0 indicates that the error is generated client-side). diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts index fb525da45..8adaeea81 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 8d0558dbd..b79ac3b27 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 42192dd9a..82260963b 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 f251b47d1..f14e2a2ab 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())) |