aboutsummaryrefslogtreecommitdiff
path: root/src/operations
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2020-05-12 14:08:58 +0530
committerFlorian Dold <florian.dold@gmail.com>2020-05-12 14:09:10 +0530
commit6206b418ff88a238762a18e7b6eeaceafc5de294 (patch)
treec4d947770ccf50e362abf083953f55140046fc2a /src/operations
parent857a2b9dcaf64d4298027644f8e6716fa22db941 (diff)
downloadwallet-core-6206b418ff88a238762a18e7b6eeaceafc5de294.tar.xz
new transactions API: withdrawal
Diffstat (limited to 'src/operations')
-rw-r--r--src/operations/history.ts4
-rw-r--r--src/operations/pending.ts2
-rw-r--r--src/operations/reserves.ts96
-rw-r--r--src/operations/transactions.ts130
4 files changed, 189 insertions, 43 deletions
diff --git a/src/operations/history.ts b/src/operations/history.ts
index 1271c56ef..4e43596f0 100644
--- a/src/operations/history.ts
+++ b/src/operations/history.ts
@@ -375,10 +375,10 @@ export async function getHistory(
return;
}
let reserveCreationDetail: ReserveCreationDetail;
- if (reserve.bankWithdrawStatusUrl) {
+ if (reserve.bankInfo) {
reserveCreationDetail = {
type: ReserveType.TalerBankWithdraw,
- bankUrl: reserve.bankWithdrawStatusUrl,
+ bankUrl: reserve.bankInfo.statusUrl,
};
} else {
reserveCreationDetail = {
diff --git a/src/operations/pending.ts b/src/operations/pending.ts
index 14072633c..c793f5f0a 100644
--- a/src/operations/pending.ts
+++ b/src/operations/pending.ts
@@ -150,7 +150,7 @@ async function gatherReservePending(
): Promise<void> {
// FIXME: this should be optimized by using an index for "onlyDue==true".
await tx.iter(Stores.reserves).forEach((reserve) => {
- const reserveType = reserve.bankWithdrawStatusUrl
+ const reserveType = reserve.bankInfo
? ReserveType.TalerBankWithdraw
: ReserveType.Manual;
if (!reserve.retryInfo.active) {
diff --git a/src/operations/reserves.ts b/src/operations/reserves.ts
index 2bbb085d5..347f6e894 100644
--- a/src/operations/reserves.ts
+++ b/src/operations/reserves.ts
@@ -108,7 +108,14 @@ export async function createReserve(
senderWire: req.senderWire,
timestampConfirmed: undefined,
timestampReserveInfoPosted: undefined,
- bankWithdrawStatusUrl: req.bankWithdrawStatusUrl,
+ bankInfo: req.bankWithdrawStatusUrl
+ ? {
+ statusUrl: req.bankWithdrawStatusUrl,
+ amount: req.amount,
+ bankWithdrawalGroupId: encodeCrock(getRandomBytes(32)),
+ withdrawalStarted: false,
+ }
+ : undefined,
exchangeWire: req.exchangeWire,
reserveStatus,
lastSuccessfulStatusQuery: undefined,
@@ -173,10 +180,10 @@ export async function createReserve(
],
async (tx) => {
// Check if we have already created a reserve for that bankWithdrawStatusUrl
- if (reserveRecord.bankWithdrawStatusUrl) {
+ if (reserveRecord.bankInfo?.statusUrl) {
const bwi = await tx.get(
Stores.bankWithdrawUris,
- reserveRecord.bankWithdrawStatusUrl,
+ reserveRecord.bankInfo.statusUrl,
);
if (bwi) {
const otherReserve = await tx.get(Stores.reserves, bwi.reservePub);
@@ -192,7 +199,7 @@ export async function createReserve(
}
await tx.put(Stores.bankWithdrawUris, {
reservePub: reserveRecord.reservePub,
- talerWithdrawUri: reserveRecord.bankWithdrawStatusUrl,
+ talerWithdrawUri: reserveRecord.bankInfo.statusUrl,
});
}
await tx.put(Stores.currencies, cr);
@@ -279,7 +286,7 @@ async function registerReserveWithBank(
default:
return;
}
- const bankStatusUrl = reserve.bankWithdrawStatusUrl;
+ const bankStatusUrl = reserve.bankInfo?.statusUrl;
if (!bankStatusUrl) {
return;
}
@@ -333,7 +340,7 @@ async function processReserveBankStatusImpl(
default:
return;
}
- const bankStatusUrl = reserve.bankWithdrawStatusUrl;
+ const bankStatusUrl = reserve.bankInfo?.statusUrl;
if (!bankStatusUrl) {
return;
}
@@ -382,7 +389,9 @@ async function processReserveBankStatusImpl(
default:
return;
}
- r.bankWithdrawConfirmUrl = status.confirm_transfer_url;
+ if (r.bankInfo) {
+ r.bankInfo.confirmUrl = status.confirm_transfer_url;
+ }
return r;
});
await incrementReserveRetry(ws, reservePub, undefined);
@@ -673,35 +682,7 @@ async function depleteReserve(
logger.trace("selected denominations");
- const withdrawalGroupId = encodeCrock(randomBytes(32));
-
- logger.trace("created plachets");
-
- const withdrawalRecord: WithdrawalGroupRecord = {
- withdrawalGroupId: withdrawalGroupId,
- exchangeBaseUrl: reserve.exchangeBaseUrl,
- source: {
- type: WithdrawalSourceType.Reserve,
- reservePub: reserve.reservePub,
- },
- rawWithdrawalAmount: withdrawAmount,
- timestampStart: getTimestampNow(),
- retryInfo: initRetryInfo(),
- lastErrorPerCoin: {},
- lastError: undefined,
- denomsSel: {
- totalCoinValue: denomsForWithdraw.totalCoinValue,
- totalWithdrawCost: denomsForWithdraw.totalWithdrawCost,
- selectedDenoms: denomsForWithdraw.selectedDenoms.map((x) => {
- return {
- count: x.count,
- denomPubHash: x.denom.denomPubHash,
- };
- }),
- },
- };
-
- const success = await ws.db.runWithWriteTransaction(
+ const newWithdrawalGroup = await ws.db.runWithWriteTransaction(
[
Stores.withdrawalGroups,
Stores.reserves,
@@ -748,20 +729,55 @@ async function depleteReserve(
}
newReserve.reserveStatus = ReserveRecordStatus.DORMANT;
newReserve.retryInfo = initRetryInfo(false);
+
+ let withdrawalGroupId: string;
+
+ const bankInfo = newReserve.bankInfo;
+ if (bankInfo && !bankInfo.withdrawalStarted) {
+ withdrawalGroupId = bankInfo.bankWithdrawalGroupId;
+ bankInfo.withdrawalStarted = true;
+ } else {
+ withdrawalGroupId = encodeCrock(randomBytes(32));
+ }
+
+ const withdrawalRecord: WithdrawalGroupRecord = {
+ withdrawalGroupId: withdrawalGroupId,
+ exchangeBaseUrl: newReserve.exchangeBaseUrl,
+ source: {
+ type: WithdrawalSourceType.Reserve,
+ reservePub: newReserve.reservePub,
+ },
+ rawWithdrawalAmount: withdrawAmount,
+ timestampStart: getTimestampNow(),
+ retryInfo: initRetryInfo(),
+ lastErrorPerCoin: {},
+ lastError: undefined,
+ denomsSel: {
+ 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.reserveHistory, newHist);
await tx.put(Stores.withdrawalGroups, withdrawalRecord);
- return true;
+ return withdrawalRecord;
},
);
- if (success) {
+ if (newWithdrawalGroup) {
console.log("processing new withdraw group");
ws.notify({
type: NotificationType.WithdrawGroupCreated,
- withdrawalGroupId: withdrawalGroupId,
+ withdrawalGroupId: newWithdrawalGroup.withdrawalGroupId,
});
- await processWithdrawGroup(ws, withdrawalGroupId);
+ await processWithdrawGroup(ws, newWithdrawalGroup.withdrawalGroupId);
} else {
console.trace("withdraw session already existed");
}
diff --git a/src/operations/transactions.ts b/src/operations/transactions.ts
new file mode 100644
index 000000000..8333b66c6
--- /dev/null
+++ b/src/operations/transactions.ts
@@ -0,0 +1,130 @@
+/*
+ This file is part of GNU Taler
+ (C) 2019 Taler Systems S.A.
+
+ 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
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import { InternalWalletState } from "./state";
+import { Stores, ProposalRecord, ReserveRecordStatus } from "../types/dbTypes";
+import { Amounts } from "../util/amounts";
+import { timestampCmp } from "../util/time";
+import {
+ TransactionsRequest,
+ TransactionsResponse,
+ Transaction,
+ TransactionType,
+} from "../types/transactions";
+import { OrderShortInfo } from "../types/history";
+
+/**
+ * Create an event ID from the type and the primary key for the event.
+ */
+function makeEventId(type: TransactionType, ...args: string[]): string {
+ return type + ";" + args.map((x) => encodeURIComponent(x)).join(";");
+}
+
+function getOrderShortInfo(
+ proposal: ProposalRecord,
+): OrderShortInfo | undefined {
+ const download = proposal.download;
+ if (!download) {
+ return undefined;
+ }
+ return {
+ amount: Amounts.stringify(download.contractData.amount),
+ fulfillmentUrl: download.contractData.fulfillmentUrl,
+ orderId: download.contractData.orderId,
+ merchantBaseUrl: download.contractData.merchantBaseUrl,
+ proposalId: proposal.proposalId,
+ summary: download.contractData.summary,
+ };
+}
+
+/**
+ * Retrive the full event history for this wallet.
+ */
+export async function getTransactions(
+ ws: InternalWalletState,
+ transactionsRequest?: TransactionsRequest,
+): Promise<TransactionsResponse> {
+ const transactions: Transaction[] = [];
+
+ await ws.db.runWithReadTransaction(
+ [
+ Stores.currencies,
+ Stores.coins,
+ Stores.denominations,
+ Stores.proposals,
+ Stores.purchases,
+ Stores.refreshGroups,
+ Stores.reserves,
+ Stores.reserveHistory,
+ Stores.tips,
+ Stores.withdrawalGroups,
+ Stores.payEvents,
+ Stores.planchets,
+ Stores.refundEvents,
+ Stores.reserveUpdatedEvents,
+ Stores.recoupGroups,
+ ],
+ async (tx) => {
+ tx.iter(Stores.withdrawalGroups).forEach((wsr) => {
+ if (wsr.timestampFinish) {
+ transactions.push({
+ type: TransactionType.Withdrawal,
+ amountEffective: Amounts.stringify(wsr.denomsSel.totalWithdrawCost),
+ amountRaw: Amounts.stringify(wsr.denomsSel.totalCoinValue),
+ confirmed: true,
+ exchangeBaseUrl: wsr.exchangeBaseUrl,
+ pending: !wsr.timestampFinish,
+ timestamp: wsr.timestampStart,
+ transactionId: makeEventId(
+ TransactionType.Withdrawal,
+ wsr.withdrawalGroupId,
+ ),
+ });
+ }
+ });
+
+ tx.iter(Stores.reserves).forEach((r) => {
+ if (r.reserveStatus !== ReserveRecordStatus.WAIT_CONFIRM_BANK) {
+ return;
+ }
+ if (!r.bankInfo) {
+ return;
+ }
+ transactions.push({
+ type: TransactionType.Withdrawal,
+ confirmed: false,
+ amountRaw: Amounts.stringify(r.bankInfo.amount),
+ amountEffective: undefined,
+ exchangeBaseUrl: undefined,
+ pending: true,
+ timestamp: r.timestampCreated,
+ bankConfirmationUrl: r.bankInfo.confirmUrl,
+ transactionId: makeEventId(
+ TransactionType.Withdrawal,
+ r.bankInfo.bankWithdrawalGroupId,
+ ),
+ });
+ });
+ },
+ );
+
+ transactions.sort((h1, h2) => timestampCmp(h1.timestamp, h2.timestamp));
+
+ return { transactions };
+}