commit d8ed968c6704cacf49f850d4c1d92d7562589435
parent 6a30be60a21b92df9feef813ea4043f5c18bfd09
Author: Florian Dold <dold@taler.net>
Date: Mon, 17 Aug 2026 22:35:03 +0200
taler-harness: consolidate integration test scenarios
Diffstat:
22 files changed, 979 insertions(+), 3016 deletions(-)
diff --git a/packages/taler-harness/src/integrationtests/kyc-dynamic-measure-helper.ts b/packages/taler-harness/src/integrationtests/kyc-dynamic-measure-helper.ts
@@ -0,0 +1,181 @@
+/*
+ This file is part of GNU Taler
+ (C) 2024, 2026 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.
+*/
+
+import {
+ codecForAny,
+ codecForKycProcessClientInformation,
+ codecOptional,
+ Configuration,
+ decodeCrock,
+ encodeCrock,
+ KycProcessClientInformation,
+ signAmlQuery,
+ TalerProtocolTimestamp,
+ TransactionIdStr,
+ TransactionMajorState,
+ TransactionMinorState,
+} from "@gnu-taler/taler-util";
+import { readResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import {
+ configureCommonKyc,
+ createKycTestkudosEnvironmentFull,
+ postAmlDecision,
+ withdrawViaBankV3,
+} from "../harness/environments.js";
+import { GlobalTestState, harnessHttpLib, waitMs } from "../harness/harness.js";
+
+function configureDynamicMeasureExchange(
+ config: Configuration,
+ amlProgramName: string,
+): void {
+ configureCommonKyc(config);
+ config.setString("KYC-RULE-R1", "operation_type", "withdraw");
+ config.setString("KYC-RULE-R1", "enabled", "yes");
+ config.setString("KYC-RULE-R1", "exposed", "yes");
+ config.setString("KYC-RULE-R1", "is_and_combinator", "no");
+ config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:5");
+ config.setString("KYC-RULE-R1", "timeframe", "1d");
+ config.setString("KYC-RULE-R1", "next_measures", "M2");
+ config.setString("KYC-MEASURE-M1", "check_name", "C1");
+ config.setString("KYC-MEASURE-M1", "context", "{}");
+ config.setString("KYC-MEASURE-M1", "program", "P1");
+ config.setString("KYC-MEASURE-M2", "check_name", "C2");
+ config.setString("KYC-MEASURE-M2", "context", "{}");
+ config.setString("KYC-MEASURE-M2", "program", "P2");
+ config.setString("KYC-MEASURE-M3", "check_name", "SKIP");
+ config.setString("KYC-MEASURE-M3", "context", "{}");
+ config.setString("KYC-MEASURE-M3", "program", "P1");
+ config.setString(
+ "AML-PROGRAM-P1",
+ "command",
+ `taler-harness aml-program run-program --name ${amlProgramName}`,
+ );
+ config.setString("AML-PROGRAM-P1", "enabled", "true");
+ config.setString("AML-PROGRAM-P1", "description", "remove all rules");
+ config.setString("AML-PROGRAM-P1", "description_i18n", "{}");
+ config.setString("AML-PROGRAM-P1", "fallback", "FREEZE");
+ config.setString("AML-PROGRAM-P2", "command", "/bin/true");
+ config.setString("AML-PROGRAM-P2", "enabled", "true");
+ config.setString("AML-PROGRAM-P2", "description", "does nothing");
+ config.setString("AML-PROGRAM-P2", "description_i18n", "{}");
+ config.setString("AML-PROGRAM-P2", "fallback", "FREEZE");
+ config.setString("KYC-CHECK-C1", "type", "FORM");
+ config.setString("KYC-CHECK-C1", "form_name", "myform");
+ config.setString("KYC-CHECK-C1", "description", "my check!");
+ config.setString("KYC-CHECK-C1", "description_i18n", "{}");
+ config.setString("KYC-CHECK-C1", "outputs", "full_name birthdate");
+ config.setString("KYC-CHECK-C1", "fallback", "FREEZE");
+ config.setString("KYC-CHECK-C2", "type", "FORM");
+ config.setString("KYC-CHECK-C2", "form_name", "dynamicform");
+ config.setString("KYC-CHECK-C2", "description", "my check info!");
+ config.setString("KYC-CHECK-C2", "description_i18n", "{}");
+ config.setString("KYC-CHECK-C2", "outputs", "what_the_officer_asked");
+ config.setString("KYC-CHECK-C2", "fallback", "FREEZE");
+}
+
+/** Exercise M3/SKIP and inspect the requirements produced by its AML program. */
+export async function runDynamicMeasureScenario(
+ t: GlobalTestState,
+ args: {
+ amlProgramName: string;
+ assertRequirements: (info: KycProcessClientInformation) => void;
+ },
+): Promise<void> {
+ const { walletClient, bankClient, exchange, amlKeypair } =
+ await createKycTestkudosEnvironmentFull(t, {
+ adjustExchangeConfig: (config) =>
+ configureDynamicMeasureExchange(config, args.amlProgramName),
+ });
+ const wres = await withdrawViaBankV3(t, {
+ amount: "TESTKUDOS:20",
+ bankClient,
+ exchange,
+ walletClient,
+ });
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: wres.transactionId as TransactionIdStr,
+ txState: {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.KycRequired,
+ },
+ });
+ const txDetails = await walletClient.call(
+ WalletApiOperation.GetTransactionById,
+ { transactionId: wres.transactionId },
+ );
+ t.assertTrue(!!txDetails.kycAccessToken);
+ t.assertTrue(!!txDetails.kycPaytoHash);
+ const accessToken = txDetails.kycAccessToken;
+
+ const initialInfoResp = await harnessHttpLib.fetch(
+ new URL(`kyc-info/${accessToken}`, exchange.baseUrl).href,
+ );
+ await readResponseJsonOrThrow(
+ initialInfoResp,
+ codecOptional(codecForKycProcessClientInformation()),
+ );
+ t.assertDeepEqual(initialInfoResp.status, 200);
+
+ const sig = signAmlQuery(decodeCrock(amlKeypair.priv));
+ const decisionHeaders = {
+ "Taler-AML-Officer-Signature": encodeCrock(sig),
+ };
+ const decisionsUrl = new URL(
+ `aml/${amlKeypair.pub}/decisions`,
+ exchange.baseUrl,
+ ).href;
+ const emptyDecisionsResp = await harnessHttpLib.fetch(decisionsUrl, {
+ headers: decisionHeaders,
+ });
+ t.assertDeepEqual(emptyDecisionsResp.status, 204);
+
+ await postAmlDecision(t, {
+ amlPriv: amlKeypair.priv,
+ amlPub: amlKeypair.pub,
+ exchangeBaseUrl: exchange.baseUrl,
+ paytoHash: txDetails.kycPaytoHash,
+ newMeasures: "M3",
+ properties: { form: { name: "string" } },
+ newRules: {
+ expiration_time: TalerProtocolTimestamp.now(),
+ custom_measures: {},
+ rules: [],
+ },
+ });
+
+ const populatedDecisionsResp = await harnessHttpLib.fetch(decisionsUrl, {
+ headers: decisionHeaders,
+ });
+ await readResponseJsonOrThrow(populatedDecisionsResp, codecForAny());
+ t.assertDeepEqual(populatedDecisionsResp.status, 200);
+
+ const deadline = Date.now() + 30_000;
+ while (true) {
+ t.assertTrue(Date.now() < deadline, "timed out waiting for AML program");
+ const infoResp = await harnessHttpLib.fetch(
+ new URL(`kyc-info/${accessToken}`, exchange.baseUrl).href,
+ );
+ if (infoResp.status === 202 || infoResp.status === 204) {
+ await waitMs(250);
+ continue;
+ }
+ const clientInfo = await readResponseJsonOrThrow(
+ infoResp,
+ codecOptional(codecForKycProcessClientInformation()),
+ );
+ t.assertDeepEqual(infoResp.status, 200);
+ if (!clientInfo || clientInfo.requirements.length === 0) {
+ await waitMs(250);
+ continue;
+ }
+ args.assertRequirements(clientInfo);
+ return;
+ }
+}
diff --git a/packages/taler-harness/src/integrationtests/kyc-form-withdrawal-helper.ts b/packages/taler-harness/src/integrationtests/kyc-form-withdrawal-helper.ts
@@ -0,0 +1,137 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020, 2026 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.
+*/
+
+import {
+ codecForAny,
+ codecForKycProcessClientInformation,
+ Configuration,
+ decodeCrock,
+ encodeCrock,
+ signAmlQuery,
+ TransactionIdStr,
+ TransactionMajorState,
+ TransactionMinorState,
+} from "@gnu-taler/taler-util";
+import {
+ expectSuccessResponseOrThrow,
+ readResponseJsonOrThrow,
+} from "@gnu-taler/taler-util/http";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import {
+ configureCommonKyc,
+ createKycTestkudosEnvironmentFull,
+ withdrawViaBankV3,
+} from "../harness/environments.js";
+import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";
+
+function adjustExchangeConfig(config: Configuration): void {
+ configureCommonKyc(config);
+ config.setString("KYC-RULE-R1", "operation_type", "withdraw");
+ config.setString("KYC-RULE-R1", "enabled", "yes");
+ config.setString("KYC-RULE-R1", "exposed", "yes");
+ config.setString("KYC-RULE-R1", "is_and_combinator", "yes");
+ config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:5");
+ config.setString("KYC-RULE-R1", "timeframe", "1d");
+ config.setString("KYC-RULE-R1", "next_measures", "M1 M2");
+ config.setString("KYC-MEASURE-M1", "check_name", "C1");
+ config.setString("KYC-MEASURE-M1", "context", "{}");
+ config.setString("KYC-MEASURE-M1", "program", "P1");
+ config.setString("KYC-MEASURE-M2", "check_name", "C2");
+ config.setString("KYC-MEASURE-M2", "context", "{}");
+ config.setString("KYC-MEASURE-M2", "program", "NONE");
+ config.setString(
+ "AML-PROGRAM-P1",
+ "command",
+ "taler-exchange-helper-measure-test-form",
+ );
+ config.setString("AML-PROGRAM-P1", "enabled", "true");
+ config.setString(
+ "AML-PROGRAM-P1",
+ "description",
+ "test for FULL_NAME and DATE_OF_BIRTH",
+ );
+ config.setString("AML-PROGRAM-P1", "description_i18n", "{}");
+ config.setString("AML-PROGRAM-P1", "fallback", "FREEZE");
+ config.setString("KYC-CHECK-C1", "type", "FORM");
+ config.setString("KYC-CHECK-C1", "form_name", "full_name_and_birthdate");
+ config.setString("KYC-CHECK-C1", "description", "my check!");
+ config.setString("KYC-CHECK-C1", "description_i18n", "{}");
+ config.setString("KYC-CHECK-C1", "outputs", "FULL_NAME DATE_OF_BIRTH");
+ config.setString("KYC-CHECK-C1", "fallback", "FREEZE");
+ config.setString("KYC-CHECK-C2", "type", "INFO");
+ config.setString("KYC-CHECK-C2", "description", "my check info!");
+ config.setString("KYC-CHECK-C2", "description_i18n", "{}");
+ config.setString("KYC-CHECK-C2", "fallback", "FREEZE");
+}
+
+export async function runKycFormWithdrawalScenario(
+ t: GlobalTestState,
+ upload: { body: object; compress?: "deflate" },
+): Promise<void> {
+ const { walletClient, bankClient, exchange, amlKeypair } =
+ await createKycTestkudosEnvironmentFull(t, { adjustExchangeConfig });
+ const wres = await withdrawViaBankV3(t, {
+ amount: "TESTKUDOS:20",
+ bankClient,
+ exchange,
+ walletClient,
+ });
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: wres.transactionId as TransactionIdStr,
+ txState: {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.KycRequired,
+ },
+ });
+ const txDetails = await walletClient.call(
+ WalletApiOperation.GetTransactionById,
+ { transactionId: wres.transactionId },
+ );
+ t.assertTrue(!!txDetails.kycAccessToken);
+ const infoResp = await harnessHttpLib.fetch(
+ new URL(`kyc-info/${txDetails.kycAccessToken}`, exchange.baseUrl).href,
+ );
+ const clientInfo = await readResponseJsonOrThrow(
+ infoResp,
+ codecForKycProcessClientInformation(),
+ );
+ const kycId = clientInfo.requirements.find((x) => x.id != null)?.id;
+ t.assertTrue(!!kycId);
+ const uploadResp = await harnessHttpLib.fetch(
+ new URL(`kyc-upload/${kycId}`, exchange.baseUrl).href,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ compress: upload.compress,
+ body: upload.body,
+ },
+ );
+ await expectSuccessResponseOrThrow(uploadResp);
+ t.assertDeepEqual(uploadResp.status, 204);
+
+ const updatedInfoResp = await harnessHttpLib.fetch(
+ new URL(`kyc-info/${txDetails.kycAccessToken}`, exchange.baseUrl).href,
+ );
+ await readResponseJsonOrThrow(
+ updatedInfoResp,
+ codecForKycProcessClientInformation(),
+ );
+
+ const sig = signAmlQuery(decodeCrock(amlKeypair.priv));
+ const decisionsResp = await harnessHttpLib.fetch(
+ new URL(`aml/${amlKeypair.pub}/decisions`, exchange.baseUrl).href,
+ { headers: { "Taler-AML-Officer-Signature": encodeCrock(sig) } },
+ );
+ await readResponseJsonOrThrow(decisionsResp, codecForAny());
+ t.assertDeepEqual(decisionsResp.status, 200);
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: wres.transactionId as TransactionIdStr,
+ txState: { major: TransactionMajorState.Done },
+ });
+}
diff --git a/packages/taler-harness/src/integrationtests/kyc-threshold-withdrawal-helper.ts b/packages/taler-harness/src/integrationtests/kyc-threshold-withdrawal-helper.ts
@@ -0,0 +1,128 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020, 2026 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.
+*/
+
+import {
+ Configuration,
+ NotificationType,
+ TransactionMajorState,
+ TransactionMinorState,
+ TransactionType,
+} from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import {
+ configureCommonKyc,
+ createKycTestkudosEnvironmentFull,
+ postAmlDecisionNoRules,
+} from "../harness/environments.js";
+import { GlobalTestState } from "../harness/harness.js";
+
+function adjustExchangeConfig(config: Configuration): void {
+ configureCommonKyc(config);
+
+ config.setString("KYC-RULE-R1", "operation_type", "withdraw");
+ config.setString("KYC-RULE-R1", "enabled", "yes");
+ config.setString("KYC-RULE-R1", "exposed", "yes");
+ config.setString("KYC-RULE-R1", "is_and_combinator", "yes");
+ config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:5");
+ config.setString("KYC-RULE-R1", "timeframe", "1d");
+ config.setString("KYC-RULE-R1", "next_measures", "M1");
+
+ config.setString("KYC-RULE-R2", "operation_type", "withdraw");
+ config.setString("KYC-RULE-R2", "enabled", "yes");
+ config.setString("KYC-RULE-R2", "exposed", "yes");
+ config.setString("KYC-RULE-R2", "is_and_combinator", "yes");
+ config.setString("KYC-RULE-R2", "threshold", "TESTKUDOS:300");
+ config.setString("KYC-RULE-R2", "timeframe", "1d");
+ config.setString("KYC-RULE-R2", "next_measures", "verboten");
+
+ config.setString("KYC-MEASURE-M1", "check_name", "C1");
+ config.setString("KYC-MEASURE-M1", "context", "{}");
+ config.setString("KYC-MEASURE-M1", "program", "NONE");
+
+ config.setString("KYC-CHECK-C1", "type", "INFO");
+ config.setString("KYC-CHECK-C1", "description", "my check!");
+ config.setString("KYC-CHECK-C1", "fallback", "FREEZE");
+}
+
+/** Run the common successful withdrawal that establishes an AML account. */
+export async function runKycThresholdWithdrawalScenario(t: GlobalTestState) {
+ const { walletClient, bankClient, exchange, amlKeypair } =
+ await createKycTestkudosEnvironmentFull(t, { adjustExchangeConfig });
+
+ const user = await bankClient.createRandomBankUser();
+ bankClient.setAuth({ username: user.username, password: user.password });
+ const wop = await bankClient.createWithdrawalOperation(
+ user.username,
+ "TESTKUDOS:20",
+ );
+
+ const withdrawalUrlInfo = await walletClient.client.call(
+ WalletApiOperation.GetWithdrawalDetailsForUri,
+ { talerWithdrawUri: wop.taler_withdraw_uri },
+ );
+ const withdrawalAmountInfo = await walletClient.call(
+ WalletApiOperation.GetWithdrawalDetailsForAmount,
+ {
+ amount: withdrawalUrlInfo.amount!,
+ exchangeBaseUrl: withdrawalUrlInfo.possibleExchanges[0].exchangeBaseUrl,
+ },
+ );
+ t.assertTrue(!!withdrawalAmountInfo.kycHardLimit);
+ t.assertAmountEquals(withdrawalAmountInfo.kycHardLimit, "TESTKUDOS:300");
+
+ const acceptResp = await walletClient.client.call(
+ WalletApiOperation.AcceptBankIntegratedWithdrawal,
+ {
+ exchangeBaseUrl: exchange.baseUrl,
+ talerWithdrawUri: wop.taler_withdraw_uri,
+ },
+ );
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: acceptResp.transactionId,
+ txState: {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.BankConfirmTransfer,
+ },
+ });
+ await bankClient.confirmWithdrawalOperation(user.username, {
+ withdrawalOperationId: wop.withdrawal_id,
+ });
+
+ await walletClient.waitForNotificationCond((x) =>
+ x.type === NotificationType.TransactionStateTransition &&
+ x.transactionId === acceptResp.transactionId &&
+ x.newTxState.major === TransactionMajorState.Pending &&
+ x.newTxState.minor === TransactionMinorState.KycRequired
+ ? x
+ : false,
+ );
+
+ const txDet = await walletClient.call(WalletApiOperation.GetTransactionById, {
+ transactionId: acceptResp.transactionId,
+ });
+ t.assertDeepEqual(txDet.type, TransactionType.Withdrawal);
+ const kycPaytoHash = txDet.kycPaytoHash;
+ t.assertTrue(!!kycPaytoHash);
+
+ await postAmlDecisionNoRules(t, {
+ amlPriv: amlKeypair.priv,
+ amlPub: amlKeypair.pub,
+ exchangeBaseUrl: exchange.baseUrl,
+ paytoHash: kycPaytoHash,
+ });
+ await walletClient.waitForNotificationCond((x) =>
+ x.type === NotificationType.TransactionStateTransition &&
+ x.transactionId === acceptResp.transactionId &&
+ x.newTxState.major === TransactionMajorState.Done
+ ? x
+ : false,
+ );
+
+ return { walletClient, bankClient, exchange, amlKeypair, user, kycPaytoHash };
+}
diff --git a/packages/taler-harness/src/integrationtests/merchant-kyc-auth-helper.ts b/packages/taler-harness/src/integrationtests/merchant-kyc-auth-helper.ts
@@ -0,0 +1,190 @@
+/*
+ This file is part of GNU Taler
+ (C) 2024, 2026 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.
+*/
+
+import {
+ AccessToken,
+ Configuration,
+ MerchantAccountKycStatus,
+ Paytos,
+ Result,
+ succeedOrThrow,
+ TalerMerchantInstanceHttpClient,
+ TalerWireGatewayHttpClient,
+} from "@gnu-taler/taler-util";
+import { configureCommonKyc } from "../harness/environments.js";
+import {
+ BankService,
+ ExchangeService,
+ GlobalTestState,
+ MerchantService,
+} from "../harness/harness.js";
+
+const merchantOnboardingConfig = `
+[kyc-measure-freeze-investigate]
+CHECK_NAME = skip
+PROGRAM = freeze-investigate
+VOLUNTARY = NO
+CONTEXT = {}
+
+[aml-program-freeze-investigate]
+DESCRIPTION = "Fallback measure on errors that freezes the account and asks AML staff to investigate the system failure."
+COMMAND = taler-exchange-helper-measure-freeze
+ENABLED = YES
+FALLBACK = freeze-investigate
+
+[aml-program-inform-investigate]
+DESCRIPTION = "Measure that asks AML staff to investigate an account and informs the account owner about it."
+COMMAND = taler-exchange-helper-measure-inform-investigate
+ENABLED = YES
+FALLBACK = freeze-investigate
+
+[kyc-check-form-gls-merchant-onboarding]
+TYPE = FORM
+FORM_NAME = gls-merchant-onboarding
+DESCRIPTION = "GLS Merchant Onboarding"
+DESCRIPTION_I18N = {}
+OUTPUTS =
+FALLBACK = freeze-investigate
+
+[kyc-measure-merchant-onboarding]
+CHECK_NAME = form-gls-merchant-onboarding
+PROGRAM = inform-investigate
+CONTEXT = {}
+VOLUNTARY = NO
+`;
+
+export function configureMerchantDepositKyc(
+ config: Configuration,
+ threshold: string,
+ includeInformInvestigateSuccessor = false,
+): void {
+ configureCommonKyc(config);
+ config.loadFromString(merchantOnboardingConfig);
+ config.setString("KYC-RULE-DEPOSIT-LIMIT", "operation_type", "deposit");
+ config.setString(
+ "KYC-RULE-DEPOSIT-LIMIT",
+ "next_measures",
+ "merchant-onboarding",
+ );
+ config.setString("KYC-RULE-DEPOSIT-LIMIT", "exposed", "yes");
+ config.setString("KYC-RULE-DEPOSIT-LIMIT", "enabled", "yes");
+ config.setString("KYC-RULE-DEPOSIT-LIMIT", "threshold", threshold);
+ config.setString("KYC-RULE-DEPOSIT-LIMIT", "timeframe", "1 days");
+ if (includeInformInvestigateSuccessor) {
+ config.setString(
+ "KYC-MEASURE-INFORM-INVESTIGATE",
+ "check_name",
+ "SKIP",
+ );
+ config.setString(
+ "KYC-MEASURE-INFORM-INVESTIGATE",
+ "program",
+ "NONE",
+ );
+ config.setString(
+ "KYC-MEASURE-INFORM-INVESTIGATE",
+ "voluntary",
+ "no",
+ );
+ config.setString(
+ "KYC-MEASURE-INFORM-INVESTIGATE",
+ "context",
+ "{}",
+ );
+ }
+}
+
+export function configureMerchantInfoDepositKyc(config: Configuration): void {
+ configureCommonKyc(config);
+ config.setString("KYC-RULE-R1", "operation_type", "deposit");
+ config.setString("KYC-RULE-R1", "enabled", "yes");
+ config.setString("KYC-RULE-R1", "exposed", "yes");
+ config.setString("KYC-RULE-R1", "is_and_combinator", "yes");
+ config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:0");
+ config.setString("KYC-RULE-R1", "timeframe", "1d");
+ config.setString("KYC-RULE-R1", "next_measures", "M1");
+ config.setString("KYC-MEASURE-M1", "check_name", "C1");
+ config.setString("KYC-MEASURE-M1", "context", "{}");
+ config.setString("KYC-MEASURE-M1", "program", "P1");
+ config.setString("KYC-MEASURE-FM", "check_name", "SKIP");
+ config.setString("KYC-MEASURE-FM", "context", "{}");
+ config.setString("KYC-MEASURE-FM", "program", "P1");
+ config.setString("AML-PROGRAM-P1", "command", "/bin/true");
+ config.setString("AML-PROGRAM-P1", "enabled", "true");
+ config.setString("AML-PROGRAM-P1", "description", "this does nothing");
+ config.setString("AML-PROGRAM-P1", "fallback", "FREEZE");
+ config.setString("KYC-CHECK-C1", "type", "INFO");
+ config.setString("KYC-CHECK-C1", "description", "my check!");
+ config.setString("KYC-CHECK-C1", "fallback", "FREEZE");
+}
+
+export async function doAccountKycAuth(
+ t: GlobalTestState,
+ args: {
+ exchange: ExchangeService;
+ merchant: MerchantService;
+ bank: BankService;
+ merchantInstId: string;
+ merchantInstPaytoUri: string;
+ merchantAccessToken: AccessToken;
+ wireGatewayApi: TalerWireGatewayHttpClient;
+ },
+): Promise<void> {
+ const merchantClient = new TalerMerchantInstanceHttpClient(
+ args.merchant.makeInstanceBaseUrl(args.merchantInstId),
+ );
+ const initialStatus = succeedOrThrow(
+ await merchantClient.getCurrentInstanceKycStatus(
+ args.merchantAccessToken,
+ {},
+ ),
+ );
+ const initialRow = initialStatus.kyc_data.find(
+ (x) => x.exchange_url === args.exchange.baseUrl,
+ );
+ t.assertTrue(
+ initialRow?.payto_kycauths != null &&
+ initialRow.payto_kycauths.length === 1,
+ );
+ const authPayto = Result.unpack(
+ Paytos.fromString(initialRow.payto_kycauths[0]),
+ );
+ const authMessage = authPayto.params["message"];
+ t.assertTrue(typeof authMessage === "string");
+ t.assertTrue(authMessage.startsWith("KYC:"));
+ await args.wireGatewayApi.addKycAuth({
+ auth: args.bank.getAdminAuth(),
+ body: {
+ amount: "TESTKUDOS:0.1",
+ debit_account: args.merchantInstPaytoUri,
+ account_pub: authMessage.substring(4),
+ },
+ });
+ await args.exchange.runWirewatchOnce();
+
+ const finalStatus = await merchantClient.getCurrentInstanceKycStatus(
+ args.merchantAccessToken,
+ {
+ longpoll: {
+ type: "state-exit",
+ status: MerchantAccountKycStatus.KYC_WIRE_REQUIRED,
+ timeout: 30_000,
+ },
+ },
+ );
+ t.assertDeepEqual(finalStatus.case, "ok");
+ const finalRow = finalStatus.body.kyc_data.find(
+ (x) =>
+ x.exchange_url === args.exchange.baseUrl &&
+ x.payto_uri === args.merchantInstPaytoUri,
+ );
+ t.assertTrue(finalRow != null);
+ t.assertDeepEqual(finalRow.status, "ready");
+ t.assertTrue(typeof finalRow.access_token === "string");
+}
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-form-compression.ts b/packages/taler-harness/src/integrationtests/test-kyc-form-compression.ts
@@ -17,136 +17,17 @@
/**
* Imports.
*/
-import {
- codecForAny,
- codecForKycProcessClientInformation,
- Configuration,
- decodeCrock,
- encodeCrock,
- j2s,
- signAmlQuery,
- TransactionIdStr,
- TransactionMajorState,
- TransactionMinorState,
-} from "@gnu-taler/taler-util";
-import {
- expectSuccessResponseOrThrow,
- readResponseJsonOrThrow,
-} from "@gnu-taler/taler-util/http";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import {
- configureCommonKyc,
- createKycTestkudosEnvironmentFull,
- withdrawViaBankV3,
-} from "../harness/environments.js";
-import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";
-
-function adjustExchangeConfig(config: Configuration) {
- configureCommonKyc(config);
-
- config.setString("KYC-RULE-R1", "operation_type", "withdraw");
- config.setString("KYC-RULE-R1", "enabled", "yes");
- config.setString("KYC-RULE-R1", "exposed", "yes");
- config.setString("KYC-RULE-R1", "is_and_combinator", "yes");
- config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:5");
- config.setString("KYC-RULE-R1", "timeframe", "1d");
- config.setString("KYC-RULE-R1", "next_measures", "M1 M2");
-
- config.setString("KYC-MEASURE-M1", "check_name", "C1");
- config.setString("KYC-MEASURE-M1", "context", "{}");
- config.setString("KYC-MEASURE-M1", "program", "P1");
-
- config.setString("KYC-MEASURE-M2", "check_name", "C2");
- config.setString("KYC-MEASURE-M2", "context", "{}");
- config.setString("KYC-MEASURE-M2", "program", "NONE");
-
- config.setString(
- "AML-PROGRAM-P1",
- "command",
- "taler-exchange-helper-measure-test-form",
- );
- config.setString("AML-PROGRAM-P1", "enabled", "true");
- config.setString(
- "AML-PROGRAM-P1",
- "description",
- "test for FULL_NAME and DATE_OF_BIRTH",
- );
- config.setString("AML-PROGRAM-P1", "description_i18n", "{}");
- config.setString("AML-PROGRAM-P1", "fallback", "FREEZE");
-
- config.setString("KYC-CHECK-C1", "type", "FORM");
- config.setString("KYC-CHECK-C1", "form_name", "full_name_and_birthdate");
- config.setString("KYC-CHECK-C1", "description", "my check!");
- config.setString("KYC-CHECK-C1", "description_i18n", "{}");
- config.setString("KYC-CHECK-C1", "outputs", "FULL_NAME DATE_OF_BIRTH");
- config.setString("KYC-CHECK-C1", "fallback", "FREEZE");
-
- config.setString("KYC-CHECK-C2", "type", "INFO");
- config.setString("KYC-CHECK-C2", "description", "my check info!");
- config.setString("KYC-CHECK-C2", "description_i18n", "{}");
- config.setString("KYC-CHECK-C2", "fallback", "FREEZE");
-}
+import { GlobalTestState } from "../harness/harness.js";
+import { runKycFormWithdrawalScenario } from "./kyc-form-withdrawal-helper.js";
+/** Submit a large deflate-compressed KYC form and finish the withdrawal. */
export async function runKycFormCompressionTest(t: GlobalTestState) {
- // Set up test environment
-
- const { walletClient, bankClient, exchange, amlKeypair } =
- await createKycTestkudosEnvironmentFull(t, { adjustExchangeConfig });
-
- // Withdraw digital cash into the wallet.
-
- const wres = await withdrawViaBankV3(t, {
- amount: "TESTKUDOS:20",
- bankClient,
- exchange,
- walletClient,
- });
-
- await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: wres.transactionId as TransactionIdStr,
- txState: {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.KycRequired,
- },
- });
-
- const txDetails = await walletClient.call(
- WalletApiOperation.GetTransactionById,
- {
- transactionId: wres.transactionId,
- },
- );
-
- console.log(j2s(txDetails));
- const accessToken = txDetails.kycAccessToken;
- t.assertTrue(!!accessToken);
-
- const infoResp = await harnessHttpLib.fetch(
- new URL(`kyc-info/${txDetails.kycAccessToken}`, exchange.baseUrl).href,
- );
-
- const clientInfo = await readResponseJsonOrThrow(
- infoResp,
- codecForKycProcessClientInformation(),
- );
-
- console.log(j2s(clientInfo));
-
- const kycId = clientInfo.requirements.find((x) => x.id != null)?.id;
- t.assertTrue(!!kycId);
-
const CONTENTS =
"JVBERi0xLjcKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp4nC3NvQrCMBgF0P17ijs7JDdJ06RQCv0THDqIAedirehQMBR8fUF8gMOhMviIbvP+XOfbjm7q5Q2CijbCV17Z4BELo2JpkO9yPWCTcepFX/Z5W+a81LWe+tMANk03/DWI/JAuiS9VRAhOVbFCWqCPBsYirTUNLR0dC3qWDCwY6VixbdJLxiTnX3PGF2mOJVkKZW5kc3RyZWFtCmVuZG9iagoKMyAwIG9iagoxNDQKZW5kb2JqCgo3IDAgb2JqCjw8L0xlbmd0aCA4IDAgUi9GaWx0ZXIvRmxhdGVEZWNvZGUvTGVuZ3RoMSA5MzQ4Pj4Kc3RyZWFtCnic5Vl7cBt3nf98dyW/ZFta11blqrZ+7kZujCS/FKdJEzeqbclOnMRKbLdS2sRaS2tLxZZUSUlIOa4GWgguuYbSoxR6NNwA0+lkruumgFtKY44WjoFCOXpMn0c4ysHMNddQwuOgsW/2p7VjhxTmbu6/+zne/b7f318ipZA7qKISMxARiE8r2WuqLDUAvgdQTfxQgXUP1W0B6Awg/PNEdnL6M1+99TxgegIofWJy6sjEJ/9rrAWoTAJlm5Kqkpjv2OgFak8A2JhMqsqexSOlQO1LANYlpwvvOyH+1A3UvgPAO5WJK1dVtpcBdesBOKaV92XtpoAA1AUAsLQyrf7+c99MAHUxwJLPZvKFBI4uAexFnZ/Nqdmdnxl/DmDnAfE4AAJBP5UAlei4IJrMJaVl5RX4/3nMx1CHAXM3rMjy55ojnkQ9HgSW3tSxi8/FnUt/+L+Moqz4+jS+hCdwDC9jv8EIIYwUDuLlNeLfwA+R4lAY+/AoZt/F7EnMY58hF8O9eiaXPWE8gFP49hovYUzj/TiGL+Nl6sB3AMrgbSrDB/Ec5vE2dWDX5UwJ1QAmODixivoqPivcgx3CGwAe1DlCm2DDs3iIDgBUQAjHVjLe+idGP4oPABhGEofw0SLJ3P3OKyhf+jXuxQewAx/CjZhapfE0PSxWAOIIHsaX8A1Oa1tmlg6ItwlfEYQLnwTwCUziE1DoZUA4Jt74LhX6Hx9xFFXUIrpRfjmusAHWxT8InUvnxXWowOjSuWXa0uDSr0VlMW0aM11t7jZ998/5KPmEaRoVwNLPF9+/mDDvNn8JX8YjQKD/ln3RyOjI8N494aHdu3YO7tg+0B8K9vX23BjYdkP31i3Xb9503caujva2Vp93/bXN7nXyNU0uR61ks1ZXWSrKy0pLzCZRIHiZRrGgJrqZFFLkoKwM+Lws6Ej2+bxBORTTmMK0UEwzNcsDA5wkKxqLMa1Z0ZiyihzTAgrTJi6RDBQlAyuSZGNbsVV3ITPt+T6ZzdO+PRGZacf65CjTznJ4F4dNzRyp6pOjTU0+L+NR6dGyoBY6lJwNxvp8XpqzVPTKvWqFz4u5Ckuv3GvxeaGtl7NztP4G4oCwPnj9nICyKt2tJrqDSkIL74kE+5xNTVGfd7tWLfdxFnq5Sa2kVyvlJllKDx33sDnvwuzH520Yj3kqE3JCuTWiiUrU550Vg7OzH9Ukj9Yi92ktd7zh8HmDquaV+4KaR7c6uHfFz+BFl6SZ3TaZzf4GGsXks2+upSgGpcRt+w10UBN6NdobadKPMySHYrOzIZmFZmOzyvzSzLjMbPLsXGXlbDYYYxrCEY2U+aWn7nFqoY9HNVssSddHjdRDewe1K/bcEtEEd4glFU10a6J7m9y0ydkkrciE340NrbRXK9Er3NSkl+Ge+QDGfd4mbWZPpIgzjDsfR6DNE9WEmM5ZWObUjeqcmWXOinpMbvJ5B4cjs5rJvT0hB1Na4B5FmxnXmHKb3hjZplX/1tkkz9ZIbHNblMsyTXRvT6SYZm7WSnSt1QqaqVlXmbVxpPq3xddZ56xmapZq2GaZbW7T7QTlYMz4cyjp0GbGmc+rDXiKgzAS0QJ9LKgFFKNjwbn2tqAcVGIaxVJ9vJlam5zVauWele7qYQVTwxGuYqhptb0aYnFDS2sL8r1iwdlYXzEE3Za8J/Ik/Etn5jYw5yk/NiDapwvbeyOa2BycjSQmNFfMmdBYbIJFnE1aIKqREpUjalQfO9mmtZxx8uGI8lkZiQwOy4N79kU2GYEUGbo5kzt4iRk54iya0cxurcxdxiKCU4xqJrdNM7tZSDO55Z6tmsmtlbrLtFK3TSspUvXB7dnKIuTEsrTWckZrYUG1z5DT8TVGzfo49Q4sWyvRUY1ivQPOpmhT8fi8gmZyM8OxZnaX6UUdWGaJbqaZ3GWa4O4d4CS9lg596FlEVuWonGRaIBzRc9PLw6tsFIPX3OjVyBpsVbF8Xg1NgyMriF5MLeRxri6u1s/xFXTgEvb2ZTabLZMHh2d147JhEJrg3q5BH+HAJsnJ7wJ9oeWQIjMbCxUXenYuENCXOXm9bkTenpiVhyNbufTg3sgHnHfovmowSIMjPT7vnICeOZmO7pkL0NHhfZEnbQA7OhJ5XCChN9YTnVtHR/dEnmRAgFMFnaoTdYTpiG5pb+RxoYzLO58MADOca+IEjsfnCZxWtkwjxOeFIs1WdNTMHQUgID5vKnICy9ImxOfLirQZTuNnDnrJAhXmQFmgPFApVAnOOdJJj5sDZU8RUE44VUlV5JybEXr3cvI8zcyVB5xFiRmUU6AY4dHRi65H90VOVaKKnPwZjUZ79OPzBh1JeVD/ayXIEvqg/FU0ORuL6ssGuya4NcFNGsk3QBPkG+ZIKKnUKmS1R7PIPTp9m07fVqSX6PRSuUcjO/m82owm9IY10ifglkiTbNPYVd9xztrO6p2KenzeWdvPfSBsAcz3mLvhxGOBdIXdLpXX1wuS0HB1uSMWtZZvKx8qFy1ieTnMNkuZaBbHolVmsd4uCRDGorWo2dBA6xqotoFMDXT9sw2UaKC+Zfx8A73B0aLQmQYSXmigEw2UbaBwA+3fv3//2P7b9ZPTzwGdgG2dHgl+v9+xzV8EpJrNjraxA/s9Ug1t3iz5iz8d7f6u66Rru5rqSqUm431lXVPXdVKTdGXdlpMnhfqTwuGTguPkyQu/PHnhYye7nKbPXdXVddWFXwlW/f3HcWdXl1PouvBdZxcEhJfeFEPic6jD1TgW2FdPZL2qrM5a19BYj3DUWu+qFyrF+vrKmhp7OFpjqzTviVbaFxpJa6QTjXS8kWYaKdtIsUYKNxIa6YZwIwUaqb2RWCPZGukcl5tppDXZ7i+m7IFjm0eqwdpEjTyprraR/J0br6urJvmaZmnDRj+T6uiakrqmDc1k6r5zcuP97e1fvOnV737/NKUWH0hm6L5b6eWa2QfDNZZNrtY3yfzbtxcn9tJDj3zh1IP6p7EWQLCZj6Ecfx/Imi0V5SXhaDlgFs3hqFj3ooWetdATFvqChe630F0WKlgoYaF1Fqq1kMlCm89zieMWErIWilkobKGAhRYspFnoBEdtFoKFznH0uIVWixlJ377S97E1xeDlkODnBaHNHe3uYlev7WqizOcX60+coFCo3uerNwsOHwTsXnpTfFx8DhWw46nAByWzBWZc6SirDkfLbEJtOCrYmYPgoDMOCjuo3UE2B53j6AsOWnCQ5qATDjruoBkHZR0Uc1DAQUWVLQ9zUpiT2jnVxhmr9U9wzaJazEFGYkaTL0mtmHYut9JxY6A72snWdE1z14aN/k576YZm+ZqSulq7v3Oj+PjiwIsvvfTaj1954q8/8uGDhz941wy9uigt/uo/3/ndr1/6x6fO/Ozrz4J/wt69+BzdiR/BBm/AUQKYLBapRjT9XbRafOSWkuoXaihWQ/v3o83v4X75eHW0u2tLSrs2dm1ovtbwTnc+fJ/W6O7r6woMdXzsyffs2pStZVfIgY2bb+V+hKU3zc+IJ1GLXwcaK8zWWnNtnV0oq6gaEKqqaq0V5lJzOCqVWqstlvml3wc+XFE1YBEJJvuInfrstM5ONjuZ7HTeTk/Y6YSd7rfTXXYqcG47F/gCJybsNGIn2GnzeTu9wZGAnYR2OzFuA3aasVPMTmHOKNJfsNNpbnXGTlk7jXHiZSfO6Mvta9qFbX6/3++R/Mt3j9/R5l/dJrNsIZn4beMkv7MImZy7f/nzHYtfy9Dph37ys5F/+5fP0ESyVpi6cL94R73P57xwt6BeeED4YL3PV4fl/fu0+BxqsSfgk0pLqbKyzl4iQbJJQrVZEoVam60qHLVZSysrKsPRiroxO7l4lsXZKa4HHHq0Ywf2S349wprNncXbQr72mpLlu/DarqYrbyC/8GnP9Z0f6/z8Ys/hw1RTvvX5reJzi2mn/UKPvkkiq/cd7LyVf0TEjqU3xV+Yj+EKNGAmMFRrsqC+3mayNbqusIWjV9RZK8NRK0qvDkdLbfWAIFy5JyrY4aL+sIsCLmp3EXMRXLTgohlOKQIxTjdKbixGMRXJSGbtShSns0Rm0oYaf+eVzd16rfWsSF+M66RmmQk/uv2BxTtfeXEqU/I56iss/n7RNXPX7fuiucV3Qvvop78jurLp7vMO3x+erPfR88987VrhFxLP8dWln9Hz5m5YYKXSwFdRVVVSWWmTqkRrNVWK1WJALP/sLeIVP5boWxKFJNoo0Vck+pJEzRLZJSrSvyLRFyS6X6IPSURZiWIShSXqk2iDROskskkEiTafk+gNiV6UaEGiJ5Y1ZiRDYW";
- const uploadResp = await harnessHttpLib.fetch(
- new URL(`kyc-upload/${kycId}`, exchange.baseUrl).href,
- {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- compress: "deflate",
- body: {
+ await runKycFormWithdrawalScenario(t, {
+ compress: "deflate",
+ body: {
FULL_NAME: "Alice Abc",
DATE_OF_BIRTH: "2000-01-01",
STUFF: {
@@ -207,49 +88,6 @@ export async function runKycFormCompressionTest(t: GlobalTestState) {
},
],
},
- },
- },
- );
-
- await expectSuccessResponseOrThrow(uploadResp);
-
- t.assertDeepEqual(uploadResp.status, 204);
-
- {
- // Do a GET on kyc-info here as this reproduces a bug in the
- // exchange.
- const infoResp = await harnessHttpLib.fetch(
- new URL(`kyc-info/${txDetails.kycAccessToken}`, exchange.baseUrl).href,
- );
-
- await readResponseJsonOrThrow(
- infoResp,
- codecForKycProcessClientInformation(),
- );
- }
-
- const sig = signAmlQuery(decodeCrock(amlKeypair.priv));
-
- const decisionsResp = await harnessHttpLib.fetch(
- new URL(`aml/${amlKeypair.pub}/decisions`, exchange.baseUrl).href,
- {
- headers: {
- "Taler-AML-Officer-Signature": encodeCrock(sig),
- },
- },
- );
-
- const decisions = await readResponseJsonOrThrow(decisionsResp, codecForAny());
- console.log(j2s(decisions));
-
- t.assertDeepEqual(decisionsResp.status, 200);
-
- // KYC should pass now
-
- await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: wres.transactionId as TransactionIdStr,
- txState: {
- major: TransactionMajorState.Done,
},
});
}
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-form-withdrawal.ts b/packages/taler-harness/src/integrationtests/test-kyc-form-withdrawal.ts
@@ -1,193 +0,0 @@
-/*
- 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/>
- */
-
-/**
- * Imports.
- */
-import {
- codecForAny,
- codecForKycProcessClientInformation,
- Configuration,
- decodeCrock,
- encodeCrock,
- j2s,
- signAmlQuery,
- TransactionIdStr,
- TransactionMajorState,
- TransactionMinorState,
-} from "@gnu-taler/taler-util";
-import { readResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import {
- configureCommonKyc,
- createKycTestkudosEnvironmentFull,
- withdrawViaBankV3,
-} from "../harness/environments.js";
-import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";
-
-function adjustExchangeConfig(config: Configuration) {
- configureCommonKyc(config);
-
- config.setString("KYC-RULE-R1", "operation_type", "withdraw");
- config.setString("KYC-RULE-R1", "enabled", "yes");
- config.setString("KYC-RULE-R1", "exposed", "yes");
- config.setString("KYC-RULE-R1", "is_and_combinator", "yes");
- config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:5");
- config.setString("KYC-RULE-R1", "timeframe", "1d");
- config.setString("KYC-RULE-R1", "next_measures", "M1 M2");
-
- config.setString("KYC-MEASURE-M1", "check_name", "C1");
- config.setString("KYC-MEASURE-M1", "context", "{}");
- config.setString("KYC-MEASURE-M1", "program", "P1");
-
- config.setString("KYC-MEASURE-M2", "check_name", "C2");
- config.setString("KYC-MEASURE-M2", "context", "{}");
- config.setString("KYC-MEASURE-M2", "program", "NONE");
-
- config.setString(
- "AML-PROGRAM-P1",
- "command",
- "taler-exchange-helper-measure-test-form",
- );
- config.setString("AML-PROGRAM-P1", "enabled", "true");
- config.setString(
- "AML-PROGRAM-P1",
- "description",
- "test for FULL_NAME and DATE_OF_BIRTH",
- );
- config.setString("AML-PROGRAM-P1", "description_i18n", "{}");
- config.setString("AML-PROGRAM-P1", "fallback", "FREEZE");
-
- config.setString("KYC-CHECK-C1", "type", "FORM");
- config.setString("KYC-CHECK-C1", "form_name", "full_name_and_birthdate");
- config.setString("KYC-CHECK-C1", "description", "my check!");
- config.setString("KYC-CHECK-C1", "description_i18n", "{}");
- config.setString("KYC-CHECK-C1", "outputs", "FULL_NAME DATE_OF_BIRTH");
- config.setString("KYC-CHECK-C1", "fallback", "FREEZE");
-
- config.setString("KYC-CHECK-C2", "type", "INFO");
- config.setString("KYC-CHECK-C2", "description", "my check info!");
- config.setString("KYC-CHECK-C2", "description_i18n", "{}");
- config.setString("KYC-CHECK-C2", "fallback", "FREEZE");
-}
-
-export async function runKycFormWithdrawalTest(t: GlobalTestState) {
- // Set up test environment
-
- const { walletClient, bankClient, exchange, amlKeypair } =
- await createKycTestkudosEnvironmentFull(t, { adjustExchangeConfig });
-
- // Withdraw digital cash into the wallet.
-
- const wres = await withdrawViaBankV3(t, {
- amount: "TESTKUDOS:20",
- bankClient,
- exchange,
- walletClient,
- });
-
- await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: wres.transactionId as TransactionIdStr,
- txState: {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.KycRequired,
- },
- });
-
- const txDetails = await walletClient.call(
- WalletApiOperation.GetTransactionById,
- {
- transactionId: wres.transactionId,
- },
- );
-
- console.log(j2s(txDetails));
- const accessToken = txDetails.kycAccessToken;
- t.assertTrue(!!accessToken);
-
- const infoResp = await harnessHttpLib.fetch(
- new URL(`kyc-info/${txDetails.kycAccessToken}`, exchange.baseUrl).href,
- );
-
- const clientInfo = await readResponseJsonOrThrow(
- infoResp,
- codecForKycProcessClientInformation(),
- );
-
- console.log(j2s(clientInfo));
-
- const kycId = clientInfo.requirements.find((x) => x.id != null)?.id;
- t.assertTrue(!!kycId);
-
- const uploadResp = await harnessHttpLib.fetch(
- new URL(`kyc-upload/${kycId}`, exchange.baseUrl).href,
- {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: {
- FULL_NAME: "Alice Abc",
- DATE_OF_BIRTH: "2000-01-01",
- FORM_ID: "full_name_and_birthdate",
- },
- },
- );
-
- console.log("resp status", uploadResp.status);
-
- t.assertDeepEqual(uploadResp.status, 204);
-
- {
- // Do a GET on kyc-info here as this reproduces a bug in the
- // exchange.
- const infoResp = await harnessHttpLib.fetch(
- new URL(`kyc-info/${txDetails.kycAccessToken}`, exchange.baseUrl).href,
- );
-
- await readResponseJsonOrThrow(
- infoResp,
- codecForKycProcessClientInformation(),
- );
- }
-
- const sig = signAmlQuery(decodeCrock(amlKeypair.priv));
-
- const decisionsResp = await harnessHttpLib.fetch(
- new URL(`aml/${amlKeypair.pub}/decisions`, exchange.baseUrl).href,
- {
- headers: {
- "Taler-AML-Officer-Signature": encodeCrock(sig),
- },
- },
- );
-
- const decisions = await readResponseJsonOrThrow(decisionsResp, codecForAny());
- console.log(j2s(decisions));
-
- t.assertDeepEqual(decisionsResp.status, 200);
-
- // KYC should pass now
-
- await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: wres.transactionId as TransactionIdStr,
- txState: {
- major: TransactionMajorState.Done,
- },
- });
-}
-
-runKycFormWithdrawalTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit-form.ts b/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit-form.ts
@@ -33,71 +33,18 @@ import {
readResponseJsonOrThrow,
readSuccessResponseJsonOrThrow,
} from "@gnu-taler/taler-util/http";
-import {
- configureCommonKyc,
- createKycTestkudosEnvironmentFull,
-} from "../harness/environments.js";
+import { createKycTestkudosEnvironmentFull } from "../harness/environments.js";
import {
delayMs,
GlobalTestState,
harnessHttpLib,
} from "../harness/harness.js";
+import { configureMerchantDepositKyc } from "./merchant-kyc-auth-helper.js";
const logger = new Logger(`test-kyc-merchant-deposit.ts`);
-const myAmlConfig = `
-# Fallback measure on errors.
-[kyc-measure-freeze-investigate]
-CHECK_NAME = skip
-PROGRAM = freeze-investigate
-VOLUNTARY = NO
-CONTEXT = {}
-
-[aml-program-freeze-investigate]
-DESCRIPTION = "Fallback measure on errors that freezes the account and asks AML staff to investigate the system failure."
-COMMAND = taler-exchange-helper-measure-freeze
-ENABLED = YES
-FALLBACK = freeze-investigate
-
-[aml-program-inform-investigate]
-DESCRIPTION = "Measure that asks AML staff to investigate an account and informs the account owner about it."
-COMMAND = taler-exchange-helper-measure-inform-investigate
-ENABLED = YES
-FALLBACK = freeze-investigate
-
-# Successor produced by taler-exchange-helper-measure-inform-investigate.
-[kyc-measure-inform-investigate]
-CHECK_NAME = SKIP
-PROGRAM = NONE
-VOLUNTARY = NO
-CONTEXT = {}
-
-[kyc-check-form-gls-merchant-onboarding]
-TYPE = FORM
-FORM_NAME = gls-merchant-onboarding
-DESCRIPTION = "GLS Merchant Onboarding"
-DESCRIPTION_I18N = {}
-OUTPUTS =
-FALLBACK = freeze-investigate
-
-[kyc-measure-merchant-onboarding]
-CHECK_NAME = form-gls-merchant-onboarding
-PROGRAM = inform-investigate
-CONTEXT = {}
-VOLUNTARY = NO
-
-[kyc-rule-deposit-limit-zero]
-OPERATION_TYPE = DEPOSIT
-NEXT_MEASURES = merchant-onboarding
-EXPOSED = YES
-ENABLED = YES
-THRESHOLD = TESTKUDOS:0
-TIMEFRAME = "1 days"
-`;
-
function adjustExchangeConfig(config: Configuration) {
- configureCommonKyc(config);
- config.loadFromString(myAmlConfig);
+ configureMerchantDepositKyc(config, "TESTKUDOS:0", true);
}
export async function runKycMerchantDepositFormTest(t: GlobalTestState) {
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit-rewrite.ts b/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit-rewrite.ts
@@ -28,41 +28,16 @@ import {
succeedOrThrow,
} from "@gnu-taler/taler-util";
import {
- configureCommonKyc,
createKycTestkudosEnvironmentFull,
postAmlDecisionNoRules,
} from "../harness/environments.js";
import { delayMs, GlobalTestState } from "../harness/harness.js";
+import { configureMerchantInfoDepositKyc } from "./merchant-kyc-auth-helper.js";
const logger = new Logger(`test-kyc-merchant-deposit-rewrite.ts`);
function adjustExchangeConfig(config: Configuration) {
- configureCommonKyc(config);
-
- config.setString("KYC-RULE-R1", "operation_type", "deposit");
- config.setString("KYC-RULE-R1", "enabled", "yes");
- config.setString("KYC-RULE-R1", "exposed", "yes");
- config.setString("KYC-RULE-R1", "is_and_combinator", "yes");
- config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:0");
- config.setString("KYC-RULE-R1", "timeframe", "1d");
- config.setString("KYC-RULE-R1", "next_measures", "M1");
-
- config.setString("KYC-MEASURE-M1", "check_name", "C1");
- config.setString("KYC-MEASURE-M1", "context", "{}");
- config.setString("KYC-MEASURE-M1", "program", "P1");
-
- config.setString("KYC-MEASURE-FM", "check_name", "SKIP");
- config.setString("KYC-MEASURE-FM", "context", "{}");
- config.setString("KYC-MEASURE-FM", "program", "P1");
-
- config.setString("AML-PROGRAM-P1", "command", "/bin/true");
- config.setString("AML-PROGRAM-P1", "enabled", "true");
- config.setString("AML-PROGRAM-P1", "description", "this does nothing");
- config.setString("AML-PROGRAM-P1", "fallback", "FREEZE");
-
- config.setString("KYC-CHECK-C1", "type", "INFO");
- config.setString("KYC-CHECK-C1", "description", "my check!");
- config.setString("KYC-CHECK-C1", "fallback", "FREEZE");
+ configureMerchantInfoDepositKyc(config);
}
function adjustMerchantConfig(config: Configuration) {
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit.ts b/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit.ts
@@ -36,7 +36,6 @@ import {
readSuccessResponseJsonOrThrow,
} from "@gnu-taler/taler-util/http";
import {
- configureCommonKyc,
createKycTestkudosEnvironmentFull,
postAmlDecisionNoRules,
} from "../harness/environments.js";
@@ -45,36 +44,12 @@ import {
GlobalTestState,
harnessHttpLib,
} from "../harness/harness.js";
+import { configureMerchantInfoDepositKyc } from "./merchant-kyc-auth-helper.js";
const logger = new Logger(`test-kyc-merchant-deposit.ts`);
function adjustExchangeConfig(config: Configuration) {
- configureCommonKyc(config);
-
- config.setString("KYC-RULE-R1", "operation_type", "deposit");
- config.setString("KYC-RULE-R1", "enabled", "yes");
- config.setString("KYC-RULE-R1", "exposed", "yes");
- config.setString("KYC-RULE-R1", "is_and_combinator", "yes");
- config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:0");
- config.setString("KYC-RULE-R1", "timeframe", "1d");
- config.setString("KYC-RULE-R1", "next_measures", "M1");
-
- config.setString("KYC-MEASURE-M1", "check_name", "C1");
- config.setString("KYC-MEASURE-M1", "context", "{}");
- config.setString("KYC-MEASURE-M1", "program", "P1");
-
- config.setString("KYC-MEASURE-FM", "check_name", "SKIP");
- config.setString("KYC-MEASURE-FM", "context", "{}");
- config.setString("KYC-MEASURE-FM", "program", "P1");
-
- config.setString("AML-PROGRAM-P1", "command", "/bin/true");
- config.setString("AML-PROGRAM-P1", "enabled", "true");
- config.setString("AML-PROGRAM-P1", "description", "this does nothing");
- config.setString("AML-PROGRAM-P1", "fallback", "FREEZE");
-
- config.setString("KYC-CHECK-C1", "type", "INFO");
- config.setString("KYC-CHECK-C1", "description", "my check!");
- config.setString("KYC-CHECK-C1", "fallback", "FREEZE");
+ configureMerchantInfoDepositKyc(config);
}
export async function runKycMerchantDepositTest(t: GlobalTestState) {
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-new-measures-prog.ts b/packages/taler-harness/src/integrationtests/test-kyc-new-measures-prog.ts
@@ -1,61 +1,24 @@
/*
This file is part of GNU Taler
- (C) 2024 Taler Systems S.A.
+ (C) 2024, 2026 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
+*/
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Imports.
- */
-import {
- codecForAny,
- codecForKycProcessClientInformation,
- codecOptional,
- Configuration,
- decodeCrock,
- encodeCrock,
- j2s,
- signAmlQuery,
- TalerKycAml,
- TalerProtocolTimestamp,
- TransactionIdStr,
- TransactionMajorState,
- TransactionMinorState,
-} from "@gnu-taler/taler-util";
-import { readResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import {
- configureCommonKyc,
- createKycTestkudosEnvironmentFull,
- postAmlDecision,
- withdrawViaBankV3,
-} from "../harness/environments.js";
-import { GlobalTestState, harnessHttpLib, waitMs } from "../harness/harness.js";
+import { TalerKycAml, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
+import { GlobalTestState, waitMs } from "../harness/harness.js";
+import { runDynamicMeasureScenario } from "./kyc-dynamic-measure-helper.js";
export const AML_PROGRAM_TEST_KYC_NEW_MEASURES_PROG: TalerKycAml.AmlProgramDefinition =
{
name: "test-kyc-new-measures-prog",
- logic: async (_input, config) => {
- // Artificially delay the AML program.
+ logic: async (input, config) => {
await waitMs(500);
- const outcome: TalerKycAml.AmlOutcome = {
+ return {
to_investigate: false,
- // pushing to info into properties for testing purposes
- properties: {
- "this comes": "from the program",
- input: _input as any,
- config,
- },
+ properties: { "this comes": "from the program", input, config },
events: [],
new_measures: "ask_more_info ask_basic_info",
new_rules: {
@@ -63,267 +26,46 @@ export const AML_PROGRAM_TEST_KYC_NEW_MEASURES_PROG: TalerKycAml.AmlProgramDefin
rules: [],
custom_measures: {
ask_basic_info: {
- context: {
- // this is the context info that the KYC-SPA will see
- infotype: "basic",
- },
+ context: { infotype: "basic" },
check_name: "C2",
prog_name: "P2",
},
ask_more_info: {
- context: {
- // this is the context info that the KYC-SPA will see
- WAT: "REALLY?",
- },
+ context: { WAT: "REALLY?" },
check_name: "C2",
prog_name: "P2",
},
},
},
};
- return outcome;
},
requiredAttributes: [],
requiredInputs: [],
requiredContext: [],
};
-function adjustExchangeConfig(config: Configuration) {
- configureCommonKyc(config);
-
- config.setString("KYC-RULE-R1", "operation_type", "withdraw");
- config.setString("KYC-RULE-R1", "enabled", "yes");
- config.setString("KYC-RULE-R1", "exposed", "yes");
- config.setString("KYC-RULE-R1", "is_and_combinator", "no");
- config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:5");
- config.setString("KYC-RULE-R1", "timeframe", "1d");
- config.setString("KYC-RULE-R1", "next_measures", "M2");
-
- config.setString("KYC-MEASURE-M1", "check_name", "C1");
- config.setString("KYC-MEASURE-M1", "context", "{}");
- config.setString("KYC-MEASURE-M1", "program", "P1");
-
- config.setString("KYC-MEASURE-M2", "check_name", "C2");
- config.setString("KYC-MEASURE-M2", "context", "{}");
- config.setString("KYC-MEASURE-M2", "program", "P2");
-
- config.setString("KYC-MEASURE-M3", "check_name", "SKIP");
- config.setString("KYC-MEASURE-M3", "context", "{}");
- config.setString("KYC-MEASURE-M3", "program", "P1");
-
- config.setString(
- "AML-PROGRAM-P1",
- "command",
- `taler-harness aml-program run-program --name ${AML_PROGRAM_TEST_KYC_NEW_MEASURES_PROG.name}`,
- );
- config.setString("AML-PROGRAM-P1", "enabled", "true");
- config.setString("AML-PROGRAM-P1", "description", "remove all rules");
- config.setString("AML-PROGRAM-P1", "description_i18n", "{}");
- config.setString("AML-PROGRAM-P1", "fallback", "FREEZE");
-
- config.setString("AML-PROGRAM-P2", "command", "/bin/true");
- config.setString("AML-PROGRAM-P2", "enabled", "true");
- config.setString("AML-PROGRAM-P2", "description", "does nothing");
- config.setString("AML-PROGRAM-P2", "description_i18n", "{}");
- config.setString("AML-PROGRAM-P2", "fallback", "FREEZE");
-
- config.setString("KYC-CHECK-C1", "type", "FORM");
- config.setString("KYC-CHECK-C1", "form_name", "myform");
- config.setString("KYC-CHECK-C1", "description", "my check!");
- config.setString("KYC-CHECK-C1", "description_i18n", "{}");
- config.setString("KYC-CHECK-C1", "outputs", "full_name birthdate");
- config.setString("KYC-CHECK-C1", "fallback", "FREEZE");
-
- config.setString("KYC-CHECK-C2", "type", "FORM");
- config.setString("KYC-CHECK-C2", "form_name", "dynamicform");
- config.setString("KYC-CHECK-C2", "description", "my check info!");
- config.setString("KYC-CHECK-C2", "description_i18n", "{}");
- config.setString("KYC-CHECK-C1", "outputs", "what_the_officer_asked");
- config.setString("KYC-CHECK-C2", "fallback", "FREEZE");
-
- config.setString("KYC-CHECK-C3", "type", "INFO");
- config.setString("KYC-CHECK-C3", "description", "this is info c3");
- config.setString("KYC-CHECK-C3", "description_i18n", "{}");
- config.setString("KYC-CHECK-C3", "fallback", "FREEZE");
-}
-
-/**
- * Test the usage of new_measures as the return
- * value of an AML measure program.
- */
+/** Verify that all measures returned by an AML program become requirements. */
export async function runKycNewMeasuresProgTest(t: GlobalTestState) {
- // Set up test environment
-
- const { walletClient, bankClient, exchange, amlKeypair } =
- await createKycTestkudosEnvironmentFull(t, { adjustExchangeConfig });
-
- // Withdraw digital cash into the wallet.
- let kycPaytoHash: string | undefined;
- let accessToken: string | undefined;
- let firstTransaction: string | undefined;
-
- {
- // step 1) Withdraw to trigger AML
- const wres = await withdrawViaBankV3(t, {
- amount: "TESTKUDOS:20",
- bankClient,
- exchange,
- walletClient,
- });
-
- await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: wres.transactionId as TransactionIdStr,
- txState: {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.KycRequired,
- },
- });
-
- const txDetails = await walletClient.call(
- WalletApiOperation.GetTransactionById,
- {
- transactionId: wres.transactionId,
- },
- );
-
- accessToken = txDetails.kycAccessToken;
- kycPaytoHash = txDetails.kycPaytoHash;
- firstTransaction = wres.transactionId;
- }
-
- t.assertTrue(!!accessToken);
-
- {
- // step 2) Check KYC info
- const infoResp = await harnessHttpLib.fetch(
- new URL(`kyc-info/${accessToken}`, exchange.baseUrl).href,
- );
-
- const clientInfo = await readResponseJsonOrThrow(
- infoResp,
- codecOptional(codecForKycProcessClientInformation()),
- );
-
- console.log(j2s(clientInfo));
- t.assertDeepEqual(infoResp.status, 200);
- }
-
- const sig = signAmlQuery(decodeCrock(amlKeypair.priv));
- {
- // step 3) Apply Measure 3 with SKIP check
- const decisionsResp = await harnessHttpLib.fetch(
- new URL(`aml/${amlKeypair.pub}/decisions`, exchange.baseUrl).href,
- {
- headers: {
- "Taler-AML-Officer-Signature": encodeCrock(sig),
- },
- },
- );
-
- console.log(decisionsResp.status);
- t.assertDeepEqual(decisionsResp.status, 204);
-
- t.assertTrue(!!kycPaytoHash);
-
- await postAmlDecision(t, {
- amlPriv: amlKeypair.priv,
- amlPub: amlKeypair.pub,
- exchangeBaseUrl: exchange.baseUrl,
- paytoHash: kycPaytoHash,
- // Immediately run M3
- newMeasures: "M3",
- properties: {
- form: { name: "string" },
- },
- newRules: {
- expiration_time: TalerProtocolTimestamp.now(),
- custom_measures: {},
- rules: [
- // No rules!
- ],
- },
- });
- }
-
- {
- // step 4) Check KYC info, it should have the result
- // of running program p1
- const decisionsResp = await harnessHttpLib.fetch(
- new URL(`aml/${amlKeypair.pub}/decisions`, exchange.baseUrl).href,
- {
- headers: {
- "Taler-AML-Officer-Signature": encodeCrock(sig),
- },
- },
- );
-
- const decisions = await readResponseJsonOrThrow(
- decisionsResp,
- codecForAny(),
- );
- console.log(j2s(decisions));
-
- t.assertDeepEqual(decisionsResp.status, 200);
- }
-
- // Make sure that there can be another decision
- await waitMs(2000);
-
- // Wait for the KYC program to run
- const deadline = Date.now() + 30_000;
- while (true) {
- t.assertTrue(Date.now() < deadline, "timed out waiting for new measures");
- const infoResp = await harnessHttpLib.fetch(
- new URL(`kyc-info/${accessToken}`, exchange.baseUrl).href,
- );
-
- console.log(`kyc-info status: ${infoResp.status}`);
- if (infoResp.status == 202) {
- await waitMs(1000);
- continue;
- }
- // KYC program still busy.
- // In the future, this should long-poll.
- if (infoResp.status == 204) {
- await waitMs(1000);
- continue;
- }
-
- const respJson = await infoResp.json();
- console.log(j2s(respJson));
-
- t.assertDeepEqual(infoResp.status, 200);
-
- const clientInfo = await readResponseJsonOrThrow(
- infoResp,
- codecOptional(codecForKycProcessClientInformation()),
- );
-
- if (clientInfo?.requirements.length == 0) {
- console.log("requirements empty, waiting ...");
- await waitMs(1000);
- continue;
- }
-
- console.log(j2s(clientInfo));
-
- // Both custom measures returned by the AML program must survive. A test
- // of only the first requirement would miss one being silently dropped.
- t.assertDeepEqual(clientInfo?.requirements.length, 2);
- for (const requirement of clientInfo?.requirements ?? []) {
- t.assertDeepEqual(requirement.form, "dynamicform");
- t.assertTrue(!!requirement.id);
- }
- const contexts = (clientInfo?.requirements ?? [])
- .map((requirement) => JSON.stringify(requirement.context))
- .sort();
- t.assertDeepEqual(
- contexts,
- [JSON.stringify({ WAT: "REALLY?" }), JSON.stringify({ infotype: "basic" })].sort(),
- );
-
- break;
- }
+ await runDynamicMeasureScenario(t, {
+ amlProgramName: AML_PROGRAM_TEST_KYC_NEW_MEASURES_PROG.name,
+ assertRequirements(info) {
+ t.assertDeepEqual(info.requirements.length, 2);
+ for (const requirement of info.requirements) {
+ t.assertDeepEqual(requirement.form, "dynamicform");
+ t.assertTrue(!!requirement.id);
+ }
+ const contexts = info.requirements
+ .map((requirement) => JSON.stringify(requirement.context))
+ .sort();
+ t.assertDeepEqual(
+ contexts,
+ [
+ JSON.stringify({ WAT: "REALLY?" }),
+ JSON.stringify({ infotype: "basic" }),
+ ].sort(),
+ );
+ },
+ });
}
runKycNewMeasuresProgTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-skip-expiration.ts b/packages/taler-harness/src/integrationtests/test-kyc-skip-expiration.ts
@@ -1,61 +1,24 @@
/*
This file is part of GNU Taler
- (C) 2024 Taler Systems S.A.
+ (C) 2024, 2026 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
+*/
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Imports.
- */
-import {
- codecForAny,
- codecForKycProcessClientInformation,
- codecOptional,
- Configuration,
- decodeCrock,
- encodeCrock,
- j2s,
- signAmlQuery,
- TalerKycAml,
- TalerProtocolTimestamp,
- TransactionIdStr,
- TransactionMajorState,
- TransactionMinorState,
-} from "@gnu-taler/taler-util";
-import { readResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import {
- configureCommonKyc,
- createKycTestkudosEnvironmentFull,
- postAmlDecision,
- withdrawViaBankV3,
-} from "../harness/environments.js";
-import { GlobalTestState, harnessHttpLib, waitMs } from "../harness/harness.js";
+import { TalerKycAml, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
+import { GlobalTestState, waitMs } from "../harness/harness.js";
+import { runDynamicMeasureScenario } from "./kyc-dynamic-measure-helper.js";
export const AML_PROGRAM_FROM_ATTRIBUTES_TO_CONTEXT: TalerKycAml.AmlProgramDefinition =
{
name: "from-attr-to-context",
- logic: async (_input, config) => {
- // Artificially delay the AML program.
+ logic: async (input, config) => {
await waitMs(500);
- const outcome: TalerKycAml.AmlOutcome = {
+ return {
to_investigate: false,
- // pushing to info into properties for testing purposes
- properties: {
- "this comes": "from the program",
- input: _input as any,
- config,
- },
+ properties: { "this comes": "from the program", input, config },
events: [],
new_rules: {
expiration_time: TalerProtocolTimestamp.zero(),
@@ -63,237 +26,28 @@ export const AML_PROGRAM_FROM_ATTRIBUTES_TO_CONTEXT: TalerKycAml.AmlProgramDefin
successor_measure: "ask_more_info",
custom_measures: {
ask_more_info: {
- context: {
- // this is the context info that the KYC-SPA will see
- WAT: "REALLY?",
- },
+ context: { WAT: "REALLY?" },
check_name: "C2",
prog_name: "P2",
},
},
},
};
- return outcome;
},
requiredAttributes: [],
requiredInputs: [],
requiredContext: [],
};
-function adjustExchangeConfig(config: Configuration) {
- configureCommonKyc(config);
-
- config.setString("KYC-RULE-R1", "operation_type", "withdraw");
- config.setString("KYC-RULE-R1", "enabled", "yes");
- config.setString("KYC-RULE-R1", "exposed", "yes");
- config.setString("KYC-RULE-R1", "is_and_combinator", "no");
- config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:5");
- config.setString("KYC-RULE-R1", "timeframe", "1d");
- config.setString("KYC-RULE-R1", "next_measures", "M2");
-
- config.setString("KYC-MEASURE-M1", "check_name", "C1");
- config.setString("KYC-MEASURE-M1", "context", "{}");
- config.setString("KYC-MEASURE-M1", "program", "P1");
-
- config.setString("KYC-MEASURE-M2", "check_name", "C2");
- config.setString("KYC-MEASURE-M2", "context", "{}");
- config.setString("KYC-MEASURE-M2", "program", "P2");
-
- config.setString("KYC-MEASURE-M3", "check_name", "SKIP");
- config.setString("KYC-MEASURE-M3", "context", "{}");
- config.setString("KYC-MEASURE-M3", "program", "P1");
-
- config.setString(
- "AML-PROGRAM-P1",
- "command",
- "taler-harness aml-program run-program --name from-attr-to-context",
- );
- config.setString("AML-PROGRAM-P1", "enabled", "true");
- config.setString("AML-PROGRAM-P1", "description", "remove all rules");
- config.setString("AML-PROGRAM-P1", "description_i18n", "{}");
- config.setString("AML-PROGRAM-P1", "fallback", "FREEZE");
-
- config.setString("AML-PROGRAM-P2", "command", "/bin/true");
- config.setString("AML-PROGRAM-P2", "enabled", "true");
- config.setString("AML-PROGRAM-P2", "description", "does nothing");
- config.setString("AML-PROGRAM-P2", "description_i18n", "{}");
- config.setString("AML-PROGRAM-P2", "fallback", "FREEZE");
-
- config.setString("KYC-CHECK-C1", "type", "FORM");
- config.setString("KYC-CHECK-C1", "form_name", "myform");
- config.setString("KYC-CHECK-C1", "description", "my check!");
- config.setString("KYC-CHECK-C1", "description_i18n", "{}");
- config.setString("KYC-CHECK-C1", "outputs", "full_name birthdate");
- config.setString("KYC-CHECK-C1", "fallback", "FREEZE");
-
- config.setString("KYC-CHECK-C2", "type", "FORM");
- config.setString("KYC-CHECK-C2", "form_name", "dynamicform");
- config.setString("KYC-CHECK-C2", "description", "my check info!");
- config.setString("KYC-CHECK-C2", "description_i18n", "{}");
- config.setString("KYC-CHECK-C1", "outputs", "what_the_officer_asked");
- config.setString("KYC-CHECK-C2", "fallback", "FREEZE");
-
- config.setString("KYC-CHECK-C3", "type", "INFO");
- config.setString("KYC-CHECK-C3", "description", "this is info c3");
- config.setString("KYC-CHECK-C3", "description_i18n", "{}");
- config.setString("KYC-CHECK-C3", "fallback", "FREEZE");
-}
-
+/** Verify that an expired rule can produce a successor measure via SKIP. */
export async function runKycSkipExpirationTest(t: GlobalTestState) {
- // Set up test environment
-
- const { walletClient, bankClient, exchange, amlKeypair } =
- await createKycTestkudosEnvironmentFull(t, { adjustExchangeConfig });
-
- // Withdraw digital cash into the wallet.
- let kycPaytoHash: string | undefined;
- let accessToken: string | undefined;
- let firstTransaction: string | undefined;
-
- {
- // step 1) Withdraw to trigger AML
- const wres = await withdrawViaBankV3(t, {
- amount: "TESTKUDOS:20",
- bankClient,
- exchange,
- walletClient,
- });
-
- await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: wres.transactionId as TransactionIdStr,
- txState: {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.KycRequired,
- },
- });
-
- const txDetails = await walletClient.call(
- WalletApiOperation.GetTransactionById,
- {
- transactionId: wres.transactionId,
- },
- );
-
- accessToken = txDetails.kycAccessToken;
- kycPaytoHash = txDetails.kycPaytoHash;
- firstTransaction = wres.transactionId;
- }
-
- t.assertTrue(!!accessToken);
-
- {
- // step 2) Check KYC info
- const infoResp = await harnessHttpLib.fetch(
- new URL(`kyc-info/${accessToken}`, exchange.baseUrl).href,
- );
-
- const clientInfo = await readResponseJsonOrThrow(
- infoResp,
- codecOptional(codecForKycProcessClientInformation()),
- );
-
- console.log(j2s(clientInfo));
- t.assertDeepEqual(infoResp.status, 200);
- }
-
- const sig = signAmlQuery(decodeCrock(amlKeypair.priv));
- {
- // step 3) Apply Measure 3 with SKIP check
- const decisionsResp = await harnessHttpLib.fetch(
- new URL(`aml/${amlKeypair.pub}/decisions`, exchange.baseUrl).href,
- {
- headers: {
- "Taler-AML-Officer-Signature": encodeCrock(sig),
- },
- },
- );
-
- console.log(decisionsResp.status);
- t.assertDeepEqual(decisionsResp.status, 204);
-
- t.assertTrue(!!kycPaytoHash);
-
- await postAmlDecision(t, {
- amlPriv: amlKeypair.priv,
- amlPub: amlKeypair.pub,
- exchangeBaseUrl: exchange.baseUrl,
- paytoHash: kycPaytoHash,
- newMeasures: "M3",
- properties: {
- form: { name: "string" },
- },
- newRules: {
- expiration_time: TalerProtocolTimestamp.now(),
- custom_measures: {},
- rules: [
- // No rules!
- ],
- },
- });
- }
-
- {
- // step 4) Check KYC info, it should have the result
- // of running program p1
- const decisionsResp = await harnessHttpLib.fetch(
- new URL(`aml/${amlKeypair.pub}/decisions`, exchange.baseUrl).href,
- {
- headers: {
- "Taler-AML-Officer-Signature": encodeCrock(sig),
- },
- },
- );
-
- const decisions = await readResponseJsonOrThrow(
- decisionsResp,
- codecForAny(),
- );
- console.log(j2s(decisions));
-
- t.assertDeepEqual(decisionsResp.status, 200);
- }
-
- // Make sure that there can be another decision
- await waitMs(2000);
-
- // Wait for the KYC program to run
- while (true) {
- const infoResp = await harnessHttpLib.fetch(
- new URL(`kyc-info/${accessToken}`, exchange.baseUrl).href,
- );
-
- console.log(`kyc-info status: ${infoResp.status}`);
- if (infoResp.status == 202) {
- await waitMs(1000);
- continue;
- }
- // KYC program still busy.
- // In the future, this should long-poll.
- if (infoResp.status == 204) {
- await waitMs(1000);
- continue;
- }
-
- const respJson = await infoResp.json();
- console.log(j2s(respJson));
-
- t.assertDeepEqual(infoResp.status, 200);
-
- const clientInfo = await readResponseJsonOrThrow(
- infoResp,
- codecOptional(codecForKycProcessClientInformation()),
- );
-
- console.log(j2s(clientInfo));
-
- // Finally here we must see the officer defined form
- t.assertDeepEqual(clientInfo?.requirements[0].context, {
- // this is fixed by the aml program
- WAT: "REALLY?",
- });
-
- break;
- }
+ await runDynamicMeasureScenario(t, {
+ amlProgramName: AML_PROGRAM_FROM_ATTRIBUTES_TO_CONTEXT.name,
+ assertRequirements(info) {
+ t.assertDeepEqual(info.requirements.length, 1);
+ t.assertDeepEqual(info.requirements[0].context, { WAT: "REALLY?" });
+ },
+ });
}
runKycSkipExpirationTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-threshold-withdrawal.ts b/packages/taler-harness/src/integrationtests/test-kyc-threshold-withdrawal.ts
@@ -1,178 +0,0 @@
-/*
- 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/>
- */
-
-/**
- * Imports.
- */
-import {
- Configuration,
- NotificationType,
- TransactionMajorState,
- TransactionMinorState,
- TransactionType,
-} from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import {
- configureCommonKyc,
- createKycTestkudosEnvironmentFull,
- postAmlDecisionNoRules,
-} from "../harness/environments.js";
-import { GlobalTestState } from "../harness/harness.js";
-
-function adjustExchangeConfig(config: Configuration): void {
- configureCommonKyc(config);
-
- config.setString("KYC-RULE-R1", "operation_type", "withdraw");
- config.setString("KYC-RULE-R1", "enabled", "yes");
- config.setString("KYC-RULE-R1", "exposed", "yes");
- config.setString("KYC-RULE-R1", "is_and_combinator", "yes");
- config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:5");
- config.setString("KYC-RULE-R1", "timeframe", "1d");
- config.setString("KYC-RULE-R1", "next_measures", "M1");
-
- config.setString("KYC-RULE-R2", "operation_type", "withdraw");
- config.setString("KYC-RULE-R2", "enabled", "yes");
- config.setString("KYC-RULE-R2", "exposed", "yes");
- config.setString("KYC-RULE-R2", "is_and_combinator", "yes");
- config.setString("KYC-RULE-R2", "threshold", "TESTKUDOS:300");
- config.setString("KYC-RULE-R2", "timeframe", "1d");
- config.setString("KYC-RULE-R2", "next_measures", "verboten");
-
- config.setString("KYC-MEASURE-M1", "check_name", "C1");
- config.setString("KYC-MEASURE-M1", "context", "{}");
- config.setString("KYC-MEASURE-M1", "program", "NONE");
-
- config.setString("KYC-CHECK-C1", "type", "INFO");
- config.setString("KYC-CHECK-C1", "description", "my check!");
- config.setString("KYC-CHECK-C1", "fallback", "FREEZE");
-}
-
-export async function runKycThresholdWithdrawalTest(t: GlobalTestState) {
- // Set up test environment
-
- const { walletClient, bankClient, exchange, amlKeypair } =
- await createKycTestkudosEnvironmentFull(t, {
- adjustExchangeConfig,
- });
-
- // Withdraw digital cash into the wallet.
-
- const amount = "TESTKUDOS:20";
- const user = await bankClient.createRandomBankUser();
- bankClient.setAuth({
- username: user.username,
- password: user.password,
- });
-
- const wop = await bankClient.createWithdrawalOperation(user.username, amount);
-
- // Hand it to the wallet
-
- const withdrawalUrlInfo = await walletClient.client.call(
- WalletApiOperation.GetWithdrawalDetailsForUri,
- {
- talerWithdrawUri: wop.taler_withdraw_uri,
- },
- );
-
- const withdrawalAmountInfo = await walletClient.call(
- WalletApiOperation.GetWithdrawalDetailsForAmount,
- {
- amount: withdrawalUrlInfo.amount!,
- exchangeBaseUrl: withdrawalUrlInfo.possibleExchanges[0].exchangeBaseUrl,
- },
- );
-
- t.assertTrue(!!withdrawalAmountInfo.kycHardLimit);
- t.assertAmountEquals(withdrawalAmountInfo.kycHardLimit, "TESTKUDOS:300");
-
- // Withdraw
-
- const acceptResp = await walletClient.client.call(
- WalletApiOperation.AcceptBankIntegratedWithdrawal,
- {
- exchangeBaseUrl: exchange.baseUrl,
- talerWithdrawUri: wop.taler_withdraw_uri,
- },
- );
-
- const withdrawalTxId = acceptResp.transactionId;
-
- await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: acceptResp.transactionId,
- txState: {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.BankConfirmTransfer,
- },
- });
-
- // Confirm it
-
- await bankClient.confirmWithdrawalOperation(user.username, {
- withdrawalOperationId: wop.withdrawal_id,
- });
-
- t.logStep("waiting for pending(kyc-required)");
-
- const kycNotificationCond = walletClient.waitForNotificationCond((x) => {
- if (
- x.type === NotificationType.TransactionStateTransition &&
- x.transactionId === withdrawalTxId &&
- x.newTxState.major === TransactionMajorState.Pending &&
- x.newTxState.minor === TransactionMinorState.KycRequired
- ) {
- return x;
- }
- return false;
- });
-
- await kycNotificationCond;
-
- const txDet = await walletClient.call(WalletApiOperation.GetTransactionById, {
- transactionId: withdrawalTxId,
- });
-
- t.assertDeepEqual(txDet.type, TransactionType.Withdrawal);
-
- const kycPaytoHash = txDet.kycPaytoHash;
- t.assertTrue(!!kycPaytoHash);
-
- t.logStep("posting aml decision");
-
- await postAmlDecisionNoRules(t, {
- amlPriv: amlKeypair.priv,
- amlPub: amlKeypair.pub,
- exchangeBaseUrl: exchange.baseUrl,
- paytoHash: kycPaytoHash,
- });
-
- t.logStep("waiting for withdrawal to be done");
-
- const doneNotificationCond = walletClient.waitForNotificationCond((x) => {
- if (
- x.type === NotificationType.TransactionStateTransition &&
- x.transactionId === withdrawalTxId &&
- x.newTxState.major === TransactionMajorState.Done
- ) {
- return x;
- }
- return false;
- });
-
- await doneNotificationCond;
-}
-
-runKycThresholdWithdrawalTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-withdrawal-verboten.ts b/packages/taler-harness/src/integrationtests/test-kyc-withdrawal-verboten.ts
@@ -24,156 +24,15 @@ import {
TalerProtocolTimestamp,
TransactionMajorState,
TransactionMinorState,
- TransactionType,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import {
- configureCommonKyc,
- createKycTestkudosEnvironmentFull,
- postAmlDecision,
- postAmlDecisionNoRules,
-} from "../harness/environments.js";
+import { postAmlDecision } from "../harness/environments.js";
import { GlobalTestState } from "../harness/harness.js";
+import { runKycThresholdWithdrawalScenario } from "./kyc-threshold-withdrawal-helper.js";
export async function runKycWithdrawalVerbotenTest(t: GlobalTestState) {
- // Set up test environment
-
- const { walletClient, bankClient, exchange, amlKeypair } =
- await createKycTestkudosEnvironmentFull(t, {
- adjustExchangeConfig(config) {
- configureCommonKyc(config);
-
- config.setString("KYC-RULE-R1", "operation_type", "withdraw");
- config.setString("KYC-RULE-R1", "enabled", "yes");
- config.setString("KYC-RULE-R1", "exposed", "yes");
- config.setString("KYC-RULE-R1", "is_and_combinator", "yes");
- config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:5");
- config.setString("KYC-RULE-R1", "timeframe", "1d");
- config.setString("KYC-RULE-R1", "next_measures", "M1");
-
- config.setString("KYC-RULE-R2", "operation_type", "withdraw");
- config.setString("KYC-RULE-R2", "enabled", "yes");
- config.setString("KYC-RULE-R2", "exposed", "yes");
- config.setString("KYC-RULE-R2", "is_and_combinator", "yes");
- config.setString("KYC-RULE-R2", "threshold", "TESTKUDOS:300");
- config.setString("KYC-RULE-R2", "timeframe", "1d");
- config.setString("KYC-RULE-R2", "next_measures", "verboten");
-
- config.setString("KYC-MEASURE-M1", "check_name", "C1");
- config.setString("KYC-MEASURE-M1", "context", "{}");
- config.setString("KYC-MEASURE-M1", "program", "NONE");
-
- config.setString("KYC-CHECK-C1", "type", "INFO");
- config.setString("KYC-CHECK-C1", "description", "my check!");
- config.setString("KYC-CHECK-C1", "fallback", "FREEZE");
- },
- });
-
- // Withdraw digital cash into the wallet.
-
- const amount = "TESTKUDOS:20";
- const user = await bankClient.createRandomBankUser();
- bankClient.setAuth({
- username: user.username,
- password: user.password,
- });
-
- const wop = await bankClient.createWithdrawalOperation(user.username, amount);
-
- // Hand it to the wallet
-
- const withdrawalUrlInfo = await walletClient.client.call(
- WalletApiOperation.GetWithdrawalDetailsForUri,
- {
- talerWithdrawUri: wop.taler_withdraw_uri,
- },
- );
-
- const withdrawalAmountInfo = await walletClient.call(
- WalletApiOperation.GetWithdrawalDetailsForAmount,
- {
- amount: withdrawalUrlInfo.amount!,
- exchangeBaseUrl: withdrawalUrlInfo.possibleExchanges[0].exchangeBaseUrl,
- },
- );
-
- t.assertTrue(!!withdrawalAmountInfo.kycHardLimit);
- t.assertAmountEquals(withdrawalAmountInfo.kycHardLimit, "TESTKUDOS:300");
-
- // Withdraw
-
- const acceptResp = await walletClient.client.call(
- WalletApiOperation.AcceptBankIntegratedWithdrawal,
- {
- exchangeBaseUrl: exchange.baseUrl,
- talerWithdrawUri: wop.taler_withdraw_uri,
- },
- );
-
- const withdrawalTxId = acceptResp.transactionId;
-
- // Confirm it
-
- await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: acceptResp.transactionId,
- txState: {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.BankConfirmTransfer,
- },
- });
-
- await bankClient.confirmWithdrawalOperation(user.username, {
- withdrawalOperationId: wop.withdrawal_id,
- });
-
- t.logStep("waiting for pending(kyc-required)");
-
- const kycNotificationCond = walletClient.waitForNotificationCond((x) => {
- if (
- x.type === NotificationType.TransactionStateTransition &&
- x.transactionId === withdrawalTxId &&
- x.newTxState.major === TransactionMajorState.Pending &&
- x.newTxState.minor === TransactionMinorState.KycRequired
- ) {
- return x;
- }
- return false;
- });
-
- await kycNotificationCond;
-
- const txDet = await walletClient.call(WalletApiOperation.GetTransactionById, {
- transactionId: withdrawalTxId,
- });
-
- t.assertDeepEqual(txDet.type, TransactionType.Withdrawal);
-
- const kycPaytoHash = txDet.kycPaytoHash;
- t.assertTrue(!!kycPaytoHash);
-
- t.logStep("posting aml decision");
-
- await postAmlDecisionNoRules(t, {
- amlPriv: amlKeypair.priv,
- amlPub: amlKeypair.pub,
- exchangeBaseUrl: exchange.baseUrl,
- paytoHash: kycPaytoHash,
- });
-
- t.logStep("waiting for withdrawal to be done");
-
- const doneNotificationCond = walletClient.waitForNotificationCond((x) => {
- if (
- x.type === NotificationType.TransactionStateTransition &&
- x.transactionId === withdrawalTxId &&
- x.newTxState.major === TransactionMajorState.Done
- ) {
- return x;
- }
- return false;
- });
-
- await doneNotificationCond;
+ const { walletClient, bankClient, exchange, amlKeypair, user, kycPaytoHash } =
+ await runKycThresholdWithdrawalScenario(t);
// Now that the first withdrawal has succeeded, we freeze the account.
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-acctsel.ts b/packages/taler-harness/src/integrationtests/test-merchant-acctsel.ts
@@ -118,64 +118,21 @@ export async function runMerchantAcctselTest(t: GlobalTestState) {
merchant.makeInstanceBaseUrl(),
);
- {
- const ordResp1 = succeedOrThrow(
- await merchApi.createOrder(adminAccessToken, {
- payment_target: "iban",
- order: {
- amount: "TESTKUDOS:5",
- summary: "Test!",
- },
- }),
- );
-
- console.log(j2s(ordResp1));
-
- const ordDet1 = succeedOrThrow(
- await merchApi.getOrderDetails(adminAccessToken, ordResp1.order_id),
- );
-
- t.assertDeepEqual(ordDet1.order_status, "unpaid");
-
- const preparePayResult = await walletClient.call(
- WalletApiOperation.PreparePayForUriV2,
- {
- talerPayUri: ordDet1.taler_pay_uri,
- },
- );
-
- // The order is only claimed once the download task has run.
- await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: preparePayResult.transactionId,
- txState: {
- major: TransactionMajorState.Dialog,
- minor: TransactionMinorState.Proposed,
- },
- });
-
- const ordDet2 = succeedOrThrow(
- await merchApi.getOrderDetails(adminAccessToken, ordResp1.order_id),
- );
-
- console.log(j2s(ordDet2));
-
- t.assertDeepEqual(ordDet2.order_status, "claimed");
-
- const numExch1 = ordDet2.contract_terms.exchanges.filter(
- (x) => x.url === exchange1.baseUrl,
- ).length;
- const numExch2 = ordDet2.contract_terms.exchanges.filter(
- (x) => x.url === exchange2.baseUrl,
- ).length;
-
- t.assertDeepEqual(numExch1, 0);
- t.assertDeepEqual(numExch2, 1);
- }
-
- {
+ for (const scenario of [
+ {
+ paymentTarget: "iban",
+ expectedExchange1: 0,
+ expectedExchange2: 1,
+ },
+ {
+ paymentTarget: "x-taler-bank",
+ expectedExchange1: 1,
+ expectedExchange2: 0,
+ },
+ ]) {
const ordResp1 = succeedOrThrow(
await merchApi.createOrder(adminAccessToken, {
- payment_target: "x-taler-bank",
+ payment_target: scenario.paymentTarget,
order: {
amount: "TESTKUDOS:5",
summary: "Test!",
@@ -222,8 +179,8 @@ export async function runMerchantAcctselTest(t: GlobalTestState) {
(x) => x.url === exchange2.baseUrl,
).length;
- t.assertDeepEqual(numExch1, 1);
- t.assertDeepEqual(numExch2, 0);
+ t.assertDeepEqual(numExch1, scenario.expectedExchange1);
+ t.assertDeepEqual(numExch2, scenario.expectedExchange2);
}
}
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-kyc-auth-multi.ts b/packages/taler-harness/src/integrationtests/test-merchant-kyc-auth-multi.ts
@@ -25,17 +25,12 @@ import {
j2s,
Logger,
MerchantAccountKycStatus,
- Paytos,
- Result,
succeedOrThrow,
TalerMerchantInstanceHttpClient,
TalerProtocolDuration,
TalerWireGatewayHttpClient,
} from "@gnu-taler/taler-util";
-import {
- configureCommonKyc,
- createKycTestkudosEnvironmentFull,
-} from "../harness/environments.js";
+import { createKycTestkudosEnvironmentFull } from "../harness/environments.js";
import {
BankService,
ExchangeService,
@@ -43,53 +38,13 @@ import {
GlobalTestState,
MerchantService,
} from "../harness/harness.js";
-
-const myAmlConfig = `
-# Fallback measure on errors.
-[kyc-measure-freeze-investigate]
-CHECK_NAME = skip
-PROGRAM = freeze-investigate
-VOLUNTARY = NO
-CONTEXT = {}
-
-[aml-program-freeze-investigate]
-DESCRIPTION = "Fallback measure on errors that freezes the account and asks AML staff to investigate the system failure."
-COMMAND = taler-exchange-helper-measure-freeze
-ENABLED = YES
-FALLBACK = freeze-investigate
-
-[aml-program-inform-investigate]
-DESCRIPTION = "Measure that asks AML staff to investigate an account and informs the account owner about it."
-COMMAND = taler-exchange-helper-measure-inform-investigate
-ENABLED = YES
-FALLBACK = freeze-investigate
-
-[kyc-check-form-gls-merchant-onboarding]
-TYPE = FORM
-FORM_NAME = gls-merchant-onboarding
-DESCRIPTION = "GLS Merchant Onboarding"
-DESCRIPTION_I18N = {}
-OUTPUTS =
-FALLBACK = freeze-investigate
-
-[kyc-measure-merchant-onboarding]
-CHECK_NAME = form-gls-merchant-onboarding
-PROGRAM = inform-investigate
-CONTEXT = {}
-VOLUNTARY = NO
-
-[kyc-rule-deposit-limit-zero]
-OPERATION_TYPE = DEPOSIT
-NEXT_MEASURES = merchant-onboarding
-EXPOSED = YES
-ENABLED = YES
-THRESHOLD = TESTKUDOS:1
-TIMEFRAME = "1 days"
-`;
+import {
+ configureMerchantDepositKyc,
+ doAccountKycAuth as runAccountKycAuth,
+} from "./merchant-kyc-auth-helper.js";
function adjustExchangeConfig(config: Configuration) {
- configureCommonKyc(config);
- config.loadFromString(myAmlConfig);
+ configureMerchantDepositKyc(config, "TESTKUDOS:1");
}
const logger = new Logger("test-merchant-kyc-auth-multi.ts");
@@ -106,68 +61,7 @@ async function doAccountKycAuth(
wireGatewayApi: TalerWireGatewayHttpClient;
},
): Promise<void> {
- const {
- merchant,
- exchange,
- merchantInstId,
- merchantAccessToken,
- merchantInstPaytoUri,
- bank,
- wireGatewayApi,
- } = args;
- const merchantClient = new TalerMerchantInstanceHttpClient(
- merchant.makeInstanceBaseUrl(merchantInstId),
- );
- {
- const kycRes1 = succeedOrThrow(
- await merchantClient.getCurrentInstanceKycStatus(merchantAccessToken, {}),
- );
- console.log(`kyc res: ${j2s(kycRes1)}`);
- const myRow = kycRes1.kyc_data.find(
- (x) => x.exchange_url === exchange.baseUrl,
- );
- t.assertTrue(
- myRow?.payto_kycauths != null && myRow.payto_kycauths.length == 1,
- );
- const authTxPayto = Result.unpack(Paytos.fromString(myRow.payto_kycauths[0]));
- const authTxMessage = authTxPayto?.params["message"];
- t.assertTrue(typeof authTxMessage === "string");
- t.assertTrue(authTxMessage.startsWith("KYC:"));
- const accountPub = authTxMessage.substring(4);
- logger.info(`merchant account pub: ${accountPub}`);
- await wireGatewayApi.addKycAuth({
- auth: bank.getAdminAuth(),
- body: {
- amount: "TESTKUDOS:0.1",
- debit_account: merchantInstPaytoUri,
- account_pub: accountPub,
- },
- });
- }
-
- // Wait for auth transfer to be registered by the exchange
- {
- const kycStatus = await merchantClient.getCurrentInstanceKycStatus(
- merchantAccessToken,
- {
- longpoll: {
- type: "state-exit",
- timeout: 30000,
- status: MerchantAccountKycStatus.KYC_WIRE_REQUIRED,
- },
- },
- );
- logger.info(`kyc status after transfer: ${j2s(kycStatus)}`);
- t.assertDeepEqual(kycStatus.case, "ok");
- const myRow = kycStatus.body.kyc_data.find(
- (x) =>
- x.exchange_url === exchange.baseUrl &&
- x.payto_uri === merchantInstPaytoUri,
- );
- t.assertTrue(myRow != null);
- t.assertDeepEqual(myRow.status, "ready");
- t.assertTrue(typeof myRow.access_token === "string");
- }
+ await runAccountKycAuth(t, args);
}
/**
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-payto-reuse.ts b/packages/taler-harness/src/integrationtests/test-merchant-payto-reuse.ts
@@ -23,11 +23,6 @@ import {
ConfirmPayResultType,
encodeCrock,
getRandomBytes,
- j2s,
- Logger,
- MerchantAccountKycStatus,
- Paytos,
- Result,
succeedOrThrow,
TalerMerchantInstanceHttpClient,
TalerProtocolDuration,
@@ -37,7 +32,6 @@ import {
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
- configureCommonKyc,
createKycTestkudosEnvironmentFull,
withdrawViaBankV4,
} from "../harness/environments.js";
@@ -48,57 +42,15 @@ import {
GlobalTestState,
MerchantService,
} from "../harness/harness.js";
-
-const myAmlConfig = `
-# Fallback measure on errors.
-[kyc-measure-freeze-investigate]
-CHECK_NAME = skip
-PROGRAM = freeze-investigate
-VOLUNTARY = NO
-CONTEXT = {}
-
-[aml-program-freeze-investigate]
-DESCRIPTION = "Fallback measure on errors that freezes the account and asks AML staff to investigate the system failure."
-COMMAND = taler-exchange-helper-measure-freeze
-ENABLED = YES
-FALLBACK = freeze-investigate
-
-[aml-program-inform-investigate]
-DESCRIPTION = "Measure that asks AML staff to investigate an account and informs the account owner about it."
-COMMAND = taler-exchange-helper-measure-inform-investigate
-ENABLED = YES
-FALLBACK = freeze-investigate
-
-[kyc-check-form-gls-merchant-onboarding]
-TYPE = FORM
-FORM_NAME = gls-merchant-onboarding
-DESCRIPTION = "GLS Merchant Onboarding"
-DESCRIPTION_I18N = {}
-OUTPUTS =
-FALLBACK = freeze-investigate
-
-[kyc-measure-merchant-onboarding]
-CHECK_NAME = form-gls-merchant-onboarding
-PROGRAM = inform-investigate
-CONTEXT = {}
-VOLUNTARY = NO
-
-[kyc-rule-deposit-limit-three]
-OPERATION_TYPE = DEPOSIT
-NEXT_MEASURES = merchant-onboarding
-EXPOSED = YES
-ENABLED = YES
-THRESHOLD = TESTKUDOS:3
-TIMEFRAME = "1 days"
-`;
+import {
+ configureMerchantDepositKyc,
+ doAccountKycAuth as runAccountKycAuth,
+} from "./merchant-kyc-auth-helper.js";
function adjustExchangeConfig(config: Configuration) {
- configureCommonKyc(config);
- config.loadFromString(myAmlConfig);
+ configureMerchantDepositKyc(config, "TESTKUDOS:3");
}
-const logger = new Logger("test-merchant-kyc-auth-multi.ts");
-
async function doAccountKycAuth(
t: GlobalTestState,
args: {
@@ -111,72 +63,7 @@ async function doAccountKycAuth(
wireGatewayApi: TalerWireGatewayHttpClient;
},
): Promise<void> {
- const {
- merchant,
- exchange,
- merchantInstId,
- merchantAccessToken,
- merchantInstPaytoUri,
- bank,
- wireGatewayApi,
- } = args;
-
- const merchantClient = new TalerMerchantInstanceHttpClient(
- merchant.makeInstanceBaseUrl(merchantInstId),
- );
- {
- const kycRes1 = succeedOrThrow(
- await merchantClient.getCurrentInstanceKycStatus(merchantAccessToken, {}),
- );
- console.log(`kyc res: ${j2s(kycRes1)}`);
- const myRow = kycRes1.kyc_data.find(
- (x) => x.exchange_url === exchange.baseUrl,
- );
- t.assertTrue(
- myRow?.payto_kycauths != null && myRow.payto_kycauths.length == 1,
- );
- const authTxPayto = Result.unpack(
- Paytos.fromString(myRow.payto_kycauths[0]),
- );
- const authTxMessage = authTxPayto?.params["message"];
- t.assertTrue(typeof authTxMessage === "string");
- t.assertTrue(authTxMessage.startsWith("KYC:"));
- const accountPub = authTxMessage.substring(4);
- logger.info(`merchant account pub: ${accountPub}`);
- await wireGatewayApi.addKycAuth({
- auth: bank.getAdminAuth(),
- body: {
- amount: "TESTKUDOS:0.1",
- debit_account: merchantInstPaytoUri,
- account_pub: accountPub,
- },
- });
- await exchange.runWirewatchOnce();
- }
-
- // Wait for auth transfer to be registered by the exchange
- {
- const kycStatus = await merchantClient.getCurrentInstanceKycStatus(
- merchantAccessToken,
- {
- longpoll: {
- type: "state-exit",
- status: MerchantAccountKycStatus.KYC_WIRE_REQUIRED,
- timeout: 30000,
- },
- },
- );
- logger.info(`kyc status after transfer: ${j2s(kycStatus)}`);
- t.assertDeepEqual(kycStatus.case, "ok");
- const myRow = kycStatus.body.kyc_data.find(
- (x) =>
- x.exchange_url === exchange.baseUrl &&
- x.payto_uri === merchantInstPaytoUri,
- );
- t.assertTrue(myRow != null);
- t.assertDeepEqual(myRow.status, "ready");
- t.assertTrue(typeof myRow.access_token === "string");
- }
+ await runAccountKycAuth(t, args);
}
/**
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-refund-api.ts b/packages/taler-harness/src/integrationtests/test-merchant-refund-api.ts
@@ -1,22 +1,12 @@
/*
This file is part of GNU Taler
- (C) 2020 Taler Systems S.A.
+ (C) 2020, 2026 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
+*/
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Imports.
- */
import {
AccessToken,
Duration,
@@ -33,63 +23,52 @@ import {
withdrawViaBankV3,
} from "../harness/environments.js";
import {
- ExchangeServiceInterface,
GlobalTestState,
harnessHttpLib,
MerchantServiceInterface,
WalletClient,
} from "../harness/harness.js";
-async function testRefundApiWithFulfillmentUrl(
+async function testRefundApi(
t: GlobalTestState,
env: {
merchant: MerchantServiceInterface;
walletClient: WalletClient;
merchantAdminAccessToken: AccessToken;
- exchange: ExchangeServiceInterface;
},
+ fulfillment:
+ | { fulfillment_url: string }
+ | { fulfillment_message: string },
): Promise<void> {
const { walletClient, merchant, merchantAdminAccessToken } = env;
-
const merchantClient = new TalerMerchantInstanceHttpClient(
merchant.makeInstanceBaseUrl(),
);
-
- // Set up order.
const orderResp = succeedOrThrow(
await merchantClient.createOrder(merchantAdminAccessToken, {
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/fulfillment",
+ ...fulfillment,
},
refund_delay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ minutes: 5 }),
),
}),
);
-
let orderStatus = succeedOrThrow(
await merchantClient.getOrderDetails(
merchantAdminAccessToken,
orderResp.order_id,
),
);
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
+ t.assertDeepEqual(orderStatus.order_status, "unpaid");
const talerPayUri = orderStatus.taler_pay_uri;
- const orderId = orderResp.order_id;
-
- // Make wallet pay for the order
let preparePayResult = await walletClient.call(
WalletApiOperation.PreparePayForUriV2,
- {
- talerPayUri,
- },
+ { talerPayUri },
);
-
await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
transactionId: preparePayResult.transactionId,
txState: {
@@ -97,237 +76,68 @@ async function testRefundApiWithFulfillmentUrl(
minor: TransactionMinorState.Proposed,
},
});
-
await walletClient.call(WalletApiOperation.ConfirmPay, {
transactionId: preparePayResult.transactionId,
choiceIndex: 0,
});
-
- // Check if payment was successful.
-
orderStatus = succeedOrThrow(
await merchantClient.getOrderDetails(
merchantAdminAccessToken,
orderResp.order_id,
),
);
-
- t.assertTrue(orderStatus.order_status === "paid");
+ t.assertDeepEqual(orderStatus.order_status, "paid");
preparePayResult = await walletClient.call(
WalletApiOperation.PreparePayForUriV2,
- {
- talerPayUri,
- },
+ { talerPayUri },
);
-
await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
transactionId: preparePayResult.transactionId,
- txState: {
- major: TransactionMajorState.Done,
- },
+ txState: { major: TransactionMajorState.Done },
});
-
const payTx = await walletClient.call(WalletApiOperation.GetTransactionById, {
transactionId: preparePayResult.transactionId,
});
-
- t.assertTrue(payTx.type === TransactionType.Payment);
+ t.assertDeepEqual(payTx.type, TransactionType.Payment);
t.assertTrue(payTx.info != null);
- await merchantClient.addRefund(merchantAdminAccessToken, orderResp.order_id, {
- refund: "TESTKUDOS:5",
- reason: "foo",
- });
-
- orderStatus = succeedOrThrow(
- await merchantClient.getOrderDetails(
- merchantAdminAccessToken,
- orderResp.order_id,
- ),
- );
-
- t.assertTrue(orderStatus.order_status === "paid");
-
- t.assertAmountEquals(orderStatus.refund_amount, "TESTKUDOS:5");
-
- // Now test what the merchant gives as a response for various requests to the
- // public order status URL!
-
- let publicOrderStatusUrl = new URL(
- `orders/${orderId}`,
- merchant.makeInstanceBaseUrl(),
- );
- publicOrderStatusUrl.searchParams.set(
- "h_contract",
- payTx.info.contractTermsHash,
- );
-
- let publicOrderStatusResp = await harnessHttpLib.fetch(
- publicOrderStatusUrl.href,
- );
- const respData = await publicOrderStatusResp.json();
- t.assertTrue(publicOrderStatusResp.status === 200);
- t.assertAmountEquals(respData.refund_amount, "TESTKUDOS:5");
-
- publicOrderStatusUrl = new URL(
- `orders/${orderId}`,
- merchant.makeInstanceBaseUrl(),
- );
- console.log(`requesting order status via '${publicOrderStatusUrl.href}'`);
- publicOrderStatusResp = await harnessHttpLib.fetch(publicOrderStatusUrl.href);
- console.log(publicOrderStatusResp.status);
- console.log(await publicOrderStatusResp.json());
- // We didn't give any authentication, so we should get a fulfillment URL back
- t.assertTrue(publicOrderStatusResp.status === 403);
-}
-
-async function testRefundApiWithFulfillmentMessage(
- t: GlobalTestState,
- env: {
- merchant: MerchantServiceInterface;
- walletClient: WalletClient;
- merchantAdminAccessToken: AccessToken;
- exchange: ExchangeServiceInterface;
- },
-): Promise<void> {
- const { walletClient, merchant, merchantAdminAccessToken } = env;
-
- const merchantClient = new TalerMerchantInstanceHttpClient(
- merchant.makeInstanceBaseUrl(),
- );
-
- // Set up order.
- const orderResp = succeedOrThrow(
- await merchantClient.createOrder(merchantAdminAccessToken, {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_message: "Thank you for buying foobar",
- },
- refund_delay: Duration.toTalerProtocolDuration(
- Duration.fromSpec({ minutes: 5 }),
- ),
- }),
- );
-
- let orderStatus = succeedOrThrow(
- await merchantClient.getOrderDetails(
- merchantAdminAccessToken,
- orderResp.order_id,
- ),
- );
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- const talerPayUri = orderStatus.taler_pay_uri;
- const orderId = orderResp.order_id;
-
- // Make wallet pay for the order
-
- let preparePayResult = await walletClient.call(
- WalletApiOperation.PreparePayForUriV2,
- {
- talerPayUri,
- },
+ await merchantClient.addRefund(
+ merchantAdminAccessToken,
+ orderResp.order_id,
+ { refund: "TESTKUDOS:5", reason: "foo" },
);
-
- await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: preparePayResult.transactionId,
- txState: {
- major: TransactionMajorState.Dialog,
- minor: TransactionMinorState.Proposed,
- },
- });
-
- await walletClient.call(WalletApiOperation.ConfirmPay, {
- transactionId: preparePayResult.transactionId,
- choiceIndex: 0,
- });
-
- // Check if payment was successful.
-
orderStatus = succeedOrThrow(
await merchantClient.getOrderDetails(
merchantAdminAccessToken,
orderResp.order_id,
),
);
-
- t.assertTrue(orderStatus.order_status === "paid");
-
- preparePayResult = await walletClient.call(
- WalletApiOperation.PreparePayForUriV2,
- {
- talerPayUri,
- },
- );
-
- await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: preparePayResult.transactionId,
- txState: {
- major: TransactionMajorState.Done,
- },
- });
-
- const payTx = await walletClient.call(WalletApiOperation.GetTransactionById, {
- transactionId: preparePayResult.transactionId,
- });
-
- t.assertTrue(payTx.type === TransactionType.Payment);
- t.assertTrue(payTx.info != null);
-
- await merchantClient.addRefund(merchantAdminAccessToken, orderId, {
- refund: "TESTKUDOS:5",
- reason: "foo",
- });
-
- orderStatus = succeedOrThrow(
- await merchantClient.getOrderDetails(merchantAdminAccessToken, orderId),
- );
-
- t.assertTrue(orderStatus.order_status === "paid");
-
+ t.assertDeepEqual(orderStatus.order_status, "paid");
t.assertAmountEquals(orderStatus.refund_amount, "TESTKUDOS:5");
- // Now test what the merchant gives as a response for various requests to the
- // public order status URL!
-
- let publicOrderStatusUrl = new URL(
- `orders/${orderId}`,
+ const authenticatedUrl = new URL(
+ `orders/${orderResp.order_id}`,
merchant.makeInstanceBaseUrl(),
);
- publicOrderStatusUrl.searchParams.set(
- "h_contract",
- payTx.info.contractTermsHash,
- );
+ authenticatedUrl.searchParams.set("h_contract", payTx.info.contractTermsHash);
+ const authenticatedResp = await harnessHttpLib.fetch(authenticatedUrl.href);
+ const authenticatedBody = await authenticatedResp.json();
+ t.assertDeepEqual(authenticatedResp.status, 200);
+ t.assertAmountEquals(authenticatedBody.refund_amount, "TESTKUDOS:5");
- let publicOrderStatusResp = await harnessHttpLib.fetch(
- publicOrderStatusUrl.href,
+ const unauthenticatedResp = await harnessHttpLib.fetch(
+ new URL(
+ `orders/${orderResp.order_id}`,
+ merchant.makeInstanceBaseUrl(),
+ ).href,
);
- let respData = await publicOrderStatusResp.json();
- console.log(respData);
- t.assertTrue(publicOrderStatusResp.status === 200);
- t.assertAmountEquals(respData.refund_amount, "TESTKUDOS:5");
-
- publicOrderStatusUrl = new URL(
- `orders/${orderId}`,
- merchant.makeInstanceBaseUrl(),
- );
-
- publicOrderStatusResp = await harnessHttpLib.fetch(publicOrderStatusUrl.href);
- respData = await publicOrderStatusResp.json();
- console.log(respData);
- // We didn't give any authentication, so we should get a fulfillment URL back
- t.assertTrue(publicOrderStatusResp.status === 403);
+ await unauthenticatedResp.json();
+ t.assertDeepEqual(unauthenticatedResp.status, 403);
}
-/**
- * Test case for the refund API of the merchant backend.
- */
+/** Test refund status for both fulfillment URL and message orders. */
export async function runMerchantRefundApiTest(t: GlobalTestState) {
- // Set up test environment
-
const {
walletClient,
bankClient,
@@ -335,9 +145,6 @@ export async function runMerchantRefundApiTest(t: GlobalTestState) {
merchant,
merchantAdminAccessToken,
} = await createSimpleTestkudosEnvironmentV3(t);
-
- // Withdraw digital cash into the wallet.
-
const wres = await withdrawViaBankV3(t, {
walletClient,
bankClient,
@@ -346,19 +153,13 @@ export async function runMerchantRefundApiTest(t: GlobalTestState) {
});
await wres.withdrawalFinishedCond;
- await testRefundApiWithFulfillmentUrl(t, {
- walletClient,
- exchange,
- merchant,
- merchantAdminAccessToken,
- });
-
- await testRefundApiWithFulfillmentMessage(t, {
- walletClient,
- exchange,
- merchant,
- merchantAdminAccessToken,
- });
+ const env = { walletClient, merchant, merchantAdminAccessToken };
+ for (const fulfillment of [
+ { fulfillment_url: "https://example.com/fulfillment" },
+ { fulfillment_message: "Thank you for buying foobar" },
+ ]) {
+ await testRefundApi(t, env, fulfillment);
+ }
}
runMerchantRefundApiTest.suites = ["merchant"];
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-self-provision-activation-two-bank-account.ts b/packages/taler-harness/src/integrationtests/test-merchant-self-provision-activation-two-bank-account.ts
@@ -1,222 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Imports.
- */
-import {
- alternativeOrThrow,
- Duration,
- HttpStatusCode,
- LoginTokenScope,
- MerchantAuthMethod,
- succeedOrThrow,
- TalerMerchantInstanceHttpClient,
- TalerMerchantManagementHttpClient,
-} from "@gnu-taler/taler-util";
-import { createSimpleTestkudosEnvironmentV3 } from "../harness/environments.js";
-import {
- getTestHarnessPaytoForLabel,
- GlobalTestState,
-} from "../harness/harness.js";
-import {
- configureTestMerchantMfa,
- doChallenge,
- makeMfaConfigEmailSms,
- solveMFA,
-} from "../harness/tan-helper.js";
-
-/**
- * Activate a self-provisioned instance with email/SMS MFA and verify that two
- * distinct bank accounts survive the MFA-protected account-add flow.
- */
-export async function runMerchantSelfProvisionActivationTwoBankAccountsTest(
- t: GlobalTestState,
-) {
- // Set up test environment
-
- const instanceInfo = {
- id: "self-instance",
- name: "My instance",
- auth: {
- method: MerchantAuthMethod.TOKEN,
- password: "123",
- },
- default_pay_delay: Duration.toTalerProtocolDuration(
- Duration.fromSpec({ days: 14 }),
- ),
- default_wire_transfer_delay: Duration.toTalerProtocolDuration(
- Duration.fromSpec({ days: 14 }),
- ),
- jurisdiction: {},
- address: {},
- email: "some@taler.net",
- phone_number: "+1111",
- use_stefan: false,
- };
-
- const mfaConfig = makeMfaConfigEmailSms(
- t,
- instanceInfo.email,
- instanceInfo.phone_number,
- );
-
- const { merchant, merchantAdminAccessToken } =
- await createSimpleTestkudosEnvironmentV3(t, undefined, {
- additionalMerchantConfig(m) {
- m.modifyConfig(async (cfg) => {
- cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes");
- configureTestMerchantMfa(cfg, mfaConfig);
- });
- },
- });
-
- const merchantClient = new TalerMerchantManagementHttpClient(
- merchant.makeInstanceBaseUrl(),
- );
-
- {
- const r = succeedOrThrow(
- await merchantClient.listInstances(merchantAdminAccessToken),
- );
- t.assertDeepEqual(r.instances.length, 2);
- }
-
- const signupStart = alternativeOrThrow(
- await merchantClient.createInstanceSelfProvision(instanceInfo),
- HttpStatusCode.Accepted,
- );
-
- // creation requires 2fa
- t.assertDeepEqual(signupStart.challenges.length, 2);
- t.assertDeepEqual(signupStart.combi_and, true);
-
- const firstChallenge = signupStart.challenges[0];
- const secondChallenge = signupStart.challenges[1];
-
- {
- // new instance is pending, then is not listed
- const r = succeedOrThrow(
- await merchantClient.listInstances(merchantAdminAccessToken),
- );
- t.assertDeepEqual(r.instances.length, 2);
- }
-
- await doChallenge(
- t,
- merchantClient,
- firstChallenge.challenge_id,
- instanceInfo.email,
- mfaConfig.email.path,
- );
-
- await doChallenge(
- t,
- merchantClient,
- secondChallenge.challenge_id,
- instanceInfo.phone_number,
- mfaConfig.sms.path,
- );
-
- const completeSignup = await merchantClient.createInstanceSelfProvision(
- instanceInfo,
- {
- challengeIds: [firstChallenge.challenge_id, secondChallenge.challenge_id],
- },
- );
-
- t.assertDeepEqual(completeSignup.type, "ok");
-
- const instanceApi = new TalerMerchantInstanceHttpClient(
- merchantClient.getSubInstanceAPI(instanceInfo.id),
- merchantClient.httpLib,
- );
-
- {
- // new instance is completed, now it should be visible
- const r = succeedOrThrow(
- await merchantClient.listInstances(merchantAdminAccessToken),
- );
- t.assertDeepEqual(r.instances.length, 3);
- }
-
- const loginChallenge = alternativeOrThrow(
- await instanceApi.createAccessToken(
- instanceInfo.id,
- instanceInfo.auth.password,
- {
- scope: LoginTokenScope.All,
- },
- ),
- HttpStatusCode.Accepted,
- );
-
- await solveMFA(t, merchantClient, loginChallenge, mfaConfig);
-
- const { access_token: token } = succeedOrThrow(
- await instanceApi.createAccessToken(
- instanceInfo.id,
- instanceInfo.auth.password,
- {
- scope: LoginTokenScope.All,
- },
- {
- challengeIds: loginChallenge.challenges.map((c) => c.challenge_id),
- },
- ),
- );
-
- const bankAccount = succeedOrThrow(
- await instanceApi.addBankAccount(token, {
- payto_uri: getTestHarnessPaytoForLabel("account1"),
- }),
- );
-
- const secondBankAccountChallenge = alternativeOrThrow(
- await instanceApi.addBankAccount(token, {
- payto_uri: getTestHarnessPaytoForLabel("account2"),
- }),
- HttpStatusCode.Accepted,
- );
-
- await solveMFA(t, instanceApi, secondBankAccountChallenge, mfaConfig);
-
- const secondBankAccount = succeedOrThrow(
- await instanceApi.addBankAccount(
- token,
- {
- payto_uri: getTestHarnessPaytoForLabel("account2"),
- },
- {
- challengeIds: secondBankAccountChallenge.challenges.map(
- (c) => c.challenge_id,
- ),
- },
- ),
- );
-
- t.assertTrue(bankAccount.h_wire !== secondBankAccount.h_wire);
- const accounts = succeedOrThrow(await instanceApi.listBankAccounts(token));
- const accountHashes = accounts.accounts.map((account) => account.h_wire);
- t.assertDeepEqual(accounts.accounts.length, 2);
- t.assertTrue(accountHashes.includes(bankAccount.h_wire));
- t.assertTrue(accountHashes.includes(secondBankAccount.h_wire));
-}
-
-runMerchantSelfProvisionActivationTwoBankAccountsTest.suites = [
- "merchant",
- "self-provision",
-];
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-self-provision-activation.ts b/packages/taler-harness/src/integrationtests/test-merchant-self-provision-activation.ts
@@ -1,28 +1,16 @@
/*
This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
+ (C) 2021, 2026 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
+*/
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Imports.
- */
import {
alternativeOrThrow,
Duration,
HttpStatusCode,
- j2s,
- Logger,
LoginTokenScope,
MerchantAuthMethod,
succeedOrThrow,
@@ -31,32 +19,28 @@ import {
TanChannel,
} from "@gnu-taler/taler-util";
import { createSimpleTestkudosEnvironmentV3 } from "../harness/environments.js";
-import { GlobalTestState } from "../harness/harness.js";
+import {
+ getTestHarnessPaytoForLabel,
+ GlobalTestState,
+} from "../harness/harness.js";
import {
configureTestMerchantMfa,
+ doChallenge,
makeMfaConfigEmailSms,
solveMFA,
- wait2FaCode,
} from "../harness/tan-helper.js";
-export const logger = new Logger("test-merchant-self-provision-activation.ts");
-
/**
- * Activate a self-provisioned instance with email/SMS MFA and verify its
- * authenticated contact details.
+ * Cover self-provision activation, instance-ID casing, contact validation and
+ * the MFA-protected addition of a second bank account in one environment.
*/
export async function runMerchantSelfProvisionActivationTest(
t: GlobalTestState,
) {
- // Set up test environment
-
const instanceInfo = {
- id: "my-awesome-instance",
+ id: "MYUPPERCASEINSTANCE",
name: "My awesome instance",
- auth: {
- method: MerchantAuthMethod.TOKEN,
- password: "123",
- },
+ auth: { method: MerchantAuthMethod.TOKEN, password: "123" },
default_pay_delay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ days: 14 }),
),
@@ -69,13 +53,11 @@ export async function runMerchantSelfProvisionActivationTest(
phone_number: "+1111",
use_stefan: false,
};
-
const mfaConfig = makeMfaConfigEmailSms(
t,
instanceInfo.email,
instanceInfo.phone_number,
);
-
const { merchant, merchantAdminAccessToken } =
await createSimpleTestkudosEnvironmentV3(t, undefined, {
additionalMerchantConfig(m) {
@@ -85,134 +67,126 @@ export async function runMerchantSelfProvisionActivationTest(
});
},
});
-
const merchantClient = new TalerMerchantManagementHttpClient(
merchant.makeInstanceBaseUrl(),
);
+ const initialInstances = succeedOrThrow(
+ await merchantClient.listInstances(merchantAdminAccessToken),
+ );
+ t.assertDeepEqual(initialInstances.instances.length, 2);
- {
- const r = succeedOrThrow(
- await merchantClient.listInstances(merchantAdminAccessToken),
- );
- t.assertDeepEqual(r.instances.length, 2);
- }
+ await t.assertThrowsTalerErrorAsync(async () => {
+ await merchantClient.createInstanceSelfProvision({
+ ...instanceInfo,
+ id: "löl",
+ });
+ });
const signupStart = alternativeOrThrow(
await merchantClient.createInstanceSelfProvision(instanceInfo),
HttpStatusCode.Accepted,
);
-
- // creation requires 2fa
t.assertDeepEqual(signupStart.challenges.length, 2);
t.assertDeepEqual(signupStart.combi_and, true);
+ const [emailChallenge, smsChallenge] = signupStart.challenges;
+ t.assertDeepEqual(emailChallenge.tan_channel, TanChannel.EMAIL);
+ t.assertDeepEqual(smsChallenge.tan_channel, TanChannel.SMS);
+ const pendingInstances = succeedOrThrow(
+ await merchantClient.listInstances(merchantAdminAccessToken),
+ );
+ t.assertDeepEqual(pendingInstances.instances.length, 2);
- const firstChallenge = signupStart.challenges[0];
- const secondChallenge = signupStart.challenges[1];
-
- // FIXME: check the order
- // always first emails since is cheaper
- t.assertTrue(firstChallenge.tan_channel === TanChannel.EMAIL);
- t.assertTrue(secondChallenge.tan_channel === TanChannel.SMS);
-
- {
- // new instance is pending, then is not listed
- const r = succeedOrThrow(
- await merchantClient.listInstances(merchantAdminAccessToken),
- );
- t.assertDeepEqual(r.instances.length, 2);
- }
-
- {
- succeedOrThrow(
- await merchantClient.sendChallenge(firstChallenge.challenge_id),
- );
- const res = await wait2FaCode(mfaConfig.email.path);
- console.log(`MFA message: ${res.message}`);
- const loginMatch = res.message.match(/^Login: (.*)$/m);
- const login = loginMatch?.[1];
- console.log(`login account: ${login}`);
- t.assertDeepEqual(login, instanceInfo.id);
- t.assertDeepEqual(res.address, instanceInfo.email);
- succeedOrThrow(
- await merchantClient.confirmChallenge(firstChallenge.challenge_id, {
- tan: res.code,
- }),
- );
- }
-
- {
- succeedOrThrow(
- await merchantClient.sendChallenge(secondChallenge.challenge_id),
- );
- const res = await wait2FaCode(mfaConfig.sms.path);
- t.assertDeepEqual(res.address, instanceInfo.phone_number);
- succeedOrThrow(
- await merchantClient.confirmChallenge(secondChallenge.challenge_id, {
- tan: res.code,
- }),
- );
- }
-
+ await doChallenge(
+ t,
+ merchantClient,
+ emailChallenge.challenge_id,
+ instanceInfo.email,
+ mfaConfig.email.path,
+ );
+ await doChallenge(
+ t,
+ merchantClient,
+ smsChallenge.challenge_id,
+ instanceInfo.phone_number,
+ mfaConfig.sms.path,
+ );
const completeSignup = await merchantClient.createInstanceSelfProvision(
instanceInfo,
{
- challengeIds: [firstChallenge.challenge_id, secondChallenge.challenge_id],
+ challengeIds: [
+ emailChallenge.challenge_id,
+ smsChallenge.challenge_id,
+ ],
},
);
-
- logger.info(`signup response:`);
- logger.info(j2s(await completeSignup.response.json()));
-
t.assertDeepEqual(completeSignup.type, "ok");
const instanceApi = new TalerMerchantInstanceHttpClient(
merchantClient.getSubInstanceAPI(instanceInfo.id),
merchantClient.httpLib,
);
-
- {
- // new instance is completed, now it should be visible
- const r = succeedOrThrow(
- await merchantClient.listInstances(merchantAdminAccessToken),
- );
- t.assertDeepEqual(r.instances.length, 3);
- }
+ const completedInstances = succeedOrThrow(
+ await merchantClient.listInstances(merchantAdminAccessToken),
+ );
+ t.assertDeepEqual(completedInstances.instances.length, 3);
const loginChallenge = alternativeOrThrow(
await instanceApi.createAccessToken(
instanceInfo.id,
instanceInfo.auth.password,
- {
- scope: LoginTokenScope.All,
- },
+ { scope: LoginTokenScope.All },
),
HttpStatusCode.Accepted,
);
-
await solveMFA(t, merchantClient, loginChallenge, mfaConfig);
-
const { access_token: token } = succeedOrThrow(
await instanceApi.createAccessToken(
instanceInfo.id,
instanceInfo.auth.password,
- {
- scope: LoginTokenScope.All,
- },
+ { scope: LoginTokenScope.All },
{
challengeIds: loginChallenge.challenges.map((c) => c.challenge_id),
},
),
);
- const det = succeedOrThrow(
+ const details = succeedOrThrow(
await instanceApi.getCurrentInstanceDetails(token),
);
-
- // check that the instance has the new email
- t.assertDeepEqual(det.email, instanceInfo.email);
- t.assertDeepEqual(det.email_validated, true);
- t.assertDeepEqual(det.phone_number, instanceInfo.phone_number);
- t.assertDeepEqual(det.phone_validated, true);
+ t.assertDeepEqual(details.email, instanceInfo.email);
+ t.assertDeepEqual(details.email_validated, true);
+ t.assertDeepEqual(details.phone_number, instanceInfo.phone_number);
+ t.assertDeepEqual(details.phone_validated, true);
+
+ const firstAccount = succeedOrThrow(
+ await instanceApi.addBankAccount(token, {
+ payto_uri: getTestHarnessPaytoForLabel("account1"),
+ }),
+ );
+ const secondAccountChallenge = alternativeOrThrow(
+ await instanceApi.addBankAccount(token, {
+ payto_uri: getTestHarnessPaytoForLabel("account2"),
+ }),
+ HttpStatusCode.Accepted,
+ );
+ await solveMFA(t, instanceApi, secondAccountChallenge, mfaConfig);
+ const secondAccount = succeedOrThrow(
+ await instanceApi.addBankAccount(
+ token,
+ { payto_uri: getTestHarnessPaytoForLabel("account2") },
+ {
+ challengeIds: secondAccountChallenge.challenges.map(
+ (c) => c.challenge_id,
+ ),
+ },
+ ),
+ );
+ t.assertTrue(firstAccount.h_wire !== secondAccount.h_wire);
+ const accounts = succeedOrThrow(await instanceApi.listBankAccounts(token));
+ const accountHashes = accounts.accounts.map((account) => account.h_wire);
+ t.assertDeepEqual(accounts.accounts.length, 2);
+ t.assertTrue(accountHashes.includes(firstAccount.h_wire));
+ t.assertTrue(accountHashes.includes(secondAccount.h_wire));
}
runMerchantSelfProvisionActivationTest.suites = ["merchant", "self-provision"];
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-self-provision-casing.ts b/packages/taler-harness/src/integrationtests/test-merchant-self-provision-casing.ts
@@ -1,221 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Imports.
- */
-import {
- alternativeOrThrow,
- Duration,
- HttpStatusCode,
- Logger,
- LoginTokenScope,
- MerchantAuthMethod,
- succeedOrThrow,
- TalerMerchantInstanceHttpClient,
- TalerMerchantManagementHttpClient,
- TanChannel,
-} from "@gnu-taler/taler-util";
-import { createSimpleTestkudosEnvironmentV3 } from "../harness/environments.js";
-import { GlobalTestState } from "../harness/harness.js";
-import {
- configureTestMerchantMfa,
- makeMfaConfigEmailSms,
- solveMFA,
- wait2FaCode,
-} from "../harness/tan-helper.js";
-
-export const logger = new Logger("test-merchant-self-provision-activation.ts");
-
-/**
- * Regression test for bad case normalization in the
- * self onboarding.
- */
-export async function runMerchantSelfProvisionCasingTest(t: GlobalTestState) {
- // Set up test environment
-
- const instanceInfo = {
- id: "MYUPPERCASEINSTANCE",
- name: "My awesome instance",
- auth: {
- method: MerchantAuthMethod.TOKEN,
- password: "123",
- },
- default_pay_delay: Duration.toTalerProtocolDuration(
- Duration.fromSpec({ days: 14 }),
- ),
- default_wire_transfer_delay: Duration.toTalerProtocolDuration(
- Duration.fromSpec({ days: 14 }),
- ),
- jurisdiction: {},
- address: {},
- email: "some@taler.net",
- phone_number: "+1111",
- use_stefan: false,
- };
-
- const mfaConfig = makeMfaConfigEmailSms(
- t,
- instanceInfo.email,
- instanceInfo.phone_number,
- );
-
- const { merchant, merchantAdminAccessToken } =
- await createSimpleTestkudosEnvironmentV3(t, undefined, {
- additionalMerchantConfig(m) {
- m.modifyConfig(async (cfg) => {
- cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes");
- configureTestMerchantMfa(cfg, mfaConfig);
- });
- },
- });
-
- const merchantClient = new TalerMerchantManagementHttpClient(
- merchant.makeInstanceBaseUrl(),
- );
-
- {
- const r = succeedOrThrow(
- await merchantClient.listInstances(merchantAdminAccessToken),
- );
- t.assertDeepEqual(r.instances.length, 2);
- }
-
- {
- // Special characters in instance name should not be allowed
- await t.assertThrowsTalerErrorAsync(async () => {
- await merchantClient.createInstanceSelfProvision({
- ...instanceInfo,
- id: "löl",
- });
- });
- }
-
- const signupStart = alternativeOrThrow(
- await merchantClient.createInstanceSelfProvision(instanceInfo),
- HttpStatusCode.Accepted,
- );
-
- // creation requires 2fa
- t.assertDeepEqual(signupStart.challenges.length, 2);
- t.assertDeepEqual(signupStart.combi_and, true);
-
- const firstChallenge = signupStart.challenges[0];
- const secondChallenge = signupStart.challenges[1];
-
- // FIXME: check the order
- // always first emails since is cheaper
- t.assertTrue(firstChallenge.tan_channel === TanChannel.EMAIL);
- t.assertTrue(secondChallenge.tan_channel === TanChannel.SMS);
-
- {
- // new instance is pending, then is not listed
- const r = succeedOrThrow(
- await merchantClient.listInstances(merchantAdminAccessToken),
- );
- t.assertDeepEqual(r.instances.length, 2);
- }
-
- {
- succeedOrThrow(
- await merchantClient.sendChallenge(firstChallenge.challenge_id),
- );
- const res = await wait2FaCode(mfaConfig.email.path);
- console.log(`MFA message: ${res.message}`);
- const loginMatch = res.message.match(/^Login: (.*)$/m);
- const login = loginMatch?.[1];
- console.log(`login account: ${login}`);
- t.assertDeepEqual(res.address, instanceInfo.email);
- succeedOrThrow(
- await merchantClient.confirmChallenge(firstChallenge.challenge_id, {
- tan: res.code,
- }),
- );
- }
-
- {
- succeedOrThrow(
- await merchantClient.sendChallenge(secondChallenge.challenge_id),
- );
- const res = await wait2FaCode(mfaConfig.sms.path);
- t.assertDeepEqual(res.address, instanceInfo.phone_number);
- succeedOrThrow(
- await merchantClient.confirmChallenge(secondChallenge.challenge_id, {
- tan: res.code,
- }),
- );
- }
-
- const completeSignup = await merchantClient.createInstanceSelfProvision(
- instanceInfo,
- {
- challengeIds: [firstChallenge.challenge_id, secondChallenge.challenge_id],
- },
- );
-
- t.assertDeepEqual(completeSignup.type, "ok");
-
- const instanceApi = new TalerMerchantInstanceHttpClient(
- merchantClient.getSubInstanceAPI(instanceInfo.id),
- merchantClient.httpLib,
- );
-
- {
- // new instance is completed, now it should be visible
- const r = succeedOrThrow(
- await merchantClient.listInstances(merchantAdminAccessToken),
- );
- t.assertDeepEqual(r.instances.length, 3);
- }
-
- const loginChallenge = alternativeOrThrow(
- await instanceApi.createAccessToken(
- instanceInfo.id,
- instanceInfo.auth.password,
- {
- scope: LoginTokenScope.All,
- },
- ),
- HttpStatusCode.Accepted,
- );
-
- await solveMFA(t, merchantClient, loginChallenge, mfaConfig);
-
- const { access_token: token } = succeedOrThrow(
- await instanceApi.createAccessToken(
- instanceInfo.id,
- instanceInfo.auth.password,
- {
- scope: LoginTokenScope.All,
- },
- {
- challengeIds: loginChallenge.challenges.map((c) => c.challenge_id),
- },
- ),
- );
-
- const det = succeedOrThrow(
- await instanceApi.getCurrentInstanceDetails(token),
- );
-
- // check that the instance has the new email
- t.assertDeepEqual(det.email, instanceInfo.email);
- t.assertDeepEqual(det.email_validated, true);
- t.assertDeepEqual(det.phone_number, instanceInfo.phone_number);
- t.assertDeepEqual(det.phone_validated, true);
-}
-
-runMerchantSelfProvisionCasingTest.suites = ["merchant", "self-provision"];
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-spec-public-orders.ts b/packages/taler-harness/src/integrationtests/test-merchant-spec-public-orders.ts
@@ -1,34 +1,24 @@
/*
This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
+ (C) 2021, 2026 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
+*/
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Imports.
- */
import {
AccessToken,
ConfirmPayResultType,
+ encodeCrock,
+ getRandomBytes,
+ succeedOrThrow,
TalerCorebankApiClient,
TalerMerchantInstanceHttpClient,
TransactionMajorState,
TransactionMinorState,
TransactionType,
URL,
- encodeCrock,
- getRandomBytes,
- succeedOrThrow,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
@@ -39,8 +29,8 @@ import {
import {
ExchangeService,
GlobalTestState,
- MerchantService,
harnessHttpLib,
+ MerchantService,
} from "../harness/harness.js";
interface Context {
@@ -51,84 +41,73 @@ interface Context {
exchange: ExchangeService;
}
-const httpLib = harnessHttpLib;
+async function getJson(
+ t: GlobalTestState,
+ url: URL,
+ expectedStatus: number,
+): Promise<any> {
+ const response = await harnessHttpLib.fetch(url.href);
+ const body = await response.json();
+ t.assertDeepEqual(response.status, expectedStatus);
+ return body;
+}
-async function testWithClaimToken(
+/** Exercise the public order state machine with or without claim tokens. */
+async function testPublicOrderScenario(
t: GlobalTestState,
c: Context,
+ useClaimToken: boolean,
): Promise<void> {
+ const suffix = useClaimToken ? "claim-token" : "no-claim-token";
const { walletClient } = await createWalletDaemonWithClient(t, {
- name: "wct",
+ name: `public-order-${suffix}`,
});
- const { bankClient, exchange } = c;
- const { merchant, merchantBaseUrl, merchantAccessToken } = c;
const wres = await withdrawViaBankV3(t, {
walletClient,
- bankClient,
- exchange,
+ bankClient: c.bankClient,
+ exchange: c.exchange,
amount: "TESTKUDOS:20",
});
await wres.withdrawalFinishedCond;
- const sessionId = "mysession";
+ const sessionId = `session-${suffix}`;
const merchantClient = new TalerMerchantInstanceHttpClient(
- merchant.makeInstanceBaseUrl(),
+ c.merchant.makeInstanceBaseUrl(),
);
- const orderResp = succeedOrThrow(
- await merchantClient.createOrder(merchantAccessToken, {
+ const createOrder = () =>
+ merchantClient.createOrder(c.merchantAccessToken, {
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
fulfillment_url: "https://example.com/article42",
public_reorder_url: "https://example.com/article42-share",
},
- }),
- );
-
- const claimToken = orderResp.token;
+ create_token: useClaimToken,
+ });
+ const orderResp = succeedOrThrow(await createOrder());
const orderId = orderResp.order_id;
- t.assertTrue(!!claimToken);
- let talerPayUri: string;
+ const claimToken = orderResp.token;
+ t.assertDeepEqual(!!claimToken, useClaimToken);
- {
- const httpResp = await httpLib.fetch(
- new URL(`orders/${orderId}`, merchantBaseUrl).href,
- );
- const r = await httpResp.json();
- t.assertDeepEqual(httpResp.status, 202);
- console.log(r);
- }
+ const orderUrl = () => new URL(`orders/${orderId}`, c.merchantBaseUrl);
+ await getJson(t, orderUrl(), useClaimToken ? 202 : 402);
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("token", claimToken);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- t.assertDeepEqual(httpResp.status, 402);
- console.log(r);
- talerPayUri = r.taler_pay_uri;
- t.assertTrue(!!talerPayUri);
- }
+ const claimUrl = orderUrl();
+ if (claimToken) claimUrl.searchParams.set("token", claimToken);
+ const claimBody = await getJson(t, claimUrl, 402);
+ t.assertTrue(!!claimBody.taler_pay_uri);
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("token", claimToken);
- const httpResp = await httpLib.fetch(url.href, {
- headers: {
- Accept: "text/html",
- },
- });
- const r = await httpResp.text();
- t.assertDeepEqual(httpResp.status, 402);
- console.log(r);
- }
+ const htmlClaimUrl = orderUrl();
+ if (claimToken) htmlClaimUrl.searchParams.set("token", claimToken);
+ const htmlClaimResp = await harnessHttpLib.fetch(htmlClaimUrl.href, {
+ headers: { Accept: "text/html" },
+ });
+ await htmlClaimResp.text();
+ t.assertDeepEqual(htmlClaimResp.status, 402);
const preparePayResp = await walletClient.call(
WalletApiOperation.PreparePayForUriV2,
- {
- talerPayUri,
- },
+ { talerPayUri: claimBody.taler_pay_uri },
);
-
await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
transactionId: preparePayResp.transactionId,
txState: {
@@ -136,545 +115,112 @@ async function testWithClaimToken(
minor: TransactionMinorState.Proposed,
},
});
-
const payTx = await walletClient.call(WalletApiOperation.GetTransactionById, {
transactionId: preparePayResp.transactionId,
});
-
- t.assertTrue(payTx.type === TransactionType.Payment);
+ t.assertDeepEqual(payTx.type, TransactionType.Payment);
t.assertTrue(payTx.info != null);
-
const contractTermsHash = payTx.info.contractTermsHash;
- const proposalTransactionId = preparePayResp.transactionId;
-
- // claimed, unpaid, access with wrong h_contract
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const hcWrong = encodeCrock(getRandomBytes(64));
- url.searchParams.set("h_contract", hcWrong);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // claimed, unpaid, access with wrong claim token
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const ctWrong = encodeCrock(getRandomBytes(16));
- url.searchParams.set("token", ctWrong);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // claimed, unpaid, access with correct claim token
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("token", claimToken);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- // claimed, unpaid, access with correct contract terms hash
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("h_contract", contractTermsHash);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- // claimed, unpaid, access without credentials
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 202);
- }
-
- const confirmPayRes = await walletClient.call(WalletApiOperation.ConfirmPay, {
- transactionId: proposalTransactionId,
- choiceIndex: 0,
- });
-
- t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
-
- // paid, access without credentials
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 202);
- }
-
- // paid, access with wrong h_contract
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const hcWrong = encodeCrock(getRandomBytes(64));
- url.searchParams.set("h_contract", hcWrong);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // paid, access with wrong claim token
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const ctWrong = encodeCrock(getRandomBytes(16));
- url.searchParams.set("token", ctWrong);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // paid, access with correct h_contract
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("h_contract", contractTermsHash);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 200);
- }
- // paid, access with correct claim token, JSON
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("token", claimToken);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 200);
- const respFulfillmentUrl = r.fulfillment_url;
- t.assertDeepEqual(respFulfillmentUrl, "https://example.com/article42");
- }
-
- // paid, access with correct claim token, HTML
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("token", claimToken);
- const httpResp = await httpLib.fetch(url.href, {
- headers: { Accept: "text/html" },
- });
- t.assertDeepEqual(httpResp.status, 200);
- }
-
- const confirmPayRes2 = await walletClient.call(
+ const wrongHashUrl = orderUrl();
+ wrongHashUrl.searchParams.set("h_contract", encodeCrock(getRandomBytes(64)));
+ await getJson(t, wrongHashUrl, 403);
+ const wrongTokenUrl = orderUrl();
+ wrongTokenUrl.searchParams.set("token", encodeCrock(getRandomBytes(16)));
+ await getJson(t, wrongTokenUrl, 403);
+ await getJson(t, claimUrl, 402);
+ const correctHashUrl = orderUrl();
+ correctHashUrl.searchParams.set("h_contract", contractTermsHash);
+ await getJson(t, correctHashUrl, 402);
+ await getJson(t, orderUrl(), useClaimToken ? 202 : 402);
+
+ const confirmPayRes = await walletClient.call(
WalletApiOperation.ConfirmPay,
- {
- transactionId: proposalTransactionId,
- sessionId: sessionId,
- },
+ { transactionId: preparePayResp.transactionId, choiceIndex: 0 },
);
-
- t.assertTrue(confirmPayRes2.type === ConfirmPayResultType.Done);
-
- // Create another order with identical fulfillment URL to test the "already paid" flow
- const alreadyPaidOrderResp = succeedOrThrow(
- await merchantClient.createOrder(merchantAccessToken, {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
- }),
+ t.assertDeepEqual(confirmPayRes.type, ConfirmPayResultType.Done);
+
+ await getJson(t, orderUrl(), useClaimToken ? 202 : 200);
+ await getJson(t, wrongHashUrl, 403);
+ await getJson(t, wrongTokenUrl, 403);
+ await getJson(t, correctHashUrl, 200);
+ const paidBody = await getJson(t, claimUrl, 200);
+ t.assertDeepEqual(
+ paidBody.fulfillment_url,
+ "https://example.com/article42",
);
-
- const apOrderId = alreadyPaidOrderResp.order_id;
- const apToken = alreadyPaidOrderResp.token;
- t.assertTrue(!!apToken);
-
- {
- const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);
- url.searchParams.set("token", apToken);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- // Check for already paid session ID, JSON
- {
- const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);
- url.searchParams.set("token", apToken);
- url.searchParams.set("session_id", sessionId);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- const alreadyPaidOrderId = r.already_paid_order_id;
- t.assertDeepEqual(alreadyPaidOrderId, orderId);
- }
-
- // Check for already paid session ID, HTML
- {
- const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);
- url.searchParams.set("token", apToken);
- url.searchParams.set("session_id", sessionId);
- const httpResp = await httpLib.fetch(url.href, {
- headers: { Accept: "text/html" },
- redirect: "manual",
- });
- console.log(
- `requesting GET ${url.href}, expected 302 got ${httpResp.status}`,
- );
- t.assertDeepEqual(httpResp.status, 302);
- const location = httpResp.headers.get("Location");
- console.log("location header:", location);
- t.assertDeepEqual(location, "https://example.com/article42");
- }
-}
-
-async function testWithoutClaimToken(
- t: GlobalTestState,
- c: Context,
-): Promise<void> {
- const { walletClient } = await createWalletDaemonWithClient(t, {
- name: "wnoct",
+ const paidHtmlResp = await harnessHttpLib.fetch(claimUrl.href, {
+ headers: { Accept: "text/html" },
});
- const sessionId = "mysession2";
- const { bankClient, exchange } = c;
- const { merchant, merchantBaseUrl, merchantAccessToken } = c;
- const merchantClient = new TalerMerchantInstanceHttpClient(
- merchant.makeInstanceBaseUrl(),
- );
- const wres = await withdrawViaBankV3(t, {
- walletClient,
- bankClient,
- exchange,
- amount: "TESTKUDOS:20",
- });
- await wres.withdrawalFinishedCond;
- const orderResp = succeedOrThrow(
- await merchantClient.createOrder(merchantAccessToken, {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
- create_token: false,
- }),
- );
-
- const orderId = orderResp.order_id;
- let talerPayUri: string;
-
- {
- const httpResp = await httpLib.fetch(
- new URL(`orders/${orderId}`, merchantBaseUrl).href,
- );
- const r = await httpResp.json();
- t.assertDeepEqual(httpResp.status, 402);
- console.log(r);
- }
-
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- t.assertDeepEqual(httpResp.status, 402);
- console.log(r);
- talerPayUri = r.taler_pay_uri;
- t.assertTrue(!!talerPayUri);
- }
-
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.fetch(url.href, {
- headers: {
- Accept: "text/html",
- },
- });
- const r = await httpResp.text();
- t.assertDeepEqual(httpResp.status, 402);
- console.log(r);
- }
-
- const preparePayResp = await walletClient.call(
- WalletApiOperation.PreparePayForUriV2,
- {
- talerPayUri,
- },
- );
-
- console.log(preparePayResp);
-
- await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: preparePayResp.transactionId,
- txState: {
- major: TransactionMajorState.Dialog,
- minor: TransactionMinorState.Proposed,
- },
- });
-
- const payTx = await walletClient.call(WalletApiOperation.GetTransactionById, {
- transactionId: preparePayResp.transactionId,
- });
-
- t.assertTrue(payTx.type === TransactionType.Payment);
- t.assertTrue(payTx.info != null);
-
- const contractTermsHash = payTx.info.contractTermsHash;
- const proposalTransactionId = preparePayResp.transactionId;
-
- // claimed, unpaid, access with wrong h_contract
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const hcWrong = encodeCrock(getRandomBytes(64));
- url.searchParams.set("h_contract", hcWrong);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // claimed, unpaid, access with wrong claim token
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const ctWrong = encodeCrock(getRandomBytes(16));
- url.searchParams.set("token", ctWrong);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // claimed, unpaid, no claim token
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- // claimed, unpaid, access with correct contract terms hash
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("h_contract", contractTermsHash);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- // claimed, unpaid, access without credentials
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- // No credentials, but the order doesn't require a claim token.
- // This effectively means that the order ID is already considered
- // enough authentication, at least to check for the basic order status
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- const confirmPayRes = await walletClient.call(WalletApiOperation.ConfirmPay, {
- transactionId: proposalTransactionId,
- choiceIndex: 0,
- });
-
- t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
+ t.assertDeepEqual(paidHtmlResp.status, 200);
- // paid, access without credentials
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 200);
- }
-
- // paid, access with wrong h_contract
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const hcWrong = encodeCrock(getRandomBytes(64));
- url.searchParams.set("h_contract", hcWrong);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // paid, access with wrong claim token
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const ctWrong = encodeCrock(getRandomBytes(16));
- url.searchParams.set("token", ctWrong);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // paid, access with correct h_contract
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("h_contract", contractTermsHash);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 200);
- }
-
- // paid, JSON
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 200);
- const respFulfillmentUrl = r.fulfillment_url;
- t.assertDeepEqual(respFulfillmentUrl, "https://example.com/article42");
- }
-
- // paid, HTML
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.fetch(url.href, {
- headers: { Accept: "text/html" },
- });
- t.assertDeepEqual(httpResp.status, 200);
- }
-
- const confirmPayRes2 = await walletClient.call(
+ const confirmPayWithSession = await walletClient.call(
WalletApiOperation.ConfirmPay,
- {
- transactionId: proposalTransactionId,
- sessionId: sessionId,
- },
+ { transactionId: preparePayResp.transactionId, sessionId },
);
+ t.assertDeepEqual(confirmPayWithSession.type, ConfirmPayResultType.Done);
- t.assertTrue(confirmPayRes2.type === ConfirmPayResultType.Done);
-
- // Create another order with identical fulfillment URL to test the "already paid" flow
- const alreadyPaidOrderResp = succeedOrThrow(
- await merchantClient.createOrder(merchantAccessToken, {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
- }),
+ const alreadyPaidOrder = succeedOrThrow(await createOrder());
+ const alreadyPaidUrl = () => {
+ const url = new URL(
+ `orders/${alreadyPaidOrder.order_id}`,
+ c.merchantBaseUrl,
+ );
+ if (alreadyPaidOrder.token) {
+ url.searchParams.set("token", alreadyPaidOrder.token);
+ }
+ return url;
+ };
+ await getJson(t, alreadyPaidUrl(), 402);
+ const sessionUrl = alreadyPaidUrl();
+ sessionUrl.searchParams.set("session_id", sessionId);
+ const alreadyPaidBody = await getJson(t, sessionUrl, 402);
+ t.assertDeepEqual(alreadyPaidBody.already_paid_order_id, orderId);
+
+ const sessionHtmlResp = await harnessHttpLib.fetch(sessionUrl.href, {
+ headers: { Accept: "text/html" },
+ redirect: "manual",
+ });
+ t.assertDeepEqual(sessionHtmlResp.status, 302);
+ t.assertDeepEqual(
+ sessionHtmlResp.headers.get("Location"),
+ "https://example.com/article42",
);
-
- const apOrderId = alreadyPaidOrderResp.order_id;
- const apToken = alreadyPaidOrderResp.token;
- t.assertTrue(!!apToken);
-
- {
- const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);
- url.searchParams.set("token", apToken);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- // Check for already paid session ID, JSON
- {
- const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);
- url.searchParams.set("token", apToken);
- url.searchParams.set("session_id", sessionId);
- const httpResp = await httpLib.fetch(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- const alreadyPaidOrderId = r.already_paid_order_id;
- t.assertDeepEqual(alreadyPaidOrderId, orderId);
- }
-
- // Check for already paid session ID, HTML
- {
- const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);
- url.searchParams.set("token", apToken);
- url.searchParams.set("session_id", sessionId);
- const httpResp = await httpLib.fetch(url.href, {
- headers: { Accept: "text/html" },
- redirect: "manual",
- });
- t.assertDeepEqual(httpResp.status, 302);
- const location = httpResp.headers.get("Location");
- console.log("location header:", location);
- t.assertDeepEqual(location, "https://example.com/article42");
- }
}
-/**
- * Checks for the /orders/{id} endpoint of the merchant.
- *
- * The tests here should exercise all code paths in the executable
- * specification of the endpoint.
- */
+/** Exercise every public-order endpoint branch for both token policies. */
export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {
const { bankClient, exchange, merchant, merchantAdminAccessToken } =
await createSimpleTestkudosEnvironmentV3(t);
-
- // Base URL for the default instance.
const merchantBaseUrl = merchant.makeInstanceBaseUrl();
- {
- const httpResp = await httpLib.fetch(
- new URL("config", merchantBaseUrl).href,
- );
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(r.currency, "TESTKUDOS");
- }
-
- {
- const httpResp = await httpLib.fetch(
- new URL("orders/foo", merchantBaseUrl).href,
- );
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 404);
- // FIXME: also check Taler error code
- }
-
- {
- const httpResp = await httpLib.fetch(
- new URL("orders/foo", merchantBaseUrl).href,
- {
- headers: {
- Accept: "text/html",
- },
- },
- );
- const r = await httpResp.text();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 404);
- // FIXME: also check Taler error code
- }
+ const configResp = await harnessHttpLib.fetch(
+ new URL("config", merchantBaseUrl).href,
+ );
+ const config = await configResp.json();
+ t.assertDeepEqual(config.currency, "TESTKUDOS");
- await testWithClaimToken(t, {
- merchant,
- merchantBaseUrl,
- exchange,
- bankClient,
- merchantAccessToken: merchantAdminAccessToken,
- });
+ const missingJsonResp = await harnessHttpLib.fetch(
+ new URL("orders/foo", merchantBaseUrl).href,
+ );
+ await missingJsonResp.json();
+ t.assertDeepEqual(missingJsonResp.status, 404);
+ const missingHtmlResp = await harnessHttpLib.fetch(
+ new URL("orders/foo", merchantBaseUrl).href,
+ { headers: { Accept: "text/html" } },
+ );
+ await missingHtmlResp.text();
+ t.assertDeepEqual(missingHtmlResp.status, 404);
- await testWithoutClaimToken(t, {
+ const context = {
merchant,
merchantBaseUrl,
exchange,
bankClient,
merchantAccessToken: merchantAdminAccessToken,
- });
+ };
+ await testPublicOrderScenario(t, context, true);
+ await testPublicOrderScenario(t, context, false);
}
runMerchantSpecPublicOrdersTest.suites = ["merchant"];
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -95,7 +95,6 @@ import { runKycFailRecoverSimpleTest } from "./test-kyc-fail-recover-simple.js";
import { runKycFormBadMeasureTest } from "./test-kyc-form-bad-measure.js";
import { runKycFormCompressionTest } from "./test-kyc-form-compression.js";
import { runKycFormValidationTest } from "./test-kyc-form-validation.js";
-import { runKycFormWithdrawalTest } from "./test-kyc-form-withdrawal.js";
import { runKycMerchantActivateBankAccountTest } from "./test-kyc-merchant-activate-bank-account.js";
import { runKycMerchantAggregateTest } from "./test-kyc-merchant-aggregate.js";
import { runKycMerchantDepositFormTest } from "./test-kyc-merchant-deposit-form.js";
@@ -107,7 +106,6 @@ import { runKycNewMeasuresProgTest } from "./test-kyc-new-measures-prog.js";
import { runKycPeerPullTest } from "./test-kyc-peer-pull.js";
import { runKycPeerPushTest } from "./test-kyc-peer-push.js";
import { runKycSkipExpirationTest } from "./test-kyc-skip-expiration.js";
-import { runKycThresholdWithdrawalTest } from "./test-kyc-threshold-withdrawal.js";
import { runKycTwoFormsTest } from "./test-kyc-two-forms.js";
import { runKycWalletDepositAbortTest } from "./test-kyc-wallet-deposit-abort.js";
import { runKycWithdrawalVerbotenTest } from "./test-kyc-withdrawal-verboten.js";
@@ -131,9 +129,7 @@ import { runMerchantPaytoReuseTest } from "./test-merchant-payto-reuse.js";
import { runMerchantRefundApiTest } from "./test-merchant-refund-api.js";
import { runMerchantRefundFeesTest } from "./test-merchant-refund-fees.js";
import { runMerchantReportsTest } from "./test-merchant-reports.js";
-import { runMerchantSelfProvisionActivationTwoBankAccountsTest } from "./test-merchant-self-provision-activation-two-bank-account.js";
import { runMerchantSelfProvisionActivationTest } from "./test-merchant-self-provision-activation.js";
-import { runMerchantSelfProvisionCasingTest } from "./test-merchant-self-provision-casing.js";
import { runMerchantSelfProvisionForgotPasswordTest } from "./test-merchant-self-provision-forgot-password.js";
import { runMerchantSelfProvisionInactiveAccountPermissionsTest } from "./test-merchant-self-provision-inactive-account-permissions.js";
import { runMerchantSpecPublicOrdersTest } from "./test-merchant-spec-public-orders.js";
@@ -392,18 +388,15 @@ const allTests: TestMainFunction[] = [
runExchangeMasterPubChangeTest,
runMerchantCategoriesTest,
runMerchantSelfProvisionActivationTest,
- runMerchantSelfProvisionActivationTwoBankAccountsTest,
runWebMerchantLoginTest,
runMerchantSelfProvisionForgotPasswordTest,
runMerchantSelfProvisionInactiveAccountPermissionsTest,
runWithdrawalExternalTest,
runWithdrawalIdempotentTest,
- runKycThresholdWithdrawalTest,
runKycExchangeWalletTest,
runKycPeerPushTest,
runKycPeerPullTest,
runKycDepositAggregateTest,
- runKycFormWithdrawalTest,
runKycBalanceWithdrawalTest,
runKycNewMeasureTest,
runKycSkipExpirationTest,
@@ -502,7 +495,6 @@ const allTests: TestMainFunction[] = [
runTopsNexusBasicTest,
runTopsNexusSwtTest,
runTopsMerchantSwtKycauthTest,
- runMerchantSelfProvisionCasingTest,
runTopsMerchantSimpleKycauthTest,
runMerchantOrderListingTest,
];