wallet-core: fix getTransactionById for refunds

This commit is contained in:
Florian Dold 2023-05-24 14:20:48 +02:00
parent f14c7e5f2a
commit f475f98f86
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
2 changed files with 91 additions and 23 deletions

View File

@ -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;

View File

@ -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<WalletContractData | undefined> {
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: {