From 8ac5080607d28f8dcd84a949221a551a3f66cea8 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Sat, 8 Oct 2022 23:45:49 +0200 Subject: wallet-core: more DB and refund fixes --- packages/taler-wallet-core/src/db.ts | 6 +- .../src/operations/backup/export.ts | 16 +-- .../src/operations/backup/import.ts | 8 +- .../src/operations/pay-merchant.ts | 117 +++++++++++---------- .../taler-wallet-core/src/operations/pending.ts | 5 +- .../src/operations/transactions.ts | 6 +- 6 files changed, 80 insertions(+), 78 deletions(-) (limited to 'packages/taler-wallet-core/src') diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index b13428183..b019be67a 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -1026,7 +1026,7 @@ export interface WalletContractData { deliveryLocation: Location | undefined; } -export enum ProposalStatus { +export enum PurchaseStatus { /** * Not downloaded yet. */ @@ -1150,7 +1150,7 @@ export interface PurchaseRecord { */ repurchaseProposalId: string | undefined; - status: ProposalStatus; + purchaseStatus: PurchaseStatus; /** * Private key for the nonce. @@ -1934,7 +1934,7 @@ export const WalletStoresV1 = { "purchases", describeContents({ keyPath: "proposalId" }), { - byStatus: describeIndex("byStatus", "operationStatus"), + byStatus: describeIndex("byStatus", "purchaseStatus"), byFulfillmentUrl: describeIndex( "byFulfillmentUrl", "download.contractData.fulfillmentUrl", diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts b/packages/taler-wallet-core/src/operations/backup/export.ts index 04fac7d38..d16b344f6 100644 --- a/packages/taler-wallet-core/src/operations/backup/export.ts +++ b/packages/taler-wallet-core/src/operations/backup/export.ts @@ -65,7 +65,7 @@ import { CoinSourceType, CoinStatus, DenominationRecord, - ProposalStatus, + PurchaseStatus, RefreshCoinStatus, RefundState, WALLET_BACKUP_STATE_KEY, @@ -382,21 +382,21 @@ export async function exportBackup( } let propStatus: BackupProposalStatus; - switch (purch.status) { - case ProposalStatus.Paid: + switch (purch.purchaseStatus) { + case PurchaseStatus.Paid: propStatus = BackupProposalStatus.Paid; return; - case ProposalStatus.DownloadingProposal: - case ProposalStatus.Proposed: + case PurchaseStatus.DownloadingProposal: + case PurchaseStatus.Proposed: propStatus = BackupProposalStatus.Proposed; break; - case ProposalStatus.ProposalDownloadFailed: + case PurchaseStatus.ProposalDownloadFailed: propStatus = BackupProposalStatus.PermanentlyFailed; break; - case ProposalStatus.ProposalRefused: + case PurchaseStatus.ProposalRefused: propStatus = BackupProposalStatus.Refused; break; - case ProposalStatus.RepurchaseDetected: + case PurchaseStatus.RepurchaseDetected: propStatus = BackupProposalStatus.Repurchase; break; default: diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index 00dbf6fa8..bb5fe56e2 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -46,7 +46,7 @@ import { DenomSelectionState, OperationStatus, ProposalDownload, - ProposalStatus, + PurchaseStatus, PurchasePayInfo, RefreshCoinStatus, RefreshSessionRecord, @@ -564,10 +564,10 @@ export async function importBackup( const existingPurchase = await tx.purchases.get( backupPurchase.proposal_id, ); - let proposalStatus: ProposalStatus; + let proposalStatus: PurchaseStatus; switch (backupPurchase.proposal_status) { case BackupProposalStatus.Paid: - proposalStatus = ProposalStatus.Paid; + proposalStatus = PurchaseStatus.Paid; break; default: throw Error(); @@ -703,7 +703,7 @@ export async function importBackup( payInfo, refundAmountAwaiting: undefined, repurchaseProposalId: backupPurchase.repurchase_proposal_id, - status: proposalStatus, + purchaseStatus: proposalStatus, timestamp: backupPurchase.timestamp_proposed, }); } diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts b/packages/taler-wallet-core/src/operations/pay-merchant.ts index 97901c71e..d590177c2 100644 --- a/packages/taler-wallet-core/src/operations/pay-merchant.ts +++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts @@ -81,7 +81,7 @@ import { CoinStatus, DenominationRecord, ProposalDownload, - ProposalStatus, + PurchaseStatus, PurchaseRecord, RefundReason, RefundState, @@ -230,7 +230,7 @@ async function failProposalPermanently( if (!p) { return; } - p.status = ProposalStatus.ProposalDownloadFailed; + p.purchaseStatus = PurchaseStatus.ProposalDownloadFailed; await tx.purchases.put(p); }); } @@ -329,7 +329,7 @@ export async function processDownloadProposal( }; } - if (proposal.status != ProposalStatus.DownloadingProposal) { + if (proposal.purchaseStatus != PurchaseStatus.DownloadingProposal) { return { type: OperationAttemptResultType.Finished, result: undefined, @@ -500,7 +500,7 @@ export async function processDownloadProposal( if (!p) { return; } - if (p.status !== ProposalStatus.DownloadingProposal) { + if (p.purchaseStatus !== PurchaseStatus.DownloadingProposal) { return; } p.download = { @@ -516,13 +516,13 @@ export async function processDownloadProposal( await tx.purchases.indexes.byFulfillmentUrl.get(fulfillmentUrl); if (differentPurchase) { logger.warn("repurchase detected"); - p.status = ProposalStatus.RepurchaseDetected; + p.purchaseStatus = PurchaseStatus.RepurchaseDetected; p.repurchaseProposalId = differentPurchase.proposalId; await tx.purchases.put(p); return; } } - p.status = ProposalStatus.Proposed; + p.purchaseStatus = PurchaseStatus.Proposed; await tx.purchases.put(p); }); @@ -595,7 +595,7 @@ async function startDownloadProposal( merchantBaseUrl, orderId, proposalId: proposalId, - status: ProposalStatus.DownloadingProposal, + purchaseStatus: PurchaseStatus.DownloadingProposal, repurchaseProposalId: undefined, downloadSessionId: sessionId, autoRefundDeadline: undefined, @@ -649,8 +649,8 @@ async function storeFirstPaySuccess( logger.warn("payment success already stored"); return; } - if (purchase.status === ProposalStatus.Paying) { - purchase.status = ProposalStatus.Paid; + if (purchase.purchaseStatus === PurchaseStatus.Paying) { + purchase.purchaseStatus = PurchaseStatus.Paid; } purchase.timestampFirstSuccessfulPay = now; purchase.lastSessionId = sessionId; @@ -659,7 +659,7 @@ async function storeFirstPaySuccess( if (protoAr) { const ar = Duration.fromTalerProtocolDuration(protoAr); logger.info("auto_refund present"); - purchase.status = ProposalStatus.QueryingAutoRefund; + purchase.purchaseStatus = PurchaseStatus.QueryingAutoRefund; purchase.autoRefundDeadline = AbsoluteTime.toTimestamp( AbsoluteTime.addDuration(AbsoluteTime.now(), ar), ); @@ -686,8 +686,8 @@ async function storePayReplaySuccess( if (isFirst) { throw Error("invalid payment state"); } - if (purchase.status === ProposalStatus.Paying) { - purchase.status = ProposalStatus.Paid; + if (purchase.purchaseStatus === PurchaseStatus.Paying) { + purchase.purchaseStatus = PurchaseStatus.Paid; } purchase.lastSessionId = sessionId; await tx.purchases.put(purchase); @@ -1239,7 +1239,7 @@ export async function checkPaymentByProposalId( if (!proposal) { throw Error(`could not get proposal ${proposalId}`); } - if (proposal.status === ProposalStatus.RepurchaseDetected) { + if (proposal.purchaseStatus === PurchaseStatus.RepurchaseDetected) { const existingProposalId = proposal.repurchaseProposalId; if (!existingProposalId) { throw Error("invalid proposal state"); @@ -1274,7 +1274,7 @@ export async function checkPaymentByProposalId( return tx.purchases.get(proposalId); }); - if (!purchase || purchase.status === ProposalStatus.Proposed) { + if (!purchase || purchase.purchaseStatus === PurchaseStatus.Proposed) { // If not already paid, check if we could pay for it. const res = await selectPayCoinsNew(ws, { auditors: contractData.allowedAuditors, @@ -1315,7 +1315,7 @@ export async function checkPaymentByProposalId( } if ( - purchase.status === ProposalStatus.Paid && + purchase.purchaseStatus === PurchaseStatus.Paid && purchase.lastSessionId !== sessionId ) { logger.trace( @@ -1330,7 +1330,7 @@ export async function checkPaymentByProposalId( return; } p.lastSessionId = sessionId; - p.status = ProposalStatus.PayingReplay; + p.purchaseStatus = PurchaseStatus.PayingReplay; await tx.purchases.put(p); }); const r = await processPurchasePay(ws, proposalId, { forceNow: true }); @@ -1361,9 +1361,9 @@ export async function checkPaymentByProposalId( }; } else { const paid = - purchase.status === ProposalStatus.Paid || - purchase.status === ProposalStatus.QueryingRefund || - purchase.status === ProposalStatus.QueryingAutoRefund; + purchase.purchaseStatus === PurchaseStatus.Paid || + purchase.purchaseStatus === PurchaseStatus.QueryingRefund || + purchase.purchaseStatus === PurchaseStatus.QueryingAutoRefund; const download = await expectProposalDownload(purchase); return { status: PreparePayResultType.AlreadyConfirmed, @@ -1615,6 +1615,9 @@ export async function confirmPay( ) { logger.trace(`changing session ID to ${sessionIdOverride}`); purchase.lastSessionId = sessionIdOverride; + if (purchase.purchaseStatus === PurchaseStatus.Paid) { + purchase.purchaseStatus = PurchaseStatus.PayingReplay; + } await tx.purchases.put(purchase); } return purchase; @@ -1688,8 +1691,8 @@ export async function confirmPay( if (!p) { return; } - switch (p.status) { - case ProposalStatus.Proposed: + switch (p.purchaseStatus) { + case PurchaseStatus.Proposed: p.payInfo = { payCoinSelection: coinSelection, payCoinSelectionUid: encodeCrock(getRandomBytes(16)), @@ -1698,7 +1701,7 @@ export async function confirmPay( }; p.lastSessionId = sessionId; p.timestampAccept = TalerProtocolTimestamp.now(); - p.status = ProposalStatus.Paying; + p.purchaseStatus = PurchaseStatus.Paying; await tx.purchases.put(p); await spendCoins(ws, tx, { allocationId: `proposal:${p.proposalId}`, @@ -1707,8 +1710,8 @@ export async function confirmPay( refreshReason: RefreshReason.PayMerchant, }); break; - case ProposalStatus.Paid: - case ProposalStatus.Paying: + case PurchaseStatus.Paid: + case PurchaseStatus.Paying: default: break; } @@ -1746,26 +1749,26 @@ export async function processPurchase( }; } - switch (purchase.status) { - case ProposalStatus.DownloadingProposal: + switch (purchase.purchaseStatus) { + case PurchaseStatus.DownloadingProposal: return processDownloadProposal(ws, proposalId, options); - case ProposalStatus.Paying: - case ProposalStatus.PayingReplay: + case PurchaseStatus.Paying: + case PurchaseStatus.PayingReplay: return processPurchasePay(ws, proposalId, options); - case ProposalStatus.QueryingAutoRefund: - case ProposalStatus.QueryingAutoRefund: - case ProposalStatus.AbortingWithRefund: + case PurchaseStatus.QueryingRefund: + case PurchaseStatus.QueryingAutoRefund: + case PurchaseStatus.AbortingWithRefund: return processPurchaseQueryRefund(ws, proposalId, options); - case ProposalStatus.ProposalDownloadFailed: - case ProposalStatus.Paid: - case ProposalStatus.AbortingWithRefund: - case ProposalStatus.RepurchaseDetected: + case PurchaseStatus.ProposalDownloadFailed: + case PurchaseStatus.Paid: + case PurchaseStatus.AbortingWithRefund: + case PurchaseStatus.RepurchaseDetected: return { type: OperationAttemptResultType.Finished, result: undefined, }; default: - throw Error(`unexpected purchase status (${purchase.status})`); + throw Error(`unexpected purchase status (${purchase.purchaseStatus})`); } } @@ -1792,9 +1795,9 @@ export async function processPurchasePay( }, }; } - switch (purchase.status) { - case ProposalStatus.Paying: - case ProposalStatus.PayingReplay: + switch (purchase.purchaseStatus) { + case PurchaseStatus.Paying: + case PurchaseStatus.PayingReplay: break; default: return OperationAttemptResult.finishedEmpty(); @@ -1870,7 +1873,7 @@ export async function processPurchasePay( return; } // FIXME: Should be some "PayPermanentlyFailed" and error info should be stored - purch.status = ProposalStatus.PaymentAbortFinished; + purch.purchaseStatus = PurchaseStatus.PaymentAbortFinished; await tx.purchases.put(purch); }); throw makePendingOperationFailedError( @@ -1975,10 +1978,10 @@ export async function refuseProposal( logger.trace(`proposal ${proposalId} not found, won't refuse proposal`); return false; } - if (proposal.status !== ProposalStatus.Proposed) { + if (proposal.purchaseStatus !== PurchaseStatus.Proposed) { return false; } - proposal.status = ProposalStatus.ProposalRefused; + proposal.purchaseStatus = PurchaseStatus.ProposalRefused; await tx.purchases.put(proposal); return true; }); @@ -2211,7 +2214,7 @@ async function storeFailedRefund( rtransactionId: r.rtransaction_id, }; - if (p.status === ProposalStatus.AbortingWithRefund) { + if (p.purchaseStatus === PurchaseStatus.AbortingWithRefund) { // Refund failed because the merchant didn't even try to deposit // the coin yet, so we try to refresh. if (r.exchange_code === TalerErrorCode.EXCHANGE_REFUND_DEPOSIT_NOT_FOUND) { @@ -2346,9 +2349,9 @@ async function acceptRefunds( if (queryDone) { p.timestampLastRefundStatus = now; - if (p.status === ProposalStatus.AbortingWithRefund) { - p.status = ProposalStatus.PaymentAbortFinished; - } else if (p.status === ProposalStatus.QueryingAutoRefund) { + if (p.purchaseStatus === PurchaseStatus.AbortingWithRefund) { + p.purchaseStatus = PurchaseStatus.PaymentAbortFinished; + } else if (p.purchaseStatus === PurchaseStatus.QueryingAutoRefund) { const autoRefundDeadline = p.autoRefundDeadline; checkDbInvariant(!!autoRefundDeadline); if ( @@ -2356,10 +2359,10 @@ async function acceptRefunds( AbsoluteTime.fromTimestamp(autoRefundDeadline), ) ) { - p.status = ProposalStatus.Paid; + p.purchaseStatus = PurchaseStatus.Paid; } - } else if (p.status === ProposalStatus.QueryingRefund) { - p.status = ProposalStatus.Paid; + } else if (p.purchaseStatus === PurchaseStatus.QueryingRefund) { + p.purchaseStatus = PurchaseStatus.Paid; } logger.trace("refund query done"); } else { @@ -2483,8 +2486,8 @@ export async function applyRefundFromPurchaseId( logger.error("no purchase found for refund URL"); return false; } - if (p.status === ProposalStatus.Paid) { - p.status = ProposalStatus.QueryingRefund; + if (p.purchaseStatus === PurchaseStatus.Paid) { + p.purchaseStatus = PurchaseStatus.QueryingRefund; } await tx.purchases.put(p); return true; @@ -2610,9 +2613,9 @@ export async function processPurchaseQueryRefund( if ( !( - purchase.status === ProposalStatus.QueryingAutoRefund || - purchase.status === ProposalStatus.QueryingRefund || - purchase.status === ProposalStatus.AbortingWithRefund + purchase.purchaseStatus === PurchaseStatus.QueryingAutoRefund || + purchase.purchaseStatus === PurchaseStatus.QueryingRefund || + purchase.purchaseStatus === PurchaseStatus.AbortingWithRefund ) ) { return OperationAttemptResult.finishedEmpty(); @@ -2659,7 +2662,7 @@ export async function processPurchaseQueryRefund( refundResponse.refunds, RefundReason.NormalRefund, ); - } else if (purchase.status === ProposalStatus.AbortingWithRefund) { + } else if (purchase.purchaseStatus === PurchaseStatus.AbortingWithRefund) { const requestUrl = new URL( `orders/${download.contractData.orderId}/abort`, download.contractData.merchantBaseUrl, @@ -2745,8 +2748,8 @@ export async function abortFailedPayWithRefund( logger.warn(`tried to abort successful payment`); return; } - if (purchase.status === ProposalStatus.Paying) { - purchase.status = ProposalStatus.AbortingWithRefund; + if (purchase.purchaseStatus === PurchaseStatus.Paying) { + purchase.purchaseStatus = PurchaseStatus.AbortingWithRefund; } await tx.purchases.put(purchase); }); diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts index db7a85432..285cef534 100644 --- a/packages/taler-wallet-core/src/operations/pending.ts +++ b/packages/taler-wallet-core/src/operations/pending.ts @@ -22,7 +22,7 @@ * Imports. */ import { - ProposalStatus, + PurchaseStatus, WalletStoresV1, BackupProviderStateTag, RefreshCoinStatus, @@ -252,7 +252,6 @@ async function gatherPurchasePending( now: AbsoluteTime, resp: PendingOperationsResponse, ): Promise { - // FIXME: Only iter purchases with some "active" flag! const keyRange = GlobalIDB.KeyRange.bound( OperationStatusRange.ACTIVE_START, OperationStatusRange.ACTIVE_END, @@ -268,7 +267,7 @@ async function gatherPurchasePending( type: PendingTaskType.Purchase, ...getPendingCommon(ws, opId, timestampDue), givesLifeness: true, - statusStr: ProposalStatus[pr.status], + statusStr: PurchaseStatus[pr.purchaseStatus], proposalId: pr.proposalId, retryInfo: retryRecord?.retryInfo, lastError: retryRecord?.lastError, diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 6ddf14f98..d8069436a 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -41,7 +41,7 @@ import { OperationRetryRecord, PeerPullPaymentIncomingRecord, PeerPushPaymentInitiationRecord, - ProposalStatus, + PurchaseStatus, PurchaseRecord, RefundState, TipRecord, @@ -679,7 +679,7 @@ async function buildTransactionForPurchase( status: purchaseRecord.timestampFirstSuccessfulPay ? PaymentStatus.Paid : PaymentStatus.Accepted, - pending: purchaseRecord.status === ProposalStatus.Paying, + pending: purchaseRecord.purchaseStatus === PurchaseStatus.Paying, refunds, timestamp, transactionId: makeEventId( @@ -689,7 +689,7 @@ async function buildTransactionForPurchase( proposalId: purchaseRecord.proposalId, info, frozen: - purchaseRecord.status === ProposalStatus.PaymentAbortFinished ?? false, + purchaseRecord.purchaseStatus === PurchaseStatus.PaymentAbortFinished ?? false, ...(ort?.lastError ? { error: ort.lastError } : {}), }; } -- cgit v1.2.3