show offered payments

This commit is contained in:
Florian Dold 2020-05-12 16:19:40 +05:30
parent ee7141e28b
commit 3007419c98
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
2 changed files with 85 additions and 19 deletions

View File

@ -18,7 +18,7 @@
* Imports. * Imports.
*/ */
import { InternalWalletState } from "./state"; 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 { Amounts, AmountJson } from "../util/amounts";
import { timestampCmp } from "../util/time"; import { timestampCmp } from "../util/time";
import { import {
@ -26,8 +26,8 @@ import {
TransactionsResponse, TransactionsResponse,
Transaction, Transaction,
TransactionType, TransactionType,
PaymentStatus,
} from "../types/transactions"; } from "../types/transactions";
import { getTotalPaymentCost } from "./pay";
/** /**
* Create an event ID from the type and the primary key for the event. * 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(";"); return type + ";" + args.map((x) => encodeURIComponent(x)).join(";");
} }
interface RefundStats { interface RefundStats {
amountInvalid: AmountJson; amountInvalid: AmountJson;
amountEffective: AmountJson; amountEffective: AmountJson;
amountRaw: 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 amountEffective = Amounts.getZero(pr.contractData.amount.currency);
let amountInvalid = Amounts.getZero(pr.contractData.amount.currency); let amountInvalid = Amounts.getZero(pr.contractData.amount.currency);
let amountRaw = 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) { if (pr.refundsDone[rk].refundGroupId !== refundGroupId) {
continue; continue;
} }
amountEffective = Amounts.add(amountEffective, Amounts.parseOrThrow(perm.refund_amount)).amount; amountEffective = Amounts.add(
amountRaw = Amounts.add(amountRaw, Amounts.parseOrThrow(perm.refund_amount)).amount; 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)) { 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) { if (pr.refundsDone[rk].refundGroupId !== refundGroupId) {
continue; 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)) { 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) { if (pr.refundsDone[rk].refundGroupId !== refundGroupId) {
continue; continue;
} }
amountInvalid = Amounts.add(amountInvalid, Amounts.parseOrThrow(perm.refund_fee)).amount; amountInvalid = Amounts.add(
amountInvalid,
Amounts.parseOrThrow(perm.refund_fee),
).amount;
} }
return { return {
amountEffective, amountEffective,
amountInvalid, amountInvalid,
amountRaw, 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) => { tx.iter(Stores.purchases).forEachAsync(async (pr) => {
if ( if (
transactionsRequest?.currency && transactionsRequest?.currency &&
@ -180,7 +223,9 @@ export async function getTransactions(
type: TransactionType.Payment, type: TransactionType.Payment,
amountRaw: Amounts.stringify(pr.contractData.amount), amountRaw: Amounts.stringify(pr.contractData.amount),
amountEffective: Amounts.stringify(pr.payCostInfo.totalCost), amountEffective: Amounts.stringify(pr.payCostInfo.totalCost),
failed: false, status: pr.timestampFirstSuccessfulPay
? PaymentStatus.Paid
: PaymentStatus.Accepted,
pending: !pr.timestampFirstSuccessfulPay, pending: !pr.timestampFirstSuccessfulPay,
timestamp: pr.timestampAccept, timestamp: pr.timestampAccept,
transactionId: makeEventId(TransactionType.Payment, pr.proposalId), transactionId: makeEventId(TransactionType.Payment, pr.proposalId),
@ -211,12 +256,17 @@ export async function getTransactions(
summary_i18n: {}, summary_i18n: {},
}, },
timestamp: rg.timestampQueried, timestamp: rg.timestampQueried,
transactionId: makeEventId(TransactionType.Refund, `{rg.timestampQueried.t_ms}`), transactionId: makeEventId(
refundedTransactionId: makeEventId(TransactionType.Payment, pr.proposalId), TransactionType.Refund,
`{rg.timestampQueried.t_ms}`,
),
refundedTransactionId: makeEventId(
TransactionType.Payment,
pr.proposalId,
),
amountEffective: Amounts.stringify(stats.amountEffective), amountEffective: Amounts.stringify(stats.amountEffective),
amountInvalid: Amounts.stringify(stats.amountInvalid), amountInvalid: Amounts.stringify(stats.amountInvalid),
amountRaw: Amounts.stringify(stats.amountRaw), amountRaw: Amounts.stringify(stats.amountRaw),
}); });
} }
}); });

View File

@ -111,15 +111,31 @@ interface TransactionWithdrawal extends TransactionCommon {
amountEffective?: AmountString; 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; type: TransactionType.Payment;
// Additional information about the payment. // Additional information about the payment.
info: PaymentShortInfo; info: PaymentShortInfo;
// true if the payment failed, false otherwise. status: PaymentStatus;
// Note that failed payments with zero effective amount will not be returned by the API.
failed: boolean;
// Amount that must be paid for the contract // Amount that must be paid for the contract
amountRaw: AmountString; amountRaw: AmountString;