gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: harness: modernize some tests


From: gnunet
Subject: [taler-wallet-core] branch master updated: harness: modernize some tests
Date: Thu, 24 Aug 2023 18:30:06 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 6cc3fb3d0 harness: modernize some tests
6cc3fb3d0 is described below

commit 6cc3fb3d0466e89b67be271009a2fc95f3ed41ca
Author: Florian Dold <florian@dold.me>
AuthorDate: Thu Aug 24 18:29:54 2023 +0200

    harness: modernize some tests
---
 packages/taler-harness/src/harness/harness.ts      |  87 +++++++-----
 packages/taler-harness/src/harness/helpers.ts      |  40 ++++--
 packages/taler-harness/src/index.ts                |  12 +-
 .../test-age-restrictions-mixed-merchant.ts        |  53 ++++----
 .../src/integrationtests/test-bank-api.ts          |  21 ++-
 .../src/integrationtests/test-claim-loop.ts        |  21 +--
 .../src/integrationtests/test-denom-unoffered.ts   |  24 ++--
 .../src/integrationtests/test-exchange-deposit.ts  |   7 +-
 .../src/integrationtests/test-forced-selection.ts  |  16 +--
 .../test-libeufin-api-bankaccount.ts               |   1 -
 .../test-libeufin-nexus-balance.ts                 |   1 -
 .../integrationtests/test-merchant-longpolling.ts  |  31 +++--
 .../src/integrationtests/test-payment-abort.ts     |   1 -
 .../src/integrationtests/test-payment-claim.ts     |  24 ++--
 .../integrationtests/test-payment-forgettable.ts   |  24 ++--
 .../integrationtests/test-payment-idempotency.ts   |  29 ++--
 .../src/integrationtests/test-payment-share.ts     |  14 +-
 .../src/integrationtests/test-payment-template.ts  |  20 ++-
 .../src/integrationtests/test-payment-zero.ts      |  22 +--
 .../src/integrationtests/test-paywall-flow.ts      |  19 ++-
 .../src/integrationtests/test-refund-auto.ts       |  30 ++--
 .../integrationtests/test-refund-incremental.ts    |  68 +++++-----
 .../src/integrationtests/test-tipping.ts           |  32 +++--
 .../src/integrationtests/test-tos-format.ts        |   1 -
 .../integrationtests/test-wallet-backup-basic.ts   |  36 ++---
 .../test-wallet-backup-doublespend.ts              |  35 ++---
 .../src/integrationtests/test-wallet-balance.ts    |  81 ++---------
 .../src/integrationtests/test-wallet-dbless.ts     |   4 +-
 .../integrationtests/test-wallet-notifications.ts  |  23 ++--
 .../integrationtests/test-withdrawal-abort-bank.ts |  18 +--
 .../test-withdrawal-bank-integrated.ts             |  20 +--
 .../src/integrationtests/test-withdrawal-fees.ts   |  15 +-
 .../src/integrationtests/test-withdrawal-huge.ts   |   9 +-
 .../src/integrationtests/test-withdrawal-manual.ts |  27 ++--
 .../src/integrationtests/testrunner.ts             |   2 -
 packages/taler-util/src/talerconfig.ts             |  96 +++++++++----
 packages/taler-wallet-core/src/bank-api-client.ts  | 151 ++++++++++++++++++---
 37 files changed, 664 insertions(+), 451 deletions(-)

diff --git a/packages/taler-harness/src/harness/harness.ts 
b/packages/taler-harness/src/harness/harness.ts
index 8f1f3f452..926a0c93b 100644
--- a/packages/taler-harness/src/harness/harness.ts
+++ b/packages/taler-harness/src/harness/harness.ts
@@ -563,7 +563,7 @@ export async function pingProc(
       return;
     } catch (e: any) {
       logger.warn(`service ${serviceName} not ready:`, e.toString());
-      logger.info(`waiting ${nextDelay}ms`);
+      logger.info(`waiting ${nextDelay}ms on ${serviceName}`);
       await delayMs(nextDelay);
       nextDelay = backoffIncrement(nextDelay);
     }
@@ -905,7 +905,7 @@ export class FakebankService
     config.setString("bank", "max_debt", bc.maxDebt ?? `${bc.currency}:100`);
     config.setString("bank", "ram_limit", `${1024}`);
     const cfgFilename = testDir + "/bank.conf";
-    config.write(cfgFilename);
+    config.write(cfgFilename, { excludeDefaults: true });
 
     return new FakebankService(gc, bc, cfgFilename);
   }
@@ -934,7 +934,7 @@ export class FakebankService
     }
     const config = Configuration.load(this.configFile);
     config.setString("bank", "suggested_exchange", e.baseUrl);
-    config.write(this.configFile);
+    config.write(this.configFile, { excludeDefaults: true });
   }
 
   get baseUrl(): string {
@@ -1037,7 +1037,9 @@ export class ExchangeService implements 
ExchangeServiceInterface {
       name: exchangeName,
       roundUnit: config.getString("taler", "currency_round_unit").required(),
     };
-    const privFile = config.getPath("exchange", "master_priv_file").required();
+    const privFile = config
+      .getPath("exchange-offline", "master_priv_file")
+      .required();
     const eddsaPriv = fs.readFileSync(privFile);
     const keyPair: EddsaKeyPair = {
       eddsaPriv,
@@ -1135,7 +1137,7 @@ export class ExchangeService implements 
ExchangeServiceInterface {
   changeConfig(f: (config: Configuration) => void) {
     const config = Configuration.load(this.configFilename);
     f(config);
-    config.write(this.configFilename);
+    config.write(this.configFilename, { excludeDefaults: true });
   }
 
   static create(gc: GlobalTestState, e: ExchangeConfig) {
@@ -1184,10 +1186,16 @@ export class ExchangeService implements 
ExchangeServiceInterface {
 
     fs.mkdirSync(path.dirname(masterPrivFile), { recursive: true });
 
+    if (fs.existsSync(masterPrivFile)) {
+      throw new Error(
+        "master priv file already exists, can't create new exchange config",
+      );
+    }
+
     fs.writeFileSync(masterPrivFile, Buffer.from(exchangeMasterKey.eddsaPriv));
 
     const cfgFilename = testDir + `/exchange-${e.name}.conf`;
-    config.write(cfgFilename);
+    config.write(cfgFilename, { excludeDefaults: true });
     return new ExchangeService(gc, e, cfgFilename, exchangeMasterKey);
   }
 
@@ -1196,13 +1204,13 @@ export class ExchangeService implements 
ExchangeServiceInterface {
     offeredCoins.forEach((cc) =>
       setCoin(config, cc(this.exchangeConfig.currency)),
     );
-    config.write(this.configFilename);
+    config.write(this.configFilename, { excludeDefaults: true });
   }
 
   addCoinConfigList(ccs: CoinConfig[]) {
     const config = Configuration.load(this.configFilename);
     ccs.forEach((cc) => setCoin(config, cc));
-    config.write(this.configFilename);
+    config.write(this.configFilename, { excludeDefaults: true });
   }
 
   enableAgeRestrictions(maskStr: string) {
@@ -1213,7 +1221,7 @@ export class ExchangeService implements 
ExchangeServiceInterface {
       "age_groups",
       maskStr,
     );
-    config.write(this.configFilename);
+    config.write(this.configFilename, { excludeDefaults: true });
   }
 
   get masterPub() {
@@ -1234,7 +1242,7 @@ export class ExchangeService implements 
ExchangeServiceInterface {
   ): Promise<void> {
     const config = Configuration.load(this.configFilename);
     await f(config);
-    config.write(this.configFilename);
+    config.write(this.configFilename, { excludeDefaults: true });
   }
 
   async addBankAccount(
@@ -1274,7 +1282,7 @@ export class ExchangeService implements 
ExchangeServiceInterface {
       "password",
       exchangeBankAccount.accountPassword,
     );
-    config.write(this.configFilename);
+    config.write(this.configFilename, { excludeDefaults: true });
   }
 
   exchangeHttpProc: ProcessWrapper | undefined;
@@ -1535,19 +1543,25 @@ export class ExchangeService implements 
ExchangeServiceInterface {
     );
   }
 
-  async start(opts: { skipSetup?: boolean } = {}): Promise<void> {
+  async dbinit() {
+    await sh(
+      this.globalState,
+      "exchange-dbinit",
+      `taler-exchange-dbinit -c "${this.configFilename}"`,
+    );
+  }
+
+  async start(
+    opts: { skipDbinit?: boolean; skipKeyup?: boolean } = {},
+  ): Promise<void> {
     if (this.isRunning()) {
       throw Error("exchange is already running");
     }
 
-    const skipSetup = opts.skipSetup ?? false;
+    const skipDbinit = opts.skipDbinit ?? false;
 
-    if (!skipSetup) {
-      await sh(
-        this.globalState,
-        "exchange-dbinit",
-        `taler-exchange-dbinit -c "${this.configFilename}"`,
-      );
+    if (!skipDbinit) {
+      await this.dbinit();
     }
 
     this.helperCryptoEddsaProc = this.globalState.spawnService(
@@ -1579,7 +1593,10 @@ export class ExchangeService implements 
ExchangeServiceInterface {
     );
 
     await this.pingUntilAvailable();
-    if (!skipSetup) {
+
+    const skipKeyup = opts.skipKeyup ?? false;
+
+    if (!skipKeyup) {
       await this.keyup();
     } else {
       logger.info("skipping keyup");
@@ -1921,16 +1938,23 @@ export class MerchantService implements 
MerchantServiceInterface {
     }
   }
 
-  async start(opts: { skipSetup?: boolean } = {}): Promise<void> {
-    const skipSetup = opts.skipSetup ?? false;
+  async dbinit() {
+    await runCommand(
+      this.globalState,
+      "merchant-dbinit",
+      "taler-merchant-dbinit",
+      ["-c", this.configFilename],
+    );
+  }
+
+  /**
+   * Start the merchant,
+   */
+  async start(opts: { skipDbinit?: boolean } = {}): Promise<void> {
+    const skipSetup = opts.skipDbinit ?? false;
 
     if (!skipSetup) {
-      await runCommand(
-        this.globalState,
-        "merchant-dbinit",
-        "taler-merchant-dbinit",
-        ["-c", this.configFilename],
-      );
+      await this.dbinit();
     }
 
     this.proc = this.globalState.spawnService(
@@ -1964,7 +1988,7 @@ export class MerchantService implements 
MerchantServiceInterface {
       "${TALER_DATA_HOME}/merchant/merchant.priv",
     );
     config.setString("merchantdb-postgres", "config", mc.database);
-    config.write(cfgFilename);
+    config.write(cfgFilename, { excludeDefaults: true });
 
     return new MerchantService(gc, mc, cfgFilename);
   }
@@ -1982,7 +2006,7 @@ export class MerchantService implements 
MerchantServiceInterface {
       this.merchantConfig.currency,
     );
     config.setString(`merchant-exchange-${e.name}`, "master_key", e.masterPub);
-    config.write(this.configFilename);
+    config.write(this.configFilename, { excludeDefaults: true });
   }
 
   async addDefaultInstance(): Promise<void> {
@@ -2303,7 +2327,8 @@ export class WalletService {
       try {
         await tryUnixConnect(this.socketPath);
       } catch (e) {
-        logger.info(`connection attempt failed: ${e}`);
+        logger.info(`wallet connection attempt failed: ${e}`);
+        logger.info(`waiting on wallet for ${nextDelay}ms`);
         await delayMs(nextDelay);
         nextDelay = backoffIncrement(nextDelay);
         continue;
diff --git a/packages/taler-harness/src/harness/helpers.ts 
b/packages/taler-harness/src/harness/helpers.ts
index 932854a1e..d41ffdd00 100644
--- a/packages/taler-harness/src/harness/helpers.ts
+++ b/packages/taler-harness/src/harness/helpers.ts
@@ -230,6 +230,11 @@ export async function useSharedTestkudosEnvironment(t: 
GlobalTestState) {
 
   logger.info(`previous setup done: ${prevSetupDone}`);
 
+
+  // Wallet has longer startup-time and no dependencies,
+  // so we start it rather early.
+  const walletStartProm = createWalletDaemonWithClient(t, { name: "wallet" })
+
   if (fs.existsSync(sharedDir + "/bank.conf")) {
     logger.info("reusing existing bank");
     bank = BankService.fromExistingConfig(t, {
@@ -249,15 +254,19 @@ export async function useSharedTestkudosEnvironment(t: 
GlobalTestState) {
   logger.info("setting up exchange");
 
   const exchangeName = "testexchange-1";
-  const exchangeConfigFilename = sharedDir + `/exchange-${exchangeName}}`;
+  const exchangeConfigFilename = sharedDir + `/exchange-${exchangeName}.conf`;
+
+  logger.info(`exchange config filename: ${exchangeConfigFilename}`);
 
   let exchange: ExchangeService;
 
   if (fs.existsSync(exchangeConfigFilename)) {
+    logger.info("reusing existing exchange config");
     exchange = ExchangeService.fromExistingConfig(t, exchangeName, {
       overridePath: sharedDir,
     });
   } else {
+    logger.info("creating new exchange config");
     exchange = ExchangeService.create(t, {
       name: "testexchange-1",
       currency: "TESTKUDOS",
@@ -305,22 +314,29 @@ export async function useSharedTestkudosEnvironment(t: 
GlobalTestState) {
 
   logger.info("basic setup done, starting services");
 
+  if (!prevSetupDone) {
+    // Must be done sequentially, due to a concurrency
+    // issue in the *-dbinit tools.
+    await exchange.dbinit();
+    await merchant.dbinit();
+  }
+
   const bankStart = async () => {
     await bank.start();
-
     await bank.pingUntilAvailable();
   };
 
   const exchangeStart = async () => {
     await exchange.start({
-      skipSetup: prevSetupDone,
+      skipDbinit: true,
+      skipKeyup: prevSetupDone,
     });
     await exchange.pingUntilAvailable();
   };
 
   const merchStart = async () => {
     await merchant.start({
-      skipSetup: prevSetupDone,
+      skipDbinit: true,
     });
     await merchant.pingUntilAvailable();
 
@@ -345,24 +361,22 @@ export async function useSharedTestkudosEnvironment(t: 
GlobalTestState) {
     }
   };
 
-  const walletStart = async () => {
-    return await createWalletDaemonWithClient(t, { name: "wallet" });
-  };
+  await bankStart()
 
   const res = await Promise.all([
     exchangeStart(),
     merchStart(),
-    bankStart(),
-    walletStart(),
+    undefined,
+    walletStartProm,
   ]);
 
   const walletClient = res[3].walletClient;
   const walletService = res[3].walletService;
 
-  console.log("setup done!");
-
   fs.writeFileSync(sharedDir + "/setup-done", "OK");
 
+  logger.info("setup done!");
+
   return {
     commonDb: db,
     exchange,
@@ -515,7 +529,7 @@ export async function createWalletDaemonWithClient(
   const walletClient = new WalletClient({
     unixPath: walletService.socketPath,
     onNotification(n) {
-      console.log("got notification", n);
+      console.log(`got ${args.name} notification`, n);
       if (args.handleNotification) {
         args.handleNotification(n);
       }
@@ -789,6 +803,8 @@ export async function applyTimeTravel(
 
 /**
  * Make a simple payment and check that it succeeded.
+ * 
+ * @deprecated
  */
 export async function makeTestPayment(
   t: GlobalTestState,
diff --git a/packages/taler-harness/src/index.ts 
b/packages/taler-harness/src/index.ts
index ec99232f5..a172bc9da 100644
--- a/packages/taler-harness/src/index.ts
+++ b/packages/taler-harness/src/index.ts
@@ -208,8 +208,10 @@ deploymentCli
 
     const bankAccessApiClient = new BankAccessApiClient({
       baseUrl: args.tipTopup.bankAccessUrl,
-      username: args.tipTopup.bankAccount,
-      password: args.tipTopup.bankPassword,
+      auth: {
+        username: args.tipTopup.bankAccount,
+        password: args.tipTopup.bankPassword,
+      },
       allowHttp: true,
     });
 
@@ -219,10 +221,12 @@ deploymentCli
 
     console.log("payto URI:", paytoUri);
 
-    const transactions = await bankAccessApiClient.getTransactions();
+    const transactions = await bankAccessApiClient.getTransactions(
+      args.tipTopup.bankAccount,
+    );
     console.log("transactions:", j2s(transactions));
 
-    await bankAccessApiClient.createTransaction({
+    await bankAccessApiClient.createTransaction(args.tipTopup.bankAccount, {
       amount,
       paytoUri,
     });
diff --git 
a/packages/taler-harness/src/integrationtests/test-age-restrictions-mixed-merchant.ts
 
b/packages/taler-harness/src/integrationtests/test-age-restrictions-mixed-merchant.ts
index 8bf71b63d..970acbb72 100644
--- 
a/packages/taler-harness/src/integrationtests/test-age-restrictions-mixed-merchant.ts
+++ 
b/packages/taler-harness/src/integrationtests/test-age-restrictions-mixed-merchant.ts
@@ -17,12 +17,15 @@
 /**
  * Imports.
  */
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { defaultCoinConfig } from "../harness/denomStructures.js";
 import { GlobalTestState, WalletCli } from "../harness/harness.js";
 import {
-  createSimpleTestkudosEnvironment,
-  withdrawViaBank,
   makeTestPayment,
+  createSimpleTestkudosEnvironmentV2,
+  withdrawViaBankV2,
+  makeTestPaymentV2,
+  createWalletDaemonWithClient,
 } from "../harness/helpers.js";
 
 /**
@@ -32,11 +35,11 @@ export async function 
runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) {
   // Set up test environment
 
   const {
-    wallet: walletOne,
+    walletClient: walletOne,
     bank,
     exchange,
     merchant,
-  } = await createSimpleTestkudosEnvironment(
+  } = await createSimpleTestkudosEnvironmentV2(
     t,
     defaultCoinConfig.map((x) => x("TESTKUDOS")),
     {
@@ -45,14 +48,19 @@ export async function 
runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) {
     },
   );
 
-  const walletTwo = new WalletCli(t, "walletTwo");
-  const walletThree = new WalletCli(t, "walletThree");
+  const { walletClient: walletTwo } = await createWalletDaemonWithClient(t, {
+    name: "w2",
+  });
+
+  const { walletClient: walletThree } = await createWalletDaemonWithClient(t, {
+    name: "w3",
+  });
 
   {
-    const wallet = walletOne;
+    const walletClient = walletOne;
 
-    await withdrawViaBank(t, {
-      wallet,
+    await withdrawViaBankV2(t, {
+      walletClient,
       bank,
       exchange,
       amount: "TESTKUDOS:20",
@@ -66,15 +74,16 @@ export async function 
runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) {
       minimum_age: 9,
     };
 
-    await makeTestPayment(t, { wallet, merchant, order });
-    await wallet.runUntilDone();
+    await makeTestPaymentV2(t, { walletClient, merchant, order });
+    await walletClient.call(
+      WalletApiOperation.TestingWaitTransactionsFinal,
+      {},
+    );
   }
 
   {
-    const wallet = walletTwo;
-
-    await withdrawViaBank(t, {
-      wallet,
+    await withdrawViaBankV2(t, {
+      walletClient: walletTwo,
       bank,
       exchange,
       amount: "TESTKUDOS:20",
@@ -87,15 +96,13 @@ export async function 
runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) {
       fulfillment_url: "taler://fulfillment-success/thx",
     };
 
-    await makeTestPayment(t, { wallet, merchant, order });
-    await wallet.runUntilDone();
+    await makeTestPaymentV2(t, { walletClient: walletTwo, merchant, order });
+    await walletTwo.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
   }
 
   {
-    const wallet = walletThree;
-
-    await withdrawViaBank(t, {
-      wallet,
+    await withdrawViaBankV2(t, {
+      walletClient: walletThree,
       bank,
       exchange,
       amount: "TESTKUDOS:20",
@@ -108,8 +115,8 @@ export async function 
runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) {
       minimum_age: 9,
     };
 
-    await makeTestPayment(t, { wallet, merchant, order });
-    await wallet.runUntilDone();
+    await makeTestPaymentV2(t, { walletClient: walletThree, merchant, order });
+    await walletThree.call(WalletApiOperation.TestingWaitTransactionsFinal, 
{});
   }
 }
 
diff --git a/packages/taler-harness/src/integrationtests/test-bank-api.ts 
b/packages/taler-harness/src/integrationtests/test-bank-api.ts
index a0e1c2085..9ac16980b 100644
--- a/packages/taler-harness/src/integrationtests/test-bank-api.ts
+++ b/packages/taler-harness/src/integrationtests/test-bank-api.ts
@@ -17,23 +17,22 @@
 /**
  * Imports.
  */
-import {
-  GlobalTestState,
-  WalletCli,
-  ExchangeService,
-  setupDb,
-  BankService,
-  MerchantService,
-  getPayto,
-} from "../harness/harness.js";
 import { createEddsaKeyPair, encodeCrock } from "@gnu-taler/taler-util";
-import { defaultCoinConfig } from "../harness/denomStructures.js";
 import {
-  BankApi,
   BankAccessApi,
+  BankApi,
   CreditDebitIndicator,
   WireGatewayApiClient,
 } from "@gnu-taler/taler-wallet-core";
+import { defaultCoinConfig } from "../harness/denomStructures.js";
+import {
+  BankService,
+  ExchangeService,
+  GlobalTestState,
+  MerchantService,
+  getPayto,
+  setupDb,
+} from "../harness/harness.js";
 
 /**
  * Run test for basic, bank-integrated withdrawal.
diff --git a/packages/taler-harness/src/integrationtests/test-claim-loop.ts 
b/packages/taler-harness/src/integrationtests/test-claim-loop.ts
index 921391283..32706c28b 100644
--- a/packages/taler-harness/src/integrationtests/test-claim-loop.ts
+++ b/packages/taler-harness/src/integrationtests/test-claim-loop.ts
@@ -17,13 +17,13 @@
 /**
  * Imports.
  */
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { URL } from "url";
 import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
 import {
-  createSimpleTestkudosEnvironment,
-  withdrawViaBank,
+  createSimpleTestkudosEnvironmentV2,
+  withdrawViaBankV2,
 } from "../harness/helpers.js";
-import { URL } from "url";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 
 /**
  * Run test for the merchant's order lifecycle.
@@ -34,10 +34,15 @@ import { WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 export async function runClaimLoopTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, {
+    walletClient,
+    bank,
+    exchange,
+    amount: "TESTKUDOS:20",
+  });
 
   // Set up order.
   const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
@@ -61,7 +66,7 @@ export async function runClaimLoopTest(t: GlobalTestState) {
   // Make wallet claim the unpaid order.
   t.assertTrue(orderStatusBefore.order_status === "unpaid");
   const talerPayUri = orderStatusBefore.taler_pay_uri;
-  await wallet.client.call(WalletApiOperation.PreparePayForUri, {
+  await walletClient.call(WalletApiOperation.PreparePayForUri, {
     talerPayUri,
   });
 
diff --git 
a/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts 
b/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts
index 27c336ea6..1b9b91603 100644
--- a/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts
+++ b/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts
@@ -22,18 +22,20 @@ import { Wallet, WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
 import {
   createSimpleTestkudosEnvironment,
+  createSimpleTestkudosEnvironmentV2,
   withdrawViaBank,
+  withdrawViaBankV2,
 } from "../harness/helpers.js";
 
 export async function runDenomUnofferedTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   // Withdraw digital cash into the wallet.
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: 
"TESTKUDOS:20" });
 
   // Make the exchange forget the denomination.
   // Effectively we completely reset the exchange,
@@ -67,7 +69,7 @@ export async function runDenomUnofferedTest(t: 
GlobalTestState) {
 
   // Make wallet pay for the order
 
-  const preparePayResult = await wallet.client.call(
+  const preparePayResult = await walletClient.call(
     WalletApiOperation.PreparePayForUri,
     {
       talerPayUri: orderStatus.taler_pay_uri,
@@ -78,11 +80,11 @@ export async function runDenomUnofferedTest(t: 
GlobalTestState) {
     preparePayResult.status === PreparePayResultType.PaymentPossible,
   );
 
-  const confirmResp = await wallet.client.call(WalletApiOperation.ConfirmPay, {
+  const confirmResp = await walletClient.call(WalletApiOperation.ConfirmPay, {
     proposalId: preparePayResult.proposalId,
   });
 
-  const tx = await wallet.client.call(WalletApiOperation.GetTransactionById, {
+  const tx = await walletClient.call(WalletApiOperation.GetTransactionById, {
     transactionId: confirmResp.transactionId,
   });
 
@@ -96,21 +98,21 @@ export async function runDenomUnofferedTest(t: 
GlobalTestState) {
     TalerErrorCode.MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND,
   );
 
-  await wallet.client.call(WalletApiOperation.AddExchange, {
+  await walletClient.call(WalletApiOperation.AddExchange, {
     exchangeBaseUrl: exchange.baseUrl,
     forceUpdate: true,
   });
 
-  await wallet.client.call(WalletApiOperation.DeleteTransaction, {
+  await walletClient.call(WalletApiOperation.DeleteTransaction, {
     transactionId: confirmResp.transactionId,
   });
 
   // Now withdrawal should work again.
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: 
"TESTKUDOS:20" });
 
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
-  const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {});
+  const txs = await walletClient.call(WalletApiOperation.GetTransactions, {});
   console.log(JSON.stringify(txs, undefined, 2));
 }
 
diff --git 
a/packages/taler-harness/src/integrationtests/test-exchange-deposit.ts 
b/packages/taler-harness/src/integrationtests/test-exchange-deposit.ts
index 6c0d070d0..05bbbfaa1 100644
--- a/packages/taler-harness/src/integrationtests/test-exchange-deposit.ts
+++ b/packages/taler-harness/src/integrationtests/test-exchange-deposit.ts
@@ -30,14 +30,15 @@ import {
   depositCoin,
   downloadExchangeInfo,
   findDenomOrThrow,
-  refreshCoin,
   SynchronousCryptoWorkerFactoryPlain,
   topupReserveWithDemobank,
   Wallet,
   withdrawCoin,
 } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
+import {
+  createSimpleTestkudosEnvironmentV2,
+} from "../harness/helpers.js";
 
 /**
  * Run test for basic, bank-integrated withdrawal and payment.
@@ -45,7 +46,7 @@ import { createSimpleTestkudosEnvironment } from 
"../harness/helpers.js";
 export async function runExchangeDepositTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { bank, exchange } = await createSimpleTestkudosEnvironment(t);
+  const { bank, exchange } = await createSimpleTestkudosEnvironmentV2(t);
 
   const http = createPlatformHttpLib({
     allowHttp: true,
diff --git 
a/packages/taler-harness/src/integrationtests/test-forced-selection.ts 
b/packages/taler-harness/src/integrationtests/test-forced-selection.ts
index d0621f000..3425dadf1 100644
--- a/packages/taler-harness/src/integrationtests/test-forced-selection.ts
+++ b/packages/taler-harness/src/integrationtests/test-forced-selection.ts
@@ -20,7 +20,7 @@
 import { j2s } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
+import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
 
 /**
  * Run test for forced denom/coin selection.
@@ -28,14 +28,14 @@ import { createSimpleTestkudosEnvironment } from 
"../harness/helpers.js";
 export async function runForcedSelectionTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
-  await wallet.client.call(WalletApiOperation.AddExchange, {
+  await walletClient.call(WalletApiOperation.AddExchange, {
     exchangeBaseUrl: exchange.baseUrl,
   });
 
-  await wallet.client.call(WalletApiOperation.WithdrawTestBalance, {
+  await walletClient.call(WalletApiOperation.WithdrawTestBalance, {
     exchangeBaseUrl: exchange.baseUrl,
     amount: "TESTKUDOS:10",
     bankAccessApiBaseUrl: bank.bankAccessApiBaseUrl,
@@ -49,13 +49,13 @@ export async function runForcedSelectionTest(t: 
GlobalTestState) {
     },
   });
 
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
-  const coinDump = await wallet.client.call(WalletApiOperation.DumpCoins, {});
+  const coinDump = await walletClient.call(WalletApiOperation.DumpCoins, {});
   console.log(coinDump);
   t.assertDeepEqual(coinDump.coins.length, 3);
 
-  const payResp = await wallet.client.call(WalletApiOperation.TestPay, {
+  const payResp = await walletClient.call(WalletApiOperation.TestPay, {
     amount: "TESTKUDOS:3",
     merchantBaseUrl: merchant.makeInstanceBaseUrl(),
     summary: "bla",
diff --git 
a/packages/taler-harness/src/integrationtests/test-libeufin-api-bankaccount.ts 
b/packages/taler-harness/src/integrationtests/test-libeufin-api-bankaccount.ts
index a5396a472..f36168301 100644
--- 
a/packages/taler-harness/src/integrationtests/test-libeufin-api-bankaccount.ts
+++ 
b/packages/taler-harness/src/integrationtests/test-libeufin-api-bankaccount.ts
@@ -19,7 +19,6 @@
  */
 import { GlobalTestState } from "../harness/harness.js";
 import {
-  NexusUserBundle,
   LibeufinNexusApi,
   LibeufinNexusService,
   LibeufinSandboxService,
diff --git 
a/packages/taler-harness/src/integrationtests/test-libeufin-nexus-balance.ts 
b/packages/taler-harness/src/integrationtests/test-libeufin-nexus-balance.ts
index 5c10c029e..68b0174cc 100644
--- a/packages/taler-harness/src/integrationtests/test-libeufin-nexus-balance.ts
+++ b/packages/taler-harness/src/integrationtests/test-libeufin-nexus-balance.ts
@@ -23,7 +23,6 @@ import {
   NexusUserBundle,
   launchLibeufinServices,
   LibeufinNexusApi,
-  LibeufinCli,
 } from "../harness/libeufin.js";
 
 /**
diff --git 
a/packages/taler-harness/src/integrationtests/test-merchant-longpolling.ts 
b/packages/taler-harness/src/integrationtests/test-merchant-longpolling.ts
index 7d2ba99b2..161e8beac 100644
--- a/packages/taler-harness/src/integrationtests/test-merchant-longpolling.ts
+++ b/packages/taler-harness/src/integrationtests/test-merchant-longpolling.ts
@@ -17,20 +17,20 @@
 /**
  * Imports.
  */
-import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
-import {
-  createSimpleTestkudosEnvironment,
-  withdrawViaBank,
-} from "../harness/helpers.js";
 import {
-  PreparePayResultType,
-  codecForMerchantOrderStatusUnpaid,
   ConfirmPayResultType,
+  PreparePayResultType,
   URL,
+  codecForMerchantOrderStatusUnpaid,
 } from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import axiosImp from "axios";
+import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
+import {
+  createSimpleTestkudosEnvironmentV2,
+  withdrawViaBankV2
+} from "../harness/helpers.js";
 const axios = axiosImp.default;
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 
 /**
  * Run test for basic, bank-integrated withdrawal.
@@ -38,12 +38,17 @@ import { WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 export async function runMerchantLongpollingTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   // Withdraw digital cash into the wallet.
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, {
+    walletClient,
+    bank,
+    exchange,
+    amount: "TESTKUDOS:20",
+  });
 
   /**
    * =========================================================================
@@ -113,7 +118,7 @@ export async function runMerchantLongpollingTest(t: 
GlobalTestState) {
    * =========================================================================
    */
 
-  let preparePayResp = await wallet.client.call(
+  let preparePayResp = await walletClient.call(
     WalletApiOperation.PreparePayForUri,
     {
       talerPayUri: pubUnpaidStatus.taler_pay_uri,
@@ -148,7 +153,7 @@ export async function runMerchantLongpollingTest(t: 
GlobalTestState) {
     publicOrderStatusResp.data,
   );
 
-  const confirmPayRes = await wallet.client.call(
+  const confirmPayRes = await walletClient.call(
     WalletApiOperation.ConfirmPay,
     {
       proposalId: proposalId,
diff --git a/packages/taler-harness/src/integrationtests/test-payment-abort.ts 
b/packages/taler-harness/src/integrationtests/test-payment-abort.ts
index 0ac953240..40438c583 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-abort.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-abort.ts
@@ -24,7 +24,6 @@ import {
 } from "../harness/helpers.js";
 import {
   FaultInjectionRequestContext,
-  FaultInjectionResponseContext,
 } from "../harness/faultInjection.js";
 import {
   codecForMerchantOrderStatusUnpaid,
diff --git a/packages/taler-harness/src/integrationtests/test-payment-claim.ts 
b/packages/taler-harness/src/integrationtests/test-payment-claim.ts
index e93d2c44c..eb219c1e7 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-claim.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-claim.ts
@@ -17,18 +17,17 @@
 /**
  * Imports.
  */
+import { PreparePayResultType, TalerErrorCode } from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import {
   GlobalTestState,
   MerchantPrivateApi,
   WalletCli,
 } from "../harness/harness.js";
 import {
-  createSimpleTestkudosEnvironment,
-  withdrawViaBank,
+  createSimpleTestkudosEnvironmentV2,
+  withdrawViaBankV2,
 } from "../harness/helpers.js";
-import { PreparePayResultType } from "@gnu-taler/taler-util";
-import { TalerErrorCode } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 
 /**
  * Run test for basic, bank-integrated withdrawal.
@@ -36,14 +35,19 @@ import { WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 export async function runPaymentClaimTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   const walletTwo = new WalletCli(t, "two");
 
   // Withdraw digital cash into the wallet.
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, {
+    walletClient,
+    bank,
+    exchange,
+    amount: "TESTKUDOS:20",
+  });
 
   // Set up order.
 
@@ -65,7 +69,7 @@ export async function runPaymentClaimTest(t: GlobalTestState) 
{
 
   // Make wallet pay for the order
 
-  const preparePayResult = await wallet.client.call(
+  const preparePayResult = await walletClient.call(
     WalletApiOperation.PreparePayForUri,
     {
       talerPayUri,
@@ -82,7 +86,7 @@ export async function runPaymentClaimTest(t: GlobalTestState) 
{
     });
   });
 
-  await wallet.client.call(WalletApiOperation.ConfirmPay, {
+  await walletClient.call(WalletApiOperation.ConfirmPay, {
     proposalId: preparePayResult.proposalId,
   });
 
diff --git 
a/packages/taler-harness/src/integrationtests/test-payment-forgettable.ts 
b/packages/taler-harness/src/integrationtests/test-payment-forgettable.ts
index b66eda2f1..83f19e58e 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-forgettable.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-forgettable.ts
@@ -17,11 +17,12 @@
 /**
  * Imports.
  */
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState } from "../harness/harness.js";
 import {
-  createSimpleTestkudosEnvironment,
-  withdrawViaBank,
-  makeTestPayment,
+  createSimpleTestkudosEnvironmentV2,
+  makeTestPaymentV2,
+  withdrawViaBankV2,
 } from "../harness/helpers.js";
 
 /**
@@ -30,12 +31,17 @@ import {
 export async function runPaymentForgettableTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   // Withdraw digital cash into the wallet.
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, {
+    walletClient,
+    bank,
+    exchange,
+    amount: "TESTKUDOS:20",
+  });
 
   {
     const order = {
@@ -50,7 +56,7 @@ export async function runPaymentForgettableTest(t: 
GlobalTestState) {
       },
     };
 
-    await makeTestPayment(t, { wallet, merchant, order });
+    await makeTestPaymentV2(t, { walletClient, merchant, order });
   }
 
   console.log("testing with forgettable field without hash");
@@ -68,10 +74,10 @@ export async function runPaymentForgettableTest(t: 
GlobalTestState) {
       },
     };
 
-    await makeTestPayment(t, { wallet, merchant, order });
+    await makeTestPaymentV2(t, { walletClient, merchant, order });
   }
 
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 }
 
 runPaymentForgettableTest.suites = ["wallet", "merchant"];
diff --git 
a/packages/taler-harness/src/integrationtests/test-payment-idempotency.ts 
b/packages/taler-harness/src/integrationtests/test-payment-idempotency.ts
index 1099a8188..e16cf9dd1 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-idempotency.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-idempotency.ts
@@ -17,13 +17,13 @@
 /**
  * Imports.
  */
+import { PreparePayResultType } from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
 import {
-  createSimpleTestkudosEnvironment,
-  withdrawViaBank,
+  createSimpleTestkudosEnvironmentV2,
+  withdrawViaBankV2,
 } from "../harness/helpers.js";
-import { PreparePayResultType } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 
 /**
  * Test the wallet-core payment API, especially that repeated operations
@@ -32,12 +32,17 @@ import { WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 export async function runPaymentIdempotencyTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   // Withdraw digital cash into the wallet.
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, {
+    walletClient,
+    bank,
+    exchange,
+    amount: "TESTKUDOS:20",
+  });
 
   // Set up order.
 
@@ -59,14 +64,14 @@ export async function runPaymentIdempotencyTest(t: 
GlobalTestState) {
 
   // Make wallet pay for the order
 
-  const preparePayResult = await wallet.client.call(
+  const preparePayResult = await walletClient.call(
     WalletApiOperation.PreparePayForUri,
     {
       talerPayUri: orderStatus.taler_pay_uri,
     },
   );
 
-  const preparePayResultRep = await wallet.client.call(
+  const preparePayResultRep = await walletClient.call(
     WalletApiOperation.PreparePayForUri,
     {
       talerPayUri: orderStatus.taler_pay_uri,
@@ -82,7 +87,7 @@ export async function runPaymentIdempotencyTest(t: 
GlobalTestState) {
 
   const proposalId = preparePayResult.proposalId;
 
-  const confirmPayResult = await wallet.client.call(
+  const confirmPayResult = await walletClient.call(
     WalletApiOperation.ConfirmPay,
     {
       proposalId: proposalId,
@@ -91,7 +96,7 @@ export async function runPaymentIdempotencyTest(t: 
GlobalTestState) {
 
   console.log("confirm pay result", confirmPayResult);
 
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
   // Check if payment was successful.
 
@@ -101,7 +106,7 @@ export async function runPaymentIdempotencyTest(t: 
GlobalTestState) {
 
   t.assertTrue(orderStatus.order_status === "paid");
 
-  const preparePayResultAfter = await wallet.client.call(
+  const preparePayResultAfter = await walletClient.call(
     WalletApiOperation.PreparePayForUri,
     {
       talerPayUri,
diff --git a/packages/taler-harness/src/integrationtests/test-payment-share.ts 
b/packages/taler-harness/src/integrationtests/test-payment-share.ts
index 2f853b91b..156571372 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-share.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-share.ts
@@ -17,21 +17,17 @@
 /**
  * Imports.
  */
+import {
+  ConfirmPayResultType,
+  PreparePayResultType,
+} from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
 import {
   createSimpleTestkudosEnvironmentV2,
-  withdrawViaBankV2,
-  makeTestPaymentV2,
   createWalletDaemonWithClient,
+  withdrawViaBankV2,
 } from "../harness/helpers.js";
-import {
-  ConfirmPayResultType,
-  PreparePayResultType,
-  j2s,
-  parsePayUri,
-  stringifyPayUri,
-} from "@gnu-taler/taler-util";
 
 /**
  * Run test for basic, bank-integrated withdrawal and payment.
diff --git 
a/packages/taler-harness/src/integrationtests/test-payment-template.ts 
b/packages/taler-harness/src/integrationtests/test-payment-template.ts
index 41e43e28a..172791648 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-template.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-template.ts
@@ -17,13 +17,12 @@
 /**
  * Imports.
  */
-import { ConfirmPayResultType, Duration, PreparePayResultType, 
TalerProtocolTimestamp } from "@gnu-taler/taler-util";
+import { ConfirmPayResultType, Duration, PreparePayResultType } from 
"@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
 import {
-  createSimpleTestkudosEnvironment,
-  withdrawViaBank,
-  makeTestPayment,
+  createSimpleTestkudosEnvironmentV2,
+  withdrawViaBankV2
 } from "../harness/helpers.js";
 
 /**
@@ -32,8 +31,8 @@ import {
 export async function runPaymentTemplateTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   await MerchantPrivateApi.createTemplate(merchant, "default", {
     template_id: "template1",
@@ -51,11 +50,11 @@ export async function runPaymentTemplateTest(t: 
GlobalTestState) {
 
   // Withdraw digital cash into the wallet.
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: 
"TESTKUDOS:20" });
 
   // Request a template payment
 
-  const preparePayResult = await wallet.client.call(
+  const preparePayResult = await walletClient.call(
     WalletApiOperation.PreparePayForTemplate,
     {
       talerPayTemplateUri: 
`taler+http://pay-template/localhost:${merchant.port}/template1?amount=TESTKUDOS:1`,
@@ -71,7 +70,7 @@ export async function runPaymentTemplateTest(t: 
GlobalTestState) {
 
   // Pay for it
 
-  const r2 = await wallet.client.call(WalletApiOperation.ConfirmPay, {
+  const r2 = await walletClient.call(WalletApiOperation.ConfirmPay, {
     proposalId: preparePayResult.proposalId,
   });
 
@@ -88,8 +87,7 @@ export async function runPaymentTemplateTest(t: 
GlobalTestState) {
   );
 
   t.assertTrue(orderStatus.order_status === "paid");
-
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 }
 
 runPaymentTemplateTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/test-payment-zero.ts 
b/packages/taler-harness/src/integrationtests/test-payment-zero.ts
index 264cffd06..7423751a5 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-zero.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-zero.ts
@@ -20,9 +20,9 @@
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState } from "../harness/harness.js";
 import {
-  createSimpleTestkudosEnvironment,
-  withdrawViaBank,
-  makeTestPayment,
+  createSimpleTestkudosEnvironmentV2,
+  withdrawViaBankV2,
+  makeTestPaymentV2,
 } from "../harness/helpers.js";
 import { TransactionMajorState } from "@gnu-taler/taler-util";
 
@@ -33,19 +33,19 @@ import { TransactionMajorState } from 
"@gnu-taler/taler-util";
 export async function runPaymentZeroTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   // First, make a "free" payment when we don't even have
   // any money in the
 
   // Withdraw digital cash into the wallet.
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: 
"TESTKUDOS:20" });
 
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
-  await makeTestPayment(t, {
-    wallet,
+  await makeTestPaymentV2(t, {
+    walletClient,
     merchant,
     order: {
       summary: "I am free!",
@@ -54,9 +54,9 @@ export async function runPaymentZeroTest(t: GlobalTestState) {
     },
   });
 
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
-  const transactions = await wallet.client.call(
+  const transactions = await walletClient.call(
     WalletApiOperation.GetTransactions,
     {},
   );
diff --git a/packages/taler-harness/src/integrationtests/test-paywall-flow.ts 
b/packages/taler-harness/src/integrationtests/test-paywall-flow.ts
index a9601c625..c90898034 100644
--- a/packages/taler-harness/src/integrationtests/test-paywall-flow.ts
+++ b/packages/taler-harness/src/integrationtests/test-paywall-flow.ts
@@ -18,10 +18,6 @@
  * Imports.
  */
 import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
-import {
-  createSimpleTestkudosEnvironment,
-  withdrawViaBank,
-} from "../harness/helpers.js";
 import {
   PreparePayResultType,
   codecForMerchantOrderStatusUnpaid,
@@ -31,6 +27,7 @@ import {
 import axiosImp from "axios";
 const axios = axiosImp.default;
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { createSimpleTestkudosEnvironmentV2, withdrawViaBankV2 } from 
"../harness/helpers.js";
 
 /**
  * Run test for basic, bank-integrated withdrawal.
@@ -38,12 +35,12 @@ import { WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 export async function runPaywallFlowTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   // Withdraw digital cash into the wallet.
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: 
"TESTKUDOS:20" });
 
   /**
    * =========================================================================
@@ -93,7 +90,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
 
   console.log(pubUnpaidStatus);
 
-  let preparePayResp = await wallet.client.call(
+  let preparePayResp = await walletClient.call(
     WalletApiOperation.PreparePayForUri,
     {
       talerPayUri: pubUnpaidStatus.taler_pay_uri,
@@ -119,7 +116,7 @@ export async function runPaywallFlowTest(t: 
GlobalTestState) {
     publicOrderStatusResp.data,
   );
 
-  const confirmPayRes = await wallet.client.call(
+  const confirmPayRes = await walletClient.call(
     WalletApiOperation.ConfirmPay,
     {
       proposalId: proposalId,
@@ -158,7 +155,7 @@ export async function runPaywallFlowTest(t: 
GlobalTestState) {
   // Pay with new taler://pay URI, which should
   // have the new session ID!
   // Wallet should now automatically re-play payment.
-  preparePayResp = await wallet.client.call(
+  preparePayResp = await walletClient.call(
     WalletApiOperation.PreparePayForUri,
     {
       talerPayUri: talerPayUriOne,
@@ -199,7 +196,7 @@ export async function runPaywallFlowTest(t: 
GlobalTestState) {
   // Here the re-purchase detection should kick in,
   // and the wallet should re-pay for the old order
   // under the new session ID (mysession-three).
-  preparePayResp = await wallet.client.call(
+  preparePayResp = await walletClient.call(
     WalletApiOperation.PreparePayForUri,
     {
       talerPayUri: orderStatus.taler_pay_uri,
diff --git a/packages/taler-harness/src/integrationtests/test-refund-auto.ts 
b/packages/taler-harness/src/integrationtests/test-refund-auto.ts
index 4c2a2f94a..607080e68 100644
--- a/packages/taler-harness/src/integrationtests/test-refund-auto.ts
+++ b/packages/taler-harness/src/integrationtests/test-refund-auto.ts
@@ -17,13 +17,13 @@
 /**
  * Imports.
  */
+import { Duration, durationFromSpec } from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
 import {
-  createSimpleTestkudosEnvironment,
-  withdrawViaBank,
+  createSimpleTestkudosEnvironmentV2,
+  withdrawViaBankV2,
 } from "../harness/helpers.js";
-import { Duration, durationFromSpec } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 
 /**
  * Run test for basic, bank-integrated withdrawal.
@@ -31,12 +31,17 @@ import { WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 export async function runRefundAutoTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   // Withdraw digital cash into the wallet.
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, {
+    walletClient,
+    bank,
+    exchange,
+    amount: "TESTKUDOS:20",
+  });
 
   // Set up order.
   const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
@@ -61,13 +66,12 @@ export async function runRefundAutoTest(t: GlobalTestState) 
{
 
   // Make wallet pay for the order
 
-  const r1 = await wallet.client.call(WalletApiOperation.PreparePayForUri, {
+  const r1 = await walletClient.call(WalletApiOperation.PreparePayForUri, {
     talerPayUri: orderStatus.taler_pay_uri,
   });
 
-  await wallet.client.call(WalletApiOperation.ConfirmPay, {
-    // FIXME: should be validated, don't cast!
-    proposalId: r1.proposalId,
+  await walletClient.call(WalletApiOperation.ConfirmPay, {
+    transactionId: r1.transactionId,
   });
 
   // Check if payment was successful.
@@ -88,9 +92,9 @@ export async function runRefundAutoTest(t: GlobalTestState) {
   console.log(ref);
 
   // The wallet should now automatically pick up the refund.
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
-  const transactions = await wallet.client.call(
+  const transactions = await walletClient.call(
     WalletApiOperation.GetTransactions,
     {},
   );
diff --git 
a/packages/taler-harness/src/integrationtests/test-refund-incremental.ts 
b/packages/taler-harness/src/integrationtests/test-refund-incremental.ts
index c8fcfc831..8e7e38b71 100644
--- a/packages/taler-harness/src/integrationtests/test-refund-incremental.ts
+++ b/packages/taler-harness/src/integrationtests/test-refund-incremental.ts
@@ -17,22 +17,22 @@
 /**
  * Imports.
  */
+import {
+  Amounts,
+  Duration,
+  TransactionType,
+  durationFromSpec,
+} from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import {
   GlobalTestState,
-  delayMs,
   MerchantPrivateApi,
+  delayMs,
 } from "../harness/harness.js";
 import {
-  createSimpleTestkudosEnvironment,
-  withdrawViaBank,
+  createSimpleTestkudosEnvironmentV2,
+  withdrawViaBankV2,
 } from "../harness/helpers.js";
-import {
-  TransactionType,
-  Amounts,
-  durationFromSpec,
-  Duration,
-} from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 
 /**
  * Run test for basic, bank-integrated withdrawal.
@@ -40,12 +40,17 @@ import { WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 export async function runRefundIncrementalTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   // Withdraw digital cash into the wallet.
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, {
+    walletClient,
+    bank,
+    exchange,
+    amount: "TESTKUDOS:20",
+  });
 
   // Set up order.
 
@@ -68,11 +73,11 @@ export async function runRefundIncrementalTest(t: 
GlobalTestState) {
 
   // Make wallet pay for the order
 
-  const r1 = await wallet.client.call(WalletApiOperation.PreparePayForUri, {
+  const r1 = await walletClient.call(WalletApiOperation.PreparePayForUri, {
     talerPayUri: orderStatus.taler_pay_uri,
   });
 
-  await wallet.client.call(WalletApiOperation.ConfirmPay, {
+  await walletClient.call(WalletApiOperation.ConfirmPay, {
     proposalId: r1.proposalId,
   });
 
@@ -94,15 +99,15 @@ export async function runRefundIncrementalTest(t: 
GlobalTestState) {
   console.log("first refund increase response", ref);
 
   {
-    let wr = await wallet.client.call(WalletApiOperation.StartRefundQuery, {
+    let wr = await walletClient.call(WalletApiOperation.StartRefundQuery, {
       transactionId: r1.transactionId,
     });
-    await wallet.runUntilDone();
-    console.log(wr);
-    const txs = await wallet.client.call(
-      WalletApiOperation.GetTransactions,
+    await walletClient.call(
+      WalletApiOperation.TestingWaitTransactionsFinal,
       {},
     );
+    console.log(wr);
+    const txs = await walletClient.call(WalletApiOperation.GetTransactions, 
{});
     console.log(
       "transactions after applying first refund:",
       JSON.stringify(txs, undefined, 2),
@@ -136,10 +141,13 @@ export async function runRefundIncrementalTest(t: 
GlobalTestState) {
   console.log("third refund increase response", ref);
 
   {
-    let wr = await wallet.client.call(WalletApiOperation.StartRefundQuery, {
+    let wr = await walletClient.call(WalletApiOperation.StartRefundQuery, {
       transactionId: r1.transactionId,
     });
-    await wallet.runUntilDone();  
+    await walletClient.call(
+      WalletApiOperation.TestingWaitTransactionsFinal,
+      {},
+    );
     console.log(wr);
   }
 
@@ -153,25 +161,17 @@ export async function runRefundIncrementalTest(t: 
GlobalTestState) {
 
   console.log(JSON.stringify(orderStatus, undefined, 2));
 
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
-  const bal = await wallet.client.call(WalletApiOperation.GetBalances, {});
+  const bal = await walletClient.call(WalletApiOperation.GetBalances, {});
   console.log(JSON.stringify(bal, undefined, 2));
 
   {
-    const txs = await wallet.client.call(
-      WalletApiOperation.GetTransactions,
-      {},
-    );
+    const txs = await walletClient.call(WalletApiOperation.GetTransactions, 
{});
     console.log(JSON.stringify(txs, undefined, 2));
 
     const txTypes = txs.transactions.map((x) => x.type);
-    t.assertDeepEqual(txTypes, [
-      "withdrawal",
-      "payment",
-      "refund",
-      "refund",
-    ]);
+    t.assertDeepEqual(txTypes, ["withdrawal", "payment", "refund", "refund"]);
 
     for (const tx of txs.transactions) {
       if (tx.type !== TransactionType.Refund) {
diff --git a/packages/taler-harness/src/integrationtests/test-tipping.ts 
b/packages/taler-harness/src/integrationtests/test-tipping.ts
index 332f702d7..53d7f08c8 100644
--- a/packages/taler-harness/src/integrationtests/test-tipping.ts
+++ b/packages/taler-harness/src/integrationtests/test-tipping.ts
@@ -18,8 +18,8 @@
  * Imports.
  */
 import {
+  BankAccessApiClient,
   WalletApiOperation,
-  BankApi,
   WireGatewayApiClient,
 } from "@gnu-taler/taler-wallet-core";
 import {
@@ -28,7 +28,7 @@ import {
   MerchantPrivateApi,
   getWireMethodForTest,
 } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
+import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
 import { TransactionMajorState } from "@gnu-taler/taler-util";
 
 /**
@@ -37,10 +37,14 @@ import { TransactionMajorState } from 
"@gnu-taler/taler-util";
 export async function runTippingTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant, exchangeBankAccount } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant, exchangeBankAccount } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
-  const mbu = await BankApi.createRandomBankUser(bank);
+  const bankAccessApiClient = new BankAccessApiClient({
+    allowHttp: true,
+    baseUrl: bank.bankAccessApiBaseUrl,
+  });
+  const mbu = await bankAccessApiClient.createRandomBankUser();
 
   const merchantClient = new MerchantApiClient(
     merchant.makeInstanceBaseUrl("default"),
@@ -99,7 +103,7 @@ export async function runTippingTest(t: GlobalTestState) {
   console.log("created tip", tip);
 
   const doTip = async (): Promise<void> => {
-    const ptr = await wallet.client.call(WalletApiOperation.PrepareReward, {
+    const ptr = await walletClient.call(WalletApiOperation.PrepareReward, {
       talerRewardUri: tip.taler_reward_uri,
     });
 
@@ -108,19 +112,22 @@ export async function runTippingTest(t: GlobalTestState) {
     t.assertAmountEquals(ptr.rewardAmountRaw, "TESTKUDOS:5");
     t.assertAmountEquals(ptr.rewardAmountEffective, "TESTKUDOS:4.85");
 
-    await wallet.client.call(WalletApiOperation.AcceptReward, {
+    await walletClient.call(WalletApiOperation.AcceptReward, {
       walletRewardId: ptr.walletRewardId,
     });
 
-    await wallet.runUntilDone();
+    await walletClient.call(
+      WalletApiOperation.TestingWaitTransactionsFinal,
+      {},
+    );
 
-    const bal = await wallet.client.call(WalletApiOperation.GetBalances, {});
+    const bal = await walletClient.call(WalletApiOperation.GetBalances, {});
 
     console.log(bal);
 
     t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:4.85");
 
-    const txns = await wallet.client.call(
+    const txns = await walletClient.call(
       WalletApiOperation.GetTransactions,
       {},
     );
@@ -128,7 +135,10 @@ export async function runTippingTest(t: GlobalTestState) {
     console.log("Transactions:", JSON.stringify(txns, undefined, 2));
 
     t.assertDeepEqual(txns.transactions[0].type, "reward");
-    t.assertDeepEqual(txns.transactions[0].txState.major, 
TransactionMajorState.Done);
+    t.assertDeepEqual(
+      txns.transactions[0].txState.major,
+      TransactionMajorState.Done,
+    );
     t.assertAmountEquals(
       txns.transactions[0].amountEffective,
       "TESTKUDOS:4.85",
diff --git a/packages/taler-harness/src/integrationtests/test-tos-format.ts 
b/packages/taler-harness/src/integrationtests/test-tos-format.ts
index ebe9c403a..e6087af9d 100644
--- a/packages/taler-harness/src/integrationtests/test-tos-format.ts
+++ b/packages/taler-harness/src/integrationtests/test-tos-format.ts
@@ -23,7 +23,6 @@ import {
   createSimpleTestkudosEnvironmentV2,
 } from "../harness/helpers.js";
 import * as fs from "fs";
-import * as path from "path";
 
 /**
  * Run test for basic, bank-integrated withdrawal and payment.
diff --git 
a/packages/taler-harness/src/integrationtests/test-wallet-backup-basic.ts 
b/packages/taler-harness/src/integrationtests/test-wallet-backup-basic.ts
index f36e2e4f1..5321cf5c7 100644
--- a/packages/taler-harness/src/integrationtests/test-wallet-backup-basic.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallet-backup-basic.ts
@@ -22,7 +22,9 @@ import { WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 import { GlobalTestState, WalletCli } from "../harness/harness.js";
 import {
   createSimpleTestkudosEnvironment,
+  createSimpleTestkudosEnvironmentV2,
   withdrawViaBank,
+  withdrawViaBankV2,
 } from "../harness/helpers.js";
 import { SyncService } from "../harness/sync.js";
 
@@ -32,8 +34,8 @@ import { SyncService } from "../harness/sync.js";
 export async function runWalletBackupBasicTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { commonDb, merchant, wallet, bank, exchange } =
-    await createSimpleTestkudosEnvironment(t);
+  const { commonDb, merchant, walletClient, bank, exchange } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   const sync = await SyncService.create(t, {
     currency: "TESTKUDOS",
@@ -49,32 +51,32 @@ export async function runWalletBackupBasicTest(t: 
GlobalTestState) {
   await sync.start();
   await sync.pingUntilAvailable();
 
-  await wallet.client.call(WalletApiOperation.AddBackupProvider, {
+  await walletClient.call(WalletApiOperation.AddBackupProvider, {
     backupProviderBaseUrl: sync.baseUrl,
     activate: false,
     name: sync.baseUrl,
   });
 
   {
-    const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {});
+    const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {});
     t.assertDeepEqual(bi.providers[0].active, false);
   }
 
-  await wallet.client.call(WalletApiOperation.AddBackupProvider, {
+  await walletClient.call(WalletApiOperation.AddBackupProvider, {
     backupProviderBaseUrl: sync.baseUrl,
     activate: true,
     name: sync.baseUrl,
   });
 
   {
-    const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {});
+    const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {});
     t.assertDeepEqual(bi.providers[0].active, true);
   }
 
-  await wallet.client.call(WalletApiOperation.RunBackupCycle, {});
+  await walletClient.call(WalletApiOperation.RunBackupCycle, {});
 
   {
-    const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {});
+    const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {});
     console.log(bi);
     t.assertDeepEqual(
       bi.providers[0].paymentStatus.type,
@@ -82,32 +84,32 @@ export async function runWalletBackupBasicTest(t: 
GlobalTestState) {
     );
   }
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:10" });
+  await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: 
"TESTKUDOS:10" });
 
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
-  await wallet.client.call(WalletApiOperation.RunBackupCycle, {});
+  await walletClient.call(WalletApiOperation.RunBackupCycle, {});
 
   {
-    const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {});
+    const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {});
     console.log(bi);
   }
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:5" });
+  await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: 
"TESTKUDOS:5" });
 
-  await wallet.client.call(WalletApiOperation.RunBackupCycle, {});
+  await walletClient.call(WalletApiOperation.RunBackupCycle, {});
 
   {
-    const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {});
+    const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {});
     console.log(bi);
   }
 
-  const backupRecovery = await wallet.client.call(
+  const backupRecovery = await walletClient.call(
     WalletApiOperation.ExportBackupRecovery,
     {},
   );
 
-  const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {});
+  const txs = await walletClient.call(WalletApiOperation.GetTransactions, {});
   console.log(`backed up transactions ${j2s(txs)}`);
 
   const wallet2 = new WalletCli(t, "wallet2");
diff --git 
a/packages/taler-harness/src/integrationtests/test-wallet-backup-doublespend.ts 
b/packages/taler-harness/src/integrationtests/test-wallet-backup-doublespend.ts
index 2aceab86e..e3e18d5a4 100644
--- 
a/packages/taler-harness/src/integrationtests/test-wallet-backup-doublespend.ts
+++ 
b/packages/taler-harness/src/integrationtests/test-wallet-backup-doublespend.ts
@@ -21,21 +21,22 @@ import { PreparePayResultType } from 
"@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import {
   GlobalTestState,
-  WalletCli,
   MerchantPrivateApi,
+  WalletCli,
 } from "../harness/harness.js";
 import {
-  createSimpleTestkudosEnvironment,
-  makeTestPayment,
+  createSimpleTestkudosEnvironmentV2,
+  makeTestPaymentV2,
   withdrawViaBank,
+  withdrawViaBankV2
 } from "../harness/helpers.js";
 import { SyncService } from "../harness/sync.js";
 
 export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { commonDb, merchant, wallet, bank, exchange } =
-    await createSimpleTestkudosEnvironment(t);
+  const { commonDb, merchant, walletClient, bank, exchange } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   const sync = await SyncService.create(t, {
     currency: "TESTKUDOS",
@@ -51,21 +52,21 @@ export async function runWalletBackupDoublespendTest(t: 
GlobalTestState) {
   await sync.start();
   await sync.pingUntilAvailable();
 
-  await wallet.client.call(WalletApiOperation.AddBackupProvider, {
+  await walletClient.call(WalletApiOperation.AddBackupProvider, {
     backupProviderBaseUrl: sync.baseUrl,
     activate: true,
     name: sync.baseUrl,
   });
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:10" });
+  await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: 
"TESTKUDOS:10" });
 
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
-  await wallet.client.call(WalletApiOperation.RunBackupCycle, {});
-  await wallet.runUntilDone();
-  await wallet.client.call(WalletApiOperation.RunBackupCycle, {});
+  await walletClient.call(WalletApiOperation.RunBackupCycle, {});
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
+  await walletClient.call(WalletApiOperation.RunBackupCycle, {});
 
-  const backupRecovery = await wallet.client.call(
+  const backupRecovery = await walletClient.call(
     WalletApiOperation.ExportBackupRecovery,
     {},
   );
@@ -80,23 +81,23 @@ export async function runWalletBackupDoublespendTest(t: 
GlobalTestState) {
 
   console.log(
     "wallet1 balance before spend:",
-    await wallet.client.call(WalletApiOperation.GetBalances, {}),
+    await walletClient.call(WalletApiOperation.GetBalances, {}),
   );
 
-  await makeTestPayment(t, {
+  await makeTestPaymentV2(t, {
     merchant,
-    wallet,
+    walletClient,
     order: {
       summary: "foo",
       amount: "TESTKUDOS:7",
     },
   });
 
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
   console.log(
     "wallet1 balance after spend:",
-    await wallet.client.call(WalletApiOperation.GetBalances, {}),
+    await walletClient.call(WalletApiOperation.GetBalances, {}),
   );
 
   {
diff --git a/packages/taler-harness/src/integrationtests/test-wallet-balance.ts 
b/packages/taler-harness/src/integrationtests/test-wallet-balance.ts
index b5b1917a6..0f75bd96e 100644
--- a/packages/taler-harness/src/integrationtests/test-wallet-balance.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallet-balance.ts
@@ -30,7 +30,11 @@ import {
   setupDb,
   WalletCli,
 } from "../harness/harness.js";
-import { withdrawViaBank } from "../harness/helpers.js";
+import {
+  createSimpleTestkudosEnvironmentV2,
+  withdrawViaBank,
+  withdrawViaBankV2,
+} from "../harness/helpers.js";
 
 /**
  * Test for wallet balance error messages / different types of insufficient 
balance.
@@ -41,72 +45,17 @@ import { withdrawViaBank } from "../harness/helpers.js";
 export async function runWalletBalanceTest(t: GlobalTestState) {
   // Set up test environment
 
-  const db = await setupDb(t);
-
-  const bank = await FakebankService.create(t, {
-    allowRegistrations: true,
-    currency: "TESTKUDOS",
-    database: db.connStr,
-    httpPort: 8082,
-  });
-
-  const exchange = ExchangeService.create(t, {
-    name: "testexchange-1",
-    currency: "TESTKUDOS",
-    httpPort: 8081,
-    database: db.connStr,
-  });
-
-  const merchant = await MerchantService.create(t, {
-    name: "testmerchant-1",
-    currency: "TESTKUDOS",
-    httpPort: 8083,
-    database: db.connStr,
-  });
-
-  const exchangeBankAccount = await bank.createExchangeAccount(
-    "myexchange",
-    "x",
-  );
-  exchange.addBankAccount("1", exchangeBankAccount);
-
-  bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri);
-
-  await bank.start();
-
-  await bank.pingUntilAvailable();
-
-  const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => 
x("TESTKUDOS"));
-  exchange.addCoinConfigList(coinConfig);
-
-  await exchange.start();
-  await exchange.pingUntilAvailable();
-
-  merchant.addExchange(exchange);
-
-  await merchant.start();
-  await merchant.pingUntilAvailable();
-
-  // Fakebank uses x-taler-bank, but merchant is configured to only accept 
sepa!
-  const label = "mymerchant";
-  await merchant.addInstance({
-    id: "default",
-    name: "Default Instance",
-    paytoUris: [
-      `payto://iban/SANDBOXX/${getRandomIban(label)}?receiver-name=${label}`,
-    ],
-    defaultWireTransferDelay: Duration.toTalerProtocolDuration(
-      Duration.fromSpec({ minutes: 1 }),
-    ),
-  });
-
-  console.log("setup done!");
-
-  const wallet = new WalletCli(t);
+  const { merchant, walletClient, exchange, bank } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   // Withdraw digital cash into the wallet.
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, {
+    walletClient,
+    bank,
+    exchange,
+    amount: "TESTKUDOS:20",
+  });
 
   const order = {
     summary: "Buy me!",
@@ -126,7 +75,7 @@ export async function runWalletBalanceTest(t: 
GlobalTestState) {
 
   // Make wallet pay for the order
 
-  const preparePayResult = await wallet.client.call(
+  const preparePayResult = await walletClient.call(
     WalletApiOperation.PreparePayForUri,
     {
       talerPayUri: orderStatus.taler_pay_uri,
@@ -152,7 +101,7 @@ export async function runWalletBalanceTest(t: 
GlobalTestState) {
     Amounts.isZero(preparePayResult.balanceDetails.balanceMerchantDepositable),
   );
 
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 }
 
 runWalletBalanceTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/test-wallet-dbless.ts 
b/packages/taler-harness/src/integrationtests/test-wallet-dbless.ts
index c384a6e74..58f564f34 100644
--- a/packages/taler-harness/src/integrationtests/test-wallet-dbless.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallet-dbless.ts
@@ -37,7 +37,7 @@ import {
   withdrawCoin,
 } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
+import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
 
 /**
  * Run test for basic, bank-integrated withdrawal and payment.
@@ -45,7 +45,7 @@ import { createSimpleTestkudosEnvironment } from 
"../harness/helpers.js";
 export async function runWalletDblessTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { bank, exchange } = await createSimpleTestkudosEnvironment(t);
+  const { bank, exchange } = await createSimpleTestkudosEnvironmentV2(t);
 
   const http = createPlatformHttpLib({
     allowHttp: true,
diff --git 
a/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts 
b/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts
index 810250f53..c70fd51d4 100644
--- a/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts
@@ -18,27 +18,24 @@
  * Imports.
  */
 import {
-  Amounts,
   Duration,
   NotificationType,
-  PreparePayResultType,
   TransactionMajorState,
 } from "@gnu-taler/taler-util";
 import {
-  BankAccessApi,
-  BankApi,
+  BankAccessApiClient,
   WalletApiOperation,
 } from "@gnu-taler/taler-wallet-core";
 import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
 import {
   ExchangeService,
   FakebankService,
-  getRandomIban,
   GlobalTestState,
   MerchantService,
-  setupDb,
   WalletClient,
   WalletService,
+  getRandomIban,
+  setupDb,
 } from "../harness/harness.js";
 
 /**
@@ -126,10 +123,14 @@ export async function runWalletNotificationsTest(t: 
GlobalTestState) {
     skipDefaults: true,
   });
 
-  const user = await BankApi.createRandomBankUser(bank);
-  const wop = await BankAccessApi.createWithdrawalOperation(
-    bank,
-    user,
+  const bankAccessApiClient = new BankAccessApiClient({
+    allowHttp: true,
+    baseUrl: bank.bankAccessApiBaseUrl,
+  });
+  const user = await bankAccessApiClient.createRandomBankUser();
+  bankAccessApiClient.setAuth(user);
+  const wop = await bankAccessApiClient.createWithdrawalOperation(
+    user.username,
     "TESTKUDOS:20",
   );
 
@@ -163,7 +164,7 @@ export async function runWalletNotificationsTest(t: 
GlobalTestState) {
 
   // Confirm it
 
-  await BankApi.confirmWithdrawalOperation(bank, user, wop);
+  await bankAccessApiClient.confirmWithdrawalOperation(user.username, wop);
 
   await withdrawalFinishedReceivedPromise;
 }
diff --git 
a/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts 
b/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts
index bf2dc0133..c3069c317 100644
--- a/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts
+++ b/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts
@@ -20,8 +20,7 @@
 import { TalerErrorCode } from "@gnu-taler/taler-util";
 import {
   WalletApiOperation,
-  BankApi,
-  BankAccessApi,
+  BankAccessApiClient,
 } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState } from "../harness/harness.js";
 import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
@@ -36,10 +35,14 @@ export async function runWithdrawalAbortBankTest(t: 
GlobalTestState) {
 
   // Create a withdrawal operation
 
-  const user = await BankApi.createRandomBankUser(bank);
-  const wop = await BankAccessApi.createWithdrawalOperation(
-    bank,
-    user,
+  const bankAccessApiClient = new BankAccessApiClient({
+    allowHttp: true,
+    baseUrl: bank.bankAccessApiBaseUrl,
+  });
+  const user = await bankAccessApiClient.createRandomBankUser();
+  bankAccessApiClient.setAuth(user);
+  const wop = await bankAccessApiClient.createWithdrawalOperation(
+    user.username,
     "TESTKUDOS:10",
   );
 
@@ -53,8 +56,7 @@ export async function runWithdrawalAbortBankTest(t: 
GlobalTestState) {
 
   // Abort it
 
-  await BankApi.abortWithdrawalOperation(bank, user, wop);
-  //await BankApi.confirmWithdrawalOperation(bank, user, wop);
+  await bankAccessApiClient.abortWithdrawalOperation(user.username, wop);
 
   // Withdraw
 
diff --git 
a/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts
 
b/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts
index 396f0f03f..61687ec02 100644
--- 
a/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts
+++ 
b/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts
@@ -19,11 +19,7 @@
  */
 import { GlobalTestState } from "../harness/harness.js";
 import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
-import {
-  WalletApiOperation,
-  BankApi,
-  BankAccessApi,
-} from "@gnu-taler/taler-wallet-core";
+import { BankAccessApiClient, WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 import {
   j2s,
   NotificationType,
@@ -44,10 +40,14 @@ export async function runWithdrawalBankIntegratedTest(t: 
GlobalTestState) {
 
   // Create a withdrawal operation
 
-  const user = await BankApi.createRandomBankUser(bank);
-  const wop = await BankAccessApi.createWithdrawalOperation(
-    bank,
-    user,
+  const bankAccessApiClient = new BankAccessApiClient({
+    allowHttp: true,
+    baseUrl: bank.bankAccessApiBaseUrl,
+  });
+  const user = await bankAccessApiClient.createRandomBankUser();
+  bankAccessApiClient.setAuth(user);
+  const wop = await bankAccessApiClient.createWithdrawalOperation(
+    user.username,
     "TESTKUDOS:10",
   );
 
@@ -129,7 +129,7 @@ export async function runWithdrawalBankIntegratedTest(t: 
GlobalTestState) {
 
   // Confirm it
 
-  await BankApi.confirmWithdrawalOperation(bank, user, wop);
+  await bankAccessApiClient.confirmWithdrawalOperation(user.username, wop);
 
   await withdrawalBankConfirmedCond;
 
diff --git 
a/packages/taler-harness/src/integrationtests/test-withdrawal-fees.ts 
b/packages/taler-harness/src/integrationtests/test-withdrawal-fees.ts
index 065b134c4..06355b964 100644
--- a/packages/taler-harness/src/integrationtests/test-withdrawal-fees.ts
+++ b/packages/taler-harness/src/integrationtests/test-withdrawal-fees.ts
@@ -26,6 +26,7 @@ import {
 } from "../harness/harness.js";
 import {
   BankAccessApi,
+  BankAccessApiClient,
   BankApi,
   WalletApiOperation,
 } from "@gnu-taler/taler-wallet-core";
@@ -112,8 +113,13 @@ export async function runWithdrawalFeesTest(t: 
GlobalTestState) {
 
   const amount = "TESTKUDOS:7.5";
 
-  const user = await BankApi.createRandomBankUser(bank);
-  const wop = await BankAccessApi.createWithdrawalOperation(bank, user, 
amount);
+  const bankAccessApiClient = new BankAccessApiClient({
+    allowHttp: true,
+    baseUrl: bank.bankAccessApiBaseUrl,
+  });
+  const user = await bankAccessApiClient.createRandomBankUser();
+  bankAccessApiClient.setAuth(user);
+  const wop = await 
bankAccessApiClient.createWithdrawalOperation(user.username, amount);
 
   // Hand it to the wallet
 
@@ -150,10 +156,7 @@ export async function runWithdrawalFeesTest(t: 
GlobalTestState) {
 
   // Confirm it
 
-  await BankApi.confirmWithdrawalOperation(bank, user, wop);
-
-  await exchange.runWirewatchOnce();
-
+  await bankAccessApiClient.confirmWithdrawalOperation(user.username, wop);
   await wallet.runUntilDone();
 
   // Check balance
diff --git 
a/packages/taler-harness/src/integrationtests/test-withdrawal-huge.ts 
b/packages/taler-harness/src/integrationtests/test-withdrawal-huge.ts
index f56e4d24d..a9d544ee0 100644
--- a/packages/taler-harness/src/integrationtests/test-withdrawal-huge.ts
+++ b/packages/taler-harness/src/integrationtests/test-withdrawal-huge.ts
@@ -27,7 +27,11 @@ import {
 } from "../harness/harness.js";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
-import { NotificationType, TransactionMajorState, URL } from 
"@gnu-taler/taler-util";
+import {
+  NotificationType,
+  TransactionMajorState,
+  URL,
+} from "@gnu-taler/taler-util";
 
 /**
  * Withdraw a high amount.  Mostly intended as a perf test.
@@ -84,7 +88,8 @@ export async function runWithdrawalHugeTest(t: 
GlobalTestState) {
   await wallet.connect();
 
   const withdrawalFinishedCond = wallet.waitForNotificationCond(
-    (wn) => wn.type === NotificationType.TransactionStateTransition &&
+    (wn) =>
+      wn.type === NotificationType.TransactionStateTransition &&
       wn.transactionId.startsWith("txn:withdrawal:") &&
       wn.newTxState.major === TransactionMajorState.Done,
   );
diff --git 
a/packages/taler-harness/src/integrationtests/test-withdrawal-manual.ts 
b/packages/taler-harness/src/integrationtests/test-withdrawal-manual.ts
index a356a5c1a..d49235f89 100644
--- a/packages/taler-harness/src/integrationtests/test-withdrawal-manual.ts
+++ b/packages/taler-harness/src/integrationtests/test-withdrawal-manual.ts
@@ -17,14 +17,14 @@
 /**
  * Imports.
  */
-import { GlobalTestState } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
+import { AbsoluteTime, Logger, j2s } from "@gnu-taler/taler-util";
 import {
+  BankAccessApiClient,
   WalletApiOperation,
-  BankApi,
   WireGatewayApiClient,
 } from "@gnu-taler/taler-wallet-core";
-import { AbsoluteTime, j2s, Logger } from "@gnu-taler/taler-util";
+import { GlobalTestState } from "../harness/harness.js";
+import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
 
 const logger = new Logger("test-withdrawal-manual.ts");
 
@@ -34,14 +34,19 @@ const logger = new Logger("test-withdrawal-manual.ts");
 export async function runWithdrawalManualTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, exchangeBankAccount } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, exchangeBankAccount } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   // Create a withdrawal operation
 
-  const user = await BankApi.createRandomBankUser(bank);
+  const bankAccessApiClient = new BankAccessApiClient({
+    baseUrl: bank.bankAccessApiBaseUrl,
+    allowHttp: true,
+  });
+
+  const user = await bankAccessApiClient.createRandomBankUser();
 
-  await wallet.client.call(WalletApiOperation.AddExchange, {
+  await walletClient.call(WalletApiOperation.AddExchange, {
     exchangeBaseUrl: exchange.baseUrl,
   });
 
@@ -50,7 +55,7 @@ export async function runWithdrawalManualTest(t: 
GlobalTestState) {
   logger.info("starting AcceptManualWithdrawal request");
   // We expect this to return immediately.
 
-  const wres = await wallet.client.call(
+  const wres = await walletClient.call(
     WalletApiOperation.AcceptManualWithdrawal,
     {
       exchangeBaseUrl: exchange.baseUrl,
@@ -84,11 +89,11 @@ export async function runWithdrawalManualTest(t: 
GlobalTestState) {
 
   await exchange.runWirewatchOnce();
 
-  await wallet.runUntilDone();
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
   // Check balance
 
-  const balResp = await wallet.client.call(WalletApiOperation.GetBalances, {});
+  const balResp = await walletClient.call(WalletApiOperation.GetBalances, {});
   t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available);
 
   await t.shutdown();
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts 
b/packages/taler-harness/src/integrationtests/testrunner.ts
index c1b06f21e..cbdca04b9 100644
--- a/packages/taler-harness/src/integrationtests/testrunner.ts
+++ b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -494,8 +494,6 @@ if (runTestInstrStr && 
process.argv.includes("__TWCLI_TESTWORKER")) {
     runTestInstrStr,
   ) as RunTestChildInstruction;
 
-  setGlobalLogLevelFromString("TRACE");
-
   process.on("disconnect", () => {
     logger.trace("got disconnect from parent");
     process.exit(3);
diff --git a/packages/taler-util/src/talerconfig.ts 
b/packages/taler-util/src/talerconfig.ts
index 59c789cae..948ccb9c4 100644
--- a/packages/taler-util/src/talerconfig.ts
+++ b/packages/taler-util/src/talerconfig.ts
@@ -39,10 +39,20 @@ export class ConfigError extends Error {
   }
 }
 
+enum EntryOrigin {
+  // From a default file
+  Default = 1,
+  // Loaded from file or string
+  Loaded = 2,
+  // Changed after loading
+  Changed = 3,
+}
+
 interface Entry {
   value: string;
   sourceLine: number;
   sourceFile: string;
+  origin: EntryOrigin;
 }
 
 interface Section {
@@ -195,6 +205,7 @@ export interface LoadOptions {
 
 export interface StringifyOptions {
   diagnostics?: boolean;
+  excludeDefaults?: boolean;
 }
 
 export interface LoadedFile {
@@ -282,7 +293,11 @@ export class Configuration {
 
   private nestLevel = 0;
 
-  private loadFromFilename(filename: string, opts: LoadOptions = {}): void {
+  private loadFromFilename(
+    filename: string,
+    isDefaultSource: boolean,
+    opts: LoadOptions = {},
+  ): void {
     filename = expandPath(filename);
 
     const checkCycle = () => {
@@ -309,7 +324,7 @@ export class Configuration {
     const oldNestLevel = this.nestLevel;
     this.nestLevel += 1;
     try {
-      this.loadFromString(s, {
+      this.internalLoadFromString(s, isDefaultSource, {
         ...opts,
         filename: filename,
       });
@@ -318,7 +333,11 @@ export class Configuration {
     }
   }
 
-  private loadGlob(parentFilename: string, fileglob: string): void {
+  private loadGlob(
+    parentFilename: string,
+    isDefaultSource: boolean,
+    fileglob: string,
+  ): void {
     const resolvedParent = nodejs_fs.realpathSync(parentFilename);
     const parentDir = nodejs_path.dirname(resolvedParent);
 
@@ -339,12 +358,16 @@ export class Configuration {
     for (const f of files) {
       if (globMatch(tail, f)) {
         const fullPath = nodejs_path.join(head, f);
-        this.loadFromFilename(fullPath);
+        this.loadFromFilename(fullPath, isDefaultSource);
       }
     }
   }
 
-  private loadSecret(sectionName: string, filename: string): void {
+  private loadSecret(
+    sectionName: string,
+    filename: string,
+    isDefaultSource: boolean,
+  ): void {
     const sec = this.provideSection(sectionName);
     sec.secretFilename = filename;
     const otherCfg = new Configuration();
@@ -354,7 +377,7 @@ export class Configuration {
       sec.inaccessible = true;
       return;
     }
-    otherCfg.loadFromFilename(filename, {
+    otherCfg.loadFromFilename(filename, isDefaultSource, {
       banDirectives: true,
     });
     const otherSec = otherCfg.provideSection(sectionName);
@@ -363,7 +386,11 @@ export class Configuration {
     }
   }
 
-  loadFromString(s: string, opts: LoadOptions = {}): void {
+  private internalLoadFromString(
+    s: string,
+    isDefaultSource: boolean,
+    opts: LoadOptions = {},
+  ): void {
     let lineNo = 0;
     const fn = opts.filename ?? "<input>";
     const reComment = /^\s*#.*$/;
@@ -399,7 +426,10 @@ export class Configuration {
               );
             }
             const arg = directiveMatch[2].trim();
-            this.loadFromFilename(normalizeInlineFilename(opts.filename, arg));
+            this.loadFromFilename(
+              normalizeInlineFilename(opts.filename, arg),
+              isDefaultSource,
+            );
             break;
           }
           case "inline-secret": {
@@ -419,7 +449,7 @@ export class Configuration {
               opts.filename,
               sp[1],
             );
-            this.loadSecret(sp[0], secretFilename);
+            this.loadSecret(sp[0], secretFilename, isDefaultSource);
             break;
           }
           case "inline-matching": {
@@ -429,7 +459,7 @@ export class Configuration {
                 `invalid configuration, @inline-matching@ directive in 
${fn}:${lineNo} can only be used from a file`,
               );
             }
-            this.loadGlob(opts.filename, arg);
+            this.loadGlob(opts.filename, isDefaultSource, arg);
             break;
           }
           default:
@@ -462,6 +492,7 @@ export class Configuration {
           value: val,
           sourceFile: opts.filename ?? "<unknown>",
           sourceLine: lineNo,
+          origin: isDefaultSource ? EntryOrigin.Default : EntryOrigin.Loaded,
         };
         continue;
       }
@@ -471,6 +502,10 @@ export class Configuration {
     }
   }
 
+  loadFromString(s: string, opts: LoadOptions = {}): void {
+    return this.internalLoadFromString(s, false, opts);
+  }
+
   private provideSection(section: string): Section {
     const secNorm = section.toUpperCase();
     if (this.sectionMap[secNorm]) {
@@ -496,6 +531,7 @@ export class Configuration {
       value,
       sourceLine: 0,
       sourceFile: "<unknown>",
+      origin: EntryOrigin.Changed,
     };
   }
 
@@ -578,11 +614,11 @@ export class Configuration {
     );
   }
 
-  loadFrom(dirname: string): void {
+  loadDefaultsFromDir(dirname: string): void {
     const files = nodejs_fs.readdirSync(dirname);
     for (const f of files) {
       const fn = nodejs_path.join(dirname, f);
-      this.loadFromFilename(fn);
+      this.loadFromFilename(fn, true);
     }
   }
 
@@ -601,7 +637,7 @@ export class Configuration {
     if (!bc) {
       bc = "/usr/share/taler/config.d";
     }
-    this.loadFrom(bc);
+    this.loadDefaultsFromDir(bc);
   }
 
   getDefaultConfigFilename(): string | undefined {
@@ -631,11 +667,13 @@ export class Configuration {
     const cfg = new Configuration();
     cfg.loadDefaults();
     if (filename) {
-      cfg.loadFromFilename(filename);
+      cfg.loadFromFilename(filename, false);
     } else {
       const fn = cfg.getDefaultConfigFilename();
       if (fn) {
-        cfg.loadFromFilename(fn);
+        // It's the default filename for the main config file,
+        // but we don't consider the values default values.
+        cfg.loadFromFilename(fn, false);
       }
     }
     cfg.hintEntrypoint = filename;
@@ -657,13 +695,20 @@ export class Configuration {
     }
     for (const sectionName of Object.keys(this.sectionMap)) {
       const sec = this.sectionMap[sectionName];
-      if (opts.diagnostics && sec.secretFilename) {
-        s += `# Secret section from ${sec.secretFilename}\n`;
-        s += `# Secret accessible: ${!sec.inaccessible}\n`;
-      }
-      s += `[${sectionName}]\n`;
+      let headerWritten = false;
       for (const optionName of Object.keys(sec.entries)) {
         const entry = this.sectionMap[sectionName].entries[optionName];
+        if (opts.excludeDefaults && entry.origin === EntryOrigin.Default) {
+          continue;
+        }
+        if (!headerWritten) {
+          if (opts.diagnostics && sec.secretFilename) {
+            s += `# Secret section from ${sec.secretFilename}\n`;
+            s += `# Secret accessible: ${!sec.inaccessible}\n`;
+          }
+          s += `[${sectionName}]\n`;
+          headerWritten = true;
+        }
         if (entry !== undefined) {
           if (opts.diagnostics) {
             s += `# ${entry.sourceFile}:${entry.sourceLine}\n`;
@@ -671,12 +716,17 @@ export class Configuration {
           s += `${optionName} = ${entry.value}\n`;
         }
       }
-      s += "\n";
+      if (headerWritten) {
+        s += "\n";
+      }
     }
     return s;
   }
 
-  write(filename: string): void {
-    nodejs_fs.writeFileSync(filename, this.stringify());
+  write(filename: string, opts: { excludeDefaults?: boolean } = {}): void {
+    nodejs_fs.writeFileSync(
+      filename,
+      this.stringify({ excludeDefaults: opts.excludeDefaults }),
+    );
   }
 }
diff --git a/packages/taler-wallet-core/src/bank-api-client.ts 
b/packages/taler-wallet-core/src/bank-api-client.ts
index 01c28e8e8..8e351cb48 100644
--- a/packages/taler-wallet-core/src/bank-api-client.ts
+++ b/packages/taler-wallet-core/src/bank-api-client.ts
@@ -267,8 +267,7 @@ export namespace BankAccessApi {
 
 export interface BankAccessApiClientArgs {
   baseUrl: string;
-  username: string;
-  password: string;
+  auth?: { username: string; password: string };
   enableThrottling?: boolean;
   allowHttp?: boolean;
 }
@@ -339,19 +338,33 @@ export class BankAccessApiClient {
     });
   }
 
-  async getTransactions(): Promise<void> {
+  setAuth(auth: { username: string; password: string }) {
+    this.args.auth = auth;
+  }
+
+  private makeAuthHeader(): Record<string, string> {
+    if (!this.args.auth) {
+      return {};
+    }
+    const authHeaderValue = makeBasicAuthHeader(
+      this.args.auth.username,
+      this.args.auth.password,
+    );
+    return {
+      Authorization: authHeaderValue,
+    };
+  }
+
+  async getTransactions(username: string): Promise<void> {
+    const auth = this.args.auth;
     const reqUrl = new URL(
-      `accounts/${this.args.username}/transactions`,
+      `accounts/${username}/transactions`,
       this.args.baseUrl,
     );
-    const authHeaderValue = makeBasicAuthHeader(
-      this.args.username,
-      this.args.password,
-    );
     const resp = await this.httpLib.fetch(reqUrl.href, {
       method: "GET",
       headers: {
-        Authorization: authHeaderValue,
+        ...this.makeAuthHeader(),
       },
     });
 
@@ -360,24 +373,128 @@ export class BankAccessApiClient {
   }
 
   async createTransaction(
+    username: string,
     req: BankAccessApiCreateTransactionRequest,
   ): Promise<any> {
     const reqUrl = new URL(
-      `accounts/${this.args.username}/transactions`,
+      `accounts/${username}/transactions`,
       this.args.baseUrl,
     );
-    const authHeaderValue = makeBasicAuthHeader(
-      this.args.username,
-      this.args.password,
-    );
+
     const resp = await this.httpLib.fetch(reqUrl.href, {
       method: "POST",
       body: req,
-      headers: {
-        Authorization: authHeaderValue,
-      },
+      headers: this.makeAuthHeader(),
     });
 
     return await readSuccessResponseJsonOrThrow(resp, codecForAny());
   }
+
+  async registerAccount(
+    username: string,
+    password: string,
+    options: {
+      iban?: string;
+    },
+  ): Promise<BankUser> {
+    const url = new URL("testing/register", this.args.baseUrl);
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body: {
+        username,
+        password,
+        iban: options?.iban,
+      },
+    });
+    let paytoUri = `payto://x-taler-bank/localhost/${username}`;
+    if (resp.status !== 200 && resp.status !== 202 && resp.status !== 204) {
+      logger.error(`${j2s(await resp.json())}`);
+      throw TalerError.fromDetail(
+        TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR,
+        {
+          httpStatusCode: resp.status,
+        },
+      );
+    }
+    try {
+      // Pybank has no body, thus this might throw.
+      const respJson = await resp.json();
+      // LibEuFin demobank returns payto URI in response
+      if (respJson.paytoUri) {
+        paytoUri = respJson.paytoUri;
+      }
+    } catch (e) {
+      // Do nothing
+    }
+    return {
+      password,
+      username,
+      accountPaytoUri: paytoUri,
+    };
+  }
+
+  async createRandomBankUser(): Promise<BankUser> {
+    const username = "user-" + encodeCrock(getRandomBytes(10)).toLowerCase();
+    const password = "pw-" + encodeCrock(getRandomBytes(10)).toLowerCase();
+    // FIXME: This is just a temporary workaround, because demobank is running 
out of short IBANs
+    const iban = generateIban("DE", 15);
+    return await this.registerAccount(username, password, {
+      iban,
+    });
+  }
+
+  async createWithdrawalOperation(
+    user: string,
+    amount: string,
+  ): Promise<WithdrawalOperationInfo> {
+    const url = new URL(`accounts/${user}/withdrawals`, this.args.baseUrl);
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body: {
+        amount,
+      },
+      headers: this.makeAuthHeader(),
+    });
+    return readSuccessResponseJsonOrThrow(
+      resp,
+      codecForWithdrawalOperationInfo(),
+    );
+  }
+
+  async confirmWithdrawalOperation(
+    username: string,
+    wopi: WithdrawalOperationInfo,
+  ): Promise<void> {
+    const url = new URL(
+      `accounts/${username}/withdrawals/${wopi.withdrawal_id}/confirm`,
+      this.args.baseUrl,
+    );
+    logger.info(`confirming withdrawal operation via ${url.href}`);
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body: {},
+      headers: this.makeAuthHeader(),
+    });
+
+    logger.info(`response status ${resp.status}`);
+    const respJson = await readSuccessResponseJsonOrThrow(resp, codecForAny());
+
+    // FIXME: We don't check the status here!
+  }
+
+  async abortWithdrawalOperation(
+    accountName: string,
+    wopi: WithdrawalOperationInfo,
+  ): Promise<void> {
+    const url = new URL(
+      `accounts/${accountName}/withdrawals/${wopi.withdrawal_id}/abort`,
+      this.args.baseUrl,
+    );
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body: {},
+      headers: this.makeAuthHeader(),
+    });
+    await readSuccessResponseJsonOrThrow(resp, codecForAny());
+  }
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]