aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations/transactions.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-05-05 19:03:44 +0200
committerFlorian Dold <florian@dold.me>2023-05-07 21:51:02 +0200
commit7f0edb6a783d9a50f94f65c815c1280baecaac89 (patch)
treeb4f3bc24fece1f651e8c2f69fc0fcf52e1d6f330 /packages/taler-wallet-core/src/operations/transactions.ts
parenta0bf83fbb5db026389cc7d203adcff050d5a1b28 (diff)
downloadwallet-core-7f0edb6a783d9a50f94f65c815c1280baecaac89.tar.xz
wallet-core: refund DD37 refactoring
Diffstat (limited to 'packages/taler-wallet-core/src/operations/transactions.ts')
-rw-r--r--packages/taler-wallet-core/src/operations/transactions.ts310
1 files changed, 49 insertions, 261 deletions
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts
index 02f11d82d..d9778f0c2 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -19,7 +19,6 @@
*/
import {
AbsoluteTime,
- AmountJson,
Amounts,
constructPayPullUri,
constructPayPushUri,
@@ -51,9 +50,7 @@ import {
PeerPushPaymentInitiationRecord,
PurchaseStatus,
PurchaseRecord,
- RefundState,
TipRecord,
- WalletRefundItem,
WithdrawalGroupRecord,
WithdrawalRecordType,
WalletContractData,
@@ -66,6 +63,7 @@ import {
PeerPushPaymentIncomingRecord,
PeerPushPaymentIncomingStatus,
PeerPullPaymentInitiationRecord,
+ RefundGroupRecord,
} from "../db.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { PendingTaskType } from "../pending-types.js";
@@ -89,6 +87,7 @@ import { getExchangeDetails } from "./exchanges.js";
import {
abortPayMerchant,
computePayMerchantTransactionState,
+ computeRefundTransactionState,
expectProposalDownload,
extractContractData,
processPurchasePay,
@@ -205,40 +204,15 @@ export async function getTransactionById(
.runReadWrite(async (tx) => {
const purchase = await tx.purchases.get(proposalId);
if (!purchase) throw Error("not found");
-
- const filteredRefunds = await Promise.all(
- Object.values(purchase.refunds).map(async (r) => {
- const t = await tx.tombstones.get(
- makeTombstoneId(
- TombstoneTag.DeleteRefund,
- purchase.proposalId,
- `${r.executionTime.t_s}`,
- ),
- );
- if (!t) return r;
- return undefined;
- }),
- );
-
const download = await expectProposalDownload(ws, purchase, tx);
-
- const cleanRefunds = filteredRefunds.filter(
- (x): x is WalletRefundItem => !!x,
- );
-
const contractData = download.contractData;
- const refunds = mergeRefundByExecutionTime(
- cleanRefunds,
- Amounts.zeroOfAmount(contractData.amount),
- );
-
const payOpId = TaskIdentifiers.forPay(purchase);
const payRetryRecord = await tx.operationRetries.get(payOpId);
return buildTransactionForPurchase(
purchase,
contractData,
- refunds,
+ [], // FIXME: Add refunds from refund group records here.
payRetryRecord,
);
});
@@ -272,66 +246,8 @@ export async function getTransactionById(
return buildTransactionForDeposit(depositRecord, retries);
});
} else if (type === TransactionType.Refund) {
- const proposalId = rest[0];
- const executionTimeStr = rest[1];
-
- return await ws.db
- .mktx((x) => [
- x.operationRetries,
- x.purchases,
- x.tombstones,
- x.contractTerms,
- ])
- .runReadWrite(async (tx) => {
- const purchase = await tx.purchases.get(proposalId);
- if (!purchase) throw Error("not found");
-
- const t = await tx.tombstones.get(
- makeTombstoneId(
- TombstoneTag.DeleteRefund,
- purchase.proposalId,
- executionTimeStr,
- ),
- );
- if (t) throw Error("deleted");
-
- const filteredRefunds = await Promise.all(
- Object.values(purchase.refunds).map(async (r) => {
- const t = await tx.tombstones.get(
- makeTombstoneId(
- TombstoneTag.DeleteRefund,
- purchase.proposalId,
- `${r.executionTime.t_s}`,
- ),
- );
- if (!t) return r;
- return undefined;
- }),
- );
-
- const cleanRefunds = filteredRefunds.filter(
- (x): x is WalletRefundItem => !!x,
- );
-
- const download = await expectProposalDownload(ws, purchase, tx);
- const contractData = download.contractData;
- const refunds = mergeRefundByExecutionTime(
- cleanRefunds,
- Amounts.zeroOfAmount(contractData.amount),
- );
-
- const theRefund = refunds.find(
- (r) => `${r.executionTime.t_s}` === executionTimeStr,
- );
- if (!theRefund) throw Error("not found");
-
- return buildTransactionForRefund(
- purchase,
- contractData,
- theRefund,
- undefined,
- );
- });
+ // FIXME!
+ throw Error("not implemented");
} else if (type === TransactionType.PeerPullDebit) {
const peerPullPaymentIncomingId = rest[0];
return await ws.db
@@ -730,6 +646,29 @@ function buildTransactionForManualWithdraw(
};
}
+function buildTransactionForRefund(
+ refundRecord: RefundGroupRecord,
+): Transaction {
+ return {
+ type: TransactionType.Refund,
+ amountEffective: refundRecord.amountEffective,
+ amountRaw: refundRecord.amountEffective,
+ refundedTransactionId: constructTransactionIdentifier({
+ tag: TransactionType.Payment,
+ proposalId: refundRecord.proposalId
+ }),
+ timestamp: refundRecord.timestampCreated,
+ transactionId: constructTransactionIdentifier({
+ tag: TransactionType.Refund,
+ refundGroupId: refundRecord.refundGroupId,
+ }),
+ txState: computeRefundTransactionState(refundRecord),
+ extendedStatus: ExtendedStatus.Done,
+ frozen: false,
+ pending: false,
+ }
+}
+
function buildTransactionForRefresh(
refreshGroupRecord: RefreshGroupRecord,
ort?: OperationRetryRecord,
@@ -850,113 +789,11 @@ function buildTransactionForTip(
};
}
-/**
- * For a set of refund with the same executionTime.
- */
-interface MergedRefundInfo {
- executionTime: TalerProtocolTimestamp;
- amountAppliedRaw: AmountJson;
- amountAppliedEffective: AmountJson;
- firstTimestamp: TalerProtocolTimestamp;
-}
-
-function mergeRefundByExecutionTime(
- rs: WalletRefundItem[],
- zero: AmountJson,
-): MergedRefundInfo[] {
- const refundByExecTime = rs.reduce((prev, refund) => {
- const key = `${refund.executionTime.t_s}`;
-
- // refunds count if applied
- const effective =
- refund.type === RefundState.Applied
- ? Amounts.sub(
- refund.refundAmount,
- refund.refundFee,
- refund.totalRefreshCostBound,
- ).amount
- : zero;
- const raw =
- refund.type === RefundState.Applied ? refund.refundAmount : zero;
-
- const v = prev.get(key);
- if (!v) {
- prev.set(key, {
- executionTime: refund.executionTime,
- amountAppliedEffective: effective,
- amountAppliedRaw: Amounts.parseOrThrow(raw),
- firstTimestamp: refund.obtainedTime,
- });
- } else {
- //v.executionTime is the same
- v.amountAppliedEffective = Amounts.add(
- v.amountAppliedEffective,
- effective,
- ).amount;
- v.amountAppliedRaw = Amounts.add(
- v.amountAppliedRaw,
- refund.refundAmount,
- ).amount;
- v.firstTimestamp = TalerProtocolTimestamp.min(
- v.firstTimestamp,
- refund.obtainedTime,
- );
- }
- return prev;
- }, new Map<string, MergedRefundInfo>());
-
- return Array.from(refundByExecTime.values());
-}
-
-async function buildTransactionForRefund(
- purchaseRecord: PurchaseRecord,
- contractData: WalletContractData,
- refundInfo: MergedRefundInfo,
- ort?: OperationRetryRecord,
-): Promise<Transaction> {
- const info: OrderShortInfo = {
- merchant: contractData.merchant,
- orderId: contractData.orderId,
- products: contractData.products,
- summary: contractData.summary,
- summary_i18n: contractData.summaryI18n,
- contractTermsHash: contractData.contractTermsHash,
- };
- if (contractData.fulfillmentUrl !== "") {
- info.fulfillmentUrl = contractData.fulfillmentUrl;
- }
-
- return {
- type: TransactionType.Refund,
- txState: mkTxStateUnknown(),
- info,
- refundedTransactionId: makeTransactionId(
- TransactionType.Payment,
- purchaseRecord.proposalId,
- ),
- transactionId: makeTransactionId(
- TransactionType.Refund,
- purchaseRecord.proposalId,
- `${refundInfo.executionTime.t_s}`,
- ),
- timestamp: refundInfo.firstTimestamp,
- amountEffective: Amounts.stringify(refundInfo.amountAppliedEffective),
- amountRaw: Amounts.stringify(refundInfo.amountAppliedRaw),
- refundPending:
- purchaseRecord.refundAmountAwaiting === undefined
- ? undefined
- : Amounts.stringify(purchaseRecord.refundAmountAwaiting),
- extendedStatus: ExtendedStatus.Done,
- pending: false,
- frozen: false,
- ...(ort?.lastError ? { error: ort.lastError } : {}),
- };
-}
async function buildTransactionForPurchase(
purchaseRecord: PurchaseRecord,
contractData: WalletContractData,
- refundsInfo: MergedRefundInfo[],
+ refundsInfo: RefundGroupRecord[],
ort?: OperationRetryRecord,
): Promise<Transaction> {
const zero = Amounts.zeroOfAmount(contractData.amount);
@@ -974,30 +811,7 @@ async function buildTransactionForPurchase(
info.fulfillmentUrl = contractData.fulfillmentUrl;
}
- const totalRefund = refundsInfo.reduce(
- (prev, cur) => {
- return {
- raw: Amounts.add(prev.raw, cur.amountAppliedRaw).amount,
- effective: Amounts.add(prev.effective, cur.amountAppliedEffective)
- .amount,
- };
- },
- {
- raw: zero,
- effective: zero,
- } as { raw: AmountJson; effective: AmountJson },
- );
-
- const refunds: RefundInfoShort[] = refundsInfo.map((r) => ({
- amountEffective: Amounts.stringify(r.amountAppliedEffective),
- amountRaw: Amounts.stringify(r.amountAppliedRaw),
- timestamp: r.executionTime,
- transactionId: makeTransactionId(
- TransactionType.Refund,
- purchaseRecord.proposalId,
- `${r.executionTime.t_s}`,
- ),
- }));
+ const refunds: RefundInfoShort[] = [];
const timestamp = purchaseRecord.timestampAccept;
checkDbInvariant(!!timestamp);
@@ -1008,7 +822,7 @@ async function buildTransactionForPurchase(
case PurchaseStatus.AbortingWithRefund:
status = ExtendedStatus.Aborting;
break;
- case PurchaseStatus.Paid:
+ case PurchaseStatus.Done:
case PurchaseStatus.RepurchaseDetected:
status = ExtendedStatus.Done;
break;
@@ -1018,10 +832,10 @@ async function buildTransactionForPurchase(
case PurchaseStatus.Paying:
status = ExtendedStatus.Pending;
break;
- case PurchaseStatus.ProposalDownloadFailed:
+ case PurchaseStatus.FailedClaim:
status = ExtendedStatus.Failed;
break;
- case PurchaseStatus.PaymentAbortFinished:
+ case PurchaseStatus.AbortedIncompletePayment:
status = ExtendedStatus.Aborted;
break;
default:
@@ -1034,8 +848,8 @@ async function buildTransactionForPurchase(
txState: computePayMerchantTransactionState(purchaseRecord),
amountRaw: Amounts.stringify(contractData.amount),
amountEffective: Amounts.stringify(purchaseRecord.payInfo.totalPayCost),
- totalRefundRaw: Amounts.stringify(totalRefund.raw),
- totalRefundEffective: Amounts.stringify(totalRefund.effective),
+ totalRefundRaw: Amounts.stringify(zero), // FIXME!
+ totalRefundEffective: Amounts.stringify(zero), // FIXME!
refundPending:
purchaseRecord.refundAmountAwaiting === undefined
? undefined
@@ -1057,7 +871,7 @@ async function buildTransactionForPurchase(
refundQueryActive:
purchaseRecord.purchaseStatus === PurchaseStatus.QueryingRefund,
frozen:
- purchaseRecord.purchaseStatus === PurchaseStatus.PaymentAbortFinished ??
+ purchaseRecord.purchaseStatus === PurchaseStatus.AbortedIncompletePayment ??
false,
...(ort?.lastError ? { error: ort.lastError } : {}),
};
@@ -1092,6 +906,7 @@ export async function getTransactions(
x.tombstones,
x.withdrawalGroups,
x.refreshGroups,
+ x.refundGroups,
])
.runReadOnly(async (tx) => {
tx.peerPushPaymentInitiations.iter().forEachAsync(async (pi) => {
@@ -1202,6 +1017,14 @@ export async function getTransactions(
);
});
+ tx.refundGroups.iter().forEachAsync(async (refundGroup) => {
+ const currency = Amounts.currencyOf(refundGroup.amountRaw);
+ if (shouldSkipCurrency(transactionsRequest, currency)) {
+ return;
+ }
+ transactions.push(buildTransactionForRefund(refundGroup))
+ });
+
tx.refreshGroups.iter().forEachAsync(async (rg) => {
if (shouldSkipCurrency(transactionsRequest, rg.currency)) {
return;
@@ -1318,47 +1141,13 @@ export async function getTransactions(
download.contractTermsMerchantSig,
);
- const filteredRefunds = await Promise.all(
- Object.values(purchase.refunds).map(async (r) => {
- const t = await tx.tombstones.get(
- makeTombstoneId(
- TombstoneTag.DeleteRefund,
- purchase.proposalId,
- `${r.executionTime.t_s}`,
- ),
- );
- if (!t) return r;
- return undefined;
- }),
- );
-
- const cleanRefunds = filteredRefunds.filter(
- (x): x is WalletRefundItem => !!x,
- );
-
- const refunds = mergeRefundByExecutionTime(
- cleanRefunds,
- Amounts.zeroOfCurrency(download.currency),
- );
-
- refunds.forEach(async (refundInfo) => {
- transactions.push(
- await buildTransactionForRefund(
- purchase,
- contractData,
- refundInfo,
- undefined,
- ),
- );
- });
-
const payOpId = TaskIdentifiers.forPay(purchase);
const payRetryRecord = await tx.operationRetries.get(payOpId);
transactions.push(
await buildTransactionForPurchase(
purchase,
contractData,
- refunds,
+ [], // FIXME!
payRetryRecord,
),
);
@@ -1425,7 +1214,7 @@ export type ParsedTransactionIdentifier =
| { tag: TransactionType.PeerPushCredit; peerPushPaymentIncomingId: string }
| { tag: TransactionType.PeerPushDebit; pursePub: string }
| { tag: TransactionType.Refresh; refreshGroupId: string }
- | { tag: TransactionType.Refund; proposalId: string; executionTime: string }
+ | { tag: TransactionType.Refund; refundGroupId: string }
| { tag: TransactionType.Tip; walletTipId: string }
| { tag: TransactionType.Withdrawal; withdrawalGroupId: string };
@@ -1448,7 +1237,7 @@ export function constructTransactionIdentifier(
case TransactionType.Refresh:
return `txn:${pTxId.tag}:${pTxId.refreshGroupId}`;
case TransactionType.Refund:
- return `txn:${pTxId.tag}:${pTxId.proposalId}:${pTxId.executionTime}`;
+ return `txn:${pTxId.tag}:${pTxId.refundGroupId}`;
case TransactionType.Tip:
return `txn:${pTxId.tag}:${pTxId.walletTipId}`;
case TransactionType.Withdrawal:
@@ -1490,8 +1279,7 @@ export function parseTransactionIdentifier(
case TransactionType.Refund:
return {
tag: TransactionType.Refund,
- proposalId: rest[0],
- executionTime: rest[1],
+ refundGroupId: rest[0],
};
case TransactionType.Tip:
return {