wallet-core: put contract terms into separate object store
This commit is contained in:
parent
8ac5080607
commit
19f3e6321d
@ -909,6 +909,8 @@ export interface BackupPurchase {
|
||||
|
||||
/**
|
||||
* Signature on the contract terms.
|
||||
*
|
||||
* FIXME: Better name needed.
|
||||
*/
|
||||
merchant_sig?: string;
|
||||
|
||||
|
@ -1085,18 +1085,16 @@ export enum PurchaseStatus {
|
||||
Paid = OperationStatusRange.DORMANT_START + 5,
|
||||
}
|
||||
|
||||
/**
|
||||
* Partial information about the downloaded proposal.
|
||||
* Only contains data that is relevant for indexing on the
|
||||
* "purchases" object stores.
|
||||
*/
|
||||
export interface ProposalDownload {
|
||||
/**
|
||||
* The contract that was offered by the merchant.
|
||||
*/
|
||||
contractTermsRaw: any;
|
||||
|
||||
/**
|
||||
* Extracted / parsed data from the contract terms.
|
||||
*
|
||||
* FIXME: Do we need to store *all* that data in duplicate?
|
||||
*/
|
||||
contractData: WalletContractData;
|
||||
contractTermsHash: string;
|
||||
fulfillmentUrl?: string;
|
||||
currency: string;
|
||||
contractTermsMerchantSig: string;
|
||||
}
|
||||
|
||||
export interface PurchasePayInfo {
|
||||
@ -1723,6 +1721,7 @@ export interface PeerPullPaymentInitiationRecord {
|
||||
* Contract terms for the other party.
|
||||
*
|
||||
* FIXME: Nail down type!
|
||||
* FIXME: Put in contractTerms store
|
||||
*/
|
||||
contractTerms: any;
|
||||
}
|
||||
@ -1819,6 +1818,18 @@ export interface CoinAvailabilityRecord {
|
||||
freshCoinCount: number;
|
||||
}
|
||||
|
||||
export interface ContractTermsRecord {
|
||||
/**
|
||||
* Contract terms hash.
|
||||
*/
|
||||
h: string;
|
||||
|
||||
/**
|
||||
* Contract terms JSON.
|
||||
*/
|
||||
contractTermsRaw: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schema definition for the IndexedDB
|
||||
* wallet database.
|
||||
@ -1937,13 +1948,8 @@ export const WalletStoresV1 = {
|
||||
byStatus: describeIndex("byStatus", "purchaseStatus"),
|
||||
byFulfillmentUrl: describeIndex(
|
||||
"byFulfillmentUrl",
|
||||
"download.contractData.fulfillmentUrl",
|
||||
"download.fulfillmentUrl",
|
||||
),
|
||||
// FIXME: Deduplicate!
|
||||
byMerchantUrlAndOrderId: describeIndex("byMerchantUrlAndOrderId", [
|
||||
"download.contractData.merchantBaseUrl",
|
||||
"download.contractData.orderId",
|
||||
]),
|
||||
byUrlAndOrderId: describeIndex("byUrlAndOrderId", [
|
||||
"merchantBaseUrl",
|
||||
"orderId",
|
||||
@ -2088,6 +2094,13 @@ export const WalletStoresV1 = {
|
||||
}),
|
||||
{},
|
||||
),
|
||||
contractTerms: describeStore(
|
||||
"contractTerms",
|
||||
describeContents<ContractTermsRecord>({
|
||||
keyPath: "h",
|
||||
}),
|
||||
{},
|
||||
),
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -88,6 +88,7 @@ export async function exportBackup(
|
||||
x.exchanges,
|
||||
x.exchangeDetails,
|
||||
x.coins,
|
||||
x.contractTerms,
|
||||
x.denominations,
|
||||
x.purchases,
|
||||
x.refreshGroups,
|
||||
@ -353,7 +354,7 @@ export async function exportBackup(
|
||||
|
||||
const purchaseProposalIdSet = new Set<string>();
|
||||
|
||||
await tx.purchases.iter().forEach((purch) => {
|
||||
await tx.purchases.iter().forEachAsync(async (purch) => {
|
||||
const refunds: BackupRefundItem[] = [];
|
||||
purchaseProposalIdSet.add(purch.proposalId);
|
||||
for (const refundKey of Object.keys(purch.refunds)) {
|
||||
@ -418,8 +419,18 @@ export async function exportBackup(
|
||||
};
|
||||
}
|
||||
|
||||
let contractTermsRaw = undefined;
|
||||
if (purch.download) {
|
||||
const contractTermsRecord = await tx.contractTerms.get(
|
||||
purch.download.contractTermsHash,
|
||||
);
|
||||
if (contractTermsRecord) {
|
||||
contractTermsRaw = contractTermsRecord.contractTermsRaw;
|
||||
}
|
||||
}
|
||||
|
||||
backupPurchases.push({
|
||||
contract_terms_raw: purch.download?.contractTermsRaw,
|
||||
contract_terms_raw: contractTermsRaw,
|
||||
auto_refund_deadline: purch.autoRefundDeadline,
|
||||
merchant_pay_sig: purch.merchantPaySig,
|
||||
pay_info: backupPayInfo,
|
||||
@ -428,7 +439,7 @@ export async function exportBackup(
|
||||
timestamp_accepted: purch.timestampAccept,
|
||||
timestamp_first_successful_pay: purch.timestampFirstSuccessfulPay,
|
||||
nonce_priv: purch.noncePriv,
|
||||
merchant_sig: purch.download?.contractData.merchantSig,
|
||||
merchant_sig: purch.download?.contractTermsMerchantSig,
|
||||
claim_token: purch.claimToken,
|
||||
merchant_base_url: purch.merchantBaseUrl,
|
||||
order_id: purch.orderId,
|
||||
|
@ -64,6 +64,7 @@ import { checkLogicInvariant } from "../../util/invariants.js";
|
||||
import { GetReadOnlyAccess, GetReadWriteAccess } from "../../util/query.js";
|
||||
import { makeCoinAvailable, makeEventId, TombstoneTag } from "../common.js";
|
||||
import { getExchangeDetails } from "../exchanges.js";
|
||||
import { extractContractData } from "../pay-merchant.js";
|
||||
import { provideBackupState } from "./state.js";
|
||||
|
||||
const logger = new Logger("operations/backup/import.ts");
|
||||
@ -630,49 +631,25 @@ export async function importBackup(
|
||||
maxWireFee = Amounts.getZero(amount.currency);
|
||||
}
|
||||
const download: ProposalDownload = {
|
||||
contractData: {
|
||||
amount,
|
||||
contractTermsHash: contractTermsHash,
|
||||
fulfillmentUrl: parsedContractTerms.fulfillment_url ?? "",
|
||||
merchantBaseUrl: parsedContractTerms.merchant_base_url,
|
||||
merchantPub: parsedContractTerms.merchant_pub,
|
||||
merchantSig: backupPurchase.merchant_sig!,
|
||||
orderId: parsedContractTerms.order_id,
|
||||
summary: parsedContractTerms.summary,
|
||||
autoRefund: parsedContractTerms.auto_refund,
|
||||
maxWireFee,
|
||||
payDeadline: parsedContractTerms.pay_deadline,
|
||||
refundDeadline: parsedContractTerms.refund_deadline,
|
||||
wireFeeAmortization:
|
||||
parsedContractTerms.wire_fee_amortization || 1,
|
||||
allowedAuditors: parsedContractTerms.auditors.map((x) => ({
|
||||
auditorBaseUrl: x.url,
|
||||
auditorPub: x.auditor_pub,
|
||||
})),
|
||||
allowedExchanges: parsedContractTerms.exchanges.map((x) => ({
|
||||
exchangeBaseUrl: x.url,
|
||||
exchangePub: x.master_pub,
|
||||
})),
|
||||
timestamp: parsedContractTerms.timestamp,
|
||||
wireMethod: parsedContractTerms.wire_method,
|
||||
wireInfoHash: parsedContractTerms.h_wire,
|
||||
maxDepositFee: Amounts.parseOrThrow(parsedContractTerms.max_fee),
|
||||
merchant: parsedContractTerms.merchant,
|
||||
products: parsedContractTerms.products,
|
||||
summaryI18n: parsedContractTerms.summary_i18n,
|
||||
deliveryDate: parsedContractTerms.delivery_date,
|
||||
deliveryLocation: parsedContractTerms.delivery_location,
|
||||
},
|
||||
contractTermsRaw: backupPurchase.contract_terms_raw,
|
||||
contractTermsHash,
|
||||
contractTermsMerchantSig: backupPurchase.merchant_sig!,
|
||||
currency: amount.currency,
|
||||
fulfillmentUrl: backupPurchase.contract_terms_raw.fulfillment_url,
|
||||
};
|
||||
|
||||
const contractData = extractContractData(
|
||||
backupPurchase.contract_terms_raw,
|
||||
contractTermsHash,
|
||||
download.contractTermsMerchantSig,
|
||||
);
|
||||
|
||||
let payInfo: PurchasePayInfo | undefined = undefined;
|
||||
if (backupPurchase.pay_info) {
|
||||
payInfo = {
|
||||
coinDepositPermissions: undefined,
|
||||
payCoinSelection: await recoverPayCoinSelection(
|
||||
tx,
|
||||
download.contractData,
|
||||
contractData,
|
||||
backupPurchase.pay_info,
|
||||
),
|
||||
payCoinSelectionUid: backupPurchase.pay_info.pay_coins_uid,
|
||||
|
@ -115,6 +115,7 @@ import {
|
||||
throwUnexpectedRequestError,
|
||||
} from "../util/http.js";
|
||||
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
|
||||
import { GetReadOnlyAccess } from "../util/query.js";
|
||||
import {
|
||||
OperationAttemptResult,
|
||||
OperationAttemptResultType,
|
||||
@ -256,12 +257,34 @@ function getPayRequestTimeout(purchase: PurchaseRecord): Duration {
|
||||
* (Async since in the future this will query the DB.)
|
||||
*/
|
||||
export async function expectProposalDownload(
|
||||
ws: InternalWalletState,
|
||||
p: PurchaseRecord,
|
||||
): Promise<ProposalDownload> {
|
||||
): Promise<{
|
||||
contractData: WalletContractData;
|
||||
contractTermsRaw: any;
|
||||
}> {
|
||||
if (!p.download) {
|
||||
throw Error("expected proposal to be downloaded");
|
||||
}
|
||||
return p.download;
|
||||
const download = p.download;
|
||||
return await ws.db
|
||||
.mktx((x) => [x.contractTerms])
|
||||
.runReadOnly(async (tx) => {
|
||||
const contractTerms = await tx.contractTerms.get(
|
||||
download.contractTermsHash,
|
||||
);
|
||||
if (!contractTerms) {
|
||||
throw Error("contract terms not found");
|
||||
}
|
||||
return {
|
||||
contractData: extractContractData(
|
||||
contractTerms.contractTermsRaw,
|
||||
download.contractTermsHash,
|
||||
download.contractTermsMerchantSig,
|
||||
),
|
||||
contractTermsRaw: contractTerms.contractTermsRaw,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function extractContractData(
|
||||
@ -494,7 +517,7 @@ export async function processDownloadProposal(
|
||||
logger.trace(`extracted contract data: ${j2s(contractData)}`);
|
||||
|
||||
await ws.db
|
||||
.mktx((x) => [x.purchases])
|
||||
.mktx((x) => [x.purchases, x.contractTerms])
|
||||
.runReadWrite(async (tx) => {
|
||||
const p = await tx.purchases.get(proposalId);
|
||||
if (!p) {
|
||||
@ -504,9 +527,15 @@ export async function processDownloadProposal(
|
||||
return;
|
||||
}
|
||||
p.download = {
|
||||
contractData,
|
||||
contractTermsRaw: proposalResp.contract_terms,
|
||||
contractTermsHash,
|
||||
contractTermsMerchantSig: contractData.merchantSig,
|
||||
currency: contractData.amount.currency,
|
||||
fulfillmentUrl: contractData.fulfillmentUrl,
|
||||
};
|
||||
await tx.contractTerms.put({
|
||||
h: contractTermsHash,
|
||||
contractTermsRaw: proposalResp.contract_terms,
|
||||
});
|
||||
if (
|
||||
fulfillmentUrl &&
|
||||
(fulfillmentUrl.startsWith("http://") ||
|
||||
@ -636,7 +665,7 @@ async function storeFirstPaySuccess(
|
||||
): Promise<void> {
|
||||
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
await ws.db
|
||||
.mktx((x) => [x.purchases])
|
||||
.mktx((x) => [x.purchases, x.contractTerms])
|
||||
.runReadWrite(async (tx) => {
|
||||
const purchase = await tx.purchases.get(proposalId);
|
||||
|
||||
@ -655,7 +684,18 @@ async function storeFirstPaySuccess(
|
||||
purchase.timestampFirstSuccessfulPay = now;
|
||||
purchase.lastSessionId = sessionId;
|
||||
purchase.merchantPaySig = paySig;
|
||||
const protoAr = purchase.download!.contractData.autoRefund;
|
||||
const dl = purchase.download;
|
||||
checkDbInvariant(!!dl);
|
||||
const contractTermsRecord = await tx.contractTerms.get(
|
||||
dl.contractTermsHash,
|
||||
);
|
||||
checkDbInvariant(!!contractTermsRecord);
|
||||
const contractData = extractContractData(
|
||||
contractTermsRecord.contractTermsRaw,
|
||||
dl.contractTermsHash,
|
||||
dl.contractTermsMerchantSig,
|
||||
);
|
||||
const protoAr = contractData.autoRefund;
|
||||
if (protoAr) {
|
||||
const ar = Duration.fromTalerProtocolDuration(protoAr);
|
||||
logger.info("auto_refund present");
|
||||
@ -739,7 +779,7 @@ async function handleInsufficientFunds(
|
||||
throw new TalerProtocolViolationError();
|
||||
}
|
||||
|
||||
const { contractData } = proposal.download!;
|
||||
const { contractData } = await expectProposalDownload(ws, proposal);
|
||||
|
||||
const prevPayCoins: PreviousPayCoins = [];
|
||||
|
||||
@ -1254,11 +1294,7 @@ export async function checkPaymentByProposalId(
|
||||
throw Error("existing proposal is in wrong state");
|
||||
}
|
||||
}
|
||||
const d = proposal.download;
|
||||
if (!d) {
|
||||
logger.error("bad proposal", proposal);
|
||||
throw Error("proposal is in invalid state");
|
||||
}
|
||||
const d = await expectProposalDownload(ws, proposal);
|
||||
const contractData = d.contractData;
|
||||
const merchantSig = d.contractData.merchantSig;
|
||||
if (!merchantSig) {
|
||||
@ -1338,7 +1374,7 @@ export async function checkPaymentByProposalId(
|
||||
// FIXME: This does not surface the original error
|
||||
throw Error("submitting pay failed");
|
||||
}
|
||||
const download = await expectProposalDownload(purchase);
|
||||
const download = await expectProposalDownload(ws, purchase);
|
||||
return {
|
||||
status: PreparePayResultType.AlreadyConfirmed,
|
||||
contractTerms: download.contractTermsRaw,
|
||||
@ -1349,7 +1385,7 @@ export async function checkPaymentByProposalId(
|
||||
proposalId,
|
||||
};
|
||||
} else if (!purchase.timestampFirstSuccessfulPay) {
|
||||
const download = await expectProposalDownload(purchase);
|
||||
const download = await expectProposalDownload(ws, purchase);
|
||||
return {
|
||||
status: PreparePayResultType.AlreadyConfirmed,
|
||||
contractTerms: download.contractTermsRaw,
|
||||
@ -1364,7 +1400,7 @@ export async function checkPaymentByProposalId(
|
||||
purchase.purchaseStatus === PurchaseStatus.Paid ||
|
||||
purchase.purchaseStatus === PurchaseStatus.QueryingRefund ||
|
||||
purchase.purchaseStatus === PurchaseStatus.QueryingAutoRefund;
|
||||
const download = await expectProposalDownload(purchase);
|
||||
const download = await expectProposalDownload(ws, purchase);
|
||||
return {
|
||||
status: PreparePayResultType.AlreadyConfirmed,
|
||||
contractTerms: download.contractTermsRaw,
|
||||
@ -1392,11 +1428,9 @@ export async function getContractTermsDetails(
|
||||
throw Error(`proposal with id ${proposalId} not found`);
|
||||
}
|
||||
|
||||
if (!proposal.download || !proposal.download.contractData) {
|
||||
throw Error("proposal is in invalid state");
|
||||
}
|
||||
const d = await expectProposalDownload(ws, proposal);
|
||||
|
||||
return proposal.download.contractData;
|
||||
return d.contractData;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1516,12 +1550,13 @@ export async function runPayForConfirmPay(
|
||||
.runReadOnly(async (tx) => {
|
||||
return tx.purchases.get(proposalId);
|
||||
});
|
||||
if (!purchase?.download) {
|
||||
if (!purchase) {
|
||||
throw Error("purchase record not available anymore");
|
||||
}
|
||||
const d = await expectProposalDownload(ws, purchase);
|
||||
return {
|
||||
type: ConfirmPayResultType.Done,
|
||||
contractTerms: purchase.download.contractTermsRaw,
|
||||
contractTerms: d.contractTermsRaw,
|
||||
transactionId: makeEventId(TransactionType.Payment, proposalId),
|
||||
};
|
||||
}
|
||||
@ -1599,7 +1634,7 @@ export async function confirmPay(
|
||||
throw Error(`proposal with id ${proposalId} not found`);
|
||||
}
|
||||
|
||||
const d = proposal.download;
|
||||
const d = await expectProposalDownload(ws, proposal);
|
||||
if (!d) {
|
||||
throw Error("proposal is in invalid state");
|
||||
}
|
||||
@ -1810,7 +1845,7 @@ export async function processPurchasePay(
|
||||
const payInfo = purchase.payInfo;
|
||||
checkDbInvariant(!!payInfo, "payInfo");
|
||||
|
||||
const download = await expectProposalDownload(purchase);
|
||||
const download = await expectProposalDownload(ws, purchase);
|
||||
if (!purchase.merchantPaySig) {
|
||||
const payUrl = new URL(
|
||||
`orders/${download.contractData.orderId}/pay`,
|
||||
@ -2007,7 +2042,7 @@ export async function prepareRefund(
|
||||
const purchase = await ws.db
|
||||
.mktx((x) => [x.purchases])
|
||||
.runReadOnly(async (tx) => {
|
||||
return tx.purchases.indexes.byMerchantUrlAndOrderId.get([
|
||||
return tx.purchases.indexes.byUrlAndOrderId.get([
|
||||
parseResult.merchantBaseUrl,
|
||||
parseResult.orderId,
|
||||
]);
|
||||
@ -2020,10 +2055,10 @@ export async function prepareRefund(
|
||||
}
|
||||
|
||||
const awaiting = await queryAndSaveAwaitingRefund(ws, purchase);
|
||||
const summary = await calculateRefundSummary(purchase);
|
||||
const summary = await calculateRefundSummary(ws, purchase);
|
||||
const proposalId = purchase.proposalId;
|
||||
|
||||
const { contractData: c } = await expectProposalDownload(purchase);
|
||||
const { contractData: c } = await expectProposalDownload(ws, purchase);
|
||||
|
||||
return {
|
||||
proposalId,
|
||||
@ -2380,9 +2415,10 @@ async function acceptRefunds(
|
||||
}
|
||||
|
||||
async function calculateRefundSummary(
|
||||
ws: InternalWalletState,
|
||||
p: PurchaseRecord,
|
||||
): Promise<RefundSummary> {
|
||||
const download = await expectProposalDownload(p);
|
||||
const download = await expectProposalDownload(ws, p);
|
||||
let amountRefundGranted = Amounts.getZero(
|
||||
download.contractData.amount.currency,
|
||||
);
|
||||
@ -2456,7 +2492,7 @@ export async function applyRefund(
|
||||
const purchase = await ws.db
|
||||
.mktx((x) => [x.purchases])
|
||||
.runReadOnly(async (tx) => {
|
||||
return tx.purchases.indexes.byMerchantUrlAndOrderId.get([
|
||||
return tx.purchases.indexes.byUrlAndOrderId.get([
|
||||
parseResult.merchantBaseUrl,
|
||||
parseResult.orderId,
|
||||
]);
|
||||
@ -2513,8 +2549,8 @@ export async function applyRefundFromPurchaseId(
|
||||
throw Error("purchase no longer exists");
|
||||
}
|
||||
|
||||
const summary = await calculateRefundSummary(purchase);
|
||||
const download = await expectProposalDownload(purchase);
|
||||
const summary = await calculateRefundSummary(ws, purchase);
|
||||
const download = await expectProposalDownload(ws, purchase);
|
||||
|
||||
return {
|
||||
contractTermsHash: download.contractData.contractTermsHash,
|
||||
@ -2542,7 +2578,7 @@ async function queryAndSaveAwaitingRefund(
|
||||
purchase: PurchaseRecord,
|
||||
waitForAutoRefund?: boolean,
|
||||
): Promise<AmountJson> {
|
||||
const download = await expectProposalDownload(purchase);
|
||||
const download = await expectProposalDownload(ws, purchase);
|
||||
const requestUrl = new URL(
|
||||
`orders/${download.contractData.orderId}`,
|
||||
download.contractData.merchantBaseUrl,
|
||||
@ -2621,7 +2657,7 @@ export async function processPurchaseQueryRefund(
|
||||
return OperationAttemptResult.finishedEmpty();
|
||||
}
|
||||
|
||||
const download = await expectProposalDownload(purchase);
|
||||
const download = await expectProposalDownload(ws, purchase);
|
||||
|
||||
if (purchase.timestampFirstSuccessfulPay) {
|
||||
if (
|
||||
|
@ -48,6 +48,7 @@ import {
|
||||
WalletRefundItem,
|
||||
WithdrawalGroupRecord,
|
||||
WithdrawalRecordType,
|
||||
WalletContractData,
|
||||
} from "../db.js";
|
||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||
import { checkDbInvariant } from "../util/invariants.js";
|
||||
@ -55,7 +56,11 @@ import { RetryTags } from "../util/retries.js";
|
||||
import { makeEventId, TombstoneTag } from "./common.js";
|
||||
import { processDepositGroup } from "./deposits.js";
|
||||
import { getExchangeDetails } from "./exchanges.js";
|
||||
import { expectProposalDownload, processPurchasePay } from "./pay-merchant.js";
|
||||
import {
|
||||
expectProposalDownload,
|
||||
extractContractData,
|
||||
processPurchasePay,
|
||||
} from "./pay-merchant.js";
|
||||
import { processRefreshGroup } from "./refresh.js";
|
||||
import { processTip } from "./tip.js";
|
||||
import {
|
||||
@ -199,7 +204,7 @@ export async function getTransactionById(
|
||||
}),
|
||||
);
|
||||
|
||||
const download = await expectProposalDownload(purchase);
|
||||
const download = await expectProposalDownload(ws, purchase);
|
||||
|
||||
const cleanRefunds = filteredRefunds.filter(
|
||||
(x): x is WalletRefundItem => !!x,
|
||||
@ -214,7 +219,12 @@ export async function getTransactionById(
|
||||
const payOpId = RetryTags.forPay(purchase);
|
||||
const payRetryRecord = await tx.operationRetries.get(payOpId);
|
||||
|
||||
return buildTransactionForPurchase(purchase, refunds, payRetryRecord);
|
||||
return buildTransactionForPurchase(
|
||||
purchase,
|
||||
contractData,
|
||||
refunds,
|
||||
payRetryRecord,
|
||||
);
|
||||
});
|
||||
} else if (type === TransactionType.Refresh) {
|
||||
const refreshGroupId = rest[0];
|
||||
@ -268,14 +278,19 @@ export async function getTransactionById(
|
||||
),
|
||||
);
|
||||
if (t) throw Error("deleted");
|
||||
const download = await expectProposalDownload(purchase);
|
||||
const download = await expectProposalDownload(ws, purchase);
|
||||
const contractData = download.contractData;
|
||||
const refunds = mergeRefundByExecutionTime(
|
||||
[theRefund],
|
||||
Amounts.getZero(contractData.amount.currency),
|
||||
);
|
||||
|
||||
return buildTransactionForRefund(purchase, refunds[0], undefined);
|
||||
return buildTransactionForRefund(
|
||||
purchase,
|
||||
contractData,
|
||||
refunds[0],
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
} else if (type === TransactionType.PeerPullDebit) {
|
||||
const peerPullPaymentIncomingId = rest[0];
|
||||
@ -572,12 +587,10 @@ function mergeRefundByExecutionTime(
|
||||
|
||||
async function buildTransactionForRefund(
|
||||
purchaseRecord: PurchaseRecord,
|
||||
contractData: WalletContractData,
|
||||
refundInfo: MergedRefundInfo,
|
||||
ort?: OperationRetryRecord,
|
||||
): Promise<Transaction> {
|
||||
const download = await expectProposalDownload(purchaseRecord);
|
||||
const contractData = download.contractData;
|
||||
|
||||
const info: OrderShortInfo = {
|
||||
merchant: contractData.merchant,
|
||||
orderId: contractData.orderId,
|
||||
@ -617,11 +630,10 @@ async function buildTransactionForRefund(
|
||||
|
||||
async function buildTransactionForPurchase(
|
||||
purchaseRecord: PurchaseRecord,
|
||||
contractData: WalletContractData,
|
||||
refundsInfo: MergedRefundInfo[],
|
||||
ort?: OperationRetryRecord,
|
||||
): Promise<Transaction> {
|
||||
const download = await expectProposalDownload(purchaseRecord);
|
||||
const contractData = download.contractData;
|
||||
const zero = Amounts.getZero(contractData.amount.currency);
|
||||
|
||||
const info: OrderShortInfo = {
|
||||
@ -689,7 +701,8 @@ async function buildTransactionForPurchase(
|
||||
proposalId: purchaseRecord.proposalId,
|
||||
info,
|
||||
frozen:
|
||||
purchaseRecord.purchaseStatus === PurchaseStatus.PaymentAbortFinished ?? false,
|
||||
purchaseRecord.purchaseStatus === PurchaseStatus.PaymentAbortFinished ??
|
||||
false,
|
||||
...(ort?.lastError ? { error: ort.lastError } : {}),
|
||||
};
|
||||
}
|
||||
@ -715,6 +728,7 @@ export async function getTransactions(
|
||||
x.peerPushPaymentInitiations,
|
||||
x.planchets,
|
||||
x.purchases,
|
||||
x.contractTerms,
|
||||
x.recoupGroups,
|
||||
x.tips,
|
||||
x.tombstones,
|
||||
@ -814,18 +828,28 @@ export async function getTransactions(
|
||||
if (!purchase.payInfo) {
|
||||
return;
|
||||
}
|
||||
if (shouldSkipCurrency(transactionsRequest, download.currency)) {
|
||||
return;
|
||||
}
|
||||
const contractTermsRecord = await tx.contractTerms.get(
|
||||
download.contractTermsHash,
|
||||
);
|
||||
if (!contractTermsRecord) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
shouldSkipCurrency(
|
||||
transactionsRequest,
|
||||
download.contractData.amount.currency,
|
||||
)
|
||||
shouldSkipSearch(transactionsRequest, [
|
||||
contractTermsRecord?.contractTermsRaw?.summary || "",
|
||||
])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const contractData = download.contractData;
|
||||
if (shouldSkipSearch(transactionsRequest, [contractData.summary])) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contractData = extractContractData(
|
||||
contractTermsRecord?.contractTermsRaw,
|
||||
download.contractTermsHash,
|
||||
download.contractTermsMerchantSig,
|
||||
);
|
||||
|
||||
const filteredRefunds = await Promise.all(
|
||||
Object.values(purchase.refunds).map(async (r) => {
|
||||
@ -847,19 +871,29 @@ export async function getTransactions(
|
||||
|
||||
const refunds = mergeRefundByExecutionTime(
|
||||
cleanRefunds,
|
||||
Amounts.getZero(contractData.amount.currency),
|
||||
Amounts.getZero(download.currency),
|
||||
);
|
||||
|
||||
refunds.forEach(async (refundInfo) => {
|
||||
transactions.push(
|
||||
await buildTransactionForRefund(purchase, refundInfo, undefined),
|
||||
await buildTransactionForRefund(
|
||||
purchase,
|
||||
contractData,
|
||||
refundInfo,
|
||||
undefined,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
const payOpId = RetryTags.forPay(purchase);
|
||||
const payRetryRecord = await tx.operationRetries.get(payOpId);
|
||||
transactions.push(
|
||||
await buildTransactionForPurchase(purchase, refunds, payRetryRecord),
|
||||
await buildTransactionForPurchase(
|
||||
purchase,
|
||||
contractData,
|
||||
refunds,
|
||||
payRetryRecord,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user