nuke some console.log statements, test wallet testing functionality in integration test
This commit is contained in:
parent
e3850158c2
commit
953cd9dc41
@ -29,6 +29,7 @@ import * as fs from "fs";
|
|||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import * as os from "os";
|
import * as os from "os";
|
||||||
import * as http from "http";
|
import * as http from "http";
|
||||||
|
import { deepStrictEqual } from "assert";
|
||||||
import { ChildProcess, spawn } from "child_process";
|
import { ChildProcess, spawn } from "child_process";
|
||||||
import {
|
import {
|
||||||
Configuration,
|
Configuration,
|
||||||
@ -53,6 +54,18 @@ import {
|
|||||||
ConfirmPayRequest,
|
ConfirmPayRequest,
|
||||||
ConfirmPayResult,
|
ConfirmPayResult,
|
||||||
codecForConfirmPayResult,
|
codecForConfirmPayResult,
|
||||||
|
IntegrationTestArgs,
|
||||||
|
TestPayArgs,
|
||||||
|
BalancesResponse,
|
||||||
|
codecForBalancesResponse,
|
||||||
|
encodeCrock,
|
||||||
|
getRandomBytes,
|
||||||
|
EddsaKeyPair,
|
||||||
|
eddsaGetPublic,
|
||||||
|
createEddsaKeyPair,
|
||||||
|
TransactionsResponse,
|
||||||
|
codecForTransactionsResponse,
|
||||||
|
WithdrawTestBalanceRequest,
|
||||||
} from "taler-wallet-core";
|
} from "taler-wallet-core";
|
||||||
import { URL } from "url";
|
import { URL } from "url";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
@ -63,14 +76,6 @@ import {
|
|||||||
PostOrderResponse,
|
PostOrderResponse,
|
||||||
MerchantOrderPrivateStatusResponse,
|
MerchantOrderPrivateStatusResponse,
|
||||||
} from "./merchantApiTypes";
|
} from "./merchantApiTypes";
|
||||||
import {
|
|
||||||
EddsaKeyPair,
|
|
||||||
getRandomBytes,
|
|
||||||
encodeCrock,
|
|
||||||
eddsaGetPublic,
|
|
||||||
createEddsaKeyPair,
|
|
||||||
} from "taler-wallet-core/lib/crypto/talerCrypto";
|
|
||||||
import { WithdrawalDetails } from "taler-wallet-core/lib/types/transactions";
|
|
||||||
|
|
||||||
const exec = util.promisify(require("child_process").exec);
|
const exec = util.promisify(require("child_process").exec);
|
||||||
|
|
||||||
@ -277,6 +282,10 @@ export class GlobalTestState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertDeepEqual(actual: any, expected: any): asserts actual is any {
|
||||||
|
deepStrictEqual(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
assertAmountEquals(
|
assertAmountEquals(
|
||||||
amtExpected: string | AmountJson,
|
amtExpected: string | AmountJson,
|
||||||
amtActual: string | AmountJson,
|
amtActual: string | AmountJson,
|
||||||
@ -521,6 +530,10 @@ export class BankService {
|
|||||||
config.setString("bank", "suggested_exchange_payto", exchangePayto);
|
config.setString("bank", "suggested_exchange_payto", exchangePayto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get baseUrl(): string {
|
||||||
|
return `http://localhost:${this.bankConfig.httpPort}/`;
|
||||||
|
}
|
||||||
|
|
||||||
async createExchangeAccount(
|
async createExchangeAccount(
|
||||||
accountName: string,
|
accountName: string,
|
||||||
password: string,
|
password: string,
|
||||||
@ -890,11 +903,10 @@ export interface MerchantConfig {
|
|||||||
database: string;
|
database: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface PrivateOrderStatusQuery {
|
export interface PrivateOrderStatusQuery {
|
||||||
instance?: string,
|
instance?: string;
|
||||||
orderId: string,
|
orderId: string;
|
||||||
sessionId?: string,
|
sessionId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MerchantService {
|
export class MerchantService {
|
||||||
@ -993,7 +1005,9 @@ export class MerchantService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async queryPrivateOrderStatus(query: PrivateOrderStatusQuery): Promise<MerchantOrderPrivateStatusResponse> {
|
async queryPrivateOrderStatus(
|
||||||
|
query: PrivateOrderStatusQuery,
|
||||||
|
): Promise<MerchantOrderPrivateStatusResponse> {
|
||||||
const reqUrl = new URL(
|
const reqUrl = new URL(
|
||||||
`private/orders/${query.orderId}`,
|
`private/orders/${query.orderId}`,
|
||||||
this.makeInstanceBaseUrl(query.instance),
|
this.makeInstanceBaseUrl(query.instance),
|
||||||
@ -1215,6 +1229,46 @@ export class WalletCli {
|
|||||||
throw new OperationFailedError(resp.error);
|
throw new OperationFailedError(resp.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getBalances(): Promise<BalancesResponse> {
|
||||||
|
const resp = await this.apiRequest("getBalances", {});
|
||||||
|
if (resp.type === "response") {
|
||||||
|
return codecForBalancesResponse().decode(resp.result);
|
||||||
|
}
|
||||||
|
throw new OperationFailedError(resp.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTransactions(): Promise<TransactionsResponse> {
|
||||||
|
const resp = await this.apiRequest("getTransactions", {});
|
||||||
|
if (resp.type === "response") {
|
||||||
|
return codecForTransactionsResponse().decode(resp.result);
|
||||||
|
}
|
||||||
|
throw new OperationFailedError(resp.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
async runIntegrationtest(args: IntegrationTestArgs): Promise<void> {
|
||||||
|
const resp = await this.apiRequest("runIntegrationtest", args);
|
||||||
|
if (resp.type === "response") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new OperationFailedError(resp.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
async testPay(args: TestPayArgs): Promise<void> {
|
||||||
|
const resp = await this.apiRequest("testPay", args);
|
||||||
|
if (resp.type === "response") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new OperationFailedError(resp.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
async withdrawTestBalance(args: WithdrawTestBalanceRequest): Promise<void> {
|
||||||
|
const resp = await this.apiRequest("withdrawTestBalance", args);
|
||||||
|
if (resp.type === "response") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new OperationFailedError(resp.error);
|
||||||
|
}
|
||||||
|
|
||||||
async getWithdrawalDetailsForUri(
|
async getWithdrawalDetailsForUri(
|
||||||
req: GetWithdrawalDetailsForUriRequest,
|
req: GetWithdrawalDetailsForUriRequest,
|
||||||
): Promise<WithdrawUriInfoResponse> {
|
): Promise<WithdrawUriInfoResponse> {
|
||||||
@ -1222,6 +1276,6 @@ export class WalletCli {
|
|||||||
if (resp.type === "response") {
|
if (resp.type === "response") {
|
||||||
return codecForWithdrawUriInfoResponse().decode(resp.result);
|
return codecForWithdrawUriInfoResponse().decode(resp.result);
|
||||||
}
|
}
|
||||||
throw new OperationFailedError(resp.error);
|
throw new OperationFailedError(resp.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
90
packages/taler-integrationtests/src/test-wallettesting.ts
Normal file
90
packages/taler-integrationtests/src/test-wallettesting.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
This file is part of GNU Taler
|
||||||
|
(C) 2020 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/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration test for the wallet testing functionality used by the exchange
|
||||||
|
* test cases.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports.
|
||||||
|
*/
|
||||||
|
import { runTest, GlobalTestState } from "./harness";
|
||||||
|
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run test for basic, bank-integrated withdrawal.
|
||||||
|
*/
|
||||||
|
runTest(async (t: GlobalTestState) => {
|
||||||
|
const {
|
||||||
|
wallet,
|
||||||
|
bank,
|
||||||
|
exchange,
|
||||||
|
merchant,
|
||||||
|
} = await createSimpleTestkudosEnvironment(t);
|
||||||
|
|
||||||
|
await wallet.runIntegrationtest({
|
||||||
|
amountToSpend: "TESTKUDOS:5",
|
||||||
|
amountToWithdraw: "TESTKUDOS:10",
|
||||||
|
bankBaseUrl: bank.baseUrl,
|
||||||
|
exchangeBaseUrl: exchange.baseUrl,
|
||||||
|
merchantApiKey: "sandbox",
|
||||||
|
merchantBaseUrl: merchant.makeInstanceBaseUrl(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let txns = await wallet.getTransactions();
|
||||||
|
console.log(JSON.stringify(txns, undefined, 2));
|
||||||
|
let txTypes = txns.transactions.map((x) => x.type);
|
||||||
|
|
||||||
|
t.assertDeepEqual(txTypes, [
|
||||||
|
"withdrawal",
|
||||||
|
"payment",
|
||||||
|
"withdrawal",
|
||||||
|
"payment",
|
||||||
|
"refund",
|
||||||
|
"payment",
|
||||||
|
]);
|
||||||
|
|
||||||
|
wallet.deleteDatabase();
|
||||||
|
|
||||||
|
await wallet.withdrawTestBalance({
|
||||||
|
amount: "TESTKUDOS:10",
|
||||||
|
bankBaseUrl: bank.baseUrl,
|
||||||
|
exchangeBaseUrl: exchange.baseUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
await wallet.runUntilDone();
|
||||||
|
|
||||||
|
await wallet.testPay({
|
||||||
|
amount: "TESTKUDOS:5",
|
||||||
|
merchantApiKey: "sandbox",
|
||||||
|
merchantBaseUrl: merchant.makeInstanceBaseUrl(),
|
||||||
|
summary: "foo",
|
||||||
|
});
|
||||||
|
|
||||||
|
await wallet.runUntilDone();
|
||||||
|
|
||||||
|
txns = await wallet.getTransactions();
|
||||||
|
console.log(JSON.stringify(txns, undefined, 2));
|
||||||
|
txTypes = txns.transactions.map((x) => x.type);
|
||||||
|
|
||||||
|
t.assertDeepEqual(txTypes, [
|
||||||
|
"withdrawal",
|
||||||
|
"payment",
|
||||||
|
]);
|
||||||
|
|
||||||
|
await t.shutdown();
|
||||||
|
});
|
@ -203,21 +203,21 @@ export class CryptoApi {
|
|||||||
|
|
||||||
handleWorkerError(ws: WorkerState, e: any): void {
|
handleWorkerError(ws: WorkerState, e: any): void {
|
||||||
if (ws.currentWorkItem) {
|
if (ws.currentWorkItem) {
|
||||||
console.error(
|
logger.error(
|
||||||
`error in worker during ${ws.currentWorkItem.operation}`,
|
`error in worker during ${ws.currentWorkItem.operation}`,
|
||||||
e,
|
e,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
console.error("error in worker", e);
|
logger.error("error in worker", e);
|
||||||
}
|
}
|
||||||
console.error(e.message);
|
logger.error(e.message);
|
||||||
try {
|
try {
|
||||||
if (ws.w) {
|
if (ws.w) {
|
||||||
ws.w.terminate();
|
ws.w.terminate();
|
||||||
ws.w = null;
|
ws.w = null;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
logger.error(e);
|
||||||
}
|
}
|
||||||
if (ws.currentWorkItem !== null) {
|
if (ws.currentWorkItem !== null) {
|
||||||
ws.currentWorkItem.reject(e);
|
ws.currentWorkItem.reject(e);
|
||||||
|
@ -105,7 +105,7 @@ export async function getDefaultNodeWallet(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const myVersionChange = (): Promise<void> => {
|
const myVersionChange = (): Promise<void> => {
|
||||||
console.error("version change requested, should not happen");
|
logger.error("version change requested, should not happen");
|
||||||
throw Error();
|
throw Error();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ export async function getDefaultNodeWallet(
|
|||||||
require("worker_threads");
|
require("worker_threads");
|
||||||
workerFactory = new NodeThreadCryptoWorkerFactory();
|
workerFactory = new NodeThreadCryptoWorkerFactory();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(
|
logger.warn(
|
||||||
"worker threads not available, falling back to synchronous workers",
|
"worker threads not available, falling back to synchronous workers",
|
||||||
);
|
);
|
||||||
workerFactory = new SynchronousCryptoWorkerFactory();
|
workerFactory = new SynchronousCryptoWorkerFactory();
|
||||||
|
@ -26,6 +26,9 @@ export { strings } from "./strings";
|
|||||||
|
|
||||||
// @ts-ignore: no type decl for this library
|
// @ts-ignore: no type decl for this library
|
||||||
import * as jedLib from "jed";
|
import * as jedLib from "jed";
|
||||||
|
import { Logger } from "../util/logging";
|
||||||
|
|
||||||
|
const logger = new Logger("i18n/index.ts");
|
||||||
|
|
||||||
export let jed: any = undefined;
|
export let jed: any = undefined;
|
||||||
|
|
||||||
@ -38,7 +41,7 @@ export function setupI18n(lang: string): any {
|
|||||||
|
|
||||||
if (!strings[lang]) {
|
if (!strings[lang]) {
|
||||||
lang = "en-US";
|
lang = "en-US";
|
||||||
console.log(`language ${lang} not found, defaulting to english`);
|
logger.warn(`language ${lang} not found, defaulting to english`);
|
||||||
}
|
}
|
||||||
jed = new jedLib.Jed(strings[lang]);
|
jed = new jedLib.Jed(strings[lang]);
|
||||||
}
|
}
|
||||||
|
@ -19,59 +19,48 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export { Wallet } from "./wallet";
|
export { Wallet } from "./wallet";
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
export { TalerErrorCode } from "./TalerErrorCode";
|
||||||
|
export * from "./operations/errors";
|
||||||
|
|
||||||
|
// Utils for using the wallet under node
|
||||||
|
export { NodeHttpLib } from "./headless/NodeHttpLib";
|
||||||
export {
|
export {
|
||||||
getDefaultNodeWallet,
|
getDefaultNodeWallet,
|
||||||
DefaultNodeWalletArgs,
|
DefaultNodeWalletArgs,
|
||||||
} from "./headless/helpers";
|
} from "./headless/helpers";
|
||||||
export { Amounts, AmountJson } from "./util/amounts";
|
|
||||||
export { Logger } from "./util/logging";
|
|
||||||
|
|
||||||
export * from "./crypto/talerCrypto";
|
|
||||||
export {
|
|
||||||
OperationFailedAndReportedError,
|
|
||||||
OperationFailedError,
|
|
||||||
makeErrorDetails,
|
|
||||||
} from "./operations/errors";
|
|
||||||
|
|
||||||
export * from "./types/walletTypes";
|
|
||||||
|
|
||||||
export * from "./types/talerTypes";
|
|
||||||
|
|
||||||
export * from "./util/taleruri";
|
|
||||||
|
|
||||||
export * from "./util/time";
|
|
||||||
|
|
||||||
export * from "./util/codec";
|
|
||||||
|
|
||||||
export { NodeHttpLib } from "./headless/NodeHttpLib";
|
|
||||||
|
|
||||||
export * from "./util/payto";
|
|
||||||
|
|
||||||
export * from "./util/testvectors";
|
|
||||||
|
|
||||||
export * from "./operations/versions";
|
export * from "./operations/versions";
|
||||||
|
|
||||||
export type { CryptoWorker } from "./crypto/workers/cryptoWorker";
|
|
||||||
export { CryptoWorkerFactory, CryptoApi } from "./crypto/workers/cryptoApi";
|
|
||||||
|
|
||||||
export * from "./util/http";
|
|
||||||
|
|
||||||
export { TalerErrorCode } from "./TalerErrorCode";
|
|
||||||
|
|
||||||
export * from "./util/query";
|
|
||||||
|
|
||||||
export { CryptoImplementation } from "./crypto/workers/cryptoImplementation";
|
|
||||||
|
|
||||||
export * from "./db";
|
export * from "./db";
|
||||||
|
|
||||||
export * from "./util/promiseUtils";
|
// Internationalization
|
||||||
|
|
||||||
export * from "./i18n";
|
export * from "./i18n";
|
||||||
|
|
||||||
|
// Crypto and crypto workers
|
||||||
export * from "./crypto/workers/nodeThreadWorker";
|
export * from "./crypto/workers/nodeThreadWorker";
|
||||||
|
export { CryptoImplementation } from "./crypto/workers/cryptoImplementation";
|
||||||
|
export type { CryptoWorker } from "./crypto/workers/cryptoWorker";
|
||||||
|
export { CryptoWorkerFactory, CryptoApi } from "./crypto/workers/cryptoApi";
|
||||||
|
export * from "./crypto/talerCrypto";
|
||||||
|
|
||||||
export * from "./types/notifications";
|
// Util functionality
|
||||||
|
export { Amounts, AmountJson } from "./util/amounts";
|
||||||
|
export { Logger } from "./util/logging";
|
||||||
export { Configuration } from "./util/talerconfig";
|
export { Configuration } from "./util/talerconfig";
|
||||||
|
export { URL } from "./util/url";
|
||||||
|
export * from "./util/codec";
|
||||||
|
export * from "./util/promiseUtils";
|
||||||
|
export * from "./util/query";
|
||||||
|
export * from "./util/http";
|
||||||
|
export * from "./util/payto";
|
||||||
|
export * from "./util/testvectors";
|
||||||
|
export * from "./util/taleruri";
|
||||||
|
export * from "./util/time";
|
||||||
|
|
||||||
export { URL } from "./util/url";
|
// Types
|
||||||
|
export * from "./types/talerTypes";
|
||||||
|
export * from "./types/walletTypes";
|
||||||
|
export * from "./types/notifications";
|
||||||
|
export * from "./types/transactions"
|
||||||
|
@ -175,7 +175,7 @@ async function updateExchangeWithKeys(
|
|||||||
async (tx) => {
|
async (tx) => {
|
||||||
const r = await tx.get(Stores.exchanges, baseUrl);
|
const r = await tx.get(Stores.exchanges, baseUrl);
|
||||||
if (!r) {
|
if (!r) {
|
||||||
console.warn(`exchange ${baseUrl} no longer present`);
|
logger.warn(`exchange ${baseUrl} no longer present`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (r.details) {
|
if (r.details) {
|
||||||
@ -222,10 +222,10 @@ async function updateExchangeWithKeys(
|
|||||||
if (oldDenom.isRevoked) {
|
if (oldDenom.isRevoked) {
|
||||||
// We already marked the denomination as revoked,
|
// We already marked the denomination as revoked,
|
||||||
// this implies we revoked all coins
|
// this implies we revoked all coins
|
||||||
console.log("denom already revoked");
|
logger.trace("denom already revoked");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
console.log("revoking denom", recoupInfo.h_denom_pub);
|
logger.trace("revoking denom", recoupInfo.h_denom_pub);
|
||||||
oldDenom.isRevoked = true;
|
oldDenom.isRevoked = true;
|
||||||
await tx.put(Stores.denominations, oldDenom);
|
await tx.put(Stores.denominations, oldDenom);
|
||||||
const affectedCoins = await tx
|
const affectedCoins = await tx
|
||||||
@ -236,7 +236,7 @@ async function updateExchangeWithKeys(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newlyRevokedCoinPubs.length != 0) {
|
if (newlyRevokedCoinPubs.length != 0) {
|
||||||
console.log("recouping coins", newlyRevokedCoinPubs);
|
logger.trace("recouping coins", newlyRevokedCoinPubs);
|
||||||
await createRecoupGroup(ws, tx, newlyRevokedCoinPubs);
|
await createRecoupGroup(ws, tx, newlyRevokedCoinPubs);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -246,7 +246,7 @@ async function updateExchangeWithKeys(
|
|||||||
// Asynchronously start recoup. This doesn't need to finish
|
// Asynchronously start recoup. This doesn't need to finish
|
||||||
// for the exchange update to be considered finished.
|
// for the exchange update to be considered finished.
|
||||||
processRecoupGroup(ws, recoupGroupId).catch((e) => {
|
processRecoupGroup(ws, recoupGroupId).catch((e) => {
|
||||||
console.log("error while recouping coins:", e);
|
logger.error("error while recouping coins:", e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,7 +353,7 @@ async function getCoinsForPayment(
|
|||||||
throw Error("db inconsistent");
|
throw Error("db inconsistent");
|
||||||
}
|
}
|
||||||
if (denom.value.currency !== currency) {
|
if (denom.value.currency !== currency) {
|
||||||
console.warn(
|
logger.warn(
|
||||||
`same pubkey for different currencies at exchange ${exchange.baseUrl}`,
|
`same pubkey for different currencies at exchange ${exchange.baseUrl}`,
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -539,7 +539,7 @@ async function incrementPurchasePayRetry(
|
|||||||
proposalId: string,
|
proposalId: string,
|
||||||
err: OperationErrorDetails | undefined,
|
err: OperationErrorDetails | undefined,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log("incrementing purchase pay retry with error", err);
|
logger.warn("incrementing purchase pay retry with error", err);
|
||||||
await ws.db.runWithWriteTransaction([Stores.purchases], async (tx) => {
|
await ws.db.runWithWriteTransaction([Stores.purchases], async (tx) => {
|
||||||
const pr = await tx.get(Stores.purchases, proposalId);
|
const pr = await tx.get(Stores.purchases, proposalId);
|
||||||
if (!pr) {
|
if (!pr) {
|
||||||
@ -693,7 +693,7 @@ async function processDownloadProposalImpl(
|
|||||||
fulfillmentUrl,
|
fulfillmentUrl,
|
||||||
);
|
);
|
||||||
if (differentPurchase) {
|
if (differentPurchase) {
|
||||||
console.log("repurchase detected");
|
logger.warn("repurchase detected");
|
||||||
p.proposalStatus = ProposalStatus.REPURCHASE;
|
p.proposalStatus = ProposalStatus.REPURCHASE;
|
||||||
p.repurchaseProposalId = differentPurchase.proposalId;
|
p.repurchaseProposalId = differentPurchase.proposalId;
|
||||||
await tx.put(Stores.proposals, p);
|
await tx.put(Stores.proposals, p);
|
||||||
@ -814,7 +814,7 @@ export async function submitPay(
|
|||||||
merchantPub,
|
merchantPub,
|
||||||
);
|
);
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
console.error("merchant payment signature invalid");
|
logger.error("merchant payment signature invalid");
|
||||||
// FIXME: properly display error
|
// FIXME: properly display error
|
||||||
throw Error("merchant payment signature invalid");
|
throw Error("merchant payment signature invalid");
|
||||||
}
|
}
|
||||||
@ -826,7 +826,7 @@ export async function submitPay(
|
|||||||
if (isFirst) {
|
if (isFirst) {
|
||||||
const ar = purchase.contractData.autoRefund;
|
const ar = purchase.contractData.autoRefund;
|
||||||
if (ar) {
|
if (ar) {
|
||||||
console.log("auto_refund present");
|
logger.info("auto_refund present");
|
||||||
purchase.refundStatusRequested = true;
|
purchase.refundStatusRequested = true;
|
||||||
purchase.refundStatusRetryInfo = initRetryInfo();
|
purchase.refundStatusRetryInfo = initRetryInfo();
|
||||||
purchase.lastRefundStatusError = undefined;
|
purchase.lastRefundStatusError = undefined;
|
||||||
@ -899,7 +899,7 @@ export async function preparePayForUri(
|
|||||||
if (!existingProposalId) {
|
if (!existingProposalId) {
|
||||||
throw Error("invalid proposal state");
|
throw Error("invalid proposal state");
|
||||||
}
|
}
|
||||||
console.log("using existing purchase for same product");
|
logger.trace("using existing purchase for same product");
|
||||||
proposal = await ws.db.get(Stores.proposals, existingProposalId);
|
proposal = await ws.db.get(Stores.proposals, existingProposalId);
|
||||||
if (!proposal) {
|
if (!proposal) {
|
||||||
throw Error("existing proposal is in wrong state");
|
throw Error("existing proposal is in wrong state");
|
||||||
@ -907,7 +907,7 @@ export async function preparePayForUri(
|
|||||||
}
|
}
|
||||||
const d = proposal.download;
|
const d = proposal.download;
|
||||||
if (!d) {
|
if (!d) {
|
||||||
console.error("bad proposal", proposal);
|
logger.error("bad proposal", proposal);
|
||||||
throw Error("proposal is in invalid state");
|
throw Error("proposal is in invalid state");
|
||||||
}
|
}
|
||||||
const contractData = d.contractData;
|
const contractData = d.contractData;
|
||||||
|
@ -51,6 +51,9 @@ import { getTimestampNow } from "../util/time";
|
|||||||
import { guardOperationException } from "./errors";
|
import { guardOperationException } from "./errors";
|
||||||
import { readSuccessResponseJsonOrThrow } from "../util/http";
|
import { readSuccessResponseJsonOrThrow } from "../util/http";
|
||||||
import { URL } from "../util/url";
|
import { URL } from "../util/url";
|
||||||
|
import { Logger } from "../util/logging";
|
||||||
|
|
||||||
|
const logger = new Logger("operations/recoup.ts");
|
||||||
|
|
||||||
async function incrementRecoupRetry(
|
async function incrementRecoupRetry(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
@ -207,7 +210,7 @@ async function recoupWithdrawCoin(
|
|||||||
});
|
});
|
||||||
|
|
||||||
forceQueryReserve(ws, reserve.reservePub).catch((e) => {
|
forceQueryReserve(ws, reserve.reservePub).catch((e) => {
|
||||||
console.log("re-querying reserve after recoup failed:", e);
|
logger.error("re-querying reserve after recoup failed:", e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +227,7 @@ async function recoupRefreshCoin(
|
|||||||
|
|
||||||
const recoupRequest = await ws.cryptoApi.createRecoupRequest(coin);
|
const recoupRequest = await ws.cryptoApi.createRecoupRequest(coin);
|
||||||
const reqUrl = new URL(`/coins/${coin.coinPub}/recoup`, coin.exchangeBaseUrl);
|
const reqUrl = new URL(`/coins/${coin.coinPub}/recoup`, coin.exchangeBaseUrl);
|
||||||
console.log("making recoup request");
|
logger.trace("making recoup request");
|
||||||
|
|
||||||
const resp = await ws.http.postJson(reqUrl.href, recoupRequest);
|
const resp = await ws.http.postJson(reqUrl.href, recoupRequest);
|
||||||
const recoupConfirmation = await readSuccessResponseJsonOrThrow(
|
const recoupConfirmation = await readSuccessResponseJsonOrThrow(
|
||||||
@ -270,7 +273,7 @@ async function recoupRefreshCoin(
|
|||||||
oldCoin.currentAmount,
|
oldCoin.currentAmount,
|
||||||
recoupGroup.oldAmountPerCoin[coinIdx],
|
recoupGroup.oldAmountPerCoin[coinIdx],
|
||||||
).amount;
|
).amount;
|
||||||
console.log(
|
logger.trace(
|
||||||
"recoup: setting old coin amount to",
|
"recoup: setting old coin amount to",
|
||||||
Amounts.stringify(oldCoin.currentAmount),
|
Amounts.stringify(oldCoin.currentAmount),
|
||||||
);
|
);
|
||||||
@ -317,14 +320,12 @@ async function processRecoupGroupImpl(
|
|||||||
if (forceNow) {
|
if (forceNow) {
|
||||||
await resetRecoupGroupRetry(ws, recoupGroupId);
|
await resetRecoupGroupRetry(ws, recoupGroupId);
|
||||||
}
|
}
|
||||||
console.log("in processRecoupGroupImpl");
|
|
||||||
const recoupGroup = await ws.db.get(Stores.recoupGroups, recoupGroupId);
|
const recoupGroup = await ws.db.get(Stores.recoupGroups, recoupGroupId);
|
||||||
if (!recoupGroup) {
|
if (!recoupGroup) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(recoupGroup);
|
|
||||||
if (recoupGroup.timestampFinished) {
|
if (recoupGroup.timestampFinished) {
|
||||||
console.log("recoup group finished");
|
logger.trace("recoup group finished");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ps = recoupGroup.coinPubs.map((x, i) =>
|
const ps = recoupGroup.coinPubs.map((x, i) =>
|
||||||
|
@ -188,7 +188,7 @@ async function refreshCreateSession(
|
|||||||
}
|
}
|
||||||
const r = Amounts.sub(c.currentAmount, refreshSession.amountRefreshInput);
|
const r = Amounts.sub(c.currentAmount, refreshSession.amountRefreshInput);
|
||||||
if (r.saturated) {
|
if (r.saturated) {
|
||||||
console.log("can't refresh coin, no amount left");
|
logger.warn("can't refresh coin, no amount left");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
c.currentAmount = r.amount;
|
c.currentAmount = r.amount;
|
||||||
@ -387,7 +387,7 @@ async function refreshReveal(
|
|||||||
async (tx) => {
|
async (tx) => {
|
||||||
const rg = await tx.get(Stores.refreshGroups, refreshGroupId);
|
const rg = await tx.get(Stores.refreshGroups, refreshGroupId);
|
||||||
if (!rg) {
|
if (!rg) {
|
||||||
console.log("no refresh session found");
|
logger.warn("no refresh session found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const rs = rg.refreshSessionPerCoin[coinIndex];
|
const rs = rg.refreshSessionPerCoin[coinIndex];
|
||||||
@ -395,7 +395,7 @@ async function refreshReveal(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (rs.finishedTimestamp) {
|
if (rs.finishedTimestamp) {
|
||||||
console.log("refresh session already finished");
|
logger.warn("refresh session already finished");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rs.finishedTimestamp = getTimestampNow();
|
rs.finishedTimestamp = getTimestampNow();
|
||||||
@ -417,7 +417,7 @@ async function refreshReveal(
|
|||||||
await tx.put(Stores.refreshGroups, rg);
|
await tx.put(Stores.refreshGroups, rg);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
console.log("refresh finished (end of reveal)");
|
logger.trace("refresh finished (end of reveal)");
|
||||||
ws.notify({
|
ws.notify({
|
||||||
type: NotificationType.RefreshRevealed,
|
type: NotificationType.RefreshRevealed,
|
||||||
});
|
});
|
||||||
|
@ -50,7 +50,7 @@ import {
|
|||||||
AmountString,
|
AmountString,
|
||||||
} from "../types/talerTypes";
|
} from "../types/talerTypes";
|
||||||
import { guardOperationException } from "./errors";
|
import { guardOperationException } from "./errors";
|
||||||
import { getTimestampNow } from "../util/time";
|
import { getTimestampNow, Timestamp } from "../util/time";
|
||||||
import { Logger } from "../util/logging";
|
import { Logger } from "../util/logging";
|
||||||
import { readSuccessResponseJsonOrThrow } from "../util/http";
|
import { readSuccessResponseJsonOrThrow } from "../util/http";
|
||||||
import { TransactionHandle } from "../util/query";
|
import { TransactionHandle } from "../util/query";
|
||||||
@ -142,6 +142,7 @@ async function applySuccessfulRefund(
|
|||||||
|
|
||||||
p.refunds[refundKey] = {
|
p.refunds[refundKey] = {
|
||||||
type: RefundState.Applied,
|
type: RefundState.Applied,
|
||||||
|
obtainedTime: getTimestampNow(),
|
||||||
executionTime: r.execution_time,
|
executionTime: r.execution_time,
|
||||||
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
||||||
refundFee: denom.feeRefund,
|
refundFee: denom.feeRefund,
|
||||||
@ -191,6 +192,7 @@ async function storePendingRefund(
|
|||||||
|
|
||||||
p.refunds[refundKey] = {
|
p.refunds[refundKey] = {
|
||||||
type: RefundState.Pending,
|
type: RefundState.Pending,
|
||||||
|
obtainedTime: getTimestampNow(),
|
||||||
executionTime: r.execution_time,
|
executionTime: r.execution_time,
|
||||||
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
refundAmount: Amounts.parseOrThrow(r.refund_amount),
|
||||||
refundFee: denom.feeRefund,
|
refundFee: denom.feeRefund,
|
||||||
|
@ -178,7 +178,7 @@ export async function createReserve(
|
|||||||
const exchangeInfo = await updateExchangeFromUrl(ws, req.exchange);
|
const exchangeInfo = await updateExchangeFromUrl(ws, req.exchange);
|
||||||
const exchangeDetails = exchangeInfo.details;
|
const exchangeDetails = exchangeInfo.details;
|
||||||
if (!exchangeDetails) {
|
if (!exchangeDetails) {
|
||||||
console.log(exchangeDetails);
|
logger.trace(exchangeDetails);
|
||||||
throw Error("exchange not updated");
|
throw Error("exchange not updated");
|
||||||
}
|
}
|
||||||
const { isAudited, isTrusted } = await getExchangeTrust(ws, exchangeInfo);
|
const { isAudited, isTrusted } = await getExchangeTrust(ws, exchangeInfo);
|
||||||
@ -576,7 +576,7 @@ async function processReserveImpl(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const reserve = await ws.db.get(Stores.reserves, reservePub);
|
const reserve = await ws.db.get(Stores.reserves, reservePub);
|
||||||
if (!reserve) {
|
if (!reserve) {
|
||||||
console.log("not processing reserve: reserve does not exist");
|
logger.trace("not processing reserve: reserve does not exist");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!forceNow) {
|
if (!forceNow) {
|
||||||
|
@ -269,11 +269,11 @@ async function makePayment(
|
|||||||
"taler://fulfillment-success/thx",
|
"taler://fulfillment-success/thx",
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("created order with orderId", orderResp.orderId);
|
logger.trace("created order with orderId", orderResp.orderId);
|
||||||
|
|
||||||
let paymentStatus = await checkPayment(http, merchant, orderResp.orderId);
|
let paymentStatus = await checkPayment(http, merchant, orderResp.orderId);
|
||||||
|
|
||||||
console.log("payment status", paymentStatus);
|
logger.trace("payment status", paymentStatus);
|
||||||
|
|
||||||
const talerPayUri = paymentStatus.taler_pay_uri;
|
const talerPayUri = paymentStatus.taler_pay_uri;
|
||||||
if (!talerPayUri) {
|
if (!talerPayUri) {
|
||||||
@ -282,7 +282,7 @@ async function makePayment(
|
|||||||
|
|
||||||
const preparePayResult = await wallet.preparePayForUri(talerPayUri);
|
const preparePayResult = await wallet.preparePayForUri(talerPayUri);
|
||||||
|
|
||||||
console.log("prepare pay result", preparePayResult);
|
logger.trace("prepare pay result", preparePayResult);
|
||||||
|
|
||||||
if (preparePayResult.status != "payment-possible") {
|
if (preparePayResult.status != "payment-possible") {
|
||||||
throw Error("payment not possible");
|
throw Error("payment not possible");
|
||||||
@ -293,11 +293,11 @@ async function makePayment(
|
|||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("confirmPayResult", confirmPayResult);
|
logger.trace("confirmPayResult", confirmPayResult);
|
||||||
|
|
||||||
paymentStatus = await checkPayment(http, merchant, orderResp.orderId);
|
paymentStatus = await checkPayment(http, merchant, orderResp.orderId);
|
||||||
|
|
||||||
console.log("payment status after wallet payment:", paymentStatus);
|
logger.trace("payment status after wallet payment:", paymentStatus);
|
||||||
|
|
||||||
if (paymentStatus.order_status !== "paid") {
|
if (paymentStatus.order_status !== "paid") {
|
||||||
throw Error("payment did not succeed");
|
throw Error("payment did not succeed");
|
||||||
@ -318,26 +318,18 @@ export async function runIntegrationTest(
|
|||||||
const parsedSpendAmount = Amounts.parseOrThrow(args.amountToSpend);
|
const parsedSpendAmount = Amounts.parseOrThrow(args.amountToSpend);
|
||||||
const currency = parsedSpendAmount.currency;
|
const currency = parsedSpendAmount.currency;
|
||||||
|
|
||||||
const myHttpLib = new NodeHttpLib();
|
|
||||||
myHttpLib.setThrottling(false);
|
|
||||||
|
|
||||||
const myWallet = await getDefaultNodeWallet({ httpLib: myHttpLib });
|
|
||||||
|
|
||||||
myWallet.runRetryLoop().catch((e) => {
|
|
||||||
console.error("exception during retry loop:", e);
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.info("withdrawing test balance");
|
logger.info("withdrawing test balance");
|
||||||
await wallet.withdrawTestBalance(
|
await wallet.withdrawTestBalance({
|
||||||
args.amountToWithdraw,
|
amount: args.amountToWithdraw,
|
||||||
args.bankBaseUrl,
|
bankBaseUrl: args.bankBaseUrl,
|
||||||
args.exchangeBaseUrl,
|
exchangeBaseUrl: args.exchangeBaseUrl,
|
||||||
);
|
});
|
||||||
|
await wallet.runUntilDone();
|
||||||
logger.info("done withdrawing test balance");
|
logger.info("done withdrawing test balance");
|
||||||
|
|
||||||
const balance = await myWallet.getBalances();
|
const balance = await wallet.getBalances();
|
||||||
|
|
||||||
console.log(JSON.stringify(balance, null, 2));
|
logger.trace(JSON.stringify(balance, null, 2));
|
||||||
|
|
||||||
const myMerchant: MerchantBackendInfo = {
|
const myMerchant: MerchantBackendInfo = {
|
||||||
baseUrl: args.merchantBaseUrl,
|
baseUrl: args.merchantBaseUrl,
|
||||||
@ -353,26 +345,26 @@ export async function runIntegrationTest(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Wait until the refresh is done
|
// Wait until the refresh is done
|
||||||
await myWallet.runUntilDone();
|
await wallet.runUntilDone();
|
||||||
|
|
||||||
console.log("withdrawing test balance for refund");
|
logger.trace("withdrawing test balance for refund");
|
||||||
const withdrawAmountTwo = Amounts.parseOrThrow(`${currency}:18`);
|
const withdrawAmountTwo = Amounts.parseOrThrow(`${currency}:18`);
|
||||||
const spendAmountTwo = Amounts.parseOrThrow(`${currency}:7`);
|
const spendAmountTwo = Amounts.parseOrThrow(`${currency}:7`);
|
||||||
const refundAmount = Amounts.parseOrThrow(`${currency}:6`);
|
const refundAmount = Amounts.parseOrThrow(`${currency}:6`);
|
||||||
const spendAmountThree = Amounts.parseOrThrow(`${currency}:3`);
|
const spendAmountThree = Amounts.parseOrThrow(`${currency}:3`);
|
||||||
|
|
||||||
await myWallet.withdrawTestBalance(
|
await wallet.withdrawTestBalance({
|
||||||
Amounts.stringify(withdrawAmountTwo),
|
amount: Amounts.stringify(withdrawAmountTwo),
|
||||||
args.bankBaseUrl,
|
bankBaseUrl: args.bankBaseUrl,
|
||||||
args.exchangeBaseUrl,
|
exchangeBaseUrl: args.exchangeBaseUrl,
|
||||||
);
|
});
|
||||||
|
|
||||||
// Wait until the withdraw is done
|
// Wait until the withdraw is done
|
||||||
await myWallet.runUntilDone();
|
await wallet.runUntilDone();
|
||||||
|
|
||||||
const { orderId: refundOrderId } = await makePayment(
|
const { orderId: refundOrderId } = await makePayment(
|
||||||
http,
|
http,
|
||||||
myWallet,
|
wallet,
|
||||||
myMerchant,
|
myMerchant,
|
||||||
Amounts.stringify(spendAmountTwo),
|
Amounts.stringify(spendAmountTwo),
|
||||||
"order that will be refunded",
|
"order that will be refunded",
|
||||||
@ -386,22 +378,30 @@ export async function runIntegrationTest(
|
|||||||
Amounts.stringify(refundAmount),
|
Amounts.stringify(refundAmount),
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("refund URI", refundUri);
|
logger.trace("refund URI", refundUri);
|
||||||
|
|
||||||
await myWallet.applyRefund(refundUri);
|
await wallet.applyRefund(refundUri);
|
||||||
|
|
||||||
|
logger.trace("integration test: applied refund");
|
||||||
|
|
||||||
// Wait until the refund is done
|
// Wait until the refund is done
|
||||||
await myWallet.runUntilDone();
|
await wallet.runUntilDone();
|
||||||
|
|
||||||
|
logger.trace("integration test: making payment after refund");
|
||||||
|
|
||||||
await makePayment(
|
await makePayment(
|
||||||
http,
|
http,
|
||||||
myWallet,
|
wallet,
|
||||||
myMerchant,
|
myMerchant,
|
||||||
Amounts.stringify(spendAmountThree),
|
Amounts.stringify(spendAmountThree),
|
||||||
"payment after refund",
|
"payment after refund",
|
||||||
);
|
);
|
||||||
|
|
||||||
await myWallet.runUntilDone();
|
logger.trace("integration test: make payment done");
|
||||||
|
|
||||||
|
await wallet.runUntilDone();
|
||||||
|
|
||||||
|
logger.trace("integration test: all done!");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function testPay(
|
export async function testPay(
|
||||||
@ -409,8 +409,8 @@ export async function testPay(
|
|||||||
wallet: Wallet,
|
wallet: Wallet,
|
||||||
args: TestPayArgs,
|
args: TestPayArgs,
|
||||||
) {
|
) {
|
||||||
console.log("creating order");
|
logger.trace("creating order");
|
||||||
const merchant = { apikey: args.apikey, baseUrl: args.merchant };
|
const merchant = { apikey: args.merchantApiKey, baseUrl: args.merchantBaseUrl };
|
||||||
const orderResp = await createOrder(
|
const orderResp = await createOrder(
|
||||||
http,
|
http,
|
||||||
merchant,
|
merchant,
|
||||||
@ -418,7 +418,7 @@ export async function testPay(
|
|||||||
args.summary,
|
args.summary,
|
||||||
"taler://fulfillment-success/thank+you",
|
"taler://fulfillment-success/thank+you",
|
||||||
);
|
);
|
||||||
console.log("created new order with order ID", orderResp.orderId);
|
logger.trace("created new order with order ID", orderResp.orderId);
|
||||||
const checkPayResp = await checkPayment(http, merchant, orderResp.orderId);
|
const checkPayResp = await checkPayment(http, merchant, orderResp.orderId);
|
||||||
const talerPayUri = checkPayResp.taler_pay_uri;
|
const talerPayUri = checkPayResp.taler_pay_uri;
|
||||||
if (!talerPayUri) {
|
if (!talerPayUri) {
|
||||||
@ -426,7 +426,7 @@ export async function testPay(
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("taler pay URI:", talerPayUri);
|
logger.trace("taler pay URI:", talerPayUri);
|
||||||
const result = await wallet.preparePayForUri(talerPayUri);
|
const result = await wallet.preparePayForUri(talerPayUri);
|
||||||
if (result.status !== PreparePayResultType.PaymentPossible) {
|
if (result.status !== PreparePayResultType.PaymentPossible) {
|
||||||
throw Error(`unexpected prepare pay status: ${result.status}`);
|
throw Error(`unexpected prepare pay status: ${result.status}`);
|
||||||
|
@ -45,6 +45,9 @@ import { NotificationType } from "../types/notifications";
|
|||||||
import { getTimestampNow } from "../util/time";
|
import { getTimestampNow } from "../util/time";
|
||||||
import { readSuccessResponseJsonOrThrow } from "../util/http";
|
import { readSuccessResponseJsonOrThrow } from "../util/http";
|
||||||
import { URL } from "../util/url";
|
import { URL } from "../util/url";
|
||||||
|
import { Logger } from "../util/logging";
|
||||||
|
|
||||||
|
const logger = new Logger("operations/tip.ts");
|
||||||
|
|
||||||
export async function getTipStatus(
|
export async function getTipStatus(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
@ -57,13 +60,13 @@ export async function getTipStatus(
|
|||||||
|
|
||||||
const tipStatusUrl = new URL("tip-pickup", res.merchantBaseUrl);
|
const tipStatusUrl = new URL("tip-pickup", res.merchantBaseUrl);
|
||||||
tipStatusUrl.searchParams.set("tip_id", res.merchantTipId);
|
tipStatusUrl.searchParams.set("tip_id", res.merchantTipId);
|
||||||
console.log("checking tip status from", tipStatusUrl.href);
|
logger.trace("checking tip status from", tipStatusUrl.href);
|
||||||
const merchantResp = await ws.http.get(tipStatusUrl.href);
|
const merchantResp = await ws.http.get(tipStatusUrl.href);
|
||||||
const tipPickupStatus = await readSuccessResponseJsonOrThrow(
|
const tipPickupStatus = await readSuccessResponseJsonOrThrow(
|
||||||
merchantResp,
|
merchantResp,
|
||||||
codecForTipPickupGetResponse(),
|
codecForTipPickupGetResponse(),
|
||||||
);
|
);
|
||||||
console.log("status", tipPickupStatus);
|
logger.trace(`status ${tipPickupStatus}`);
|
||||||
|
|
||||||
const amount = Amounts.parseOrThrow(tipPickupStatus.amount);
|
const amount = Amounts.parseOrThrow(tipPickupStatus.amount);
|
||||||
|
|
||||||
@ -191,7 +194,7 @@ async function processTipImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tipRecord.pickedUp) {
|
if (tipRecord.pickedUp) {
|
||||||
console.log("tip already picked up");
|
logger.warn("tip already picked up");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +233,7 @@ async function processTipImpl(
|
|||||||
throw Error("invariant violated");
|
throw Error("invariant violated");
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("got planchets for tip!");
|
logger.trace("got planchets for tip!");
|
||||||
|
|
||||||
// Planchets in the form that the merchant expects
|
// Planchets in the form that the merchant expects
|
||||||
const planchetsDetail: TipPlanchetDetail[] = tipRecord.planchets.map((p) => ({
|
const planchetsDetail: TipPlanchetDetail[] = tipRecord.planchets.map((p) => ({
|
||||||
@ -248,9 +251,9 @@ async function processTipImpl(
|
|||||||
if (merchantResp.status !== 200) {
|
if (merchantResp.status !== 200) {
|
||||||
throw Error(`unexpected status ${merchantResp.status} for tip-pickup`);
|
throw Error(`unexpected status ${merchantResp.status} for tip-pickup`);
|
||||||
}
|
}
|
||||||
console.log("got merchant resp:", merchantResp);
|
logger.trace("got merchant resp:", merchantResp);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("tipping failed", e);
|
logger.warn("tipping failed", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +334,7 @@ export async function acceptTip(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const tipRecord = await ws.db.get(Stores.tips, tipId);
|
const tipRecord = await ws.db.get(Stores.tips, tipId);
|
||||||
if (!tipRecord) {
|
if (!tipRecord) {
|
||||||
console.log("tip not found");
|
logger.error("tip not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ import {
|
|||||||
RefundState,
|
RefundState,
|
||||||
} from "../types/dbTypes";
|
} from "../types/dbTypes";
|
||||||
import { Amounts, AmountJson } from "../util/amounts";
|
import { Amounts, AmountJson } from "../util/amounts";
|
||||||
import { timestampCmp } from "../util/time";
|
import { timestampCmp, Timestamp } from "../util/time";
|
||||||
import {
|
import {
|
||||||
TransactionsRequest,
|
TransactionsRequest,
|
||||||
TransactionsResponse,
|
TransactionsResponse,
|
||||||
@ -297,12 +297,13 @@ export async function getTransactions(
|
|||||||
if (!r0) {
|
if (!r0) {
|
||||||
throw Error("invariant violated");
|
throw Error("invariant violated");
|
||||||
}
|
}
|
||||||
|
let ts: Timestamp;
|
||||||
transactions.push({
|
transactions.push({
|
||||||
type: TransactionType.Refund,
|
type: TransactionType.Refund,
|
||||||
info,
|
info,
|
||||||
refundedTransactionId: paymentTransactionId,
|
refundedTransactionId: paymentTransactionId,
|
||||||
transactionId: refundTransactionId,
|
transactionId: refundTransactionId,
|
||||||
timestamp: r0.executionTime,
|
timestamp: r0.obtainedTime,
|
||||||
amountEffective: Amounts.stringify(amountEffective),
|
amountEffective: Amounts.stringify(amountEffective),
|
||||||
amountRaw: Amounts.stringify(amountRaw),
|
amountRaw: Amounts.stringify(amountRaw),
|
||||||
pending: false,
|
pending: false,
|
||||||
|
@ -1164,7 +1164,14 @@ export type WalletRefundItem =
|
|||||||
| WalletRefundAppliedItem;
|
| WalletRefundAppliedItem;
|
||||||
|
|
||||||
export interface WalletRefundItemCommon {
|
export interface WalletRefundItemCommon {
|
||||||
|
// Execution time as claimed by the merchant
|
||||||
executionTime: Timestamp;
|
executionTime: Timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time when the wallet became aware of the refund.
|
||||||
|
*/
|
||||||
|
obtainedTime: Timestamp;
|
||||||
|
|
||||||
refundAmount: AmountJson;
|
refundAmount: AmountJson;
|
||||||
refundFee: AmountJson;
|
refundFee: AmountJson;
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@ import {
|
|||||||
buildCodecForObject,
|
buildCodecForObject,
|
||||||
codecOptional,
|
codecOptional,
|
||||||
codecForString,
|
codecForString,
|
||||||
|
codecForList,
|
||||||
|
codecForAny,
|
||||||
} from "../util/codec";
|
} from "../util/codec";
|
||||||
|
|
||||||
export interface TransactionsRequest {
|
export interface TransactionsRequest {
|
||||||
@ -309,3 +311,9 @@ export const codecForTransactionsRequest = (): Codec<TransactionsRequest> =>
|
|||||||
.property("currency", codecOptional(codecForString()))
|
.property("currency", codecOptional(codecForString()))
|
||||||
.property("search", codecOptional(codecForString()))
|
.property("search", codecOptional(codecForString()))
|
||||||
.build("TransactionsRequest");
|
.build("TransactionsRequest");
|
||||||
|
|
||||||
|
// FIXME: do full validation here!
|
||||||
|
export const codecForTransactionsResponse = (): Codec<TransactionsResponse> =>
|
||||||
|
buildCodecForObject<TransactionsResponse>()
|
||||||
|
.property("transactions", codecForList(codecForAny()))
|
||||||
|
.build("TransactionsResponse");
|
@ -653,16 +653,16 @@ export interface GetExchangeTosResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TestPayArgs {
|
export interface TestPayArgs {
|
||||||
merchant: string;
|
merchantBaseUrl: string;
|
||||||
apikey: string;
|
merchantApiKey: string;
|
||||||
amount: string;
|
amount: string;
|
||||||
summary: string;
|
summary: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const codecForTestPayArgs = (): Codec<TestPayArgs> =>
|
export const codecForTestPayArgs = (): Codec<TestPayArgs> =>
|
||||||
buildCodecForObject<TestPayArgs>()
|
buildCodecForObject<TestPayArgs>()
|
||||||
.property("merchant", codecForString())
|
.property("merchantBaseUrl", codecForString())
|
||||||
.property("apikey", codecForString())
|
.property("merchantApiKey", codecForString())
|
||||||
.property("amount", codecForString())
|
.property("amount", codecForString())
|
||||||
.property("summary", codecForString())
|
.property("summary", codecForString())
|
||||||
.build("TestPayArgs");
|
.build("TestPayArgs");
|
||||||
@ -829,3 +829,22 @@ export interface CoreApiResponseError {
|
|||||||
id: string;
|
id: string;
|
||||||
error: OperationErrorDetails;
|
error: OperationErrorDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WithdrawTestBalanceRequest {
|
||||||
|
amount: string;
|
||||||
|
bankBaseUrl: string;
|
||||||
|
exchangeBaseUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const withdrawTestBalanceDefaults = {
|
||||||
|
amount: "TESTKUDOS:10",
|
||||||
|
bankBaseUrl: "https://bank.test.taler.net/",
|
||||||
|
exchangeBaseUrl: "https://exchange.test.taler.net/",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const codecForWithdrawTestBalance = (): Codec<WithdrawTestBalanceRequest> =>
|
||||||
|
buildCodecForObject<WithdrawTestBalanceRequest>()
|
||||||
|
.property("amount", codecForString())
|
||||||
|
.property("bankBaseUrl", codecForString())
|
||||||
|
.property("exchangeBaseUrl", codecForString())
|
||||||
|
.build("WithdrawTestBalanceRequest");
|
||||||
|
@ -23,6 +23,9 @@
|
|||||||
*/
|
*/
|
||||||
import { getTimestampNow, timestampDifference } from "../util/time";
|
import { getTimestampNow, timestampDifference } from "../util/time";
|
||||||
import { URL } from "./url";
|
import { URL } from "./url";
|
||||||
|
import { Logger } from "./logging";
|
||||||
|
|
||||||
|
const logger = new Logger("RequestThrottler.ts");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum request per second, per origin.
|
* Maximum request per second, per origin.
|
||||||
@ -77,15 +80,15 @@ class OriginState {
|
|||||||
applyThrottle(): boolean {
|
applyThrottle(): boolean {
|
||||||
this.refill();
|
this.refill();
|
||||||
if (this.tokensSecond < 1) {
|
if (this.tokensSecond < 1) {
|
||||||
console.log("request throttled (per second limit exceeded)");
|
logger.warn("request throttled (per second limit exceeded)");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.tokensMinute < 1) {
|
if (this.tokensMinute < 1) {
|
||||||
console.log("request throttled (per minute limit exceeded)");
|
logger.warn("request throttled (per minute limit exceeded)");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.tokensHour < 1) {
|
if (this.tokensHour < 1) {
|
||||||
console.log("request throttled (per hour limit exceeded)");
|
logger.warn("request throttled (per hour limit exceeded)");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
this.tokensSecond--;
|
this.tokensSecond--;
|
||||||
|
@ -35,6 +35,10 @@ import {
|
|||||||
Event,
|
Event,
|
||||||
IDBCursor,
|
IDBCursor,
|
||||||
} from "idb-bridge";
|
} from "idb-bridge";
|
||||||
|
import { Logger } from "./logging";
|
||||||
|
|
||||||
|
|
||||||
|
const logger = new Logger("query.ts");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception that should be thrown by client code to abort a transaction.
|
* Exception that should be thrown by client code to abort a transaction.
|
||||||
@ -72,9 +76,9 @@ function requestToPromise(req: IDBRequest): Promise<any> {
|
|||||||
resolve(req.result);
|
resolve(req.result);
|
||||||
};
|
};
|
||||||
req.onerror = () => {
|
req.onerror = () => {
|
||||||
console.log("error in DB request", req.error);
|
console.error("error in DB request", req.error);
|
||||||
reject(req.error);
|
reject(req.error);
|
||||||
console.log("Request failed:", stack);
|
console.error("Request failed:", stack);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -341,14 +345,14 @@ function runWithTransaction<T>(
|
|||||||
resolve(funResult);
|
resolve(funResult);
|
||||||
};
|
};
|
||||||
tx.onerror = () => {
|
tx.onerror = () => {
|
||||||
console.error("error in transaction");
|
logger.error("error in transaction");
|
||||||
console.error(stack);
|
logger.error(`${stack}`);
|
||||||
};
|
};
|
||||||
tx.onabort = () => {
|
tx.onabort = () => {
|
||||||
if (tx.error) {
|
if (tx.error) {
|
||||||
console.error("Transaction aborted with error:", tx.error);
|
logger.error("Transaction aborted with error:", tx.error);
|
||||||
} else {
|
} else {
|
||||||
console.log("Trasaction aborted (no error)");
|
logger.error("Trasaction aborted (no error)");
|
||||||
}
|
}
|
||||||
reject(TransactionAbort);
|
reject(TransactionAbort);
|
||||||
};
|
};
|
||||||
@ -361,7 +365,7 @@ function runWithTransaction<T>(
|
|||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
if (e == TransactionAbort) {
|
if (e == TransactionAbort) {
|
||||||
console.info("aborting transaction");
|
logger.trace("aborting transaction");
|
||||||
} else {
|
} else {
|
||||||
console.error("Transaction failed:", e);
|
console.error("Transaction failed:", e);
|
||||||
console.error(stack);
|
console.error(stack);
|
||||||
@ -427,12 +431,12 @@ export function openDatabase(
|
|||||||
return new Promise<IDBDatabase>((resolve, reject) => {
|
return new Promise<IDBDatabase>((resolve, reject) => {
|
||||||
const req = idbFactory.open(databaseName, databaseVersion);
|
const req = idbFactory.open(databaseName, databaseVersion);
|
||||||
req.onerror = (e) => {
|
req.onerror = (e) => {
|
||||||
console.log("taler database error", e);
|
logger.error("taler database error", e);
|
||||||
reject(new Error("database error"));
|
reject(new Error("database error"));
|
||||||
};
|
};
|
||||||
req.onsuccess = (e) => {
|
req.onsuccess = (e) => {
|
||||||
req.result.onversionchange = (evt: IDBVersionChangeEvent) => {
|
req.result.onversionchange = (evt: IDBVersionChangeEvent) => {
|
||||||
console.log(
|
logger.info(
|
||||||
`handling live db version change from ${evt.oldVersion} to ${evt.newVersion}`,
|
`handling live db version change from ${evt.oldVersion} to ${evt.newVersion}`,
|
||||||
);
|
);
|
||||||
req.result.close();
|
req.result.close();
|
||||||
@ -491,7 +495,7 @@ export class Database {
|
|||||||
|
|
||||||
importDatabase(dump: any): Promise<void> {
|
importDatabase(dump: any): Promise<void> {
|
||||||
const db = this.db;
|
const db = this.db;
|
||||||
console.log("importing db", dump);
|
logger.info("importing db", dump);
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite");
|
const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite");
|
||||||
if (dump.stores) {
|
if (dump.stores) {
|
||||||
@ -501,7 +505,7 @@ export class Database {
|
|||||||
for (const key in dumpStore) {
|
for (const key in dumpStore) {
|
||||||
objects.push(dumpStore[key]);
|
objects.push(dumpStore[key]);
|
||||||
}
|
}
|
||||||
console.log(`importing ${objects.length} records into ${storeName}`);
|
logger.info(`importing ${objects.length} records into ${storeName}`);
|
||||||
const store = tx.objectStore(storeName);
|
const store = tx.objectStore(storeName);
|
||||||
for (const obj of objects) {
|
for (const obj of objects) {
|
||||||
store.put(obj);
|
store.put(obj);
|
||||||
|
@ -86,6 +86,10 @@ import {
|
|||||||
CoreApiResponse,
|
CoreApiResponse,
|
||||||
codecForPreparePayRequest,
|
codecForPreparePayRequest,
|
||||||
codecForIntegrationTestArgs,
|
codecForIntegrationTestArgs,
|
||||||
|
WithdrawTestBalanceRequest,
|
||||||
|
withdrawTestBalanceDefaults,
|
||||||
|
codecForWithdrawTestBalance,
|
||||||
|
codecForTestPayArgs,
|
||||||
} from "./types/walletTypes";
|
} from "./types/walletTypes";
|
||||||
import { Logger } from "./util/logging";
|
import { Logger } from "./util/logging";
|
||||||
|
|
||||||
@ -313,7 +317,7 @@ export class Wallet {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.runRetryLoop().catch((e) => {
|
this.runRetryLoop().catch((e) => {
|
||||||
console.log("exception in wallet retry loop");
|
logger.error("exception in wallet retry loop");
|
||||||
reject(e);
|
reject(e);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -377,7 +381,7 @@ export class Wallet {
|
|||||||
numPending,
|
numPending,
|
||||||
});
|
});
|
||||||
await Promise.race([timeout, this.latch.wait()]);
|
await Promise.race([timeout, this.latch.wait()]);
|
||||||
console.log("timeout done");
|
logger.trace("timeout done");
|
||||||
} else {
|
} else {
|
||||||
// FIXME: maybe be a bit smarter about executing these
|
// FIXME: maybe be a bit smarter about executing these
|
||||||
// operations in parallel?
|
// operations in parallel?
|
||||||
@ -899,11 +903,9 @@ export class Wallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async withdrawTestBalance(
|
async withdrawTestBalance(
|
||||||
amount = "TESTKUDOS:10",
|
req: WithdrawTestBalanceRequest,
|
||||||
bankBaseUrl = "https://bank.test.taler.net/",
|
|
||||||
exchangeBaseUrl = "https://exchange.test.taler.net/",
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await withdrawTestBalance(this.ws, amount, bankBaseUrl, exchangeBaseUrl);
|
await withdrawTestBalance(this.ws, req.amount, req.bankBaseUrl, req.exchangeBaseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
async runIntegrationtest(args: IntegrationTestArgs): Promise<void> {
|
async runIntegrationtest(args: IntegrationTestArgs): Promise<void> {
|
||||||
@ -924,7 +926,16 @@ export class Wallet {
|
|||||||
): Promise<Record<string, any>> {
|
): Promise<Record<string, any>> {
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case "withdrawTestkudos": {
|
case "withdrawTestkudos": {
|
||||||
await this.withdrawTestBalance();
|
await this.withdrawTestBalance({
|
||||||
|
amount: "TESTKUDOS:10",
|
||||||
|
bankBaseUrl: "https://bank.test.taler.net/",
|
||||||
|
exchangeBaseUrl: "https://exchange.test.taler.net/",
|
||||||
|
});
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case "withdrawTestBalance": {
|
||||||
|
const req = codecForWithdrawTestBalance().decode(payload);
|
||||||
|
await this.withdrawTestBalance(req);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
case "runIntegrationtest": {
|
case "runIntegrationtest": {
|
||||||
@ -933,8 +944,8 @@ export class Wallet {
|
|||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
case "testPay": {
|
case "testPay": {
|
||||||
const req = codecForIntegrationTestArgs().decode(payload);
|
const req = codecForTestPayArgs().decode(payload);
|
||||||
await this.runIntegrationtest(req);
|
await this.testPay(req);
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
case "getTransactions": {
|
case "getTransactions": {
|
||||||
|
@ -22,7 +22,9 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CryptoImplementation } from "taler-wallet-core";
|
import { CryptoImplementation, Logger } from "taler-wallet-core";
|
||||||
|
|
||||||
|
const logger = new Logger("browserWorkerEntry.ts");
|
||||||
|
|
||||||
const worker: Worker = (self as any) as Worker;
|
const worker: Worker = (self as any) as Worker;
|
||||||
|
|
||||||
@ -42,7 +44,7 @@ async function handleRequest(
|
|||||||
const result = (impl as any)[operation](...args);
|
const result = (impl as any)[operation](...args);
|
||||||
worker.postMessage({ result, id });
|
worker.postMessage({ result, id });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("error during operation", e);
|
logger.error("error during operation", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,10 +66,6 @@ worker.onmessage = (msg: MessageEvent) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CryptoImplementation.enableTracing) {
|
|
||||||
console.log("onmessage with", operation);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRequest(operation, id, args).catch((e) => {
|
handleRequest(operation, id, args).catch((e) => {
|
||||||
console.error("error in browsere worker", e);
|
console.error("error in browsere worker", e);
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user