taler-typescript-core

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

commit 9c5bfb3f9417c7fb8177285c65ad05909dd46aaa
parent 3616fa1970efdfa103ffa95cb98356729a8ca9c7
Author: Florian Dold <florian@dold.me>
Date:   Thu,  5 Jun 2025 01:46:21 +0200

wallet-core: remove deprecated notifyTransition function

Diffstat:
Mpackages/taler-wallet-core/src/deposits.ts | 101++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mpackages/taler-wallet-core/src/pay-merchant.ts | 505++++++++++++++++++++++++++++---------------------------------------------------
Mpackages/taler-wallet-core/src/transactions.ts | 19-------------------
3 files changed, 236 insertions(+), 389 deletions(-)

diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts @@ -138,7 +138,6 @@ import { applyNotifyTransition, constructTransactionIdentifier, isUnsuccessfulTransaction, - notifyTransition, parseTransactionIdentifier, } from "./transactions.js"; import { WalletExecutionContext, getDenomInfo } from "./wallet.js"; @@ -483,7 +482,7 @@ export class DepositTransactionContext implements TransactionContext { async resumeTransaction(): Promise<void> { const { wex, depositGroupId, transactionId, taskId: retryTag } = this; - const transitionInfo = await wex.db.runReadWriteTx( + await wex.db.runReadWriteTx( { storeNames: ["depositGroups", "transactionsMeta"] }, async (tx) => { const dg = await tx.depositGroups.get(depositGroupId); @@ -538,14 +537,13 @@ export class DepositTransactionContext implements TransactionContext { dg.operationStatus = newOpStatus; await tx.depositGroups.put(dg); await this.updateTransactionMeta(tx); - return { + applyNotifyTransition(tx.notify, transactionId, { oldTxState: oldState, newTxState: computeDepositTransactionStatus(dg), balanceEffect: BalanceEffect.None, - }; + }); }, ); - notifyTransition(wex, transactionId, transitionInfo); wex.taskScheduler.startShepherdTask(retryTag); } @@ -596,15 +594,14 @@ export class DepositTransactionContext implements TransactionContext { dg.failReason = reason; await tx.depositGroups.put(dg); await this.updateTransactionMeta(tx); - return { + applyNotifyTransition(tx.notify, transactionId, { oldTxState: oldState, newTxState: computeDepositTransactionStatus(dg), balanceEffect: BalanceEffect.Any, - }; + }); }, ); wex.taskScheduler.stopShepherdTask(taskId); - notifyTransition(wex, transactionId, transitionInfo); } } @@ -985,7 +982,7 @@ async function waitForRefreshOnDepositGroup( }, }); - const transitionInfo = await wex.db.runReadWriteTx( + const didTransition = await wex.db.runReadWriteTx( { storeNames: ["depositGroups", "refreshGroups", "transactionsMeta"] }, async (tx) => { const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId); @@ -1004,23 +1001,26 @@ async function waitForRefreshOnDepositGroup( break; } default: - return undefined; + return false; } const newDg = await tx.depositGroups.get(depositGroup.depositGroupId); if (!newDg) { - return; + return false; } const oldTxState = computeDepositTransactionStatus(newDg); newDg.operationStatus = newOpState; const newTxState = computeDepositTransactionStatus(newDg); await tx.depositGroups.put(newDg); await ctx.updateTransactionMeta(tx); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; + applyNotifyTransition(tx.notify, ctx.transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, + }); + return true; }, ); - - notifyTransition(wex, ctx.transactionId, transitionInfo); - if (transitionInfo) { + if (didTransition) { return TaskRunResult.progress(); } return TaskRunResult.backoff(); @@ -1118,10 +1118,13 @@ async function processDepositGroupPendingKyc( await tx.depositGroups.put(newDg); await ctx.updateTransactionMeta(tx); const newTxState = computeDepositTransactionStatus(newDg); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; + applyNotifyTransition(tx.notify, ctx.transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, + }); }, ); - notifyTransition(wex, ctx.transactionId, transitionInfo); } else if (kycStatusRes.status === HttpStatusCode.Accepted) { const statusResp = await readResponseJsonOrThrow( kycStatusRes, @@ -1202,12 +1205,12 @@ async function processDepositGroupPendingKycAuth( async function transitionKycAuthSuccess( ctx: DepositTransactionContext, ): Promise<TaskRunResult> { - const transitionInfo = await ctx.wex.db.runReadWriteTx( + const didTransition = await ctx.wex.db.runReadWriteTx( { storeNames: ["depositGroups", "transactionsMeta"] }, async (tx) => { const newDg = await tx.depositGroups.get(ctx.depositGroupId); if (!newDg) { - return; + return false; } const oldTxState = computeDepositTransactionStatus(newDg); switch (newDg.operationStatus) { @@ -1215,16 +1218,20 @@ async function transitionKycAuthSuccess( newDg.operationStatus = DepositOperationStatus.PendingDeposit; break; default: - return; + return false; } await tx.depositGroups.put(newDg); await ctx.updateTransactionMeta(tx); const newTxState = computeDepositTransactionStatus(newDg); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.None }; + applyNotifyTransition(tx.notify, ctx.transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.None, + }); + return true; }, ); - notifyTransition(ctx.wex, ctx.transactionId, transitionInfo); - if (transitionInfo) { + if (didTransition) { return TaskRunResult.progress(); } else { return TaskRunResult.backoff(); @@ -1343,10 +1350,13 @@ async function transitionToKycRequired( await tx.depositGroups.put(dg); await ctx.updateTransactionMeta(tx); const newTxState = computeDepositTransactionStatus(dg); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; + applyNotifyTransition(tx.notify, ctx.transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, + }); }, ); - notifyTransition(wex, ctx.transactionId, transitionInfo); return TaskRunResult.progress(); } default: { @@ -1394,10 +1404,13 @@ async function transitionToKycAuthRequired( await tx.depositGroups.put(dg); await ctx.updateTransactionMeta(tx); const newTxState = computeDepositTransactionStatus(dg); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Flags }; + applyNotifyTransition(tx.notify, ctx.transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Flags, + }); }, ); - notifyTransition(wex, ctx.transactionId, transitionInfo); return TaskRunResult.progress(); } @@ -1560,10 +1573,13 @@ async function processDepositGroupTrack( await ctx.updateTransactionMeta(tx); } const newTxState = computeDepositTransactionStatus(dg); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; + applyNotifyTransition(tx.notify, ctx.transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, + }); }, ); - notifyTransition(wex, ctx.transactionId, transitionInfo); if (allWired) { return TaskRunResult.finished(); } else { @@ -1833,7 +1849,7 @@ async function processDepositGroupPendingDeposit( ); } - const transitionInfo = await wex.db.runReadWriteTx( + await wex.db.runReadWriteTx( { storeNames: ["depositGroups", "transactionsMeta"] }, async (tx) => { const dg = await tx.depositGroups.get(depositGroupId); @@ -1845,11 +1861,13 @@ async function processDepositGroupPendingDeposit( await tx.depositGroups.put(dg); await ctx.updateTransactionMeta(tx); const newTxState = computeDepositTransactionStatus(dg); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.None }; + applyNotifyTransition(tx.notify, ctx.transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.None, + }); }, ); - - notifyTransition(wex, ctx.transactionId, transitionInfo); return TaskRunResult.progress(); } @@ -2382,18 +2400,17 @@ export async function createDepositGroup( h: contractTermsHash, }); await ctx.updateTransactionMeta(tx); - return computeDepositTransactionStatus(depositGroup); + const oldTxState = { major: TransactionMajorState.None }; + const newTxState = computeDepositTransactionStatus(depositGroup); + applyNotifyTransition(tx.notify, transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, + }); + return newTxState; }, ); - notifyTransition(wex, transactionId, { - oldTxState: { - major: TransactionMajorState.None, - }, - newTxState, - balanceEffect: BalanceEffect.Any, - }); - wex.taskScheduler.startShepherdTask(ctx.taskId); return { diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts @@ -172,12 +172,11 @@ import { SelectPayTokensResult, } from "./tokenSelection.js"; import { + applyNotifyTransition, BalanceEffect, constructTransactionIdentifier, isUnsuccessfulTransaction, - notifyTransition, parseTransactionIdentifier, - TransitionInfo, } from "./transactions.js"; import { EXCHANGE_COINS_LOCK, @@ -382,7 +381,7 @@ export class PayMerchantTransactionContext implements TransactionContext { ): Promise<void> { const ws = this.wex; const extraStores = opts.extraStores ?? []; - const transitionInfo = await ws.db.runReadWriteTx( + await ws.db.runReadWriteTx( { storeNames: ["purchases", "transactionsMeta", ...extraStores] }, async (tx) => { const purchaseRec = await tx.purchases.get(this.proposalId); @@ -396,30 +395,31 @@ export class PayMerchantTransactionContext implements TransactionContext { await tx.purchases.put(purchaseRec); await this.updateTransactionMeta(tx); const newTxState = computePayMerchantTransactionState(purchaseRec); - return { + applyNotifyTransition(tx.notify, this.transactionId, { oldTxState, newTxState, // FIXME: The transition function should really return the effect // and not just the status. balanceEffect: BalanceEffect.Any, - }; + }); + return; } case TransitionResultType.Delete: await tx.purchases.delete(this.proposalId); await this.updateTransactionMeta(tx); - return { + applyNotifyTransition(tx.notify, this.transactionId, { oldTxState, newTxState: { major: TransactionMajorState.None, }, balanceEffect: BalanceEffect.Any, - }; + }); + return; default: return undefined; } }, ); - notifyTransition(ws, this.transactionId, transitionInfo); } async deleteTransaction(): Promise<void> { @@ -496,7 +496,7 @@ export class PayMerchantTransactionContext implements TransactionContext { async suspendTransaction(): Promise<void> { const { wex, proposalId, transactionId } = this; wex.taskScheduler.stopShepherdTask(this.taskId); - const transitionInfo = await wex.db.runReadWriteTx( + await wex.db.runReadWriteTx( { storeNames: ["purchases", "transactionsMeta"] }, async (tx) => { const purchase = await tx.purchases.get(proposalId); @@ -506,15 +506,18 @@ export class PayMerchantTransactionContext implements TransactionContext { const oldTxState = computePayMerchantTransactionState(purchase); let newStatus = transitionSuspend[purchase.purchaseStatus]; if (!newStatus) { - return undefined; + return; } await tx.purchases.put(purchase); await this.updateTransactionMeta(tx); const newTxState = computePayMerchantTransactionState(purchase); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.None }; + applyNotifyTransition(tx.notify, transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.None, + }); }, ); - notifyTransition(wex, transactionId, transitionInfo); } async abortTransaction(reason?: TalerErrorDetail): Promise<void> { @@ -590,16 +593,19 @@ export class PayMerchantTransactionContext implements TransactionContext { await tx.purchases.put(purchase); await this.updateTransactionMeta(tx); const newTxState = computePayMerchantTransactionState(purchase); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; + applyNotifyTransition(tx.notify, transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, + }); }, ); wex.taskScheduler.stopShepherdTask(this.taskId); - notifyTransition(wex, transactionId, transitionInfo); wex.taskScheduler.startShepherdTask(this.taskId); } async resumeTransaction(): Promise<void> { - const { wex, proposalId, transactionId, taskId: retryTag } = this; + const { wex, proposalId, transactionId } = this; const transitionInfo = await wex.db.runReadWriteTx( { storeNames: ["purchases", "transactionsMeta"] }, async (tx) => { @@ -615,10 +621,13 @@ export class PayMerchantTransactionContext implements TransactionContext { await tx.purchases.put(purchase); await this.updateTransactionMeta(tx); const newTxState = computePayMerchantTransactionState(purchase); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; + applyNotifyTransition(tx.notify, transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, + }); }, ); - notifyTransition(wex, transactionId, transitionInfo); wex.taskScheduler.startShepherdTask(this.taskId); } @@ -655,10 +664,13 @@ export class PayMerchantTransactionContext implements TransactionContext { } await this.updateTransactionMeta(tx); const newTxState = computePayMerchantTransactionState(purchase); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; + applyNotifyTransition(tx.notify, transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, + }); }, ); - notifyTransition(wex, transactionId, transitionInfo); wex.taskScheduler.stopShepherdTask(this.taskId); } } @@ -894,7 +906,7 @@ async function failProposalPermanently( err: TalerErrorDetail, ): Promise<void> { const ctx = new PayMerchantTransactionContext(wex, proposalId); - const transitionInfo = await wex.db.runReadWriteTx( + await wex.db.runReadWriteTx( { storeNames: ["purchases", "transactionsMeta"] }, async (tx) => { const p = await tx.purchases.get(proposalId); @@ -907,10 +919,13 @@ async function failProposalPermanently( const newTxState = computePayMerchantTransactionState(p); await tx.purchases.put(p); await ctx.updateTransactionMeta(tx); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; + applyNotifyTransition(tx.notify, ctx.transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, + }); }, ); - notifyTransition(wex, ctx.transactionId, transitionInfo); } function getPayRequestTimeout(purchase: PurchaseRecord): Duration { @@ -1174,7 +1189,7 @@ async function processDownloadProposal( logger.trace(`extracted contract data: ${j2s(contractData)}`); - const transitionInfo = await wex.db.runReadWriteTx( + await wex.db.runReadWriteTx( { storeNames: ["purchases", "contractTerms", "transactionsMeta"] }, async (tx) => { const p = await tx.purchases.get(proposalId); @@ -1243,16 +1258,14 @@ async function processDownloadProposal( } await ctx.updateTransactionMeta(tx); const newTxState = computePayMerchantTransactionState(p); - return { + applyNotifyTransition(tx.notify, transactionId, { oldTxState, newTxState, balanceEffect: BalanceEffect.None, - } satisfies TransitionInfo; + }); }, ); - notifyTransition(wex, transactionId, transitionInfo); - return TaskRunResult.progress(); } @@ -1390,33 +1403,20 @@ async function createOrReusePurchase( // if this transaction was shared and the order is paid then it // means that another wallet already paid the proposal if (paid) { - const transitionInfo = await wex.db.runReadWriteTx( - { storeNames: ["purchases", "transactionsMeta"] }, - async (tx) => { - const p = await tx.purchases.get(oldProposal.proposalId); - if (!p) { - logger.warn("purchase does not exist anymore"); - return; - } - // The order is only paid by another wallet - // if the merchant says it's paid but the local - // wallet is still in a dialog state. - switch (p.purchaseStatus) { - case PurchaseStatus.DialogProposed: - case PurchaseStatus.DialogShared: - break; - default: - return undefined; - } - const oldTxState = computePayMerchantTransactionState(p); - p.purchaseStatus = PurchaseStatus.FailedPaidByOther; - const newTxState = computePayMerchantTransactionState(p); - await tx.purchases.put(p); - await oldCtx.updateTransactionMeta(tx); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; - }, - ); - notifyTransition(wex, oldCtx.transactionId, transitionInfo); + await oldCtx.transition(async (p) => { + // The order is only paid by another wallet + // if the merchant says it's paid but the local + // wallet is still in a dialog state. + switch (p.purchaseStatus) { + case PurchaseStatus.DialogProposed: + case PurchaseStatus.DialogShared: + break; + default: + return TransitionResultType.Stay; + } + p.purchaseStatus = PurchaseStatus.FailedPaidByOther; + return TransitionResultType.Transition; + }); } } return { @@ -1475,24 +1475,23 @@ async function createOrReusePurchase( const ctx = new PayMerchantTransactionContext(wex, proposalId); - const transitionInfo = await wex.db.runReadWriteTx( + await wex.db.runReadWriteTx( { storeNames: ["purchases", "transactionsMeta"] }, - async (tx): Promise<TransitionInfo> => { + async (tx) => { await tx.purchases.put(proposalRecord); await ctx.updateTransactionMeta(tx); const oldTxState: TransactionState = { major: TransactionMajorState.None, }; const newTxState = computePayMerchantTransactionState(proposalRecord); - return { + applyNotifyTransition(tx.notify, ctx.transactionId, { oldTxState, newTxState, balanceEffect: BalanceEffect.None, - }; + }); }, ); - notifyTransition(wex, ctx.transactionId, transitionInfo); return { proposalId, transactionId: ctx.transactionId, @@ -1507,21 +1506,16 @@ async function storeFirstPaySuccess( ): Promise<void> { const ctx = new PayMerchantTransactionContext(wex, proposalId); const now = AbsoluteTime.toPreciseTimestamp(AbsoluteTime.now()); - const transitionInfo = await wex.db.runReadWriteTx( - { storeNames: ["contractTerms", "purchases", "transactionsMeta"] }, - async (tx) => { - const purchase = await tx.purchases.get(proposalId); - - if (!purchase) { - logger.warn("purchase does not exist anymore"); - return; - } + await ctx.transitionExtra( + { + extraStores: ["contractTerms"], + }, + async (purchase, tx) => { const isFirst = purchase.timestampFirstSuccessfulPay === undefined; if (!isFirst) { logger.warn("payment success already stored"); - return; + return TransitionResultType.Stay; } - const oldTxState = computePayMerchantTransactionState(purchase); if (purchase.purchaseStatus === PurchaseStatus.PendingPaying) { purchase.purchaseStatus = PurchaseStatus.Done; } @@ -1557,17 +1551,9 @@ async function storeFirstPaySuccess( ), ); } - await tx.purchases.put(purchase); - await ctx.updateTransactionMeta(tx); - const newTxState = computePayMerchantTransactionState(purchase); - return { - oldTxState, - newTxState, - balanceEffect: BalanceEffect.Any, - }; + return TransitionResultType.Transition; }, ); - notifyTransition(wex, ctx.transactionId, transitionInfo); } async function storePayReplaySuccess( @@ -1576,34 +1562,20 @@ async function storePayReplaySuccess( sessionId: string | undefined, ): Promise<void> { const ctx = new PayMerchantTransactionContext(wex, proposalId); - const transitionInfo = await wex.db.runReadWriteTx( - { storeNames: ["purchases", "transactionsMeta"] }, - async (tx) => { - const purchase = await tx.purchases.get(proposalId); - - if (!purchase) { - logger.warn("purchase does not exist anymore"); - return; - } - const isFirst = purchase.timestampFirstSuccessfulPay === undefined; - if (isFirst) { - throw Error("invalid payment state"); - } - const oldTxState = computePayMerchantTransactionState(purchase); - if ( - purchase.purchaseStatus === PurchaseStatus.PendingPaying || - purchase.purchaseStatus === PurchaseStatus.PendingPayingReplay - ) { + await ctx.transition(async (purchase) => { + const isFirst = purchase.timestampFirstSuccessfulPay === undefined; + if (isFirst) { + throw Error("invalid payment state"); + } + switch (purchase.purchaseStatus) { + case PurchaseStatus.PendingPaying: + case PurchaseStatus.PendingPayingReplay: purchase.purchaseStatus = PurchaseStatus.Done; - } - purchase.lastSessionId = sessionId; - await tx.purchases.put(purchase); - await ctx.updateTransactionMeta(tx); - const newTxState = computePayMerchantTransactionState(purchase); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; - }, - ); - notifyTransition(wex, ctx.transactionId, transitionInfo); + break; + } + purchase.lastSessionId = sessionId; + return TransitionResultType.Transition; + }); } /** @@ -1996,23 +1968,12 @@ async function checkPaymentByProposalId( "automatically re-submitting payment with different session ID", ); logger.trace(`last: ${purchase.lastSessionId}, current: ${sessionId}`); - const transitionInfo = await wex.db.runReadWriteTx( - { storeNames: ["purchases", "transactionsMeta"] }, - async (tx) => { - const p = await tx.purchases.get(proposalId); - if (!p) { - return; - } - const oldTxState = computePayMerchantTransactionState(p); - p.lastSessionId = sessionId; - p.purchaseStatus = PurchaseStatus.PendingPayingReplay; - await tx.purchases.put(p); - await ctx.updateTransactionMeta(tx); - const newTxState = computePayMerchantTransactionState(p); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; - }, - ); - notifyTransition(wex, transactionId, transitionInfo); + await ctx.transition(async (p) => { + p.lastSessionId = sessionId; + p.purchaseStatus = PurchaseStatus.PendingPayingReplay; + return TransitionResultType.Transition; + }); + wex.taskScheduler.startShepherdTask(ctx.taskId); // FIXME: Consider changing the API here so that we don't have to @@ -2933,7 +2894,11 @@ export async function confirmPay( break; } const newTxState = computePayMerchantTransactionState(p); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; + applyNotifyTransition(tx.notify, transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, + }); }, ); @@ -2955,8 +2920,6 @@ export async function confirmPay( } } - notifyTransition(wex, transactionId, transitionInfo); - // In case we're sharing the payment and we're long-polling wex.taskScheduler.stopShepherdTask(ctx.taskId); @@ -3091,29 +3054,11 @@ async function processPurchasePay( ); if (paid) { - const transitionInfo = await wex.db.runReadWriteTx( - { storeNames: ["purchases", "transactionsMeta"] }, - async (tx) => { - const p = await tx.purchases.get(purchase.proposalId); - if (!p) { - logger.warn("purchase does not exist anymore"); - return; - } - const oldTxState = computePayMerchantTransactionState(p); - p.purchaseStatus = PurchaseStatus.FailedPaidByOther; - const newTxState = computePayMerchantTransactionState(p); - await tx.purchases.put(p); - await ctx.updateTransactionMeta(tx); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; - }, - ); - const transactionId = constructTransactionIdentifier({ - tag: TransactionType.Payment, - proposalId, + await ctx.transition(async (p) => { + p.purchaseStatus = PurchaseStatus.FailedPaidByOther; + return TransitionResultType.Transition; }); - notifyTransition(wex, transactionId, transitionInfo); - return { type: TaskRunResultType.Error, errorDetail: makeErrorDetail(TalerErrorCode.WALLET_ORDER_ALREADY_PAID, { @@ -3592,30 +3537,17 @@ export async function refuseProposal( proposalId: string, ): Promise<void> { const ctx = new PayMerchantTransactionContext(wex, proposalId); - const transitionInfo = await wex.db.runReadWriteTx( - { storeNames: ["purchases", "transactionsMeta"] }, - async (tx) => { - const proposal = await tx.purchases.get(proposalId); - if (!proposal) { - logger.trace(`proposal ${proposalId} not found, won't refuse proposal`); - return undefined; - } - if ( - proposal.purchaseStatus !== PurchaseStatus.DialogProposed && - proposal.purchaseStatus !== PurchaseStatus.DialogShared - ) { - return undefined; - } - const oldTxState = computePayMerchantTransactionState(proposal); - proposal.purchaseStatus = PurchaseStatus.AbortedProposalRefused; - const newTxState = computePayMerchantTransactionState(proposal); - await tx.purchases.put(proposal); - await ctx.updateTransactionMeta(tx); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; - }, - ); - - notifyTransition(wex, ctx.transactionId, transitionInfo); + await ctx.transition(async (proposal) => { + switch (proposal.purchaseStatus) { + case PurchaseStatus.DialogProposed: + case PurchaseStatus.DialogShared: + break; + default: + return TransitionResultType.Stay; + } + proposal.purchaseStatus = PurchaseStatus.AbortedProposalRefused; + return TransitionResultType.Transition; + }); } const transitionSuspend: { @@ -3966,16 +3898,17 @@ export async function sharePayment( const newTxState = computePayMerchantTransactionState(p); + applyNotifyTransition(tx.notify, ctx.transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, + }); + return { proposalId: p.proposalId, nonce: p.noncePriv, session: p.lastSessionId ?? p.downloadSessionId, token: p.claimToken, - transitionInfo: { - oldTxState, - newTxState, - balanceEffect: BalanceEffect.Any, - }, }; }, ); @@ -3984,8 +3917,6 @@ export async function sharePayment( throw Error("This purchase can't be shared"); } - notifyTransition(wex, ctx.transactionId, result.transitionInfo); - // schedule a task to watch for the status wex.taskScheduler.startShepherdTask(ctx.taskId); @@ -4050,29 +3981,13 @@ async function processPurchaseDialogShared( download.contractData, true, ); + if (paid) { - const transitionInfo = await wex.db.runReadWriteTx( - { storeNames: ["purchases", "transactionsMeta"] }, - async (tx) => { - const p = await tx.purchases.get(purchase.proposalId); - if (!p) { - logger.warn("purchase does not exist anymore"); - return; - } - const oldTxState = computePayMerchantTransactionState(p); - p.purchaseStatus = PurchaseStatus.FailedPaidByOther; - const newTxState = computePayMerchantTransactionState(p); - await tx.purchases.put(p); - await ctx.updateTransactionMeta(tx); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; - }, - ); - const transactionId = constructTransactionIdentifier({ - tag: TransactionType.Payment, - proposalId, + await ctx.transition(async (p) => { + p.purchaseStatus = PurchaseStatus.FailedPaidByOther; + return TransitionResultType.Transition; }); - - notifyTransition(wex, transactionId, transitionInfo); + return TaskRunResult.progress(); } return TaskRunResult.backoff(); @@ -4130,32 +4045,19 @@ async function processPurchaseAutoRefund( // is over or the product is already fully refunded. if (noAutoRefundOrExpired || fullyRefunded) { - const transitionInfo = await wex.db.runReadWriteTx( - { storeNames: ["purchases", "transactionsMeta"] }, - async (tx) => { - const p = await tx.purchases.get(purchase.proposalId); - if (!p) { - logger.warn("purchase does not exist anymore"); - return; - } - switch (p.purchaseStatus) { - case PurchaseStatus.PendingQueryingAutoRefund: - case PurchaseStatus.FinalizingQueryingAutoRefund: - break; - default: - return; - } - const oldTxState = computePayMerchantTransactionState(p); - p.purchaseStatus = PurchaseStatus.Done; - p.refundAmountAwaiting = undefined; - const newTxState = computePayMerchantTransactionState(p); - await tx.purchases.put(p); - await ctx.updateTransactionMeta(tx); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; - }, - ); - notifyTransition(wex, ctx.transactionId, transitionInfo); - return TaskRunResult.finished(); + await ctx.transition(async (p) => { + switch (p.purchaseStatus) { + case PurchaseStatus.PendingQueryingAutoRefund: + case PurchaseStatus.FinalizingQueryingAutoRefund: + break; + default: + return TransitionResultType.Stay; + } + p.purchaseStatus = PurchaseStatus.Done; + p.refundAmountAwaiting = undefined; + return TransitionResultType.Transition; + }); + return TaskRunResult.progress(); } const requestUrl = new URL( @@ -4178,35 +4080,22 @@ async function processPurchaseAutoRefund( codecForMerchantOrderStatusPaid(), ); - if (orderStatus.refund_pending) { - const transitionInfo = await wex.db.runReadWriteTx( - { storeNames: ["purchases", "transactionsMeta"] }, - async (tx) => { - const p = await tx.purchases.get(purchase.proposalId); - if (!p) { - logger.warn("purchase does not exist anymore"); - return; - } - switch (p.purchaseStatus) { - case PurchaseStatus.PendingQueryingAutoRefund: - case PurchaseStatus.FinalizingQueryingAutoRefund: - break; - default: - return; - } - const oldTxState = computePayMerchantTransactionState(p); - p.purchaseStatus = PurchaseStatus.PendingAcceptRefund; - const newTxState = computePayMerchantTransactionState(p); - await tx.purchases.put(p); - await ctx.updateTransactionMeta(tx); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; - }, - ); - notifyTransition(wex, ctx.transactionId, transitionInfo); - return TaskRunResult.progress(); + if (!orderStatus.refund_pending) { + return TaskRunResult.longpollReturnedPending(); } - return TaskRunResult.longpollReturnedPending(); + await ctx.transition(async (p) => { + switch (p.purchaseStatus) { + case PurchaseStatus.PendingQueryingAutoRefund: + case PurchaseStatus.FinalizingQueryingAutoRefund: + break; + default: + return TransitionResultType.Stay; + } + p.purchaseStatus = PurchaseStatus.PendingAcceptRefund; + return TransitionResultType.Transition; + }); + return TaskRunResult.progress(); } async function processPurchaseAbortingRefund( @@ -4337,57 +4226,30 @@ async function processPurchaseQueryRefund( const ctx = new PayMerchantTransactionContext(wex, proposalId); if (!orderStatus.refund_pending) { - const transitionInfo = await wex.db.runReadWriteTx( - { storeNames: ["purchases", "transactionsMeta"] }, - async (tx) => { - const p = await tx.purchases.get(purchase.proposalId); - if (!p) { - logger.warn("purchase does not exist anymore"); - return undefined; - } - if (p.purchaseStatus !== PurchaseStatus.PendingQueryingRefund) { - return undefined; - } - const oldTxState = computePayMerchantTransactionState(p); - p.purchaseStatus = PurchaseStatus.Done; - p.refundAmountAwaiting = undefined; - const newTxState = computePayMerchantTransactionState(p); - await tx.purchases.put(p); - await ctx.updateTransactionMeta(tx); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; - }, - ); - notifyTransition(wex, ctx.transactionId, transitionInfo); - return TaskRunResult.progress(); + await ctx.transition(async (p) => { + if (p.purchaseStatus !== PurchaseStatus.PendingQueryingRefund) { + return TransitionResultType.Stay; + } + p.purchaseStatus = PurchaseStatus.Done; + p.refundAmountAwaiting = undefined; + return TransitionResultType.Transition; + }); } else { const refundAwaiting = Amounts.sub( Amounts.parseOrThrow(orderStatus.refund_amount), Amounts.parseOrThrow(orderStatus.refund_taken), ).amount; - const transitionInfo = await wex.db.runReadWriteTx( - { storeNames: ["purchases", "transactionsMeta"] }, - async (tx) => { - const p = await tx.purchases.get(purchase.proposalId); - if (!p) { - logger.warn("purchase does not exist anymore"); - return; - } - if (p.purchaseStatus !== PurchaseStatus.PendingQueryingRefund) { - return; - } - const oldTxState = computePayMerchantTransactionState(p); - p.refundAmountAwaiting = Amounts.stringify(refundAwaiting); - p.purchaseStatus = PurchaseStatus.PendingAcceptRefund; - const newTxState = computePayMerchantTransactionState(p); - await tx.purchases.put(p); - await ctx.updateTransactionMeta(tx); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; - }, - ); - notifyTransition(wex, ctx.transactionId, transitionInfo); - return TaskRunResult.progress(); + await ctx.transition(async (p) => { + if (p.purchaseStatus !== PurchaseStatus.PendingQueryingRefund) { + return TransitionResultType.Stay; + } + p.refundAmountAwaiting = Amounts.stringify(refundAwaiting); + p.purchaseStatus = PurchaseStatus.PendingAcceptRefund; + return TransitionResultType.Transition; + }); } + return TaskRunResult.progress(); } async function processPurchaseAcceptRefund( @@ -4480,10 +4342,13 @@ export async function startQueryRefund( const newTxState = computePayMerchantTransactionState(p); await tx.purchases.put(p); await ctx.updateTransactionMeta(tx); - return { oldTxState, newTxState, balanceEffect: BalanceEffect.Any }; + applyNotifyTransition(tx.notify, ctx.transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, + }); }, ); - notifyTransition(wex, ctx.transactionId, transitionInfo); wex.taskScheduler.startShepherdTask(ctx.taskId); } @@ -4564,11 +4429,6 @@ async function storeRefunds( const currency = Amounts.currencyOf(amountRaw); - const transitions: { - transactionId: string; - transitionInfo: TransitionInfo; - }[] = []; - const result = await wex.db.runReadWriteTx( { storeNames: [ @@ -4691,13 +4551,10 @@ async function storeRefunds( ); await tx.refundGroups.put(newGroup); await refundCtx.updateTransactionMeta(tx); - transitions.push({ - transactionId: refundCtx.transactionId, - transitionInfo: { - oldTxState: { major: TransactionMajorState.None }, - newTxState: computeRefundTransactionState(newGroup), - balanceEffect: BalanceEffect.Any, - }, + applyNotifyTransition(tx.notify, refundCtx.transactionId, { + oldTxState: { major: TransactionMajorState.None }, + newTxState: computeRefundTransactionState(newGroup), + balanceEffect: BalanceEffect.Any, }); } @@ -4748,13 +4605,10 @@ async function storeRefunds( const refreshCoins = await computeRefreshRequest(wex, tx, items); const newTxState: TransactionState = computeRefundTransactionState(refundGroup); - transitions.push({ - transactionId: refundCtx.transactionId, - transitionInfo: { - oldTxState, - newTxState, - balanceEffect: BalanceEffect.Any, - }, + applyNotifyTransition(tx.notify, refundCtx.transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, }); await createRefreshGroup( wex, @@ -4797,13 +4651,14 @@ async function storeRefunds( await ctx.updateTransactionMeta(tx); const newTxState = computePayMerchantTransactionState(myPurchase); + applyNotifyTransition(tx.notify, ctx.transactionId, { + oldTxState, + newTxState, + balanceEffect: BalanceEffect.Any, + }); + return { numPendingItemsTotal, - transitionInfo: { - oldTxState, - newTxState, - balanceEffect: BalanceEffect.Any, - }, }; }, ); @@ -4812,12 +4667,6 @@ async function storeRefunds( return TaskRunResult.finished(); } - for (const trs of transitions) { - notifyTransition(wex, trs.transactionId, trs.transitionInfo); - } - - notifyTransition(wex, ctx.transactionId, result.transitionInfo); - if (result.numPendingItemsTotal > 0) { return TaskRunResult.backoff(); } else { diff --git a/packages/taler-wallet-core/src/transactions.ts b/packages/taler-wallet-core/src/transactions.ts @@ -991,22 +991,3 @@ export function applyNotifyTransition( ); } } - -/** - * Notify of a state transition if necessary. - * - * @deprecated use applyNotifyTransition inside transaction - */ -export function notifyTransition( - wex: WalletExecutionContext, - transactionId: string, - transitionInfo: TransitionInfo | undefined, - experimentalUserData: any = undefined, -): void { - applyNotifyTransition( - (wn: WalletNotification) => wex.ws.notify(wn), - transactionId, - transitionInfo, - experimentalUserData, - ); -}