From 3007419c98c24502c45409d4eea2e933662bc5e6 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 12 May 2020 16:19:40 +0530 Subject: show offered payments --- src/operations/transactions.ts | 80 ++++++++++++++++++++++++++++++++++-------- src/types/transactions.ts | 24 ++++++++++--- 2 files changed, 85 insertions(+), 19 deletions(-) diff --git a/src/operations/transactions.ts b/src/operations/transactions.ts index 5e4de4188..4cc6154b5 100644 --- a/src/operations/transactions.ts +++ b/src/operations/transactions.ts @@ -18,7 +18,7 @@ * Imports. */ import { InternalWalletState } from "./state"; -import { Stores, ReserveRecordStatus, PurchaseRecord } from "../types/dbTypes"; +import { Stores, ReserveRecordStatus, PurchaseRecord, ProposalStatus } from "../types/dbTypes"; import { Amounts, AmountJson } from "../util/amounts"; import { timestampCmp } from "../util/time"; import { @@ -26,8 +26,8 @@ import { TransactionsResponse, Transaction, TransactionType, + PaymentStatus, } from "../types/transactions"; -import { getTotalPaymentCost } from "./pay"; /** * Create an event ID from the type and the primary key for the event. @@ -36,14 +36,16 @@ function makeEventId(type: TransactionType, ...args: string[]): string { return type + ";" + args.map((x) => encodeURIComponent(x)).join(";"); } - interface RefundStats { amountInvalid: AmountJson; amountEffective: AmountJson; amountRaw: AmountJson; } -function getRefundStats(pr: PurchaseRecord, refundGroupId: string): RefundStats { +function getRefundStats( + pr: PurchaseRecord, + refundGroupId: string, +): RefundStats { let amountEffective = Amounts.getZero(pr.contractData.amount.currency); let amountInvalid = Amounts.getZero(pr.contractData.amount.currency); let amountRaw = Amounts.getZero(pr.contractData.amount.currency); @@ -53,8 +55,12 @@ function getRefundStats(pr: PurchaseRecord, refundGroupId: string): RefundStats if (pr.refundsDone[rk].refundGroupId !== refundGroupId) { continue; } - amountEffective = Amounts.add(amountEffective, Amounts.parseOrThrow(perm.refund_amount)).amount; - amountRaw = Amounts.add(amountRaw, Amounts.parseOrThrow(perm.refund_amount)).amount; + amountEffective = Amounts.add( + amountEffective, + Amounts.parseOrThrow(perm.refund_amount), + ).amount; + amountRaw = Amounts.add(amountRaw, Amounts.parseOrThrow(perm.refund_amount)) + .amount; } for (const rk of Object.keys(pr.refundsDone)) { @@ -62,7 +68,10 @@ function getRefundStats(pr: PurchaseRecord, refundGroupId: string): RefundStats if (pr.refundsDone[rk].refundGroupId !== refundGroupId) { continue; } - amountEffective = Amounts.sub(amountEffective, Amounts.parseOrThrow(perm.refund_fee)).amount; + amountEffective = Amounts.sub( + amountEffective, + Amounts.parseOrThrow(perm.refund_fee), + ).amount; } for (const rk of Object.keys(pr.refundsFailed)) { @@ -70,15 +79,17 @@ function getRefundStats(pr: PurchaseRecord, refundGroupId: string): RefundStats if (pr.refundsDone[rk].refundGroupId !== refundGroupId) { continue; } - amountInvalid = Amounts.add(amountInvalid, Amounts.parseOrThrow(perm.refund_fee)).amount; + amountInvalid = Amounts.add( + amountInvalid, + Amounts.parseOrThrow(perm.refund_fee), + ).amount; } return { amountEffective, amountInvalid, amountRaw, - } - + }; } /** @@ -165,6 +176,38 @@ export async function getTransactions( }); }); + tx.iter(Stores.proposals).forEachAsync(async (proposal) => { + if (!proposal.download) { + return; + } + if (proposal.proposalStatus !== ProposalStatus.PROPOSED) { + return; + } + const dl = proposal.download; + const purchase = await tx.get(Stores.purchases, proposal.proposalId); + if (purchase) { + return; + } + + transactions.push({ + type: TransactionType.Payment, + amountRaw: Amounts.stringify(dl.contractData.amount), + amountEffective: undefined, + status: PaymentStatus.Offered, + pending: true, + timestamp: proposal.timestamp, + transactionId: makeEventId(TransactionType.Payment, proposal.proposalId), + info: { + fulfillmentUrl: dl.contractData.fulfillmentUrl, + merchant: {}, + orderId: dl.contractData.orderId, + products: [], + summary: dl.contractData.summary, + summary_i18n: {}, + }, + }); + }); + tx.iter(Stores.purchases).forEachAsync(async (pr) => { if ( transactionsRequest?.currency && @@ -180,7 +223,9 @@ export async function getTransactions( type: TransactionType.Payment, amountRaw: Amounts.stringify(pr.contractData.amount), amountEffective: Amounts.stringify(pr.payCostInfo.totalCost), - failed: false, + status: pr.timestampFirstSuccessfulPay + ? PaymentStatus.Paid + : PaymentStatus.Accepted, pending: !pr.timestampFirstSuccessfulPay, timestamp: pr.timestampAccept, transactionId: makeEventId(TransactionType.Payment, pr.proposalId), @@ -198,7 +243,7 @@ export async function getTransactions( const pending = Object.keys(pr.refundsDone).length > 0; const stats = getRefundStats(pr, rg.refundGroupId); - + transactions.push({ type: TransactionType.Refund, pending, @@ -211,12 +256,17 @@ export async function getTransactions( summary_i18n: {}, }, timestamp: rg.timestampQueried, - transactionId: makeEventId(TransactionType.Refund, `{rg.timestampQueried.t_ms}`), - refundedTransactionId: makeEventId(TransactionType.Payment, pr.proposalId), + transactionId: makeEventId( + TransactionType.Refund, + `{rg.timestampQueried.t_ms}`, + ), + refundedTransactionId: makeEventId( + TransactionType.Payment, + pr.proposalId, + ), amountEffective: Amounts.stringify(stats.amountEffective), amountInvalid: Amounts.stringify(stats.amountInvalid), amountRaw: Amounts.stringify(stats.amountRaw), - }); } }); diff --git a/src/types/transactions.ts b/src/types/transactions.ts index 7dda46f73..263e08a4e 100644 --- a/src/types/transactions.ts +++ b/src/types/transactions.ts @@ -111,15 +111,31 @@ interface TransactionWithdrawal extends TransactionCommon { amountEffective?: AmountString; } -interface TransactionPayment extends TransactionCommon { +export const enum PaymentStatus { + // Explicitly aborted after timeout / failure + Aborted = "aborted", + + // Payment failed, wallet will auto-retry. + // User should be given the option to retry now / abort. + Failed = "failed", + + // Paid successfully + Paid = "paid", + + // Only offered, user must accept / decline + Offered = "offered", + + // User accepted, payment is processing. + Accepted = "accepted", +} + +export interface TransactionPayment extends TransactionCommon { type: TransactionType.Payment; // Additional information about the payment. info: PaymentShortInfo; - // true if the payment failed, false otherwise. - // Note that failed payments with zero effective amount will not be returned by the API. - failed: boolean; + status: PaymentStatus; // Amount that must be paid for the contract amountRaw: AmountString; -- cgit v1.2.3