taler-typescript-core

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

commit bcc6f2b106699145a0f75f44b27a0e49adcbb57a
parent 0e198ee7e361ec7cdee0c0996d2224c744dbbb14
Author: Florian Dold <dold@taler.net>
Date:   Tue, 28 Jul 2026 16:11:42 +0200

wallet: don't materialize recoup groups as transactions

A recoup group got a transaction-meta record but has no transaction context,
so a single revocation made getTransactions throw for the whole wallet.

Diffstat:
Mpackages/taler-wallet-core/src/recoup.ts | 34+++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/packages/taler-wallet-core/src/recoup.ts b/packages/taler-wallet-core/src/recoup.ts @@ -279,6 +279,12 @@ export async function processRecoupGroup( } let recoupGroup = await wex.runWalletDbTx(async (tx) => { + // Drops a meta record left behind by an older wallet version, which would + // otherwise keep breaking getTransactions until the next DB migration. + await new RecoupTransactionContext( + wex, + recoupGroupId, + ).updateTransactionMeta(tx); return tx.getRecoupGroup(recoupGroupId); }); if (!recoupGroup) { @@ -410,24 +416,18 @@ export class RecoupTransactionContext implements TransactionContext { }); } + /** + * A recoup group is not a materialized transaction yet: lookupFullTransaction + * below, getContextForTransaction, rematerializeTransactions and the + * shepherd's retry notifications all leave recoup out. Writing a meta record + * anyway would put an entry in the transaction list that nothing can render, + * and getTransactions would then fail for the *whole* wallet. + * + * So only ever remove the record here. That also repairs wallets that + * already stored one before this was fixed. + */ async updateTransactionMeta(tx: WalletDbTransaction): Promise<void> { - const recoupRec = await tx.getRecoupGroup(this.recoupGroupId); - if (!recoupRec) { - await tx.deleteTransactionMeta(this.transactionId); - return; - } - const exch = await tx.getExchange(recoupRec.exchangeBaseUrl); - if (!exch || !exch.detailsPointer) { - await tx.deleteTransactionMeta(this.transactionId); - return; - } - await tx.upsertTransactionMeta({ - transactionId: this.transactionId, - status: recoupRec.operationStatus, - timestamp: recoupRec.timestampStarted, - currency: exch.detailsPointer?.currency, - exchanges: [recoupRec.exchangeBaseUrl], - }); + await tx.deleteTransactionMeta(this.transactionId); } userAbortTransaction(): Promise<void> {