From 274b72f6ea4ac92e334b97a9cc427d64b2307217 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 23 May 2024 22:11:23 +0200 Subject: wallet-core: implement acceptBankIntegratedWithdrawal via prepare/confirm step This avoids duplication of some subtle logic. --- packages/taler-util/src/wallet-types.ts | 4 +- packages/taler-wallet-core/src/versions.ts | 2 +- packages/taler-wallet-core/src/withdraw.ts | 158 +++++++++++------------------ 3 files changed, 65 insertions(+), 99 deletions(-) (limited to 'packages') diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts index 9301a9723..dce811462 100644 --- a/packages/taler-util/src/wallet-types.ts +++ b/packages/taler-util/src/wallet-types.ts @@ -1865,7 +1865,7 @@ export interface PrepareBankIntegratedWithdrawalResponse { export interface ConfirmWithdrawalRequest { transactionId: string; exchangeBaseUrl: string; - amount: AmountString; + amount?: AmountString; forcedDenomSel?: ForcedDenomSel; restrictAge?: number; } @@ -1874,7 +1874,7 @@ export const codecForConfirmWithdrawalRequestRequest = (): Codec => buildCodecForObject() .property("transactionId", codecForString()) - .property("amount", codecForAmountString()) + .property("amount", codecOptional(codecForAmountString())) .property("exchangeBaseUrl", codecForCanonBaseUrl()) .property("forcedDenomSel", codecForAny()) .property("restrictAge", codecOptional(codecForNumber())) diff --git a/packages/taler-wallet-core/src/versions.ts b/packages/taler-wallet-core/src/versions.ts index d33a23cdd..b89ff16f6 100644 --- a/packages/taler-wallet-core/src/versions.ts +++ b/packages/taler-wallet-core/src/versions.ts @@ -52,7 +52,7 @@ export const WALLET_BANK_CONVERSION_API_PROTOCOL_VERSION = "2:0:0"; /** * Libtool version of the wallet-core API. */ -export const WALLET_CORE_API_PROTOCOL_VERSION = "5:0:0"; +export const WALLET_CORE_API_PROTOCOL_VERSION = "6:0:0"; /** * Libtool rules: diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts index 1dc4e0999..7f43b4333 100644 --- a/packages/taler-wallet-core/src/withdraw.ts +++ b/packages/taler-wallet-core/src/withdraw.ts @@ -2953,6 +2953,7 @@ export async function prepareBankIntegratedWithdrawal( }, }, reserveStatus: WithdrawalGroupStatus.DialogProposed, + amount: info.amount == null ? undefined : Amounts.parseOrThrow(info.amount), }); const withdrawalGroupId = withdrawalGroup.withdrawalGroupId; @@ -2999,6 +3000,25 @@ export async function confirmWithdrawal( const talerWithdrawUri = withdrawalGroup.wgInfo.bankInfo.talerWithdrawUri; const confirmUrl = withdrawalGroup.wgInfo.bankInfo.confirmUrl; + let amount: AmountString; + + if (withdrawalGroup.instructedAmount == null) { + if (req.amount == null) { + throw Error( + "neither the withdrawal group nor the request specifies an amount", + ); + } + amount = req.amount; + } else { + if ( + req.amount != null && + Amounts.cmp(withdrawalGroup.instructedAmount, req.amount) != 0 + ) { + throw Error("conflicting amount"); + } + amount = withdrawalGroup.instructedAmount; + } + /** * The only reason this could be undefined is because it is an old wallet * database before adding the wireType field was added @@ -3024,7 +3044,7 @@ export async function confirmWithdrawal( wex, { exchange, - instructedAmount: Amounts.parseOrThrow(req.amount), + instructedAmount: Amounts.parseOrThrow(amount), }, wex.cancellationToken, ); @@ -3036,7 +3056,7 @@ export async function confirmWithdrawal( const initalDenoms = await getInitialDenomsSelection( wex, req.exchangeBaseUrl, - Amounts.parseOrThrow(req.amount), + Amounts.parseOrThrow(amount), req.forcedDenomSel, ); @@ -3047,7 +3067,7 @@ export async function confirmWithdrawal( switch (rec.status) { case WithdrawalGroupStatus.DialogProposed: { rec.exchangeBaseUrl = req.exchangeBaseUrl; - rec.instructedAmount = req.amount; + rec.instructedAmount = amount; rec.denomsSel = initalDenoms; rec.rawWithdrawalAmount = initalDenoms.totalWithdrawCost; rec.effectiveWithdrawalAmount = initalDenoms.totalCoinValue; @@ -3074,6 +3094,11 @@ export async function confirmWithdrawal( } }); + wex.ws.notify({ + type: NotificationType.BalanceChange, + hintTransactionId: ctx.transactionId, + }); + await wex.taskScheduler.resetTaskRetries(ctx.taskId); } @@ -3097,113 +3122,54 @@ export async function acceptWithdrawalFromUri( amount?: AmountLike; }, ): Promise { - const selectedExchange = req.selectedExchange; - logger.info( - `accepting withdrawal via ${req.talerWithdrawUri}, canonicalized selected exchange ${selectedExchange}`, - ); - 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 { - reservePub: existingWithdrawalGroup.reservePub, - confirmTransferUrl: url, - transactionId: constructTransactionIdentifier({ - tag: TransactionType.Withdrawal, - withdrawalGroupId: existingWithdrawalGroup.withdrawalGroupId, - }), - }; - } - - const exchange = await fetchFreshExchange(wex, selectedExchange); - const withdrawInfo = await getBankWithdrawalInfo( - wex.http, - req.talerWithdrawUri, - ); - const exchangePaytoUri = await getExchangePaytoUri( - wex, - selectedExchange, - withdrawInfo.wireTypes, - ); + const resp = await prepareBankIntegratedWithdrawal(wex, { + talerWithdrawUri: req.talerWithdrawUri, + selectedExchange: req.selectedExchange, + }); - let amount: AmountJson; - if (withdrawInfo.amount == null) { - if (req.amount == null) { - throw Error( - "amount required, as withdrawal operation has flexible amount", - ); - } - amount = Amounts.parseOrThrow(req.amount); - } else { - if ( - req.amount != null && - Amounts.cmp(req.amount, withdrawInfo.amount) != 0 - ) { - throw Error( - "mismatched amount, amount is fixed by bank but client provided different amount", - ); - } - amount = withdrawInfo.amount; + const transactionId = resp.transactionId; + if (!transactionId) { + throw Error("internal error, no transaction ID"); } - const withdrawalAccountList = await fetchWithdrawalAccountInfo( - wex, - { - exchange, - instructedAmount: amount, - }, - CancellationToken.CONTINUE, - ); - - const withdrawalGroup = await internalCreateWithdrawalGroup(wex, { - amount, + await confirmWithdrawal(wex, { + transactionId, + amount: req.amount == null ? undefined : Amounts.stringify(req.amount), exchangeBaseUrl: req.selectedExchange, - wgInfo: { - withdrawalType: WithdrawalRecordType.BankIntegrated, - exchangeCreditAccounts: withdrawalAccountList, - bankInfo: { - exchangePaytoUri, - talerWithdrawUri: req.talerWithdrawUri, - confirmUrl: withdrawInfo.confirmTransferUrl, - timestampBankConfirmed: undefined, - timestampReserveInfoPosted: undefined, - wireTypes: withdrawInfo.wireTypes, - }, - }, - restrictAge: req.restrictAge, forcedDenomSel: req.forcedDenomSel, - reserveStatus: WithdrawalGroupStatus.PendingRegisteringBank, + restrictAge: req.restrictAge, }); - const withdrawalGroupId = withdrawalGroup.withdrawalGroupId; + const parsedTx = parseTransactionIdentifier(transactionId); + checkLogicInvariant(parsedTx?.tag === TransactionType.Withdrawal); - const ctx = new WithdrawTransactionContext(wex, withdrawalGroupId); + const ctx = new WithdrawTransactionContext(wex, parsedTx.withdrawalGroupId); - wex.ws.notify({ - type: NotificationType.BalanceChange, - hintTransactionId: ctx.transactionId, - }); + await waitWithdrawalRegistered(wex, ctx); - wex.taskScheduler.startShepherdTask(ctx.taskId); + const withdrawalGroup = await wex.db.runReadOnlyTx( + { + storeNames: ["withdrawalGroups"], + }, + async (tx) => { + return tx.withdrawalGroups.get(parsedTx.withdrawalGroupId); + }, + ); - await waitWithdrawalRegistered(wex, ctx); + if (!withdrawalGroup) { + throw Error("withdrawal group does not exist anymore"); + } + + if ( + withdrawalGroup.wgInfo.withdrawalType !== + WithdrawalRecordType.BankIntegrated + ) { + throw Error("withdrawal group has unexpected withdrawal type"); + } return { reservePub: withdrawalGroup.reservePub, - confirmTransferUrl: withdrawInfo.confirmTransferUrl, + confirmTransferUrl: withdrawalGroup.wgInfo.bankInfo.confirmUrl, transactionId: ctx.transactionId, }; } -- cgit v1.2.3