2020-08-03 09:30:48 +02:00
|
|
|
/*
|
|
|
|
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/>
|
|
|
|
*/
|
|
|
|
|
2021-03-17 17:56:37 +01:00
|
|
|
/**
|
|
|
|
* Imports.
|
|
|
|
*/
|
2022-06-10 13:03:47 +02:00
|
|
|
import {
|
|
|
|
ConfirmPayResultType,
|
|
|
|
Logger,
|
|
|
|
TestPayResult,
|
|
|
|
WithdrawTestBalanceRequest,
|
|
|
|
} from "@gnu-taler/taler-util";
|
2020-08-03 09:30:48 +02:00
|
|
|
import {
|
|
|
|
HttpRequestLibrary,
|
|
|
|
readSuccessResponseJsonOrThrow,
|
|
|
|
checkSuccessResponseOrThrow,
|
2021-06-14 16:08:58 +02:00
|
|
|
} from "../util/http.js";
|
2021-06-09 15:26:18 +02:00
|
|
|
import {
|
|
|
|
AmountString,
|
|
|
|
codecForAny,
|
|
|
|
CheckPaymentResponse,
|
|
|
|
codecForCheckPaymentResponse,
|
|
|
|
IntegrationTestArgs,
|
|
|
|
Amounts,
|
|
|
|
TestPayArgs,
|
2021-06-20 21:14:45 +02:00
|
|
|
URL,
|
2021-06-09 15:26:18 +02:00
|
|
|
PreparePayResultType,
|
|
|
|
} from "@gnu-taler/taler-util";
|
2022-03-23 13:11:36 +01:00
|
|
|
import { InternalWalletState } from "../internal-wallet-state.js";
|
2021-06-15 18:52:43 +02:00
|
|
|
import { confirmPay, preparePayForUri } from "./pay.js";
|
|
|
|
import { getBalances } from "./balance.js";
|
|
|
|
import { applyRefund } from "./refund.js";
|
2022-06-10 13:03:47 +02:00
|
|
|
import { checkLogicInvariant } from "../util/invariants.js";
|
2022-08-09 15:00:45 +02:00
|
|
|
import { acceptWithdrawalFromUri } from "./withdraw.js";
|
2020-08-03 09:30:48 +02:00
|
|
|
|
|
|
|
const logger = new Logger("operations/testing.ts");
|
|
|
|
|
|
|
|
interface BankUser {
|
|
|
|
username: string;
|
|
|
|
password: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface BankWithdrawalResponse {
|
|
|
|
taler_withdraw_uri: string;
|
|
|
|
withdrawal_id: string;
|
|
|
|
}
|
|
|
|
|
2020-08-14 09:36:42 +02:00
|
|
|
interface MerchantBackendInfo {
|
|
|
|
baseUrl: string;
|
2021-02-04 17:13:31 +01:00
|
|
|
authToken?: string;
|
2020-08-14 09:36:42 +02:00
|
|
|
}
|
|
|
|
|
2020-08-03 09:30:48 +02:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2022-03-14 18:31:30 +01:00
|
|
|
function makeBasicAuthHeader(username: string, password: string): string {
|
2020-08-03 09:30:48 +02:00
|
|
|
const auth = `${username}:${password}`;
|
|
|
|
const authEncoded: string = Buffer.from(auth).toString("base64");
|
|
|
|
return `Basic ${authEncoded}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function withdrawTestBalance(
|
|
|
|
ws: InternalWalletState,
|
2022-06-10 13:03:47 +02:00
|
|
|
req: WithdrawTestBalanceRequest,
|
2020-08-03 09:30:48 +02:00
|
|
|
): Promise<void> {
|
2022-06-10 13:03:47 +02:00
|
|
|
const amount = req.amount;
|
|
|
|
const exchangeBaseUrl = req.exchangeBaseUrl;
|
2022-09-13 17:41:47 +02:00
|
|
|
const bankAccessApiBaseUrl = req.bankAccessApiBaseUrl ?? req.bankBaseUrl;
|
2022-06-10 13:03:47 +02:00
|
|
|
|
2022-08-25 23:35:29 +02:00
|
|
|
logger.trace(
|
2022-09-13 17:41:47 +02:00
|
|
|
`Registered bank user, bank access base url ${bankAccessApiBaseUrl}`,
|
2022-08-25 23:35:29 +02:00
|
|
|
);
|
2022-09-13 17:41:47 +02:00
|
|
|
const bankUser = await registerRandomBankUser(ws.http, bankAccessApiBaseUrl);
|
2020-08-03 09:30:48 +02:00
|
|
|
logger.trace(`Registered bank user ${JSON.stringify(bankUser)}`);
|
|
|
|
|
2022-03-14 18:31:30 +01:00
|
|
|
const wresp = await createDemoBankWithdrawalUri(
|
2020-08-03 09:30:48 +02:00
|
|
|
ws.http,
|
2022-09-13 17:41:47 +02:00
|
|
|
bankAccessApiBaseUrl,
|
2020-08-03 09:30:48 +02:00
|
|
|
bankUser,
|
|
|
|
amount,
|
|
|
|
);
|
|
|
|
|
2022-08-09 15:00:45 +02:00
|
|
|
await acceptWithdrawalFromUri(ws, {
|
|
|
|
talerWithdrawUri: wresp.taler_withdraw_uri,
|
|
|
|
selectedExchange: exchangeBaseUrl,
|
|
|
|
forcedDenomSel: req.forcedDenomSel,
|
|
|
|
});
|
2020-08-03 09:30:48 +02:00
|
|
|
|
|
|
|
await confirmBankWithdrawalUri(
|
|
|
|
ws.http,
|
2022-09-13 17:41:47 +02:00
|
|
|
bankAccessApiBaseUrl,
|
2020-08-03 09:30:48 +02:00
|
|
|
bankUser,
|
|
|
|
wresp.withdrawal_id,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-02-04 17:13:31 +01:00
|
|
|
function getMerchantAuthHeader(m: MerchantBackendInfo): Record<string, string> {
|
|
|
|
if (m.authToken) {
|
|
|
|
return {
|
2021-06-09 15:26:18 +02:00
|
|
|
Authorization: `Bearer ${m.authToken}`,
|
|
|
|
};
|
2021-02-04 17:13:31 +01:00
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2022-03-14 18:31:30 +01:00
|
|
|
/**
|
|
|
|
* Use the testing API of a demobank to create a taler://withdraw URI
|
|
|
|
* that the wallet can then use to make a withdrawal.
|
|
|
|
*/
|
|
|
|
export async function createDemoBankWithdrawalUri(
|
2020-08-03 09:30:48 +02:00
|
|
|
http: HttpRequestLibrary,
|
2022-08-25 23:35:29 +02:00
|
|
|
bankAccessApiBaseUrl: string,
|
2020-08-03 09:30:48 +02:00
|
|
|
bankUser: BankUser,
|
|
|
|
amount: AmountString,
|
|
|
|
): Promise<BankWithdrawalResponse> {
|
|
|
|
const reqUrl = new URL(
|
|
|
|
`accounts/${bankUser.username}/withdrawals`,
|
2022-08-25 23:35:29 +02:00
|
|
|
bankAccessApiBaseUrl,
|
2020-08-03 09:30:48 +02:00
|
|
|
).href;
|
|
|
|
const resp = await http.postJson(
|
|
|
|
reqUrl,
|
|
|
|
{
|
|
|
|
amount,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
headers: {
|
2022-06-10 13:03:47 +02:00
|
|
|
Authorization: makeBasicAuthHeader(
|
|
|
|
bankUser.username,
|
|
|
|
bankUser.password,
|
|
|
|
),
|
2020-08-03 09:30:48 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
2020-08-12 12:32:58 +02:00
|
|
|
const respJson = await readSuccessResponseJsonOrThrow(resp, codecForAny());
|
2020-08-03 09:30:48 +02:00
|
|
|
return respJson;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function confirmBankWithdrawalUri(
|
|
|
|
http: HttpRequestLibrary,
|
2022-08-25 23:35:29 +02:00
|
|
|
bankAccessApiBaseUrl: string,
|
2020-08-03 09:30:48 +02:00
|
|
|
bankUser: BankUser,
|
|
|
|
withdrawalId: string,
|
|
|
|
): Promise<void> {
|
|
|
|
const reqUrl = new URL(
|
|
|
|
`accounts/${bankUser.username}/withdrawals/${withdrawalId}/confirm`,
|
2022-08-25 23:35:29 +02:00
|
|
|
bankAccessApiBaseUrl,
|
2020-08-03 09:30:48 +02:00
|
|
|
).href;
|
|
|
|
const resp = await http.postJson(
|
|
|
|
reqUrl,
|
|
|
|
{},
|
|
|
|
{
|
|
|
|
headers: {
|
2022-06-10 13:03:47 +02:00
|
|
|
Authorization: makeBasicAuthHeader(
|
|
|
|
bankUser.username,
|
|
|
|
bankUser.password,
|
|
|
|
),
|
2020-08-03 09:30:48 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
2020-08-12 12:32:58 +02:00
|
|
|
await readSuccessResponseJsonOrThrow(resp, codecForAny());
|
2020-08-03 09:30:48 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function registerRandomBankUser(
|
|
|
|
http: HttpRequestLibrary,
|
2022-08-25 23:35:29 +02:00
|
|
|
bankAccessApiBaseUrl: string,
|
2020-08-03 09:30:48 +02:00
|
|
|
): Promise<BankUser> {
|
2022-08-25 23:35:29 +02:00
|
|
|
const reqUrl = new URL("testing/register", bankAccessApiBaseUrl).href;
|
2020-08-03 09:30:48 +02:00
|
|
|
const randId = makeId(8);
|
|
|
|
const bankUser: BankUser = {
|
2021-11-13 12:53:48 +01:00
|
|
|
// euFin doesn't allow resource names to have upper case letters.
|
|
|
|
username: `testuser-${randId.toLowerCase()}`,
|
2020-08-03 09:30:48 +02:00
|
|
|
password: `testpw-${randId}`,
|
|
|
|
};
|
|
|
|
|
|
|
|
const resp = await http.postJson(reqUrl, bankUser);
|
|
|
|
await checkSuccessResponseOrThrow(resp);
|
|
|
|
return bankUser;
|
|
|
|
}
|
2020-08-14 09:36:42 +02:00
|
|
|
|
|
|
|
async function refund(
|
|
|
|
http: HttpRequestLibrary,
|
|
|
|
merchantBackend: MerchantBackendInfo,
|
|
|
|
orderId: string,
|
|
|
|
reason: string,
|
|
|
|
refundAmount: string,
|
|
|
|
): Promise<string> {
|
|
|
|
const reqUrl = new URL(
|
|
|
|
`private/orders/${orderId}/refund`,
|
|
|
|
merchantBackend.baseUrl,
|
|
|
|
);
|
|
|
|
const refundReq = {
|
|
|
|
order_id: orderId,
|
|
|
|
reason,
|
|
|
|
refund: refundAmount,
|
|
|
|
};
|
|
|
|
const resp = await http.postJson(reqUrl.href, refundReq, {
|
2021-02-04 17:13:31 +01:00
|
|
|
headers: getMerchantAuthHeader(merchantBackend),
|
2020-08-14 09:36:42 +02:00
|
|
|
});
|
|
|
|
const r = await readSuccessResponseJsonOrThrow(resp, codecForAny());
|
|
|
|
const refundUri = r.taler_refund_uri;
|
|
|
|
if (!refundUri) {
|
|
|
|
throw Error("no refund URI in response");
|
|
|
|
}
|
|
|
|
return refundUri;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function createOrder(
|
|
|
|
http: HttpRequestLibrary,
|
|
|
|
merchantBackend: MerchantBackendInfo,
|
|
|
|
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", merchantBackend.baseUrl).href;
|
|
|
|
const orderReq = {
|
|
|
|
order: {
|
|
|
|
amount,
|
|
|
|
summary,
|
|
|
|
fulfillment_url: fulfillmentUrl,
|
2022-03-18 15:32:41 +01:00
|
|
|
refund_deadline: { t_s: t },
|
|
|
|
wire_transfer_deadline: { t_s: t },
|
2020-08-14 09:36:42 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
const resp = await http.postJson(reqUrl, orderReq, {
|
2021-02-04 17:13:31 +01:00
|
|
|
headers: getMerchantAuthHeader(merchantBackend),
|
2020-08-14 09:36:42 +02:00
|
|
|
});
|
|
|
|
const r = await readSuccessResponseJsonOrThrow(resp, codecForAny());
|
|
|
|
const orderId = r.order_id;
|
|
|
|
if (!orderId) {
|
|
|
|
throw Error("no order id in response");
|
|
|
|
}
|
|
|
|
return { orderId };
|
|
|
|
}
|
|
|
|
|
|
|
|
async function checkPayment(
|
|
|
|
http: HttpRequestLibrary,
|
|
|
|
merchantBackend: MerchantBackendInfo,
|
|
|
|
orderId: string,
|
|
|
|
): Promise<CheckPaymentResponse> {
|
2021-08-07 17:39:50 +02:00
|
|
|
const reqUrl = new URL(`private/orders/${orderId}`, merchantBackend.baseUrl);
|
2020-08-14 09:36:42 +02:00
|
|
|
reqUrl.searchParams.set("order_id", orderId);
|
|
|
|
const resp = await http.get(reqUrl.href, {
|
2021-02-04 17:13:31 +01:00
|
|
|
headers: getMerchantAuthHeader(merchantBackend),
|
2020-08-14 09:36:42 +02:00
|
|
|
});
|
|
|
|
return readSuccessResponseJsonOrThrow(resp, codecForCheckPaymentResponse());
|
|
|
|
}
|
|
|
|
|
|
|
|
interface BankUser {
|
|
|
|
username: string;
|
|
|
|
password: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface BankWithdrawalResponse {
|
|
|
|
taler_withdraw_uri: string;
|
|
|
|
withdrawal_id: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function makePayment(
|
2021-06-15 18:52:43 +02:00
|
|
|
ws: InternalWalletState,
|
2020-08-14 09:36:42 +02:00
|
|
|
merchant: MerchantBackendInfo,
|
|
|
|
amount: string,
|
|
|
|
summary: string,
|
|
|
|
): Promise<{ orderId: string }> {
|
|
|
|
const orderResp = await createOrder(
|
2021-06-15 18:52:43 +02:00
|
|
|
ws.http,
|
2020-08-14 09:36:42 +02:00
|
|
|
merchant,
|
|
|
|
amount,
|
|
|
|
summary,
|
|
|
|
"taler://fulfillment-success/thx",
|
|
|
|
);
|
|
|
|
|
2020-08-14 12:23:50 +02:00
|
|
|
logger.trace("created order with orderId", orderResp.orderId);
|
2020-08-14 09:36:42 +02:00
|
|
|
|
2021-06-15 18:52:43 +02:00
|
|
|
let paymentStatus = await checkPayment(ws.http, merchant, orderResp.orderId);
|
2020-08-14 09:36:42 +02:00
|
|
|
|
2020-08-14 12:23:50 +02:00
|
|
|
logger.trace("payment status", paymentStatus);
|
2020-08-14 09:36:42 +02:00
|
|
|
|
|
|
|
const talerPayUri = paymentStatus.taler_pay_uri;
|
|
|
|
if (!talerPayUri) {
|
|
|
|
throw Error("no taler://pay/ URI in payment response");
|
|
|
|
}
|
|
|
|
|
2021-06-15 18:52:43 +02:00
|
|
|
const preparePayResult = await preparePayForUri(ws, talerPayUri);
|
2020-08-14 09:36:42 +02:00
|
|
|
|
2020-08-14 12:23:50 +02:00
|
|
|
logger.trace("prepare pay result", preparePayResult);
|
2020-08-14 09:36:42 +02:00
|
|
|
|
|
|
|
if (preparePayResult.status != "payment-possible") {
|
|
|
|
throw Error("payment not possible");
|
|
|
|
}
|
|
|
|
|
2021-06-15 18:52:43 +02:00
|
|
|
const confirmPayResult = await confirmPay(
|
|
|
|
ws,
|
2020-08-14 09:36:42 +02:00
|
|
|
preparePayResult.proposalId,
|
|
|
|
undefined,
|
|
|
|
);
|
|
|
|
|
2020-08-14 12:23:50 +02:00
|
|
|
logger.trace("confirmPayResult", confirmPayResult);
|
2020-08-14 09:36:42 +02:00
|
|
|
|
2021-06-15 18:52:43 +02:00
|
|
|
paymentStatus = await checkPayment(ws.http, merchant, orderResp.orderId);
|
2020-08-14 09:36:42 +02:00
|
|
|
|
2020-08-14 12:23:50 +02:00
|
|
|
logger.trace("payment status after wallet payment:", paymentStatus);
|
2020-08-14 09:36:42 +02:00
|
|
|
|
|
|
|
if (paymentStatus.order_status !== "paid") {
|
|
|
|
throw Error("payment did not succeed");
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
orderId: orderResp.orderId,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function runIntegrationTest(
|
2021-06-15 18:52:43 +02:00
|
|
|
ws: InternalWalletState,
|
2020-08-14 09:36:42 +02:00
|
|
|
args: IntegrationTestArgs,
|
|
|
|
): Promise<void> {
|
|
|
|
logger.info("running test with arguments", args);
|
|
|
|
|
|
|
|
const parsedSpendAmount = Amounts.parseOrThrow(args.amountToSpend);
|
|
|
|
const currency = parsedSpendAmount.currency;
|
|
|
|
|
|
|
|
logger.info("withdrawing test balance");
|
2022-06-10 13:03:47 +02:00
|
|
|
await withdrawTestBalance(ws, {
|
|
|
|
amount: args.amountToWithdraw,
|
|
|
|
bankBaseUrl: args.bankBaseUrl,
|
2022-09-12 14:09:28 +02:00
|
|
|
bankAccessApiBaseUrl: args.bankAccessApiBaseUrl ?? args.bankBaseUrl,
|
2022-06-10 13:03:47 +02:00
|
|
|
exchangeBaseUrl: args.exchangeBaseUrl,
|
|
|
|
});
|
2021-06-22 12:18:12 +02:00
|
|
|
await ws.runUntilDone();
|
2020-08-14 09:36:42 +02:00
|
|
|
logger.info("done withdrawing test balance");
|
|
|
|
|
2021-06-15 18:52:43 +02:00
|
|
|
const balance = await getBalances(ws);
|
2020-08-14 09:36:42 +02:00
|
|
|
|
2020-08-14 12:23:50 +02:00
|
|
|
logger.trace(JSON.stringify(balance, null, 2));
|
2020-08-14 09:36:42 +02:00
|
|
|
|
|
|
|
const myMerchant: MerchantBackendInfo = {
|
|
|
|
baseUrl: args.merchantBaseUrl,
|
2021-02-04 17:13:31 +01:00
|
|
|
authToken: args.merchantAuthToken,
|
2020-08-14 09:36:42 +02:00
|
|
|
};
|
|
|
|
|
2021-06-15 18:52:43 +02:00
|
|
|
await makePayment(ws, myMerchant, args.amountToSpend, "hello world");
|
2020-08-14 09:36:42 +02:00
|
|
|
|
|
|
|
// Wait until the refresh is done
|
2021-06-22 12:18:12 +02:00
|
|
|
await ws.runUntilDone();
|
2020-08-14 09:36:42 +02:00
|
|
|
|
2020-08-14 12:23:50 +02:00
|
|
|
logger.trace("withdrawing test balance for refund");
|
2020-08-14 09:36:42 +02:00
|
|
|
const withdrawAmountTwo = Amounts.parseOrThrow(`${currency}:18`);
|
|
|
|
const spendAmountTwo = Amounts.parseOrThrow(`${currency}:7`);
|
|
|
|
const refundAmount = Amounts.parseOrThrow(`${currency}:6`);
|
|
|
|
const spendAmountThree = Amounts.parseOrThrow(`${currency}:3`);
|
|
|
|
|
2022-06-10 13:03:47 +02:00
|
|
|
await withdrawTestBalance(ws, {
|
|
|
|
amount: Amounts.stringify(withdrawAmountTwo),
|
|
|
|
bankBaseUrl: args.bankBaseUrl,
|
2022-09-12 14:09:28 +02:00
|
|
|
bankAccessApiBaseUrl: args.bankAccessApiBaseUrl ?? args.bankBaseUrl,
|
2022-06-10 13:03:47 +02:00
|
|
|
exchangeBaseUrl: args.exchangeBaseUrl,
|
|
|
|
});
|
2020-08-14 09:36:42 +02:00
|
|
|
|
|
|
|
// Wait until the withdraw is done
|
2021-06-22 12:18:12 +02:00
|
|
|
await ws.runUntilDone();
|
2020-08-14 09:36:42 +02:00
|
|
|
|
|
|
|
const { orderId: refundOrderId } = await makePayment(
|
2021-06-15 18:52:43 +02:00
|
|
|
ws,
|
2020-08-14 09:36:42 +02:00
|
|
|
myMerchant,
|
|
|
|
Amounts.stringify(spendAmountTwo),
|
|
|
|
"order that will be refunded",
|
|
|
|
);
|
|
|
|
|
|
|
|
const refundUri = await refund(
|
2021-06-15 18:52:43 +02:00
|
|
|
ws.http,
|
2020-08-14 09:36:42 +02:00
|
|
|
myMerchant,
|
|
|
|
refundOrderId,
|
|
|
|
"test refund",
|
|
|
|
Amounts.stringify(refundAmount),
|
|
|
|
);
|
|
|
|
|
2020-08-14 12:23:50 +02:00
|
|
|
logger.trace("refund URI", refundUri);
|
2020-08-14 09:36:42 +02:00
|
|
|
|
2021-06-15 18:52:43 +02:00
|
|
|
await applyRefund(ws, refundUri);
|
2020-08-14 12:23:50 +02:00
|
|
|
|
|
|
|
logger.trace("integration test: applied refund");
|
2020-08-14 09:36:42 +02:00
|
|
|
|
|
|
|
// Wait until the refund is done
|
2021-06-22 12:18:12 +02:00
|
|
|
await ws.runUntilDone();
|
2020-08-14 12:23:50 +02:00
|
|
|
|
|
|
|
logger.trace("integration test: making payment after refund");
|
2020-08-14 09:36:42 +02:00
|
|
|
|
|
|
|
await makePayment(
|
2021-06-15 18:52:43 +02:00
|
|
|
ws,
|
2020-08-14 09:36:42 +02:00
|
|
|
myMerchant,
|
|
|
|
Amounts.stringify(spendAmountThree),
|
|
|
|
"payment after refund",
|
|
|
|
);
|
|
|
|
|
2020-08-14 12:23:50 +02:00
|
|
|
logger.trace("integration test: make payment done");
|
|
|
|
|
2021-06-22 12:18:12 +02:00
|
|
|
await ws.runUntilDone();
|
2020-08-14 12:23:50 +02:00
|
|
|
|
|
|
|
logger.trace("integration test: all done!");
|
2020-08-14 09:36:42 +02:00
|
|
|
}
|
|
|
|
|
2022-06-10 13:03:47 +02:00
|
|
|
export async function testPay(
|
|
|
|
ws: InternalWalletState,
|
|
|
|
args: TestPayArgs,
|
|
|
|
): Promise<TestPayResult> {
|
2020-08-14 12:23:50 +02:00
|
|
|
logger.trace("creating order");
|
2020-09-01 15:37:14 +02:00
|
|
|
const merchant = {
|
2021-02-04 17:13:31 +01:00
|
|
|
authToken: args.merchantAuthToken,
|
2020-09-01 15:37:14 +02:00
|
|
|
baseUrl: args.merchantBaseUrl,
|
|
|
|
};
|
2020-08-14 09:36:42 +02:00
|
|
|
const orderResp = await createOrder(
|
2021-06-15 18:52:43 +02:00
|
|
|
ws.http,
|
2020-08-14 09:36:42 +02:00
|
|
|
merchant,
|
|
|
|
args.amount,
|
|
|
|
args.summary,
|
|
|
|
"taler://fulfillment-success/thank+you",
|
|
|
|
);
|
2020-08-14 12:23:50 +02:00
|
|
|
logger.trace("created new order with order ID", orderResp.orderId);
|
2021-06-15 18:52:43 +02:00
|
|
|
const checkPayResp = await checkPayment(ws.http, merchant, orderResp.orderId);
|
2020-08-14 09:36:42 +02:00
|
|
|
const talerPayUri = checkPayResp.taler_pay_uri;
|
|
|
|
if (!talerPayUri) {
|
|
|
|
console.error("fatal: no taler pay URI received from backend");
|
|
|
|
process.exit(1);
|
|
|
|
}
|
2020-08-14 12:23:50 +02:00
|
|
|
logger.trace("taler pay URI:", talerPayUri);
|
2021-06-15 18:52:43 +02:00
|
|
|
const result = await preparePayForUri(ws, talerPayUri);
|
2020-08-14 09:36:42 +02:00
|
|
|
if (result.status !== PreparePayResultType.PaymentPossible) {
|
|
|
|
throw Error(`unexpected prepare pay status: ${result.status}`);
|
|
|
|
}
|
2022-06-10 13:03:47 +02:00
|
|
|
const r = await confirmPay(
|
|
|
|
ws,
|
|
|
|
result.proposalId,
|
|
|
|
undefined,
|
|
|
|
args.forcedCoinSel,
|
|
|
|
);
|
|
|
|
if (r.type != ConfirmPayResultType.Done) {
|
|
|
|
throw Error("payment not done");
|
|
|
|
}
|
|
|
|
const purchase = await ws.db
|
2022-09-13 13:25:41 +02:00
|
|
|
.mktx((x) => [x.purchases])
|
2022-06-10 13:03:47 +02:00
|
|
|
.runReadOnly(async (tx) => {
|
|
|
|
return tx.purchases.get(result.proposalId);
|
|
|
|
});
|
|
|
|
checkLogicInvariant(!!purchase);
|
|
|
|
return {
|
|
|
|
payCoinSelection: purchase.payCoinSelection,
|
|
|
|
};
|
2020-08-14 09:36:42 +02:00
|
|
|
}
|