gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (66e1529f0 -> 685f747b6)


From: gnunet
Subject: [taler-wallet-core] branch master updated (66e1529f0 -> 685f747b6)
Date: Fri, 01 Dec 2023 18:50:19 +0100

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

sebasjm pushed a change to branch master
in repository wallet-core.

    from 66e1529f0 show conversion service in withdrawal details
     new 75e0517fd sync with libeufin api
     new 6b1bee3fe unused harness
     new 685f747b6 sync demobank with new libeufin API, still missing when the 
withdrawal operation is not the same user that created the transfer

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 packages/demobank-ui/src/hooks/access.ts           |  14 +-
 .../demobank-ui/src/pages/OperationState/index.ts  |   4 +-
 .../demobank-ui/src/pages/OperationState/state.ts  |  34 +-
 .../demobank-ui/src/pages/OperationState/views.tsx |  48 +-
 .../demobank-ui/src/pages/PublicHistoriesPage.tsx  |   3 +-
 packages/demobank-ui/src/pages/QrCodeSection.tsx   |   7 +-
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |  33 +-
 .../demobank-ui/src/pages/WithdrawalQRCode.tsx     |  11 +-
 .../src/pages/account/ShowAccountDetails.tsx       |  16 +-
 .../src/pages/account/UpdateAccountPassword.tsx    |  14 +-
 .../taler-harness/src/http-client/bank-core.ts     | 978 ---------------------
 packages/taler-harness/src/index.ts                |  58 --
 .../taler-util/src/http-client/bank-conversion.ts  |  30 +-
 packages/taler-util/src/http-client/bank-core.ts   |  88 +-
 .../taler-util/src/http-client/bank-integration.ts |  14 +-
 .../taler-util/src/http-client/bank-revenue.ts     |   2 +-
 packages/taler-util/src/http-client/bank-wire.ts   |  19 +-
 packages/taler-util/src/http-client/types.ts       | 197 +++--
 18 files changed, 348 insertions(+), 1222 deletions(-)
 delete mode 100644 packages/taler-harness/src/http-client/bank-core.ts

diff --git a/packages/demobank-ui/src/hooks/access.ts 
b/packages/demobank-ui/src/hooks/access.ts
index da9812ffa..1e09c444a 100644
--- a/packages/demobank-ui/src/hooks/access.ts
+++ b/packages/demobank-ui/src/hooks/access.ts
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { AccessToken, TalerCoreBankResultByMethod, TalerHttpError } from 
"@gnu-taler/taler-util";
+import { AccessToken, TalerBankIntegrationResultByMethod, 
TalerCoreBankResultByMethod, TalerHttpError } from "@gnu-taler/taler-util";
 import { useState } from "preact/hooks";
 import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils.js";
 import { useBackendState } from "./backend.js";
@@ -62,10 +62,10 @@ export function useWithdrawalDetails(wid: string) {
   const { api } = useBankCoreApiContext();
 
   async function fetcher([wid]: [string]) {
-    return await api.getWithdrawalById(wid)
+    return await api.getIntegrationAPI().getWithdrawalOperationById(wid)
   }
 
-  const { data, error } = 
useSWR<TalerCoreBankResultByMethod<"getWithdrawalById">, TalerHttpError>(
+  const { data, error } = 
useSWR<TalerBankIntegrationResultByMethod<"getWithdrawalOperationById">, 
TalerHttpError>(
     [wid, "getWithdrawalById"], fetcher, {
     refreshInterval: 1000,
     refreshWhenHidden: false,
@@ -110,12 +110,12 @@ export function useTransactionDetails(account: string, 
tid: number) {
   return undefined;
 }
 
-export function usePublicAccounts(initial?: number) {
+export function usePublicAccounts(filterAccount: string |undefined ,initial?: 
number) {
   const [offset, setOffset] = useState<number | undefined>(initial);
   const { api } = useBankCoreApiContext();
 
-  async function fetcher([txid]: [number | undefined]) {
-    return await api.getPublicAccounts({
+  async function fetcher([account, txid]: [string | undefined , number | 
undefined]) {
+    return await api.getPublicAccounts({account},{
       limit: MAX_RESULT_SIZE,
       offset: txid ? String(txid) : undefined,
       order: "asc"
@@ -123,7 +123,7 @@ export function usePublicAccounts(initial?: number) {
   }
 
   const { data, error } = 
useSWR<TalerCoreBankResultByMethod<"getPublicAccounts">, TalerHttpError>(
-    [offset, "getPublicAccounts"], fetcher, {
+    [filterAccount, offset, "getPublicAccounts"], fetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
diff --git a/packages/demobank-ui/src/pages/OperationState/index.ts 
b/packages/demobank-ui/src/pages/OperationState/index.ts
index 120fd7b45..0776bbed5 100644
--- a/packages/demobank-ui/src/pages/OperationState/index.ts
+++ b/packages/demobank-ui/src/pages/OperationState/index.ts
@@ -83,8 +83,8 @@ export namespace State {
   }
   export interface NeedConfirmation {
     status: "need-confirmation",
-    onAbort: () => Promise<TalerCoreBankErrorsByMethod<"abortWithdrawalById"> 
| undefined>;
-    onConfirm: () => 
Promise<TalerCoreBankErrorsByMethod<"confirmWithdrawalById"> | undefined>;
+    onAbort: undefined | (() => 
Promise<TalerCoreBankErrorsByMethod<"abortWithdrawalById"> | undefined>);
+    onConfirm: undefined | (() => 
Promise<TalerCoreBankErrorsByMethod<"confirmWithdrawalById"> | undefined>);
     error: undefined;
     busy: boolean,
   }
diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts 
b/packages/demobank-ui/src/pages/OperationState/state.ts
index 30f7419f0..da924104a 100644
--- a/packages/demobank-ui/src/pages/OperationState/state.ts
+++ b/packages/demobank-ui/src/pages/OperationState/state.ts
@@ -74,7 +74,8 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
   const wid = withdrawalOperationId
 
   async function doAbort() {
-    const resp = await api.abortWithdrawalById(wid);
+    if (!creds) return;
+    const resp = await api.abortWithdrawalById(creds, wid);
     if (resp.type === "ok") {
       updateSettings("currentWithdrawalOperationId", undefined)
       onClose();
@@ -84,8 +85,9 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
   }
 
   async function doConfirm(): 
Promise<TalerCoreBankErrorsByMethod<"confirmWithdrawalById"> | undefined> {
+    if (!creds) return;
     setBusy({})
-    const resp = await api.confirmWithdrawalById(wid);
+    const resp = await api.confirmWithdrawalById(creds, wid);
     setBusy(undefined)
     if (resp.type === "ok") {
       mutate(() => true)//clean withdrawal state
@@ -142,23 +144,12 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
             },
           }
         }
-        case "invalid-id": {
-          return {
-            status: "aborted",
-            error: undefined,
-            onClose: async () => {
-              updateSettings("currentWithdrawalOperationId", undefined)
-              onClose()
-            },
-          }
-
-        }
-        default: assertUnreachable(result)
+        default: assertUnreachable(result.case)
       }
     }
 
     const { body: data } = result;
-    if (data.aborted) {
+    if (data.status === "aborted") {
       return {
         status: "aborted",
         error: undefined,
@@ -169,7 +160,7 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
       }
     }
 
-    if (data.confirmation_done) {
+    if (data.status === "confirmed") {
       if (!settings.showWithdrawalSuccess) {
         updateSettings("currentWithdrawalOperationId", undefined)
         onClose()
@@ -184,12 +175,15 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
       }
     }
 
-    if (!data.selection_done) {
+    if (data.status === "pending") {
       return {
         status: "ready",
         error: undefined,
         uri: parsedUri,
-        onClose: doAbort,
+        onClose: !creds ? (async () => { 
+          onClose(); 
+          return undefined 
+        }) : doAbort,
       }
     }
 
@@ -216,9 +210,9 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
     return {
       status: "need-confirmation",
       error: undefined,
-      onAbort: doAbort,
+      onAbort: !creds ? undefined : doAbort,
       busy: !!busy,
-      onConfirm: doConfirm
+      onConfirm: !creds ? undefined : doConfirm
     }
   }
 
diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx 
b/packages/demobank-ui/src/pages/OperationState/views.tsx
index a06147039..a3b30c179 100644
--- a/packages/demobank-ui/src/pages/OperationState/views.tsx
+++ b/packages/demobank-ui/src/pages/OperationState/views.tsx
@@ -69,6 +69,7 @@ export function NeedConfirmationView({ error, onAbort: 
doAbort, onConfirm: doCon
 
   async function onCancel() {
     errorHandler(async () => {
+      if (!doAbort) return;
       const resp = await doAbort()
       if (!resp) return;
       switch (resp.case) {
@@ -97,6 +98,7 @@ export function NeedConfirmationView({ error, onAbort: 
doAbort, onConfirm: doCon
 
   async function onConfirm() {
     errorHandler(async () => {
+      if (!doConfirm) return;
       const hasError = await doConfirm()
       if (!hasError) {
         if (!settings.showWithdrawalSuccess) {
@@ -186,26 +188,34 @@ export function NeedConfirmationView({ error, onAbort: 
doAbort, onConfirm: doCon
                 <ShowInputErrorLabel message={errors?.answer} 
isDirty={captchaAnswer !== undefined} />
               </div>
             </div>
-            <div class="flex items-center justify-between gap-x-6 border-t 
border-gray-900/10 px-4 py-4 sm:px-8">
-              <button type="button" class="text-sm font-semibold leading-6 
text-gray-900"
-                onClick={(e) => {
-                  e.preventDefault()
-                  onCancel()
-                }}
-              >
-                <i18n.Translate>Cancel</i18n.Translate></button>
-              <button type="submit"
-                class="disabled:opacity-50 disabled:cursor-default 
cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold 
text-white shadow-sm hover:bg-indigo-500 focus-visible:outline 
focus-visible:outline-2 focus-visible:outline-offset-2 
focus-visible:outline-indigo-600"
-                disabled={!!errors}
-                onClick={(e) => {
-                  e.preventDefault()
-                  onConfirm()
-                }}
-              >
-                <i18n.Translate>Transfer</i18n.Translate>
-              </button>
-            </div>
+            {!doAbort || !doConfirm ?
+              <Attention type="warning" title={i18n.str`not logged in`}>
+                <i18n.Translate>
+                  You need to log in as pepito to complete the operation
+                </i18n.Translate>
+              </Attention>
+              :
 
+              <div class="flex items-center justify-between gap-x-6 border-t 
border-gray-900/10 px-4 py-4 sm:px-8">
+                <button type="button" class="text-sm font-semibold leading-6 
text-gray-900"
+                  onClick={(e) => {
+                    e.preventDefault()
+                    onCancel()
+                  }}
+                >
+                  <i18n.Translate>Cancel</i18n.Translate></button>
+                <button type="submit"
+                  class="disabled:opacity-50 disabled:cursor-default 
cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold 
text-white shadow-sm hover:bg-indigo-500 focus-visible:outline 
focus-visible:outline-2 focus-visible:outline-offset-2 
focus-visible:outline-indigo-600"
+                  disabled={!!errors}
+                  onClick={(e) => {
+                    e.preventDefault()
+                    onConfirm()
+                  }}
+                >
+                  <i18n.Translate>Transfer</i18n.Translate>
+                </button>
+              </div>
+            }
           </form>
         </div>
         <div class="px-4 mt-4 ">
diff --git a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx 
b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
index 7d93e7222..eb6f6fd27 100644
--- a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
+++ b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
@@ -32,7 +32,8 @@ interface Props { }
 export function PublicHistoriesPage({ }: Props): VNode {
   const { i18n } = useTranslationContext();
 
-  const result = usePublicAccounts();
+  //TODO: implemented filter by account name
+  const result = usePublicAccounts(undefined);
   const firstAccount = result && !(result instanceof TalerError) && 
result.data.public_accounts.length > 0
     ? result.data.public_accounts[0].account_name
     : undefined;
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx 
b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index f1394b3f6..60beb012e 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -30,6 +30,7 @@ import { useBankCoreApiContext } from "../context/config.js";
 import { withRuntimeErrorHandling } from "../utils.js";
 import { assertUnreachable } from "./WithdrawalOperationPage.js";
 import { LocalNotificationBanner } from "@gnu-taler/web-util/browser";
+import { useBackendState } from "../hooks/backend.js";
 
 export function QrCodeSection({
   withdrawUri,
@@ -40,6 +41,9 @@ export function QrCodeSection({
 }): VNode {
   const { i18n } = useTranslationContext();
   const talerWithdrawUri = stringifyWithdrawUri(withdrawUri);
+  const { state: credentials } = useBackendState();
+  const creds = credentials.status !== "loggedIn" ? undefined : credentials
+
   useEffect(() => {
     //Taler Wallet WebExtension is listening to headers response and tab 
updates.
     //In the SPA there is no header response with the Taler URI so
@@ -58,7 +62,8 @@ export function QrCodeSection({
 
   async function doAbort() {
     await handleError(async () => {
-      const resp = await 
api.abortWithdrawalById(withdrawUri.withdrawalOperationId);
+      if (!creds) return;
+      const resp = await api.abortWithdrawalById(creds, 
withdrawUri.withdrawalOperationId);
       if (resp.type === "ok") {
         onAborted();
       } else {
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx 
b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index bfb118c6c..e21c0917b 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -20,10 +20,13 @@ import {
   PaytoUri,
   PaytoUriIBAN,
   PaytoUriTalerBank,
+  TalerError,
   TranslatedString,
   WithdrawUriResult
 } from "@gnu-taler/taler-util";
 import {
+  ErrorLoading,
+  Loading,
   notifyInfo,
   useLocalNotification,
   useTranslationContext
@@ -38,6 +41,10 @@ import { undefinedIfEmpty, withRuntimeErrorHandling } from 
"../utils.js";
 import { RenderAmount } from "./PaytoWireTransferForm.js";
 import { assertUnreachable } from "./WithdrawalOperationPage.js";
 import { LocalNotificationBanner } from "@gnu-taler/web-util/browser";
+import { useBackendState } from "../hooks/backend.js";
+import { useWithdrawalDetails } from "../hooks/access.js";
+import { OperationState } from "./OperationState/index.js";
+import { OperationNotFound } from "./WithdrawalQRCode.js";
 
 const logger = new Logger("WithdrawalConfirmationQuestion");
 
@@ -60,8 +67,23 @@ export function WithdrawalConfirmationQuestion({
   withdrawUri,
 }: Props): VNode {
   const { i18n } = useTranslationContext();
-  const [settings, updateSettings] = usePreferences()
-
+  const [settings] = usePreferences()
+  const { state: credentials } = useBackendState();
+  const creds = credentials.status !== "loggedIn" ? undefined : credentials
+  const withdrawalInfo = 
useWithdrawalDetails(withdrawUri.withdrawalOperationId)
+  if (!withdrawalInfo) {
+    return <Loading />
+  }
+  if (withdrawalInfo instanceof TalerError) {
+    return <ErrorLoading error={withdrawalInfo} />
+  }
+  if (withdrawalInfo.type === "fail") {
+    switch(withdrawalInfo.case) {
+      case "not-found": return <OperationNotFound onClose={onAborted}  />
+      default: assertUnreachable(withdrawalInfo.case)
+    }
+  }
+  
   const captchaNumbers = useMemo(() => {
     return {
       a: Math.floor(Math.random() * 10),
@@ -87,7 +109,8 @@ export function WithdrawalConfirmationQuestion({
   async function doTransfer() {
     setBusy({})
     await handleError(async () => {
-      const resp = await 
api.confirmWithdrawalById(withdrawUri.withdrawalOperationId);
+      if (!creds) return;
+      const resp = await api.confirmWithdrawalById(creds, 
withdrawUri.withdrawalOperationId);
       if (resp.type === "ok") {
         mutate(() => true)// clean any info that we have
         if (!settings.showWithdrawalSuccess) {
@@ -135,7 +158,8 @@ export function WithdrawalConfirmationQuestion({
   async function doCancel() {
     setBusy({})
     await handleError(async () => {
-      const resp = await 
api.abortWithdrawalById(withdrawUri.withdrawalOperationId);
+      if (!creds) return;
+      const resp = await api.abortWithdrawalById(creds, 
withdrawUri.withdrawalOperationId);
       if (resp.type === "ok") {
         onAborted();
       } else {
@@ -217,6 +241,7 @@ export function WithdrawalConfirmationQuestion({
                     <ShowInputErrorLabel message={errors?.answer} 
isDirty={captchaAnswer !== undefined} />
                   </div>
                 </div>
+
                 <div class="flex items-center justify-between gap-x-6 border-t 
border-gray-900/10 px-4 py-4 sm:px-8">
                   <button type="button" class="text-sm font-semibold leading-6 
text-gray-900"
                     onClick={doCancel}
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx 
b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index 52e3c63ee..0c3d83c3b 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -58,14 +58,13 @@ export function WithdrawalQRCode({
   if (result.type === "fail") {
     switch (result.case) {
       case "not-found": return <OperationNotFound onClose={onClose} />
-      case "invalid-id": return <OperationNotFound onClose={onClose} />
-      default: assertUnreachable(result)
+      default: assertUnreachable(result.case)
     }
   }
 
   const { body: data } = result;
 
-  if (data.aborted) {
+  if (data.status === "aborted") {
     return <section id="main" class="content">
       <h1 class="nav">{i18n.str`Operation aborted`}</h1>
       <section>
@@ -93,7 +92,7 @@ export function WithdrawalQRCode({
     </section>
   }
 
-  if (data.confirmation_done) {
+  if (data.status === "confirmed") {
     return <div class="relative ml-auto mr-auto transform overflow-hidden 
rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 
sm:w-full sm:max-w-sm sm:p-6">
       <div>
         <div class="mx-auto flex h-12 w-12 items-center justify-center 
rounded-full bg-green-100">
@@ -128,7 +127,7 @@ export function WithdrawalQRCode({
 
 
   }
-  if (!data.selection_done) {
+  if (data.status === "pending") {
     return (
       <QrCodeSection
         withdrawUri={withdrawUri}
@@ -173,7 +172,7 @@ export function WithdrawalQRCode({
 }
 
 
-function OperationNotFound({ onClose }: { onClose: () => void }): VNode {
+export function OperationNotFound({ onClose }: { onClose: () => void }): VNode 
{
   const { i18n } = useTranslationContext();
   return <div class="relative ml-auto mr-auto transform overflow-hidden 
rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 
sm:w-full sm:max-w-sm sm:p-6">
     <div>
diff --git a/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx 
b/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
index 4332284e8..b5579c199 100644
--- a/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
+++ b/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
@@ -82,9 +82,21 @@ export function ShowAccountDetails({
             description: resp.detail.hint as TranslatedString,
             debug: resp.detail,
           })
-          case "cant-change-legal-name-or-admin": return notify({
+          case "admin-cant-be-exchange": return notify({
             type: "error",
-            title: i18n.str`Can't change legal name`,
+            title: i18n.str`This is an administration account, changing to 
exchange account is denied.`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          })
+          case "user-cant-change-name": return notify({
+            type: "error",
+            title: i18n.str`You can't change the legal name, please contact 
the your account administrator.`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          })
+          case "user-cant-change-debt": return notify({
+            type: "error",
+            title: i18n.str`You can't change the debt limit, please contact 
the your account administrator.`,
             description: resp.detail.hint as TranslatedString,
             debug: resp.detail,
           })
diff --git a/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx 
b/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx
index 3c00ad1b8..eef2a0692 100644
--- a/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx
+++ b/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx
@@ -61,16 +61,18 @@ export function UpdateAccountPassword({
             type: "error",
             title: i18n.str`Not authorized to change the password, maybe the 
session is invalid.`
           })
-          case "old-password-invalid-or-not-allowed": return notify({
-            type: "error",
-            title: current ?
-              i18n.str`This user have no right on to change the password.` :
-              i18n.str`This user have no right on to change the password or 
the old password doesn't match.`
-          })
           case "not-found": return notify({
             type: "error",
             title: i18n.str`Account not found`
           })
+          case "user-require-old-password": return notify({
+            type: "error",
+            title: i18n.str`Old password need to be provided in order to 
change new one. If you don't have it contact your account administrator.`
+          })
+          case "wrong-old-password": return notify({
+            type: "error",
+            title: i18n.str`Your current password doesn't match, can't change 
to a new password.`
+          })
           default: assertUnreachable(resp)
         }
       }
diff --git a/packages/taler-harness/src/http-client/bank-core.ts 
b/packages/taler-harness/src/http-client/bank-core.ts
deleted file mode 100644
index 615cb29f5..000000000
--- a/packages/taler-harness/src/http-client/bank-core.ts
+++ /dev/null
@@ -1,978 +0,0 @@
-import { AccessToken, AmountJson, Amounts, TalerCoreBankHttpClient, 
TalerCorebankApi, TalerRevenueHttpClient, TalerWireGatewayApi, 
TalerWireGatewayHttpClient, TestForApi, buildPayto, encodeCrock, failOrThrow, 
getRandomBytes, parsePaytoUri, stringifyPaytoUri, succeedOrThrow } from 
"@gnu-taler/taler-util"
-
-
-
-export function createTestForBankCore(api: TalerCoreBankHttpClient, 
adminToken: AccessToken): TestForApi<TalerCoreBankHttpClient> {
-  return {
-    test_abortCashoutById: {
-      success: undefined,
-      "already-confirmed": undefined,
-      "cashout-not-supported": undefined,
-      "not-found": undefined,
-    },
-    test_createCashout: {
-      "account-not-found": undefined,
-      "incorrect-exchange-rate": undefined,
-      "no-contact-info": undefined,
-      "no-enough-balance": undefined,
-      "cashout-not-supported": undefined,
-      "request-already-used": undefined,
-      "tan-failed": undefined,
-      success: undefined,
-    },
-    test_confirmCashoutById: {
-      "already-aborted": undefined,
-      "incorrect-exchange-rate": undefined,
-      "no-cashout-payto": undefined,
-      "no-enough-balance": undefined,
-      "invalid-code": undefined,
-      "too-many-attempts": undefined,
-      "cashout-not-supported": undefined,
-      "not-found": undefined,
-      success: undefined,
-    },
-    test_getAccountCashouts: {
-      "cashout-not-supported": undefined,
-      "account-not-found": undefined,
-      success: undefined,
-    },
-    test_getCashoutById: {
-      "cashout-not-supported": undefined,
-      success: undefined,
-      "not-found": undefined,
-    },
-    test_getGlobalCashouts: {
-      "cashout-not-supported": undefined,
-      success: undefined,
-    },
-    test_abortWithdrawalById: {
-      "invalid-id": async () => {
-        await failOrThrow("invalid-id", () =>
-          api.abortWithdrawalById("invalid")
-        )
-      },
-      "not-found": async () => {
-        await failOrThrow("not-found", () =>
-          api.abortWithdrawalById("11111111-1111-1111-1111-111111111111")
-        )
-      },
-      "previously-confirmed": async () => {
-        const { username: exchangeUser, token: exchangeToken } = await 
createRandomTestUser(api, adminToken, { is_taler_exchange: true })
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-        const exchangeInfo = await succeedOrThrow(() =>
-          api.getAccount({ username: exchangeUser, token: exchangeToken })
-        )
-
-        const { withdrawal_id } = await succeedOrThrow(() =>
-          api.createWithdrawal({ username, token }, {
-            amount: userInfo.balance.amount
-          })
-        )
-
-        await succeedOrThrow(() =>
-          
api.getIntegrationAPI().completeWithdrawalOperationById(withdrawal_id, {
-            reserve_pub: encodeCrock(getRandomBytes(32)),
-            selected_exchange: exchangeInfo.payto_uri,
-          })
-        )
-        await succeedOrThrow(() =>
-          api.confirmWithdrawalById(withdrawal_id)
-        )
-        await failOrThrow("previously-confirmed", () =>
-          api.abortWithdrawalById(withdrawal_id)
-        )
-      },
-      success: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-
-        const { withdrawal_id: firstWithdrawal } = await succeedOrThrow(() =>
-          api.createWithdrawal({ username, token }, {
-            amount: userInfo.balance.amount
-          })
-        )
-
-        await succeedOrThrow(() =>
-          api.abortWithdrawalById(firstWithdrawal)
-        )
-      },
-    },
-    test_confirmWithdrawalById: {
-      "insufficient-funds": async () => {
-
-      },
-      "invalid-id": async () => {
-        await failOrThrow("invalid-id", () =>
-          api.confirmWithdrawalById("invalid")
-        )
-      },
-      "no-exchange-or-reserve-selected": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-
-        const { withdrawal_id } = await succeedOrThrow(() =>
-          api.createWithdrawal({ username, token }, {
-            amount: userInfo.balance.amount
-          })
-        )
-
-        await failOrThrow("no-exchange-or-reserve-selected", () =>
-          api.confirmWithdrawalById(withdrawal_id)
-        )
-      },
-      "not-found": async () => {
-        await failOrThrow("not-found", () =>
-          api.confirmWithdrawalById("11111111-1111-1111-1111-111111111111")
-        )
-      },
-      "previously-aborted": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-        const { withdrawal_id } = await succeedOrThrow(() =>
-          api.createWithdrawal({ username, token }, {
-            amount: userInfo.balance.amount
-          })
-        )
-
-        await succeedOrThrow(() =>
-          api.abortWithdrawalById(withdrawal_id)
-        )
-        await failOrThrow("previously-aborted", () =>
-          api.confirmWithdrawalById(withdrawal_id)
-        )
-      },
-      success: async () => {
-        const { username: exchangeUser, token: exchangeToken } = await 
createRandomTestUser(api, adminToken, { is_taler_exchange: true })
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-        const exchangeInfo = await succeedOrThrow(() =>
-          api.getAccount({ username: exchangeUser, token: exchangeToken })
-        )
-
-        const { withdrawal_id } = await succeedOrThrow(() =>
-          api.createWithdrawal({ username, token }, {
-            amount: userInfo.balance.amount
-          })
-        )
-
-        await succeedOrThrow(() =>
-          
api.getIntegrationAPI().completeWithdrawalOperationById(withdrawal_id, {
-            reserve_pub: encodeCrock(getRandomBytes(32)),
-            selected_exchange: exchangeInfo.payto_uri,
-          })
-        )
-
-        await succeedOrThrow(() =>
-          api.confirmWithdrawalById(withdrawal_id)
-        )
-
-      },
-    },
-    test_createAccount: {
-      "insufficient-funds": undefined,
-      "payto-already-exists": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-
-        const anotherUsername = "harness-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
-        await failOrThrow("payto-already-exists", () =>
-          api.createAccount(adminToken, {
-            name: anotherUsername,
-            username: anotherUsername,
-            password: "123",
-            internal_payto_uri: userInfo.payto_uri,
-          })
-        );
-
-      },
-      "username-reserved": async () => {
-        await failOrThrow("username-reserved", () =>
-          api.createAccount(adminToken, {
-            name: "admin",
-            username: "admin", password: "123"
-          })
-        )
-      },
-      "username-already-exists": async () => {
-        const username = "harness-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
-        await succeedOrThrow(() =>
-          api.createAccount(adminToken, {
-            name: username,
-            username, password: "123"
-          })
-        )
-
-        await failOrThrow("username-already-exists", () =>
-          api.createAccount(adminToken, {
-            name: username,
-            username, password: "123"
-          })
-        );
-      },
-      "invalid-phone-or-email": async () => {
-        const username = "harness-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
-        await failOrThrow("invalid-input", () =>
-          api.createAccount(adminToken, {
-            name: username,
-            username, password: "123",
-            challenge_contact_data: {
-              email: "invalid email",
-              phone: "invalid phone",
-            }
-          })
-        )
-      },
-      success: async () => {
-        const username = "harness-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
-
-        await succeedOrThrow(() =>
-          api.createAccount(adminToken, {
-            name: username,
-            username, password: "123"
-          })
-        )
-      },
-      unauthorized: async () => {
-        const username = "harness-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
-
-        await succeedOrThrow(() =>
-          api.createAccount(adminToken, {
-            name: username,
-            username, password: "123"
-          })
-        )
-
-        const { access_token } = await succeedOrThrow(() =>
-          api.getAuthenticationAPI(username).createAccessToken("123", {
-            scope: "readwrite"
-          })
-        )
-
-        const anotherUser = "harness-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
-        await failOrThrow("unauthorized", () =>
-          api.createAccount(access_token, {
-            name: anotherUser,
-            username: anotherUser, password: "123"
-          })
-        )
-
-      },
-
-    },
-    test_createTransaction: {
-      "creditor-not-found": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-
-        const notFoundAccount = buildPayto("iban", "DE1231231231", undefined)
-        notFoundAccount.params["message"] = "not-found"
-        await failOrThrow("creditor-not-found", () =>
-          api.createTransaction({ username, token }, {
-            payto_uri: stringifyPaytoUri(notFoundAccount),
-            amount: userInfo.balance.amount
-          })
-        )
-      },
-      "creditor-same": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-        const account = parsePaytoUri(userInfo.payto_uri)!
-        account.params["message"] = "myaccount"
-
-        await failOrThrow("creditor-same", () =>
-          api.createTransaction({ username, token }, {
-            payto_uri: stringifyPaytoUri(account),
-            amount: userInfo.balance.amount
-          })
-        )
-
-      },
-      "insufficient-funds": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        const { username: otherUser, token: otherToken } = await 
createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-        const otherInfo = await succeedOrThrow(() =>
-          api.getAccount({ username: otherUser, token: otherToken })
-        )
-        const otherAccount = parsePaytoUri(otherInfo.payto_uri)!
-        otherAccount.params["message"] = "all"
-
-        await failOrThrow("insufficient-funds", () =>
-          api.createTransaction({ username, token }, {
-            payto_uri: stringifyPaytoUri(otherAccount),
-            amount: Amounts.stringify(Amounts.mult(userInfo.balance.amount, 
20).amount)
-          })
-        )
-      },
-      "not-found": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        const { username: otherUser, token: otherToken } = await 
createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-        const otherInfo = await succeedOrThrow(() =>
-          api.getAccount({ username: otherUser, token: otherToken })
-        )
-        const otherAccount = parsePaytoUri(otherInfo.payto_uri)!
-        otherAccount.params["message"] = "all"
-
-        await succeedOrThrow(() =>
-          api.createTransaction({ username: "notfound", token }, {
-            payto_uri: stringifyPaytoUri(otherAccount),
-            amount: userInfo.balance.amount
-          })
-        )
-      },
-      "invalid-input": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        const { username: otherUser, token: otherToken } = await 
createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-        const otherInfo = await succeedOrThrow(() =>
-          api.getAccount({ username: otherUser, token: otherToken })
-        )
-        const otherAccount = parsePaytoUri(otherInfo.payto_uri)!
-        otherAccount.params["message"] = "all"
-
-        //missing amount
-        await failOrThrow("invalid-input", () =>
-          api.createTransaction({ username, token }, {
-            payto_uri: stringifyPaytoUri(otherAccount),
-            // amount: userInfo.balance.amount
-          })
-        )
-        //missing subject
-        await failOrThrow("invalid-input", () =>
-          api.createTransaction({ username, token }, {
-            payto_uri: otherInfo.payto_uri,
-            amount: userInfo.balance.amount
-          })
-        )
-
-      },
-      success: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        const { username: otherUser, token: otherToken } = await 
createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-        const otherInfo = await succeedOrThrow(() =>
-          api.getAccount({ username: otherUser, token: otherToken })
-        )
-        const otherAccount = parsePaytoUri(otherInfo.payto_uri)!
-        otherAccount.params["message"] = "all"
-
-        await succeedOrThrow(() =>
-          api.createTransaction({ username, token }, {
-            payto_uri: stringifyPaytoUri(otherAccount),
-            amount: userInfo.balance.amount
-          })
-        )
-
-      },
-      unauthorized: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        const { username: otherUser, token: otherToken } = await 
createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-        const otherInfo = await succeedOrThrow(() =>
-          api.getAccount({ username: otherUser, token: otherToken })
-        )
-        const otherAccount = parsePaytoUri(otherInfo.payto_uri)!
-        otherAccount.params["message"] = "all"
-
-        await failOrThrow("unauthorized", () =>
-          api.createTransaction({ username, token: "wrongtoken" as AccessToken 
}, {
-            payto_uri: stringifyPaytoUri(otherAccount),
-            amount: userInfo.balance.amount
-          })
-        )
-      },
-    },
-    test_createWithdrawal: {
-      "account-not-found": async () => {
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username: "admin", token: adminToken })
-        )
-        await succeedOrThrow(() =>
-          api.createWithdrawal({ username: "notfound", token: adminToken }, {
-            amount: userInfo.balance.amount
-          })
-        )
-
-      },
-      "insufficient-funds": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-
-        const balance = Amounts.parseOrThrow(userInfo.balance.amount)
-        const moreThanBalance = Amounts.stringify(Amounts.mult(balance, 
5).amount)
-        await failOrThrow("insufficient-funds", () =>
-          api.createWithdrawal({ username, token }, {
-            amount: moreThanBalance
-          })
-        )
-      },
-      success: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-        await succeedOrThrow(() =>
-          api.createWithdrawal({ username, token }, {
-            amount: userInfo.balance.amount
-          })
-        )
-
-      },
-      unauthorized: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-        await failOrThrow("unauthorized", () =>
-          api.createWithdrawal({ username, token: "wrongtoken" as AccessToken 
}, {
-            amount: userInfo.balance.amount
-          })
-        )
-
-      },
-    },
-    test_deleteAccount: {
-      "balance-not-zero": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        await failOrThrow("balance-not-zero", () =>
-          api.deleteAccount({ username, token: adminToken })
-        )
-
-      },
-      "not-found": async () => {
-        await failOrThrow("not-found", () =>
-          api.deleteAccount({ username: "not-found", token: adminToken })
-        )
-      },
-      "username-reserved": async () => {
-        await failOrThrow("username-reserved", () =>
-          api.deleteAccount({ username: "admin", token: adminToken })
-        )
-        await failOrThrow("username-reserved", () =>
-          api.deleteAccount({ username: "bank", token: adminToken })
-        )
-      },
-      success: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-
-        const adminInfo = await succeedOrThrow(() =>
-          api.getAccount({ username: "admin", token: adminToken })
-        )
-
-        const adminAccount = parsePaytoUri(adminInfo.payto_uri)!
-        adminAccount.params["message"] = "all my money"
-        const withSubject = stringifyPaytoUri(adminAccount)
-
-        await succeedOrThrow(() =>
-          api.createTransaction({ username, token }, {
-            payto_uri: withSubject,
-            amount: userInfo.balance.amount
-          })
-        )
-
-      },
-      unauthorized: async () => {
-        const username = "harness-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
-
-        await succeedOrThrow(() =>
-          api.createAccount(adminToken, {
-            name: username,
-            username, password: "123"
-          })
-        )
-
-        const { token } = await createRandomTestUser(api, adminToken)
-        await failOrThrow("unauthorized", () =>
-          api.deleteAccount({ username: username, token })
-        )
-
-      },
-    },
-    test_getAccount: {
-      "not-found": async () => {
-        await failOrThrow("not-found", () =>
-          api.getAccount({ username: "not-found", token: adminToken })
-        )
-
-      },
-      success: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-      },
-      unauthorized: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        await failOrThrow("unauthorized", () =>
-          api.getAccount({ username, token: "wrongtoken" as AccessToken })
-        )
-      },
-    },
-    test_getAccounts: {
-      success: async () => {
-        await succeedOrThrow(() =>
-          api.getAccounts(adminToken)
-        )
-        await succeedOrThrow(() =>
-          api.getAccounts(adminToken, {
-            account: "admin"
-          })
-        )
-        await succeedOrThrow(() =>
-          api.getAccounts(adminToken, undefined, {
-            order: "dec",
-            limit: 10,
-            offset: "1"
-          })
-        )
-      },
-      unauthorized: async () => {
-        await failOrThrow("unauthorized", () =>
-          api.getAccounts("ASDASD" as AccessToken)
-        )
-      },
-    },
-    test_getConfig: {
-      success: async () => {
-        const config = await succeedOrThrow(() => api.getConfig())
-
-        if (!api.isCompatible(config.version)) {
-          throw Error(`not compatible with server ${config.version}`)
-        }
-
-      },
-    },
-    test_getMonitor: {
-      "unauthorized": async () => {
-        await failOrThrow("unauthorized", () => (
-          api.getMonitor("wrongtoken" as AccessToken)
-        ))
-
-      },
-      "invalid-input": async () => {
-
-        await failOrThrow("invalid-input", () => (
-          api.getMonitor(adminToken, {
-            timeframe: TalerCorebankApi.MonitorTimeframeParam.day,
-            which: 100
-          })
-        ))
-
-      },
-      "monitor-not-supported": undefined,
-      success: async () => {
-
-        await succeedOrThrow(() => (
-          api.getMonitor(adminToken)
-        ))
-
-        await succeedOrThrow(() => (
-          api.getMonitor(adminToken, {
-            timeframe: TalerCorebankApi.MonitorTimeframeParam.day,
-            which: (new Date()).getDate() - 1
-          })
-        ))
-
-      },
-    },
-    test_getPublicAccounts: {
-      success: async () => {
-        await succeedOrThrow(() => (
-          api.getPublicAccounts()
-        ))
-
-        await succeedOrThrow(() => (
-          api.getPublicAccounts({
-            order: "asc"
-          })
-        ))
-        await succeedOrThrow(() => (
-          api.getPublicAccounts({
-            order: "dec"
-          })
-        ))
-        await succeedOrThrow(() => (
-          api.getPublicAccounts({
-            order: "dec", limit: 10, offset: String(1)
-          })
-        ))
-      },
-    },
-    test_getTransactionById: {
-      "not-found": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        await failOrThrow("not-found", () =>
-          api.getTransactionById({ username, token }, 123123123)
-        )
-      },
-      success: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        const { username: otherUser, token: otherToken } = await 
createRandomTestUser(api, adminToken)
-
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-        const otherInfo = await succeedOrThrow(() =>
-          api.getAccount({ username: otherUser, token: otherToken })
-        )
-        const otherAccount = parsePaytoUri(otherInfo.payto_uri)!
-        otherAccount.params["message"] = "all"
-
-        await succeedOrThrow(() =>
-          api.createTransaction({ username, token }, {
-            payto_uri: stringifyPaytoUri(otherAccount),
-            amount: userInfo.balance.amount
-          })
-        )
-
-        const txs = await succeedOrThrow(() => api.getTransactions({ username, 
token }, {
-          limit: 5,
-          order: "asc"
-        }))
-        const rowId = txs.transactions[0].row_id
-
-        await succeedOrThrow(() =>
-          api.getTransactionById({ username, token }, rowId)
-        )
-
-      },
-      unauthorized: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        await failOrThrow("unauthorized", () =>
-          api.getTransactionById({ username, token: "wrongtoken" as 
AccessToken }, 123123123)
-        )
-      },
-    },
-    test_getTransactions: {
-      "not-found": async () => {
-        await failOrThrow("not-found", () => api.getTransactions({
-          username: "not-found",
-          token: adminToken,
-        }))
-      },
-      success: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        // await succeedOrThrow(() => api.getTransactions(creds))
-        const txs = await succeedOrThrow(() => api.getTransactions({ username, 
token }, {
-          limit: 5,
-          order: "asc"
-        }))
-      },
-      unauthorized: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        await failOrThrow("unauthorized", () => api.getTransactions({
-          username: username,
-          token: "wrongtoken" as AccessToken,
-        }))
-
-      },
-    },
-    test_getWithdrawalById: {
-      "invalid-id": async () => {
-
-        await failOrThrow("invalid-id", () =>
-          api.getWithdrawalById("invalid")
-        )
-
-      },
-      "not-found": async () => {
-        await failOrThrow("not-found", () =>
-          api.getWithdrawalById("11111111-1111-1111-1111-111111111111")
-        )
-
-      },
-      success: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        const userInfo = await succeedOrThrow(() =>
-          api.getAccount({ username, token })
-        )
-        const { withdrawal_id } = await succeedOrThrow(() =>
-          api.createWithdrawal({ username, token }, {
-            amount: userInfo.balance.amount
-          })
-        )
-        await succeedOrThrow(() =>
-          api.getWithdrawalById(withdrawal_id)
-        )
-      },
-    },
-    test_updateAccount: {
-      "cant-change-legal-name-or-admin": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        await failOrThrow("cant-change-legal-name-or-admin", () =>
-          api.updateAccount({ username, token }, {
-            name: "something else",
-          })
-        )
-
-      },
-      "not-found": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        await failOrThrow("not-found", () =>
-          api.updateAccount({ username: "notfound", token }, {
-            challenge_contact_data: {
-              email: "asd@Aasd.com"
-            }
-          })
-        )
-      },
-      success: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        await succeedOrThrow(() =>
-          api.updateAccount({ username, token }, {
-            challenge_contact_data: {
-              email: "asd@Aasd.com"
-            }
-          })
-        )
-
-      },
-      unauthorized: async () => {
-
-        await failOrThrow("unauthorized", () =>
-          api.updateAccount({ username: "notfound", token: "wrongtoken" as 
AccessToken }, {
-            challenge_contact_data: {
-              email: "asd@Aasd.com"
-            }
-          })
-        )
-      },
-    },
-    test_updatePassword: {
-      "not-found": async () => {
-
-        await failOrThrow("not-found", () =>
-          api.updatePassword({ username: "notfound", token: adminToken }, {
-            old_password: "123",
-            new_password: "234"
-          })
-        )
-
-
-      },
-      "old-password-invalid-or-not-allowed": async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        await failOrThrow("old-password-invalid-or-not-allowed", () =>
-          api.updatePassword({ username, token }, {
-            old_password: "1233",
-            new_password: "234"
-          })
-        )
-
-      },
-      success: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-
-        await succeedOrThrow(() =>
-          api.updatePassword({ username, token }, {
-            old_password: "123",
-            new_password: "234"
-          })
-        )
-
-
-      },
-      unauthorized: async () => {
-        const { username, token } = await createRandomTestUser(api, adminToken)
-        await failOrThrow("unauthorized", () =>
-          api.updatePassword({ username: "admin", token }, {
-            old_password: "123",
-            new_password: "234"
-          })
-        )
-
-
-      },
-    },
-  }
-}
-
-export function createTestForBankRevenue(bank: TalerCoreBankHttpClient, 
adminToken: AccessToken): TestForApi<TalerRevenueHttpClient> {
-
-  return {
-    test_getHistory: {
-      "endpoint-wrong-or-username-wrong": async () => {
-        const history = await failOrThrow("endpoint-wrong-or-username-wrong", 
() =>
-          bank.getRevenueAPI("notfound").getHistory("wrongtoken" as 
AccessToken)
-        )
-      },
-      "invalid-input": undefined,
-      success: async () => {
-        const { token: exchangeToken, username: exchangeUsername } = await 
createRandomTestUser(bank, adminToken, {
-          is_taler_exchange: true
-        })
-        const { token: merchantToken, username: merchantUsername } = await 
createRandomTestUser(bank, adminToken)
-        const config = await succeedOrThrow(() => bank.getConfig())
-
-        const merchantinfo = await succeedOrThrow(() =>
-          bank.getAccount({ username: merchantUsername, token: merchantToken })
-        )
-        const account = parsePaytoUri(merchantinfo.payto_uri)!
-        account.params["message"] = "all"
-
-        const amount = Amounts.stringify({
-          currency: config.currency,
-          fraction: 0,
-          value: 1
-        })
-
-        await succeedOrThrow(() =>
-          bank.createTransaction({ username: exchangeUsername, token: 
exchangeToken }, {
-            payto_uri: stringifyPaytoUri(account),
-            amount
-          })
-        )
-        const history = await succeedOrThrow(() =>
-          bank.getRevenueAPI(merchantUsername).getHistory(merchantToken)
-        )
-      },
-      unauthorized: async () => {
-        const { token: merchantToken, username: merchantUsername } = await 
createRandomTestUser(bank, adminToken)
-        const history = await failOrThrow("unauthorized", () =>
-          bank.getRevenueAPI(merchantUsername).getHistory("wrongtoken" as 
AccessToken)
-        )
-      },
-    }
-  }
-}
-
-export function createTestForBankWireGateway(bank: TalerCoreBankHttpClient, 
adminToken: AccessToken): TestForApi<TalerWireGatewayHttpClient> {
-  return {
-    //not used in production
-    test_addIncoming: {
-      "invalid-input": undefined,
-      "not-found": undefined,
-      "reserve-id-already-used": undefined,
-      success: undefined,
-      unauthorized: undefined,
-    },
-    test_getHistoryIncoming: {
-      "invalid-input": async () => {
-      },
-      "not-found": async () => {
-      },
-      success: async () => {
-      },
-      unauthorized: async () => {
-      },
-    },
-    test_getHistoryOutgoing: {
-      "invalid-input": async () => {
-      },
-      "not-found": async () => {
-      },
-      success: async () => {
-      },
-      unauthorized: async () => {
-      },
-    },
-    test_transfer: {
-      "invalid-input": async () => {
-      },
-      "not-found": async () => {
-      },
-      "request-uid-already-used": async () => {
-      },
-      success: async () => {
-        const { token: exchangeToken, username: exchangeUsername } = await 
createRandomTestUser(bank, adminToken, {
-          is_taler_exchange: true
-        })
-        const { token: merchantToken, username: merchantUsername } = await 
createRandomTestUser(bank, adminToken)
-        const config = await succeedOrThrow(() => bank.getConfig())
-
-        const merchantInfo = await succeedOrThrow(() =>
-          bank.getAccount({ username: merchantUsername, token: merchantToken })
-        )
-        const account = parsePaytoUri(merchantInfo.payto_uri)!
-        account.params["message"] = "all"
-
-        const amount = Amounts.stringify({
-          currency: config.currency,
-          fraction: 0,
-          value: 1
-        })
-        const resp = await succeedOrThrow(() =>
-          bank.getWireGatewayAPI(merchantUsername).transfer(exchangeToken, {
-            amount,
-            credit_account: merchantInfo.payto_uri,
-            exchange_base_url: "",
-            request_uid: "",
-            wtid: ""
-          })
-        )
-
-      },
-      unauthorized: async () => {
-      },
-    }
-  }
-}
-
-
-export async function createRandomTestUser(api: TalerCoreBankHttpClient, 
adminToken: AccessToken, options: 
Partial<TalerCorebankApi.RegisterAccountRequest> = {}) {
-  const username = "harness-" + encodeCrock(getRandomBytes(10)).toLowerCase();
-  await succeedOrThrow(() =>
-    api.createAccount(adminToken, {
-      name: username,
-      username, password: "123",
-      ...options
-    })
-  )
-  const { access_token } = await succeedOrThrow(() =>
-    api.getAuthenticationAPI(username).createAccessToken("123", {
-      scope: "readwrite"
-    })
-  )
-  return { username, token: access_token }
-}
diff --git a/packages/taler-harness/src/index.ts 
b/packages/taler-harness/src/index.ts
index 5bf60d54f..9c3d8bf1f 100644
--- a/packages/taler-harness/src/index.ts
+++ b/packages/taler-harness/src/index.ts
@@ -52,7 +52,6 @@ import {
 } from "@gnu-taler/taler-wallet-core";
 import { deepStrictEqual } from "assert";
 import fs from "fs";
-import { createRandomTestUser, createTestForBankCore } from 
"http-client/bank-core.js";
 import os from "os";
 import path from "path";
 import { runBench1 } from "./bench1.js";
@@ -690,63 +689,6 @@ async function getTestSummary<T extends object>(filter: 
string | undefined, ...a
   }, { "ok": [] as TestResult[], "skiped": [] as TestResult[], "fail": [] as 
TestResult[] })
 }
 
-deploymentCli
-  .subcommand("testBankAPI", "test-bank-api", {
-    help: "test api compatibility.",
-  })
-  .requiredArgument("corebankApiBaseUrl", clk.STRING)
-  .maybeOption("adminPwd", ["--admin-password"], clk.STRING)
-  .maybeOption("filter", ["--filter"], clk.STRING)
-  .flag("showCurl", ["--show-curl"])
-  .action(async (args) => {
-    const httpLib = createPlatformHttpLib();
-    const api = new 
TalerCoreBankHttpClient(args.testBankAPI.corebankApiBaseUrl, httpLib);
-
-    process.stdout.write("config: ");
-    const config = await api.getConfig()
-    if (!api.isCompatible(config.body.version)) {
-      console.log("fail")
-      return;
-    } else {
-      console.log("ok")
-    }
-
-    if (!args.testBankAPI.adminPwd) {
-      console.log("no admin password, exit")
-      return;
-    }
-
-    const resp = await 
api.getAuthenticationAPI("admin").createAccessToken(args.testBankAPI.adminPwd, {
-      scope: "readwrite"
-    })
-
-    if (args.testBankAPI.showCurl) {
-      setPrintHttpRequestAsCurl(true)
-    }
-
-    if (resp.type === "fail") {
-      console.log("wrong admin password")
-      return;
-    }
-
-    const bankCore = createTestForBankCore(api, resp.body.access_token)
-
-    const summary = await getTestSummary(args.testBankAPI.filter, bankCore)
-
-    console.log("successful tests:", summary.ok.length)
-    console.log("uncompleted tests:", summary.skiped.length)
-    summary.skiped.forEach((testResult) => {
-      if (testResult.result === "skiped") {
-        console.log("    ", testResult.testName, testResult.caseName)
-      }
-    })
-    console.log("failed tests:", summary.fail.length)
-    summary.fail.forEach((testResult, i) => {
-      console.log(i, ")", testResult)
-    })
-  });
-
-
 deploymentCli
   .subcommand("coincfg", "gen-coin-config", {
     help: "Generate a coin/denomination configuration for the exchange.",
diff --git a/packages/taler-util/src/http-client/bank-conversion.ts 
b/packages/taler-util/src/http-client/bank-conversion.ts
index f53b6a661..2bc9fdb79 100644
--- a/packages/taler-util/src/http-client/bank-conversion.ts
+++ b/packages/taler-util/src/http-client/bank-conversion.ts
@@ -2,14 +2,18 @@ import { AmountJson, Amounts } from "../amounts.js";
 import { HttpRequestLibrary } from "../http-common.js";
 import { HttpStatusCode } from "../http-status-codes.js";
 import { createPlatformHttpLib } from "../http.js";
-import { FailCasesByMethod, ResultByMethod, opKnownFailure, opSuccess, 
opUnknownFailure } from "../operation.js";
+import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opKnownFailure, 
opSuccess, opUnknownFailure } from "../operation.js";
 import { TalerErrorCode } from "../taler-error-codes.js";
 import { codecForTalerErrorDetail } from "../wallet-types.js";
 import {
+  AccessToken,
+  TalerBankConversionApi,
+  UserAndToken,
   codecForCashinConversionResponse,
   codecForCashoutConversionResponse,
   codecForConversionBankConfig
 } from "./types.js";
+import { makeBearerTokenAuthHeader } from "./utils.js";
 
 export type TalerBankConversionResultByMethod<prop extends keyof 
TalerBankConversionHttpClient> = ResultByMethod<TalerBankConversionHttpClient, 
prop>
 export type TalerBankConversionErrorsByMethod<prop extends keyof 
TalerBankConversionHttpClient> = 
FailCasesByMethod<TalerBankConversionHttpClient, prop>
@@ -70,7 +74,7 @@ export class TalerBankConversionHttpClient {
         }
       }
       case HttpStatusCode.Conflict: return opKnownFailure("amount-too-small", 
resp);
-      case HttpStatusCode.NotImplemented: return 
opKnownFailure("cashout-not-supported", resp);
+      case HttpStatusCode.NotImplemented: return 
opKnownFailure("conversion-not-supported", resp);
       default: return opUnknownFailure(resp, await resp.text())
     }
   }
@@ -103,11 +107,31 @@ export class TalerBankConversionHttpClient {
         }
       }
       case HttpStatusCode.Conflict: return opKnownFailure("amount-too-small", 
resp);
-      case HttpStatusCode.NotImplemented: return 
opKnownFailure("cashout-not-supported", resp);
+      case HttpStatusCode.NotImplemented: return 
opKnownFailure("conversion-not-supported", resp);
       default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
+  /**
+   * 
https://docs.taler.net/core/api-bank-conversion-info.html#post--conversion-rate
+   * 
+   */
+  async updateConversionRate(auth: AccessToken, body: 
TalerBankConversionApi.ConversionRate) {
+    const url = new URL(`conversion-rate`, this.baseUrl);
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      headers: {
+        Authorization: makeBearerTokenAuthHeader(auth)
+      },
+      body
+    });
+    switch (resp.status) {
+      case HttpStatusCode.NoContent: return opEmptySuccess()
+      case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", 
resp);
+      case HttpStatusCode.NotImplemented: return 
opKnownFailure("conversion-not-supported", resp);
+      default: return opUnknownFailure(resp, await resp.text())
+    }
+  }
 
 }
 
diff --git a/packages/taler-util/src/http-client/bank-core.ts 
b/packages/taler-util/src/http-client/bank-core.ts
index 83adb6370..4557a34e4 100644
--- a/packages/taler-util/src/http-client/bank-core.ts
+++ b/packages/taler-util/src/http-client/bank-core.ts
@@ -15,8 +15,6 @@
  */
 
 import {
-  AmountJson,
-  Amounts,
   HttpStatusCode,
   LibtoolVersion,
   TalerErrorCode,
@@ -28,12 +26,12 @@ import {
 } from "@gnu-taler/taler-util/http";
 import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opFixedSuccess, 
opKnownFailure, opSuccess, opUnknownFailure } from "../operation.js";
 import { TalerAuthenticationHttpClient } from "./authentication.js";
+import { TalerBankConversionHttpClient } from "./bank-conversion.js";
 import { TalerBankIntegrationHttpClient } from "./bank-integration.js";
 import { TalerRevenueHttpClient } from "./bank-revenue.js";
 import { TalerWireGatewayHttpClient } from "./bank-wire.js";
-import { AccessToken, PaginationParams, TalerCorebankApi, UserAndToken, 
codecForAccountData, codecForBankAccountCreateWithdrawalResponse, 
codecForBankAccountGetWithdrawalResponse, codecForBankAccountTransactionInfo, 
codecForBankAccountTransactionsResponse, codecForCashinConversionResponse, 
codecForCashoutConversionResponse, codecForCashoutPending, 
codecForCashoutStatusResponse, codecForCashouts, codecForCoreBankConfig, 
codecForGlobalCashouts, codecForListBankAccountsResponse, codecForMon [...]
+import { AccessToken, PaginationParams, TalerCorebankApi, UserAndToken, 
codecForAccountData, codecForBankAccountCreateWithdrawalResponse, 
codecForBankAccountTransactionInfo, codecForBankAccountTransactionsResponse, 
codecForCashoutPending, codecForCashoutStatusResponse, codecForCashouts, 
codecForCoreBankConfig, codecForCreateTransactionResponse, 
codecForGlobalCashouts, codecForListBankAccountsResponse, 
codecForMonitorResponse, codecForPublicAccountsResponse, 
codecForRegisterAccountRespons [...]
 import { addPaginationParams, makeBearerTokenAuthHeader } from "./utils.js";
-import { TalerBankConversionHttpClient } from "./bank-conversion.js";
 
 
 export type TalerCoreBankResultByMethod<prop extends keyof 
TalerCoreBankHttpClient> = ResultByMethod<TalerCoreBankHttpClient, prop>
@@ -62,7 +60,7 @@ export class TalerCoreBankHttpClient {
   }
 
   /**
-   * https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME
+   * https://docs.taler.net/core/api-corebank.html#config
    * 
    */
   async getConfig() {
@@ -157,10 +155,16 @@ export class TalerCoreBankHttpClient {
       case HttpStatusCode.NoContent: return opEmptySuccess()
       case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
       case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", 
resp);
-      //FIXME: missing error code for cases:
-      // * change legal name
-      // * admin tries to change its own account
-      case HttpStatusCode.Forbidden: return 
opKnownFailure("cant-change-legal-name-or-admin", resp);
+      case HttpStatusCode.Conflict: {
+        const body = await resp.json()
+        const details = codecForTalerErrorDetail().decode(body)
+        switch (details.code) {
+          case TalerErrorCode.BANK_PATCH_ADMIN_EXCHANGE: return 
opKnownFailure("admin-cant-be-exchange", resp);
+          case TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME: return 
opKnownFailure("user-cant-change-name", resp);
+          case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: return 
opKnownFailure("user-cant-change-debt", resp);
+          default: return opUnknownFailure(resp, body)
+        }
+      }
       default: return opUnknownFailure(resp, await resp.text())
     }
   }
@@ -182,25 +186,31 @@ export class TalerCoreBankHttpClient {
       case HttpStatusCode.NoContent: return opEmptySuccess()
       case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
       case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", 
resp);
-      //FIXME: add code to split cases
-      // * An admin account tried to make its account an exchange
-      // * A non-admin user tried to change properties reserved for the admin
-      case HttpStatusCode.Forbidden: return 
opKnownFailure("old-password-invalid-or-not-allowed", resp);
+      case HttpStatusCode.Conflict: {
+        const body = await resp.json()
+        const details = codecForTalerErrorDetail().decode(body)
+        switch (details.code) {
+          case TalerErrorCode.BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD: 
return opKnownFailure("user-require-old-password", resp);
+          case TalerErrorCode.BANK_PATCH_BAD_OLD_PASSWORD: return 
opKnownFailure("wrong-old-password", resp);
+          default: return opUnknownFailure(resp, body)
+        }
+      }
       default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
   /**
-   * https://docs.taler.net/core/get-$BANK_API_BASE_URL-public-accounts
+   * https://docs.taler.net/core/api-corebank.html#get--public-accounts
    * 
    */
-  async getPublicAccounts(pagination?: PaginationParams) {
+  async getPublicAccounts(filter: { account?: string } = {}, pagination?: 
PaginationParams) {
     const url = new URL(`public-accounts`, this.baseUrl);
     addPaginationParams(url, pagination)
+    if (filter.account !== undefined) {
+      url.searchParams.set("filter_name", filter.account)
+    }
     const resp = await this.httpLib.fetch(url.href, {
       method: "GET",
-      headers: {
-      },
     });
     switch (resp.status) {
       case HttpStatusCode.Ok: return opSuccess(resp, 
codecForPublicAccountsResponse())
@@ -217,7 +227,7 @@ export class TalerCoreBankHttpClient {
   async getAccounts(auth: AccessToken, filter: { account?: string } = {}, 
pagination?: PaginationParams) {
     const url = new URL(`accounts`, this.baseUrl);
     addPaginationParams(url, pagination)
-    if (filter.account) {
+    if (filter.account !== undefined) {
       url.searchParams.set("filter_name", filter.account)
     }
     const resp = await this.httpLib.fetch(url.href, {
@@ -259,7 +269,7 @@ export class TalerCoreBankHttpClient {
   //
 
   /**
-   * 
https://docs.taler.net/core/api-corebank.html#get-$BANK_API_BASE_URL-accounts-$account_name-transactions
+   * 
https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME-transactions
    * 
    */
   async getTransactions(auth: UserAndToken, pagination?: PaginationParams) {
@@ -274,14 +284,14 @@ export class TalerCoreBankHttpClient {
     switch (resp.status) {
       case HttpStatusCode.Ok: return opSuccess(resp, 
codecForBankAccountTransactionsResponse())
       case HttpStatusCode.NoContent: return opFixedSuccess({ transactions: [] 
})
-      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
       case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", 
resp);
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
       default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
   /**
-   * 
https://docs.taler.net/core/api-corebank.html#get-$BANK_API_BASE_URL-accounts-$account_name-transactions-$transaction_id
+   * 
https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME-transactions-$TRANSACTION_ID
    * 
    */
   async getTransactionById(auth: UserAndToken, txid: number) {
@@ -301,10 +311,10 @@ export class TalerCoreBankHttpClient {
   }
 
   /**
-   * 
https://docs.taler.net/core/api-corebank.html#post-$BANK_API_BASE_URL-accounts-$account_name-transactions
+   * 
https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-transactions
    * 
    */
-  async createTransaction(auth: UserAndToken, body: 
TalerCorebankApi.CreateBankAccountTransactionCreate) {
+  async createTransaction(auth: UserAndToken, body: 
TalerCorebankApi.CreateTransactionRequest) {
     const url = new URL(`accounts/${auth.username}/transactions`, 
this.baseUrl);
     const resp = await this.httpLib.fetch(url.href, {
       method: "POST",
@@ -314,8 +324,7 @@ export class TalerCoreBankHttpClient {
       body,
     });
     switch (resp.status) {
-      //FIXME: expect txid as response
-      case HttpStatusCode.NoContent: return opEmptySuccess()
+      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForCreateTransactionResponse())
       case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", 
resp);
       case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", 
resp);
       case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
@@ -338,7 +347,7 @@ export class TalerCoreBankHttpClient {
   //
 
   /**
-   * 
https://docs.taler.net/core/api-corebank.html#post-$BANK_API_BASE_URL-accounts-$account_name-withdrawals
+   * 
https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-withdrawals
    * 
    */
   async createWithdrawal(auth: UserAndToken, body: 
TalerCorebankApi.BankAccountCreateWithdrawalRequest) {
@@ -360,13 +369,16 @@ export class TalerCoreBankHttpClient {
   }
 
   /**
-   * 
https://docs.taler.net/core/api-corebank.html#post-$BANK_API_BASE_URL-withdrawals-$withdrawal_id-abort
+   * 
https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-withdrawals-$WITHDRAWAL_ID-abort
    * 
    */
-  async abortWithdrawalById(wid: string) {
-    const url = new URL(`withdrawals/${wid}/abort`, this.baseUrl);
+  async abortWithdrawalById(auth: UserAndToken, wid: string) {
+    const url = new URL(`accounts/${auth.username}/withdrawals/${wid}/abort`, 
this.baseUrl);
     const resp = await this.httpLib.fetch(url.href, {
       method: "POST",
+      headers: {
+        Authorization: makeBearerTokenAuthHeader(auth.token)
+      },
     });
     switch (resp.status) {
       case HttpStatusCode.NoContent: return opEmptySuccess()
@@ -379,13 +391,16 @@ export class TalerCoreBankHttpClient {
   }
 
   /**
-   * 
https://docs.taler.net/core/api-corebank.html#post-$BANK_API_BASE_URL-withdrawals-$withdrawal_id-confirm
+   * 
https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-withdrawals-$WITHDRAWAL_ID-confirm
    * 
    */
-  async confirmWithdrawalById(wid: string) {
-    const url = new URL(`withdrawals/${wid}/confirm`, this.baseUrl);
+  async confirmWithdrawalById(auth: UserAndToken, wid: string) {
+    const url = new 
URL(`accounts/${auth.username}/withdrawals/${wid}/confirm`, this.baseUrl);
     const resp = await this.httpLib.fetch(url.href, {
       method: "POST",
+      headers: {
+        Authorization: makeBearerTokenAuthHeader(auth.token)
+      },
     });
     switch (resp.status) {
       case HttpStatusCode.NoContent: return opEmptySuccess()
@@ -396,9 +411,9 @@ export class TalerCoreBankHttpClient {
         const body = await resp.json()
         const details = codecForTalerErrorDetail().decode(body)
         switch (details.code) {
-          case TalerErrorCode.BANK_UNALLOWED_DEBIT: return 
opKnownFailure("insufficient-funds", resp);
-          case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return 
opKnownFailure("no-exchange-or-reserve-selected", resp);
           case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: return 
opKnownFailure("previously-aborted", resp);
+          case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return 
opKnownFailure("no-exchange-or-reserve-selected", resp);
+          case TalerErrorCode.BANK_UNALLOWED_DEBIT: return 
opKnownFailure("insufficient-funds", resp);
           default: return opUnknownFailure(resp, body)
         }
       }
@@ -407,7 +422,7 @@ export class TalerCoreBankHttpClient {
   }
 
   /**
-   * 
https://docs.taler.net/core/api-corebank.html#post-$BANK_API_BASE_URL-accounts-$account_name-withdrawals
+   * 
https://docs.taler.net/core/api-corebank.html#get--withdrawals-$WITHDRAWAL_ID
    * 
    */
   async getWithdrawalById(wid: string) {
@@ -416,7 +431,7 @@ export class TalerCoreBankHttpClient {
       method: "GET",
     });
     switch (resp.status) {
-      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForBankAccountGetWithdrawalResponse())
+      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForWithdrawalPublicInfo())
       //FIXME: missing in docs
       case HttpStatusCode.BadRequest: return opKnownFailure("invalid-id", resp)
       case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp)
@@ -477,7 +492,6 @@ export class TalerCoreBankHttpClient {
       case HttpStatusCode.NoContent: return opEmptySuccess()
       case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
       case HttpStatusCode.Conflict: return opKnownFailure("already-confirmed", 
resp);
-      //FIXME: should be 404 ?
       case HttpStatusCode.NotImplemented: return 
opKnownFailure("cashout-not-supported", resp);
       default: return opUnknownFailure(resp, await resp.text())
     }
diff --git a/packages/taler-util/src/http-client/bank-integration.ts 
b/packages/taler-util/src/http-client/bank-integration.ts
index b526805df..d552d3b76 100644
--- a/packages/taler-util/src/http-client/bank-integration.ts
+++ b/packages/taler-util/src/http-client/bank-integration.ts
@@ -43,13 +43,17 @@ export class TalerBankIntegrationHttpClient {
   }
 
   /**
-   * 
https://docs.taler.net/core/api-bank-integration.html#get-$BANK_API_BASE_URL-withdrawal-operation-$wopid
+   * 
https://docs.taler.net/core/api-bank-integration.html#get--withdrawal-operation-$WITHDRAWAL_ID
    * 
    */
-  async getWithdrawalOperationById(woid: string, timeoutMs?: number) {
+  async getWithdrawalOperationById(woid: string, wait?: {
+    old_state?: "pending" | "selected" | "aborted" | "confirmed", 
+    timeoutMs: number
+  }) {
     const url = new URL(`withdrawal-operation/${woid}`, this.baseUrl);
-    if (timeoutMs) {
-      url.searchParams.set("long_poll_ms", String(timeoutMs))
+    if (wait) {
+      url.searchParams.set("long_poll_ms", String(wait.timeoutMs))
+      url.searchParams.set("old_state", !wait.old_state ? "pending" : 
wait.old_state)
     }
     const resp = await this.httpLib.fetch(url.href, {
       method: "GET"
@@ -80,7 +84,7 @@ export class TalerBankIntegrationHttpClient {
         switch (details.code) {
           case 
TalerErrorCode.BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT: return 
opKnownFailure("already-selected", resp);
           case TalerErrorCode.BANK_DUPLICATE_RESERVE_PUB_SUBJECT: return 
opKnownFailure("duplicated-reserve-id", resp);
-          // case TalerErrorCode.BANK_ACCOUNT_NOT_FOUND: return 
opKnownFailure("account-not-found", resp);
+          case TalerErrorCode.BANK_UNKNOWN_ACCOUNT: return 
opKnownFailure("account-not-found", resp);
           case TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE: return 
opKnownFailure("account-not-exchange", resp);
           default: return opUnknownFailure(resp, body)
         }
diff --git a/packages/taler-util/src/http-client/bank-revenue.ts 
b/packages/taler-util/src/http-client/bank-revenue.ts
index 14d93fbe6..040ad8dd2 100644
--- a/packages/taler-util/src/http-client/bank-revenue.ts
+++ b/packages/taler-util/src/http-client/bank-revenue.ts
@@ -24,7 +24,7 @@ export class TalerRevenueHttpClient {
   }
 
   /**
-   * https://docs.taler.net/core/api-bank-revenue.html#get-$BASE_URL-history
+   * https://docs.taler.net/core/api-bank-revenue.html#get--history
    * 
    * @returns 
    */
diff --git a/packages/taler-util/src/http-client/bank-wire.ts 
b/packages/taler-util/src/http-client/bank-wire.ts
index 0d312704e..7e3c00637 100644
--- a/packages/taler-util/src/http-client/bank-wire.ts
+++ b/packages/taler-util/src/http-client/bank-wire.ts
@@ -27,12 +27,10 @@ export class TalerWireGatewayHttpClient {
   }
 
   /**
-   * https://docs.taler.net/core/api-bank-wire.html#post-$BASE_URL-transfer
+   * https://docs.taler.net/core/api-bank-wire.html#post--transfer
    * 
    */
-  async transfer(
-    auth: string,
-    body: TalerWireGatewayApi.TransferRequest,
+  async transfer(auth: string, body: TalerWireGatewayApi.TransferRequest,
   ) {
     const url = new URL(`transfer`, this.baseUrl);
     const resp = await this.httpLib.fetch(url.href, {
@@ -53,7 +51,7 @@ export class TalerWireGatewayHttpClient {
   }
 
   /**
-   * 
https://docs.taler.net/core/api-bank-wire.html#get-$BASE_URL-history-incoming
+   * https://docs.taler.net/core/api-bank-wire.html#get--history-incoming
    * 
    */
   async getHistoryIncoming(auth: string, pagination?: PaginationParams) {
@@ -67,7 +65,7 @@ export class TalerWireGatewayHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok: return opSuccess(resp, codecForIncomingHistory())
-      case HttpStatusCode.NoContent: return opFixedSuccess({ 
incoming_transactions: [], credit_account: "" })
+      case HttpStatusCode.NoContent: return opFixedSuccess({ 
incoming_transactions: [] })
       case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", 
resp);
       case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", 
resp);
       case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
@@ -75,8 +73,9 @@ export class TalerWireGatewayHttpClient {
     }
     // return readSuccessResponseJsonOrThrow(resp, codecForIncomingHistory());
   }
+
   /**
-   * 
https://docs.taler.net/core/api-bank-wire.html#get-$BASE_URL-history-outgoing
+   * https://docs.taler.net/core/api-bank-wire.html#get--history-outgoing
    * 
    */
   async getHistoryOutgoing(auth: string, pagination?: PaginationParams) {
@@ -90,15 +89,16 @@ export class TalerWireGatewayHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok: return opSuccess(resp, codecForOutgoingHistory())
-      case HttpStatusCode.NoContent: return opFixedSuccess({ 
outgoing_transactions: [], debit_account: "" })
+      case HttpStatusCode.NoContent: return opFixedSuccess({ 
outgoing_transactions: [] })
       case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", 
resp);
       case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", 
resp);
       case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
       default: return opUnknownFailure(resp, await resp.text())
     }
   }
+
   /**
-   * 
https://docs.taler.net/core/api-bank-wire.html#post-$BASE_URL-admin-add-incoming
+   * https://docs.taler.net/core/api-bank-wire.html#post--admin-add-incoming
    * 
    */
   async addIncoming(auth: string, body: 
TalerWireGatewayApi.AddIncomingRequest,) {
@@ -112,7 +112,6 @@ export class TalerWireGatewayHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok: return opSuccess(resp, 
codecForAddIncomingResponse())
-      case HttpStatusCode.NoContent: return opFixedSuccess({ 
outgoing_transactions: [], debit_account: "" })
       case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", 
resp);
       case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", 
resp);
       case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
diff --git a/packages/taler-util/src/http-client/types.ts 
b/packages/taler-util/src/http-client/types.ts
index 153cb340c..d9cc8ec90 100644
--- a/packages/taler-util/src/http-client/types.ts
+++ b/packages/taler-util/src/http-client/types.ts
@@ -264,6 +264,7 @@ export const codecForIntegrationBankConfig =
       .property("name", codecForConstString("taler-bank-integration"))
       .property("version", codecForString())
       .property("currency", codecForString())
+      .property("currency_specification", codecForCurrencySpecificiation())
       .build("TalerCorebankApi.IntegrationConfig");
 
 export const codecForCoreBankConfig = (): Codec<TalerCorebankApi.Config> =>
@@ -353,6 +354,12 @@ export const codecForChallengeContactData =
       .property("phone", codecOptional(codecForString()))
       .build("TalerCorebankApi.ChallengeContactData");
 
+export const codecForWithdrawalPublicInfo =
+  (): Codec<TalerCorebankApi.WithdrawalPublicInfo> =>
+    buildCodecForObject<TalerCorebankApi.WithdrawalPublicInfo>()
+      .property("username", codecForString(),)
+      .build("TalerCorebankApi.WithdrawalPublicInfo");
+
 export const codecForBankAccountTransactionsResponse =
   (): Codec<TalerCorebankApi.BankAccountTransactionsResponse> =>
     buildCodecForObject<TalerCorebankApi.BankAccountTransactionsResponse>()
@@ -381,6 +388,12 @@ export const codecForBankAccountTransactionInfo =
       .build("TalerCorebankApi.BankAccountTransactionInfo");
 
 
+export const codecForCreateTransactionResponse =
+  (): Codec<TalerCorebankApi.CreateTransactionResponse> =>
+    buildCodecForObject<TalerCorebankApi.CreateTransactionResponse>()
+      .property("row_id", codecForNumber())
+      .build("TalerCorebankApi.CreateTransactionResponse");
+
 export const codecForRegisterAccountResponse =
   (): Codec<TalerCorebankApi.RegisterAccountResponse> =>
     buildCodecForObject<TalerCorebankApi.RegisterAccountResponse>()
@@ -395,19 +408,19 @@ export const codecForBankAccountCreateWithdrawalResponse =
       .property("withdrawal_id", codecForString())
       .build("TalerCorebankApi.BankAccountCreateWithdrawalResponse");
 
-export const codecForBankAccountGetWithdrawalResponse =
-  (): Codec<TalerCorebankApi.BankAccountGetWithdrawalResponse> =>
-    buildCodecForObject<TalerCorebankApi.BankAccountGetWithdrawalResponse>()
-      .property("amount", codecForAmountString())
-      .property("aborted", codecForBoolean())
-      .property("confirmation_done", codecForBoolean())
-      .property(
-        "selected_exchange_account",
-        codecOptional(codecForPaytoString()),
-      )
-      .property("selected_reserve_pub", codecOptional(codecForString()))
-      .property("selection_done", codecForBoolean())
-      .build("TalerCorebankApi.BankAccountGetWithdrawalResponse");
+// export const codecForBankAccountGetWithdrawalResponse =
+//   (): Codec<TalerCorebankApi.BankAccountGetWithdrawalResponse> =>
+//     buildCodecForObject<TalerCorebankApi.BankAccountGetWithdrawalResponse>()
+//       .property("amount", codecForAmountString())
+//       .property("aborted", codecForBoolean())
+//       .property("confirmation_done", codecForBoolean())
+//       .property(
+//         "selected_exchange_account",
+//         codecOptional(codecForPaytoString()),
+//       )
+//       .property("selected_reserve_pub", codecOptional(codecForString()))
+//       .property("selection_done", codecForBoolean())
+//       .build("TalerCorebankApi.BankAccountGetWithdrawalResponse");
 
 export const codecForCashoutPending =
   (): Codec<TalerCorebankApi.CashoutPending> =>
@@ -484,7 +497,15 @@ export const codecForCashoutStatusResponse =
           codecForConstString("confirmed"),
         ),
       )
+      .property(
+        "tan_channel",
+        codecForEither(
+          codecForConstString(TanChannel.SMS),
+          codecForConstString(TanChannel.EMAIL),
+        ),
+      )
       .property("subject", codecForString())
+      .property("tan_info", codecForString())
       .build("TalerCorebankApi.CashoutStatusResponse");
 
 export const codecForConversionRatesResponse =
@@ -542,21 +563,30 @@ export const codecForBankVersion =
 export const codecForBankWithdrawalOperationStatus =
   (): Codec<TalerBankIntegrationApi.BankWithdrawalOperationStatus> =>
     
buildCodecForObject<TalerBankIntegrationApi.BankWithdrawalOperationStatus>()
-      .property("aborted", codecForBoolean())
-      .property("selection_done", codecForBoolean())
-      .property("transfer_done", codecForBoolean())
+      .property("status", codecForEither(
+        codecForConstString("pending"),
+        codecForConstString("selected"),
+        codecForConstString("aborted"),
+        codecForConstString("confirmed")
+      ))
       .property("amount", codecForAmountString())
       .property("sender_wire", codecOptional(codecForPaytoString()))
       .property("suggested_exchange", codecOptional(codecForString()))
       .property("confirm_transfer_url", codecOptional(codecForURL()))
       .property("wire_types", codecForList(codecForString()))
+      .property("selected_reserve_pub", codecOptional(codecForString()))
+      .property("selected_exchange_account", codecOptional(codecForString()))
       .build("TalerBankIntegrationApi.BankWithdrawalOperationStatus");
 
 export const codecForBankWithdrawalOperationPostResponse =
   (): Codec<TalerBankIntegrationApi.BankWithdrawalOperationPostResponse> =>
     
buildCodecForObject<TalerBankIntegrationApi.BankWithdrawalOperationPostResponse>()
+      .property("status", codecForEither(
+        codecForConstString("selected"),
+        codecForConstString("aborted"),
+        codecForConstString("confirmed")
+      ))
       .property("confirm_transfer_url", codecOptional(codecForURL()))
-      .property("transfer_done", codecForBoolean())
       .build("TalerBankIntegrationApi.BankWithdrawalOperationPostResponse");
 
 export const codecForMerchantIncomingHistory =
@@ -890,7 +920,9 @@ export namespace TalerWireGatewayApi {
     // This must be one of the exchange's bank accounts.
     // Credit account is shared by all incoming transactions
     // as per the nature of the request.
-    credit_account: PaytoString;
+
+    // undefined if incoming transaction is empty
+    credit_account?: PaytoString;
   }
 
   // Union discriminated by the "type" field.
@@ -951,7 +983,9 @@ export namespace TalerWireGatewayApi {
     // This must be one of the exchange's bank accounts.
     // Credit account is shared by all incoming transactions
     // as per the nature of the request.
-    debit_account: PaytoString;
+
+    // undefined if outgoing transactions is empty
+    debit_account?: PaytoString;
   }
 
   export interface OutgoingBankTransaction {
@@ -1113,6 +1147,40 @@ export namespace TalerBankConversionApi {
     // bank account, according to 'amount_debit'.
     amount_credit: AmountString;
   }
+
+  export type RoundingMode = "zero" | "up" | "nearest";
+
+  export interface ConversionRate {
+    // Exchange rate to buy regional currency from fiat
+    cashin_ratio: DecimalNumber;
+
+    // Fee to subtract after applying the cashin ratio.
+    cashin_fee: AmountString;
+
+    // Minimum amount authorised for cashin, in fiat before conversion
+    cashin_min_amount: AmountString;
+
+    // Smallest possible regional amount, converted amount is rounded to this 
amount
+    cashin_tiny_amount: AmountString;
+
+    // Rounding mode used during cashin conversion
+    cashin_rounding_mode: RoundingMode;
+
+    // Exchange rate to sell regional currency for fiat
+    cashout_ratio: DecimalNumber;
+
+    // Fee to subtract after applying the cashout ratio.
+    cashout_fee: AmountString;
+
+    // Minimum amount authorised for cashout, in regional before conversion
+    cashout_min_amount: AmountString;
+
+    // Smallest possible fiat amount, converted amount is rounded to this 
amount
+    cashout_tiny_amount: AmountString;
+
+    // Rounding mode used during cashout conversion
+    cashout_rounding_mode: RoundingMode;
+  }
 }
 export namespace TalerBankIntegrationApi {
   export interface BankVersion {
@@ -1131,18 +1199,15 @@ export namespace TalerBankIntegrationApi {
     name: "taler-bank-integration";
   }
 
+  export type WithdrawalOperationStatus = "pending" | "selected" | "aborted" | 
"confirmed"
   export interface BankWithdrawalOperationStatus {
-    // Indicates whether the withdrawal was aborted.
-    aborted: boolean;
-
-    // Has the wallet selected parameters for the withdrawal operation
-    // (exchange and reserve public key) and successfully sent it
-    // to the bank?
-    selection_done: boolean;
+    // Current status of the operation
+    // pending: the operation is pending parameters selection (exchange and 
reserve public key)
+    // selected: the operations has been selected and is pending confirmation
+    // aborted: the operation has been aborted
+    // confirmed: the transfer has been confirmed and registered by the bank
+    status: WithdrawalOperationStatus;
 
-    // The transfer has been confirmed and registered by the bank.
-    // Does not guarantee that the funds have arrived at the exchange already.
-    transfer_done: boolean;
 
     // Amount that will be withdrawn with this operation
     // (raw amount without fee considerations).
@@ -1162,6 +1227,14 @@ export namespace TalerBankIntegrationApi {
 
     // Wire transfer types supported by the bank.
     wire_types: string[];
+
+    // Reserve public key selected by the exchange,
+    // only non-null if status is selected or confirmed.
+    selected_reserve_pub?: string;
+
+    // Exchange account selected by the wallet
+    // only non-null if status is selected or confirmed.
+    selected_exchange_account?: string;
   }
 
   export interface BankWithdrawalOperationPostRequest {
@@ -1173,14 +1246,17 @@ export namespace TalerBankIntegrationApi {
   }
 
   export interface BankWithdrawalOperationPostResponse {
-    // The transfer has been confirmed and registered by the bank.
-    // Does not guarantee that the funds have arrived at the exchange already.
-    transfer_done: boolean;
+    // Current status of the operation
+    // pending: the operation is pending parameters selection (exchange and 
reserve public key)
+    // selected: the operations has been selected and is pending confirmation
+    // aborted: the operation has been aborted
+    // confirmed: the transfer has been confirmed and registered by the bank
+    status: Omit<"pending", WithdrawalOperationStatus>;
 
     // URL that the user needs to navigate to in order to
     // complete some final confirmation (e.g. 2FA).
     //
-    // Only applicable when transfer_done is false.
+    // Only applicable when status is selected.
     // It may contain withdrawal operation id
     confirm_transfer_url?: string;
   }
@@ -1192,7 +1268,10 @@ export namespace TalerCorebankApi {
     // The format is "current:revision:age".
     version: string;
 
-    currency: String;
+    currency: string;
+
+    // How the bank SPA should render this currency.
+    currency_specification: CurrencySpecification;
 
     // Name of the API.
     name: "taler-bank-integration";
@@ -1235,31 +1314,12 @@ export namespace TalerCorebankApi {
     // URI that can be passed to the wallet to initiate the withdrawal.
     taler_withdraw_uri: TalerActionString;
   }
-  export interface BankAccountGetWithdrawalResponse {
-    // Amount that will be withdrawn with this withdrawal operation.
-    amount: AmountString;
-
-    // Was the withdrawal aborted?
-    aborted: boolean;
-
-    // Has the withdrawal been confirmed by the bank?
-    // The wire transfer for a withdrawal is only executed once
-    // both confirmation_done is true and selection_done is true.
-    confirmation_done: boolean;
-
-    // Did the wallet select reserve details?
-    selection_done: boolean;
-
-    // Reserve public key selected by the exchange,
-    // only non-null if selection_done is true.
-    selected_reserve_pub: string | undefined;
-
-    // Exchange account selected by the wallet, or by the bank
-    // (with the default exchange) in case the wallet did not provide one
-    // through the Integration API.
-    selected_exchange_account: PaytoString | undefined;
+  export interface WithdrawalPublicInfo {
+    // Account username
+    username: string;
   }
 
+
   export interface BankAccountTransactionsResponse {
     transactions: BankAccountTransactionInfo[];
   }
@@ -1279,7 +1339,7 @@ export namespace TalerCorebankApi {
     date: Timestamp;
   }
 
-  export interface CreateBankAccountTransactionCreate {
+  export interface CreateTransactionRequest {
     // Address in the Payto format of the wire transfer receiver.
     // It needs at least the 'message' query string parameter.
     payto_uri: PaytoString;
@@ -1291,6 +1351,11 @@ export namespace TalerCorebankApi {
     amount?: AmountString;
   }
 
+  export interface CreateTransactionResponse {
+    // ID identifying the transaction being created
+    row_id: Integer;
+  }
+
   export interface RegisterAccountResponse {
     // Internal payto URI of this bank account.
     internal_payto_uri: PaytoString;
@@ -1324,17 +1389,17 @@ export namespace TalerCorebankApi {
     // as well.
     challenge_contact_data?: ChallengeContactData;
 
-    // 'payto' address pointing a bank account
-    // external to the libeufin-bank.
+    // 'payto' address of a fiat bank account.
     // Payments will be sent to this bank account
-    // when the user wants to convert the local currency
-    // back to fiat currency outside libeufin-bank.
+    // when the user wants to convert the regional currency
+    // back to fiat currency outside bank.
     cashout_payto_uri?: PaytoString;
 
     // Internal payto URI of this bank account.
     // Used mostly for testing.
     internal_payto_uri?: PaytoString;
   }
+
   export interface ChallengeContactData {
     // E-Mail address
     email?: EmailAddress;
@@ -1525,6 +1590,14 @@ export namespace TalerCorebankApi {
     // Time when the cashout was confirmed via its TAN.
     // Missing when the operation wasn't confirmed yet.
     confirmation_time?: Timestamp;
+
+    // Channel of the last successful transmission of the TAN challenge.
+    // Missing when all transmissions failed.
+    tan_channel?: TanChannel;
+
+    // Info of the last successful transmission of the TAN challenge.
+    // Missing when all transmissions failed.
+    tan_info?: string;
   }
 
   export interface ConversionRatesResponse {

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