taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 9a299f4e4e60df79eeaf96724ef79d016350a0b7
parent b048d0ea9b9378b4802611de548129fbb1b996ac
Author: Florian Dold <florian@dold.me>
Date:   Fri, 22 May 2026 21:40:10 +0200

harness: add (incomplete!) test for payto reuse between wallet/merchant

Diffstat:
Apackages/taler-harness/src/integrationtests/test-kyc-merchant-wallet-reuse.ts | 243+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/taler-harness/src/integrationtests/testrunner.ts | 2++
2 files changed, 245 insertions(+), 0 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-kyc-merchant-wallet-reuse.ts b/packages/taler-harness/src/integrationtests/test-kyc-merchant-wallet-reuse.ts @@ -0,0 +1,243 @@ +/* + This file is part of GNU Taler + (C) 2024 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 { + AbsoluteTime, + AccessToken, + Duration, + j2s, + Logger, + succeedOrThrow, + TalerExchangeHttpClient, + TalerMerchantInstanceHttpClient, + TalerProtocolDuration, + TransactionMajorState, + TransactionMinorState, + TransactionType, +} from "@gnu-taler/taler-util"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { withdrawViaBankV3 } from "../harness/environments.js"; +import { startFakeChallenger } from "../harness/fake-challenger.js"; +import { GlobalTestState, harnessHttpLib, waitMs } from "../harness/harness.js"; +import { createTopsEnvironment } from "../harness/tops.js"; + +const logger = new Logger(`test-kyc-merchant-deposit.ts`); + +export async function runKycMerchantWalletReuseTest(t: GlobalTestState) { + // Set up test environment + + const { + walletClient, + bankClient, + exchange, + exchangeApi, + officerAcc, + merchant, + merchantAdminAccessToken, + wireGatewayApi, + } = await createTopsEnvironment(t); + + const challenger = await startFakeChallenger({ + port: 6001, + addressType: "postal-ch", + }); + + const exchangeClient = new TalerExchangeHttpClient(exchange.baseUrl, { + httpClient: harnessHttpLib, + }); + + t.logStep("starting withdrawal"); + + // Withdrawal below threshold succeeds! + const wres = await withdrawViaBankV3(t, { + amount: "CHF:20", + bankClient, + exchange, + walletClient, + }); + + await wres.withdrawalFinishedCond; + + t.logStep("withdrawal done"); + + const peerResp = await walletClient.call( + WalletApiOperation.InitiatePeerPushDebit, + { + partialContractTerms: { + amount: "CHF:5", + purse_expiration: AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.addDuration( + AbsoluteTime.now(), + Duration.fromSpec({ minutes: 5 }), + ), + ), + summary: "Test", + }, + }, + ); + + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: peerResp.transactionId, + txState: { + major: TransactionMajorState.Pending, + minor: TransactionMinorState.Ready, + }, + }); + + t.logStep("p2p ready"); + + const pushDebitTxDet = await walletClient.call( + WalletApiOperation.GetTransactionById, + { + transactionId: peerResp.transactionId, + }, + ); + t.assertDeepEqual(pushDebitTxDet.type, TransactionType.PeerPushDebit); + const talerUri = pushDebitTxDet.talerUri; + t.assertTrue(typeof talerUri === "string"); + + const prepareResp = await walletClient.call( + WalletApiOperation.PreparePeerPushCredit, + { + talerUri, + }, + ); + + await walletClient.call(WalletApiOperation.ConfirmPeerPushCredit, { + transactionId: prepareResp.transactionId, + }); + + t.logStep("p2p confirmed"); + + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepareResp.transactionId, + timeout: { seconds: 10 }, + txState: { + major: TransactionMajorState.Pending, + minor: TransactionMinorState.KycRequired, + }, + }); + + t.logStep("p2p merge kyc required"); + + const pushCreditTxDet = await walletClient.call( + WalletApiOperation.GetTransactionById, + { + transactionId: prepareResp.transactionId, + }, + ); + + const paytoHash = pushCreditTxDet.kycPaytoHash; + t.assertTrue(!!paytoHash); + + const accessToken = pushCreditTxDet.kycAccessToken; + + t.assertTrue(typeof accessToken === "string"); + + const infoResp = await exchangeApi.checkKycInfo(accessToken as AccessToken); + + console.log(j2s(infoResp)); + + t.assertDeepEqual(infoResp.case, "ok"); + + const myId = infoResp.body.requirements.find((x) => + x.description.includes("TAN letter"), + )?.id; + t.assertTrue(!!myId); + + const startResp = succeedOrThrow( + await exchangeApi.startExternalKycProcess(myId, {}), + ); + console.log(`start resp`, j2s(startResp)); + + let challengerRedirectUrl = startResp.redirect_url; + + const resp = await harnessHttpLib.fetch(challengerRedirectUrl); + const respJson = await resp.json(); + console.log(`challenger resp: ${j2s(respJson)}`); + + const nonce = respJson.nonce; + t.assertTrue(typeof nonce === "string"); + const proofRedirectUrl = respJson.redirect_url; + + challenger.fakeVerification(nonce, { + CONTACT_NAME: "Richard Stallman", + ADDRESS_LINES: "Bundesgasse 1\n1234 Bern", + }); + + console.log("nonce", nonce); + console.log("proof redirect URL", proofRedirectUrl); + + const proofResp = await harnessHttpLib.fetch(proofRedirectUrl, { + redirect: "manual", + }); + console.log("proof status:", proofResp.status); + if (proofResp.status === 404) { + console.log(j2s(await proofResp.text())); + } + t.assertDeepEqual(proofResp.status, 303); + + const infoResp2 = await exchangeApi.checkKycInfo(accessToken as AccessToken); + + console.log(j2s(infoResp2)); + + if (infoResp2.case === "ok" && infoResp2.body.requirements.length != 0) { + t.fail("requirements may not include ToS after KYC for P2P"); + } + + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepareResp.transactionId, + txState: { + major: TransactionMajorState.Done, + }, + }); + + { + const { accessToken: mt1Tok } = await merchant.addInstanceWithWireAccount( + { + id: "mt1", + name: "MerchantTest Instance", + paytoUris: [wres.accountPaytoUri], + defaultWireTransferDelay: TalerProtocolDuration.fromSpec({ + minutes: 1, + }), + }, + { adminAccessToken: merchantAdminAccessToken }, + ); + + const merchantApi = new TalerMerchantInstanceHttpClient( + merchant.makeInstanceBaseUrl("mt1"), + harnessHttpLib, + ); + + while (true) { + const st = await merchantApi.getCurrentInstanceKycStatus(mt1Tok); + + console.log(j2s(st)); + + if (st.case != "ok") { + await waitMs(1000); + } + + + } + } +} + +runKycMerchantWalletReuseTest.suites = ["merchant", "kyc"]; diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts @@ -94,6 +94,7 @@ import { runKycMerchantAggregateTest } from "./test-kyc-merchant-aggregate.js"; import { runKycMerchantDepositFormTest } from "./test-kyc-merchant-deposit-form.js"; import { runKycMerchantDepositRewriteTest } from "./test-kyc-merchant-deposit-rewrite.js"; import { runKycMerchantDepositTest } from "./test-kyc-merchant-deposit.js"; +import { runKycMerchantWalletReuseTest } from "./test-kyc-merchant-wallet-reuse.js"; import { runKycNewMeasureTest } from "./test-kyc-new-measure.js"; import { runKycNewMeasuresProgTest } from "./test-kyc-new-measures-prog.js"; import { runKycPeerPullTest } from "./test-kyc-peer-pull.js"; @@ -436,6 +437,7 @@ const allTests: TestMainFunction[] = [ runPaivanaTest, runPaivanaRepurchaseTest, runKycFormValidationTest, + runKycMerchantWalletReuseTest, ]; export interface TestRunSpec {