commit c7adf74c9dd6b9b74588b37cbc6fbbcadc7c6a2e
parent 18a27cc4cf1b9582fe9ef5f46bcec0a7c2482e0f
Author: Florian Dold <florian@dold.me>
Date: Wed, 22 Jul 2026 12:36:55 +0200
harness: new test for SIMPLE kyc auth
Diffstat:
2 files changed, 182 insertions(+), 0 deletions(-)
diff --git a/packages/taler-harness/src/integrationtests/test-tops-merchant-simple-kycauth.ts b/packages/taler-harness/src/integrationtests/test-tops-merchant-simple-kycauth.ts
@@ -0,0 +1,180 @@
+/*
+ This file is part of GNU Taler
+ (C) 2025 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 {
+ IbanString,
+ j2s,
+ Logger,
+ MerchantAccountKycStatus,
+ Paytos,
+ succeedOrThrow,
+ TalerMerchantInstanceHttpClient,
+ TalerProtocolDuration,
+} from "@gnu-taler/taler-util";
+import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
+import {
+ ExchangeService,
+ GlobalTestState,
+ LibeufinNexusService,
+ MerchantService,
+ NexusBankAccountInfo,
+ setupDb,
+} from "../harness/harness.js";
+import { topsKycRulesConf, topsProvidersTestConf } from "../harness/tops.js";
+
+const logger = new Logger("test-tops-merchant-kycauths.ts");
+
+/**
+ * Test short wire transfers in an exchange setup that simulates
+ * the Taler Operations CH exchange, using libeufin-nexus
+ * as the taler wire gateway API.
+ *
+ * This test exercises the .../kycauths endpoint of the merchant backend.
+ */
+export async function runTopsMerchantSimpleKycauthTest(t: GlobalTestState) {
+ const db = await setupDb(t);
+
+ let coinConfig: CoinConfig[];
+ coinConfig = defaultCoinConfig.map((x) => x("CHF"));
+
+ const bankAccountInfo: NexusBankAccountInfo = {
+ // Random IBAN generated via "libeufin-nexus testing iban gen --country CH"
+ iban: "CH7347363QVFHHFR8BWWB",
+ // PostFinance BIC
+ bic: "POFICHBEXXX",
+ name: "Harness Test Exchange",
+ qrIban: "CH1130000001166556117",
+ };
+
+ const nexus = await LibeufinNexusService.create(t, {
+ currency: "CHF",
+ database: db.connStr,
+ httpPort: 8085,
+ bankAccountInfo,
+ });
+
+ const exchangePayto = Paytos.toFullString(
+ Paytos.createIban(bankAccountInfo.qrIban as IbanString, undefined, {
+ "receiver-name": bankAccountInfo.name,
+ }),
+ );
+
+ await nexus.dbinit();
+
+ await nexus.start();
+
+ const exchange = ExchangeService.create(t, {
+ name: "testexchange-1",
+ currency: "CHF",
+ httpPort: 8081,
+ database: db.connStr,
+ // FIXME: This is a terrible way to configure the exchange, should be moved into config.
+ extraProcEnv: {
+ EXCHANGE_AML_PROGRAM_TOPS_ENABLE_DEPOSITS_TOS_NAME: "v1",
+ EXCHANGE_AML_PROGRAM_TOPS_ENABLE_DEPOSITS_THRESHOLD: "CHF:0",
+ EXCHANGE_AML_PROGRAM_TOPS_POSTAL_CHECK_COUNTRY_REGEX: "ch|CH|Ch",
+ },
+ });
+
+ exchange.addBankAccount("nexusacct", {
+ accountPaytoUri: exchangePayto,
+ wireGatewayApiBaseUrl: nexus.wireGatewayApiBaseUrl,
+ wireGatewayAuth: {
+ type: "basic",
+ username: "exchange-test",
+ password: "exchange-test",
+ },
+ });
+
+ exchange.addCoinConfigList(coinConfig);
+
+ await exchange.modifyConfig(async (config) => {
+ config.loadFromString(topsKycRulesConf);
+ config.loadFromString(topsProvidersTestConf);
+ });
+
+ await exchange.start();
+
+ const merchantIban = "CH4810303KL83V1QZ05T7";
+
+ const merchant = await MerchantService.create(t, {
+ name: "testmerchant-1",
+ httpPort: 8083,
+ database: db.connStr,
+ });
+
+ merchant.addExchange(exchange);
+
+ await merchant.start();
+
+ const merchantPayto = Paytos.toFullString(
+ Paytos.createIban(merchantIban as IbanString, undefined, {
+ "receiver-name": "Merchant Test",
+ }),
+ );
+
+ const { accessToken: merchantAdminAccessToken } =
+ await merchant.addInstanceWithWireAccount({
+ id: "admin",
+ name: "Default Instance",
+ paytoUris: [merchantPayto],
+ defaultWireTransferDelay: TalerProtocolDuration.fromSpec({ minutes: 1 }),
+ });
+
+ const merchantClient = new TalerMerchantInstanceHttpClient(
+ merchant.makeInstanceBaseUrl(),
+ );
+ // Do KYC auth transfer
+
+ const kycStatus = await merchantClient.getCurrentInstanceKycStatus(
+ merchantAdminAccessToken,
+ {
+ longpoll: {
+ type: "state-enter",
+ status: MerchantAccountKycStatus.KYC_WIRE_REQUIRED,
+ timeout: 10000,
+ },
+ },
+ );
+ t.assertTrue(kycStatus.type === "ok");
+ console.log(`kyc status: ${j2s(kycStatus)}`);
+
+ t.assertDeepEqual(kycStatus.body.kyc_data[0].status, "kyc-wire-required");
+
+ const kycData = kycStatus.body.kyc_data[0];
+
+ const kycResp = succeedOrThrow(
+ await merchantClient.getInstancePrivateAccountKycauth(
+ merchantAdminAccessToken,
+ {
+ exchangeBaseUrl: kycData.exchange_url,
+ wireAccountHash: kycData.h_wire,
+ },
+ ),
+ );
+
+ console.log(j2s(kycResp));
+
+ t.assertTrue(
+ kycResp.wire_instructions.length > 0,
+ "expected at least one wire instruction",
+ );
+}
+
+runTopsMerchantSimpleKycauthTest.suites = ["tops", "libeufin"];
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -173,6 +173,7 @@ import { runTopsAmlLegiTest } from "./test-tops-aml-legi.js";
import { runTopsAmlMeasuresTest } from "./test-tops-aml-measures.js";
import { runTopsAmlPdfTest } from "./test-tops-aml-pdf.js";
import { runTopsChallengerTwiceTest } from "./test-tops-challenger-twice.js";
+import { runTopsMerchantSimpleKycauthTest } from "./test-tops-merchant-simple-kycauth.js";
import { runTopsMerchantSwtKycauthTest } from "./test-tops-merchant-swt-kycauth.js";
import { runTopsMerchantTosTest } from "./test-tops-merchant-tos.js";
import { runTopsNexusBasicTest } from "./test-tops-nexus-basic.js";
@@ -444,6 +445,7 @@ const allTests: TestMainFunction[] = [
runTopsNexusSwtTest,
runTopsMerchantSwtKycauthTest,
runMerchantSelfProvisionCasingTest,
+ runTopsMerchantSimpleKycauthTest,
];
export interface TestRunSpec {