summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-05-30 12:28:21 +0200
committerFlorian Dold <florian@dold.me>2023-05-30 12:28:21 +0200
commit000359a5e746d9b704b05f2f3eb8442e10a31f75 (patch)
tree9d6d2a1dc2f094ec5de2aeb2ae9a8cb43c238906 /packages/taler-wallet-core/src
parentd1dade44265ce515526cef90cb4651bb905b17ee (diff)
downloadwallet-core-000359a5e746d9b704b05f2f3eb8442e10a31f75.tar.gz
wallet-core-000359a5e746d9b704b05f2f3eb8442e10a31f75.tar.bz2
wallet-core-000359a5e746d9b704b05f2f3eb8442e10a31f75.zip
finish implementation of abort / cancelAborting on all tx types
Diffstat (limited to 'packages/taler-wallet-core/src')
-rw-r--r--packages/taler-wallet-core/src/db.ts21
-rw-r--r--packages/taler-wallet-core/src/operations/deposits.ts2
-rw-r--r--packages/taler-wallet-core/src/operations/pay-merchant.ts83
-rw-r--r--packages/taler-wallet-core/src/operations/pay-peer.ts410
-rw-r--r--packages/taler-wallet-core/src/operations/refresh.ts64
-rw-r--r--packages/taler-wallet-core/src/operations/tip.ts97
-rw-r--r--packages/taler-wallet-core/src/operations/transactions.ts67
7 files changed, 689 insertions, 55 deletions
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index 3edaf8af5..3147cb9b9 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -879,6 +879,7 @@ export enum TipRecordStatus {
SuspendidPickup = 21,
Done = 50,
+ Aborted = 51,
}
export enum RefreshCoinStatus {
@@ -899,9 +900,10 @@ export enum OperationStatus {
export enum RefreshOperationStatus {
Pending = 10 /* ACTIVE_START */,
+ Suspended = 20 /* DORMANT_START + 2 */,
+
Finished = 50 /* DORMANT_START */,
- FinishedWithError = 51 /* DORMANT_START + 1 */,
- Suspended = 52 /* DORMANT_START + 2 */,
+ Failed = 51 /* DORMANT_START + 1 */,
}
export enum DepositGroupOperationStatus {
@@ -1155,6 +1157,8 @@ export enum PurchaseStatus {
* Payment was successful.
*/
Done = 54,
+
+ FailedAbort = 55,
}
/**
@@ -1778,6 +1782,7 @@ export enum PeerPushPaymentInitiationStatus {
Done = 50 /* DORMANT_START */,
Aborted = 51,
+ Failed = 52,
}
export interface PeerPushPaymentCoinSelection {
@@ -1850,13 +1855,17 @@ export enum PeerPullPaymentInitiationStatus {
PendingReady = 11 /* ACTIVE_START + 1 */,
PendingMergeKycRequired = 12 /* ACTIVE_START + 2 */,
PendingWithdrawing = 13,
+ AbortingDeletePurse = 14,
SuspendedCreatePurse = 30,
SuspendedReady = 31,
SuspendedMergeKycRequired = 32,
SuspendedWithdrawing = 33,
+ SuspendedAbortingDeletePurse = 34,
DonePurseDeposited = 50 /* DORMANT_START */,
+ Failed = 51,
+ Aborted = 52,
}
export interface PeerPullPaymentInitiationRecord {
@@ -1927,6 +1936,8 @@ export enum PeerPushPaymentIncomingStatus {
DialogProposed = 30 /* USER_ATTENTION_START */,
Done = 50 /* DORMANT_START */,
+ Aborted = 51,
+ Failed = 52,
}
/**
@@ -1978,12 +1989,16 @@ export interface PeerPushPaymentIncomingRecord {
export enum PeerPullDebitRecordStatus {
PendingDeposit = 10 /* ACTIVE_START */,
+ AbortingRefresh = 11,
- SuspendedDeposit = 11,
+ SuspendedDeposit = 20,
+ SuspendedAbortingRefresh = 21,
DialogProposed = 30 /* USER_ATTENTION_START */,
DonePaid = 50 /* DORMANT_START */,
+ Aborted = 51,
+ Failed = 52,
}
export interface PeerPullPaymentCoinSelection {
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts
index de881ddd2..1ed2a705e 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -447,7 +447,7 @@ async function waitForRefreshOnDepositGroup(
newOpState = DepositOperationStatus.Aborted;
} else if (
refreshGroup.operationStatus ===
- RefreshOperationStatus.FinishedWithError
+ RefreshOperationStatus.Failed
) {
newOpState = DepositOperationStatus.Aborted;
}
diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts b/packages/taler-wallet-core/src/operations/pay-merchant.ts
index 30c75f695..8462f2fb9 100644
--- a/packages/taler-wallet-core/src/operations/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts
@@ -1508,6 +1508,7 @@ export async function processPurchase(
case PurchaseStatus.SuspendedPendingAcceptRefund:
case PurchaseStatus.SuspendedQueryingAutoRefund:
case PurchaseStatus.SuspendedQueryingRefund:
+ case PurchaseStatus.FailedAbort:
return {
type: OperationAttemptResultType.Finished,
result: undefined,
@@ -1790,10 +1791,55 @@ export async function abortPayMerchant(
ws.workAvailable.trigger();
}
+export async function cancelAbortingPaymentTransaction(
+ ws: InternalWalletState,
+ proposalId: string,
+): Promise<void> {
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.Payment,
+ proposalId,
+ });
+ const opId = constructTaskIdentifier({
+ tag: PendingTaskType.Purchase,
+ proposalId,
+ });
+ const transitionInfo = await ws.db
+ .mktx((x) => [
+ x.purchases,
+ x.refreshGroups,
+ x.denominations,
+ x.coinAvailability,
+ x.coins,
+ x.operationRetries,
+ ])
+ .runReadWrite(async (tx) => {
+ const purchase = await tx.purchases.get(proposalId);
+ if (!purchase) {
+ throw Error("purchase not found");
+ }
+ const oldTxState = computePayMerchantTransactionState(purchase);
+ let newState: PurchaseStatus | undefined = undefined;
+ switch (purchase.purchaseStatus) {
+ case PurchaseStatus.AbortingWithRefund:
+ newState = PurchaseStatus.FailedAbort;
+ break;
+ }
+ if (newState) {
+ purchase.purchaseStatus = newState;
+ await tx.purchases.put(purchase);
+ }
+ const newTxState = computePayMerchantTransactionState(purchase);
+ return { oldTxState, newTxState };
+ });
+ notifyTransition(ws, transactionId, transitionInfo);
+ ws.workAvailable.trigger();
+}
-const transitionSuspend: { [x in PurchaseStatus]?: {
- next: PurchaseStatus | undefined,
-} } = {
+const transitionSuspend: {
+ [x in PurchaseStatus]?: {
+ next: PurchaseStatus | undefined;
+ };
+} = {
[PurchaseStatus.PendingDownloadingProposal]: {
next: PurchaseStatus.SuspendedDownloadingProposal,
},
@@ -1808,12 +1854,14 @@ const transitionSuspend: { [x in PurchaseStatus]?: {
},
[PurchaseStatus.PendingQueryingAutoRefund]: {
next: PurchaseStatus.SuspendedQueryingAutoRefund,
- }
-}
+ },
+};
-const transitionResume: { [x in PurchaseStatus]?: {
- next: PurchaseStatus | undefined,
-} } = {
+const transitionResume: {
+ [x in PurchaseStatus]?: {
+ next: PurchaseStatus | undefined;
+ };
+} = {
[PurchaseStatus.SuspendedDownloadingProposal]: {
next: PurchaseStatus.PendingDownloadingProposal,
},
@@ -1828,9 +1876,8 @@ const transitionResume: { [x in PurchaseStatus]?: {
},
[PurchaseStatus.SuspendedQueryingAutoRefund]: {
next: PurchaseStatus.PendingQueryingAutoRefund,
- }
-}
-
+ },
+};
export async function suspendPayMerchant(
ws: InternalWalletState,
@@ -1846,9 +1893,7 @@ export async function suspendPayMerchant(
});
stopLongpolling(ws, opId);
const transitionInfo = await ws.db
- .mktx((x) => [
- x.purchases,
- ])
+ .mktx((x) => [x.purchases])
.runReadWrite(async (tx) => {
const purchase = await tx.purchases.get(proposalId);
if (!purchase) {
@@ -1867,7 +1912,6 @@ export async function suspendPayMerchant(
ws.workAvailable.trigger();
}
-
export async function resumePayMerchant(
ws: InternalWalletState,
proposalId: string,
@@ -1882,9 +1926,7 @@ export async function resumePayMerchant(
});
stopLongpolling(ws, opId);
const transitionInfo = await ws.db
- .mktx((x) => [
- x.purchases,
- ])
+ .mktx((x) => [x.purchases])
.runReadWrite(async (tx) => {
const purchase = await tx.purchases.get(proposalId);
if (!purchase) {
@@ -2010,6 +2052,11 @@ export function computePayMerchantTransactionState(
major: TransactionMajorState.Failed,
minor: TransactionMinorState.ClaimProposal,
};
+ case PurchaseStatus.FailedAbort:
+ return {
+ major: TransactionMajorState.Failed,
+ minor: TransactionMinorState.AbortingBank,
+ };
}
}
diff --git a/packages/taler-wallet-core/src/operations/pay-peer.ts b/packages/taler-wallet-core/src/operations/pay-peer.ts
index 95878543b..031bdfb92 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer.ts
@@ -2005,6 +2005,10 @@ export function computePeerPushDebitTransactionState(
return {
major: TransactionMajorState.Done,
};
+ case PeerPushPaymentInitiationStatus.Failed:
+ return {
+ major: TransactionMajorState.Failed,
+ }
}
}
@@ -2048,6 +2052,8 @@ export async function abortPeerPushDebitTransaction(
case PeerPushPaymentInitiationStatus.Aborted:
// Do nothing
break;
+ case PeerPushPaymentInitiationStatus.Failed:
+ break;
default:
assertUnreachable(pushDebitRec.status);
}
@@ -2104,6 +2110,7 @@ export async function cancelAbortingPeerPushDebitTransaction(
case PeerPushPaymentInitiationStatus.PendingCreatePurse:
case PeerPushPaymentInitiationStatus.Done:
case PeerPushPaymentInitiationStatus.Aborted:
+ case PeerPushPaymentInitiationStatus.Failed:
// Do nothing
break;
default:
@@ -2166,6 +2173,7 @@ export async function suspendPeerPushDebitTransaction(
case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
case PeerPushPaymentInitiationStatus.Done:
case PeerPushPaymentInitiationStatus.Aborted:
+ case PeerPushPaymentInitiationStatus.Failed:
// Do nothing
break;
default:
@@ -2220,6 +2228,138 @@ export async function suspendPeerPullDebitTransaction(
break;
case PeerPullDebitRecordStatus.SuspendedDeposit:
break;
+ case PeerPullDebitRecordStatus.Aborted:
+ break;
+ case PeerPullDebitRecordStatus.AbortingRefresh:
+ newStatus = PeerPullDebitRecordStatus.SuspendedAbortingRefresh;
+ break;
+ case PeerPullDebitRecordStatus.Failed:
+ break;
+ case PeerPullDebitRecordStatus.SuspendedAbortingRefresh:
+ 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 abortPeerPullDebitTransaction(
+ 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:
+ newStatus = PeerPullDebitRecordStatus.Aborted;
+ break;
+ case PeerPullDebitRecordStatus.DonePaid:
+ break;
+ case PeerPullDebitRecordStatus.PendingDeposit:
+ newStatus = PeerPullDebitRecordStatus.AbortingRefresh;
+ break;
+ case PeerPullDebitRecordStatus.SuspendedDeposit:
+ break;
+ case PeerPullDebitRecordStatus.Aborted:
+ break;
+ case PeerPullDebitRecordStatus.AbortingRefresh:
+ break;
+ case PeerPullDebitRecordStatus.Failed:
+ break;
+ case PeerPullDebitRecordStatus.SuspendedAbortingRefresh:
+ 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 cancelAbortingPeerPullDebitTransaction(
+ 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:
+ newStatus = PeerPullDebitRecordStatus.Aborted;
+ break;
+ case PeerPullDebitRecordStatus.DonePaid:
+ break;
+ case PeerPullDebitRecordStatus.PendingDeposit:
+ break;
+ case PeerPullDebitRecordStatus.SuspendedDeposit:
+ break;
+ case PeerPullDebitRecordStatus.Aborted:
+ break;
+ case PeerPullDebitRecordStatus.Failed:
+ break;
+ case PeerPullDebitRecordStatus.SuspendedAbortingRefresh:
+ case PeerPullDebitRecordStatus.AbortingRefresh:
+ // FIXME: abort underlying refresh!
+ newStatus = PeerPullDebitRecordStatus.Failed;
+ break;
default:
assertUnreachable(pullDebitRec.status);
}
@@ -2270,6 +2410,15 @@ export async function resumePeerPullDebitTransaction(
case PeerPullDebitRecordStatus.SuspendedDeposit:
newStatus = PeerPullDebitRecordStatus.PendingDeposit;
break;
+ case PeerPullDebitRecordStatus.Aborted:
+ break;
+ case PeerPullDebitRecordStatus.AbortingRefresh:
+ break;
+ case PeerPullDebitRecordStatus.Failed:
+ break;
+ case PeerPullDebitRecordStatus.SuspendedAbortingRefresh:
+ newStatus = PeerPullDebitRecordStatus.AbortingRefresh;
+ break;
default:
assertUnreachable(pullDebitRec.status);
}
@@ -2330,6 +2479,10 @@ export async function suspendPeerPushCreditTransaction(
// FIXME: Suspend internal withdrawal transaction!
newStatus = PeerPushPaymentIncomingStatus.SuspendedWithdrawing;
break;
+ case PeerPushPaymentIncomingStatus.Aborted:
+ break;
+ case PeerPushPaymentIncomingStatus.Failed:
+ break;
default:
assertUnreachable(pushCreditRec.status);
}
@@ -2348,6 +2501,81 @@ export async function suspendPeerPushCreditTransaction(
notifyTransition(ws, transactionId, transitionInfo);
}
+
+export async function abortPeerPushCreditTransaction(
+ 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:
+ newStatus = PeerPushPaymentIncomingStatus.Aborted;
+ break;
+ case PeerPushPaymentIncomingStatus.Done:
+ break;
+ case PeerPushPaymentIncomingStatus.SuspendedMerge:
+ case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired:
+ case PeerPushPaymentIncomingStatus.SuspendedWithdrawing:
+ newStatus = PeerPushPaymentIncomingStatus.Aborted;
+ break;
+ case PeerPushPaymentIncomingStatus.PendingMergeKycRequired:
+ newStatus = PeerPushPaymentIncomingStatus.Aborted;
+ break;
+ case PeerPushPaymentIncomingStatus.PendingMerge:
+ newStatus = PeerPushPaymentIncomingStatus.Aborted;
+ break;
+ case PeerPushPaymentIncomingStatus.PendingWithdrawing:
+ newStatus = PeerPushPaymentIncomingStatus.Aborted;
+ break;
+ case PeerPushPaymentIncomingStatus.Aborted:
+ break;
+ case PeerPushPaymentIncomingStatus.Failed:
+ 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 cancelAbortingPeerPushCreditTransaction(
+ ws: InternalWalletState,
+ peerPushPaymentIncomingId: string,
+) {
+ // We don't have any "aborting" states!
+ throw Error("can't run cancel-aborting on peer-push-credit transaction");
+}
+
export async function resumePeerPushCreditTransaction(
ws: InternalWalletState,
peerPushPaymentIncomingId: string,
@@ -2388,6 +2616,10 @@ export async function resumePeerPushCreditTransaction(
// FIXME: resume underlying "internal-withdrawal" transaction.
newStatus = PeerPushPaymentIncomingStatus.PendingWithdrawing;
break;
+ case PeerPushPaymentIncomingStatus.Aborted:
+ break;
+ case PeerPushPaymentIncomingStatus.Failed:
+ break;
default:
assertUnreachable(pushCreditRec.status);
}
@@ -2442,11 +2674,135 @@ export async function suspendPeerPullCreditTransaction(
case PeerPullPaymentInitiationStatus.PendingReady:
newStatus = PeerPullPaymentInitiationStatus.SuspendedReady;
break;
+ case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
+ newStatus =
+ PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse;
+ break;
+ case PeerPullPaymentInitiationStatus.DonePurseDeposited:
+ case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
+ case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
+ case PeerPullPaymentInitiationStatus.SuspendedReady:
+ case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
+ case PeerPullPaymentInitiationStatus.Aborted:
+ case PeerPullPaymentInitiationStatus.Failed:
+ case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+ 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 abortPeerPullCreditTransaction(
+ 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:
+ newStatus = PeerPullPaymentInitiationStatus.AbortingDeletePurse;
+ break;
+ case PeerPullPaymentInitiationStatus.PendingWithdrawing:
+ throw Error("can't abort anymore");
+ case PeerPullPaymentInitiationStatus.PendingReady:
+ newStatus = PeerPullPaymentInitiationStatus.AbortingDeletePurse;
+ break;
+ case PeerPullPaymentInitiationStatus.DonePurseDeposited:
+ case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
+ case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
+ case PeerPullPaymentInitiationStatus.SuspendedReady:
+ case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
+ case PeerPullPaymentInitiationStatus.Aborted:
+ case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
+ case PeerPullPaymentInitiationStatus.Failed:
+ case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+ 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 cancelAbortingPeerPullCreditTransaction(
+ 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:
case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
case PeerPullPaymentInitiationStatus.SuspendedReady:
case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
+ case PeerPullPaymentInitiationStatus.Aborted:
+ case PeerPullPaymentInitiationStatus.Failed:
+ break;
+ case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
+ case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+ newStatus = PeerPullPaymentInitiationStatus.Failed;
break;
default:
assertUnreachable(pullCreditRec.status);
@@ -2493,7 +2849,11 @@ export async function resumePeerPullCreditTransaction(
case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
case PeerPullPaymentInitiationStatus.PendingWithdrawing:
case PeerPullPaymentInitiationStatus.PendingReady:
+ case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
case PeerPullPaymentInitiationStatus.DonePurseDeposited:
+ case PeerPullPaymentInitiationStatus.Failed:
+ case PeerPullPaymentInitiationStatus.Aborted:
+ break;
case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
newStatus = PeerPullPaymentInitiationStatus.PendingCreatePurse;
break;
@@ -2506,6 +2866,9 @@ export async function resumePeerPullCreditTransaction(
case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
newStatus = PeerPullPaymentInitiationStatus.PendingWithdrawing;
break;
+ case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+ newStatus = PeerPullPaymentInitiationStatus.AbortingDeletePurse;
+ break;
default:
assertUnreachable(pullCreditRec.status);
}
@@ -2566,6 +2929,7 @@ export async function resumePeerPushDebitTransaction(
case PeerPushPaymentInitiationStatus.PendingReady:
case PeerPushPaymentInitiationStatus.Done:
case PeerPushPaymentInitiationStatus.Aborted:
+ case PeerPushPaymentInitiationStatus.Failed:
// Do nothing
break;
default:
@@ -2630,6 +2994,16 @@ export function computePeerPushCreditTransactionState(
major: TransactionMajorState.Suspended,
minor: TransactionMinorState.Withdraw,
};
+ case PeerPushPaymentIncomingStatus.Aborted:
+ return {
+ major: TransactionMajorState.Aborted
+ };
+ case PeerPushPaymentIncomingStatus.Failed:
+ return {
+ major: TransactionMajorState.Failed,
+ }
+ default:
+ assertUnreachable(pushCreditRecord.status);
}
}
@@ -2681,6 +3055,24 @@ export function computePeerPullCreditTransactionState(
major: TransactionMajorState.Suspended,
minor: TransactionMinorState.MergeKycRequired,
};
+ case PeerPullPaymentInitiationStatus.Aborted:
+ return {
+ major: TransactionMajorState.Aborted,
+ };
+ case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
+ return {
+ major: TransactionMajorState.Aborting,
+ minor: TransactionMinorState.DeletePurse,
+ };
+ case PeerPullPaymentInitiationStatus.Failed:
+ return {
+ major: TransactionMajorState.Failed,
+ };
+ case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+ return {
+ major: TransactionMajorState.Aborting,
+ minor: TransactionMinorState.DeletePurse,
+ };
}
}
@@ -2707,5 +3099,23 @@ export function computePeerPullDebitTransactionState(
major: TransactionMajorState.Suspended,
minor: TransactionMinorState.Deposit,
};
+ case PeerPullDebitRecordStatus.Aborted:
+ return {
+ major: TransactionMajorState.Aborted,
+ };
+ case PeerPullDebitRecordStatus.AbortingRefresh:
+ return {
+ major: TransactionMajorState.Aborting,
+ minor: TransactionMinorState.Refresh,
+ };
+ case PeerPullDebitRecordStatus.Failed:
+ return {
+ major: TransactionMajorState.Failed,
+ };
+ case PeerPullDebitRecordStatus.SuspendedAbortingRefresh:
+ return {
+ major: TransactionMajorState.SuspendedAborting,
+ minor: TransactionMinorState.Refresh,
+ };
}
}
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts
index c46344313..8437d2d0b 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -96,7 +96,7 @@ import {
PendingTaskType,
WalletConfig,
} from "../index.js";
-import { constructTransactionIdentifier } from "./transactions.js";
+import { constructTransactionIdentifier, notifyTransition } from "./transactions.js";
const logger = new Logger("refresh.ts");
@@ -158,7 +158,7 @@ function updateGroupStatus(rg: RefreshGroupRecord): void {
if (allDone) {
if (anyFrozen) {
rg.timestampFinished = TalerPreciseTimestamp.now();
- rg.operationStatus = RefreshOperationStatus.FinishedWithError;
+ rg.operationStatus = RefreshOperationStatus.Failed;
} else {
rg.timestampFinished = TalerPreciseTimestamp.now();
rg.operationStatus = RefreshOperationStatus.Finished;
@@ -1189,7 +1189,7 @@ export function computeRefreshTransactionState(
return {
major: TransactionMajorState.Done,
};
- case RefreshOperationStatus.FinishedWithError:
+ case RefreshOperationStatus.Failed:
return {
major: TransactionMajorState.Failed,
};
@@ -1261,7 +1261,7 @@ export async function resumeRefreshGroup(
tag: TransactionType.Refresh,
refreshGroupId,
});
- let res = await ws.db
+ const transitionInfo = await ws.db
.mktx((x) => [x.refreshGroups])
.runReadWrite(async (tx) => {
const dg = await tx.refreshGroups.get(refreshGroupId);
@@ -1289,19 +1289,57 @@ export async function resumeRefreshGroup(
return undefined;
});
ws.workAvailable.trigger();
- if (res) {
- ws.notify({
- type: NotificationType.TransactionStateTransition,
- transactionId,
- oldTxState: res.oldTxState,
- newTxState: res.newTxState,
- });
- }
+ notifyTransition(ws, transactionId, transitionInfo);
+}
+
+export async function cancelAbortingRefreshGroup(
+ ws: InternalWalletState,
+ refreshGroupId: string,
+): Promise<void> {
+ throw Error("action cancel-aborting not allowed on refreshes");
}
export async function abortRefreshGroup(
ws: InternalWalletState,
refreshGroupId: string,
): Promise<void> {
- throw Error("can't abort refresh groups.");
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.Refresh,
+ refreshGroupId,
+ });
+ const transitionInfo = await ws.db
+ .mktx((x) => [x.refreshGroups])
+ .runReadWrite(async (tx) => {
+ const dg = await tx.refreshGroups.get(refreshGroupId);
+ if (!dg) {
+ logger.warn(
+ `can't resume refresh group, refreshGroupId=${refreshGroupId} not found`,
+ );
+ return;
+ }
+ const oldState = computeRefreshTransactionState(dg);
+ let newStatus: RefreshOperationStatus | undefined;
+ switch (dg.operationStatus) {
+ case RefreshOperationStatus.Finished:
+ break;;
+ case RefreshOperationStatus.Pending:
+ case RefreshOperationStatus.Suspended:
+ newStatus = RefreshOperationStatus.Failed;
+ break;
+ case RefreshOperationStatus.Failed:
+ break;
+ default:
+ assertUnreachable(dg.operationStatus);
+ }
+ if (newStatus) {
+ dg.operationStatus = newStatus;
+ await tx.refreshGroups.put(dg);
+ }
+ return {
+ oldTxState: oldState,
+ newTxState: computeRefreshTransactionState(dg),
+ };
+ });
+ ws.workAvailable.trigger();
+ notifyTransition(ws, transactionId, transitionInfo);
}
diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts
index 70b595c2f..0bee2b406 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -87,21 +87,28 @@ const logger = new Logger("operations/tip.ts");
export function computeTipTransactionStatus(
tipRecord: TipRecord,
): TransactionState {
- if (tipRecord.pickedUpTimestamp) {
- return {
- major: TransactionMajorState.Done,
- };
- }
- if (tipRecord.acceptedTimestamp) {
- return {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.Pickup,
- };
+ switch (tipRecord.status) {
+ case TipRecordStatus.Done:
+ return {
+ major: TransactionMajorState.Done,
+ };
+ case TipRecordStatus.Aborted:
+ return {
+ major: TransactionMajorState.Aborted,
+ };
+ case TipRecordStatus.PendingPickup:
+ return {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.Pickup,
+ };
+ case TipRecordStatus.SuspendidPickup:
+ return {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.User,
+ };
+ default:
+ assertUnreachable(tipRecord.status);
}
- return {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.User,
- };
}
export async function prepareTip(
@@ -445,6 +452,7 @@ export async function suspendTipTransaction(
switch (tipRec.status) {
case TipRecordStatus.Done:
case TipRecordStatus.SuspendidPickup:
+ case TipRecordStatus.Aborted:
break;
case TipRecordStatus.PendingPickup:
newStatus = TipRecordStatus.SuspendidPickup;
@@ -492,11 +500,72 @@ export async function resumeTipTransaction(
let newStatus: TipRecordStatus | undefined = undefined;
switch (tipRec.status) {
case TipRecordStatus.Done:
+ break;
case TipRecordStatus.SuspendidPickup:
newStatus = TipRecordStatus.PendingPickup;
break;
case TipRecordStatus.PendingPickup:
break;
+ case TipRecordStatus.Aborted:
+ 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);
+}
+
+export async function cancelAbortingTipTransaction(
+ ws: InternalWalletState,
+ walletTipId: string,
+): Promise<void> {
+ // We don't have an "aborting" state, so this should never happen!
+ throw Error("can't run cance-aborting on tip transaction");
+}
+
+export async function abortTipTransaction(
+ 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:
+ break;
+ case TipRecordStatus.SuspendidPickup:
+ newStatus = TipRecordStatus.Aborted;
+ break;
+ case TipRecordStatus.PendingPickup:
+ break;
+ case TipRecordStatus.Aborted:
+ break;
default:
assertUnreachable(tipRec.status);
}
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts
index f1cfaed45..d424019ac 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -82,6 +82,7 @@ import {
import { getExchangeDetails } from "./exchanges.js";
import {
abortPayMerchant,
+ cancelAbortingPaymentTransaction,
computePayMerchantTransactionState,
computeRefundTransactionState,
expectProposalDownload,
@@ -90,6 +91,14 @@ import {
suspendPayMerchant,
} from "./pay-merchant.js";
import {
+ abortPeerPullCreditTransaction,
+ abortPeerPullDebitTransaction,
+ abortPeerPushCreditTransaction,
+ abortPeerPushDebitTransaction,
+ cancelAbortingPeerPullCreditTransaction,
+ cancelAbortingPeerPullDebitTransaction,
+ cancelAbortingPeerPushCreditTransaction,
+ cancelAbortingPeerPushDebitTransaction,
computePeerPullCreditTransactionState,
computePeerPullDebitTransactionState,
computePeerPushCreditTransactionState,
@@ -104,11 +113,15 @@ import {
suspendPeerPushDebitTransaction,
} from "./pay-peer.js";
import {
+ abortRefreshGroup,
+ cancelAbortingRefreshGroup,
computeRefreshTransactionState,
resumeRefreshGroup,
suspendRefreshGroup,
} from "./refresh.js";
import {
+ abortTipTransaction,
+ cancelAbortingTipTransaction,
computeTipTransactionStatus,
resumeTipTransaction,
suspendTipTransaction,
@@ -1492,11 +1505,35 @@ export async function cancelAbortingTransaction(
case TransactionType.Deposit:
await cancelAbortingDepositGroup(ws, tx.depositGroupId);
return;
+ case TransactionType.InternalWithdrawal:
case TransactionType.Withdrawal:
await cancelAbortingWithdrawalTransaction(ws, tx.withdrawalGroupId);
return;
+ case TransactionType.Payment:
+ await cancelAbortingPaymentTransaction(ws, tx.proposalId);
+ return;
+ case TransactionType.Refund:
+ throw Error("can't do cancel-aborting on refund transaction");
+ case TransactionType.Tip:
+ await cancelAbortingTipTransaction(ws, tx.walletTipId);
+ return;
+ case TransactionType.Refresh:
+ await cancelAbortingRefreshGroup(ws, tx.refreshGroupId);
+ return;
+ case TransactionType.PeerPullCredit:
+ await cancelAbortingPeerPullCreditTransaction(ws, tx.pursePub);
+ return;
+ case TransactionType.PeerPullDebit:
+ await cancelAbortingPeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId);
+ return;
+ case TransactionType.PeerPushCredit:
+ await cancelAbortingPeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId);
+ return;
+ case TransactionType.PeerPushDebit:
+ await cancelAbortingPeerPushDebitTransaction(ws, tx.pursePub);
+ return;
default:
- logger.warn(`unable to suspend transaction of type '${tx.tag}'`);
+ assertUnreachable(tx);
}
}
@@ -1774,18 +1811,36 @@ export async function abortTransaction(
await abortPayMerchant(ws, txId.proposalId);
break;
}
- case TransactionType.Withdrawal: {
+ case TransactionType.Withdrawal:
+ case TransactionType.InternalWithdrawal: {
await abortWithdrawalTransaction(ws, txId.withdrawalGroupId);
break;
}
case TransactionType.Deposit:
await abortDepositGroup(ws, txId.depositGroupId);
break;
+ case TransactionType.Tip:
+ await abortTipTransaction(ws, txId.walletTipId);
+ break;
+ case TransactionType.Refund:
+ throw Error("can't abort refund transactions");
+ case TransactionType.Refresh:
+ await abortRefreshGroup(ws, txId.refreshGroupId);
+ break;
+ case TransactionType.PeerPullCredit:
+ await abortPeerPullCreditTransaction(ws, txId.pursePub);
+ break;
+ case TransactionType.PeerPullDebit:
+ await abortPeerPullDebitTransaction(ws, txId.peerPullPaymentIncomingId);
+ break;
+ case TransactionType.PeerPushCredit:
+ await abortPeerPushCreditTransaction(ws, txId.peerPushPaymentIncomingId);
+ break;
+ case TransactionType.PeerPushDebit:
+ await abortPeerPushDebitTransaction(ws, txId.pursePub);
+ break;
default: {
- const unknownTxType: any = txId.tag;
- throw Error(
- `can't abort a '${unknownTxType}' transaction: not yet implemented`,
- );
+ assertUnreachable(txId);
}
}
}