address first batch of transaction list issues
This commit is contained in:
parent
35c83414f9
commit
3eb88574bc
@ -7,7 +7,7 @@ import { openDatabase, Database, Store, Index } from "./util/query";
|
|||||||
* with each major change. When incrementing the major version,
|
* with each major change. When incrementing the major version,
|
||||||
* the wallet should import data from the previous version.
|
* the wallet should import data from the previous version.
|
||||||
*/
|
*/
|
||||||
const TALER_DB_NAME = "taler-walletdb-v3";
|
const TALER_DB_NAME = "taler-walletdb-v4";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current database minor version, should be incremented
|
* Current database minor version, should be incremented
|
||||||
|
@ -137,11 +137,7 @@ export async function getTotalPaymentCost(
|
|||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
pcs: PayCoinSelection,
|
pcs: PayCoinSelection,
|
||||||
): Promise<PayCostInfo> {
|
): Promise<PayCostInfo> {
|
||||||
const costs = [
|
const costs = [];
|
||||||
pcs.paymentAmount,
|
|
||||||
pcs.customerDepositFees,
|
|
||||||
pcs.customerWireFees,
|
|
||||||
];
|
|
||||||
for (let i = 0; i < pcs.coinPubs.length; i++) {
|
for (let i = 0; i < pcs.coinPubs.length; i++) {
|
||||||
const coin = await ws.db.get(Stores.coins, pcs.coinPubs[i]);
|
const coin = await ws.db.get(Stores.coins, pcs.coinPubs[i]);
|
||||||
if (!coin) {
|
if (!coin) {
|
||||||
@ -165,6 +161,7 @@ export async function getTotalPaymentCost(
|
|||||||
const amountLeft = Amounts.sub(denom.value, pcs.coinContributions[i])
|
const amountLeft = Amounts.sub(denom.value, pcs.coinContributions[i])
|
||||||
.amount;
|
.amount;
|
||||||
const refreshCost = getTotalRefreshCost(allDenoms, denom, amountLeft);
|
const refreshCost = getTotalRefreshCost(allDenoms, denom, amountLeft);
|
||||||
|
costs.push(pcs.coinContributions[i]);
|
||||||
costs.push(refreshCost);
|
costs.push(refreshCost);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@ -670,6 +667,9 @@ async function processDownloadProposalImpl(
|
|||||||
wireMethod: parsedContractTerms.wire_method,
|
wireMethod: parsedContractTerms.wire_method,
|
||||||
wireInfoHash: parsedContractTerms.h_wire,
|
wireInfoHash: parsedContractTerms.h_wire,
|
||||||
maxDepositFee: Amounts.parseOrThrow(parsedContractTerms.max_fee),
|
maxDepositFee: Amounts.parseOrThrow(parsedContractTerms.max_fee),
|
||||||
|
merchant: parsedContractTerms.merchant,
|
||||||
|
products: parsedContractTerms.products,
|
||||||
|
summaryI18n: parsedContractTerms.summary_i18n,
|
||||||
},
|
},
|
||||||
contractTermsRaw: JSON.stringify(proposalResp.contract_terms),
|
contractTermsRaw: JSON.stringify(proposalResp.contract_terms),
|
||||||
};
|
};
|
||||||
|
@ -35,6 +35,7 @@ import {
|
|||||||
WalletReserveHistoryItemType,
|
WalletReserveHistoryItemType,
|
||||||
WithdrawalSourceType,
|
WithdrawalSourceType,
|
||||||
ReserveHistoryRecord,
|
ReserveHistoryRecord,
|
||||||
|
ReserveBankInfo,
|
||||||
} from "../types/dbTypes";
|
} from "../types/dbTypes";
|
||||||
import { Logger } from "../util/logging";
|
import { Logger } from "../util/logging";
|
||||||
import { Amounts } from "../util/amounts";
|
import { Amounts } from "../util/amounts";
|
||||||
@ -48,9 +49,11 @@ import { assertUnreachable } from "../util/assertUnreachable";
|
|||||||
import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto";
|
import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto";
|
||||||
import { randomBytes } from "../crypto/primitives/nacl-fast";
|
import { randomBytes } from "../crypto/primitives/nacl-fast";
|
||||||
import {
|
import {
|
||||||
getVerifiedWithdrawDenomList,
|
selectWithdrawalDenoms,
|
||||||
processWithdrawGroup,
|
processWithdrawGroup,
|
||||||
getBankWithdrawalInfo,
|
getBankWithdrawalInfo,
|
||||||
|
denomSelectionInfoToState,
|
||||||
|
getWithdrawDenomList,
|
||||||
} from "./withdraw";
|
} from "./withdraw";
|
||||||
import {
|
import {
|
||||||
guardOperationException,
|
guardOperationException,
|
||||||
@ -100,6 +103,20 @@ export async function createReserve(
|
|||||||
reserveStatus = ReserveRecordStatus.UNCONFIRMED;
|
reserveStatus = ReserveRecordStatus.UNCONFIRMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let bankInfo: ReserveBankInfo | undefined;
|
||||||
|
|
||||||
|
if (req.bankWithdrawStatusUrl) {
|
||||||
|
const denomSelInfo = await selectWithdrawalDenoms(ws, canonExchange, req.amount);
|
||||||
|
const denomSel = denomSelectionInfoToState(denomSelInfo);
|
||||||
|
bankInfo = {
|
||||||
|
statusUrl: req.bankWithdrawStatusUrl,
|
||||||
|
amount: req.amount,
|
||||||
|
bankWithdrawalGroupId: encodeCrock(getRandomBytes(32)),
|
||||||
|
withdrawalStarted: false,
|
||||||
|
denomSel,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const reserveRecord: ReserveRecord = {
|
const reserveRecord: ReserveRecord = {
|
||||||
timestampCreated: now,
|
timestampCreated: now,
|
||||||
exchangeBaseUrl: canonExchange,
|
exchangeBaseUrl: canonExchange,
|
||||||
@ -108,14 +125,7 @@ export async function createReserve(
|
|||||||
senderWire: req.senderWire,
|
senderWire: req.senderWire,
|
||||||
timestampConfirmed: undefined,
|
timestampConfirmed: undefined,
|
||||||
timestampReserveInfoPosted: undefined,
|
timestampReserveInfoPosted: undefined,
|
||||||
bankInfo: req.bankWithdrawStatusUrl
|
bankInfo,
|
||||||
? {
|
|
||||||
statusUrl: req.bankWithdrawStatusUrl,
|
|
||||||
amount: req.amount,
|
|
||||||
bankWithdrawalGroupId: encodeCrock(getRandomBytes(32)),
|
|
||||||
withdrawalStarted: false,
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
exchangeWire: req.exchangeWire,
|
exchangeWire: req.exchangeWire,
|
||||||
reserveStatus,
|
reserveStatus,
|
||||||
lastSuccessfulStatusQuery: undefined,
|
lastSuccessfulStatusQuery: undefined,
|
||||||
@ -286,10 +296,11 @@ async function registerReserveWithBank(
|
|||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const bankStatusUrl = reserve.bankInfo?.statusUrl;
|
const bankInfo = reserve.bankInfo;
|
||||||
if (!bankStatusUrl) {
|
if (!bankInfo) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const bankStatusUrl = bankInfo.statusUrl;
|
||||||
console.log("making selection");
|
console.log("making selection");
|
||||||
if (reserve.timestampReserveInfoPosted) {
|
if (reserve.timestampReserveInfoPosted) {
|
||||||
throw Error("bank claims that reserve info selection is not done");
|
throw Error("bank claims that reserve info selection is not done");
|
||||||
@ -309,6 +320,9 @@ async function registerReserveWithBank(
|
|||||||
}
|
}
|
||||||
r.timestampReserveInfoPosted = getTimestampNow();
|
r.timestampReserveInfoPosted = getTimestampNow();
|
||||||
r.reserveStatus = ReserveRecordStatus.WAIT_CONFIRM_BANK;
|
r.reserveStatus = ReserveRecordStatus.WAIT_CONFIRM_BANK;
|
||||||
|
if (!r.bankInfo) {
|
||||||
|
throw Error("invariant failed");
|
||||||
|
}
|
||||||
r.retryInfo = initRetryInfo();
|
r.retryInfo = initRetryInfo();
|
||||||
return r;
|
return r;
|
||||||
});
|
});
|
||||||
@ -657,7 +671,7 @@ async function depleteReserve(
|
|||||||
|
|
||||||
logger.trace(`getting denom list`);
|
logger.trace(`getting denom list`);
|
||||||
|
|
||||||
const denomsForWithdraw = await getVerifiedWithdrawDenomList(
|
const denomsForWithdraw = await selectWithdrawalDenoms(
|
||||||
ws,
|
ws,
|
||||||
reserve.exchangeBaseUrl,
|
reserve.exchangeBaseUrl,
|
||||||
withdrawAmount,
|
withdrawAmount,
|
||||||
@ -752,17 +766,8 @@ async function depleteReserve(
|
|||||||
retryInfo: initRetryInfo(),
|
retryInfo: initRetryInfo(),
|
||||||
lastErrorPerCoin: {},
|
lastErrorPerCoin: {},
|
||||||
lastError: undefined,
|
lastError: undefined,
|
||||||
denomsSel: {
|
denomsSel: denomSelectionInfoToState(denomsForWithdraw),
|
||||||
totalCoinValue: denomsForWithdraw.totalCoinValue,
|
};
|
||||||
totalWithdrawCost: denomsForWithdraw.totalWithdrawCost,
|
|
||||||
selectedDenoms: denomsForWithdraw.selectedDenoms.map((x) => {
|
|
||||||
return {
|
|
||||||
count: x.count,
|
|
||||||
denomPubHash: x.denom.denomPubHash,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
await tx.put(Stores.reserves, newReserve);
|
await tx.put(Stores.reserves, newReserve);
|
||||||
await tx.put(Stores.reserveHistory, newHist);
|
await tx.put(Stores.reserveHistory, newHist);
|
||||||
|
@ -34,8 +34,9 @@ import {
|
|||||||
} from "../types/dbTypes";
|
} from "../types/dbTypes";
|
||||||
import {
|
import {
|
||||||
getExchangeWithdrawalInfo,
|
getExchangeWithdrawalInfo,
|
||||||
getVerifiedWithdrawDenomList,
|
selectWithdrawalDenoms,
|
||||||
processWithdrawGroup,
|
processWithdrawGroup,
|
||||||
|
denomSelectionInfoToState,
|
||||||
} from "./withdraw";
|
} from "./withdraw";
|
||||||
import { updateExchangeFromUrl } from "./exchanges";
|
import { updateExchangeFromUrl } from "./exchanges";
|
||||||
import { getRandomBytes, encodeCrock } from "../crypto/talerCrypto";
|
import { getRandomBytes, encodeCrock } from "../crypto/talerCrypto";
|
||||||
@ -81,7 +82,7 @@ export async function getTipStatus(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const tipId = encodeCrock(getRandomBytes(32));
|
const tipId = encodeCrock(getRandomBytes(32));
|
||||||
const selectedDenoms = await getVerifiedWithdrawDenomList(
|
const selectedDenoms = await selectWithdrawalDenoms(
|
||||||
ws,
|
ws,
|
||||||
tipPickupStatus.exchange_url,
|
tipPickupStatus.exchange_url,
|
||||||
amount,
|
amount,
|
||||||
@ -107,16 +108,7 @@ export async function getTipStatus(
|
|||||||
).amount,
|
).amount,
|
||||||
retryInfo: initRetryInfo(),
|
retryInfo: initRetryInfo(),
|
||||||
lastError: undefined,
|
lastError: undefined,
|
||||||
denomsSel: {
|
denomsSel: denomSelectionInfoToState(selectedDenoms),
|
||||||
totalCoinValue: selectedDenoms.totalCoinValue,
|
|
||||||
totalWithdrawCost: selectedDenoms.totalWithdrawCost,
|
|
||||||
selectedDenoms: selectedDenoms.selectedDenoms.map((x) => {
|
|
||||||
return {
|
|
||||||
count: x.count,
|
|
||||||
denomPubHash: x.denom.denomPubHash,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
await ws.db.put(Stores.tips, tipRecord);
|
await ws.db.put(Stores.tips, tipRecord);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { InternalWalletState } from "./state";
|
import { InternalWalletState } from "./state";
|
||||||
import { Stores, ReserveRecordStatus, PurchaseRecord, ProposalStatus } from "../types/dbTypes";
|
import { Stores, ReserveRecordStatus, PurchaseRecord } 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 {
|
||||||
@ -131,10 +131,8 @@ export async function getTransactions(
|
|||||||
if (wsr.timestampFinish) {
|
if (wsr.timestampFinish) {
|
||||||
transactions.push({
|
transactions.push({
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
amountEffective: Amounts.stringify(
|
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
|
||||||
wsr.denomsSel.totalWithdrawCost,
|
amountRaw: Amounts.stringify(wsr.denomsSel.totalWithdrawCost),
|
||||||
),
|
|
||||||
amountRaw: Amounts.stringify(wsr.denomsSel.totalCoinValue),
|
|
||||||
confirmed: true,
|
confirmed: true,
|
||||||
exchangeBaseUrl: wsr.exchangeBaseUrl,
|
exchangeBaseUrl: wsr.exchangeBaseUrl,
|
||||||
pending: !wsr.timestampFinish,
|
pending: !wsr.timestampFinish,
|
||||||
@ -163,9 +161,9 @@ export async function getTransactions(
|
|||||||
transactions.push({
|
transactions.push({
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
confirmed: false,
|
confirmed: false,
|
||||||
amountRaw: Amounts.stringify(r.bankInfo.amount),
|
amountRaw: Amounts.stringify(r.bankInfo.denomSel.totalWithdrawCost),
|
||||||
amountEffective: undefined,
|
amountEffective: Amounts.stringify(r.bankInfo.denomSel.totalCoinValue),
|
||||||
exchangeBaseUrl: undefined,
|
exchangeBaseUrl: r.exchangeBaseUrl,
|
||||||
pending: true,
|
pending: true,
|
||||||
timestamp: r.timestampCreated,
|
timestamp: r.timestampCreated,
|
||||||
bankConfirmationUrl: r.bankInfo.confirmUrl,
|
bankConfirmationUrl: r.bankInfo.confirmUrl,
|
||||||
@ -176,38 +174,6 @@ 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 &&
|
||||||
@ -231,11 +197,11 @@ export async function getTransactions(
|
|||||||
transactionId: makeEventId(TransactionType.Payment, pr.proposalId),
|
transactionId: makeEventId(TransactionType.Payment, pr.proposalId),
|
||||||
info: {
|
info: {
|
||||||
fulfillmentUrl: pr.contractData.fulfillmentUrl,
|
fulfillmentUrl: pr.contractData.fulfillmentUrl,
|
||||||
merchant: {},
|
merchant: pr.contractData.merchant,
|
||||||
orderId: pr.contractData.orderId,
|
orderId: pr.contractData.orderId,
|
||||||
products: [],
|
products: pr.contractData.products,
|
||||||
summary: pr.contractData.summary,
|
summary: pr.contractData.summary,
|
||||||
summary_i18n: {},
|
summary_i18n: pr.contractData.summaryI18n,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -258,7 +224,8 @@ export async function getTransactions(
|
|||||||
timestamp: rg.timestampQueried,
|
timestamp: rg.timestampQueried,
|
||||||
transactionId: makeEventId(
|
transactionId: makeEventId(
|
||||||
TransactionType.Refund,
|
TransactionType.Refund,
|
||||||
`{rg.timestampQueried.t_ms}`,
|
pr.proposalId,
|
||||||
|
`${rg.timestampQueried.t_ms}`,
|
||||||
),
|
),
|
||||||
refundedTransactionId: makeEventId(
|
refundedTransactionId: makeEventId(
|
||||||
TransactionType.Payment,
|
TransactionType.Payment,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of GNU Taler
|
This file is part of GNU Taler
|
||||||
(C) 2019-2029 Taler Systems SA
|
(C) 2019-2020 Taler Systems SA
|
||||||
|
|
||||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU General Public License as published by the Free Software
|
terms of the GNU General Public License as published by the Free Software
|
||||||
@ -27,6 +27,7 @@ import {
|
|||||||
DenominationSelectionInfo,
|
DenominationSelectionInfo,
|
||||||
PlanchetRecord,
|
PlanchetRecord,
|
||||||
WithdrawalSourceType,
|
WithdrawalSourceType,
|
||||||
|
DenomSelectionState,
|
||||||
} from "../types/dbTypes";
|
} from "../types/dbTypes";
|
||||||
import {
|
import {
|
||||||
BankWithdrawDetails,
|
BankWithdrawDetails,
|
||||||
@ -419,6 +420,19 @@ async function processPlanchet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function denomSelectionInfoToState(dsi: DenominationSelectionInfo): DenomSelectionState {
|
||||||
|
return {
|
||||||
|
selectedDenoms: dsi.selectedDenoms.map((x) => {
|
||||||
|
return {
|
||||||
|
count: x.count,
|
||||||
|
denomPubHash: x.denom.denomPubHash
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
totalCoinValue: dsi.totalCoinValue,
|
||||||
|
totalWithdrawCost: dsi.totalWithdrawCost,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of denominations to withdraw from the given exchange for the
|
* Get a list of denominations to withdraw from the given exchange for the
|
||||||
* given amount, making sure that all denominations' signatures are verified.
|
* given amount, making sure that all denominations' signatures are verified.
|
||||||
@ -426,7 +440,7 @@ async function processPlanchet(
|
|||||||
* Writes to the DB in order to record the result from verifying
|
* Writes to the DB in order to record the result from verifying
|
||||||
* denominations.
|
* denominations.
|
||||||
*/
|
*/
|
||||||
export async function getVerifiedWithdrawDenomList(
|
export async function selectWithdrawalDenoms(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
exchangeBaseUrl: string,
|
exchangeBaseUrl: string,
|
||||||
amount: AmountJson,
|
amount: AmountJson,
|
||||||
@ -603,7 +617,7 @@ export async function getExchangeWithdrawalInfo(
|
|||||||
throw Error(`exchange ${exchangeInfo.baseUrl} wire details not available`);
|
throw Error(`exchange ${exchangeInfo.baseUrl} wire details not available`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedDenoms = await getVerifiedWithdrawDenomList(
|
const selectedDenoms = await selectWithdrawalDenoms(
|
||||||
ws,
|
ws,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
amount,
|
amount,
|
||||||
|
@ -31,6 +31,8 @@ import {
|
|||||||
PayReq,
|
PayReq,
|
||||||
TipResponse,
|
TipResponse,
|
||||||
ExchangeSignKeyJson,
|
ExchangeSignKeyJson,
|
||||||
|
MerchantInfo,
|
||||||
|
Product,
|
||||||
} from "./talerTypes";
|
} from "./talerTypes";
|
||||||
|
|
||||||
import { Index, Store } from "../util/query";
|
import { Index, Store } from "../util/query";
|
||||||
@ -216,6 +218,15 @@ export interface ReserveHistoryRecord {
|
|||||||
reserveTransactions: WalletReserveHistoryItem[];
|
reserveTransactions: WalletReserveHistoryItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ReserveBankInfo {
|
||||||
|
statusUrl: string;
|
||||||
|
confirmUrl?: string;
|
||||||
|
amount: AmountJson;
|
||||||
|
bankWithdrawalGroupId: string;
|
||||||
|
withdrawalStarted: boolean;
|
||||||
|
denomSel: DenomSelectionState;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reserve record as stored in the wallet's database.
|
* A reserve record as stored in the wallet's database.
|
||||||
*/
|
*/
|
||||||
@ -278,13 +289,7 @@ export interface ReserveRecord {
|
|||||||
* Extra state for when this is a withdrawal involving
|
* Extra state for when this is a withdrawal involving
|
||||||
* a Taler-integrated bank.
|
* a Taler-integrated bank.
|
||||||
*/
|
*/
|
||||||
bankInfo?: {
|
bankInfo?: ReserveBankInfo;
|
||||||
statusUrl: string;
|
|
||||||
confirmUrl?: string;
|
|
||||||
amount: AmountJson;
|
|
||||||
bankWithdrawalGroupId: string;
|
|
||||||
withdrawalStarted: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
reserveStatus: ReserveRecordStatus;
|
reserveStatus: ReserveRecordStatus;
|
||||||
|
|
||||||
@ -1179,10 +1184,13 @@ export interface AllowedExchangeInfo {
|
|||||||
* processing in the wallet.
|
* processing in the wallet.
|
||||||
*/
|
*/
|
||||||
export interface WalletContractData {
|
export interface WalletContractData {
|
||||||
|
products?: Product[];
|
||||||
|
summaryI18n: { [lang_tag: string]: string } | undefined;
|
||||||
fulfillmentUrl: string;
|
fulfillmentUrl: string;
|
||||||
contractTermsHash: string;
|
contractTermsHash: string;
|
||||||
merchantSig: string;
|
merchantSig: string;
|
||||||
merchantPub: string;
|
merchantPub: string;
|
||||||
|
merchant: MerchantInfo;
|
||||||
amount: AmountJson;
|
amount: AmountJson;
|
||||||
orderId: string;
|
orderId: string;
|
||||||
merchantBaseUrl: string;
|
merchantBaseUrl: string;
|
||||||
|
@ -260,6 +260,55 @@ export class AuditorHandle {
|
|||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MerchantInfo {
|
||||||
|
name: string;
|
||||||
|
jurisdiction: string | undefined;
|
||||||
|
address: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Tax {
|
||||||
|
// the name of the tax
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
// amount paid in tax
|
||||||
|
tax: AmountString;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Product {
|
||||||
|
// merchant-internal identifier for the product.
|
||||||
|
product_id?: string;
|
||||||
|
|
||||||
|
// Human-readable product description.
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
// Map from IETF BCP 47 language tags to localized descriptions
|
||||||
|
description_i18n?: { [lang_tag: string]: string };
|
||||||
|
|
||||||
|
// The number of units of the product to deliver to the customer.
|
||||||
|
quantity?: number;
|
||||||
|
|
||||||
|
// The unit in which the product is measured (liters, kilograms, packages, etc.)
|
||||||
|
unit?: string;
|
||||||
|
|
||||||
|
// The price of the product; this is the total price for quantity times unit of this product.
|
||||||
|
price?: AmountString;
|
||||||
|
|
||||||
|
// An optional base64-encoded product image
|
||||||
|
image?: string;
|
||||||
|
|
||||||
|
// a list of taxes paid by the merchant for this product. Can be empty.
|
||||||
|
taxes?: Tax[];
|
||||||
|
|
||||||
|
// time indicating when this product should be delivered
|
||||||
|
delivery_date?: Timestamp;
|
||||||
|
|
||||||
|
// where to deliver this product. This may be an URL for online delivery
|
||||||
|
// (i.e. 'http://example.com/download' or 'mailto:customer@example.com'),
|
||||||
|
// or a location label defined inside the proposition's 'locations'.
|
||||||
|
// The presence of a colon (':') indicates the use of an URL.
|
||||||
|
delivery_location?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contract terms from a merchant.
|
* Contract terms from a merchant.
|
||||||
*/
|
*/
|
||||||
@ -284,6 +333,8 @@ export class ContractTerms {
|
|||||||
*/
|
*/
|
||||||
summary: string;
|
summary: string;
|
||||||
|
|
||||||
|
summary_i18n?: { [lang_tag: string]: string };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nonce used to ensure freshness.
|
* Nonce used to ensure freshness.
|
||||||
*/
|
*/
|
||||||
@ -317,7 +368,7 @@ export class ContractTerms {
|
|||||||
/**
|
/**
|
||||||
* Information about the merchant.
|
* Information about the merchant.
|
||||||
*/
|
*/
|
||||||
merchant: any;
|
merchant: MerchantInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public key of the merchant.
|
* Public key of the merchant.
|
||||||
@ -332,7 +383,7 @@ export class ContractTerms {
|
|||||||
/**
|
/**
|
||||||
* Products that are sold in this contract.
|
* Products that are sold in this contract.
|
||||||
*/
|
*/
|
||||||
products?: any[];
|
products?: Product[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deadline for refunds.
|
* Deadline for refunds.
|
||||||
@ -805,6 +856,35 @@ export const codecForAuditorHandle = (): Codec<AuditorHandle> =>
|
|||||||
.property("url", codecForString)
|
.property("url", codecForString)
|
||||||
.build("AuditorHandle");
|
.build("AuditorHandle");
|
||||||
|
|
||||||
|
export const codecForMerchantInfo = (): Codec<MerchantInfo> =>
|
||||||
|
makeCodecForObject<MerchantInfo>()
|
||||||
|
.property("name", codecForString)
|
||||||
|
.property("address", makeCodecOptional(codecForString))
|
||||||
|
.property("jurisdiction", makeCodecOptional(codecForString))
|
||||||
|
.build("MerchantInfo");
|
||||||
|
|
||||||
|
export const codecForTax = (): Codec<Tax> =>
|
||||||
|
makeCodecForObject<Tax>()
|
||||||
|
.property("name", codecForString)
|
||||||
|
.property("tax", codecForString)
|
||||||
|
.build("Tax");
|
||||||
|
|
||||||
|
|
||||||
|
export const codecForI18n = (): Codec<{ [lang_tag: string]: string }> =>
|
||||||
|
makeCodecForMap(codecForString)
|
||||||
|
|
||||||
|
export const codecForProduct = (): Codec<Product> =>
|
||||||
|
makeCodecForObject<Product>()
|
||||||
|
.property("product_id", makeCodecOptional(codecForString))
|
||||||
|
.property("description", codecForString)
|
||||||
|
.property("description_i18n", makeCodecOptional(codecForI18n()))
|
||||||
|
.property("quantity", makeCodecOptional(codecForNumber))
|
||||||
|
.property("unit", makeCodecOptional(codecForString))
|
||||||
|
.property("price", makeCodecOptional(codecForString))
|
||||||
|
.property("delivery_date", makeCodecOptional(codecForTimestamp))
|
||||||
|
.property("delivery_location", makeCodecOptional(codecForString))
|
||||||
|
.build("Tax");
|
||||||
|
|
||||||
export const codecForContractTerms = (): Codec<ContractTerms> =>
|
export const codecForContractTerms = (): Codec<ContractTerms> =>
|
||||||
makeCodecForObject<ContractTerms>()
|
makeCodecForObject<ContractTerms>()
|
||||||
.property("order_id", codecForString)
|
.property("order_id", codecForString)
|
||||||
@ -814,6 +894,7 @@ export const codecForContractTerms = (): Codec<ContractTerms> =>
|
|||||||
.property("auto_refund", makeCodecOptional(codecForDuration))
|
.property("auto_refund", makeCodecOptional(codecForDuration))
|
||||||
.property("wire_method", codecForString)
|
.property("wire_method", codecForString)
|
||||||
.property("summary", codecForString)
|
.property("summary", codecForString)
|
||||||
|
.property("summary_i18n", makeCodecOptional(codecForI18n()))
|
||||||
.property("nonce", codecForString)
|
.property("nonce", codecForString)
|
||||||
.property("amount", codecForString)
|
.property("amount", codecForString)
|
||||||
.property("auditors", makeCodecForList(codecForAuditorHandle()))
|
.property("auditors", makeCodecForList(codecForAuditorHandle()))
|
||||||
@ -824,10 +905,10 @@ export const codecForContractTerms = (): Codec<ContractTerms> =>
|
|||||||
.property("locations", codecForAny)
|
.property("locations", codecForAny)
|
||||||
.property("max_fee", codecForString)
|
.property("max_fee", codecForString)
|
||||||
.property("max_wire_fee", makeCodecOptional(codecForString))
|
.property("max_wire_fee", makeCodecOptional(codecForString))
|
||||||
.property("merchant", codecForAny)
|
.property("merchant", codecForMerchantInfo())
|
||||||
.property("merchant_pub", codecForString)
|
.property("merchant_pub", codecForString)
|
||||||
.property("exchanges", makeCodecForList(codecForExchangeHandle()))
|
.property("exchanges", makeCodecForList(codecForExchangeHandle()))
|
||||||
.property("products", makeCodecOptional(makeCodecForList(codecForAny)))
|
.property("products", makeCodecOptional(makeCodecForList(codecForProduct())))
|
||||||
.property("extra", codecForAny)
|
.property("extra", codecForAny)
|
||||||
.build("ContractTerms");
|
.build("ContractTerms");
|
||||||
|
|
||||||
@ -852,10 +933,7 @@ export const codecForMerchantRefundResponse = (): Codec<
|
|||||||
makeCodecForObject<MerchantRefundResponse>()
|
makeCodecForObject<MerchantRefundResponse>()
|
||||||
.property("merchant_pub", codecForString)
|
.property("merchant_pub", codecForString)
|
||||||
.property("h_contract_terms", codecForString)
|
.property("h_contract_terms", codecForString)
|
||||||
.property(
|
.property("refunds", makeCodecForList(codecForMerchantRefundPermission()))
|
||||||
"refunds",
|
|
||||||
makeCodecForList(codecForMerchantRefundPermission()),
|
|
||||||
)
|
|
||||||
.build("MerchantRefundResponse");
|
.build("MerchantRefundResponse");
|
||||||
|
|
||||||
export const codecForReserveSigSingleton = (): Codec<ReserveSigSingleton> =>
|
export const codecForReserveSigSingleton = (): Codec<ReserveSigSingleton> =>
|
||||||
|
@ -16,13 +16,16 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Type and schema definitions for the wallet's transaction list.
|
* Type and schema definitions for the wallet's transaction list.
|
||||||
|
*
|
||||||
|
* @author Florian Dold
|
||||||
|
* @author Torsten Grote
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { Timestamp } from "../util/time";
|
import { Timestamp } from "../util/time";
|
||||||
import { AmountString } from "./talerTypes";
|
import { AmountString, Product } from "./talerTypes";
|
||||||
|
|
||||||
export interface TransactionsRequest {
|
export interface TransactionsRequest {
|
||||||
/**
|
/**
|
||||||
@ -44,6 +47,24 @@ export interface TransactionsResponse {
|
|||||||
transactions: Transaction[];
|
transactions: Transaction[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TransactionError {
|
||||||
|
/**
|
||||||
|
* TALER_EC_* unique error code.
|
||||||
|
* The action(s) offered and message displayed on the transaction item depend on this code.
|
||||||
|
*/
|
||||||
|
ec: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* English-only error hint, if available.
|
||||||
|
*/
|
||||||
|
hint?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error details specific to "ec", if applicable/available
|
||||||
|
*/
|
||||||
|
details?: any;
|
||||||
|
}
|
||||||
|
|
||||||
export interface TransactionCommon {
|
export interface TransactionCommon {
|
||||||
// opaque unique ID for the transaction, used as a starting point for paginating queries
|
// opaque unique ID for the transaction, used as a starting point for paginating queries
|
||||||
// and for invoking actions on the transaction (e.g. deleting/hiding it from the history)
|
// and for invoking actions on the transaction (e.g. deleting/hiding it from the history)
|
||||||
@ -64,16 +85,17 @@ export interface TransactionCommon {
|
|||||||
amountRaw: AmountString;
|
amountRaw: AmountString;
|
||||||
|
|
||||||
// Amount added or removed from the wallet's balance (including all fees and other costs)
|
// Amount added or removed from the wallet's balance (including all fees and other costs)
|
||||||
amountEffective?: AmountString;
|
amountEffective: AmountString;
|
||||||
|
|
||||||
|
error?: TransactionError;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Transaction = (
|
export type Transaction =
|
||||||
TransactionWithdrawal |
|
| TransactionWithdrawal
|
||||||
TransactionPayment |
|
| TransactionPayment
|
||||||
TransactionRefund |
|
| TransactionRefund
|
||||||
TransactionTip |
|
| TransactionTip
|
||||||
TransactionRefresh
|
| TransactionRefresh;
|
||||||
)
|
|
||||||
|
|
||||||
export const enum TransactionType {
|
export const enum TransactionType {
|
||||||
Withdrawal = "withdrawal",
|
Withdrawal = "withdrawal",
|
||||||
@ -93,79 +115,109 @@ interface TransactionWithdrawal extends TransactionCommon {
|
|||||||
*/
|
*/
|
||||||
exchangeBaseUrl?: string;
|
exchangeBaseUrl?: string;
|
||||||
|
|
||||||
// true if the bank has confirmed the withdrawal, false if not.
|
/**
|
||||||
// An unconfirmed withdrawal usually requires user-input and should be highlighted in the UI.
|
* true if the bank has confirmed the withdrawal, false if not.
|
||||||
// See also bankConfirmationUrl below.
|
* An unconfirmed withdrawal usually requires user-input and should be highlighted in the UI.
|
||||||
|
* See also bankConfirmationUrl below.
|
||||||
|
*/
|
||||||
confirmed: boolean;
|
confirmed: boolean;
|
||||||
|
|
||||||
// If the withdrawal is unconfirmed, this can include a URL for user initiated confirmation.
|
/**
|
||||||
|
* If the withdrawal is unconfirmed, this can include a URL for user
|
||||||
|
* initiated confirmation.
|
||||||
|
*/
|
||||||
bankConfirmationUrl?: string;
|
bankConfirmationUrl?: string;
|
||||||
|
|
||||||
// Amount that has been subtracted from the reserve's balance for this withdrawal.
|
/**
|
||||||
|
* Amount that got subtracted from the reserve balance.
|
||||||
|
*/
|
||||||
amountRaw: AmountString;
|
amountRaw: AmountString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Amount that actually was (or will be) added to the wallet's balance.
|
* Amount that actually was (or will be) added to the wallet's balance.
|
||||||
* Only present if an exchange has already been selected.
|
|
||||||
*/
|
*/
|
||||||
amountEffective?: AmountString;
|
amountEffective: AmountString;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum PaymentStatus {
|
export const enum PaymentStatus {
|
||||||
// Explicitly aborted after timeout / failure
|
/**
|
||||||
|
* Explicitly aborted after timeout / failure
|
||||||
|
*/
|
||||||
Aborted = "aborted",
|
Aborted = "aborted",
|
||||||
|
|
||||||
// Payment failed, wallet will auto-retry.
|
/**
|
||||||
// User should be given the option to retry now / abort.
|
* Payment failed, wallet will auto-retry.
|
||||||
|
* User should be given the option to retry now / abort.
|
||||||
|
*/
|
||||||
Failed = "failed",
|
Failed = "failed",
|
||||||
|
|
||||||
// Paid successfully
|
/**
|
||||||
|
* Paid successfully
|
||||||
|
*/
|
||||||
Paid = "paid",
|
Paid = "paid",
|
||||||
|
|
||||||
// Only offered, user must accept / decline
|
/**
|
||||||
Offered = "offered",
|
* User accepted, payment is processing.
|
||||||
|
*/
|
||||||
// User accepted, payment is processing.
|
|
||||||
Accepted = "accepted",
|
Accepted = "accepted",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransactionPayment extends TransactionCommon {
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How far did the wallet get with processing the payment?
|
||||||
|
*/
|
||||||
status: PaymentStatus;
|
status: PaymentStatus;
|
||||||
|
|
||||||
// Amount that must be paid for the contract
|
/**
|
||||||
|
* Amount that must be paid for the contract
|
||||||
|
*/
|
||||||
amountRaw: AmountString;
|
amountRaw: AmountString;
|
||||||
|
|
||||||
// Amount that was paid, including deposit, wire and refresh fees.
|
/**
|
||||||
amountEffective?: AmountString;
|
* Amount that was paid, including deposit, wire and refresh fees.
|
||||||
|
*/
|
||||||
|
amountEffective: AmountString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface PaymentShortInfo {
|
interface PaymentShortInfo {
|
||||||
// Order ID, uniquely identifies the order within a merchant instance
|
/**
|
||||||
|
* Order ID, uniquely identifies the order within a merchant instance
|
||||||
|
*/
|
||||||
orderId: string;
|
orderId: string;
|
||||||
|
|
||||||
// More information about the merchant
|
/**
|
||||||
|
* More information about the merchant
|
||||||
|
*/
|
||||||
merchant: any;
|
merchant: any;
|
||||||
|
|
||||||
// Summary of the order, given by the merchant
|
/**
|
||||||
|
* Summary of the order, given by the merchant
|
||||||
|
*/
|
||||||
summary: string;
|
summary: string;
|
||||||
|
|
||||||
// Map from IETF BCP 47 language tags to localized summaries
|
/**
|
||||||
|
* Map from IETF BCP 47 language tags to localized summaries
|
||||||
|
*/
|
||||||
summary_i18n?: { [lang_tag: string]: string };
|
summary_i18n?: { [lang_tag: string]: string };
|
||||||
|
|
||||||
// List of products that are part of the order
|
/**
|
||||||
products: any[];
|
* List of products that are part of the order
|
||||||
|
*/
|
||||||
|
products: Product[] | undefined;
|
||||||
|
|
||||||
// URL of the fulfillment, given by the merchant
|
/**
|
||||||
|
* URL of the fulfillment, given by the merchant
|
||||||
|
*/
|
||||||
fulfillmentUrl: string;
|
fulfillmentUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface TransactionRefund extends TransactionCommon {
|
interface TransactionRefund extends TransactionCommon {
|
||||||
type: TransactionType.Refund;
|
type: TransactionType.Refund;
|
||||||
|
|
||||||
@ -221,4 +273,4 @@ interface TransactionRefresh extends TransactionCommon {
|
|||||||
|
|
||||||
// Amount that will be paid as fees for the refresh
|
// Amount that will be paid as fees for the refresh
|
||||||
amountEffective: AmountString;
|
amountEffective: AmountString;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user