pending ops / history / notification tweaks
This commit is contained in:
parent
d8170c8159
commit
4159367d8c
@ -31,6 +31,8 @@ import { HttpRequestLibrary, HttpResponse } from "../util/http";
|
|||||||
// @ts-ignore: special built-in module
|
// @ts-ignore: special built-in module
|
||||||
//import akono = require("akono");
|
//import akono = require("akono");
|
||||||
|
|
||||||
|
export { handleWorkerError, handleWorkerMessage } from "../crypto/workers/nodeThreadWorker";
|
||||||
|
|
||||||
export class AndroidHttpLib implements HttpRequestLibrary {
|
export class AndroidHttpLib implements HttpRequestLibrary {
|
||||||
useNfcTunnel: boolean = false;
|
useNfcTunnel: boolean = false;
|
||||||
|
|
||||||
|
@ -1003,10 +1003,7 @@ export interface PurchaseRecord {
|
|||||||
*/
|
*/
|
||||||
merchantSig: string;
|
merchantSig: string;
|
||||||
|
|
||||||
/**
|
firstSuccessfulPayTimestamp: Timestamp | undefined;
|
||||||
* A successful payment has been made.
|
|
||||||
*/
|
|
||||||
payFinished: boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pending refunds for the purchase.
|
* Pending refunds for the purchase.
|
||||||
|
@ -32,7 +32,8 @@ import { Bank } from "./bank";
|
|||||||
import fs = require("fs");
|
import fs = require("fs");
|
||||||
import { Logger } from "../util/logging";
|
import { Logger } from "../util/logging";
|
||||||
import { NodeThreadCryptoWorkerFactory } from "../crypto/workers/nodeThreadWorker";
|
import { NodeThreadCryptoWorkerFactory } from "../crypto/workers/nodeThreadWorker";
|
||||||
import { NotificationType } from "../walletTypes";
|
import { NotificationType, WalletNotification } from "../walletTypes";
|
||||||
|
import { SynchronousCryptoWorkerFactory } from "../crypto/workers/synchronousWorker";
|
||||||
|
|
||||||
const logger = new Logger("helpers.ts");
|
const logger = new Logger("helpers.ts");
|
||||||
|
|
||||||
@ -87,7 +88,7 @@ export interface DefaultNodeWalletArgs {
|
|||||||
/**
|
/**
|
||||||
* Handler for asynchronous notifications from the wallet.
|
* Handler for asynchronous notifications from the wallet.
|
||||||
*/
|
*/
|
||||||
notifyHandler?: (reason: string) => void;
|
notifyHandler?: (n: WalletNotification) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If specified, use this as HTTP request library instead
|
* If specified, use this as HTTP request library instead
|
||||||
@ -163,11 +164,15 @@ export async function getDefaultNodeWallet(
|
|||||||
|
|
||||||
const worker = new NodeThreadCryptoWorkerFactory();
|
const worker = new NodeThreadCryptoWorkerFactory();
|
||||||
|
|
||||||
return new Wallet(
|
const w = new Wallet(
|
||||||
myDb,
|
myDb,
|
||||||
myHttpLib,
|
myHttpLib,
|
||||||
worker,
|
worker,
|
||||||
);
|
);
|
||||||
|
if (args.notifyHandler) {
|
||||||
|
w.addNotificationListener(args.notifyHandler);
|
||||||
|
}
|
||||||
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function withdrawTestBalance(
|
export async function withdrawTestBalance(
|
||||||
|
@ -138,7 +138,7 @@ export async function getBalances(
|
|||||||
});
|
});
|
||||||
|
|
||||||
await tx.iter(Stores.purchases).forEach(t => {
|
await tx.iter(Stores.purchases).forEach(t => {
|
||||||
if (t.payFinished) {
|
if (t.firstSuccessfulPayTimestamp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const c of t.payReq.coins) {
|
for (const c of t.payReq.coins) {
|
||||||
|
@ -14,11 +14,11 @@
|
|||||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { HistoryQuery, HistoryEvent } from "../walletTypes";
|
import { HistoryQuery, HistoryEvent } from "../walletTypes";
|
||||||
import { oneShotIter } from "../util/query";
|
import { oneShotIter, runWithReadTransaction } from "../util/query";
|
||||||
import { InternalWalletState } from "./state";
|
import { InternalWalletState } from "./state";
|
||||||
import { Stores, TipRecord } from "../dbTypes";
|
import { Stores, TipRecord } from "../dbTypes";
|
||||||
import * as Amounts from "../util/amounts";
|
import * as Amounts from "../util/amounts";
|
||||||
@ -34,139 +34,186 @@ export async function getHistory(
|
|||||||
const history: HistoryEvent[] = [];
|
const history: HistoryEvent[] = [];
|
||||||
|
|
||||||
// FIXME: do pagination instead of generating the full history
|
// FIXME: do pagination instead of generating the full history
|
||||||
|
|
||||||
// We uniquely identify history rows via their timestamp.
|
// We uniquely identify history rows via their timestamp.
|
||||||
// This works as timestamps are guaranteed to be monotonically
|
// This works as timestamps are guaranteed to be monotonically
|
||||||
// increasing even
|
// increasing even
|
||||||
|
|
||||||
/*
|
await runWithReadTransaction(
|
||||||
const proposals = await oneShotIter(ws.db, Stores.proposals).toArray();
|
|
||||||
for (const p of proposals) {
|
|
||||||
history.push({
|
|
||||||
detail: {
|
|
||||||
contractTermsHash: p.contractTermsHash,
|
|
||||||
merchantName: p.contractTerms.merchant.name,
|
|
||||||
},
|
|
||||||
timestamp: p.timestamp,
|
|
||||||
type: "claim-order",
|
|
||||||
explicit: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const withdrawals = await oneShotIter(
|
|
||||||
ws.db,
|
ws.db,
|
||||||
Stores.withdrawalSession,
|
[
|
||||||
).toArray();
|
Stores.currencies,
|
||||||
for (const w of withdrawals) {
|
Stores.coins,
|
||||||
history.push({
|
Stores.denominations,
|
||||||
detail: {
|
Stores.exchanges,
|
||||||
withdrawalAmount: w.rawWithdrawalAmount,
|
Stores.proposals,
|
||||||
},
|
Stores.purchases,
|
||||||
timestamp: w.startTimestamp,
|
Stores.refresh,
|
||||||
type: "withdraw",
|
Stores.reserves,
|
||||||
explicit: false,
|
Stores.tips,
|
||||||
});
|
Stores.withdrawalSession,
|
||||||
}
|
],
|
||||||
|
async tx => {
|
||||||
const purchases = await oneShotIter(ws.db, Stores.purchases).toArray();
|
await tx.iter(Stores.proposals).forEach(p => {
|
||||||
for (const p of purchases) {
|
history.push({
|
||||||
history.push({
|
detail: {},
|
||||||
detail: {
|
timestamp: p.timestamp,
|
||||||
amount: p.contractTerms.amount,
|
type: "claim-order",
|
||||||
contractTermsHash: p.contractTermsHash,
|
explicit: false,
|
||||||
fulfillmentUrl: p.contractTerms.fulfillment_url,
|
});
|
||||||
merchantName: p.contractTerms.merchant.name,
|
|
||||||
},
|
|
||||||
timestamp: p.acceptTimestamp,
|
|
||||||
type: "pay",
|
|
||||||
explicit: false,
|
|
||||||
});
|
|
||||||
if (p.lastRefundStatusTimestamp) {
|
|
||||||
const contractAmount = Amounts.parseOrThrow(p.contractTerms.amount);
|
|
||||||
const amountsPending = Object.keys(p.refundsPending).map(x =>
|
|
||||||
Amounts.parseOrThrow(p.refundsPending[x].refund_amount),
|
|
||||||
);
|
|
||||||
const amountsDone = Object.keys(p.refundsDone).map(x =>
|
|
||||||
Amounts.parseOrThrow(p.refundsDone[x].refund_amount),
|
|
||||||
);
|
|
||||||
const amounts: AmountJson[] = amountsPending.concat(amountsDone);
|
|
||||||
const amount = Amounts.add(
|
|
||||||
Amounts.getZero(contractAmount.currency),
|
|
||||||
...amounts,
|
|
||||||
).amount;
|
|
||||||
|
|
||||||
history.push({
|
|
||||||
detail: {
|
|
||||||
contractTermsHash: p.contractTermsHash,
|
|
||||||
fulfillmentUrl: p.contractTerms.fulfillment_url,
|
|
||||||
merchantName: p.contractTerms.merchant.name,
|
|
||||||
refundAmount: amount,
|
|
||||||
},
|
|
||||||
timestamp: p.lastRefundStatusTimestamp,
|
|
||||||
type: "refund",
|
|
||||||
explicit: false,
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const reserves = await oneShotIter(ws.db, Stores.reserves).toArray();
|
await tx.iter(Stores.withdrawalSession).forEach(w => {
|
||||||
|
history.push({
|
||||||
for (const r of reserves) {
|
detail: {
|
||||||
const reserveType = r.bankWithdrawStatusUrl ? "taler-bank" : "manual";
|
withdrawalAmount: w.rawWithdrawalAmount,
|
||||||
history.push({
|
},
|
||||||
detail: {
|
timestamp: w.startTimestamp,
|
||||||
exchangeBaseUrl: r.exchangeBaseUrl,
|
type: "withdraw-started",
|
||||||
requestedAmount: Amounts.toString(r.initiallyRequestedAmount),
|
explicit: false,
|
||||||
reservePub: r.reservePub,
|
});
|
||||||
reserveType,
|
if (w.finishTimestamp) {
|
||||||
bankWithdrawStatusUrl: r.bankWithdrawStatusUrl,
|
history.push({
|
||||||
},
|
detail: {
|
||||||
timestamp: r.created,
|
withdrawalAmount: w.rawWithdrawalAmount,
|
||||||
type: "reserve-created",
|
},
|
||||||
explicit: false,
|
timestamp: w.finishTimestamp,
|
||||||
});
|
type: "withdraw-finished",
|
||||||
if (r.timestampConfirmed) {
|
explicit: false,
|
||||||
history.push({
|
});
|
||||||
detail: {
|
}
|
||||||
exchangeBaseUrl: r.exchangeBaseUrl,
|
|
||||||
requestedAmount: Amounts.toString(r.initiallyRequestedAmount),
|
|
||||||
reservePub: r.reservePub,
|
|
||||||
reserveType,
|
|
||||||
bankWithdrawStatusUrl: r.bankWithdrawStatusUrl,
|
|
||||||
},
|
|
||||||
timestamp: r.created,
|
|
||||||
type: "reserve-confirmed",
|
|
||||||
explicit: false,
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const tips: TipRecord[] = await oneShotIter(ws.db, Stores.tips).toArray();
|
await tx.iter(Stores.purchases).forEach(p => {
|
||||||
for (const tip of tips) {
|
history.push({
|
||||||
history.push({
|
detail: {
|
||||||
detail: {
|
amount: p.contractTerms.amount,
|
||||||
accepted: tip.accepted,
|
contractTermsHash: p.contractTermsHash,
|
||||||
amount: tip.amount,
|
fulfillmentUrl: p.contractTerms.fulfillment_url,
|
||||||
merchantBaseUrl: tip.merchantBaseUrl,
|
merchantName: p.contractTerms.merchant.name,
|
||||||
tipId: tip.merchantTipId,
|
},
|
||||||
},
|
timestamp: p.acceptTimestamp,
|
||||||
timestamp: tip.createdTimestamp,
|
type: "pay-started",
|
||||||
explicit: false,
|
explicit: false,
|
||||||
type: "tip",
|
});
|
||||||
});
|
if (p.firstSuccessfulPayTimestamp) {
|
||||||
}
|
history.push({
|
||||||
|
detail: {
|
||||||
|
amount: p.contractTerms.amount,
|
||||||
|
contractTermsHash: p.contractTermsHash,
|
||||||
|
fulfillmentUrl: p.contractTerms.fulfillment_url,
|
||||||
|
merchantName: p.contractTerms.merchant.name,
|
||||||
|
},
|
||||||
|
timestamp: p.firstSuccessfulPayTimestamp,
|
||||||
|
type: "pay-finished",
|
||||||
|
explicit: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (p.lastRefundStatusTimestamp) {
|
||||||
|
const contractAmount = Amounts.parseOrThrow(p.contractTerms.amount);
|
||||||
|
const amountsPending = Object.keys(p.refundsPending).map(x =>
|
||||||
|
Amounts.parseOrThrow(p.refundsPending[x].refund_amount),
|
||||||
|
);
|
||||||
|
const amountsDone = Object.keys(p.refundsDone).map(x =>
|
||||||
|
Amounts.parseOrThrow(p.refundsDone[x].refund_amount),
|
||||||
|
);
|
||||||
|
const amounts: AmountJson[] = amountsPending.concat(amountsDone);
|
||||||
|
const amount = Amounts.add(
|
||||||
|
Amounts.getZero(contractAmount.currency),
|
||||||
|
...amounts,
|
||||||
|
).amount;
|
||||||
|
|
||||||
await oneShotIter(ws.db, Stores.exchanges).forEach(exchange => {
|
history.push({
|
||||||
history.push({
|
detail: {
|
||||||
type: "exchange-added",
|
contractTermsHash: p.contractTermsHash,
|
||||||
explicit: false,
|
fulfillmentUrl: p.contractTerms.fulfillment_url,
|
||||||
timestamp: exchange.timestampAdded,
|
merchantName: p.contractTerms.merchant.name,
|
||||||
detail: {
|
refundAmount: amount,
|
||||||
exchangeBaseUrl: exchange.baseUrl,
|
},
|
||||||
},
|
timestamp: p.lastRefundStatusTimestamp,
|
||||||
});
|
type: "refund",
|
||||||
});
|
explicit: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.iter(Stores.reserves).forEach(r => {
|
||||||
|
const reserveType = r.bankWithdrawStatusUrl ? "taler-bank" : "manual";
|
||||||
|
history.push({
|
||||||
|
detail: {
|
||||||
|
exchangeBaseUrl: r.exchangeBaseUrl,
|
||||||
|
requestedAmount: Amounts.toString(r.initiallyRequestedAmount),
|
||||||
|
reservePub: r.reservePub,
|
||||||
|
reserveType,
|
||||||
|
bankWithdrawStatusUrl: r.bankWithdrawStatusUrl,
|
||||||
|
},
|
||||||
|
timestamp: r.created,
|
||||||
|
type: "reserve-created",
|
||||||
|
explicit: false,
|
||||||
|
});
|
||||||
|
if (r.timestampConfirmed) {
|
||||||
|
history.push({
|
||||||
|
detail: {
|
||||||
|
exchangeBaseUrl: r.exchangeBaseUrl,
|
||||||
|
requestedAmount: Amounts.toString(r.initiallyRequestedAmount),
|
||||||
|
reservePub: r.reservePub,
|
||||||
|
reserveType,
|
||||||
|
bankWithdrawStatusUrl: r.bankWithdrawStatusUrl,
|
||||||
|
},
|
||||||
|
timestamp: r.created,
|
||||||
|
type: "reserve-confirmed",
|
||||||
|
explicit: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.iter(Stores.tips).forEach(tip => {
|
||||||
|
history.push({
|
||||||
|
detail: {
|
||||||
|
accepted: tip.accepted,
|
||||||
|
amount: tip.amount,
|
||||||
|
merchantBaseUrl: tip.merchantBaseUrl,
|
||||||
|
tipId: tip.merchantTipId,
|
||||||
|
},
|
||||||
|
timestamp: tip.createdTimestamp,
|
||||||
|
explicit: false,
|
||||||
|
type: "tip",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.iter(Stores.exchanges).forEach(exchange => {
|
||||||
|
history.push({
|
||||||
|
type: "exchange-added",
|
||||||
|
explicit: false,
|
||||||
|
timestamp: exchange.timestampAdded,
|
||||||
|
detail: {
|
||||||
|
exchangeBaseUrl: exchange.baseUrl,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.iter(Stores.refresh).forEach((r) => {
|
||||||
|
history.push({
|
||||||
|
type: "refresh-started",
|
||||||
|
explicit: false,
|
||||||
|
timestamp: r.created,
|
||||||
|
detail: {
|
||||||
|
refreshSessionId: r.refreshSessionId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (r.finishedTimestamp) {
|
||||||
|
history.push({
|
||||||
|
type: "refresh-finished",
|
||||||
|
explicit: false,
|
||||||
|
timestamp: r.finishedTimestamp,
|
||||||
|
detail: {
|
||||||
|
refreshSessionId: r.refreshSessionId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
history.sort((h1, h2) => Math.sign(h1.timestamp.t_ms - h2.timestamp.t_ms));
|
history.sort((h1, h2) => Math.sign(h1.timestamp.t_ms - h2.timestamp.t_ms));
|
||||||
|
|
||||||
|
@ -343,7 +343,6 @@ async function recordConfirmPay(
|
|||||||
abortRequested: false,
|
abortRequested: false,
|
||||||
contractTerms: d.contractTerms,
|
contractTerms: d.contractTerms,
|
||||||
contractTermsHash: d.contractTermsHash,
|
contractTermsHash: d.contractTermsHash,
|
||||||
payFinished: false,
|
|
||||||
lastSessionId: undefined,
|
lastSessionId: undefined,
|
||||||
merchantSig: d.merchantSig,
|
merchantSig: d.merchantSig,
|
||||||
payReq,
|
payReq,
|
||||||
@ -359,6 +358,7 @@ async function recordConfirmPay(
|
|||||||
refundStatusRequested: false,
|
refundStatusRequested: false,
|
||||||
lastRefundApplyError: undefined,
|
lastRefundApplyError: undefined,
|
||||||
refundApplyRetryInfo: initRetryInfo(),
|
refundApplyRetryInfo: initRetryInfo(),
|
||||||
|
firstSuccessfulPayTimestamp: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
await runWithWriteTransaction(
|
await runWithWriteTransaction(
|
||||||
@ -405,7 +405,7 @@ export async function abortFailedPayment(
|
|||||||
if (!purchase) {
|
if (!purchase) {
|
||||||
throw Error("Purchase not found, unable to abort with refund");
|
throw Error("Purchase not found, unable to abort with refund");
|
||||||
}
|
}
|
||||||
if (purchase.payFinished) {
|
if (purchase.firstSuccessfulPayTimestamp) {
|
||||||
throw Error("Purchase already finished, not aborting");
|
throw Error("Purchase already finished, not aborting");
|
||||||
}
|
}
|
||||||
if (purchase.abortDone) {
|
if (purchase.abortDone) {
|
||||||
@ -465,6 +465,7 @@ async function incrementProposalRetry(
|
|||||||
pr.lastError = err;
|
pr.lastError = err;
|
||||||
await tx.put(Stores.proposals, pr);
|
await tx.put(Stores.proposals, pr);
|
||||||
});
|
});
|
||||||
|
ws.notify({ type: NotificationType.ProposalOperationError });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function incrementPurchasePayRetry(
|
async function incrementPurchasePayRetry(
|
||||||
@ -486,6 +487,7 @@ async function incrementPurchasePayRetry(
|
|||||||
pr.lastPayError = err;
|
pr.lastPayError = err;
|
||||||
await tx.put(Stores.purchases, pr);
|
await tx.put(Stores.purchases, pr);
|
||||||
});
|
});
|
||||||
|
ws.notify({ type: NotificationType.PayOperationError });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function incrementPurchaseQueryRefundRetry(
|
async function incrementPurchaseQueryRefundRetry(
|
||||||
@ -507,6 +509,7 @@ async function incrementPurchaseQueryRefundRetry(
|
|||||||
pr.lastRefundStatusError = err;
|
pr.lastRefundStatusError = err;
|
||||||
await tx.put(Stores.purchases, pr);
|
await tx.put(Stores.purchases, pr);
|
||||||
});
|
});
|
||||||
|
ws.notify({ type: NotificationType.RefundStatusOperationError });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function incrementPurchaseApplyRefundRetry(
|
async function incrementPurchaseApplyRefundRetry(
|
||||||
@ -528,6 +531,7 @@ async function incrementPurchaseApplyRefundRetry(
|
|||||||
pr.lastRefundApplyError = err;
|
pr.lastRefundApplyError = err;
|
||||||
await tx.put(Stores.purchases, pr);
|
await tx.put(Stores.purchases, pr);
|
||||||
});
|
});
|
||||||
|
ws.notify({ type: NotificationType.RefundApplyOperationError });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function processDownloadProposal(
|
export async function processDownloadProposal(
|
||||||
@ -698,7 +702,7 @@ export async function submitPay(
|
|||||||
// FIXME: properly display error
|
// FIXME: properly display error
|
||||||
throw Error("merchant payment signature invalid");
|
throw Error("merchant payment signature invalid");
|
||||||
}
|
}
|
||||||
purchase.payFinished = true;
|
purchase.firstSuccessfulPayTimestamp = getTimestampNow();
|
||||||
purchase.lastPayError = undefined;
|
purchase.lastPayError = undefined;
|
||||||
purchase.payRetryInfo = initRetryInfo(false);
|
purchase.payRetryInfo = initRetryInfo(false);
|
||||||
const modifiedCoins: CoinRecord[] = [];
|
const modifiedCoins: CoinRecord[] = [];
|
||||||
@ -1044,10 +1048,9 @@ async function acceptRefundResponse(
|
|||||||
throw Error("empty refund");
|
throw Error("empty refund");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let numNewRefunds = 0;
|
let numNewRefunds = 0;
|
||||||
|
|
||||||
await runWithWriteTransaction(ws.db, [Stores.purchases], async (tx) => {
|
await runWithWriteTransaction(ws.db, [Stores.purchases], async tx => {
|
||||||
const p = await tx.get(Stores.purchases, proposalId);
|
const p = await tx.get(Stores.purchases, proposalId);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
console.error("purchase not found, not adding refunds");
|
console.error("purchase not found, not adding refunds");
|
||||||
@ -1080,6 +1083,9 @@ async function acceptRefundResponse(
|
|||||||
|
|
||||||
await tx.put(Stores.purchases, p);
|
await tx.put(Stores.purchases, p);
|
||||||
});
|
});
|
||||||
|
ws.notify({
|
||||||
|
type: NotificationType.RefundQueried,
|
||||||
|
});
|
||||||
if (numNewRefunds > 0) {
|
if (numNewRefunds > 0) {
|
||||||
await processPurchaseApplyRefund(ws, proposalId);
|
await processPurchaseApplyRefund(ws, proposalId);
|
||||||
}
|
}
|
||||||
@ -1099,7 +1105,6 @@ async function startRefundQuery(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (p.refundStatusRequested) {
|
if (p.refundStatusRequested) {
|
||||||
|
|
||||||
}
|
}
|
||||||
p.refundStatusRequested = true;
|
p.refundStatusRequested = true;
|
||||||
p.lastRefundStatusError = undefined;
|
p.lastRefundStatusError = undefined;
|
||||||
@ -1113,6 +1118,10 @@ async function startRefundQuery(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ws.notify({
|
||||||
|
type: NotificationType.RefundStarted,
|
||||||
|
});
|
||||||
|
|
||||||
await processPurchaseQueryRefund(ws, proposalId);
|
await processPurchaseQueryRefund(ws, proposalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1169,7 +1178,7 @@ async function processPurchasePayImpl(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.trace(`processing purchase pay ${proposalId}`);
|
logger.trace(`processing purchase pay ${proposalId}`);
|
||||||
if (purchase.payFinished) {
|
if (purchase.firstSuccessfulPayTimestamp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await submitPay(ws, proposalId, purchase.lastSessionId);
|
await submitPay(ws, proposalId, purchase.lastSessionId);
|
||||||
|
@ -353,7 +353,7 @@ async function gatherPurchasePending(
|
|||||||
onlyDue: boolean = false,
|
onlyDue: boolean = false,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await tx.iter(Stores.purchases).forEach((pr) => {
|
await tx.iter(Stores.purchases).forEach((pr) => {
|
||||||
if (!pr.payFinished) {
|
if (!pr.firstSuccessfulPayTimestamp) {
|
||||||
resp.nextRetryDelay = updateRetryDelay(
|
resp.nextRetryDelay = updateRetryDelay(
|
||||||
resp.nextRetryDelay,
|
resp.nextRetryDelay,
|
||||||
now,
|
now,
|
||||||
|
@ -312,6 +312,7 @@ async function incrementRefreshRetry(
|
|||||||
r.lastError = err;
|
r.lastError = err;
|
||||||
await tx.put(Stores.refresh, r);
|
await tx.put(Stores.refresh, r);
|
||||||
});
|
});
|
||||||
|
ws.notify({ type: NotificationType.RefreshOperationError });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -342,6 +342,7 @@ async function incrementReserveRetry(
|
|||||||
r.lastError = err;
|
r.lastError = err;
|
||||||
await tx.put(Stores.reserves, r);
|
await tx.put(Stores.reserves, r);
|
||||||
});
|
});
|
||||||
|
ws.notify({ type: NotificationType.ReserveOperationError });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -606,6 +607,10 @@ async function depleteReserve(
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
console.log("processing new withdraw session");
|
console.log("processing new withdraw session");
|
||||||
|
ws.notify({
|
||||||
|
type: NotificationType.WithdrawSessionCreated,
|
||||||
|
withdrawSessionId: withdrawalSessionId,
|
||||||
|
});
|
||||||
await processWithdrawSession(ws, withdrawalSessionId);
|
await processWithdrawSession(ws, withdrawalSessionId);
|
||||||
} else {
|
} else {
|
||||||
console.trace("withdraw session already existed");
|
console.trace("withdraw session already existed");
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
import { oneShotGet, oneShotPut, oneShotMutate, runWithWriteTransaction } from "../util/query";
|
import { oneShotGet, oneShotPut, oneShotMutate, runWithWriteTransaction } from "../util/query";
|
||||||
import { InternalWalletState } from "./state";
|
import { InternalWalletState } from "./state";
|
||||||
import { parseTipUri } from "../util/taleruri";
|
import { parseTipUri } from "../util/taleruri";
|
||||||
import { TipStatus, getTimestampNow, OperationError } from "../walletTypes";
|
import { TipStatus, getTimestampNow, OperationError, NotificationType } from "../walletTypes";
|
||||||
import { TipPickupGetResponse, TipPlanchetDetail, TipResponse } from "../talerTypes";
|
import { TipPickupGetResponse, TipPlanchetDetail, TipResponse } from "../talerTypes";
|
||||||
import * as Amounts from "../util/amounts";
|
import * as Amounts from "../util/amounts";
|
||||||
import { Stores, PlanchetRecord, WithdrawalSessionRecord, initRetryInfo, updateRetryInfoTimeout } from "../dbTypes";
|
import { Stores, PlanchetRecord, WithdrawalSessionRecord, initRetryInfo, updateRetryInfoTimeout } from "../dbTypes";
|
||||||
@ -122,6 +122,7 @@ async function incrementTipRetry(
|
|||||||
t.lastError = err;
|
t.lastError = err;
|
||||||
await tx.put(Stores.tips, t);
|
await tx.put(Stores.tips, t);
|
||||||
});
|
});
|
||||||
|
ws.notify({ type: NotificationType.TipOperationError });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function processTip(
|
export async function processTip(
|
||||||
|
@ -260,17 +260,17 @@ async function processPlanchet(
|
|||||||
let withdrawSessionFinished = false;
|
let withdrawSessionFinished = false;
|
||||||
let reserveDepleted = false;
|
let reserveDepleted = false;
|
||||||
|
|
||||||
await runWithWriteTransaction(
|
const success = await runWithWriteTransaction(
|
||||||
ws.db,
|
ws.db,
|
||||||
[Stores.coins, Stores.withdrawalSession, Stores.reserves],
|
[Stores.coins, Stores.withdrawalSession, Stores.reserves],
|
||||||
async tx => {
|
async tx => {
|
||||||
const ws = await tx.get(Stores.withdrawalSession, withdrawalSessionId);
|
const ws = await tx.get(Stores.withdrawalSession, withdrawalSessionId);
|
||||||
if (!ws) {
|
if (!ws) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (ws.withdrawn[coinIdx]) {
|
if (ws.withdrawn[coinIdx]) {
|
||||||
// Already withdrawn
|
// Already withdrawn
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
ws.withdrawn[coinIdx] = true;
|
ws.withdrawn[coinIdx] = true;
|
||||||
ws.lastCoinErrors[coinIdx] = undefined;
|
ws.lastCoinErrors[coinIdx] = undefined;
|
||||||
@ -301,9 +301,16 @@ async function processPlanchet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
await tx.add(Stores.coins, coin);
|
await tx.add(Stores.coins, coin);
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
ws.notify( {
|
||||||
|
type: NotificationType.CoinWithdrawn,
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
if (withdrawSessionFinished) {
|
if (withdrawSessionFinished) {
|
||||||
ws.notify({
|
ws.notify({
|
||||||
type: NotificationType.WithdrawSessionFinished,
|
type: NotificationType.WithdrawSessionFinished,
|
||||||
@ -503,6 +510,7 @@ async function incrementWithdrawalRetry(
|
|||||||
wsr.lastError = err;
|
wsr.lastError = err;
|
||||||
await tx.put(Stores.withdrawalSession, wsr);
|
await tx.put(Stores.withdrawalSession, wsr);
|
||||||
});
|
});
|
||||||
|
ws.notify({ type: NotificationType.WithdrawOperationError });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function processWithdrawSession(
|
export async function processWithdrawSession(
|
||||||
|
@ -506,6 +506,7 @@ export interface PendingPayOperation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const enum NotificationType {
|
export const enum NotificationType {
|
||||||
|
CoinWithdrawn = "coin-withdrawn",
|
||||||
ProposalAccepted = "proposal-accepted",
|
ProposalAccepted = "proposal-accepted",
|
||||||
ProposalDownloaded = "proposal-downloaded",
|
ProposalDownloaded = "proposal-downloaded",
|
||||||
RefundsSubmitted = "refunds-submitted",
|
RefundsSubmitted = "refunds-submitted",
|
||||||
@ -518,9 +519,21 @@ export const enum NotificationType {
|
|||||||
ReserveUpdated = "reserve-updated",
|
ReserveUpdated = "reserve-updated",
|
||||||
ReserveConfirmed = "reserve-confirmed",
|
ReserveConfirmed = "reserve-confirmed",
|
||||||
ReserveDepleted = "reserve-depleted",
|
ReserveDepleted = "reserve-depleted",
|
||||||
|
WithdrawSessionCreated = "withdraw-session-created",
|
||||||
WithdrawSessionFinished = "withdraw-session-finished",
|
WithdrawSessionFinished = "withdraw-session-finished",
|
||||||
WaitingForRetry = "waiting-for-retry",
|
WaitingForRetry = "waiting-for-retry",
|
||||||
|
RefundStarted = "refund-started",
|
||||||
|
RefundQueried = "refund-queried",
|
||||||
RefundFinished = "refund-finished",
|
RefundFinished = "refund-finished",
|
||||||
|
ExchangeOperationError = "exchange-operation-error",
|
||||||
|
RefreshOperationError = "refresh-operation-error",
|
||||||
|
RefundApplyOperationError = "refund-apply-error",
|
||||||
|
RefundStatusOperationError = "refund-status-error",
|
||||||
|
ProposalOperationError = "proposal-error",
|
||||||
|
TipOperationError = "tip-error",
|
||||||
|
PayOperationError = "pay-error",
|
||||||
|
WithdrawOperationError = "withdraw-error",
|
||||||
|
ReserveOperationError = "reserve-error",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProposalAcceptedNotification {
|
export interface ProposalAcceptedNotification {
|
||||||
@ -528,6 +541,18 @@ export interface ProposalAcceptedNotification {
|
|||||||
proposalId: string;
|
proposalId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CoinWithdrawnNotification {
|
||||||
|
type: NotificationType.CoinWithdrawn;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RefundStartedNotification {
|
||||||
|
type: NotificationType.RefundStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RefundQueriedNotification {
|
||||||
|
type: NotificationType.RefundQueried;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ProposalDownloadedNotification {
|
export interface ProposalDownloadedNotification {
|
||||||
type: NotificationType.ProposalDownloaded;
|
type: NotificationType.ProposalDownloaded;
|
||||||
proposalId: string;
|
proposalId: string;
|
||||||
@ -570,6 +595,11 @@ export interface ReserveConfirmedNotification {
|
|||||||
type: NotificationType.ReserveConfirmed;
|
type: NotificationType.ReserveConfirmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WithdrawSessionCreatedNotification {
|
||||||
|
type: NotificationType.WithdrawSessionCreated;
|
||||||
|
withdrawSessionId: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface WithdrawSessionFinishedNotification {
|
export interface WithdrawSessionFinishedNotification {
|
||||||
type: NotificationType.WithdrawSessionFinished;
|
type: NotificationType.WithdrawSessionFinished;
|
||||||
withdrawSessionId: string;
|
withdrawSessionId: string;
|
||||||
@ -590,7 +620,52 @@ export interface RefundFinishedNotification {
|
|||||||
type: NotificationType.RefundFinished;
|
type: NotificationType.RefundFinished;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ExchangeOperationErrorNotification {
|
||||||
|
type: NotificationType.ExchangeOperationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RefreshOperationErrorNotification {
|
||||||
|
type: NotificationType.RefreshOperationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RefundStatusOperationErrorNotification {
|
||||||
|
type: NotificationType.RefundStatusOperationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RefundApplyOperationErrorNotification {
|
||||||
|
type: NotificationType.RefundApplyOperationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PayOperationErrorNotification {
|
||||||
|
type: NotificationType.PayOperationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProposalOperationErrorNotification {
|
||||||
|
type: NotificationType.ProposalOperationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TipOperationErrorNotification {
|
||||||
|
type: NotificationType.TipOperationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WithdrawOperationErrorNotification {
|
||||||
|
type: NotificationType.WithdrawOperationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReserveOperationErrorNotification {
|
||||||
|
type: NotificationType.ReserveOperationError;
|
||||||
|
}
|
||||||
|
|
||||||
export type WalletNotification =
|
export type WalletNotification =
|
||||||
|
| WithdrawOperationErrorNotification
|
||||||
|
| ReserveOperationErrorNotification
|
||||||
|
| ExchangeOperationErrorNotification
|
||||||
|
| RefreshOperationErrorNotification
|
||||||
|
| RefundStatusOperationErrorNotification
|
||||||
|
| RefundApplyOperationErrorNotification
|
||||||
|
| ProposalOperationErrorNotification
|
||||||
|
| PayOperationErrorNotification
|
||||||
|
| TipOperationErrorNotification
|
||||||
| ProposalAcceptedNotification
|
| ProposalAcceptedNotification
|
||||||
| ProposalDownloadedNotification
|
| ProposalDownloadedNotification
|
||||||
| RefundsSubmittedNotification
|
| RefundsSubmittedNotification
|
||||||
@ -605,7 +680,11 @@ export type WalletNotification =
|
|||||||
| WithdrawSessionFinishedNotification
|
| WithdrawSessionFinishedNotification
|
||||||
| ReserveDepletedNotification
|
| ReserveDepletedNotification
|
||||||
| WaitingForRetryNotification
|
| WaitingForRetryNotification
|
||||||
| RefundFinishedNotification;
|
| RefundStartedNotification
|
||||||
|
| RefundFinishedNotification
|
||||||
|
| RefundQueriedNotification
|
||||||
|
| WithdrawSessionCreatedNotification
|
||||||
|
| CoinWithdrawnNotification;
|
||||||
|
|
||||||
export interface OperationError {
|
export interface OperationError {
|
||||||
type: string;
|
type: string;
|
||||||
|
Loading…
Reference in New Issue
Block a user