gnunet-svn
[Top][All Lists]
Advanced

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

[taler-typescript-core] branch master updated: unify diverged exchange c


From: Admin
Subject: [taler-typescript-core] branch master updated: unify diverged exchange clients
Date: Thu, 05 Jun 2025 02:22:38 +0200

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 6eee7ef19 unify diverged exchange clients
6eee7ef19 is described below

commit 6eee7ef1967d69875684f7d80d79c8cdd5a912f4
Author: Florian Dold <florian@dold.me>
AuthorDate: Thu Jun 5 02:21:45 2025 +0200

    unify diverged exchange clients
    
    Issue: https://bugs.taler.net/n/10049
---
 packages/aml-backoffice-ui/src/App.tsx             |   14 +-
 packages/aml-backoffice-ui/src/hooks/account.ts    |    4 +-
 packages/aml-backoffice-ui/src/hooks/decisions.ts  |   12 +-
 .../aml-backoffice-ui/src/hooks/server-info.ts     |    9 +-
 packages/aml-backoffice-ui/src/hooks/transfers.ts  |    8 +-
 .../aml-backoffice-ui/src/pages/CaseDetails.tsx    |    6 +-
 packages/aml-backoffice-ui/src/pages/Search.tsx    |    7 +-
 .../src/pages/ShowCollectedInfo.tsx                |    1 -
 packages/aml-backoffice-ui/src/pages/Transfers.tsx |   27 +-
 .../aml-backoffice-ui/src/pages/decision/Rules.tsx |  168 ++--
 packages/bank-ui/src/hooks/account.ts              |    7 +-
 packages/kyc-ui/src/hooks/kyc.ts                   |    8 +-
 packages/kyc-ui/src/pages/FillForm.tsx             |   22 +-
 packages/taler-harness/src/harness/environments.ts |    6 +-
 packages/taler-harness/src/harness/tops.ts         |    9 +-
 .../integrationtests/test-withdrawal-idempotent.ts |    4 +-
 packages/taler-util/src/aml/reporting.ts           |   42 +-
 .../taler-util/src/http-client/exchange-client.ts  |  137 ++-
 packages/taler-util/src/http-client/exchange.ts    | 1001 --------------------
 .../taler-util/src/http-client/officer-account.ts  |   31 +-
 packages/taler-util/src/index.ts                   |    1 -
 packages/taler-util/src/types-taler-common.ts      |   26 +-
 packages/taler-util/src/types-taler-exchange.ts    |    3 +
 packages/taler-wallet-core/src/dbless.ts           |    6 +-
 packages/taler-wallet-core/src/versions.ts         |    4 +-
 packages/taler-wallet-core/src/wallet.ts           |    6 +-
 packages/web-util/src/context/exchange-api.ts      |    2 +-
 packages/web-util/src/hooks/useAsync.ts            |    8 +-
 28 files changed, 318 insertions(+), 1261 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/App.tsx 
b/packages/aml-backoffice-ui/src/App.tsx
index cb9abd800..6b0efd2a5 100644
--- a/packages/aml-backoffice-ui/src/App.tsx
+++ b/packages/aml-backoffice-ui/src/App.tsx
@@ -24,21 +24,23 @@ import {
   ExchangeApiProvider,
   Loading,
   TranslationProvider,
-  UiForms,
 } from "@gnu-taler/web-util/browser";
 import { VNode, h } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import { SWRConfig } from "swr";
 import { ExchangeAmlFrame } from "./ExchangeAmlFrame.js";
 import { Routing } from "./Routing.js";
-import { UiSettingsProvider } from "./context/ui-settings.js";
-import { strings } from "./i18n/strings.js";
-import "./scss/main.css";
-import { UiSettings, fetchUiSettings } from "./context/ui-settings.js";
 import { UiFormsProvider } from "./context/ui-forms.js";
-import { revalidateAccountDecisions } from "./hooks/decisions.js";
+import {
+  UiSettings,
+  UiSettingsProvider,
+  fetchUiSettings,
+} from "./context/ui-settings.js";
 import { revalidateAccountInformation } from "./hooks/account.js";
+import { revalidateAccountDecisions } from "./hooks/decisions.js";
 import { usePreferences } from "./hooks/preferences.js";
+import { strings } from "./i18n/strings.js";
+import "./scss/main.css";
 
 const WITH_LOCAL_STORAGE_CACHE = false;
 
diff --git a/packages/aml-backoffice-ui/src/hooks/account.ts 
b/packages/aml-backoffice-ui/src/hooks/account.ts
index f34da0825..b71a70c85 100644
--- a/packages/aml-backoffice-ui/src/hooks/account.ts
+++ b/packages/aml-backoffice-ui/src/hooks/account.ts
@@ -16,7 +16,7 @@
 import {
   OfficerAccount,
   PaytoString,
-  TalerExchangeResultByMethod,
+  TalerExchangeResultByMethod2,
   TalerHttpError,
 } from "@gnu-taler/taler-util";
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
@@ -50,7 +50,7 @@ export function useAccountInformation(paytoHash?: string) {
   }
 
   const { data, error } = useSWR<
-    TalerExchangeResultByMethod<"getAmlAttributesForAccount">,
+    TalerExchangeResultByMethod2<"getAmlAttributesForAccount">,
     TalerHttpError
   >(!session ? undefined : [session, paytoHash], fetcher, {
     refreshInterval: 0,
diff --git a/packages/aml-backoffice-ui/src/hooks/decisions.ts 
b/packages/aml-backoffice-ui/src/hooks/decisions.ts
index 7a0846ca4..29b8ef222 100644
--- a/packages/aml-backoffice-ui/src/hooks/decisions.ts
+++ b/packages/aml-backoffice-ui/src/hooks/decisions.ts
@@ -17,14 +17,10 @@ import { useState } from "preact/hooks";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
 import {
-  AmlDecision,
-  HttpStatusCode,
   OfficerAccount,
-  OperationFail,
   OperationOk,
   opFixedSuccess,
-  TalerError,
-  TalerExchangeResultByMethod,
+  TalerExchangeResultByMethod2,
   TalerHttpError,
 } from "@gnu-taler/taler-util";
 import { useExchangeApiContext } from "@gnu-taler/web-util/browser";
@@ -70,7 +66,7 @@ export function useCurrentDecisions({
   }
 
   const { data, error } = useSWR<
-    TalerExchangeResultByMethod<"getAmlDecisions">,
+    TalerExchangeResultByMethod2<"getAmlDecisions">,
     TalerHttpError
   >(
     !session ? undefined : [session, offset, investigated, "getAmlDecisions"],
@@ -121,7 +117,7 @@ export function useAccountDecisions(accountStr: string) {
   }
 
   const { data, error } = useSWR<
-    TalerExchangeResultByMethod<"getAmlDecisions">,
+    TalerExchangeResultByMethod2<"getAmlDecisions">,
     TalerHttpError
   >(
     !session ? undefined : [session, accountStr, offset, "getAmlDecisions"],
@@ -171,7 +167,7 @@ export function useAccountActiveDecision(accountStr?: 
string) {
   }
 
   const { data, error } = useSWR<
-    TalerExchangeResultByMethod<"getAmlDecisions">,
+    TalerExchangeResultByMethod2<"getAmlDecisions">,
     TalerHttpError
   >(
     !session ? undefined : [session, accountStr, offset, "getAmlDecisions"],
diff --git a/packages/aml-backoffice-ui/src/hooks/server-info.ts 
b/packages/aml-backoffice-ui/src/hooks/server-info.ts
index b10f2f7d6..0096fef8a 100644
--- a/packages/aml-backoffice-ui/src/hooks/server-info.ts
+++ b/packages/aml-backoffice-ui/src/hooks/server-info.ts
@@ -16,9 +16,7 @@
 import {
   AbsoluteTime,
   CounterResultByEventName,
-  EventReporting_TOPS_calculation,
   EventReporting_TOPS_queries,
-  EventReporting_VQF_calculation,
   EventReporting_VQF_queries,
   fetchTopsInfoFromServer,
   fetchVqfInfoFromServer,
@@ -26,16 +24,15 @@ import {
   OfficerAccount,
   OperationOk,
   opFixedSuccess,
-  TalerExchangeHttpClient,
-  TalerExchangeResultByMethod,
+  TalerExchangeResultByMethod2,
   TalerHttpError,
   TOPS_AmlEventsName,
 } from "@gnu-taler/taler-util";
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
 import { useExchangeApiContext } from "@gnu-taler/web-util/browser";
+import { endOfYear, setYear, startOfYear } from "date-fns";
 import _useSWR, { SWRHook } from "swr";
 import { useOfficer } from "./officer.js";
-import { endOfYear, setYear, startOfYear } from "date-fns";
 const useSWR = _useSWR as unknown as SWRHook;
 
 export function useServerMeasures() {
@@ -51,7 +48,7 @@ export function useServerMeasures() {
   }
 
   const { data, error } = useSWR<
-    TalerExchangeResultByMethod<"getAmlMeasures">,
+    TalerExchangeResultByMethod2<"getAmlMeasures">,
     TalerHttpError
   >(!session ? undefined : [session], fetcher, {
     refreshInterval: 0,
diff --git a/packages/aml-backoffice-ui/src/hooks/transfers.ts 
b/packages/aml-backoffice-ui/src/hooks/transfers.ts
index eef7eb129..7df794927 100644
--- a/packages/aml-backoffice-ui/src/hooks/transfers.ts
+++ b/packages/aml-backoffice-ui/src/hooks/transfers.ts
@@ -20,8 +20,7 @@ import {
   AmountJson,
   OfficerAccount,
   OperationOk,
-  opFixedSuccess,
-  TalerExchangeResultByMethod,
+  TalerExchangeResultByMethod2,
   TalerHttpError,
 } from "@gnu-taler/taler-util";
 import { useExchangeApiContext } from "@gnu-taler/web-util/browser";
@@ -68,7 +67,7 @@ export function useTransferDebit() {
   }
 
   const { data, error } = useSWR<
-    TalerExchangeResultByMethod<"getTransfersDebit">,
+    TalerExchangeResultByMethod2<"getTransfersDebit">,
     TalerHttpError
   >(!session ? undefined : [session, offset, "getTransfersDebit"], fetcher);
 
@@ -121,7 +120,7 @@ export function useTransferList({
   }
 
   const { data, error } = useSWR<
-    TalerExchangeResultByMethod<"getTransfersCredit">,
+    TalerExchangeResultByMethod2<"getTransfersCredit">,
     TalerHttpError
   >(
     !session
@@ -139,7 +138,6 @@ export function useTransferList({
   );
 }
 
-
 type PaginatedResult<T> = OperationOk<T> & {
   isLastPage: boolean;
   isFirstPage: boolean;
diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx 
b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
index ed228a50a..990da4fe0 100644
--- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
@@ -54,16 +54,13 @@ import { Fragment, h, Ref, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
 import { useAccountInformation } from "../hooks/account.js";
-import {
-  DecisionRequest,
-} from "../hooks/decision-request.js";
+import { DecisionRequest } from "../hooks/decision-request.js";
 import { useAccountDecisions } from "../hooks/decisions.js";
 import { useOfficer } from "../hooks/officer.js";
 import { useServerMeasures } from "../hooks/server-info.js";
 import { CurrentMeasureTable, MeasureInfo, Mesaures } from 
"./MeasuresTable.js";
 import { Officer } from "./Officer.js";
 import { RulesInfo } from "./RulesInfo.js";
-import { ShowConsolidated } from "./ShowConsolidated.js";
 
 // export type AmlEvent =
 //   | AmlFormEvent
@@ -172,7 +169,6 @@ export function CaseDetails({
 
   // const events = getEventsFromAmlHistory(accountDetails, i18n);
 
-  
   function ShortcutActionButtons(): VNode {
     return (
       <div>
diff --git a/packages/aml-backoffice-ui/src/pages/Search.tsx 
b/packages/aml-backoffice-ui/src/pages/Search.tsx
index 43e2419cf..f6b4c9775 100644
--- a/packages/aml-backoffice-ui/src/pages/Search.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Search.tsx
@@ -38,7 +38,6 @@ import {
   Loading,
   Time,
   UIFormElementConfig,
-  UIHandlerId,
   useExchangeApiContext,
   useForm,
   useTranslationContext,
@@ -52,7 +51,6 @@ import { privatePages } from "../Routing.js";
 import { Pagination, ToInvestigateIcon } from "./Cases.js";
 import { HandleAccountNotReady } from "./HandleAccountNotReady.js";
 import { Officer } from "./Officer.js";
-import { DecisionRequest } from "../hooks/decision-request.js";
 
 export function Search({
   onNewDecision,
@@ -298,10 +296,7 @@ function ShowResult({
           //   payto: encodeCrockForURI(paytoStr),
           // })}
           onClick={async () => {
-            onNewDecision(
-              account,
-              encodeCrockForURI(paytoStr),
-            );
+            onNewDecision(account, encodeCrockForURI(paytoStr));
           }}
           class="text-indigo-600 hover:text-indigo-900"
         >
diff --git a/packages/aml-backoffice-ui/src/pages/ShowCollectedInfo.tsx 
b/packages/aml-backoffice-ui/src/pages/ShowCollectedInfo.tsx
index d0e007b07..a98fe7ce9 100644
--- a/packages/aml-backoffice-ui/src/pages/ShowCollectedInfo.tsx
+++ b/packages/aml-backoffice-ui/src/pages/ShowCollectedInfo.tsx
@@ -13,7 +13,6 @@ import {
   FormUI,
   Loading,
   RouteDefinition,
-  useExchangeApiContext,
   useForm,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
diff --git a/packages/aml-backoffice-ui/src/pages/Transfers.tsx 
b/packages/aml-backoffice-ui/src/pages/Transfers.tsx
index 6a217c736..7215f9312 100644
--- a/packages/aml-backoffice-ui/src/pages/Transfers.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Transfers.tsx
@@ -1,3 +1,14 @@
+import {
+  AbsoluteTime,
+  AmountJson,
+  Amounts,
+  assertUnreachable,
+  CurrencySpecification,
+  encodeCrock,
+  hashNormalizedPaytoUri,
+  HttpStatusCode,
+  TalerError,
+} from "@gnu-taler/taler-util";
 import {
   Attention,
   FormDesign,
@@ -5,27 +16,15 @@ import {
   Loading,
   RouteDefinition,
   Time,
-  UIHandlerId,
   useExchangeApiContext,
   useForm,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
+import { format } from "date-fns";
 import { Fragment, h, VNode } from "preact";
-import { useTransferList } from "../hooks/transfers.js";
-import {
-  AbsoluteTime,
-  AmountJson,
-  Amounts,
-  assertUnreachable,
-  CurrencySpecification,
-  encodeCrock,
-  hashNormalizedPaytoUri,
-  HttpStatusCode,
-  TalerError,
-} from "@gnu-taler/taler-util";
 import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
+import { useTransferList } from "../hooks/transfers.js";
 import { Officer } from "./Officer.js";
-import { format } from "date-fns";
 
 export function Transfers({
   routeToCaseById,
diff --git a/packages/aml-backoffice-ui/src/pages/decision/Rules.tsx 
b/packages/aml-backoffice-ui/src/pages/decision/Rules.tsx
index 09b8ab998..d95bf8a7f 100644
--- a/packages/aml-backoffice-ui/src/pages/decision/Rules.tsx
+++ b/packages/aml-backoffice-ui/src/pages/decision/Rules.tsx
@@ -21,7 +21,6 @@ import {
   FormDesign,
   FormUI,
   InternationalizationAPI,
-  Loading,
   onComponentUnload,
   useExchangeApiContext,
   useForm,
@@ -30,7 +29,6 @@ import {
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { useCurrentDecisionRequest } from "../../hooks/decision-request.js";
-import { useAccountActiveDecision } from "../../hooks/decisions.js";
 import { useServerMeasures } from "../../hooks/server-info.js";
 import { ShowDecisionLimitInfo } from "../CaseDetails.js";
 import { RulesInfo } from "../RulesInfo.js";
@@ -119,14 +117,10 @@ export function findRuleInconsistency(
  * @param param0
  * @returns
  */
-export function Rules({
-  newPayto,
-}: {
-  newPayto?: PaytoString;
-}): VNode {
+export function Rules({ newPayto }: { newPayto?: PaytoString }): VNode {
   const { i18n } = useTranslationContext();
   const { config } = useExchangeApiContext();
-  const [request] = useCurrentDecisionRequest()
+  const [request] = useCurrentDecisionRequest();
 
   let newPaytoParsed: PaytoUri | undefined;
   const isNewAccountAWallet =
@@ -148,14 +142,15 @@ export function Rules({
       ? undefined
       : measures.body.roots;
 
-  const defaultRules =
-    (!measures || measures instanceof TalerError || measures.type === "fail"
+  const defaultRules = (
+    !measures || measures instanceof TalerError || measures.type === "fail"
       ? []
-      : measures.body.default_rules).filter((r) => {
-      return isWallet
-        ? WALLET_RULES.includes(r.operation_type)
-        : BANK_RULES.includes(r.operation_type);
-    });
+      : measures.body.default_rules
+  ).filter((r) => {
+    return isWallet
+      ? WALLET_RULES.includes(r.operation_type)
+      : BANK_RULES.includes(r.operation_type);
+  });
 
   return (
     <div>
@@ -180,7 +175,9 @@ export function Rules({
         ) : (
           <ShowDecisionLimitInfo
             fixed
-            
since={AbsoluteTime.fromProtocolTimestamp(request.original.decision_time)}
+            since={AbsoluteTime.fromProtocolTimestamp(
+              request.original.decision_time,
+            )}
             until={AbsoluteTime.fromProtocolTimestamp(
               request.original.limits.expiration_time,
             )}
@@ -258,11 +255,10 @@ function UpdateRulesForm({
   limits: LegitimizationRuleSet;
   isWallet: boolean | undefined;
   rootMeasures: AvailableMeasureSummary["roots"] | undefined;
-  defaultRules: KycRule[]
+  defaultRules: KycRule[];
 }): VNode {
   const { i18n } = useTranslationContext();
-  const [request, updateRequest] =
-    useCurrentDecisionRequest();
+  const [request, updateRequest] = useCurrentDecisionRequest();
   const [showAddRuleForm, setShowAddRuleForm] = useState(false);
   const measureList = !rootMeasures ? [] : Object.keys(rootMeasures);
   const customMeasures = Object.keys(request.custom_measures ?? {});
@@ -270,7 +266,9 @@ function UpdateRulesForm({
     ...measureList,
     ...customMeasures,
   ]);
-  const [currentRules, setRules] = useState<KycRule[]>(!request.rules ? 
limits.rules : request.rules);
+  const [currentRules, setRules] = useState<KycRule[]>(
+    !request.rules ? limits.rules : request.rules,
+  );
   const expirationForm = useForm<ExpirationFormType>(expirationFormDesign, {
     expiration:
       request.deadline ??
@@ -284,7 +282,7 @@ function UpdateRulesForm({
         ? undefined
         : expirationForm.status.result.expiration;
     const doesntExpire = !deadline || AbsoluteTime.isNever(deadline);
-    updateRequest("unload rules",{
+    updateRequest("unload rules", {
       rules: currentRules,
       deadline,
       onExpire_measure:
@@ -427,7 +425,7 @@ function UpdateRulesForm({
       </button>
       <button
         onClick={() => {
-          setRules( FREEZE_PLAN(config.currency, isWallet));
+          setRules(FREEZE_PLAN(config.currency, isWallet));
         }}
         class="m-4  rounded-md w-fit border-0 px-3 py-2 text-center text-sm 
bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600"
       >
@@ -435,7 +433,7 @@ function UpdateRulesForm({
       </button>
       <button
         onClick={() => {
-          setRules( BASIC_PLAN(config.currency, isWallet));
+          setRules(BASIC_PLAN(config.currency, isWallet));
         }}
         class="m-4  rounded-md w-fit border-0 px-3 py-2 text-center text-sm 
bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600"
       >
@@ -443,7 +441,7 @@ function UpdateRulesForm({
       </button>
       <button
         onClick={() => {
-          setRules( PREMIUM_PLAN(config.currency, isWallet));
+          setRules(PREMIUM_PLAN(config.currency, isWallet));
         }}
         class="m-4  rounded-md w-fit border-0 px-3 py-2 text-center text-sm 
bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600"
       >
@@ -451,7 +449,7 @@ function UpdateRulesForm({
       </button>
       <button
         onClick={() => {
-          setRules( E_COMMERCE(config.currency, isWallet));
+          setRules(E_COMMERCE(config.currency, isWallet));
         }}
         class="m-4  rounded-md w-fit border-0 px-3 py-2 text-center text-sm 
bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600"
       >
@@ -459,7 +457,7 @@ function UpdateRulesForm({
       </button>
       <button
         onClick={() => {
-          setRules( POINT_OF_SALE(config.currency, isWallet));
+          setRules(POINT_OF_SALE(config.currency, isWallet));
         }}
         class="m-4  rounded-md w-fit border-0 px-3 py-2 text-center text-sm 
bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600"
       >
@@ -954,62 +952,62 @@ const POINT_OF_SALE: TemplateRulesFunction = (currency, 
isWallet) =>
       : BANK_RULES.includes(r.operation_type);
   });
 
-  const E_COMMERCE: TemplateRulesFunction = (currency, isWallet) =>
-    [
-      {
-        display_priority: 1,
-        measures: ["VERBOTEN"],
-        operation_type: LimitOperationType.withdraw,
-        threshold: Amounts.stringify({
-          currency,
-          fraction: 0,
-          value: 0,
-        }),
-        timeframe: Duration.toTalerProtocolDuration(
-          Duration.fromSpec({ months: 1 }),
-        ),
-      },
-      {
-        display_priority: 1,
-        measures: ["VERBOTEN"],
-        operation_type: LimitOperationType.merge,
-        threshold: Amounts.stringify({
-          currency,
-          fraction: 0,
-          value: 0,
-        }),
-        timeframe: Duration.toTalerProtocolDuration(
-          Duration.fromSpec({ months: 1 }),
-        ),
-      },
-      {
-        display_priority: 1,
-        measures: ["preserve-investigate"],
-        operation_type: LimitOperationType.deposit,
-        threshold: Amounts.stringify({
-          currency,
-          fraction: 0,
-          value: 25 * 1000,
-        }),
-        timeframe: Duration.toTalerProtocolDuration(
-          Duration.fromSpec({ months: 1 }),
-        ),
-      },
-      {
-        display_priority: 1,
-        measures: ["preserve-investigate"],
-        operation_type: LimitOperationType.aggregate,
-        threshold: Amounts.stringify({
-          currency,
-          fraction: 0,
-          value: 25 * 1000,
-        }),
-        timeframe: Duration.toTalerProtocolDuration(
-          Duration.fromSpec({ months: 1 }),
-        ),
-      },
-    ].filter((r) => {
-      return isWallet
-        ? WALLET_RULES.includes(r.operation_type)
-        : BANK_RULES.includes(r.operation_type);
-    });
\ No newline at end of file
+const E_COMMERCE: TemplateRulesFunction = (currency, isWallet) =>
+  [
+    {
+      display_priority: 1,
+      measures: ["VERBOTEN"],
+      operation_type: LimitOperationType.withdraw,
+      threshold: Amounts.stringify({
+        currency,
+        fraction: 0,
+        value: 0,
+      }),
+      timeframe: Duration.toTalerProtocolDuration(
+        Duration.fromSpec({ months: 1 }),
+      ),
+    },
+    {
+      display_priority: 1,
+      measures: ["VERBOTEN"],
+      operation_type: LimitOperationType.merge,
+      threshold: Amounts.stringify({
+        currency,
+        fraction: 0,
+        value: 0,
+      }),
+      timeframe: Duration.toTalerProtocolDuration(
+        Duration.fromSpec({ months: 1 }),
+      ),
+    },
+    {
+      display_priority: 1,
+      measures: ["preserve-investigate"],
+      operation_type: LimitOperationType.deposit,
+      threshold: Amounts.stringify({
+        currency,
+        fraction: 0,
+        value: 25 * 1000,
+      }),
+      timeframe: Duration.toTalerProtocolDuration(
+        Duration.fromSpec({ months: 1 }),
+      ),
+    },
+    {
+      display_priority: 1,
+      measures: ["preserve-investigate"],
+      operation_type: LimitOperationType.aggregate,
+      threshold: Amounts.stringify({
+        currency,
+        fraction: 0,
+        value: 25 * 1000,
+      }),
+      timeframe: Duration.toTalerProtocolDuration(
+        Duration.fromSpec({ months: 1 }),
+      ),
+    },
+  ].filter((r) => {
+    return isWallet
+      ? WALLET_RULES.includes(r.operation_type)
+      : BANK_RULES.includes(r.operation_type);
+  });
diff --git a/packages/bank-ui/src/hooks/account.ts 
b/packages/bank-ui/src/hooks/account.ts
index 2f4eda7c7..a555614d6 100644
--- a/packages/bank-ui/src/hooks/account.ts
+++ b/packages/bank-ui/src/hooks/account.ts
@@ -23,18 +23,15 @@ import {
   TalerCoreBankResultByMethod,
   TalerError,
   TalerHttpError,
-  WithdrawalOperationStatusFlag,
 } from "@gnu-taler/taler-util";
-import { useState, useMemo } from "preact/hooks";
+import { useState } from "preact/hooks";
 import { useSessionState } from "./session.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
 import {
-  delayMs,
-  preventBurst,
   useAsync,
-  useLongPolling,
   useBankCoreApiContext,
+  useLongPolling,
 } from "@gnu-taler/web-util/browser";
 import _useSWR, { mutate, SWRHook } from "swr";
 import { PAGINATED_LIST_REQUEST } from "../utils.js";
diff --git a/packages/kyc-ui/src/hooks/kyc.ts b/packages/kyc-ui/src/hooks/kyc.ts
index 4efdfc6ff..6f8e1d13f 100644
--- a/packages/kyc-ui/src/hooks/kyc.ts
+++ b/packages/kyc-ui/src/hooks/kyc.ts
@@ -13,11 +13,11 @@
  You should have received a copy of the GNU General Public License along with
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { opFixedSuccess } from "@gnu-taler/taler-util";
 import {
   AccessToken,
+  HttpStatusCode,
   KycProcessClientInformationWithEtag,
+  opFixedSuccess,
 } from "@gnu-taler/taler-util";
 import {
   useAsync,
@@ -44,7 +44,7 @@ export function useKycInfo(token?: AccessToken) {
     token === undefined
       ? undefined
       : () => {
-          return api.checkKycInfo(token, undefined);
+          return api.checkKycInfoSpa(token, undefined);
         },
     [token],
   );
@@ -57,7 +57,7 @@ export function useKycInfo(token?: AccessToken) {
       return result.body;
     },
     async (latestStatus: KycProcessClientInformationWithEtag) => {
-      const res = await api.checkKycInfo(token!, latestStatus.etag, {
+      const res = await api.checkKycInfoSpa(token!, latestStatus.etag, {
         timeoutMs: 5000,
       });
       if (res.type === "fail" && res.case === HttpStatusCode.NotModified) {
diff --git a/packages/kyc-ui/src/pages/FillForm.tsx 
b/packages/kyc-ui/src/pages/FillForm.tsx
index a0b863265..bf6b0e93e 100644
--- a/packages/kyc-ui/src/pages/FillForm.tsx
+++ b/packages/kyc-ui/src/pages/FillForm.tsx
@@ -16,10 +16,10 @@
 import {
   AbsoluteTime,
   AccessToken,
-  AmountJson,
-  Amounts,
   HttpStatusCode,
   KycRequirementInformation,
+  KycRequirementInformationId,
+  TalerFormAttributes,
   assertUnreachable,
 } from "@gnu-taler/taler-util";
 import {
@@ -42,8 +42,6 @@ import { Fragment, VNode, h } from "preact";
 import { usePreferences } from "../context/preferences.js";
 import { useUiFormsContext } from "../context/ui-forms.js";
 import { preloadedForms } from "../forms/index.js";
-import { TalerFormAttributes } from "@gnu-taler/taler-util";
-import { KycRequirementInformationId } from "@gnu-taler/taler-util";
 
 const TALER_SCREEN_ID = 103;
 
@@ -57,7 +55,7 @@ type Props = {
 type FormType = {
   form_id: string;
   [TalerFormAttributes.FORM_ID]: string;
-  [TalerFormAttributes.FORM_VERSION]: number,
+  [TalerFormAttributes.FORM_VERSION]: number;
   // state: TalerExchangeApi.AmlState;
 };
 
@@ -149,7 +147,7 @@ function ShowForm({
               case HttpStatusCode.Conflict:
                 return i18n.str`Officer disabled or more recent decision was 
already submitted.`;
               default:
-                assertUnreachable(fail.case);
+                assertUnreachable(fail);
             }
           },
         );
@@ -234,20 +232,12 @@ export function FillForm({
   if (!reqId) {
     return (
       <Attention title={i18n.str`Can't upload information`} type="danger">
-        <i18n.Translate>
-          The KYC requirement doesn't have an ID
-        </i18n.Translate>
+        <i18n.Translate>The KYC requirement doesn't have an ID</i18n.Translate>
       </Attention>
     );
   }
 
-  return (
-    <ShowForm
-      onComplete={onComplete}
-      reqId={reqId}
-      theForm={theForm}
-    />
-  );
+  return <ShowForm onComplete={onComplete} reqId={reqId} theForm={theForm} />;
 }
 
 function searchForm(
diff --git a/packages/taler-harness/src/harness/environments.ts 
b/packages/taler-harness/src/harness/environments.ts
index ac56d857e..b4c598d63 100644
--- a/packages/taler-harness/src/harness/environments.ts
+++ b/packages/taler-harness/src/harness/environments.ts
@@ -46,7 +46,7 @@ import {
   succeedOrThrow,
   TalerCorebankApiClient,
   TalerCoreBankHttpClient,
-  TalerExchangeHttpClient2,
+  TalerExchangeHttpClient,
   TalerMerchantApi,
   TalerMerchantInstanceHttpClient,
   TalerProtocolTimestamp,
@@ -1098,7 +1098,7 @@ export interface KycTestEnv {
   amlKeypair: EddsaKeyPairStrings;
   merchant: MerchantService;
   bankApi: TalerCoreBankHttpClient;
-  exchangeApi: TalerExchangeHttpClient2;
+  exchangeApi: TalerExchangeHttpClient;
   wireGatewayApi: TalerWireGatewayHttpClient;
   merchantApi: TalerMerchantInstanceHttpClient;
 }
@@ -1272,7 +1272,7 @@ export async function createKycTestkudosEnvironment(
     harnessHttpLib,
   );
 
-  const exchangeApi = new TalerExchangeHttpClient2(exchange.baseUrl, {
+  const exchangeApi = new TalerExchangeHttpClient(exchange.baseUrl, {
     httpClient: harnessHttpLib,
   });
 
diff --git a/packages/taler-harness/src/harness/tops.ts 
b/packages/taler-harness/src/harness/tops.ts
index 90d50b8f5..bff73afc3 100644
--- a/packages/taler-harness/src/harness/tops.ts
+++ b/packages/taler-harness/src/harness/tops.ts
@@ -36,7 +36,6 @@ import {
   TalerCorebankApiClient,
   TalerCoreBankHttpClient,
   TalerExchangeHttpClient,
-  TalerExchangeHttpClient2,
   TalerMerchantInstanceHttpClient,
   TalerProtocolDuration,
   TalerProtocolTimestamp,
@@ -948,7 +947,7 @@ export interface MeasuresTestEnvironment {
   challengerPostal: TestfakeChallengerService;
   challengerSms: TestfakeChallengerService;
   officerAcc: OfficerAccount;
-  exchangeClient: TalerExchangeHttpClient2;
+  exchangeClient: TalerExchangeHttpClient;
 }
 
 export async function setupMeasuresTestEnvironment(
@@ -975,7 +974,7 @@ export async function setupMeasuresTestEnvironment(
   const merchantClient = new TalerMerchantInstanceHttpClient(
     merchant.makeInstanceBaseUrl(),
   );
-  const exchangeClient = new TalerExchangeHttpClient2(exchange.baseUrl, {
+  const exchangeClient = new TalerExchangeHttpClient(exchange.baseUrl, {
     httpClient: harnessHttpLib,
   });
 
@@ -1143,7 +1142,7 @@ async function doTriggerReset(
   t: GlobalTestState,
   args: {
     officerAcc: OfficerAccount;
-    exchangeClient: TalerExchangeHttpClient2;
+    exchangeClient: TalerExchangeHttpClient;
     merchantPaytoHash: string;
   },
 ): Promise<void> {
@@ -1205,7 +1204,7 @@ async function doTriggerMeasure(
   t: GlobalTestState,
   args: {
     officerAcc: OfficerAccount;
-    exchangeClient: TalerExchangeHttpClient2;
+    exchangeClient: TalerExchangeHttpClient;
     merchantPaytoHash: string;
     measure: string;
   },
diff --git 
a/packages/taler-harness/src/integrationtests/test-withdrawal-idempotent.ts 
b/packages/taler-harness/src/integrationtests/test-withdrawal-idempotent.ts
index 147480442..f41fe0469 100644
--- a/packages/taler-harness/src/integrationtests/test-withdrawal-idempotent.ts
+++ b/packages/taler-harness/src/integrationtests/test-withdrawal-idempotent.ts
@@ -25,7 +25,7 @@ import {
   ExchangeWithdrawRequest,
   getRandomBytes,
   succeedOrThrow,
-  TalerExchangeHttpClient2,
+  TalerExchangeHttpClient,
 } from "@gnu-taler/taler-util";
 import { HttpRequestLibrary } from "@gnu-taler/taler-util/http";
 import {
@@ -117,7 +117,7 @@ async function myWithdrawCoin(args: {
     value: Amounts.parseOrThrow(denom.value),
   });
 
-  const exchangeClient = new TalerExchangeHttpClient2(exchangeBaseUrl);
+  const exchangeClient = new TalerExchangeHttpClient(exchangeBaseUrl);
 
   const sigResp = await cryptoApi.signWithdrawal({
     amount: Amounts.stringify(denom.value),
diff --git a/packages/taler-util/src/aml/reporting.ts 
b/packages/taler-util/src/aml/reporting.ts
index cd1a76772..610dea005 100644
--- a/packages/taler-util/src/aml/reporting.ts
+++ b/packages/taler-util/src/aml/reporting.ts
@@ -1,4 +1,10 @@
-import { AbsoluteTime, Duration, OfficerAccount, TalerExchangeHttpClient, 
TOPS_AmlEventsName } from "../index.js";
+import {
+  AbsoluteTime,
+  Duration,
+  OfficerAccount,
+  TalerExchangeHttpClient,
+  TOPS_AmlEventsName,
+} from "../index.js";
 
 /**
  * Define a set of parameters to make a request to the server
@@ -21,7 +27,7 @@ export type QueryModel<Ev> = {
  * https://docs.taler.net/deployments/tops.html#event-reporting-tops
  *
  * Maps a request key to request parameters
- * 
+ *
  */
 export const EventReporting_TOPS_queries = {
   // Number of accounts that are opened
@@ -107,12 +113,12 @@ export const EventReporting_TOPS_queries = {
 /**
  * All the calculation needed to create the Event Reporting (TOPS)
  * https://docs.taler.net/deployments/tops.html#event-reporting-tops
- * 
- * Maps a event reporting name with a calculation which uses the 
+ *
+ * Maps a event reporting name with a calculation which uses the
  * result of a query to the server.
- * 
+ *
  * @param events The result of event reporting query
- * @returns 
+ * @returns
  */
 export const EventReporting_TOPS_calculation = (
   events: CounterResultByEventName<typeof EventReporting_TOPS_queries>,
@@ -159,9 +165,12 @@ export const EventReporting_TOPS_calculation = (
  *
  * Maps a request key to request parameters.
  * Requires the times for the query range.
- * 
+ *
  */
-export const EventReporting_VQF_queries = (jan_1st: AbsoluteTime, dec_31st: 
AbsoluteTime) => {
+export const EventReporting_VQF_queries = (
+  jan_1st: AbsoluteTime,
+  dec_31st: AbsoluteTime,
+) => {
   const zero = AbsoluteTime.fromMilliseconds(0);
 
   return {
@@ -256,7 +265,9 @@ export const EventReporting_VQF_queries = (jan_1st: 
AbsoluteTime, dec_31st: Abso
 };
 
 export const EventReporting_VQF_calculation = (
-  events: CounterResultByEventName<ReturnType<typeof 
EventReporting_VQF_queries>>,
+  events: CounterResultByEventName<
+    ReturnType<typeof EventReporting_VQF_queries>
+  >,
 ) => {
   return {
     // Number of open accounts on January 1st (self-declaration 3.1.1)
@@ -336,8 +347,10 @@ function safeAdd(
   return a === undefined || b == undefined ? undefined : a + b;
 }
 
-
-export async function fetchTopsInfoFromServer(api: TalerExchangeHttpClient, 
officer: OfficerAccount) {
+export async function fetchTopsInfoFromServer(
+  api: TalerExchangeHttpClient,
+  officer: OfficerAccount,
+) {
   type EventType = typeof EventReporting_TOPS_queries;
   const eventList = Object.entries(EventReporting_TOPS_queries);
 
@@ -361,7 +374,12 @@ export async function fetchTopsInfoFromServer(api: 
TalerExchangeHttpClient, offi
   return EventReporting_TOPS_calculation(resultMap);
 }
 
-export async function fetchVqfInfoFromServer(api: TalerExchangeHttpClient, 
officer: OfficerAccount, jan_1st: AbsoluteTime, dec_31st: AbsoluteTime) {
+export async function fetchVqfInfoFromServer(
+  api: TalerExchangeHttpClient,
+  officer: OfficerAccount,
+  jan_1st: AbsoluteTime,
+  dec_31st: AbsoluteTime,
+) {
   const VQF_EVENTS_THIS_YEAR = EventReporting_VQF_queries(jan_1st, dec_31st);
   type EventType = typeof VQF_EVENTS_THIS_YEAR;
   const eventList = Object.entries(VQF_EVENTS_THIS_YEAR);
diff --git a/packages/taler-util/src/http-client/exchange-client.ts 
b/packages/taler-util/src/http-client/exchange-client.ts
index 4ad168193..84c50d31f 100644
--- a/packages/taler-util/src/http-client/exchange-client.ts
+++ b/packages/taler-util/src/http-client/exchange-client.ts
@@ -33,6 +33,7 @@ import {
   opEmptySuccess,
   opFixedSuccess,
   opKnownAlternativeHttpFailure,
+  opKnownFailure,
   opKnownHttpFailure,
   opSuccessFromHttp,
   opUnknownHttpFailure,
@@ -40,12 +41,11 @@ import {
 import { encodeCrock } from "../taler-crypto.js";
 import {
   AccessToken,
-  AmountString,
   EddsaPublicKeyString,
   EddsaSignatureString,
+  LongPollParams,
   OfficerAccount,
   PaginationParams,
-  ReserveAccount,
   codecForTalerCommonConfigResponse,
 } from "../types-taler-common.js";
 import {
@@ -72,12 +72,13 @@ import {
   ExchangeWithdrawResponse,
   KycAttributes,
   KycProcessClientInformation,
+  KycProcessClientInformationWithEtag,
   KycProcessStartInformation,
   KycRequirementInformationId,
+  LegitimizationMeasuresList,
   LegitimizationNeededResponse,
   PurseConflict,
   PurseConflictPartial,
-  WalletKycCheckResponse,
   WalletKycRequest,
   codecForAccountKycStatus,
   codecForAmlDecisionsResponse,
@@ -100,7 +101,12 @@ import {
   codecForPurseConflict,
   codecForPurseConflictPartial,
 } from "../types-taler-exchange.js";
-import { CacheEvictor, addPaginationParams, nullEvictor } from "./utils.js";
+import {
+  CacheEvictor,
+  addLongPollingParam,
+  addPaginationParams,
+  nullEvictor,
+} from "./utils.js";
 
 import { TalerError } from "../errors.js";
 import {
@@ -110,20 +116,19 @@ import {
   LongpollQueue,
   signAmlDecision,
   signAmlQuery,
-  signWalletAccountSetup,
 } from "../index.js";
 import { TalerErrorCode } from "../taler-error-codes.js";
 import { AbsoluteTime } from "../time.js";
 import { EmptyObject, codecForEmptyObject } from "../types-taler-wallet.js";
 
 export type TalerExchangeResultByMethod2<
-  prop extends keyof TalerExchangeHttpClient2,
-> = ResultByMethod<TalerExchangeHttpClient2, prop>;
+  prop extends keyof TalerExchangeHttpClient,
+> = ResultByMethod<TalerExchangeHttpClient, prop>;
 export type TalerExchangeErrorsByMethod2<
-  prop extends keyof TalerExchangeHttpClient2,
-> = FailCasesByMethod<TalerExchangeHttpClient2, prop>;
+  prop extends keyof TalerExchangeHttpClient,
+> = FailCasesByMethod<TalerExchangeHttpClient, prop>;
 
-export enum TalerExchangeCacheEviction2 {
+export enum TalerExchangeCacheEviction {
   UPLOAD_KYC_FORM,
   MAKE_AML_DECISION,
 }
@@ -131,10 +136,10 @@ export enum TalerExchangeCacheEviction2 {
 /**
  * Client library for the GNU Taler exchange service.
  */
-export class TalerExchangeHttpClient2 {
+export class TalerExchangeHttpClient {
   public static readonly SUPPORTED_EXCHANGE_PROTOCOL_VERSION = "27:0:2";
   private httpLib: HttpRequestLibrary;
-  private cacheEvictor: CacheEvictor<TalerExchangeCacheEviction2>;
+  private cacheEvictor: CacheEvictor<TalerExchangeCacheEviction>;
   private preventCompression: boolean;
   private cancelationToken: CancellationToken;
   private longPollQueue: LongpollQueue;
@@ -143,7 +148,7 @@ export class TalerExchangeHttpClient2 {
     readonly baseUrl: string,
     params: {
       httpClient?: HttpRequestLibrary;
-      cacheEvictor?: CacheEvictor<TalerExchangeCacheEviction2>;
+      cacheEvictor?: CacheEvictor<TalerExchangeCacheEviction>;
       preventCompression?: boolean;
       cancelationToken?: CancellationToken;
       longPollQueue?: LongpollQueue;
@@ -159,7 +164,7 @@ export class TalerExchangeHttpClient2 {
 
   isCompatible(version: string): boolean {
     const compare = LibtoolVersion.compare(
-      TalerExchangeHttpClient2.SUPPORTED_EXCHANGE_PROTOCOL_VERSION,
+      TalerExchangeHttpClient.SUPPORTED_EXCHANGE_PROTOCOL_VERSION,
       version,
     );
     return compare?.compatible ?? false;
@@ -243,7 +248,7 @@ export class TalerExchangeHttpClient2 {
             code: TalerErrorCode.GENERIC_CLIENT_UNSUPPORTED_PROTOCOL_VERSION,
             requestUrl: resp.requestUrl,
             httpStatusCode: resp.status,
-            detail: `Unsupported protocol version, client supports 
${TalerExchangeHttpClient2.SUPPORTED_EXCHANGE_PROTOCOL_VERSION}, server 
supports ${minBody.version}`,
+            detail: `Unsupported protocol version, client supports 
${TalerExchangeHttpClient.SUPPORTED_EXCHANGE_PROTOCOL_VERSION}, server supports 
${minBody.version}`,
           });
         }
         // Now that we've checked the basic body, re-parse the full response.
@@ -568,26 +573,10 @@ export class TalerExchangeHttpClient2 {
    * https://docs.taler.net/core/api-exchange.html#post--kyc-wallet
    *
    */
-  async notifyKycBalanceLimit(
-    account: ReserveAccount,
-    balance: AmountString,
-  ): Promise<
-    | OperationOk<void>
-    | OperationFail<HttpStatusCode.Forbidden>
-    | OperationAlternative<
-        HttpStatusCode.UnavailableForLegalReasons,
-        LegitimizationNeededResponse
-      >
-    | OperationOk<WalletKycCheckResponse>
-  > {
-    const body: WalletKycRequest = {
-      balance,
-      reserve_pub: account.id as any,
-      reserve_sig: encodeCrock(
-        signWalletAccountSetup(account.signingKey, balance),
-      ) as any,
-    };
-    const resp = await this.fetch(`kyc-wallet`, {
+  async notifyKycBalanceLimit(body: WalletKycRequest) {
+    const url = new URL(`kyc-wallet`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
       method: "POST",
       body,
     });
@@ -610,6 +599,35 @@ export class TalerExchangeHttpClient2 {
     }
   }
 
+  async getAmlLegitimizations(args: {
+    officerAcc: OfficerAccount;
+  }): Promise<OperationOk<LegitimizationMeasuresList>> {
+    const url = new URL(
+      `aml/${args.officerAcc.id}/legitimizations`,
+      this.baseUrl,
+    );
+
+    const resp = await this.httpLib.fetch(url.href, {
+      headers: {
+        "Taler-AML-Officer-Signature": encodeCrock(
+          signAmlQuery(args.officerAcc.signingKey),
+        ),
+      },
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        // FIXME: Parse properly.
+        return opSuccessFromHttp(resp, codecForAny());
+      case HttpStatusCode.NoContent:
+        return opFixedSuccess({
+          measures: [],
+        });
+      default:
+        return opUnknownHttpFailure(resp);
+    }
+  }
+
   /**
    * 
https://docs.taler.net/core/api-exchange.html#get--kyc-check-$H_NORMALIZED_PAYTO
    *
@@ -759,6 +777,51 @@ export class TalerExchangeHttpClient2 {
     }
   }
 
+  /**
+   * SPA-Specific version of checkKycInfo
+   */
+  async checkKycInfoSpa(
+    token: AccessToken,
+    etag: string | undefined,
+    params: LongPollParams = {},
+  ) {
+    const url = new URL(`kyc-info/${token}`, this.baseUrl);
+
+    addLongPollingParam(url, params);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+      headers: {
+        "If-None-Match": etag,
+      },
+    });
+    switch (resp.status) {
+      case HttpStatusCode.Ok: {
+        // we need to add the etag to the response because the
+        // client needs it to repeat the request and
+        // do the long polling
+        const etag = resp.headers.get("etag") ?? undefined;
+        const body = await readSuccessResponseJsonOrThrow(
+          resp,
+          codecForKycProcessClientInformation(),
+        );
+        return opFixedSuccess<KycProcessClientInformationWithEtag>({
+          ...body,
+          etag,
+        });
+      }
+      case HttpStatusCode.Accepted:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NoContent:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotModified:
+        // do not read details from response
+        return opKnownFailure(resp.status);
+      default:
+        return opUnknownHttpFailure(resp);
+    }
+  }
+
   /**
    * https://docs.taler.net/core/api-exchange.html#post--kyc-upload-$ID
    *
@@ -780,7 +843,7 @@ export class TalerExchangeHttpClient2 {
     switch (resp.status) {
       case HttpStatusCode.NoContent: {
         this.cacheEvictor.notifySuccess(
-          TalerExchangeCacheEviction2.UPLOAD_KYC_FORM,
+          TalerExchangeCacheEviction.UPLOAD_KYC_FORM,
         );
         return opEmptySuccess();
       }
@@ -1071,7 +1134,7 @@ export class TalerExchangeHttpClient2 {
     switch (resp.status) {
       case HttpStatusCode.NoContent: {
         this.cacheEvictor.notifySuccess(
-          TalerExchangeCacheEviction2.MAKE_AML_DECISION,
+          TalerExchangeCacheEviction.MAKE_AML_DECISION,
         );
         return opEmptySuccess();
       }
diff --git a/packages/taler-util/src/http-client/exchange.ts 
b/packages/taler-util/src/http-client/exchange.ts
deleted file mode 100644
index 3aa49afe0..000000000
--- a/packages/taler-util/src/http-client/exchange.ts
+++ /dev/null
@@ -1,1001 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022-2025 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-import { codecForAny } from "../codec.js";
-import {
-  HttpRequestLibrary,
-  readSuccessResponseJsonOrThrow,
-} from "../http-common.js";
-import { HttpStatusCode } from "../http-status-codes.js";
-import { createPlatformHttpLib } from "../http.js";
-import { LibtoolVersion } from "../libtool-version.js";
-import {
-  FailCasesByMethod,
-  OperationAlternative,
-  OperationFail,
-  OperationOk,
-  ResultByMethod,
-  opEmptySuccess,
-  opFixedSuccess,
-  opKnownAlternativeHttpFailure,
-  opKnownFailure,
-  opKnownHttpFailure,
-  opSuccessFromHttp,
-  opUnknownHttpFailure,
-} from "../operation.js";
-import { encodeCrock } from "../taler-crypto.js";
-import {
-  AccessToken,
-  EddsaPublicKeyString,
-  EddsaSignatureString,
-  LongPollParams,
-  OfficerAccount,
-  PaginationParams,
-  codecForTalerCommonConfigResponse,
-} from "../types-taler-common.js";
-import {
-  AccountKycStatus,
-  AmlDecisionRequest,
-  ExchangeKycUploadFormRequest,
-  ExchangePurseDeposits,
-  ExchangePurseMergeRequest,
-  ExchangeReservePurseRequest,
-  ExchangeVersionResponse,
-  KycProcessClientInformation,
-  KycRequirementInformationId,
-  LegitimizationMeasuresList,
-  PurseCreate,
-  WalletKycRequest,
-  codecForAccountKycStatus,
-  codecForAmlDecisionsResponse,
-  codecForAmlKycAttributes,
-  codecForAmlWalletKycCheckResponse,
-  codecForAvailableMeasureSummary,
-  codecForEventCounter,
-  codecForExchangeConfig,
-  codecForExchangeGetContractResponse,
-  codecForExchangeKeysResponse,
-  codecForExchangeMergeConflictResponse,
-  codecForExchangeMergeSuccessResponse,
-  codecForExchangePurseStatus,
-  codecForExchangeTransferList,
-  codecForKycProcessClientInformation,
-  codecForKycProcessStartInformation,
-  codecForLegitimizationNeededResponse,
-  codecForPurseConflict,
-  codecForPurseConflictPartial,
-} from "../types-taler-exchange.js";
-import {
-  CacheEvictor,
-  addLongPollingParam,
-  addPaginationParams,
-  nullEvictor,
-} from "./utils.js";
-
-import { TalerError } from "../errors.js";
-import {
-  AmountJson,
-  Amounts,
-  CancellationToken,
-  LongpollQueue,
-  signAmlDecision,
-  signAmlQuery,
-} from "../index.js";
-import { TalerErrorCode } from "../taler-error-codes.js";
-import { AbsoluteTime } from "../time.js";
-
-export type TalerExchangeResultByMethod<
-  prop extends keyof TalerExchangeHttpClient,
-> = ResultByMethod<TalerExchangeHttpClient, prop>;
-export type TalerExchangeErrorsByMethod<
-  prop extends keyof TalerExchangeHttpClient,
-> = FailCasesByMethod<TalerExchangeHttpClient, prop>;
-
-export enum TalerExchangeCacheEviction {
-  UPLOAD_KYC_FORM,
-  MAKE_AML_DECISION,
-}
-
-declare const __pubId: unique symbol;
-export type ReservePub = string & { [__pubId]: true };
-
-export type KycProcessClientInformationWithEtag =
-  KycProcessClientInformation & { etag: string | undefined };
-
-/**
- * Client library for the GNU Taler exchange service.
- *
- * FIXME: This client library is currently used by the SPA.
- * However, we should merge it with the other exchange client implementation.
- */
-export class TalerExchangeHttpClient {
-  public static readonly PROTOCOL_VERSION = "28:0:2";
-  private httpLib: HttpRequestLibrary;
-  private cacheEvictor: CacheEvictor<TalerExchangeCacheEviction>;
-  private preventCompression: boolean;
-
-  constructor(
-    readonly baseUrl: string,
-    params: {
-      httpClient?: HttpRequestLibrary;
-      cacheEvictor?: CacheEvictor<TalerExchangeCacheEviction>;
-      preventCompression?: boolean;
-      cancelationToken?: CancellationToken;
-      longPollQueue?: LongpollQueue;
-    },
-  ) {
-    this.httpLib = params.httpClient ?? createPlatformHttpLib();
-    this.cacheEvictor = params.cacheEvictor ?? nullEvictor;
-    this.preventCompression = !!params.preventCompression;
-  }
-
-  isCompatible(version: string): boolean {
-    const compare = LibtoolVersion.compare(
-      TalerExchangeHttpClient.PROTOCOL_VERSION,
-      version,
-    );
-    return compare?.compatible ?? false;
-  }
-
-  // EXCHANGE INFORMATION
-
-  /**
-   * https://docs.taler.net/core/api-exchange.html#get--seed
-   *
-   */
-  async getSeed() {
-    const url = new URL(`seed`, this.baseUrl);
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "GET",
-    });
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        const buffer = await resp.bytes();
-        const uintar = new Uint8Array(buffer);
-        return opFixedSuccess(uintar);
-      case HttpStatusCode.NotFound:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-  /**
-   * https://docs.taler.net/core/api-exchange.html#get--config
-   *
-   */
-  async getConfig(): Promise<
-    | OperationFail<HttpStatusCode.NotFound>
-    | OperationOk<ExchangeVersionResponse>
-  > {
-    const url = new URL(`config`, this.baseUrl);
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "GET",
-    });
-    switch (resp.status) {
-      case HttpStatusCode.Ok: {
-        const minBody = await readSuccessResponseJsonOrThrow(
-          resp,
-          codecForTalerCommonConfigResponse(),
-        );
-        const expectedName = "taler-exchange";
-        if (minBody.name !== expectedName) {
-          throw TalerError.fromUncheckedDetail({
-            code: TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR,
-            requestUrl: resp.requestUrl,
-            httpStatusCode: resp.status,
-            detail: `Unexpected server component name (got ${minBody.name}, 
expected ${expectedName}})`,
-          });
-        }
-        if (!this.isCompatible(minBody.version)) {
-          throw TalerError.fromUncheckedDetail({
-            code: TalerErrorCode.GENERIC_CLIENT_UNSUPPORTED_PROTOCOL_VERSION,
-            requestUrl: resp.requestUrl,
-            httpStatusCode: resp.status,
-            detail: `Unsupported protocol version, client supports 
${TalerExchangeHttpClient.PROTOCOL_VERSION}, server supports 
${minBody.version}`,
-          });
-        }
-        // Now that we've checked the basic body, re-parse the full response.
-        const body = await readSuccessResponseJsonOrThrow(
-          resp,
-          codecForExchangeConfig(),
-        );
-        return opFixedSuccess(body);
-      }
-      case HttpStatusCode.NotFound:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * https://docs.taler.net/core/api-merchant.html#get--config
-   *
-   * PARTIALLY IMPLEMENTED!!
-   */
-  async getKeys() {
-    const url = new URL(`keys`, this.baseUrl);
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "GET",
-    });
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccessFromHttp(resp, codecForExchangeKeysResponse());
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  // WALLET TO WALLET
-
-  /**
-   * https://docs.taler.net/core/api-exchange.html#get--purses-$PURSE_PUB-merge
-   *
-   */
-  async getPurseStatusAtMerge(pursePub: EddsaPublicKeyString) {
-    const url = new URL(`purses/${pursePub}/merge`, this.baseUrl);
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "GET",
-    });
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccessFromHttp(resp, codecForExchangePurseStatus());
-      case HttpStatusCode.Gone:
-      case HttpStatusCode.NotFound:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * 
https://docs.taler.net/core/api-exchange.html#get--purses-$PURSE_PUB-deposit
-   *
-   */
-  async getPurseStatusAtDeposit(pursePub: EddsaPublicKeyString) {
-    const url = new URL(`purses/${pursePub}/deposit`, this.baseUrl);
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "GET",
-    });
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccessFromHttp(resp, codecForExchangePurseStatus());
-      case HttpStatusCode.Gone:
-      case HttpStatusCode.NotFound:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * 
https://docs.taler.net/core/api-exchange.html#post--purses-$PURSE_PUB-create
-   *
-   */
-  async createPurseFromDeposit(
-    pursePub: EddsaPublicKeyString,
-    body: PurseCreate,
-  ) {
-    const url = new URL(`purses/${pursePub}/create`, this.baseUrl);
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "POST",
-      body,
-    });
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        // FIXME: parse PurseCreateSuccessResponse
-        return opSuccessFromHttp(resp, codecForAny());
-      case HttpStatusCode.Conflict:
-        return opKnownAlternativeHttpFailure(
-          resp,
-          resp.status,
-          codecForPurseConflict(),
-        );
-      case HttpStatusCode.Forbidden:
-      case HttpStatusCode.NotFound:
-      case HttpStatusCode.TooEarly:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * https://docs.taler.net/core/api-exchange.html#delete--purses-$PURSE_PUB
-   *
-   */
-  async deletePurse(pursePub: string, purseSig: string) {
-    const url = new URL(`purses/${pursePub}`, this.baseUrl);
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "DELETE",
-      headers: {
-        "taler-purse-signature": purseSig,
-      },
-    });
-    switch (resp.status) {
-      case HttpStatusCode.NoContent:
-        return opEmptySuccess();
-      case HttpStatusCode.NotFound:
-        return opKnownHttpFailure(resp.status, resp);
-      case HttpStatusCode.Conflict:
-        return opKnownHttpFailure(resp.status, resp);
-      case HttpStatusCode.Forbidden:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * POST /purses/$PURSE_PUB/merge
-   *
-   * 
https://docs.taler.net/core/api-exchange.html#post--purses-$PURSE_PUB-merge
-   */
-  async postPurseMerge(pursePub: string, body: ExchangePurseMergeRequest) {
-    const url = new URL(`purses/${pursePub}/merge`, this.baseUrl);
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "POST",
-      body,
-    });
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccessFromHttp(resp, codecForExchangeMergeSuccessResponse());
-      case HttpStatusCode.UnavailableForLegalReasons:
-        return opKnownAlternativeHttpFailure(
-          resp,
-          resp.status,
-          codecForLegitimizationNeededResponse(),
-        );
-      case HttpStatusCode.Conflict:
-        return opKnownAlternativeHttpFailure(
-          resp,
-          resp.status,
-          codecForExchangeMergeConflictResponse(),
-        );
-      case HttpStatusCode.Gone:
-      case HttpStatusCode.Forbidden:
-      case HttpStatusCode.NotFound:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * 
https://docs.taler.net/core/api-exchange.html#post--reserves-$RESERVE_PUB-purse
-   *
-   */
-  async createPurseFromReserve(
-    pursePub: EddsaPublicKeyString,
-    body: ExchangeReservePurseRequest,
-  ) {
-    const url = new URL(`reserves/${pursePub}/purse`, this.baseUrl);
-
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "POST",
-      body,
-    });
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        // FIXME: parse PurseCreateSuccessResponse
-        return opSuccessFromHttp(resp, codecForAny());
-      case HttpStatusCode.PaymentRequired:
-      case HttpStatusCode.Forbidden:
-      case HttpStatusCode.NotFound:
-        return opKnownHttpFailure(resp.status, resp);
-      case HttpStatusCode.Conflict:
-        return opKnownAlternativeHttpFailure(
-          resp,
-          resp.status,
-          codecForPurseConflictPartial(),
-        );
-      case HttpStatusCode.UnavailableForLegalReasons:
-        return opKnownAlternativeHttpFailure(
-          resp,
-          resp.status,
-          codecForLegitimizationNeededResponse(),
-        );
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * https://docs.taler.net/core/api-exchange.html#get--contracts-$CONTRACT_PUB
-   *
-   */
-  async getContract(pursePub: EddsaPublicKeyString) {
-    const url = new URL(`contracts/${pursePub}`, this.baseUrl);
-
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "GET",
-    });
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccessFromHttp(resp, codecForExchangeGetContractResponse());
-      case HttpStatusCode.NotFound:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * 
https://docs.taler.net/core/api-exchange.html#post--purses-$PURSE_PUB-deposit
-   *
-   */
-  async depositIntoPurse(
-    pursePub: EddsaPublicKeyString,
-    body: ExchangePurseDeposits,
-  ) {
-    const url = new URL(`purses/${pursePub}/deposit`, this.baseUrl);
-
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "POST",
-      body,
-    });
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        // FIXME: parse PurseDepositSuccessResponse
-        return opSuccessFromHttp(resp, codecForAny());
-      case HttpStatusCode.Conflict:
-        return opKnownAlternativeHttpFailure(
-          resp,
-          resp.status,
-          codecForPurseConflict(),
-        );
-      case HttpStatusCode.Forbidden:
-      case HttpStatusCode.NotFound:
-      case HttpStatusCode.Gone:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  // WADS
-
-  /**
-   * https://docs.taler.net/core/api-exchange.html#get--wads-$WAD_ID
-   *
-   */
-  async getWadInfo(): Promise<never> {
-    throw Error("not yet implemented");
-  }
-
-  //
-  // KYC
-  //
-
-  /**
-   * https://docs.taler.net/core/api-exchange.html#post--kyc-wallet
-   *
-   */
-  async notifyKycBalanceLimit(body: WalletKycRequest) {
-    const url = new URL(`kyc-wallet`, this.baseUrl);
-
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "POST",
-      body,
-    });
-
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccessFromHttp(resp, codecForAmlWalletKycCheckResponse());
-      case HttpStatusCode.NoContent:
-        return opEmptySuccess();
-      case HttpStatusCode.Forbidden:
-        return opKnownHttpFailure(resp.status, resp);
-      case HttpStatusCode.UnavailableForLegalReasons:
-        return opKnownAlternativeHttpFailure(
-          resp,
-          resp.status,
-          codecForLegitimizationNeededResponse(),
-        );
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * Endpoint: GET /kyc-check/$H_NORMALIZED_PAYTO
-   *
-   * 
https://docs.taler.net/core/api-exchange.html#get--kyc-check-$H_NORMALIZED_PAYTO
-   */
-  async checkKycStatus(args: {
-    paytoHash: string;
-    accountPub: EddsaPublicKeyString;
-    accountSig: EddsaSignatureString;
-    longpoll?: boolean;
-    awaitAuth?: boolean;
-  }): Promise<
-    | OperationOk<void>
-    | OperationAlternative<HttpStatusCode.Ok, AccountKycStatus>
-    | OperationAlternative<HttpStatusCode.Accepted, AccountKycStatus>
-    | OperationFail<HttpStatusCode.Forbidden>
-    | OperationFail<HttpStatusCode.NotFound>
-    | OperationFail<HttpStatusCode.Conflict>
-  > {
-    const { paytoHash, accountPub, accountSig, longpoll, awaitAuth } = args;
-    const url = new URL(`kyc-check/${paytoHash}`, this.baseUrl);
-    if (awaitAuth !== undefined) {
-      url.searchParams.set("await_auth", awaitAuth ? "YES" : "NO");
-    }
-
-    const resp = await this.httpLib.fetch(url.href, {
-      headers: {
-        "Account-Owner-Signature": accountSig,
-        "Account-Owner-Pub": accountPub,
-      },
-    });
-
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-      case HttpStatusCode.Accepted:
-        return opKnownAlternativeHttpFailure(
-          resp,
-          resp.status,
-          codecForAccountKycStatus(),
-        );
-      case HttpStatusCode.NoContent:
-        return opEmptySuccess();
-      case HttpStatusCode.Forbidden:
-      case HttpStatusCode.NotFound:
-      case HttpStatusCode.Conflict:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * https://docs.taler.net/core/api-exchange.html#get--kyc-info-$ACCESS_TOKEN
-   *
-   */
-  async checkKycInfo(
-    token: AccessToken,
-    etag: string | undefined,
-    params: LongPollParams = {},
-  ) {
-    const url = new URL(`kyc-info/${token}`, this.baseUrl);
-
-    addLongPollingParam(url, params);
-
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "GET",
-      headers: {
-        "If-None-Match": etag,
-      },
-    });
-    switch (resp.status) {
-      case HttpStatusCode.Ok: {
-        // we need to add the etag to the response because the
-        // client needs it to repeat the request and
-        // do the long polling
-        const etag = resp.headers.get("etag") ?? undefined;
-        const body = await readSuccessResponseJsonOrThrow(
-          resp,
-          codecForKycProcessClientInformation(),
-        );
-        return opFixedSuccess<KycProcessClientInformationWithEtag>({
-          ...body,
-          etag,
-        });
-      }
-      case HttpStatusCode.Accepted:
-        return opKnownHttpFailure(resp.status, resp);
-      case HttpStatusCode.NoContent:
-        return opKnownHttpFailure(resp.status, resp);
-      case HttpStatusCode.NotModified:
-        // do not read details from response
-        return opKnownFailure(resp.status);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * https://docs.taler.net/core/api-exchange.html#post--kyc-upload-$ID
-   *
-   */
-  async uploadKycForm<T extends ExchangeKycUploadFormRequest>(
-    requirement: KycRequirementInformationId,
-    body: T,
-  ) {
-    const url = new URL(`kyc-upload/${requirement}`, this.baseUrl);
-
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "POST",
-      body,
-      compress: this.preventCompression ? undefined : "deflate",
-    });
-    switch (resp.status) {
-      case HttpStatusCode.NoContent: {
-        this.cacheEvictor.notifySuccess(
-          TalerExchangeCacheEviction.UPLOAD_KYC_FORM,
-        );
-        return opEmptySuccess();
-      }
-      case HttpStatusCode.NotFound:
-      case HttpStatusCode.Conflict:
-      case HttpStatusCode.PayloadTooLarge:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * https://docs.taler.net/core/api-exchange.html#post--kyc-start-$ID
-   *
-   */
-  async startExternalKycProcess(
-    requirement: KycRequirementInformationId,
-    body: object = {},
-  ) {
-    const url = new URL(`kyc-start/${requirement}`, this.baseUrl);
-
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "POST",
-      body,
-    });
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccessFromHttp(resp, codecForKycProcessStartInformation());
-      case HttpStatusCode.NotFound:
-      case HttpStatusCode.Conflict:
-      case HttpStatusCode.PayloadTooLarge:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * 
https://docs.taler.net/core/api-exchange.html#get--kyc-proof-$PROVIDER_NAME?state=$H_PAYTO
-   *
-   */
-  async completeExternalKycProcess(
-    provider: string,
-    state: string,
-    code: string,
-  ) {
-    const url = new URL(`kyc-proof/${provider}`, this.baseUrl);
-    url.searchParams.set("state", state);
-    url.searchParams.set("code", code);
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "GET",
-      redirect: "manual",
-    });
-
-    switch (resp.status) {
-      case HttpStatusCode.SeeOther:
-        return opEmptySuccess();
-      case HttpStatusCode.NotFound:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  //
-  // AML operations
-  //
-
-  /**
-   * 
https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-measures
-   *
-   */
-  async getAmlMeasures(auth: OfficerAccount) {
-    const url = new URL(`aml/${auth.id}/measures`, this.baseUrl);
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "GET",
-      headers: {
-        "Taler-AML-Officer-Signature": encodeCrock(
-          signAmlQuery(auth.signingKey),
-        ),
-      },
-    });
-
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccessFromHttp(resp, codecForAvailableMeasureSummary());
-      case HttpStatusCode.Conflict:
-      case HttpStatusCode.NotFound:
-      case HttpStatusCode.Forbidden:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * 
https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-measures
-   *
-   */
-  async getAmlKycStatistics(
-    auth: OfficerAccount,
-    name: string,
-    filter: {
-      since?: AbsoluteTime;
-      until?: AbsoluteTime;
-    } = {},
-  ) {
-    const url = new URL(`aml/${auth.id}/kyc-statistics/${name}`, this.baseUrl);
-
-    if (filter.since !== undefined && filter.since.t_ms !== "never") {
-      url.searchParams.set("start_date", String(filter.since.t_ms));
-    }
-    if (filter.until !== undefined && filter.until.t_ms !== "never") {
-      url.searchParams.set("end_date", String(filter.until.t_ms));
-    }
-
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "GET",
-      headers: {
-        "Taler-AML-Officer-Signature": encodeCrock(
-          signAmlQuery(auth.signingKey),
-        ),
-      },
-    });
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccessFromHttp(resp, codecForEventCounter());
-      case HttpStatusCode.Conflict:
-      case HttpStatusCode.NotFound:
-      case HttpStatusCode.Forbidden:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * 
https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-decisions
-   *
-   */
-  async getAmlDecisions(
-    auth: OfficerAccount,
-    params: PaginationParams & {
-      account?: string;
-      active?: boolean;
-      investigation?: boolean;
-    } = {},
-  ) {
-    const url = new URL(`aml/${auth.id}/decisions`, this.baseUrl);
-
-    addPaginationParams(url, params);
-    if (params.account !== undefined) {
-      url.searchParams.set("h_payto", params.account);
-    }
-    if (params.active !== undefined) {
-      url.searchParams.set("active", params.active ? "YES" : "NO");
-    }
-    if (params.investigation !== undefined) {
-      url.searchParams.set(
-        "investigation",
-        params.investigation ? "YES" : "NO",
-      );
-    }
-
-    const resp = await this.httpLib.fetch(url.href, {
-      headers: {
-        "Taler-AML-Officer-Signature": encodeCrock(
-          signAmlQuery(auth.signingKey),
-        ),
-      },
-    });
-
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccessFromHttp(resp, codecForAmlDecisionsResponse());
-      case HttpStatusCode.NoContent:
-        return opFixedSuccess({ records: [] });
-      case HttpStatusCode.Forbidden:
-      case HttpStatusCode.NotFound:
-      case HttpStatusCode.Conflict:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * 
https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-decisions
-   *
-   */
-  async getAmlLegitimizations(args: {
-    officerAcc: OfficerAccount;
-  }): Promise<OperationOk<LegitimizationMeasuresList>> {
-    const url = new URL(
-      `aml/${args.officerAcc.id}/legitimizations`,
-      this.baseUrl,
-    );
-
-    const resp = await this.httpLib.fetch(url.href, {
-      headers: {
-        "Taler-AML-Officer-Signature": encodeCrock(
-          signAmlQuery(args.officerAcc.signingKey),
-        ),
-      },
-    });
-
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        // FIXME: Parse properly.
-        return opSuccessFromHttp(resp, codecForAny());
-      case HttpStatusCode.NoContent:
-        return opFixedSuccess({
-          measures: [],
-        });
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * 
https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-attributes-$H_PAYTO
-   *
-   */
-  async getAmlAttributesForAccount(
-    auth: OfficerAccount,
-    account: string,
-    params: PaginationParams = {},
-  ) {
-    const url = new URL(`aml/${auth.id}/attributes/${account}`, this.baseUrl);
-
-    addPaginationParams(url, params);
-    const resp = await this.httpLib.fetch(url.href, {
-      headers: {
-        "Taler-AML-Officer-Signature": encodeCrock(
-          signAmlQuery(auth.signingKey),
-        ),
-      },
-    });
-
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccessFromHttp(resp, codecForAmlKycAttributes());
-      case HttpStatusCode.NoContent:
-        return opFixedSuccess({ details: [] });
-      case HttpStatusCode.Forbidden:
-      case HttpStatusCode.NotFound:
-      case HttpStatusCode.Conflict:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * 
https://docs.taler.net/core/api-exchange.html#post--aml-$OFFICER_PUB-decision
-   *
-   */
-  async makeAmlDesicion(
-    auth: OfficerAccount,
-    decision: Omit<AmlDecisionRequest, "officer_sig">,
-  ) {
-    const body: AmlDecisionRequest = {
-      officer_sig: encodeCrock(
-        signAmlDecision(auth.signingKey, decision),
-      ) as EddsaSignatureString,
-      ...decision,
-    };
-    const url = new URL(`aml/${auth.id}/decision`, this.baseUrl);
-
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "POST",
-      headers: {
-        "Taler-AML-Officer-Signature": encodeCrock(
-          signAmlQuery(auth.signingKey),
-        ),
-      },
-      body,
-      compress: this.preventCompression ? undefined : "deflate",
-    });
-
-    switch (resp.status) {
-      case HttpStatusCode.NoContent: {
-        this.cacheEvictor.notifySuccess(
-          TalerExchangeCacheEviction.MAKE_AML_DECISION,
-        );
-        return opEmptySuccess();
-      }
-      case HttpStatusCode.Forbidden:
-      case HttpStatusCode.NotFound:
-      case HttpStatusCode.Conflict:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * 
https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-transfers-credit
-   *
-   */
-  async getTransfersCredit(
-    auth: OfficerAccount,
-    params: PaginationParams & { threshold?: AmountJson } = {},
-  ) {
-    const url = new URL(`aml/${auth.id}/transfers-credit`, this.baseUrl);
-
-    addPaginationParams(url, params);
-
-    if (params.threshold) {
-      url.searchParams.set("threshold", Amounts.stringify(params.threshold));
-    }
-
-    const resp = await this.httpLib.fetch(url.href, {
-      headers: {
-        "Taler-AML-Officer-Signature": encodeCrock(
-          signAmlQuery(auth.signingKey),
-        ),
-      },
-    });
-
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccessFromHttp(resp, codecForExchangeTransferList());
-      case HttpStatusCode.NoContent:
-        return opFixedSuccess({ transfers: [] });
-      case HttpStatusCode.Forbidden:
-      case HttpStatusCode.NotFound:
-      case HttpStatusCode.Conflict:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-
-  /**
-   * 
https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-transfers-debit
-   *
-   */
-  async getTransfersDebit(
-    auth: OfficerAccount,
-    params: PaginationParams & { threshold?: AmountJson } = {},
-  ) {
-    const url = new URL(`aml/${auth.id}/transfers-debit`, this.baseUrl);
-
-    addPaginationParams(url, params);
-
-    if (params.threshold) {
-      url.searchParams.set("threshold", Amounts.stringify(params.threshold));
-    }
-
-    const resp = await this.httpLib.fetch(url.href, {
-      headers: {
-        "Taler-AML-Officer-Signature": encodeCrock(
-          signAmlQuery(auth.signingKey),
-        ),
-      },
-    });
-
-    switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccessFromHttp(resp, codecForExchangeTransferList());
-      case HttpStatusCode.NoContent:
-        return opFixedSuccess({ transfers: [] });
-      case HttpStatusCode.Forbidden:
-      case HttpStatusCode.NotFound:
-      case HttpStatusCode.Conflict:
-        return opKnownHttpFailure(resp.status, resp);
-      default:
-        return opUnknownHttpFailure(resp);
-    }
-  }
-}
diff --git a/packages/taler-util/src/http-client/officer-account.ts 
b/packages/taler-util/src/http-client/officer-account.ts
index f6bee214d..19128393d 100644
--- a/packages/taler-util/src/http-client/officer-account.ts
+++ b/packages/taler-util/src/http-client/officer-account.ts
@@ -21,7 +21,6 @@ import {
   OfficerAccount,
   OfficerId,
   ReserveAccount,
-  ReservePub,
   createEddsaKeyPair,
   decodeCrock,
   decryptWithDerivedKey,
@@ -30,7 +29,7 @@ import {
   encryptWithDerivedKey,
   getRandomBytes,
   kdf,
-  stringToBytes
+  stringToBytes,
 } from "@gnu-taler/taler-util";
 
 /**
@@ -112,21 +111,25 @@ export async function createNewWalletKycAccount(
 ): Promise<ReserveAccount & { safe?: LockedAccount }> {
   const { eddsaPriv, eddsaPub } = createEddsaKeyPair();
 
-  const mergedRnd: EncryptionNonceP = extraNonce && password
-    ? kdf(24, stringToBytes("aml-officer"), extraNonce, getRandomBytes(24))
-    : getRandomBytes(24);
+  const mergedRnd: EncryptionNonceP =
+    extraNonce && password
+      ? kdf(24, stringToBytes("aml-officer"), extraNonce, getRandomBytes(24))
+      : getRandomBytes(24);
 
-  
-  const protectedPrivKey = password ? await encryptWithDerivedKey(
-    mergedRnd,
-    stringToBytes(password),
-    eddsaPriv,
-    password,
-  ) : undefined;
+  const protectedPrivKey = password
+    ? await encryptWithDerivedKey(
+        mergedRnd,
+        stringToBytes(password),
+        eddsaPriv,
+        password,
+      )
+    : undefined;
 
   const signingKey = eddsaPriv as EddsaPrivP;
-  const accountId = encodeCrock(eddsaPub) as ReservePub;
-  const safe = protectedPrivKey ? encodeCrock(protectedPrivKey) as 
LockedAccount : undefined;
+  const accountId = encodeCrock(eddsaPub);
+  const safe = protectedPrivKey
+    ? (encodeCrock(protectedPrivKey) as LockedAccount)
+    : undefined;
 
   return { id: accountId, signingKey, safe };
 }
diff --git a/packages/taler-util/src/index.ts b/packages/taler-util/src/index.ts
index c19a5208c..6be513afc 100644
--- a/packages/taler-util/src/index.ts
+++ b/packages/taler-util/src/index.ts
@@ -16,7 +16,6 @@ export * from "./http-client/bank-integration.js";
 export * from "./http-client/bank-revenue.js";
 export * from "./http-client/bank-wire.js";
 export * from "./http-client/challenger.js";
-export * from "./http-client/exchange.js";
 export * from "./http-client/exchange-client.js";
 export * from "./http-client/merchant.js";
 export * from "./http-client/officer-account.js";
diff --git a/packages/taler-util/src/types-taler-common.ts 
b/packages/taler-util/src/types-taler-common.ts
index a20788404..14311c132 100644
--- a/packages/taler-util/src/types-taler-common.ts
+++ b/packages/taler-util/src/types-taler-common.ts
@@ -39,11 +39,10 @@ import {
   codecForString,
 } from "./codec.js";
 import {
+  EddsaPrivP,
   codecForEither,
   codecForList,
   codecOptional,
-  EddsaPrivP,
-  ReservePub,
 } from "./index.js";
 import {
   TalerProtocolDuration,
@@ -194,11 +193,11 @@ export type AmountString =
 // export type AmountString = string;
 export type Base32String = string;
 declare const __eddsasign_str: unique symbol;
-export type EddsaSignatureString = string;// & { [__eddsasign_str]: true };
+export type EddsaSignatureString = string; // & { [__eddsasign_str]: true };
 declare const __eddsapub_str: unique symbol;
-export type EddsaPublicKeyString = string;// & { [__eddsapub_str]: true };
+export type EddsaPublicKeyString = string; // & { [__eddsapub_str]: true };
 declare const __eddsapriv_str: unique symbol;
-export type EddsaPrivateKeyString = string;// & { [__eddsapriv_str]: true };
+export type EddsaPrivateKeyString = string; // & { [__eddsapriv_str]: true };
 export type CoinPublicKeyString = string;
 
 // FIXME: implement this codec
@@ -210,11 +209,14 @@ export const codecForCurrencyName = codecForString;
 // FIXME: implement this codec
 export const codecForDecimalNumber = codecForString;
 // FIXME: implement this codec
-export const codecForEddsaPublicKey = codecForString as () => 
Codec<EddsaPublicKeyString>;
+export const codecForEddsaPublicKey =
+  codecForString as () => Codec<EddsaPublicKeyString>;
 // FIXME: implement this codec
-export const codecForEddsaPrivateKey = codecForString as () => 
Codec<EddsaPrivateKeyString>;
+export const codecForEddsaPrivateKey =
+  codecForString as () => Codec<EddsaPrivateKeyString>;
 // FIXME: implement this codec
-export const codecForEddsaSignature = codecForString as () => 
Codec<EddsaSignatureString>;
+export const codecForEddsaSignature =
+  codecForString as () => Codec<EddsaSignatureString>;
 
 export const codecForInternationalizedString =
   (): Codec<InternationalizedString> => codecForMap(codecForString());
@@ -359,7 +361,11 @@ export const codecForCoinHistoryResponse = () =>
     .build("CoinHistoryResponse");
 
 export type TokenScope = BankTokenScope | MerchantTokenScope;
-export type BankTokenScope = "readonly" | "readwrite" | "revenue" | 
"wiregateway";
+export type BankTokenScope =
+  | "readonly"
+  | "readwrite"
+  | "revenue"
+  | "wiregateway";
 export type MerchantTokenScope = "write";
 export interface TokenRequest {
   // Service-defined scope for the token.
@@ -567,7 +573,7 @@ export interface OfficerAccount {
 }
 
 export interface ReserveAccount {
-  id: ReservePub;
+  id: EddsaPublicKeyString;
   signingKey: EddsaPrivP;
 }
 
diff --git a/packages/taler-util/src/types-taler-exchange.ts 
b/packages/taler-util/src/types-taler-exchange.ts
index 73f696839..03c6d89c5 100644
--- a/packages/taler-util/src/types-taler-exchange.ts
+++ b/packages/taler-util/src/types-taler-exchange.ts
@@ -2014,6 +2014,9 @@ export interface KycProcessClientInformation {
   voluntary_measures?: KycRequirementInformation[];
 }
 
+export type KycProcessClientInformationWithEtag =
+  KycProcessClientInformation & { etag: string | undefined };
+
 declare const opaque_brand: unique symbol;
 
 declare const opaque_kycReq: unique symbol;
diff --git a/packages/taler-wallet-core/src/dbless.ts 
b/packages/taler-wallet-core/src/dbless.ts
index 01920d3aa..35cddb50f 100644
--- a/packages/taler-wallet-core/src/dbless.ts
+++ b/packages/taler-wallet-core/src/dbless.ts
@@ -39,7 +39,7 @@ import {
   ExchangeRefreshRevealRequestV2,
   Logger,
   TalerCorebankApiClient,
-  TalerExchangeHttpClient2,
+  TalerExchangeHttpClient,
   UnblindedDenominationSignature,
   codecForAny,
   codecForBankWithdrawalOperationPostResponse,
@@ -147,7 +147,7 @@ export async function withdrawCoin(args: {
     secretSeed: encodeCrock(getRandomBytes(32)),
     value: Amounts.parseOrThrow(denom.value),
   });
-  const exchangeClient = new TalerExchangeHttpClient2(exchangeBaseUrl);
+  const exchangeClient = new TalerExchangeHttpClient(exchangeBaseUrl);
 
   const sigResp = await cryptoApi.signWithdrawal({
     amount: Amounts.stringify(denom.value),
@@ -323,7 +323,7 @@ export async function refreshCoin(req: {
 
   logger.info(`requesting melt: ${j2s(meltReqBody)}`);
 
-  const exchangeClient = new TalerExchangeHttpClient2(oldCoin.exchangeBaseUrl);
+  const exchangeClient = new TalerExchangeHttpClient(oldCoin.exchangeBaseUrl);
 
   const meltResponse = succeedOrThrow(
     await exchangeClient.postMelt({ body: meltReqBody }),
diff --git a/packages/taler-wallet-core/src/versions.ts 
b/packages/taler-wallet-core/src/versions.ts
index ab83a7fb6..ed94da138 100644
--- a/packages/taler-wallet-core/src/versions.ts
+++ b/packages/taler-wallet-core/src/versions.ts
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { TalerExchangeHttpClient2 } from "@gnu-taler/taler-util";
+import { TalerExchangeHttpClient } from "@gnu-taler/taler-util";
 
 /**
  * Protocol version spoken with the exchange.
@@ -22,7 +22,7 @@ import { TalerExchangeHttpClient2 } from 
"@gnu-taler/taler-util";
  * Uses libtool's current:revision:age versioning.
  */
 export const WALLET_EXCHANGE_PROTOCOL_VERSION =
-  TalerExchangeHttpClient2.SUPPORTED_EXCHANGE_PROTOCOL_VERSION;
+  TalerExchangeHttpClient.SUPPORTED_EXCHANGE_PROTOCOL_VERSION;
 
 /**
  * Protocol version spoken with the merchant.
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index 4b2872f9c..1fb5a24d9 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -113,7 +113,7 @@ import {
   TalerBankIntegrationHttpClient,
   TalerError,
   TalerErrorCode,
-  TalerExchangeHttpClient2,
+  TalerExchangeHttpClient,
   TalerMerchantInstanceHttpClient,
   TalerProtocolTimestamp,
   TalerUriAction,
@@ -427,8 +427,8 @@ export interface WalletExecutionContext {
 export function walletExchangeClient(
   baseUrl: string,
   wex: WalletExecutionContext,
-): TalerExchangeHttpClient2 {
-  return new TalerExchangeHttpClient2(baseUrl, {
+): TalerExchangeHttpClient {
+  return new TalerExchangeHttpClient(baseUrl, {
     httpClient: wex.http,
     cancelationToken: wex.cancellationToken,
     longPollQueue: wex.ws.longpollQueue,
diff --git a/packages/web-util/src/context/exchange-api.ts 
b/packages/web-util/src/context/exchange-api.ts
index eb839a534..52f46859a 100644
--- a/packages/web-util/src/context/exchange-api.ts
+++ b/packages/web-util/src/context/exchange-api.ts
@@ -246,7 +246,7 @@ function buildExchangeApiClient(
 
   return {
     getRemoteConfig,
-    VERSION: TalerExchangeHttpClient.PROTOCOL_VERSION,
+    VERSION: TalerExchangeHttpClient.SUPPORTED_EXCHANGE_PROTOCOL_VERSION,
     lib: {
       exchange: ex,
     },
diff --git a/packages/web-util/src/hooks/useAsync.ts 
b/packages/web-util/src/hooks/useAsync.ts
index 51e89cb18..bb685d9da 100644
--- a/packages/web-util/src/hooks/useAsync.ts
+++ b/packages/web-util/src/hooks/useAsync.ts
@@ -13,8 +13,8 @@
  You should have received a copy of the GNU General Public License along with
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
+import { opUnknownFailure } from "@gnu-taler/taler-util";
 import { useEffect, useState } from "preact/hooks";
-import { opFixedSuccess, opUnknownFailure } from "@gnu-taler/taler-util";
 
 /**
  * convert the async function into preact hook
@@ -57,10 +57,10 @@ export function useAsync<Res>(
  * First start with `first` value
  * Check if the it should do long-polling with `shouldRetryFn`
  * Call `retryFn` if is required, long poll is expected for this function
- * 
- * If `retryFn` returns faster than `minTime` (by error or because server 
+ *
+ * If `retryFn` returns faster than `minTime` (by error or because server
  * returned a response faster) then wait until `minTime`
- * 
+ *
  * @param fetcher fetcher should be a memoized function
  * @param retry
  * @param deps

-- 
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]