From 0323067c0757262084e16a9bba9d58bcd773fc23 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 30 May 2023 09:33:32 +0200 Subject: wallet-core: add missing resume/suspend implementations --- .../src/operations/pay-merchant.ts | 179 +++++++++++++++++---- 1 file changed, 147 insertions(+), 32 deletions(-) (limited to 'packages/taler-wallet-core/src/operations/pay-merchant.ts') 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 { + 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 { + 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 }; -- cgit v1.2.3