From f475f98f86d03e808d2c88c13eafa712c2013fe3 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 24 May 2023 14:20:48 +0200 Subject: wallet-core: fix getTransactionById for refunds --- packages/taler-util/src/transactions-types.ts | 43 ++++++++++++- .../src/operations/transactions.ts | 71 ++++++++++++++++------ 2 files changed, 91 insertions(+), 23 deletions(-) diff --git a/packages/taler-util/src/transactions-types.ts b/packages/taler-util/src/transactions-types.ts index aaf527b89..30178b7c7 100644 --- a/packages/taler-util/src/transactions-types.ts +++ b/packages/taler-util/src/transactions-types.ts @@ -203,10 +203,12 @@ export type Transaction = | TransactionPeerPullCredit | TransactionPeerPullDebit | TransactionPeerPushCredit - | TransactionPeerPushDebit; + | TransactionPeerPushDebit + | TransactionInternalWithdrawal; export enum TransactionType { Withdrawal = "withdrawal", + InternalWithdrawal = "internal-withdrawal", Payment = "payment", Refund = "refund", Refresh = "refresh", @@ -271,8 +273,9 @@ interface WithdrawalDetailsForTalerBankIntegrationApi { reserveIsReady: boolean; } -// This should only be used for actual withdrawals -// and not for tips that have their own transactions type. +/** + * A withdrawal transaction (either bank-integrated or manual). + */ export interface TransactionWithdrawal extends TransactionCommon { type: TransactionType.Withdrawal; @@ -294,6 +297,40 @@ export interface TransactionWithdrawal extends TransactionCommon { withdrawalDetails: WithdrawalDetails; } +/** + * Internal withdrawal operation, only reported on request. + * + * Some transactions (peer-*-credit) internally do a withdrawal, + * but only the peer-*-credit transaction is reported. + * + * The internal withdrawal transaction allows to access the details of + * the underlying withdrawal for testing/debugging. + * + * It is usually not reported, so that amounts of transactions properly + * add up, since the amountEffecive of the withdrawal is already reported + * in the peer-*-credit transaction. + */ +export interface TransactionInternalWithdrawal extends TransactionCommon { + type: TransactionType.InternalWithdrawal; + + /** + * Exchange of the withdrawal. + */ + exchangeBaseUrl: string; + + /** + * Amount that got subtracted from the reserve balance. + */ + amountRaw: AmountString; + + /** + * Amount that actually was (or will be) added to the wallet's balance. + */ + amountEffective: AmountString; + + withdrawalDetails: WithdrawalDetails; +} + export interface PeerInfoShort { expiration: TalerProtocolTimestamp | undefined; summary: string | undefined; diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 2e42eb8d8..b5a6ba74e 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -111,6 +111,7 @@ import { computeWithdrawalTransactionStatus, processWithdrawalGroup, } from "./withdraw.js"; +import { GetReadOnlyAccess, WalletStoresV1 } from "../index.js"; const logger = new Logger("taler-wallet-core:transactions.ts"); @@ -155,6 +156,7 @@ const txOrder: { [t in TransactionType]: number } = { [TransactionType.Deposit]: 9, [TransactionType.Refresh]: 10, [TransactionType.Tip]: 11, + [TransactionType.InternalWithdrawal]: 12, }; export async function getTransactionById( @@ -271,9 +273,23 @@ export async function getTransactionById( }); } - case TransactionType.Refund: - // FIXME! - throw Error("not implemented"); + case TransactionType.Refund: { + return await ws.db + .mktx((x) => [x.refundGroups, x.contractTerms, x.purchases]) + .runReadOnly(async (tx) => { + const refundRecord = await tx.refundGroups.get( + parsedTx.refundGroupId, + ); + if (!refundRecord) { + throw Error("not found"); + } + const contractData = await lookupMaybeContractData( + tx, + refundRecord?.proposalId, + ); + return buildTransactionForRefund(refundRecord, contractData); + }); + } case TransactionType.PeerPullDebit: { return await ws.db .mktx((x) => [x.peerPullPaymentIncoming]) @@ -829,6 +845,33 @@ function buildTransactionForTip( }; } +async function lookupMaybeContractData( + tx: GetReadOnlyAccess<{ + purchases: typeof WalletStoresV1.purchases; + contractTerms: typeof WalletStoresV1.contractTerms; + }>, + proposalId: string, +): Promise { + let contractData: WalletContractData | undefined = undefined; + const purchaseTx = await tx.purchases.get(proposalId); + if (purchaseTx && purchaseTx.download) { + const download = purchaseTx.download; + const contractTermsRecord = await tx.contractTerms.get( + download.contractTermsHash, + ); + if (!contractTermsRecord) { + return; + } + contractData = extractContractData( + contractTermsRecord?.contractTermsRaw, + download.contractTermsHash, + download.contractTermsMerchantSig, + ); + } + + return contractData; +} + async function buildTransactionForPurchase( purchaseRecord: PurchaseRecord, contractData: WalletContractData, @@ -1061,22 +1104,10 @@ export async function getTransactions( if (shouldSkipCurrency(transactionsRequest, currency)) { return; } - let contractData: WalletContractData | undefined = undefined; - const purchaseTx = await tx.purchases.get(refundGroup.proposalId); - if (purchaseTx && purchaseTx.download) { - const download = purchaseTx.download; - const contractTermsRecord = await tx.contractTerms.get( - download.contractTermsHash, - ); - if (!contractTermsRecord) { - return; - } - contractData = extractContractData( - contractTermsRecord?.contractTermsRaw, - download.contractTermsHash, - download.contractTermsMerchantSig, - ); - } + const contractData = await lookupMaybeContractData( + tx, + refundGroup.proposalId, + ); transactions.push(buildTransactionForRefund(refundGroup, contractData)); }); @@ -1704,7 +1735,7 @@ export async function deleteTransaction( } }); - return; + return; } case TransactionType.PeerPushDebit: { -- cgit v1.2.3