diff options
-rw-r--r-- | src/headless/bank.ts | 143 | ||||
-rw-r--r-- | src/headless/helpers.ts | 50 | ||||
-rw-r--r-- | src/headless/integrationtest.ts | 327 | ||||
-rw-r--r-- | src/headless/merchant.ts | 145 | ||||
-rw-r--r-- | src/headless/taler-wallet-cli.ts | 286 | ||||
-rw-r--r-- | src/index.ts | 1 | ||||
-rw-r--r-- | src/util/http.ts | 22 | ||||
-rw-r--r-- | src/wallet.ts | 10 | ||||
-rw-r--r-- | src/walletCoreApiHandler.ts | 3 |
9 files changed, 36 insertions, 951 deletions
diff --git a/src/headless/bank.ts b/src/headless/bank.ts deleted file mode 100644 index 2177908a6..000000000 --- a/src/headless/bank.ts +++ /dev/null @@ -1,143 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2019 GNUnet e.V. - - 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/> - */ - -/** - * Helper functions to deal with the GNU Taler demo bank. - * - * Mostly useful for automated tests. - */ - -/** - * Imports. - */ -import Axios from "axios"; - -export interface BankUser { - username: string; - password: string; -} - -/** - * Generate a random alphanumeric ID. Does *not* use cryptographically - * secure randomness. - */ -function makeId(length: number): string { - let result = ""; - const characters = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - for (let i = 0; i < length; i++) { - result += characters.charAt(Math.floor(Math.random() * characters.length)); - } - return result; -} - -/** - * Helper function to generate the "Authorization" HTTP header. - */ -function makeAuth(username: string, password: string): string { - const auth = `${username}:${password}`; - const authEncoded: string = Buffer.from(auth).toString("base64"); - return `Basic ${authEncoded}`; -} - -/** - * Client for the Taler bank access API. - */ -export class Bank { - constructor(private bankBaseUrl: string) {} - - async generateWithdrawUri( - bankUser: BankUser, - amount: string, - ): Promise<string> { - const body = { - amount, - }; - - const reqUrl = new URL("api/withdraw-headless-uri", this.bankBaseUrl).href; - - const resp = await Axios({ - method: "post", - url: reqUrl, - data: body, - responseType: "json", - headers: { - Authorization: makeAuth(bankUser.username, bankUser.password), - }, - }); - - if (resp.status != 200) { - throw Error("failed to create bank reserve"); - } - - const withdrawUri = resp.data["taler_withdraw_uri"]; - if (!withdrawUri) { - throw Error("Bank's response did not include withdraw URI"); - } - return withdrawUri; - } - - async createReserve( - bankUser: BankUser, - amount: string, - reservePub: string, - exchangePaytoUri: string, - ): Promise<void> { - const reqUrl = new URL("testing/withdraw", this.bankBaseUrl).href; - - const body = { - username: bankUser, - amount, - reserve_pub: reservePub, - exchange_payto_uri: exchangePaytoUri, - }; - - const resp = await Axios({ - method: "post", - url: reqUrl, - data: body, - responseType: "json", - headers: { - Authorization: makeAuth(bankUser.username, bankUser.password), - }, - }); - - if (resp.status != 200) { - throw Error("failed to create bank reserve"); - } - } - - async registerRandomUser(): Promise<BankUser> { - const reqUrl = new URL("testing/register", this.bankBaseUrl).href; - const randId = makeId(8); - const bankUser: BankUser = { - username: `testuser-${randId}`, - password: `testpw-${randId}`, - }; - - const resp = await Axios({ - method: "post", - url: reqUrl, - data: bankUser, - responseType: "json", - }); - - if (resp.status != 200) { - throw Error("could not register bank user"); - } - return bankUser; - } -} diff --git a/src/headless/helpers.ts b/src/headless/helpers.ts index 3b2f65313..570ec9e69 100644 --- a/src/headless/helpers.ts +++ b/src/headless/helpers.ts @@ -26,18 +26,15 @@ import { Wallet } from "../wallet"; import { MemoryBackend, BridgeIDBFactory, shimIndexedDB } from "idb-bridge"; import { openTalerDatabase } from "../db"; import { HttpRequestLibrary } from "../util/http"; -import { Bank } from "./bank"; import fs from "fs"; import { NodeThreadCryptoWorkerFactory } from "../crypto/workers/nodeThreadWorker"; -import { WalletNotification, NotificationType } from "../types/notifications"; +import { WalletNotification } from "../types/notifications"; import { Database } from "../util/query"; import { NodeHttpLib } from "./NodeHttpLib"; import { Logger } from "../util/logging"; import { SynchronousCryptoWorkerFactory } from "../crypto/workers/synchronousWorker"; -import { WithdrawalSourceType } from "../types/dbTypes"; -import { Amounts } from "../util/amounts"; -const logger = new Logger("helpers.ts"); +const logger = new Logger("headless/helpers.ts"); export interface DefaultNodeWalletArgs { /** @@ -135,46 +132,3 @@ export async function getDefaultNodeWallet( } return w; } - -export async function withdrawTestBalance( - myWallet: Wallet, - amount = "TESTKUDOS:10", - bankBaseUrl = "https://bank.test.taler.net/", - exchangeBaseUrl = "https://exchange.test.taler.net/", -): Promise<void> { - await myWallet.updateExchangeFromUrl(exchangeBaseUrl, true); - const reserveResponse = await myWallet.acceptManualWithdrawal( - exchangeBaseUrl, - Amounts.parseOrThrow(amount), - ); - - const reservePub = reserveResponse.reservePub; - - const bank = new Bank(bankBaseUrl); - - const bankUser = await bank.registerRandomUser(); - - logger.trace(`Registered bank user ${JSON.stringify(bankUser)}`); - - const exchangePaytoUri = await myWallet.getExchangePaytoUri(exchangeBaseUrl, [ - "x-taler-bank", - ]); - - const donePromise = new Promise((resolve, reject) => { - myWallet.runRetryLoop().catch((x) => { - reject(x); - }); - myWallet.addNotificationListener((n) => { - if ( - n.type === NotificationType.WithdrawGroupFinished && - n.withdrawalSource.type === WithdrawalSourceType.Reserve && - n.withdrawalSource.reservePub === reservePub - ) { - resolve(); - } - }); - }); - - await bank.createReserve(bankUser, amount, reservePub, exchangePaytoUri); - await donePromise; -} diff --git a/src/headless/integrationtest.ts b/src/headless/integrationtest.ts deleted file mode 100644 index 8e1effbea..000000000 --- a/src/headless/integrationtest.ts +++ /dev/null @@ -1,327 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2019 GNUnet e.V. - - 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 tests against real Taler bank/exchange/merchant deployments. - */ - -import { getDefaultNodeWallet, withdrawTestBalance } from "./helpers"; -import { MerchantBackendConnection } from "./merchant"; -import { Logger } from "../util/logging"; -import { NodeHttpLib } from "./NodeHttpLib"; -import { Wallet } from "../wallet"; -import { Configuration } from "../util/talerconfig"; -import { Amounts, AmountJson } from "../util/amounts"; - -const logger = new Logger("integrationtest.ts"); - -export interface IntegrationTestArgs { - exchangeBaseUrl: string; - bankBaseUrl: string; - merchantBaseUrl: string; - merchantApiKey: string; - amountToWithdraw: string; - amountToSpend: string; -} - -async function makePayment( - wallet: Wallet, - merchant: MerchantBackendConnection, - amount: string, - summary: string, -): Promise<{ orderId: string }> { - const orderResp = await merchant.createOrder( - amount, - summary, - "taler://fulfillment-success/thx", - ); - - console.log("created order with orderId", orderResp.orderId); - - let paymentStatus = await merchant.checkPayment(orderResp.orderId); - - console.log("payment status", paymentStatus); - - const talerPayUri = paymentStatus.taler_pay_uri; - if (!talerPayUri) { - throw Error("no taler://pay/ URI in payment response"); - } - - const preparePayResult = await wallet.preparePayForUri(talerPayUri); - - console.log("prepare pay result", preparePayResult); - - if (preparePayResult.status != "payment-possible") { - throw Error("payment not possible"); - } - - const confirmPayResult = await wallet.confirmPay( - preparePayResult.proposalId, - undefined, - ); - - console.log("confirmPayResult", confirmPayResult); - - paymentStatus = await merchant.checkPayment(orderResp.orderId); - - if (paymentStatus.order_status !== "paid") { - console.log("payment status:", paymentStatus); - throw Error("payment did not succeed"); - } - - return { - orderId: orderResp.orderId, - }; -} - -export async function runIntegrationTest( - args: IntegrationTestArgs, -): Promise<void> { - logger.info("running test with arguments", args); - - const parsedSpendAmount = Amounts.parseOrThrow(args.amountToSpend); - 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"); - await withdrawTestBalance( - myWallet, - args.amountToWithdraw, - args.bankBaseUrl, - args.exchangeBaseUrl, - ); - logger.info("done withdrawing test balance"); - - const myMerchant = new MerchantBackendConnection( - args.merchantBaseUrl, - args.merchantApiKey, - ); - - await makePayment(myWallet, myMerchant, args.amountToSpend, "hello world"); - - // Wait until the refresh is done - await myWallet.runUntilDone(); - - console.log("withdrawing test balance for refund"); - const withdrawAmountTwo: AmountJson = { - currency, - value: 18, - fraction: 0, - }; - const spendAmountTwo: AmountJson = { - currency, - value: 7, - fraction: 0, - }; - - const refundAmount: AmountJson = { - currency, - value: 6, - fraction: 0, - }; - - const spendAmountThree: AmountJson = { - currency, - value: 3, - fraction: 0, - }; - await withdrawTestBalance( - myWallet, - Amounts.stringify(withdrawAmountTwo), - args.bankBaseUrl, - args.exchangeBaseUrl, - ); - - // Wait until the withdraw is done - await myWallet.runUntilDone(); - - const { orderId: refundOrderId } = await makePayment( - myWallet, - myMerchant, - Amounts.stringify(spendAmountTwo), - "order that will be refunded", - ); - - const refundUri = await myMerchant.refund( - refundOrderId, - "test refund", - Amounts.stringify(refundAmount), - ); - - console.log("refund URI", refundUri); - - await myWallet.applyRefund(refundUri); - - // Wait until the refund is done - await myWallet.runUntilDone(); - - await makePayment( - myWallet, - myMerchant, - Amounts.stringify(spendAmountThree), - "payment after refund", - ); - - await myWallet.runUntilDone(); -} - -export async function runIntegrationTestBasic( - cfg: Configuration, -): Promise<void> { - const walletDbPath = cfg.getString("integrationtest", "walletdb").required(); - - const bankBaseUrl = cfg - .getString("integrationtest", "bank_base_url") - .required(); - - const exchangeBaseUrl = cfg - .getString("integrationtest", "exchange_base_url") - .required(); - - const merchantBaseUrl = cfg - .getString("integrationtest", "merchant_base_url") - .required(); - - const merchantApiKey = cfg - .getString("integrationtest", "merchant_api_key") - .required(); - - const parsedWithdrawAmount = cfg - .getAmount("integrationtest-basic", "amount_withdraw") - .required(); - - const parsedSpendAmount = cfg - .getAmount("integrationtest-basic", "amount_spend") - .required(); - - const currency = parsedSpendAmount.currency; - - const myHttpLib = new NodeHttpLib(); - myHttpLib.setThrottling(false); - - const myWallet = await getDefaultNodeWallet({ - httpLib: myHttpLib, - persistentStoragePath: walletDbPath, - }); - - myWallet.runRetryLoop().catch((e) => { - console.error("exception during retry loop:", e); - }); - - logger.info("withdrawing test balance"); - await withdrawTestBalance( - myWallet, - Amounts.stringify(parsedWithdrawAmount), - bankBaseUrl, - exchangeBaseUrl, - ); - logger.info("done withdrawing test balance"); - - const balance = await myWallet.getBalances(); - - console.log(JSON.stringify(balance, null, 2)); - - const myMerchant = new MerchantBackendConnection( - merchantBaseUrl, - merchantApiKey, - ); - - await makePayment( - myWallet, - myMerchant, - Amounts.stringify(parsedSpendAmount), - "hello world", - ); - - // Wait until the refresh is done - await myWallet.runUntilDone(); - - console.log("withdrawing test balance for refund"); - const withdrawAmountTwo: AmountJson = { - currency, - value: 18, - fraction: 0, - }; - const spendAmountTwo: AmountJson = { - currency, - value: 7, - fraction: 0, - }; - - const refundAmount: AmountJson = { - currency, - value: 6, - fraction: 0, - }; - - const spendAmountThree: AmountJson = { - currency, - value: 3, - fraction: 0, - }; - - await withdrawTestBalance( - myWallet, - Amounts.stringify(withdrawAmountTwo), - bankBaseUrl, - exchangeBaseUrl, - ); - - // Wait until the withdraw is done - await myWallet.runUntilDone(); - - const { orderId: refundOrderId } = await makePayment( - myWallet, - myMerchant, - Amounts.stringify(spendAmountTwo), - "order that will be refunded", - ); - - const refundUri = await myMerchant.refund( - refundOrderId, - "test refund", - Amounts.stringify(refundAmount), - ); - - console.log("refund URI", refundUri); - - await myWallet.applyRefund(refundUri); - - // Wait until the refund is done - await myWallet.runUntilDone(); - - await makePayment( - myWallet, - myMerchant, - Amounts.stringify(spendAmountThree), - "payment after refund", - ); - - await myWallet.runUntilDone(); - - console.log( - "history after integration test:", - JSON.stringify(history, undefined, 2), - ); -} diff --git a/src/headless/merchant.ts b/src/headless/merchant.ts deleted file mode 100644 index 34ca5564d..000000000 --- a/src/headless/merchant.ts +++ /dev/null @@ -1,145 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2019 GNUnet e.V. - - 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/> - */ - -/** - * Helpers for talking to the GNU Taler merchant backend. - * Used mostly for integration tests. - */ - -/** - * Imports. - */ -import axios from "axios"; -import { - CheckPaymentResponse, - codecForCheckPaymentResponse, -} from "../types/talerTypes"; - -/** - * Connection to the *internal* merchant backend. - */ -export class MerchantBackendConnection { - async refund( - orderId: string, - reason: string, - refundAmount: string, - ): Promise<string> { - const reqUrl = new URL( - `private/orders/${orderId}/refund`, - this.merchantBaseUrl, - ); - const refundReq = { - reason, - refund: refundAmount, - }; - const resp = await axios({ - method: "post", - url: reqUrl.href, - data: refundReq, - responseType: "json", - headers: { - Authorization: `ApiKey ${this.apiKey}`, - }, - }); - if (resp.status != 200) { - throw Error("failed to do refund"); - } - console.log("response", resp.data); - const refundUri = resp.data.taler_refund_uri; - if (!refundUri) { - throw Error("no refund URI in response"); - } - return refundUri; - } - - constructor(public merchantBaseUrl: string, public apiKey: string) {} - - async authorizeTip(amount: string, justification: string): Promise<string> { - const reqUrl = new URL("private/tips", this.merchantBaseUrl).href; - const tipReq = { - amount, - justification, - next_url: "about:blank", - }; - const resp = await axios({ - method: "post", - url: reqUrl, - data: tipReq, - responseType: "json", - headers: { - Authorization: `ApiKey ${this.apiKey}`, - }, - }); - const tipUri = resp.data.taler_tip_uri; - if (!tipUri) { - throw Error("response does not contain tip URI"); - } - return tipUri; - } - - async createOrder( - amount: string, - summary: string, - fulfillmentUrl: string, - ): Promise<{ orderId: string }> { - const t = Math.floor(new Date().getTime() / 1000) + 15 * 60; - const reqUrl = new URL("private/orders", this.merchantBaseUrl).href; - const orderReq = { - order: { - amount, - summary, - fulfillment_url: fulfillmentUrl, - refund_deadline: { t_ms: t * 1000 }, - wire_transfer_deadline: { t_ms: t * 1000 }, - }, - }; - const resp = await axios({ - method: "post", - url: reqUrl, - data: orderReq, - responseType: "json", - headers: { - Authorization: `ApiKey ${this.apiKey}`, - }, - }); - if (resp.status != 200) { - throw Error("failed to create bank reserve"); - } - const orderId = resp.data.order_id; - if (!orderId) { - throw Error("no order id in response"); - } - return { orderId }; - } - - async checkPayment(orderId: string): Promise<CheckPaymentResponse> { - const reqUrl = new URL(`private/orders/${orderId}`, this.merchantBaseUrl) - .href; - const resp = await axios({ - method: "get", - url: reqUrl, - responseType: "json", - headers: { - Authorization: `ApiKey ${this.apiKey}`, - }, - }); - if (resp.status != 200) { - throw Error("failed to check payment"); - } - - return codecForCheckPaymentResponse().decode(resp.data); - } -} diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts index ba629537e..a7f306ec3 100644 --- a/src/headless/taler-wallet-cli.ts +++ b/src/headless/taler-wallet-cli.ts @@ -16,9 +16,7 @@ import os from "os"; import fs from "fs"; -import { getDefaultNodeWallet, withdrawTestBalance } from "./helpers"; -import { MerchantBackendConnection } from "./merchant"; -import { runIntegrationTest, runIntegrationTestBasic } from "./integrationtest"; +import { getDefaultNodeWallet } from "./helpers"; import { Wallet } from "../wallet"; import qrcodeGenerator from "qrcode-generator"; import * as clk from "./clk"; @@ -34,7 +32,6 @@ import { OperationFailedAndReportedError, OperationFailedError, } from "../operations/errors"; -import { Bank } from "./bank"; import { classifyTalerUri, TalerUriType } from "../util/taleruri"; import { Configuration } from "../util/talerconfig"; import { setDangerousTimetravel } from "../util/time"; @@ -658,285 +655,4 @@ testCli.subcommand("vectors", "vectors").action(async (args) => { console.log(` (out) coin pub: ${encodeCrock(p.coinPub)}`); }); -testCli - .subcommand("integrationtestBasic", "integrationtest-basic") - .requiredArgument("cfgfile", clk.STRING) - .action(async (args) => { - const cfgStr = fs.readFileSync(args.integrationtestBasic.cfgfile, "utf8"); - const cfg = new Configuration(); - cfg.loadFromString(cfgStr); - try { - await runIntegrationTestBasic(cfg); - } catch (e) { - console.log("integration test failed"); - console.log(e); - process.exit(1); - } - process.exit(0); - }); - -testCli - .subcommand("testPayCmd", "test-pay", { help: "Create contract and pay." }) - .requiredOption("merchant", ["-m", "--mechant-url"], clk.STRING) - .requiredOption("apikey", ["-k", "--mechant-api-key"], clk.STRING) - .requiredOption("amount", ["-a", "--amount"], clk.STRING) - .requiredOption("summary", ["-s", "--summary"], clk.STRING, { - default: "Test Payment", - }) - .action(async (args) => { - const cmdArgs = args.testPayCmd; - console.log("creating order"); - const merchantBackend = new MerchantBackendConnection( - args.testPayCmd.merchant, - args.testPayCmd.apikey, - ); - const orderResp = await merchantBackend.createOrder( - cmdArgs.amount, - cmdArgs.summary, - "", - ); - console.log("created new order with order ID", orderResp.orderId); - const checkPayResp = await merchantBackend.checkPayment(orderResp.orderId); - const talerPayUri = checkPayResp.taler_pay_uri; - if (!talerPayUri) { - console.error("fatal: no taler pay URI received from backend"); - process.exit(1); - return; - } - console.log("taler pay URI:", talerPayUri); - await withWallet(args, async (wallet) => { - await doPay(wallet, talerPayUri, { alwaysYes: true }); - }); - }); - -testCli - .subcommand("integrationtestCmd", "integrationtest", { - help: "Run integration test with bank, exchange and merchant.", - }) - .requiredOption("exchange", ["-e", "--exchange"], clk.STRING, { - default: "https://exchange.test.taler.net/", - }) - .requiredOption("merchant", ["-m", "--merchant"], clk.STRING, { - default: "https://backend.test.taler.net/", - }) - .requiredOption("merchantApiKey", ["-k", "--merchant-api-key"], clk.STRING, { - default: "sandbox", - }) - .requiredOption("bank", ["-b", "--bank"], clk.STRING, { - default: "https://bank.test.taler.net/", - }) - .requiredOption("withdrawAmount", ["-w", "--amount"], clk.STRING, { - default: "TESTKUDOS:10", - }) - .requiredOption("spendAmount", ["-s", "--spend-amount"], clk.STRING, { - default: "TESTKUDOS:4", - }) - .action(async (args) => { - applyVerbose(args.wallet.verbose); - const cmdObj = args.integrationtestCmd; - - try { - await runIntegrationTest({ - amountToSpend: cmdObj.spendAmount, - amountToWithdraw: cmdObj.withdrawAmount, - bankBaseUrl: cmdObj.bank, - exchangeBaseUrl: cmdObj.exchange, - merchantApiKey: cmdObj.merchantApiKey, - merchantBaseUrl: cmdObj.merchant, - }).catch((err) => { - console.error("Integration test failed with exception:"); - console.error(err); - process.exit(1); - }); - process.exit(0); - } catch (e) { - console.error(e); - process.exit(1); - } - }); - -testCli - .subcommand("genTipUri", "gen-tip-uri", { - help: "Generate a taler://tip URI.", - }) - .requiredOption("amount", ["-a", "--amount"], clk.STRING, { - default: "TESTKUDOS:10", - }) - .maybeOption("merchant", ["-m", "--merchant"], clk.STRING, { - default: "https://backend.test.taler.net/", - }) - .maybeOption("merchantApiKey", ["-k", "--merchant-api-key"], clk.STRING, { - default: "sandbox", - }) - .action(async (args) => { - const merchantBackend = new MerchantBackendConnection( - args.genTipUri.merchant ?? "https://backend.test.taler.net/", - args.genTipUri.merchantApiKey ?? "sandbox", - ); - const tipUri = await merchantBackend.authorizeTip( - args.genTipUri.amount, - "test", - ); - console.log(tipUri); - }); - -testCli - .subcommand("genWithdrawUri", "gen-withdraw-uri", { - help: "Generate a taler://withdraw URI.", - }) - .requiredOption("amount", ["-a", "--amount"], clk.STRING, { - default: "TESTKUDOS:20", - }) - .requiredOption("bank", ["-b", "--bank"], clk.STRING, { - default: "https://bank.test.taler.net/", - }) - .action(async (args) => { - const b = new Bank(args.genWithdrawUri.bank); - const user = await b.registerRandomUser(); - const url = await b.generateWithdrawUri(user, args.genWithdrawUri.amount); - console.log(url); - }); - -testCli - .subcommand("genRefundUri", "gen-refund-uri", { - help: "Generate a taler://refund URI.", - }) - .requiredOption("amount", ["-a", "--amount"], clk.STRING, { - default: "TESTKUDOS:5", - }) - .requiredOption("refundAmount", ["-r", "--refund"], clk.STRING, { - default: "TESTKUDOS:3", - }) - .requiredOption("summary", ["-s", "--summary"], clk.STRING, { - default: "Test Payment (for refund)", - }) - .maybeOption("merchant", ["-m", "--merchant"], clk.STRING, { - default: "https://backend.test.taler.net/", - }) - .maybeOption("merchantApiKey", ["-k", "--merchant-api-key"], clk.STRING, { - default: "sandbox", - }) - .action(async (args) => { - const cmdArgs = args.genRefundUri; - const merchantBackend = new MerchantBackendConnection( - cmdArgs.merchant ?? "https://backend.test.taler.net/", - cmdArgs.merchantApiKey ?? "sandbox", - ); - const orderResp = await merchantBackend.createOrder( - cmdArgs.amount, - cmdArgs.summary, - "", - ); - console.log("created new order with order ID", orderResp.orderId); - const checkPayResp = await merchantBackend.checkPayment(orderResp.orderId); - const talerPayUri = checkPayResp.taler_pay_uri; - if (!talerPayUri) { - console.error("fatal: no taler pay URI received from backend"); - process.exit(1); - return; - } - await withWallet(args, async (wallet) => { - await doPay(wallet, talerPayUri, { alwaysYes: true }); - }); - const refundUri = await merchantBackend.refund( - orderResp.orderId, - "test refund", - cmdArgs.refundAmount, - ); - console.log(refundUri); - }); - -testCli - .subcommand("genPayUri", "gen-pay-uri", { - help: "Generate a taler://pay URI.", - }) - .flag("qrcode", ["--qr"], { - help: "Show a QR code with the taler://pay URI", - }) - .flag("wait", ["--wait"], { - help: "Wait until payment has completed", - }) - .requiredOption("amount", ["-a", "--amount"], clk.STRING, { - default: "TESTKUDOS:1", - }) - .requiredOption("summary", ["-s", "--summary"], clk.STRING, { - default: "Test Payment", - }) - .requiredOption("merchant", ["-m", "--merchant"], clk.STRING, { - default: "https://backend.test.taler.net/", - }) - .requiredOption("merchantApiKey", ["-k", "--merchant-api-key"], clk.STRING, { - default: "sandbox", - }) - .action(async (args) => { - const cmdArgs = args.genPayUri; - console.log("creating order"); - const merchantBackend = new MerchantBackendConnection( - cmdArgs.merchant, - cmdArgs.merchantApiKey, - ); - const orderResp = await merchantBackend.createOrder( - cmdArgs.amount, - cmdArgs.summary, - "", - ); - console.log("created new order with order ID", orderResp.orderId); - const checkPayResp = await merchantBackend.checkPayment(orderResp.orderId); - const talerPayUri = checkPayResp.taler_pay_uri; - if (!talerPayUri) { - console.error("fatal: no taler pay URI received from backend"); - process.exit(1); - return; - } - console.log("taler pay URI:", talerPayUri); - if (cmdArgs.qrcode) { - const qrcode = qrcodeGenerator(0, "M"); - qrcode.addData(talerPayUri); - qrcode.make(); - console.log(qrcode.createASCII()); - } - if (cmdArgs.wait) { - console.log("waiting for payment ..."); - while (1) { - await asyncSleep(500); - const checkPayResp2 = await merchantBackend.checkPayment( - orderResp.orderId, - ); - if (checkPayResp2.order_status === "paid") { - console.log("payment successfully received!"); - break; - } - } - } - }); - -testCli - .subcommand("withdrawArgs", "withdraw", { - help: "Withdraw from a test bank (must support test registrations).", - }) - .requiredOption("amount", ["-a", "--amount"], clk.STRING, { - default: "TESTKUDOS:10", - help: "Amount to withdraw.", - }) - .requiredOption("exchange", ["-e", "--exchange"], clk.STRING, { - default: "https://exchange.test.taler.net/", - help: "Exchange base URL.", - }) - .requiredOption("bank", ["-b", "--bank"], clk.STRING, { - default: "https://bank.test.taler.net/", - help: "Bank base URL", - }) - .action(async (args) => { - await withWallet(args, async (wallet) => { - await wallet.updateExchangeFromUrl(args.withdrawArgs.exchange, true); - await withdrawTestBalance( - wallet, - args.withdrawArgs.amount, - args.withdrawArgs.bank, - args.withdrawArgs.exchange, - ); - logger.info("Withdraw done"); - }); - }); - walletCli.run(); diff --git a/src/index.ts b/src/index.ts index 3a5cd9497..147d7b1e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,5 +19,4 @@ */ export { Wallet } from "./wallet"; -export { runIntegrationTest } from "./headless/integrationtest"; export { installAndroidWalletListener } from "./android"; diff --git a/src/util/http.ts b/src/util/http.ts index abbc8df03..38892491b 100644 --- a/src/util/http.ts +++ b/src/util/http.ts @@ -300,6 +300,7 @@ export async function readSuccessResponseJsonOrThrow<T>( throwUnexpectedRequestError(httpResponse, r.talerErrorResponse); } + export async function readSuccessResponseTextOrErrorCode<T>( httpResponse: HttpResponse, ): Promise<ResponseOrError<string>> { @@ -329,6 +330,27 @@ export async function readSuccessResponseTextOrErrorCode<T>( }; } +export async function checkSuccessResponseOrThrow( + httpResponse: HttpResponse, +): Promise<void> { + if (!(httpResponse.status >= 200 && httpResponse.status < 300)) { + const errJson = await httpResponse.json(); + const talerErrorCode = errJson.code; + if (typeof talerErrorCode !== "number") { + throw new OperationFailedError( + makeErrorDetails( + TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE, + "Error response did not contain error code", + { + requestUrl: httpResponse.requestUrl, + }, + ), + ); + } + throwUnexpectedRequestError(httpResponse, errJson); + } +} + export async function readSuccessResponseTextOrThrow<T>( httpResponse: HttpResponse, ): Promise<string> { diff --git a/src/wallet.ts b/src/wallet.ts index 758336c58..4d22bd591 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -113,6 +113,7 @@ import { TransactionsResponse, } from "./types/transactions"; import { getTransactions } from "./operations/transactions"; +import { withdrawTestBalance } from "./operations/testing"; const builtinCurrencies: CurrencyRecord[] = [ { @@ -868,4 +869,13 @@ export class Wallet { ): Promise<TransactionsResponse> { return getTransactions(this.ws, request); } + + + async withdrawTestBalance( + amount = "TESTKUDOS:10", + bankBaseUrl = "https://bank.test.taler.net/", + exchangeBaseUrl = "https://exchange.test.taler.net/", + ): Promise<void> { + await withdrawTestBalance(this.ws, amount, bankBaseUrl, exchangeBaseUrl); + } } diff --git a/src/walletCoreApiHandler.ts b/src/walletCoreApiHandler.ts index a16490d5a..7ab6a6284 100644 --- a/src/walletCoreApiHandler.ts +++ b/src/walletCoreApiHandler.ts @@ -21,7 +21,6 @@ import { makeErrorDetails, } from "./operations/errors"; import { TalerErrorCode } from "./TalerErrorCode"; -import { withdrawTestBalance } from "./headless/helpers"; import { codecForTransactionsRequest } from "./types/transactions"; import { makeCodecForObject, @@ -160,7 +159,7 @@ async function dispatchRequestInternal( ): Promise<Record<string, any>> { switch (operation) { case "withdrawTestkudos": - await withdrawTestBalance(wallet); + await wallet.withdrawTestBalance(); return {}; case "getTransactions": { const req = codecForTransactionsRequest().decode(payload); |