gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] 02/02: wallet-core: add missing resume/suspend imple


From: gnunet
Subject: [taler-wallet-core] 02/02: wallet-core: add missing resume/suspend implementations
Date: Tue, 30 May 2023 09:33:43 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

commit 0323067c0757262084e16a9bba9d58bcd773fc23
Author: Florian Dold <florian@dold.me>
AuthorDate: Tue May 30 09:33:32 2023 +0200

    wallet-core: add missing resume/suspend implementations
---
 packages/taler-wallet-core/src/db.ts               |  39 ++-
 .../src/operations/backup/export.ts                |  10 +-
 .../src/operations/pay-merchant.ts                 | 179 +++++++++--
 .../taler-wallet-core/src/operations/pay-peer.ts   | 348 ++++++++++++++++++++-
 packages/taler-wallet-core/src/operations/tip.ts   | 108 ++++++-
 .../src/operations/transactions.ts                 | 127 +++++++-
 .../taler-wallet-core/src/operations/withdraw.ts   |   2 +-
 7 files changed, 755 insertions(+), 58 deletions(-)

diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index 0e35fe27c..3edaf8af5 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -863,11 +863,22 @@ export interface TipRecord {
    * The url to be redirected after the tip is accepted.
    */
   next_url: string | undefined;
+
   /**
    * Timestamp for when the wallet finished picking up the tip
    * from the merchant.
    */
   pickedUpTimestamp: TalerPreciseTimestamp | undefined;
+
+  status: TipRecordStatus;
+}
+
+export enum TipRecordStatus {
+  PendingPickup = 10,
+
+  SuspendidPickup = 21,
+
+  Done = 50,
 }
 
 export enum RefreshCoinStatus {
@@ -1078,12 +1089,12 @@ export enum PurchaseStatus {
   /**
    * Not downloaded yet.
    */
-  DownloadingProposal = 10,
+  PendingDownloadingProposal = 10,
 
   /**
    * The user has accepted the proposal.
    */
-  Paying = 11,
+  PendingPaying = 11,
 
   /**
    * Currently in the process of aborting with a refund.
@@ -1093,17 +1104,17 @@ export enum PurchaseStatus {
   /**
    * Paying a second time, likely with different session ID
    */
-  PayingReplay = 13,
+  PendingPayingReplay = 13,
 
   /**
    * Query for refunds (until query succeeds).
    */
-  QueryingRefund = 14,
+  PendingQueryingRefund = 14,
 
   /**
    * Query for refund (until auto-refund deadline is reached).
    */
-  QueryingAutoRefund = 15,
+  PendingQueryingAutoRefund = 15,
 
   PendingAcceptRefund = 16,
 
@@ -1902,7 +1913,7 @@ export interface PeerPullPaymentInitiationRecord {
 
 export enum PeerPushPaymentIncomingStatus {
   PendingMerge = 10 /* ACTIVE_START */,
-  MergeKycRequired = 11 /* ACTIVE_START + 1 */,
+  PendingMergeKycRequired = 11 /* ACTIVE_START + 1 */,
   /**
    * Merge was successful and withdrawal group has been created, now
    * everything is in the hand of the withdrawal group.
@@ -2829,6 +2840,22 @@ export const walletDbFixups: FixupDescription[] = [
       });
     },
   },
+  {
+    name: "TipRecordRecord_status_add",
+    async fn(tx): Promise<void> {
+      await tx.tips.iter().forEachAsync(async (r) => {
+        // Remove legacy transactions that don't have the totalCost field yet.
+        if (r.status == null) {
+          if (r.pickedUpTimestamp) {
+            r.status = TipRecordStatus.Done;
+          } else {
+            r.status = TipRecordStatus.PendingPickup;
+          }
+          await tx.tips.put(r);
+        }
+      });
+    },
+  },
 ];
 
 const logger = new Logger("db.ts");
diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts 
b/packages/taler-wallet-core/src/operations/backup/export.ts
index ff5f1e177..0aca45551 100644
--- a/packages/taler-wallet-core/src/operations/backup/export.ts
+++ b/packages/taler-wallet-core/src/operations/backup/export.ts
@@ -412,14 +412,14 @@ export async function exportBackup(
         let propStatus: BackupProposalStatus;
         switch (purch.purchaseStatus) {
           case PurchaseStatus.Done:
-          case PurchaseStatus.QueryingAutoRefund:
-          case PurchaseStatus.QueryingRefund:
+          case PurchaseStatus.PendingQueryingAutoRefund:
+          case PurchaseStatus.PendingQueryingRefund:
             propStatus = BackupProposalStatus.Paid;
             break;
-          case PurchaseStatus.PayingReplay:
-          case PurchaseStatus.DownloadingProposal:
+          case PurchaseStatus.PendingPayingReplay:
+          case PurchaseStatus.PendingDownloadingProposal:
           case PurchaseStatus.Proposed:
-          case PurchaseStatus.Paying:
+          case PurchaseStatus.PendingPaying:
             propStatus = BackupProposalStatus.Proposed;
             break;
           case PurchaseStatus.FailedClaim:
diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts 
b/packages/taler-wallet-core/src/operations/pay-merchant.ts
index 4ea41c695..30c75f695 100644
--- a/packages/taler-wallet-core/src/operations/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts
@@ -131,6 +131,7 @@ import {
 import {
   constructTransactionIdentifier,
   notifyTransition,
+  stopLongpolling,
 } from "./transactions.js";
 
 /**
@@ -339,7 +340,7 @@ async function processDownloadProposal(
     };
   }
 
-  if (proposal.purchaseStatus != PurchaseStatus.DownloadingProposal) {
+  if (proposal.purchaseStatus != PurchaseStatus.PendingDownloadingProposal) {
     return {
       type: OperationAttemptResultType.Finished,
       result: undefined,
@@ -516,7 +517,7 @@ async function processDownloadProposal(
       if (!p) {
         return;
       }
-      if (p.purchaseStatus !== PurchaseStatus.DownloadingProposal) {
+      if (p.purchaseStatus !== PurchaseStatus.PendingDownloadingProposal) {
         return;
       }
       const oldTxState = computePayMerchantTransactionState(p);
@@ -626,7 +627,7 @@ async function createPurchase(
     merchantBaseUrl,
     orderId,
     proposalId: proposalId,
-    purchaseStatus: PurchaseStatus.DownloadingProposal,
+    purchaseStatus: PurchaseStatus.PendingDownloadingProposal,
     repurchaseProposalId: undefined,
     downloadSessionId: sessionId,
     autoRefundDeadline: undefined,
@@ -699,7 +700,7 @@ async function storeFirstPaySuccess(
         return;
       }
       const oldTxState = computePayMerchantTransactionState(purchase);
-      if (purchase.purchaseStatus === PurchaseStatus.Paying) {
+      if (purchase.purchaseStatus === PurchaseStatus.PendingPaying) {
         purchase.purchaseStatus = PurchaseStatus.Done;
       }
       purchase.timestampFirstSuccessfulPay = now;
@@ -721,7 +722,7 @@ async function storeFirstPaySuccess(
       if (protoAr) {
         const ar = Duration.fromTalerProtocolDuration(protoAr);
         logger.info("auto_refund present");
-        purchase.purchaseStatus = PurchaseStatus.QueryingAutoRefund;
+        purchase.purchaseStatus = PurchaseStatus.PendingQueryingAutoRefund;
         purchase.autoRefundDeadline = AbsoluteTime.toProtocolTimestamp(
           AbsoluteTime.addDuration(AbsoluteTime.now(), ar),
         );
@@ -760,8 +761,8 @@ async function storePayReplaySuccess(
       }
       const oldTxState = computePayMerchantTransactionState(purchase);
       if (
-        purchase.purchaseStatus === PurchaseStatus.Paying ||
-        purchase.purchaseStatus === PurchaseStatus.PayingReplay
+        purchase.purchaseStatus === PurchaseStatus.PendingPaying ||
+        purchase.purchaseStatus === PurchaseStatus.PendingPayingReplay
       ) {
         purchase.purchaseStatus = PurchaseStatus.Done;
       }
@@ -1058,7 +1059,7 @@ export async function checkPaymentByProposalId(
         }
         const oldTxState = computePayMerchantTransactionState(p);
         p.lastSessionId = sessionId;
-        p.purchaseStatus = PurchaseStatus.PayingReplay;
+        p.purchaseStatus = PurchaseStatus.PendingPayingReplay;
         await tx.purchases.put(p);
         const newTxState = computePayMerchantTransactionState(p);
         return { oldTxState, newTxState };
@@ -1098,8 +1099,8 @@ export async function checkPaymentByProposalId(
   } else {
     const paid =
       purchase.purchaseStatus === PurchaseStatus.Done ||
-      purchase.purchaseStatus === PurchaseStatus.QueryingRefund ||
-      purchase.purchaseStatus === PurchaseStatus.QueryingAutoRefund;
+      purchase.purchaseStatus === PurchaseStatus.PendingQueryingRefund ||
+      purchase.purchaseStatus === PurchaseStatus.PendingQueryingAutoRefund;
     const download = await expectProposalDownload(ws, purchase);
     return {
       status: PreparePayResultType.AlreadyConfirmed,
@@ -1348,7 +1349,7 @@ export async function confirmPay(
         logger.trace(`changing session ID to ${sessionIdOverride}`);
         purchase.lastSessionId = sessionIdOverride;
         if (purchase.purchaseStatus === PurchaseStatus.Done) {
-          purchase.purchaseStatus = PurchaseStatus.PayingReplay;
+          purchase.purchaseStatus = PurchaseStatus.PendingPayingReplay;
         }
         await tx.purchases.put(purchase);
       }
@@ -1424,7 +1425,7 @@ export async function confirmPay(
           };
           p.lastSessionId = sessionId;
           p.timestampAccept = TalerPreciseTimestamp.now();
-          p.purchaseStatus = PurchaseStatus.Paying;
+          p.purchaseStatus = PurchaseStatus.PendingPaying;
           await tx.purchases.put(p);
           await spendCoins(ws, tx, {
             //`txn:proposal:${p.proposalId}`
@@ -1440,7 +1441,7 @@ export async function confirmPay(
           });
           break;
         case PurchaseStatus.Done:
-        case PurchaseStatus.Paying:
+        case PurchaseStatus.PendingPaying:
         default:
           break;
       }
@@ -1481,14 +1482,14 @@ export async function processPurchase(
   }
 
   switch (purchase.purchaseStatus) {
-    case PurchaseStatus.DownloadingProposal:
+    case PurchaseStatus.PendingDownloadingProposal:
       return processDownloadProposal(ws, proposalId);
-    case PurchaseStatus.Paying:
-    case PurchaseStatus.PayingReplay:
+    case PurchaseStatus.PendingPaying:
+    case PurchaseStatus.PendingPayingReplay:
       return processPurchasePay(ws, proposalId);
-    case PurchaseStatus.QueryingRefund:
+    case PurchaseStatus.PendingQueryingRefund:
       return processPurchaseQueryRefund(ws, purchase);
-    case PurchaseStatus.QueryingAutoRefund:
+    case PurchaseStatus.PendingQueryingAutoRefund:
       return processPurchaseAutoRefund(ws, purchase);
     case PurchaseStatus.AbortingWithRefund:
       return processPurchaseAbortingRefund(ws, purchase);
@@ -1540,8 +1541,8 @@ export async function processPurchasePay(
     };
   }
   switch (purchase.purchaseStatus) {
-    case PurchaseStatus.Paying:
-    case PurchaseStatus.PayingReplay:
+    case PurchaseStatus.PendingPaying:
+    case PurchaseStatus.PendingPayingReplay:
       break;
     default:
       return OperationAttemptResult.finishedEmpty();
@@ -1757,11 +1758,11 @@ export async function abortPayMerchant(
         logger.warn(`tried to abort successful payment`);
         return;
       }
-      if (oldStatus === PurchaseStatus.Paying) {
+      if (oldStatus === PurchaseStatus.PendingPaying) {
         purchase.purchaseStatus = PurchaseStatus.AbortingWithRefund;
       }
       await tx.purchases.put(purchase);
-      if (oldStatus === PurchaseStatus.Paying) {
+      if (oldStatus === PurchaseStatus.PendingPaying) {
         if (purchase.payInfo) {
           const coinSel = purchase.payInfo.payCoinSelection;
           const currency = Amounts.currencyOf(purchase.payInfo.totalPayCost);
@@ -1789,32 +1790,146 @@ export async function abortPayMerchant(
   ws.workAvailable.trigger();
 }
 
+
+const transitionSuspend: { [x in PurchaseStatus]?: {
+  next: PurchaseStatus | undefined,
+} } = {
+  [PurchaseStatus.PendingDownloadingProposal]: {
+    next: PurchaseStatus.SuspendedDownloadingProposal,
+  },
+  [PurchaseStatus.AbortingWithRefund]: {
+    next: PurchaseStatus.SuspendedAbortingWithRefund,
+  },
+  [PurchaseStatus.PendingPaying]: {
+    next: PurchaseStatus.SuspendedPaying,
+  },
+  [PurchaseStatus.PendingPayingReplay]: {
+    next: PurchaseStatus.SuspendedPayingReplay,
+  },
+  [PurchaseStatus.PendingQueryingAutoRefund]: {
+    next: PurchaseStatus.SuspendedQueryingAutoRefund,
+  }
+}
+
+const transitionResume: { [x in PurchaseStatus]?: {
+  next: PurchaseStatus | undefined,
+} } = {
+  [PurchaseStatus.SuspendedDownloadingProposal]: {
+    next: PurchaseStatus.PendingDownloadingProposal,
+  },
+  [PurchaseStatus.SuspendedAbortingWithRefund]: {
+    next: PurchaseStatus.AbortingWithRefund,
+  },
+  [PurchaseStatus.SuspendedPaying]: {
+    next: PurchaseStatus.PendingPaying,
+  },
+  [PurchaseStatus.SuspendedPayingReplay]: {
+    next: PurchaseStatus.PendingPayingReplay,
+  },
+  [PurchaseStatus.SuspendedQueryingAutoRefund]: {
+    next: PurchaseStatus.PendingQueryingAutoRefund,
+  }
+}
+
+
+export async function suspendPayMerchant(
+  ws: InternalWalletState,
+  proposalId: string,
+): Promise<void> {
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.Payment,
+    proposalId,
+  });
+  const opId = constructTaskIdentifier({
+    tag: PendingTaskType.Purchase,
+    proposalId,
+  });
+  stopLongpolling(ws, opId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [
+      x.purchases,
+    ])
+    .runReadWrite(async (tx) => {
+      const purchase = await tx.purchases.get(proposalId);
+      if (!purchase) {
+        throw Error("purchase not found");
+      }
+      const oldTxState = computePayMerchantTransactionState(purchase);
+      let newStatus = transitionSuspend[purchase.purchaseStatus];
+      if (!newStatus) {
+        return undefined;
+      }
+      await tx.purchases.put(purchase);
+      const newTxState = computePayMerchantTransactionState(purchase);
+      return { oldTxState, newTxState };
+    });
+  notifyTransition(ws, transactionId, transitionInfo);
+  ws.workAvailable.trigger();
+}
+
+
+export async function resumePayMerchant(
+  ws: InternalWalletState,
+  proposalId: string,
+): Promise<void> {
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.Payment,
+    proposalId,
+  });
+  const opId = constructTaskIdentifier({
+    tag: PendingTaskType.Purchase,
+    proposalId,
+  });
+  stopLongpolling(ws, opId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [
+      x.purchases,
+    ])
+    .runReadWrite(async (tx) => {
+      const purchase = await tx.purchases.get(proposalId);
+      if (!purchase) {
+        throw Error("purchase not found");
+      }
+      const oldTxState = computePayMerchantTransactionState(purchase);
+      let newStatus = transitionResume[purchase.purchaseStatus];
+      if (!newStatus) {
+        return undefined;
+      }
+      await tx.purchases.put(purchase);
+      const newTxState = computePayMerchantTransactionState(purchase);
+      return { oldTxState, newTxState };
+    });
+  ws.workAvailable.trigger();
+  notifyTransition(ws, transactionId, transitionInfo);
+  ws.workAvailable.trigger();
+}
+
 export function computePayMerchantTransactionState(
   purchaseRecord: PurchaseRecord,
 ): TransactionState {
   switch (purchaseRecord.purchaseStatus) {
     // Pending States
-    case PurchaseStatus.DownloadingProposal:
+    case PurchaseStatus.PendingDownloadingProposal:
       return {
         major: TransactionMajorState.Pending,
         minor: TransactionMinorState.ClaimProposal,
       };
-    case PurchaseStatus.Paying:
+    case PurchaseStatus.PendingPaying:
       return {
         major: TransactionMajorState.Pending,
         minor: TransactionMinorState.SubmitPayment,
       };
-    case PurchaseStatus.PayingReplay:
+    case PurchaseStatus.PendingPayingReplay:
       return {
         major: TransactionMajorState.Pending,
         minor: TransactionMinorState.RebindSession,
       };
-    case PurchaseStatus.QueryingAutoRefund:
+    case PurchaseStatus.PendingQueryingAutoRefund:
       return {
         major: TransactionMajorState.Pending,
         minor: TransactionMinorState.AutoRefund,
       };
-    case PurchaseStatus.QueryingRefund:
+    case PurchaseStatus.PendingQueryingRefund:
       return {
         major: TransactionMajorState.Pending,
         minor: TransactionMinorState.CheckRefund,
@@ -1937,7 +2052,7 @@ async function processPurchaseAutoRefund(
             logger.warn("purchase does not exist anymore");
             return;
           }
-          if (p.purchaseStatus !== PurchaseStatus.QueryingRefund) {
+          if (p.purchaseStatus !== PurchaseStatus.PendingQueryingRefund) {
             return;
           }
           const oldTxState = computePayMerchantTransactionState(p);
@@ -1982,7 +2097,7 @@ async function processPurchaseAutoRefund(
             logger.warn("purchase does not exist anymore");
             return;
           }
-          if (p.purchaseStatus !== PurchaseStatus.QueryingAutoRefund) {
+          if (p.purchaseStatus !== PurchaseStatus.PendingQueryingAutoRefund) {
             return;
           }
           const oldTxState = computePayMerchantTransactionState(p);
@@ -2118,7 +2233,7 @@ async function processPurchaseQueryRefund(
           logger.warn("purchase does not exist anymore");
           return undefined;
         }
-        if (p.purchaseStatus !== PurchaseStatus.QueryingRefund) {
+        if (p.purchaseStatus !== PurchaseStatus.PendingQueryingRefund) {
           return undefined;
         }
         const oldTxState = computePayMerchantTransactionState(p);
@@ -2143,7 +2258,7 @@ async function processPurchaseQueryRefund(
           logger.warn("purchase does not exist anymore");
           return;
         }
-        if (p.purchaseStatus !== PurchaseStatus.QueryingRefund) {
+        if (p.purchaseStatus !== PurchaseStatus.PendingQueryingRefund) {
           return;
         }
         const oldTxState = computePayMerchantTransactionState(p);
@@ -2242,7 +2357,7 @@ export async function startQueryRefund(
         return;
       }
       const oldTxState = computePayMerchantTransactionState(p);
-      p.purchaseStatus = PurchaseStatus.QueryingRefund;
+      p.purchaseStatus = PurchaseStatus.PendingQueryingRefund;
       const newTxState = computePayMerchantTransactionState(p);
       await tx.purchases.put(p);
       return { oldTxState, newTxState };
diff --git a/packages/taler-wallet-core/src/operations/pay-peer.ts 
b/packages/taler-wallet-core/src/operations/pay-peer.ts
index fb1260e3c..95878543b 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer.ts
@@ -1008,7 +1008,7 @@ export async function processPeerPushCredit(
   const amount = Amounts.parseOrThrow(contractTerms.amount);
 
   if (
-    peerInc.status === PeerPushPaymentIncomingStatus.MergeKycRequired &&
+    peerInc.status === PeerPushPaymentIncomingStatus.PendingMergeKycRequired &&
     peerInc.kycInfo
   ) {
     const txId = constructTransactionIdentifier({
@@ -1080,7 +1080,7 @@ export async function processPeerPushCredit(
           paytoHash: kycPending.h_payto,
           requirementRow: kycPending.requirement_row,
         };
-        peerInc.status = PeerPushPaymentIncomingStatus.MergeKycRequired;
+        peerInc.status = PeerPushPaymentIncomingStatus.PendingMergeKycRequired;
         await tx.peerPushPaymentIncoming.put(peerInc);
       });
     return {
@@ -1122,7 +1122,7 @@ export async function processPeerPushCredit(
       }
       if (
         peerInc.status === PeerPushPaymentIncomingStatus.PendingMerge ||
-        peerInc.status === PeerPushPaymentIncomingStatus.MergeKycRequired
+        peerInc.status === 
PeerPushPaymentIncomingStatus.PendingMergeKycRequired
       ) {
         peerInc.status = PeerPushPaymentIncomingStatus.Done;
       }
@@ -2186,6 +2186,345 @@ export async function suspendPeerPushDebitTransaction(
   notifyTransition(ws, transactionId, transitionInfo);
 }
 
+export async function suspendPeerPullDebitTransaction(
+  ws: InternalWalletState,
+  peerPullPaymentIncomingId: string,
+) {
+  const taskId = constructTaskIdentifier({
+    tag: PendingTaskType.PeerPullDebit,
+    peerPullPaymentIncomingId,
+  });
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPullDebit,
+    peerPullPaymentIncomingId,
+  });
+  stopLongpolling(ws, taskId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.peerPullPaymentIncoming])
+    .runReadWrite(async (tx) => {
+      const pullDebitRec = await tx.peerPullPaymentIncoming.get(
+        peerPullPaymentIncomingId,
+      );
+      if (!pullDebitRec) {
+        logger.warn(`peer pull debit ${peerPullPaymentIncomingId} not found`);
+        return;
+      }
+      let newStatus: PeerPullDebitRecordStatus | undefined = undefined;
+      switch (pullDebitRec.status) {
+        case PeerPullDebitRecordStatus.DialogProposed:
+          break;
+        case PeerPullDebitRecordStatus.DonePaid:
+          break;
+        case PeerPullDebitRecordStatus.PendingDeposit:
+          newStatus = PeerPullDebitRecordStatus.SuspendedDeposit;
+          break;
+        case PeerPullDebitRecordStatus.SuspendedDeposit:
+          break;
+        default:
+          assertUnreachable(pullDebitRec.status);
+      }
+      if (newStatus != null) {
+        const oldTxState = computePeerPullDebitTransactionState(pullDebitRec);
+        pullDebitRec.status = newStatus;
+        const newTxState = computePeerPullDebitTransactionState(pullDebitRec);
+        await tx.peerPullPaymentIncoming.put(pullDebitRec);
+        return {
+          oldTxState,
+          newTxState,
+        };
+      }
+      return undefined;
+    });
+  notifyTransition(ws, transactionId, transitionInfo);
+}
+
+export async function resumePeerPullDebitTransaction(
+  ws: InternalWalletState,
+  peerPullPaymentIncomingId: string,
+) {
+  const taskId = constructTaskIdentifier({
+    tag: PendingTaskType.PeerPullDebit,
+    peerPullPaymentIncomingId,
+  });
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPullDebit,
+    peerPullPaymentIncomingId,
+  });
+  stopLongpolling(ws, taskId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.peerPullPaymentIncoming])
+    .runReadWrite(async (tx) => {
+      const pullDebitRec = await tx.peerPullPaymentIncoming.get(
+        peerPullPaymentIncomingId,
+      );
+      if (!pullDebitRec) {
+        logger.warn(`peer pull debit ${peerPullPaymentIncomingId} not found`);
+        return;
+      }
+      let newStatus: PeerPullDebitRecordStatus | undefined = undefined;
+      switch (pullDebitRec.status) {
+        case PeerPullDebitRecordStatus.DialogProposed:
+        case PeerPullDebitRecordStatus.DonePaid:
+        case PeerPullDebitRecordStatus.PendingDeposit:
+          break;
+        case PeerPullDebitRecordStatus.SuspendedDeposit:
+          newStatus = PeerPullDebitRecordStatus.PendingDeposit;
+          break;
+        default:
+          assertUnreachable(pullDebitRec.status);
+      }
+      if (newStatus != null) {
+        const oldTxState = computePeerPullDebitTransactionState(pullDebitRec);
+        pullDebitRec.status = newStatus;
+        const newTxState = computePeerPullDebitTransactionState(pullDebitRec);
+        await tx.peerPullPaymentIncoming.put(pullDebitRec);
+        return {
+          oldTxState,
+          newTxState,
+        };
+      }
+      return undefined;
+    });
+  ws.workAvailable.trigger();
+  notifyTransition(ws, transactionId, transitionInfo);
+}
+
+export async function suspendPeerPushCreditTransaction(
+  ws: InternalWalletState,
+  peerPushPaymentIncomingId: string,
+) {
+  const taskId = constructTaskIdentifier({
+    tag: PendingTaskType.PeerPushCredit,
+    peerPushPaymentIncomingId,
+  });
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPushCredit,
+    peerPushPaymentIncomingId,
+  });
+  stopLongpolling(ws, taskId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.peerPushPaymentIncoming])
+    .runReadWrite(async (tx) => {
+      const pushCreditRec = await tx.peerPushPaymentIncoming.get(
+        peerPushPaymentIncomingId,
+      );
+      if (!pushCreditRec) {
+        logger.warn(`peer push credit ${peerPushPaymentIncomingId} not found`);
+        return;
+      }
+      let newStatus: PeerPushPaymentIncomingStatus | undefined = undefined;
+      switch (pushCreditRec.status) {
+        case PeerPushPaymentIncomingStatus.DialogProposed:
+        case PeerPushPaymentIncomingStatus.Done:
+        case PeerPushPaymentIncomingStatus.SuspendedMerge:
+        case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired:
+        case PeerPushPaymentIncomingStatus.SuspendedWithdrawing:
+          break;
+        case PeerPushPaymentIncomingStatus.PendingMergeKycRequired:
+          newStatus = PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired;
+          break;
+        case PeerPushPaymentIncomingStatus.PendingMerge:
+          newStatus = PeerPushPaymentIncomingStatus.SuspendedMerge;
+          break;
+        case PeerPushPaymentIncomingStatus.PendingWithdrawing:
+          // FIXME: Suspend internal withdrawal transaction!
+          newStatus = PeerPushPaymentIncomingStatus.SuspendedWithdrawing;
+          break;
+        default:
+          assertUnreachable(pushCreditRec.status);
+      }
+      if (newStatus != null) {
+        const oldTxState = 
computePeerPushCreditTransactionState(pushCreditRec);
+        pushCreditRec.status = newStatus;
+        const newTxState = 
computePeerPushCreditTransactionState(pushCreditRec);
+        await tx.peerPushPaymentIncoming.put(pushCreditRec);
+        return {
+          oldTxState,
+          newTxState,
+        };
+      }
+      return undefined;
+    });
+  notifyTransition(ws, transactionId, transitionInfo);
+}
+
+export async function resumePeerPushCreditTransaction(
+  ws: InternalWalletState,
+  peerPushPaymentIncomingId: string,
+) {
+  const taskId = constructTaskIdentifier({
+    tag: PendingTaskType.PeerPushCredit,
+    peerPushPaymentIncomingId,
+  });
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPushCredit,
+    peerPushPaymentIncomingId,
+  });
+  stopLongpolling(ws, taskId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.peerPushPaymentIncoming])
+    .runReadWrite(async (tx) => {
+      const pushCreditRec = await tx.peerPushPaymentIncoming.get(
+        peerPushPaymentIncomingId,
+      );
+      if (!pushCreditRec) {
+        logger.warn(`peer push credit ${peerPushPaymentIncomingId} not found`);
+        return;
+      }
+      let newStatus: PeerPushPaymentIncomingStatus | undefined = undefined;
+      switch (pushCreditRec.status) {
+        case PeerPushPaymentIncomingStatus.DialogProposed:
+        case PeerPushPaymentIncomingStatus.Done:
+        case PeerPushPaymentIncomingStatus.PendingMergeKycRequired:
+        case PeerPushPaymentIncomingStatus.PendingMerge:
+        case PeerPushPaymentIncomingStatus.PendingWithdrawing:
+        case PeerPushPaymentIncomingStatus.SuspendedMerge:
+          newStatus = PeerPushPaymentIncomingStatus.PendingMerge;
+          break;
+        case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired:
+          newStatus = PeerPushPaymentIncomingStatus.PendingMergeKycRequired;
+          break;
+        case PeerPushPaymentIncomingStatus.SuspendedWithdrawing:
+          // FIXME: resume underlying "internal-withdrawal" transaction.
+          newStatus = PeerPushPaymentIncomingStatus.PendingWithdrawing;
+          break;
+        default:
+          assertUnreachable(pushCreditRec.status);
+      }
+      if (newStatus != null) {
+        const oldTxState = 
computePeerPushCreditTransactionState(pushCreditRec);
+        pushCreditRec.status = newStatus;
+        const newTxState = 
computePeerPushCreditTransactionState(pushCreditRec);
+        await tx.peerPushPaymentIncoming.put(pushCreditRec);
+        return {
+          oldTxState,
+          newTxState,
+        };
+      }
+      return undefined;
+    });
+  ws.workAvailable.trigger();
+  notifyTransition(ws, transactionId, transitionInfo);
+}
+
+export async function suspendPeerPullCreditTransaction(
+  ws: InternalWalletState,
+  pursePub: string,
+) {
+  const taskId = constructTaskIdentifier({
+    tag: PendingTaskType.PeerPullCredit,
+    pursePub,
+  });
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPullCredit,
+    pursePub,
+  });
+  stopLongpolling(ws, taskId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.peerPullPaymentInitiations])
+    .runReadWrite(async (tx) => {
+      const pullCreditRec = await tx.peerPullPaymentInitiations.get(pursePub);
+      if (!pullCreditRec) {
+        logger.warn(`peer pull credit ${pursePub} not found`);
+        return;
+      }
+      let newStatus: PeerPullPaymentInitiationStatus | undefined = undefined;
+      switch (pullCreditRec.status) {
+        case PeerPullPaymentInitiationStatus.PendingCreatePurse:
+          newStatus = PeerPullPaymentInitiationStatus.SuspendedCreatePurse;
+          break;
+        case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
+          newStatus = 
PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired;
+          break;
+        case PeerPullPaymentInitiationStatus.PendingWithdrawing:
+          newStatus = PeerPullPaymentInitiationStatus.SuspendedWithdrawing;
+          break;
+        case PeerPullPaymentInitiationStatus.PendingReady:
+          newStatus = PeerPullPaymentInitiationStatus.SuspendedReady;
+          break;
+        case PeerPullPaymentInitiationStatus.DonePurseDeposited:
+        case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
+        case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
+        case PeerPullPaymentInitiationStatus.SuspendedReady:
+        case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
+          break;
+        default:
+          assertUnreachable(pullCreditRec.status);
+      }
+      if (newStatus != null) {
+        const oldTxState = 
computePeerPullCreditTransactionState(pullCreditRec);
+        pullCreditRec.status = newStatus;
+        const newTxState = 
computePeerPullCreditTransactionState(pullCreditRec);
+        await tx.peerPullPaymentInitiations.put(pullCreditRec);
+        return {
+          oldTxState,
+          newTxState,
+        };
+      }
+      return undefined;
+    });
+  notifyTransition(ws, transactionId, transitionInfo);
+}
+
+export async function resumePeerPullCreditTransaction(
+  ws: InternalWalletState,
+  pursePub: string,
+) {
+  const taskId = constructTaskIdentifier({
+    tag: PendingTaskType.PeerPullCredit,
+    pursePub,
+  });
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPullCredit,
+    pursePub,
+  });
+  stopLongpolling(ws, taskId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.peerPullPaymentInitiations])
+    .runReadWrite(async (tx) => {
+      const pullCreditRec = await tx.peerPullPaymentInitiations.get(pursePub);
+      if (!pullCreditRec) {
+        logger.warn(`peer pull credit ${pursePub} not found`);
+        return;
+      }
+      let newStatus: PeerPullPaymentInitiationStatus | undefined = undefined;
+      switch (pullCreditRec.status) {
+        case PeerPullPaymentInitiationStatus.PendingCreatePurse:
+        case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
+        case PeerPullPaymentInitiationStatus.PendingWithdrawing:
+        case PeerPullPaymentInitiationStatus.PendingReady:
+        case PeerPullPaymentInitiationStatus.DonePurseDeposited:
+        case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
+          newStatus = PeerPullPaymentInitiationStatus.PendingCreatePurse;
+          break;
+        case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
+          newStatus = PeerPullPaymentInitiationStatus.PendingMergeKycRequired;
+          break;
+        case PeerPullPaymentInitiationStatus.SuspendedReady:
+          newStatus = PeerPullPaymentInitiationStatus.PendingReady;
+          break;
+        case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
+          newStatus = PeerPullPaymentInitiationStatus.PendingWithdrawing;
+          break;
+        default:
+          assertUnreachable(pullCreditRec.status);
+      }
+      if (newStatus != null) {
+        const oldTxState = 
computePeerPullCreditTransactionState(pullCreditRec);
+        pullCreditRec.status = newStatus;
+        const newTxState = 
computePeerPullCreditTransactionState(pullCreditRec);
+        await tx.peerPullPaymentInitiations.put(pullCreditRec);
+        return {
+          oldTxState,
+          newTxState,
+        };
+      }
+      return undefined;
+    });
+  ws.workAvailable.trigger();
+  notifyTransition(ws, transactionId, transitionInfo);
+}
+
 export async function resumePeerPushDebitTransaction(
   ws: InternalWalletState,
   pursePub: string,
@@ -2244,6 +2583,7 @@ export async function resumePeerPushDebitTransaction(
       }
       return undefined;
     });
+  ws.workAvailable.trigger();
   notifyTransition(ws, transactionId, transitionInfo);
 }
 
@@ -2265,7 +2605,7 @@ export function computePeerPushCreditTransactionState(
       return {
         major: TransactionMajorState.Done,
       };
-    case PeerPushPaymentIncomingStatus.MergeKycRequired:
+    case PeerPushPaymentIncomingStatus.PendingMergeKycRequired:
       return {
         major: TransactionMajorState.Pending,
         minor: TransactionMinorState.KycRequired,
diff --git a/packages/taler-wallet-core/src/operations/tip.ts 
b/packages/taler-wallet-core/src/operations/tip.ts
index 524970faa..70b595c2f 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -48,6 +48,7 @@ import {
   CoinSourceType,
   DenominationRecord,
   TipRecord,
+  TipRecordStatus,
 } from "../db.js";
 import { makeErrorDetail } from "@gnu-taler/taler-util";
 import { InternalWalletState } from "../internal-wallet-state.js";
@@ -57,6 +58,7 @@ import {
 } from "@gnu-taler/taler-util/http";
 import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
 import {
+  constructTaskIdentifier,
   OperationAttemptResult,
   OperationAttemptResultType,
 } from "../util/retries.js";
@@ -68,7 +70,13 @@ import {
   updateWithdrawalDenoms,
 } from "./withdraw.js";
 import { selectWithdrawalDenominations } from "../util/coinSelection.js";
-import { constructTransactionIdentifier } from "./transactions.js";
+import {
+  constructTransactionIdentifier,
+  notifyTransition,
+  stopLongpolling,
+} from "./transactions.js";
+import { PendingTaskType } from "../pending-types.js";
+import { assertUnreachable } from "../util/assertUnreachable.js";
 
 const logger = new Logger("operations/tip.ts");
 
@@ -156,6 +164,7 @@ export async function prepareTip(
     const newTipRecord: TipRecord = {
       walletTipId: walletTipId,
       acceptedTimestamp: undefined,
+      status: TipRecordStatus.PendingPickup,
       tipAmountRaw: Amounts.stringify(amount),
       tipExpiration: tipPickupStatus.expiration,
       exchangeBaseUrl: tipPickupStatus.exchange_url,
@@ -180,7 +189,7 @@ export async function prepareTip(
   const transactionId = constructTransactionIdentifier({
     tag: TransactionType.Tip,
     walletTipId: tipRecord.walletTipId,
-  })
+  });
 
   const tipStatus: PrepareTipResult = {
     accepted: !!tipRecord && !!tipRecord.acceptedTimestamp,
@@ -410,3 +419,98 @@ export async function acceptTip(
     next_url: found?.next_url,
   };
 }
+
+export async function suspendTipTransaction(
+  ws: InternalWalletState,
+  walletTipId: string,
+): Promise<void> {
+  const taskId = constructTaskIdentifier({
+    tag: PendingTaskType.TipPickup,
+    walletTipId,
+  });
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.Tip,
+    walletTipId,
+  });
+  stopLongpolling(ws, taskId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.tips])
+    .runReadWrite(async (tx) => {
+      const tipRec = await tx.tips.get(walletTipId);
+      if (!tipRec) {
+        logger.warn(`transaction tip ${walletTipId} not found`);
+        return;
+      }
+      let newStatus: TipRecordStatus | undefined = undefined;
+      switch (tipRec.status) {
+        case TipRecordStatus.Done:
+        case TipRecordStatus.SuspendidPickup:
+          break;
+        case TipRecordStatus.PendingPickup:
+          newStatus = TipRecordStatus.SuspendidPickup;
+          break;
+        default:
+          assertUnreachable(tipRec.status);
+      }
+      if (newStatus != null) {
+        const oldTxState = computeTipTransactionStatus(tipRec);
+        tipRec.status = newStatus;
+        const newTxState = computeTipTransactionStatus(tipRec);
+        await tx.tips.put(tipRec);
+        return {
+          oldTxState,
+          newTxState,
+        };
+      }
+      return undefined;
+    });
+  ws.workAvailable.trigger();
+  notifyTransition(ws, transactionId, transitionInfo);
+}
+
+export async function resumeTipTransaction(
+  ws: InternalWalletState,
+  walletTipId: string,
+): Promise<void> {
+  const taskId = constructTaskIdentifier({
+    tag: PendingTaskType.TipPickup,
+    walletTipId,
+  });
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.Tip,
+    walletTipId,
+  });
+  stopLongpolling(ws, taskId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.tips])
+    .runReadWrite(async (tx) => {
+      const tipRec = await tx.tips.get(walletTipId);
+      if (!tipRec) {
+        logger.warn(`transaction tip ${walletTipId} not found`);
+        return;
+      }
+      let newStatus: TipRecordStatus | undefined = undefined;
+      switch (tipRec.status) {
+        case TipRecordStatus.Done:
+        case TipRecordStatus.SuspendidPickup:
+          newStatus = TipRecordStatus.PendingPickup;
+          break;
+        case TipRecordStatus.PendingPickup:
+          break;
+        default:
+          assertUnreachable(tipRec.status);
+      }
+      if (newStatus != null) {
+        const oldTxState = computeTipTransactionStatus(tipRec);
+        tipRec.status = newStatus;
+        const newTxState = computeTipTransactionStatus(tipRec);
+        await tx.tips.put(tipRec);
+        return {
+          oldTxState,
+          newTxState,
+        };
+      }
+      return undefined;
+    });
+  notifyTransition(ws, transactionId, transitionInfo);
+}
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index 3645edd93..f1cfaed45 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -86,20 +86,40 @@ import {
   computeRefundTransactionState,
   expectProposalDownload,
   extractContractData,
+  resumePayMerchant,
+  suspendPayMerchant,
 } from "./pay-merchant.js";
 import {
   computePeerPullCreditTransactionState,
   computePeerPullDebitTransactionState,
   computePeerPushCreditTransactionState,
   computePeerPushDebitTransactionState,
+  resumePeerPullCreditTransaction,
+  resumePeerPullDebitTransaction,
+  resumePeerPushCreditTransaction,
+  resumePeerPushDebitTransaction,
+  suspendPeerPullCreditTransaction,
+  suspendPeerPullDebitTransaction,
+  suspendPeerPushCreditTransaction,
+  suspendPeerPushDebitTransaction,
 } from "./pay-peer.js";
-import { computeRefreshTransactionState } from "./refresh.js";
-import { computeTipTransactionStatus } from "./tip.js";
+import {
+  computeRefreshTransactionState,
+  resumeRefreshGroup,
+  suspendRefreshGroup,
+} from "./refresh.js";
+import {
+  computeTipTransactionStatus,
+  resumeTipTransaction,
+  suspendTipTransaction,
+} from "./tip.js";
 import {
   abortWithdrawalTransaction,
   augmentPaytoUrisForWithdrawal,
   cancelAbortingWithdrawalTransaction,
   computeWithdrawalTransactionStatus,
+  resumeWithdrawalTransaction,
+  suspendWithdrawalTransaction,
 } from "./withdraw.js";
 
 const logger = new Logger("taler-wallet-core:transactions.ts");
@@ -159,6 +179,7 @@ export async function getTransactionById(
   }
 
   switch (parsedTx.tag) {
+    case TransactionType.InternalWithdrawal:
     case TransactionType.Withdrawal: {
       const withdrawalGroupId = parsedTx.withdrawalGroupId;
       return await ws.db
@@ -844,7 +865,7 @@ async function buildTransactionForPurchase(
     proposalId: purchaseRecord.proposalId,
     info,
     refundQueryActive:
-      purchaseRecord.purchaseStatus === PurchaseStatus.QueryingRefund,
+      purchaseRecord.purchaseStatus === PurchaseStatus.PendingQueryingRefund,
     ...(ort?.lastError ? { error: ort.lastError } : {}),
   };
 }
@@ -1197,7 +1218,8 @@ export type ParsedTransactionIdentifier =
   | { tag: TransactionType.Refresh; refreshGroupId: string }
   | { tag: TransactionType.Refund; refundGroupId: string }
   | { tag: TransactionType.Tip; walletTipId: string }
-  | { tag: TransactionType.Withdrawal; withdrawalGroupId: string };
+  | { tag: TransactionType.Withdrawal; withdrawalGroupId: string }
+  | { tag: TransactionType.InternalWithdrawal; withdrawalGroupId: string };
 
 export function constructTransactionIdentifier(
   pTxId: ParsedTransactionIdentifier,
@@ -1223,6 +1245,8 @@ export function constructTransactionIdentifier(
       return `txn:${pTxId.tag}:${pTxId.walletTipId}` as TransactionIdStr;
     case TransactionType.Withdrawal:
       return `txn:${pTxId.tag}:${pTxId.withdrawalGroupId}` as TransactionIdStr;
+    case TransactionType.InternalWithdrawal:
+      return `txn:${pTxId.tag}:${pTxId.withdrawalGroupId}` as TransactionIdStr;
     default:
       assertUnreachable(pTxId);
   }
@@ -1242,6 +1266,10 @@ export function parseTransactionIdentifier(
 
   const [prefix, type, ...rest] = txnParts;
 
+  if (prefix != "txn") {
+    throw Error("invalid transaction identifier");
+  }
+
   switch (type) {
     case TransactionType.Deposit:
       return { tag: TransactionType.Deposit, depositGroupId: rest[0] };
@@ -1329,6 +1357,7 @@ export async function retryTransaction(
       stopLongpolling(ws, taskId);
       break;
     }
+    case TransactionType.InternalWithdrawal:
     case TransactionType.Withdrawal: {
       // FIXME: Abort current long-poller!
       const taskId = constructTaskIdentifier({
@@ -1366,8 +1395,38 @@ export async function retryTransaction(
       stopLongpolling(ws, taskId);
       break;
     }
-    default:
+    case TransactionType.PeerPullDebit: {
+      const taskId = constructTaskIdentifier({
+        tag: PendingTaskType.PeerPullDebit,
+        peerPullPaymentIncomingId: parsedTx.peerPullPaymentIncomingId,
+      });
+      await resetOperationTimeout(ws, taskId);
+      stopLongpolling(ws, taskId);
+      break;
+    }
+    case TransactionType.PeerPushCredit: {
+      const taskId = constructTaskIdentifier({
+        tag: PendingTaskType.PeerPushCredit,
+        peerPushPaymentIncomingId: parsedTx.peerPushPaymentIncomingId,
+      });
+      await resetOperationTimeout(ws, taskId);
+      stopLongpolling(ws, taskId);
       break;
+    }
+    case TransactionType.PeerPushDebit: {
+      const taskId = constructTaskIdentifier({
+        tag: PendingTaskType.PeerPushDebit,
+        pursePub: parsedTx.pursePub,
+      });
+      await resetOperationTimeout(ws, taskId);
+      stopLongpolling(ws, taskId);
+      break;
+    }
+    case TransactionType.Refund:
+      // Nothing to do for a refund transaction.
+      break;
+    default:
+      assertUnreachable(parsedTx);
   }
 }
 
@@ -1389,8 +1448,35 @@ export async function suspendTransaction(
     case TransactionType.Deposit:
       await suspendDepositGroup(ws, tx.depositGroupId);
       return;
+    case TransactionType.Refresh:
+      await suspendRefreshGroup(ws, tx.refreshGroupId);
+      return;
+    case TransactionType.InternalWithdrawal:
+    case TransactionType.Withdrawal:
+      await suspendWithdrawalTransaction(ws, tx.withdrawalGroupId);
+      return;
+    case TransactionType.Payment:
+      await suspendPayMerchant(ws, tx.proposalId);
+      return;
+    case TransactionType.PeerPullCredit:
+      await suspendPeerPullCreditTransaction(ws, tx.pursePub);
+      break;
+    case TransactionType.PeerPushDebit:
+      await suspendPeerPushDebitTransaction(ws, tx.pursePub);
+      break;
+    case TransactionType.PeerPullDebit:
+      await suspendPeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId);
+      break;
+    case TransactionType.PeerPushCredit:
+      await suspendPeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId);
+      break;
+    case TransactionType.Refund:
+      throw Error("refund transactions can't be suspended or resumed");
+    case TransactionType.Tip:
+      await suspendTipTransaction(ws, tx.walletTipId);
+      break;
     default:
-      logger.warn(`unable to suspend transaction of type '${tx.tag}'`);
+      assertUnreachable(tx);
   }
 }
 
@@ -1429,8 +1515,33 @@ export async function resumeTransaction(
     case TransactionType.Deposit:
       await resumeDepositGroup(ws, tx.depositGroupId);
       return;
-    default:
-      logger.warn(`unable to resume transaction of type '${tx.tag}'`);
+    case TransactionType.Refresh:
+      await resumeRefreshGroup(ws, tx.refreshGroupId);
+      return;
+    case TransactionType.InternalWithdrawal:
+    case TransactionType.Withdrawal:
+      await resumeWithdrawalTransaction(ws, tx.withdrawalGroupId);
+      return;
+    case TransactionType.Payment:
+      await resumePayMerchant(ws, tx.proposalId);
+      return;
+    case TransactionType.PeerPullCredit:
+      await resumePeerPullCreditTransaction(ws, tx.pursePub);
+      break;
+    case TransactionType.PeerPushDebit:
+      await resumePeerPushDebitTransaction(ws, tx.pursePub);
+      break;
+    case TransactionType.PeerPullDebit:
+      await resumePeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId);
+      break;
+    case TransactionType.PeerPushCredit:
+      await resumePeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId);
+      break;
+    case TransactionType.Refund:
+      throw Error("refund transactions can't be suspended or resumed");
+    case TransactionType.Tip:
+      await resumeTipTransaction(ws, tx.walletTipId);
+      break;
   }
 }
 
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index ae170fa2c..7636395bd 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -259,7 +259,7 @@ export async function resumeWithdrawalTransaction(
       }
       return undefined;
     });
-
+  ws.workAvailable.trigger();
   const transactionId = constructTransactionIdentifier({
     tag: TransactionType.Withdrawal,
     withdrawalGroupId,

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]