commit 504c0c1b27e7171aa1c44beaa6a161023e88a75d
parent 496fb2697ed65a699f759ef12281b51f66c43e51
Author: Florian Dold <dold@taler.net>
Date: Thu, 20 Aug 2026 19:06:44 +0200
wallet-core: align peer pull-credit actions and handlers
Diffstat:
2 files changed, 113 insertions(+), 8 deletions(-)
diff --git a/packages/taler-wallet-core/src/pay-peer-pull-credit.test.ts b/packages/taler-wallet-core/src/pay-peer-pull-credit.test.ts
@@ -0,0 +1,106 @@
+/*
+ This file is part of GNU Taler
+ (C) 2026 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { TransactionAction } from "@gnu-taler/taler-util";
+import assert from "node:assert";
+import { test } from "node:test";
+import {
+ PeerPullPaymentCreditStatus,
+ WalletPeerPullCredit,
+} from "./db-common.js";
+import { WalletDbTransaction } from "./dbtx.js";
+import {
+ computePeerPullCreditTransactionActions,
+ PeerPullCreditTransactionContext,
+} from "./pay-peer-pull-credit.js";
+import { WalletExecutionContext } from "./wallet.js";
+
+function makeRecord(status: PeerPullPaymentCreditStatus): WalletPeerPullCredit {
+ return {
+ pursePub: "purse-pub",
+ exchangeBaseUrl: "https://exchange.example/",
+ amount: "TESTKUDOS:1",
+ estimatedAmountEffective: "TESTKUDOS:1",
+ pursePriv: "purse-priv",
+ contractTermsHash: "contract-hash",
+ mergePub: "merge-pub",
+ mergePriv: "merge-priv",
+ contractPub: "contract-pub",
+ contractPriv: "contract-priv",
+ contractEncNonce: "nonce",
+ mergeTimestamp: 1 as any,
+ mergeReserveRowId: 1,
+ withdrawalGroupId: undefined,
+ status,
+ };
+}
+
+test("peer pull-credit actions match the withdrawal handlers", () => {
+ assert.ok(
+ !computePeerPullCreditTransactionActions(
+ makeRecord(PeerPullPaymentCreditStatus.PendingWithdrawing),
+ ).includes(TransactionAction.Abort),
+ );
+ assert.deepStrictEqual(
+ computePeerPullCreditTransactionActions(
+ makeRecord(PeerPullPaymentCreditStatus.SuspendedWithdrawing),
+ ),
+ [TransactionAction.Resume],
+ );
+ assert.ok(
+ computePeerPullCreditTransactionActions(
+ makeRecord(PeerPullPaymentCreditStatus.SuspendedMergeKycRequired),
+ ).includes(TransactionAction.Abort),
+ );
+});
+
+test("aborting a suspended pre-withdrawal pull credit starts cleanup", async () => {
+ let record = makeRecord(PeerPullPaymentCreditStatus.SuspendedCreatePurse);
+ const tx = {
+ async getPeerPullCredit(): Promise<WalletPeerPullCredit> {
+ return record;
+ },
+ async upsertPeerPullCredit(updated: WalletPeerPullCredit): Promise<void> {
+ record = updated;
+ },
+ async deletePeerPullCredit(): Promise<void> {},
+ async upsertTransactionMeta(): Promise<void> {},
+ async deleteTransactionMeta(): Promise<void> {},
+ notify(): void {},
+ } as unknown as WalletDbTransaction;
+ let reset = false;
+ const wex = {
+ async runWalletDbTx<T>(
+ f: (transaction: WalletDbTransaction) => Promise<T>,
+ ): Promise<T> {
+ return await f(tx);
+ },
+ taskScheduler: {
+ async resetTask(): Promise<void> {
+ reset = true;
+ },
+ },
+ } as unknown as WalletExecutionContext;
+
+ const context = new PeerPullCreditTransactionContext(wex, record.pursePub);
+ await context.userAbortTransaction();
+
+ assert.strictEqual(
+ record.status,
+ PeerPullPaymentCreditStatus.AbortingDeletePurse,
+ );
+ assert.strictEqual(reset, true);
+});
diff --git a/packages/taler-wallet-core/src/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/pay-peer-pull-credit.ts
@@ -489,10 +489,13 @@ export class PeerPullCreditTransactionContext implements TransactionContext {
rec.abortReason = reason;
rec.status = PeerPullPaymentCreditStatus.AbortingDeletePurse;
break;
- case PeerPullPaymentCreditStatus.Done:
case PeerPullPaymentCreditStatus.SuspendedCreatePurse:
case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired:
case PeerPullPaymentCreditStatus.SuspendedReady:
+ rec.status = PeerPullPaymentCreditStatus.AbortingDeletePurse;
+ rec.abortReason = reason;
+ break;
+ case PeerPullPaymentCreditStatus.Done:
case PeerPullPaymentCreditStatus.SuspendedWithdrawing:
case PeerPullPaymentCreditStatus.Aborted:
case PeerPullPaymentCreditStatus.AbortingDeletePurse:
@@ -1430,19 +1433,15 @@ export function computePeerPullCreditTransactionActions(
case PeerPullPaymentCreditStatus.Done:
return [TransactionAction.Delete];
case PeerPullPaymentCreditStatus.PendingWithdrawing:
- return [
- TransactionAction.Retry,
- TransactionAction.Abort,
- TransactionAction.Suspend,
- ];
+ return [TransactionAction.Retry, TransactionAction.Suspend];
case PeerPullPaymentCreditStatus.SuspendedCreatePurse:
return [TransactionAction.Resume, TransactionAction.Abort];
case PeerPullPaymentCreditStatus.SuspendedReady:
return [TransactionAction.Abort, TransactionAction.Resume];
case PeerPullPaymentCreditStatus.SuspendedWithdrawing:
- return [TransactionAction.Resume, TransactionAction.Fail];
+ return [TransactionAction.Resume];
case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired:
- return [TransactionAction.Resume, TransactionAction.Fail];
+ return [TransactionAction.Resume, TransactionAction.Abort];
case PeerPullPaymentCreditStatus.Aborted:
return [TransactionAction.Delete];
case PeerPullPaymentCreditStatus.AbortingDeletePurse: