taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit c164ffa9fde655f845787c7c6622f36a830cd20e
parent 20b8855efedcbe4b5544b36576b306247fd89158
Author: Florian Dold <dold@taler.net>
Date:   Thu, 23 Jul 2026 01:00:54 +0200

wallet: retry and cancel withdrawal-group network requests

Diffstat:
Mpackages/taler-wallet-core/src/withdraw.ts | 36+++++++++++++++++++++++++-----------
1 file changed, 25 insertions(+), 11 deletions(-)

diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts @@ -168,6 +168,7 @@ import { ReadyExchangeSummary, checkIncomingAmountLegalUnderKycBalanceThreshold, fetchFreshExchange, + fetchFreshExchangeWithRetryNow, getExchangeDetailsInTx, getExchangePaytoUri, getPreferredExchangeForCurrency, @@ -185,7 +186,10 @@ import { isKycOperationDue, runKycCheckAlgo, } from "./kyc.js"; -import { runWithMaybeProgressContext } from "./progress.js"; +import { + runWithMaybeProgressContext, + runWithProgressRetries, +} from "./progress.js"; import { constructTransactionIdentifier, isUnsuccessfulTransaction, @@ -2902,7 +2906,11 @@ async function getWithdrawalDetailsForBankInfo( try { // If the exchange entry doesn't exist yet, // it'll be created as an ephemeral entry. - await fetchFreshExchange(wex, info.exchange); + // + // noBail: false even under a progress context, since this is best-effort + // (other exchanges might be available); we must not block here waiting on + // a bank-suggested exchange that is broken/offline. + await fetchFreshExchange(wex, info.exchange, { noBail: false }); } catch (e) { // We still continued if it failed, as other exchanges might be available. // We don't want to fail if the bank-suggested exchange is broken/offline. @@ -2965,7 +2973,11 @@ export async function getWithdrawalDetailsForUri( talerWithdrawUri: string, ): Promise<WithdrawUriInfoResponse> { logger.trace(`getting withdrawal details for URI ${talerWithdrawUri}`); - const info = await getBankWithdrawalInfo(wex.http, talerWithdrawUri); + // Retried under a progress context: the bank integration API is the network + // hop most likely to be slow or briefly unavailable here. + const info = await runWithProgressRetries(wex, () => + getBankWithdrawalInfo(wex.http, talerWithdrawUri), + ); logger.trace(`got bank info`); return getWithdrawalDetailsForBankInfo(wex, info); } @@ -3625,9 +3637,10 @@ export async function prepareBankIntegratedWithdrawal( `creating withdrawal with externalConfirmation=${externalConfirmation}`, ); - const withdrawInfo = await getBankWithdrawalInfo( - wex.http, - req.talerWithdrawUri, + // Retried under a progress context: the bank integration API is the network + // hop most likely to be slow or briefly unavailable here. + const withdrawInfo = await runWithProgressRetries(wex, () => + getBankWithdrawalInfo(wex.http, req.talerWithdrawUri), ); const info = await getWithdrawalDetailsForBankInfo(wex, withdrawInfo); @@ -3772,7 +3785,7 @@ export async function confirmWithdrawal( instructedCurrency = withdrawalGroup.wgInfo.bankInfo.currency; } - const exchange = await fetchFreshExchange(wex, selectedExchange); + const exchange = await fetchFreshExchangeWithRetryNow(wex, selectedExchange); requireExchangeTosAcceptedOrThrow(wex, exchange); if (req.amount && checkWithdrawalHardLimitExceeded(exchange, req.amount)) { @@ -3791,9 +3804,10 @@ export async function confirmWithdrawal( withdrawalGroup.wgInfo.bankInfo.wireTypes === undefined || withdrawalGroup.wgInfo.bankInfo.currency === undefined ) { - const withdrawInfo = await getBankWithdrawalInfo( - wex.http, - talerWithdrawUri, + // Retried under a progress context, like the other bank integration + // queries in this file. + const withdrawInfo = await runWithProgressRetries(wex, () => + getBankWithdrawalInfo(wex.http, talerWithdrawUri), ); bankWireTypes = withdrawInfo.wireTypes; bankCurrency = withdrawInfo.currency; @@ -4231,7 +4245,7 @@ export async function createManualWithdrawal( ): Promise<AcceptManualWithdrawalResult> { const { exchangeBaseUrl } = req; const amount = Amounts.parseOrThrow(req.amount); - const exchange = await fetchFreshExchange(wex, exchangeBaseUrl); + const exchange = await fetchFreshExchangeWithRetryNow(wex, exchangeBaseUrl); if (exchange.currency != amount.currency) { throw Error(