gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 01/02: wip #8655: updating paginated queries


From: gnunet
Subject: [taler-wallet-core] 01/02: wip #8655: updating paginated queries
Date: Wed, 03 Apr 2024 19:57:11 +0200

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

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

commit 56da180423029a1b53d2be343eed4f073e96dc89
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Wed Apr 3 09:52:53 2024 -0300

    wip #8655: updating paginated queries
---
 .../bank-ui/src/components/Transactions/state.ts   |  22 +-
 packages/bank-ui/src/hooks/account.ts              |  77 ++---
 packages/bank-ui/src/hooks/regional.ts             |  29 +-
 packages/bank-ui/src/pages/PublicHistoriesPage.tsx |  10 +-
 packages/bank-ui/src/pages/admin/AccountList.tsx   |   8 +-
 packages/bank-ui/src/pages/admin/AdminHome.tsx     |   5 +-
 packages/merchant-backoffice-ui/src/Routing.tsx    |   2 +-
 .../merchant-backoffice-ui/src/hooks/backend.ts    | 373 ---------------------
 packages/merchant-backoffice-ui/src/hooks/bank.ts  | 247 ++++----------
 .../merchant-backoffice-ui/src/hooks/instance.ts   |  60 +---
 packages/merchant-backoffice-ui/src/hooks/order.ts | 307 ++++-------------
 packages/merchant-backoffice-ui/src/hooks/otp.ts   | 231 +++----------
 .../merchant-backoffice-ui/src/hooks/product.ts    | 227 +++++--------
 .../merchant-backoffice-ui/src/hooks/templates.ts  | 286 +++-------------
 .../merchant-backoffice-ui/src/hooks/transfer.ts   | 192 +++--------
 .../merchant-backoffice-ui/src/hooks/webhooks.ts   | 233 +++++--------
 .../src/paths/instance/accounts/create/index.tsx   |   2 +-
 .../src/paths/instance/accounts/list/index.tsx     |  26 +-
 .../src/paths/instance/products/list/index.tsx     |   2 +-
 packages/taler-util/src/http-client/merchant.ts    |  34 +-
 20 files changed, 537 insertions(+), 1836 deletions(-)

diff --git a/packages/bank-ui/src/components/Transactions/state.ts 
b/packages/bank-ui/src/components/Transactions/state.ts
index 4e4552a82..ce6338e57 100644
--- a/packages/bank-ui/src/components/Transactions/state.ts
+++ b/packages/bank-ui/src/components/Transactions/state.ts
@@ -17,7 +17,9 @@
 import {
   AbsoluteTime,
   Amounts,
+  HttpStatusCode,
   TalerError,
+  assertUnreachable,
   parsePaytoUri,
 } from "@gnu-taler/taler-util";
 import { useTransactions } from "../../hooks/account.js";
@@ -27,21 +29,27 @@ export function useComponentState({
   account,
   routeCreateWireTransfer,
 }: Props): State {
-  const txResult = useTransactions(account);
-  if (!txResult) {
+  const result = useTransactions(account);
+  if (!result) {
     return {
       status: "loading",
       error: undefined,
     };
   }
-  if (txResult instanceof TalerError) {
+  if (result instanceof TalerError) {
     return {
       status: "loading-error",
-      error: txResult,
+      error: result,
+    };
+  }
+  if (result.type === "fail") {
+    return {
+      status: "loading",
+      error: undefined,
     };
   }
 
-  const transactions = txResult.result
+  const transactions = result.body
     .map((tx) => {
       const negative = tx.direction === "debit";
       const cp = parsePaytoUri(
@@ -76,7 +84,7 @@ export function useComponentState({
     error: undefined,
     routeCreateWireTransfer,
     transactions,
-    onGoNext: txResult.isLastPage ? undefined : txResult.loadNext,
-    onGoStart: txResult.isFirstPage ? undefined : txResult.loadFirst,
+    onGoNext: result.isLastPage ? undefined : result.loadNext,
+    onGoStart: result.isFirstPage ? undefined : result.loadFirst,
   };
 }
diff --git a/packages/bank-ui/src/hooks/account.ts 
b/packages/bank-ui/src/hooks/account.ts
index 24309183f..543c49aed 100644
--- a/packages/bank-ui/src/hooks/account.ts
+++ b/packages/bank-ui/src/hooks/account.ts
@@ -16,6 +16,7 @@
 
 import {
   AccessToken,
+  OperationOk,
   TalerCoreBankResultByMethod,
   TalerHttpError,
   WithdrawalOperationStatus,
@@ -197,36 +198,44 @@ export function usePublicAccounts(
     keepPreviousData: true,
   });
 
-  const isLastPage =
-    data && data.type === "ok" && data.body.public_accounts.length <= 
PAGE_SIZE;
-  const isFirstPage = !offset;
+  if (error) return error;
+  if (data === undefined) return undefined;
+  // if (data.type !== "ok") return data;
+
+  //TODO: row_id should not be optional
+  return buildPaginatedResult(data.body.public_accounts, offset, setOffset, 
(d) => d.row_id ?? 0)
+}
+
 
-  const result =
-    data && data.type == "ok" ? structuredClone(data.body.public_accounts) : 
[];
+type PaginatedResult<T> = OperationOk<T> & {
+  isLastPage: boolean;
+  isFirstPage: boolean;
+  loadNext(): void;
+  loadFirst(): void;
+}
+//TODO: consider sending this to web-util
+export function buildPaginatedResult<DataType, OffsetId>(data: DataType[], 
offset: OffsetId | undefined, setOffset: (o: OffsetId | undefined) => void, 
getId: (r: DataType) => OffsetId): PaginatedResult<DataType[]> {
+  const isLastPage = data.length <= PAGE_SIZE;
+  const isFirstPage = offset === undefined;
+
+  const result = structuredClone(data);
   if (result.length == PAGE_SIZE + 1) {
     result.pop();
   }
-  const pagination = {
-    result,
+  return {
+    type: "ok",
+    body: result,
     isLastPage,
     isFirstPage,
     loadNext: () => {
       if (!result.length) return;
-      setOffset(result[result.length - 1].row_id);
+      const id = getId(result[result.length - 1])
+      setOffset(id);
     },
     loadFirst: () => {
-      setOffset(0);
+      setOffset(undefined);
     },
   };
-
-  // const public_accountslist = data?.type !== "ok" ? [] : 
data.body.public_accounts;
-  if (data) {
-    return { ok: true, data: data.body, ...pagination };
-  }
-  if (error) {
-    return error;
-  }
-  return undefined;
 }
 
 export function revalidateTransactions() {
@@ -271,34 +280,10 @@ export function useTransactions(account: string, 
initial?: number) {
     revalidateOnFocus: false,
     revalidateOnReconnect: false,
   });
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
 
-  const isLastPage =
-    data && data.type === "ok" && data.body.transactions.length <= PAGE_SIZE;
-  const isFirstPage = !offset;
-
-  const result =
-    data && data.type == "ok" ? structuredClone(data.body.transactions) : [];
-  if (result.length == PAGE_SIZE + 1) {
-    result.pop();
-  }
-  const pagination = {
-    result,
-    isLastPage,
-    isFirstPage,
-    loadNext: () => {
-      if (!result.length) return;
-      setOffset(result[result.length - 1].row_id);
-    },
-    loadFirst: () => {
-      setOffset(0);
-    },
-  };
+  return buildPaginatedResult(data.body.transactions, offset, setOffset, (d) 
=> d.row_id)
 
-  if (data) {
-    return { ok: true, data, ...pagination };
-  }
-  if (error) {
-    return error;
-  }
-  return undefined;
 }
diff --git a/packages/bank-ui/src/hooks/regional.ts 
b/packages/bank-ui/src/hooks/regional.ts
index 274638f74..3e832a944 100644
--- a/packages/bank-ui/src/hooks/regional.ts
+++ b/packages/bank-ui/src/hooks/regional.ts
@@ -34,6 +34,7 @@ import {
 import { useState } from "preact/hooks";
 import _useSWR, { SWRHook, mutate } from "swr";
 import { useBankCoreApiContext } from "@gnu-taler/web-util/browser";
+import { buildPaginatedResult } from "./account.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
 const useSWR = _useSWR as unknown as SWRHook;
@@ -249,31 +250,13 @@ export function useBusinessAccounts() {
     keepPreviousData: true,
   });
 
-  const isLastPage =
-    data && data.type === "ok" && data.body.accounts.length <= PAGE_SIZE;
-  const isFirstPage = !offset;
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
 
-  const result =
-    data && data.type == "ok" ? structuredClone(data.body.accounts) : [];
-  if (result.length == PAGE_SIZE + 1) {
-    result.pop();
-  }
-  const pagination = {
-    result,
-    isLastPage,
-    isFirstPage,
-    loadNext: () => {
-      if (!result.length) return;
-      setOffset(result[result.length - 1].row_id);
-    },
-    loadFirst: () => {
-      setOffset(0);
-    },
-  };
+  //TODO: row_id should not be optional
+  return buildPaginatedResult(data.body.accounts, offset, setOffset, (d) => 
d.row_id ?? 0)
 
-  if (data) return { ok: true, data, ...pagination };
-  if (error) return error;
-  return undefined;
 }
 
 type CashoutWithId = TalerCorebankApi.CashoutStatusResponse & { id: number };
diff --git a/packages/bank-ui/src/pages/PublicHistoriesPage.tsx 
b/packages/bank-ui/src/pages/PublicHistoriesPage.tsx
index 554da0c3f..1810bd5dd 100644
--- a/packages/bank-ui/src/pages/PublicHistoriesPage.tsx
+++ b/packages/bank-ui/src/pages/PublicHistoriesPage.tsx
@@ -31,9 +31,9 @@ export function PublicHistoriesPage(): VNode {
   const result = usePublicAccounts(undefined);
   const firstAccount =
     result &&
-    !(result instanceof TalerError) &&
-    result.data.public_accounts.length > 0
-      ? result.data.public_accounts[0].username
+      !(result instanceof TalerError) &&
+      result.body.length > 0
+      ? result.body[0].username
       : undefined;
 
   const [showAccount, setShowAccount] = useState(firstAccount);
@@ -45,13 +45,13 @@ export function PublicHistoriesPage(): VNode {
     return <Loading />;
   }
 
-  const { data } = result;
+  const { body: accountList } = result;
 
   const txs: Record<string, h.JSX.Element> = {};
   const accountsBar = [];
 
   // Ask story of all the public accounts.
-  for (const account of data.public_accounts) {
+  for (const account of accountList) {
     const isSelected = account.username == showAccount;
     accountsBar.push(
       <li
diff --git a/packages/bank-ui/src/pages/admin/AccountList.tsx 
b/packages/bank-ui/src/pages/admin/AccountList.tsx
index c4e529f9f..6402c2bcd 100644
--- a/packages/bank-ui/src/pages/admin/AccountList.tsx
+++ b/packages/bank-ui/src/pages/admin/AccountList.tsx
@@ -51,19 +51,19 @@ export function AccountList({
   if (result instanceof TalerError) {
     return <ErrorLoadingWithDebug error={result} />;
   }
-  if (result.data.type === "fail") {
-    switch (result.data.case) {
+  if (result.type === "fail") {
+    switch (result.case) {
       case HttpStatusCode.Unauthorized:
         return <Fragment />;
       default:
-        assertUnreachable(result.data.case);
+        assertUnreachable(result.case);
     }
   }
 
   const onGoStart = result.isFirstPage ? undefined : result.loadFirst;
   const onGoNext = result.isLastPage ? undefined : result.loadNext;
 
-  const accounts = result.result;
+  const accounts = result.body;
   return (
     <Fragment>
       <div class="px-4 sm:px-6 lg:px-8 mt-8">
diff --git a/packages/bank-ui/src/pages/admin/AdminHome.tsx 
b/packages/bank-ui/src/pages/admin/AdminHome.tsx
index 94b88dc89..4784fc73a 100644
--- a/packages/bank-ui/src/pages/admin/AdminHome.tsx
+++ b/packages/bank-ui/src/pages/admin/AdminHome.tsx
@@ -26,6 +26,7 @@ import { Attention, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import {
   format,
   getDate,
+  getDaysInMonth,
   getHours,
   getMonth,
   getYear,
@@ -127,8 +128,8 @@ export function getTimeframesForDate(
       };
     case TalerCorebankApi.MonitorTimeframeParam.day:
       return {
-        current: getDate(sub(time, { days: 1 })),
-        previous: getDate(sub(time, { days: 2 })),
+        current: getDaysInMonth(sub(time, { days: 1 })),
+        previous: getDaysInMonth(sub(time, { days: 2 })),
       };
     case TalerCorebankApi.MonitorTimeframeParam.month:
       return {
diff --git a/packages/merchant-backoffice-ui/src/Routing.tsx 
b/packages/merchant-backoffice-ui/src/Routing.tsx
index 1fccc2637..9e801a233 100644
--- a/packages/merchant-backoffice-ui/src/Routing.tsx
+++ b/packages/merchant-backoffice-ui/src/Routing.tsx
@@ -158,7 +158,7 @@ export function Routing(_p: Props): VNode {
   const now = AbsoluteTime.now();
 
   const instance = useInstanceBankAccounts();
-  const accounts = !instance.ok ? undefined : instance.data.accounts;
+  const accounts = !instance || instance instanceof TalerError || 
instance.data.type === "fail" ? undefined : instance.result;
   const shouldWarnAboutMissingBankAccounts =
     !state.isAdmin && accounts !== undefined && accounts.length < 1 &&
     (AbsoluteTime.isNever(preference.hideMissingAccountUntil) ||
diff --git a/packages/merchant-backoffice-ui/src/hooks/backend.ts 
b/packages/merchant-backoffice-ui/src/hooks/backend.ts
deleted file mode 100644
index 8c54f70db..000000000
--- a/packages/merchant-backoffice-ui/src/hooks/backend.ts
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-import {
-  TalerErrorDetail,
-  TalerMerchantApi
-} from "@gnu-taler/taler-util";
-import {
-  HttpResponse,
-  HttpResponseOk,
-  RequestError,
-  RequestOptions,
-  useApiContext
-} from "@gnu-taler/web-util/browser";
-import { useCallback, useEffect, useState } from "preact/hooks";
-import { useSWRConfig } from "swr";
-import { useSessionContext } from "../context/session.js";
-
-export function useMatchMutate(): (
-  re?: RegExp,
-  value?: unknown,
-) => Promise<any> {
-  const { cache, mutate } = useSWRConfig();
-
-  if (!(cache instanceof Map)) {
-    throw new Error(
-      "matchMutate requires the cache provider to be a Map instance",
-    );
-  }
-
-  return function matchRegexMutate(re?: RegExp) {
-    return mutate(
-      (key) => {
-        // evict if no key or regex === all
-        if (!key || !re) return true;
-        // match string
-        if (typeof key === "string" && re.test(key)) return true;
-        // record or object have the path at [0]
-        if (typeof key === "object" && re.test(key[0])) return true;
-        //key didn't match regex
-        return false;
-      },
-      undefined,
-      {
-        revalidate: true,
-      },
-    );
-  };
-}
-
-export function useBackendInstancesTestForAdmin(): HttpResponse<
-  TalerMerchantApi.InstancesResponse,
-  TalerErrorDetail
-> {
-  const { request } = useBackendBaseRequest();
-
-  type Type = TalerMerchantApi.InstancesResponse;
-
-  const [result, setResult] = useState<
-    HttpResponse<Type, TalerErrorDetail>
-  >({ loading: true });
-
-  useEffect(() => {
-    request<Type>(`/management/instances`)
-      .then((data) => setResult(data))
-      .catch((error: RequestError<TalerErrorDetail>) =>
-        setResult(error.cause),
-      );
-  }, [request]);
-
-  return result;
-}
-
-const CHECK_CONFIG_INTERVAL_OK = 5 * 60 * 1000;
-const CHECK_CONFIG_INTERVAL_FAIL = 2 * 1000;
-
-export function useBackendConfig(): HttpResponse<
-  TalerMerchantApi.VersionResponse | undefined,
-  RequestError<TalerErrorDetail>
-> {
-  const { request } = useBackendBaseRequest();
-
-  type Type = TalerMerchantApi.VersionResponse;
-  type State = {
-    data: HttpResponse<Type, RequestError<TalerErrorDetail>>;
-    timer: number;
-  };
-  const [result, setResult] = useState<State>({
-    data: { loading: true },
-    timer: 0,
-  });
-
-  useEffect(() => {
-    if (result.timer) {
-      clearTimeout(result.timer);
-    }
-    function tryConfig(): void {
-      request<Type>(`/config`)
-        .then((data) => {
-          const timer: any = setTimeout(() => {
-            tryConfig();
-          }, CHECK_CONFIG_INTERVAL_OK);
-          setResult({ data, timer });
-        })
-        .catch((error) => {
-          const timer: any = setTimeout(() => {
-            tryConfig();
-          }, CHECK_CONFIG_INTERVAL_FAIL);
-          const data = error.cause;
-          setResult({ data, timer });
-        });
-    }
-    tryConfig();
-  }, [request]);
-
-  return result.data;
-}
-
-interface useBackendInstanceRequestType {
-  request: <T>(
-    endpoint: string,
-    options?: RequestOptions,
-  ) => Promise<HttpResponseOk<T>>;
-  fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
-  multiFetcher: <T>(params: [url: string[]]) => Promise<HttpResponseOk<T>[]>;
-  orderFetcher: <T>(
-    params: [
-      endpoint: string,
-      paid?: YesOrNo,
-      refunded?: YesOrNo,
-      wired?: YesOrNo,
-      searchDate?: Date,
-      delta?: number,
-    ],
-  ) => Promise<HttpResponseOk<T>>;
-  transferFetcher: <T>(
-    params: [
-      endpoint: string,
-      payto_uri?: string,
-      verified?: string,
-      position?: string,
-      delta?: number,
-    ],
-  ) => Promise<HttpResponseOk<T>>;
-  templateFetcher: <T>(
-    params: [endpoint: string, position?: string, delta?: number],
-  ) => Promise<HttpResponseOk<T>>;
-  webhookFetcher: <T>(
-    params: [endpoint: string, position?: string, delta?: number],
-  ) => Promise<HttpResponseOk<T>>;
-}
-interface useBackendBaseRequestType {
-  request: <T>(
-    endpoint: string,
-    options?: RequestOptions,
-  ) => Promise<HttpResponseOk<T>>;
-}
-
-type YesOrNo = "yes" | "no";
-
-/**
- *
- * @param root the request is intended to the base URL and no the instance URL
- * @returns request handler to
- */
-export function useBackendBaseRequest(): useBackendBaseRequestType {
-  const { request: requestHandler } = useApiContext();
-  const { state } = useSessionContext();
-  const token = state.token;
-  const baseUrl = state.backendUrl;
-
-  const request = useCallback(
-    function requestImpl<T>(
-      endpoint: string,
-      options: RequestOptions = {},
-    ): Promise<HttpResponseOk<T>> {
-      return requestHandler<T>(baseUrl, endpoint, { ...options, token })
-        .then((res) => {
-          return res;
-        })
-        .catch((err) => {
-          throw err;
-        });
-    },
-    [baseUrl, token],
-  );
-
-  return { request };
-}
-
-export function useBackendInstanceRequest(): useBackendInstanceRequestType {
-  const { request: requestHandler } = useApiContext();
-
-  const { state } = useSessionContext();
-  const token = state.token;
-  const baseUrl = state.backendUrl;
-
-  const request = useCallback(
-    function requestImpl<T>(
-      endpoint: string,
-      options: RequestOptions = {},
-    ): Promise<HttpResponseOk<T>> {
-      return requestHandler<T>(baseUrl, endpoint, { token, ...options });
-    },
-    [baseUrl, token],
-  );
-
-  const multiFetcher = useCallback(
-    function multiFetcherImpl<T>(
-      args: [endpoints: string[]],
-    ): Promise<HttpResponseOk<T>[]> {
-      const [endpoints] = args;
-      return Promise.all(
-        endpoints.map((endpoint) =>
-          requestHandler<T>(baseUrl, endpoint, { token }),
-        ),
-      );
-    },
-    [baseUrl, token],
-  );
-
-  const fetcher = useCallback(
-    function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> {
-      return requestHandler<T>(baseUrl, endpoint, { token });
-    },
-    [baseUrl, token],
-  );
-
-  const orderFetcher = useCallback(
-    function orderFetcherImpl<T>(
-      args: [
-        endpoint: string,
-        paid?: YesOrNo,
-        refunded?: YesOrNo,
-        wired?: YesOrNo,
-        searchDate?: Date,
-        delta?: number,
-      ],
-    ): Promise<HttpResponseOk<T>> {
-      const [endpoint, paid, refunded, wired, searchDate, delta] = args;
-      const date_s =
-        delta && delta < 0 && searchDate
-          ? Math.floor(searchDate.getTime() / 1000) + 1
-          : searchDate !== undefined
-            ? Math.floor(searchDate.getTime() / 1000)
-            : undefined;
-      const params: any = {};
-      if (paid !== undefined) params.paid = paid;
-      if (delta !== undefined) params.delta = delta;
-      if (refunded !== undefined) params.refunded = refunded;
-      if (wired !== undefined) params.wired = wired;
-      if (date_s !== undefined) params.date_s = date_s;
-      if (delta === 0) {
-        //in this case we can already assume the response
-        //and avoid network
-        return Promise.resolve({
-          ok: true,
-          data: { orders: [] } as T,
-        });
-      }
-      return requestHandler<T>(baseUrl, endpoint, { params, token });
-    },
-    [baseUrl, token],
-  );
-
-  const transferFetcher = useCallback(
-    function transferFetcherImpl<T>(
-      args: [
-        endpoint: string,
-        payto_uri?: string,
-        verified?: string,
-        position?: string,
-        delta?: number,
-      ],
-    ): Promise<HttpResponseOk<T>> {
-      const [endpoint, payto_uri, verified, position, delta] = args;
-      const params: any = {};
-      if (payto_uri !== undefined) params.payto_uri = payto_uri;
-      if (verified !== undefined) params.verified = verified;
-      if (delta === 0) {
-        //in this case we can already assume the response
-        //and avoid network
-        return Promise.resolve({
-          ok: true,
-          data: { transfers: [] } as T,
-        });
-      }
-      if (delta !== undefined) {
-        params.limit = delta;
-      }
-      if (position !== undefined) params.offset = position;
-
-      return requestHandler<T>(baseUrl, endpoint, { params, token });
-    },
-    [baseUrl, token],
-  );
-
-  const templateFetcher = useCallback(
-    function templateFetcherImpl<T>(
-      args: [endpoint: string, position?: string, delta?: number],
-    ): Promise<HttpResponseOk<T>> {
-      const [endpoint, position, delta] = args;
-      const params: any = {};
-      if (delta === 0) {
-        //in this case we can already assume the response
-        //and avoid network
-        return Promise.resolve({
-          ok: true,
-          data: { templates: [] } as T,
-        });
-      }
-      if (delta !== undefined) {
-        params.limit = delta;
-      }
-      if (position !== undefined) params.offset = position;
-
-      return requestHandler<T>(baseUrl, endpoint, { params, token });
-    },
-    [baseUrl, token],
-  );
-
-  const webhookFetcher = useCallback(
-    function webhookFetcherImpl<T>(
-      args: [endpoint: string, position?: string, delta?: number],
-    ): Promise<HttpResponseOk<T>> {
-      const [endpoint, position, delta] = args;
-      const params: any = {};
-      if (delta === 0) {
-        //in this case we can already assume the response
-        //and avoid network
-        return Promise.resolve({
-          ok: true,
-          data: { webhooks: [] } as T,
-        });
-      }
-      if (delta !== undefined) {
-        params.limit = delta;
-      }
-      if (position !== undefined) params.offset = position;
-
-      return requestHandler<T>(baseUrl, endpoint, { params, token });
-    },
-    [baseUrl, token],
-  );
-
-  return {
-    request,
-    fetcher,
-    multiFetcher,
-    orderFetcher,
-    transferFetcher,
-    templateFetcher,
-    webhookFetcher,
-  };
-}
diff --git a/packages/merchant-backoffice-ui/src/hooks/bank.ts 
b/packages/merchant-backoffice-ui/src/hooks/bank.ts
index 9ad4c3069..e1f2638ed 100644
--- a/packages/merchant-backoffice-ui/src/hooks/bank.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/bank.ts
@@ -14,198 +14,97 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
-import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+import { useState } from "preact/hooks";
+import { PAGE_SIZE } from "../utils/constants.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } 
from "@gnu-taler/taler-util";
 import _useSWR, { SWRHook, mutate } from "swr";
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
+import { useSessionContext } from "../context/session.js";
+import { buildPaginatedResult } from "./webhooks.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// const MOCKED_ACCOUNTS: Record<string, TalerMerchantApi.AccountAddDetails> = 
{
-//   "hwire1": {
-//     h_wire: "hwire1",
-//     payto_uri: "payto://fake/iban/123",
-//     salt: "qwe",
-//   },
-//   "hwire2": {
-//     h_wire: "hwire2",
-//     payto_uri: "payto://fake/iban/123",
-//     salt: "qwe2",
-//   },
-// }
-
-// export function useBankAccountAPI(): BankAccountAPI {
-//   const mutateAll = useMatchMutate();
-//   const { request } = useBackendInstanceRequest();
-
-//   const createBankAccount = async (
-//     data: TalerMerchantApi.AccountAddDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     // MOCKED_ACCOUNTS[data.h_wire] = data
-//     // return Promise.resolve({ ok: true, data: undefined });
-//     const res = await request<void>(`/private/accounts`, {
-//       method: "POST",
-//       data,
-//     });
-//     await mutateAll(/.*private\/accounts.*/);
-//     return res;
-//   };
-
-//   const updateBankAccount = async (
-//     h_wire: string,
-//     data: TalerMerchantApi.AccountPatchDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     // MOCKED_ACCOUNTS[h_wire].credit_facade_credentials = 
data.credit_facade_credentials
-//     // MOCKED_ACCOUNTS[h_wire].credit_facade_url = data.credit_facade_url
-//     // return Promise.resolve({ ok: true, data: undefined });
-//     const res = await request<void>(`/private/accounts/${h_wire}`, {
-//       method: "PATCH",
-//       data,
-//     });
-//     await mutateAll(/.*private\/accounts.*/);
-//     return res;
-//   };
-
-//   const deleteBankAccount = async (
-//     h_wire: string,
-//   ): Promise<HttpResponseOk<void>> => {
-//     // delete MOCKED_ACCOUNTS[h_wire]
-//     // return Promise.resolve({ ok: true, data: undefined });
-//     const res = await request<void>(`/private/accounts/${h_wire}`, {
-//       method: "DELETE",
-//     });
-//     await mutateAll(/.*private\/accounts.*/);
-//     return res;
-//   };
-
-//   return {
-//     createBankAccount,
-//     updateBankAccount,
-//     deleteBankAccount,
-//   };
-// }
-
-// export interface BankAccountAPI {
-//   createBankAccount: (
-//     data: TalerMerchantApi.AccountAddDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   updateBankAccount: (
-//     id: string,
-//     data: TalerMerchantApi.AccountPatchDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   deleteBankAccount: (id: string) => Promise<HttpResponseOk<void>>;
-// }
-
 export interface InstanceBankAccountFilter {
 }
 
 export function revalidateInstanceBankAccounts() {
-  // mutate(key => key instanceof)
-  return mutate((key) => Array.isArray(key) && key[key.length - 1] === 
"/private/accounts", undefined, { revalidate: true });
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "listBankAccounts",
+    undefined,
+    { revalidate: true },
+  );
 }
-export function useInstanceBankAccounts(
-  args?: InstanceBankAccountFilter,
-  updatePosition?: (id: string) => void,
-): HttpResponsePaginated<
-  TalerMerchantApi.AccountsSummaryResponse,
-  TalerErrorDetail
-> {
-
-  const { fetcher } = useBackendInstanceRequest();
-
-  const [pageAfter, setPageAfter] = useState(1);
-
-  const totalAfter = pageAfter * PAGE_SIZE;
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.AccountsSummaryResponse>,
-    RequestError<TalerErrorDetail>
-  >([`/private/accounts`], fetcher);
-
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      TalerMerchantApi.AccountsSummaryResponse,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-  }, [afterData /*, beforeData*/]);
-
-  if (afterError) return afterError.cause;
-
-  // if the query returns less that we ask, then we have reach the end or 
beginning
-  const isReachingEnd =
-    afterData && afterData.data.accounts.length < totalAfter;
-  const isReachingStart = false;
+export function useInstanceBankAccounts() {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  const [offset, setOffset] = useState<string | undefined>();
+
+  async function fetcher([token, bid]: [AccessToken, string]) {
+    return await management.listBankAccounts(token, {
+      limit: 5,
+      offset: bid,
+      order: "dec",
+    });
+  }
+
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"listBankAccounts">,
+    TalerHttpError
+  >([session.token, offset, "listBankAccounts"], fetcher);
 
+  const isLastPage =
+    data && data.type === "ok" && data.body.accounts.length <= PAGE_SIZE;
+  const isFirstPage = !offset;
+
+  const result =
+    data && data.type == "ok" ? structuredClone(data.body.accounts) : [];
+  if (result.length == PAGE_SIZE + 1) {
+    result.pop();
+  }
   const pagination = {
-    isReachingEnd,
-    isReachingStart,
-    loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data.accounts.length < MAX_RESULT_SIZE) {
-        setPageAfter(pageAfter + 1);
-      } else {
-        const from = `${afterData.data.accounts[afterData.data.accounts.length 
- 1]
-          .h_wire
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
+    result,
+    isLastPage,
+    isFirstPage,
+    loadNext: () => {
+      if (!result.length) return;
+      setOffset(result[result.length - 1].h_wire);
     },
-    loadMorePrev: () => {
+    loadFirst: () => {
+      setOffset(undefined);
     },
   };
 
-  const accounts = !afterData ? [] : (afterData || lastAfter).data.accounts;
-  if (loadingAfter /* || loadingBefore */)
-    return { loading: true, data: { accounts } };
-  if (/*beforeData &&*/ afterData) {
-    return { ok: true, data: { accounts }, ...pagination };
-  }
-  return { loading: true };
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
+
+  return buildPaginatedResult(data.body.accounts, offset, setOffset, (d) => 
d.h_wire)
+}
+
+export function revalidateBankAccountDetails() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === 
"getBankAccountDetails",
+    undefined,
+    { revalidate: true },
+  );
 }
+export function useBankAccountDetails(h_wire: string) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
 
-export function useBankAccountDetails(
-  h_wire: string,
-): HttpResponse<
-  TalerMerchantApi.BankAccountEntry,
-  TalerErrorDetail
-> {
-  // return {
-  //   ok: true,
-  //   data: {
-  //     ...MOCKED_ACCOUNTS[h_wire],
-  //     active: true,
-  //   }
-  // }
-  const { fetcher } = useBackendInstanceRequest();
-
-  const { data, error, isValidating } = useSWR<
-    HttpResponseOk<TalerMerchantApi.BankAccountEntry>,
-    RequestError<TalerErrorDetail>
-  >([`/private/accounts/${h_wire}`], fetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
-
-  if (isValidating) return { loading: true, data: data?.data };
-  if (data) {
-    return data;
+  async function fetcher([token, wireId]: [AccessToken, string]) {
+    return await management.getBankAccountDetails(token, wireId);
   }
-  if (error) return error.cause;
-  return { loading: true };
+
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"getBankAccountDetails">,
+    TalerHttpError
+  >([session.token, h_wire, "getBankAccountDetails"], fetcher);
+
+  if (data) return data;
+  if (error) return error;
+  return undefined;
 }
diff --git a/packages/merchant-backoffice-ui/src/hooks/instance.ts 
b/packages/merchant-backoffice-ui/src/hooks/instance.ts
index e8a431ae5..cc907bd8f 100644
--- a/packages/merchant-backoffice-ui/src/hooks/instance.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/instance.ts
@@ -14,18 +14,11 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  RequestError,
   useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import {
-  useBackendBaseRequest,
-  useBackendInstanceRequest
-} from "./backend.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import { AccessToken, TalerErrorDetail, TalerHttpError, TalerMerchantApi, 
TalerMerchantInstanceResultByMethod, TalerMerchantManagementResultByMethod, 
TalerMerchantResultByMethod } from "@gnu-taler/taler-util";
+import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } 
from "@gnu-taler/taler-util";
 import _useSWR, { SWRHook, mutate } from "swr";
 import { useSessionContext } from "../context/session.js";
 const useSWR = _useSWR as unknown as SWRHook;
@@ -39,28 +32,6 @@ export function revalidateInstanceDetails() {
   );
 }
 export function useInstanceDetails() {
-  // const { fetcher } = useBackendInstanceRequest();
-
-  // const { data, error, isValidating } = useSWR<
-  //   HttpResponseOk<TalerMerchantApi.QueryInstancesResponse>,
-  //   RequestError<TalerErrorDetail>
-  // >([`/private/`], fetcher, {
-  //   refreshInterval: 0,
-  //   refreshWhenHidden: false,
-  //   revalidateOnFocus: false,
-  //   revalidateOnReconnect: false,
-  //   refreshWhenOffline: false,
-  //   revalidateIfStale: false,
-  //   errorRetryCount: 0,
-  //   errorRetryInterval: 1,
-  //   shouldRetryOnError: false,
-  // });
-
-  // if (isValidating) return { loading: true, data: data?.data };
-  // if (data) return data;
-  // if (error) return error.cause;
-  // return { loading: true };
-
   const { state: session } = useSessionContext();
   const { lib: { management } } = useMerchantApiContext();
 
@@ -78,9 +49,6 @@ export function useInstanceDetails() {
   return undefined;
 }
 
-// type KYCStatus =
-//   | { type: "ok" }
-//   | { type: "redirect"; status: TalerMerchantApi.AccountKycRedirects };
 export function revalidateInstanceKYCDetails() {
   return mutate(
     (key) => Array.isArray(key) && key[key.length - 1] === 
"getCurrentIntanceKycStatus",
@@ -89,32 +57,6 @@ export function revalidateInstanceKYCDetails() {
   );
 }
 export function useInstanceKYCDetails() {
-  // const { fetcher } = useBackendInstanceRequest();
-
-  // const { data, error } = useSWR<
-  //   HttpResponseOk<TalerMerchantApi.AccountKycRedirects>,
-  //   RequestError<TalerErrorDetail>
-  // >([`/private/kyc`], fetcher, {
-  //   refreshInterval: 60 * 1000,
-  //   refreshWhenHidden: false,
-  //   revalidateOnFocus: false,
-  //   revalidateIfStale: false,
-  //   revalidateOnMount: false,
-  //   revalidateOnReconnect: false,
-  //   refreshWhenOffline: false,
-  //   errorRetryCount: 0,
-  //   errorRetryInterval: 1,
-  //   shouldRetryOnError: false,
-  // });
-
-  // if (data) {
-  //   if (data.info?.status === 202)
-  //     return { ok: true, data: { type: "redirect", status: data.data } };
-  //   return { ok: true, data: { type: "ok" } };
-  // }
-  // if (error) return error.cause;
-  // return { loading: true };
-
   const { state: session } = useSessionContext();
   const { lib: { management } } = useMerchantApiContext();
 
diff --git a/packages/merchant-backoffice-ui/src/hooks/order.ts 
b/packages/merchant-backoffice-ui/src/hooks/order.ts
index 40932ac62..47ddf1c38 100644
--- a/packages/merchant-backoffice-ui/src/hooks/order.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/order.ts
@@ -14,276 +14,81 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
-import { useBackendInstanceRequest } from "./backend.js";
+import { PAGE_SIZE } from "../utils/constants.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
-import _useSWR, { SWRHook } from "swr";
+import { AbsoluteTime, AccessToken, TalerHttpError, 
TalerMerchantManagementResultByMethod } from "@gnu-taler/taler-util";
+import _useSWR, { SWRHook, mutate } from "swr";
+import { useSessionContext } from "../context/session.js";
+import { buildPaginatedResult } from "./webhooks.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// export interface OrderAPI {
-//   //FIXME: add OutOfStockResponse on 410
-//   createOrder: (
-//     data: TalerMerchantApi.PostOrderRequest,
-//   ) => Promise<HttpResponseOk<TalerMerchantApi.PostOrderResponse>>;
-//   forgetOrder: (
-//     id: string,
-//     data: TalerMerchantApi.ForgetRequest,
-//   ) => Promise<HttpResponseOk<void>>;
-//   refundOrder: (
-//     id: string,
-//     data: TalerMerchantApi.RefundRequest,
-//   ) => Promise<HttpResponseOk<TalerMerchantApi.MerchantRefundResponse>>;
-//   deleteOrder: (id: string) => Promise<HttpResponseOk<void>>;
-//   getPaymentURL: (id: string) => Promise<HttpResponseOk<string>>;
-// }
 
-type YesOrNo = "yes" | "no";
 
-// export function useOrderAPI(): OrderAPI {
-//   const mutateAll = useMatchMutate();
-//   const { request } = useBackendInstanceRequest();
-
-//   const createOrder = async (
-//     data: TalerMerchantApi.PostOrderRequest,
-//   ): Promise<HttpResponseOk<TalerMerchantApi.PostOrderResponse>> => {
-//     const res = await request<TalerMerchantApi.PostOrderResponse>(
-//       `/private/orders`,
-//       {
-//         method: "POST",
-//         data,
-//       },
-//     );
-//     await mutateAll(/.*private\/orders.*/);
-//     // mutate('')
-//     return res;
-//   };
-//   const refundOrder = async (
-//     orderId: string,
-//     data: TalerMerchantApi.RefundRequest,
-//   ): Promise<HttpResponseOk<TalerMerchantApi.MerchantRefundResponse>> => {
-//     mutateAll(/@"\/private\/orders"@/);
-//     const res = request<TalerMerchantApi.MerchantRefundResponse>(
-//       `/private/orders/${orderId}/refund`,
-//       {
-//         method: "POST",
-//         data,
-//       },
-//     );
-
-//     // order list returns refundable information, so we must evict 
everything
-//     await mutateAll(/.*private\/orders.*/);
-//     return res;
-//   };
-
-//   const forgetOrder = async (
-//     orderId: string,
-//     data: TalerMerchantApi.ForgetRequest,
-//   ): Promise<HttpResponseOk<void>> => {
-//     mutateAll(/@"\/private\/orders"@/);
-//     const res = request<void>(`/private/orders/${orderId}/forget`, {
-//       method: "PATCH",
-//       data,
-//     });
-//     // we may be forgetting some fields that are pare of the listing, so we 
must evict everything
-//     await mutateAll(/.*private\/orders.*/);
-//     return res;
-//   };
-//   const deleteOrder = async (
-//     orderId: string,
-//   ): Promise<HttpResponseOk<void>> => {
-//     mutateAll(/@"\/private\/orders"@/);
-//     const res = request<void>(`/private/orders/${orderId}`, {
-//       method: "DELETE",
-//     });
-//     await mutateAll(/.*private\/orders.*/);
-//     return res;
-//   };
-
-//   const getPaymentURL = async (
-//     orderId: string,
-//   ): Promise<HttpResponseOk<string>> => {
-//     return request<TalerMerchantApi.MerchantOrderStatusResponse>(
-//       `/private/orders/${orderId}`,
-//       {
-//         method: "GET",
-//       },
-//     ).then((res) => {
-//       const url =
-//         res.data.order_status === "unpaid"
-//           ? res.data.taler_pay_uri
-//           : res.data.contract_terms.fulfillment_url;
-//       const response: HttpResponseOk<string> = res as any;
-//       response.data = url || "";
-//       return response;
-//     });
-//   };
-
-//   return { createOrder, forgetOrder, deleteOrder, refundOrder, 
getPaymentURL };
-// }
+export function revalidateOrderDetails() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "getOrderDetails",
+    undefined,
+    { revalidate: true },
+  );
+}
+export function useOrderDetails(oderId: string) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
 
-export function useOrderDetails(
-  oderId: string,
-): HttpResponse<
-  TalerMerchantApi.MerchantOrderStatusResponse,
-  TalerErrorDetail
-> {
-  const { fetcher } = useBackendInstanceRequest();
+  async function fetcher([dId, token]: [string, AccessToken]) {
+    return await management.getOrderDetails(token, dId);
+  }
 
-  const { data, error, isValidating } = useSWR<
-    HttpResponseOk<TalerMerchantApi.MerchantOrderStatusResponse>,
-    RequestError<TalerErrorDetail>
-  >([`/private/orders/${oderId}`], fetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"getOrderDetails">,
+    TalerHttpError
+  >([oderId, session.token, "getOrderDetails"], fetcher);
 
-  if (isValidating) return { loading: true, data: data?.data };
   if (data) return data;
-  if (error) return error.cause;
-  return { loading: true };
+  if (error) return error;
+  return undefined;
 }
 
 export interface InstanceOrderFilter {
-  paid?: YesOrNo;
-  refunded?: YesOrNo;
-  wired?: YesOrNo;
-  date?: Date;
+  paid?: boolean;
+  refunded?: boolean;
+  wired?: boolean;
+  date?: AbsoluteTime;
+  position?: string;
 }
 
 export function useInstanceOrders(
   args?: InstanceOrderFilter,
-  updateFilter?: (d: Date) => void,
-): HttpResponsePaginated<
-  TalerMerchantApi.OrderHistory,
-  TalerErrorDetail
-> {
-  const { orderFetcher } = useBackendInstanceRequest();
-
-  const [pageBefore, setPageBefore] = useState(1);
-  const [pageAfter, setPageAfter] = useState(1);
-
-  const totalAfter = pageAfter * PAGE_SIZE;
-  const totalBefore = args?.date ? pageBefore * PAGE_SIZE : 0;
-
-  /**
-   * FIXME: this can be cleaned up a little
-   *
-   * the logic of double query should be inside the orderFetch so from the 
hook perspective and cache
-   * is just one query and one error status
-   */
-  const {
-    data: beforeData,
-    error: beforeError,
-    isValidating: loadingBefore,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.OrderHistory>,
-    RequestError<TalerErrorDetail>
-  >(
-    [
-      `/private/orders`,
-      args?.paid,
-      args?.refunded,
-      args?.wired,
-      args?.date,
-      totalBefore,
-    ],
-    orderFetcher,
-  );
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.OrderHistory>,
-    RequestError<TalerErrorDetail>
-  >(
-    [
-      `/private/orders`,
-      args?.paid,
-      args?.refunded,
-      args?.wired,
-      args?.date,
-      -totalAfter,
-    ],
-    orderFetcher,
-  );
-
-  //this will save last result
-  const [lastBefore, setLastBefore] = useState<
-    HttpResponse<
-      TalerMerchantApi.OrderHistory,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      TalerMerchantApi.OrderHistory,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-    if (beforeData) setLastBefore(beforeData);
-  }, [afterData, beforeData]);
-
-  if (beforeError) return beforeError.cause;
-  if (afterError) return afterError.cause;
+  updatePosition: (d: string | undefined) => void = () => { },
+) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  // const [offset, setOffset] = useState<string | undefined>(args?.position);
+
+  async function fetcher([token, o, p, r, w, d]: [AccessToken, string, 
boolean, boolean, boolean, AbsoluteTime]) {
+    return await management.listOrders(token, {
+      limit: PAGE_SIZE,
+      offset: o,
+      order: "dec",
+      paid: p,
+      refunded: r,
+      wired: w,
+      date: d,
+    });
+  }
 
-  // if the query returns less that we ask, then we have reach the end or 
beginning
-  const isReachingEnd = afterData && afterData.data.orders.length < totalAfter;
-  const isReachingStart =
-    args?.date === undefined ||
-    (beforeData && beforeData.data.orders.length < totalBefore);
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"listOrders">,
+    TalerHttpError
+  >([session.token, args?.position, args?.paid, args?.refunded, args?.wired, 
args?.date, "listOrders"], fetcher);
 
-  const pagination = {
-    isReachingEnd,
-    isReachingStart,
-    loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data.orders.length < MAX_RESULT_SIZE) {
-        setPageAfter(pageAfter + 1);
-      } else {
-        const from =
-          afterData.data.orders[afterData.data.orders.length - 
1].timestamp.t_s;
-        if (from && from !== "never" && updateFilter)
-          updateFilter(new Date(from * 1000));
-      }
-    },
-    loadMorePrev: () => {
-      if (!beforeData || isReachingStart) return;
-      if (beforeData.data.orders.length < MAX_RESULT_SIZE) {
-        setPageBefore(pageBefore + 1);
-      } else if (beforeData) {
-        const from =
-          beforeData.data.orders[beforeData.data.orders.length - 1].timestamp
-            .t_s;
-        if (from && from !== "never" && updateFilter)
-          updateFilter(new Date(from * 1000));
-      }
-    },
-  };
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
 
-  const orders =
-    !beforeData || !afterData
-      ? []
-      : (beforeData || lastBefore).data.orders
-        .slice()
-        .reverse()
-        .concat((afterData || lastAfter).data.orders);
-  if (loadingAfter || loadingBefore) return { loading: true, data: { orders } 
};
-  if (beforeData && afterData) {
-    return { ok: true, data: { orders }, ...pagination };
-  }
-  return { loading: true };
+  return buildPaginatedResult(data.body.orders, args?.position, 
updatePosition, (d) => String(d.row_id))
 }
diff --git a/packages/merchant-backoffice-ui/src/hooks/otp.ts 
b/packages/merchant-backoffice-ui/src/hooks/otp.ts
index 36db2ea90..69e4a0f4f 100644
--- a/packages/merchant-backoffice-ui/src/hooks/otp.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/otp.ts
@@ -14,195 +14,72 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
-import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+import { useState } from "preact/hooks";
+import { PAGE_SIZE } from "../utils/constants.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
-import _useSWR, { SWRHook } from "swr";
+import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } 
from "@gnu-taler/taler-util";
+import _useSWR, { SWRHook, mutate } from "swr";
+import { useSessionContext } from "../context/session.js";
+import { buildPaginatedResult } from "./webhooks.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// export function useOtpDeviceAPI(): OtpDeviceAPI {
-//   const mutateAll = useMatchMutate();
-//   const { request } = useBackendInstanceRequest();
-
-//   const createOtpDevice = async (
-//     data: TalerMerchantApi.OtpDeviceAddDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     // MOCKED_DEVICES[data.otp_device_id] = data
-//     // return Promise.resolve({ ok: true, data: undefined });
-//     const res = await request<void>(`/private/otp-devices`, {
-//       method: "POST",
-//       data,
-//     });
-//     await mutateAll(/.*private\/otp-devices.*/);
-//     return res;
-//   };
-
-//   const updateOtpDevice = async (
-//     deviceId: string,
-//     data: TalerMerchantApi.OtpDevicePatchDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     // MOCKED_DEVICES[deviceId].otp_algorithm = data.otp_algorithm
-//     // MOCKED_DEVICES[deviceId].otp_ctr = data.otp_ctr
-//     // MOCKED_DEVICES[deviceId].otp_device_description = 
data.otp_device_description
-//     // MOCKED_DEVICES[deviceId].otp_key = data.otp_key
-//     // return Promise.resolve({ ok: true, data: undefined });
-//     const res = await request<void>(`/private/otp-devices/${deviceId}`, {
-//       method: "PATCH",
-//       data,
-//     });
-//     await mutateAll(/.*private\/otp-devices.*/);
-//     return res;
-//   };
-
-//   const deleteOtpDevice = async (
-//     deviceId: string,
-//   ): Promise<HttpResponseOk<void>> => {
-//     // delete MOCKED_DEVICES[deviceId]
-//     // return Promise.resolve({ ok: true, data: undefined });
-//     const res = await request<void>(`/private/otp-devices/${deviceId}`, {
-//       method: "DELETE",
-//     });
-//     await mutateAll(/.*private\/otp-devices.*/);
-//     return res;
-//   };
-
-//   return {
-//     createOtpDevice,
-//     updateOtpDevice,
-//     deleteOtpDevice,
-//   };
-// }
-
-// export interface OtpDeviceAPI {
-//   createOtpDevice: (
-//     data: TalerMerchantApi.OtpDeviceAddDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   updateOtpDevice: (
-//     id: string,
-//     data: TalerMerchantApi.OtpDevicePatchDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   deleteOtpDevice: (id: string) => Promise<HttpResponseOk<void>>;
-// }
-
-export interface InstanceOtpDeviceFilter {
+export function revalidateInstanceOtpDevices() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "listOtpDevices",
+    undefined,
+    { revalidate: true },
+  );
 }
+export function useInstanceOtpDevices() {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  const [offset, setOffset] = useState<string | undefined>();
+
+  async function fetcher([token, bid]: [AccessToken, string]) {
+    return await management.listOtpDevices(token, {
+      limit: PAGE_SIZE,
+      offset: bid,
+      order: "dec",
+    });
+  }
 
-export function useInstanceOtpDevices(
-  args?: InstanceOtpDeviceFilter,
-  updatePosition?: (id: string) => void,
-): HttpResponsePaginated<
-  TalerMerchantApi.OtpDeviceSummaryResponse,
-  TalerErrorDetail
-> {
-  // return {
-  //   ok: true,
-  //   loadMore: () => { },
-  //   loadMorePrev: () => { },
-  //   data: {
-  //     otp_devices: Object.values(MOCKED_DEVICES).map(d => ({
-  //       device_description: d.otp_device_description,
-  //       otp_device_id: d.otp_device_id
-  //     }))
-  //   }
-  // }
-
-  const { fetcher } = useBackendInstanceRequest();
-
-  const [pageAfter, setPageAfter] = useState(1);
-
-  const totalAfter = pageAfter * PAGE_SIZE;
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.OtpDeviceSummaryResponse>,
-    RequestError<TalerErrorDetail>
-  >([`/private/otp-devices`], fetcher);
-
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      TalerMerchantApi.OtpDeviceSummaryResponse,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-  }, [afterData /*, beforeData*/]);
-
-  if (afterError) return afterError.cause;
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"listOtpDevices">,
+    TalerHttpError
+  >([session.token, offset, "listOtpDevices"], fetcher);
 
-  // if the query returns less that we ask, then we have reach the end or 
beginning
-  const isReachingEnd =
-    afterData && afterData.data.otp_devices.length < totalAfter;
-  const isReachingStart = true;
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
 
-  const pagination = {
-    isReachingEnd,
-    isReachingStart,
-    loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data.otp_devices.length < MAX_RESULT_SIZE) {
-        setPageAfter(pageAfter + 1);
-      } else {
-        const from = 
`${afterData.data.otp_devices[afterData.data.otp_devices.length - 1]
-          .otp_device_id
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
-    },
-    loadMorePrev: () => {
-    },
-  };
+  return buildPaginatedResult(data.body.otp_devices, offset, setOffset, (d) => 
d.otp_device_id)
+}
 
-  const otp_devices = !afterData ? [] : (afterData || 
lastAfter).data.otp_devices;
-  if (loadingAfter /* || loadingBefore */)
-    return { loading: true, data: { otp_devices } };
-  if (/*beforeData &&*/ afterData) {
-    return { ok: true, data: { otp_devices }, ...pagination };
-  }
-  return { loading: true };
+export function revalidateOtpDeviceDetails() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === 
"getOtpDeviceDetails",
+    undefined,
+    { revalidate: true },
+  );
 }
+export function useOtpDeviceDetails(deviceId: string) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
 
-export function useOtpDeviceDetails(
-  deviceId: string,
-): HttpResponse<
-  TalerMerchantApi.OtpDeviceDetails,
-  TalerErrorDetail
-> {
-  // return {
-  //   ok: true,
-  //   data: {
-  //     device_description: MOCKED_DEVICES[deviceId].otp_device_description,
-  //     otp_algorithm: MOCKED_DEVICES[deviceId].otp_algorithm,
-  //     otp_ctr: MOCKED_DEVICES[deviceId].otp_ctr
-  //   }
-  // }
-  const { fetcher } = useBackendInstanceRequest();
+  async function fetcher([dId, token]: [string, AccessToken]) {
+    return await management.getOtpDeviceDetails(token, dId);
+  }
 
-  const { data, error, isValidating } = useSWR<
-    HttpResponseOk<TalerMerchantApi.OtpDeviceDetails>,
-    RequestError<TalerErrorDetail>
-  >([`/private/otp-devices/${deviceId}`], fetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"getOtpDeviceDetails">,
+    TalerHttpError
+  >([deviceId, session.token, "getOtpDeviceDetails"], fetcher);
 
-  if (isValidating) return { loading: true, data: data?.data };
-  if (data) {
-    return data;
-  }
-  if (error) return error.cause;
-  return { loading: true };
+  if (data) return data;
+  if (error) return error;
+  return undefined;
 }
diff --git a/packages/merchant-backoffice-ui/src/hooks/product.ts 
b/packages/merchant-backoffice-ui/src/hooks/product.ts
index 0eb54f717..6721136a5 100644
--- a/packages/merchant-backoffice-ui/src/hooks/product.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/product.ts
@@ -14,164 +14,93 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import _useSWR, { SWRHook, useSWRConfig } from "swr";
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
+import { AccessToken, OperationOk, TalerHttpError, TalerMerchantApi, 
TalerMerchantManagementErrorsByMethod, TalerMerchantManagementResultByMethod, 
opFixedSuccess } from "@gnu-taler/taler-util";
+import { useState } from "preact/hooks";
+import _useSWR, { SWRHook, mutate } from "swr";
+import { useSessionContext } from "../context/session.js";
+import { PAGE_SIZE } from "../utils/constants.js";
+import { buildPaginatedResult } from "./webhooks.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// export interface ProductAPI {
-//   getProduct: (
-//     id: string,  
-//   ) => Promise<void>;
-//   createProduct: (
-//     data: TalerMerchantApi.ProductAddDetail,
-//   ) => Promise<void>;
-//   updateProduct: (
-//     id: string,
-//     data: TalerMerchantApi.ProductPatchDetail,
-//   ) => Promise<void>;
-//   deleteProduct: (id: string) => Promise<void>;
-//   lockProduct: (
-//     id: string,
-//     data: TalerMerchantApi.LockRequest,
-//   ) => Promise<void>;
-// }
-
-// export function useProductAPI(): ProductAPI {
-//   const mutateAll = useMatchMutate();
-//   const { mutate } = useSWRConfig();
-
-//   const { request } = useBackendInstanceRequest();
-
-//   const createProduct = async (
-//     data: TalerMerchantApi.ProductAddDetail,
-//   ): Promise<void> => {
-//     const res = await request(`/private/products`, {
-//       method: "POST",
-//       data,
-//     });
-
-//     return await mutateAll(/.*\/private\/products.*/);
-//   };
-
-//   const updateProduct = async (
-//     productId: string,
-//     data: TalerMerchantApi.ProductPatchDetail,
-//   ): Promise<void> => {
-//     const r = await request(`/private/products/${productId}`, {
-//       method: "PATCH",
-//       data,
-//     });
-
-//     return await mutateAll(/.*\/private\/products.*/);
-//   };
-
-//   const deleteProduct = async (productId: string): Promise<void> => {
-//     await request(`/private/products/${productId}`, {
-//       method: "DELETE",
-//     });
-//     await mutate([`/private/products`]);
-//   };
-
-//   const lockProduct = async (
-//     productId: string,
-//     data: TalerMerchantApi.LockRequest,
-//   ): Promise<void> => {
-//     await request(`/private/products/${productId}/lock`, {
-//       method: "POST",
-//       data,
-//     });
-
-//     return await mutateAll(/.*"\/private\/products.*/);
-//   };
-
-//   const getProduct = async (
-//     productId: string,
-//   ): Promise<void> => {
-//     await request(`/private/products/${productId}`, {
-//       method: "GET",
-//     });
-
-//     return
-//   };
-
-//   return { createProduct, updateProduct, deleteProduct, lockProduct, 
getProduct };
-// }
-
-export function useInstanceProducts(): HttpResponse<
-  (TalerMerchantApi.ProductDetail & WithId)[],
-  TalerErrorDetail
-> {
-  const { fetcher, multiFetcher } = useBackendInstanceRequest();
-
-  const { data: list, error: listError } = useSWR<
-    HttpResponseOk<TalerMerchantApi.InventorySummaryResponse>,
-    RequestError<TalerErrorDetail>
-  >([`/private/products`], fetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
-
-  const paths = (list?.data.products || []).map(
-    (p) => `/private/products/${p.product_id}`,
+type ProductWithId = TalerMerchantApi.ProductDetail & { id: string, serial: 
number };
+function notUndefined(c: ProductWithId | undefined): c is ProductWithId {
+  return c !== undefined;
+}
+
+export function revalidateInstanceProducts() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === 
"listProductsWithId",
+    undefined,
+    { revalidate: true },
   );
-  const { data: products, error: productError } = useSWR<
-    HttpResponseOk<TalerMerchantApi.ProductDetail>[],
-    RequestError<TalerErrorDetail>
-  >([paths], multiFetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
-
-  if (listError) return listError.cause;
-  if (productError) return productError.cause;
-
-  if (products) {
-    const dataWithId = products.map((d) => {
-      //take the id from the queried url
-      return {
-        ...d.data,
-        id: d.info?.url.replace(/.*\/private\/products\//, "") || "",
-      };
+}
+export function useInstanceProducts() {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  const [offset, setOffset] = useState<number | undefined>();
+
+  async function fetcher([token, bid]: [AccessToken, number]) {
+    const list = await management.listProducts(token, {
+      limit: PAGE_SIZE,
+      offset: String(bid),
+      order: "dec",
     });
-    return { ok: true, data: dataWithId };
+    if (list.type !== "ok") {
+      return list;
+    }
+    const all: Array<ProductWithId | undefined> = await Promise.all(
+      list.body.products.map(async (c) => {
+        const r = await management.getProductDetails(token, c.product_id);
+        if (r.type === "fail") {
+          return undefined;
+        }
+        return { ...r.body, id: c.product_id, serial: c.product_serial };
+      }),
+    );
+    const products = all.filter(notUndefined);
+
+    return opFixedSuccess({ products });
   }
-  return { loading: true };
+
+  const { data, error } = useSWR<
+    OperationOk<{ products: ProductWithId[] }> |
+    TalerMerchantManagementErrorsByMethod<"listProducts">,
+    TalerHttpError
+  >([session.token, offset, "listProductsWithId"], fetcher);
+
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
+
+  return buildPaginatedResult(data.body.products, offset, setOffset, (d) => 
d.serial)
+}
+
+export function revalidateProductDetails() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "getProductDetails",
+    undefined,
+    { revalidate: true },
+  );
 }
+export function useProductDetails(productId: string) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  async function fetcher([pid, token]: [string, AccessToken]) {
+    return await management.getProductDetails(token, pid);
+  }
+
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"getProductDetails">,
+    TalerHttpError
+  >([productId, session.token, "getProductDetails"], fetcher);
 
-export function useProductDetails(
-  productId: string,
-): HttpResponse<
-  TalerMerchantApi.ProductDetail,
-  TalerErrorDetail
-> {
-  const { fetcher } = useBackendInstanceRequest();
-
-  const { data, error, isValidating } = useSWR<
-    HttpResponseOk<TalerMerchantApi.ProductDetail>,
-    RequestError<TalerErrorDetail>
-  >([`/private/products/${productId}`], fetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
-
-  if (isValidating) return { loading: true, data: data?.data };
   if (data) return data;
-  if (error) return error.cause;
-  return { loading: true };
+  if (error) return error;
+  return undefined;
 }
diff --git a/packages/merchant-backoffice-ui/src/hooks/templates.ts 
b/packages/merchant-backoffice-ui/src/hooks/templates.ts
index ff0461a67..10e480b01 100644
--- a/packages/merchant-backoffice-ui/src/hooks/templates.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/templates.ts
@@ -14,253 +14,77 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
-import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+import { useState } from "preact/hooks";
+import { PAGE_SIZE } from "../utils/constants.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
-import _useSWR, { SWRHook } from "swr";
+import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } 
from "@gnu-taler/taler-util";
+import _useSWR, { SWRHook, mutate } from "swr";
+import { useSessionContext } from "../context/session.js";
+import { buildPaginatedResult } from "./webhooks.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// export function useTemplateAPI(): TemplateAPI {
-//   const mutateAll = useMatchMutate();
-//   const { request } = useBackendInstanceRequest();
-
-//   const createTemplate = async (
-//     data: TalerMerchantApi.TemplateAddDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/templates`, {
-//       method: "POST",
-//       data,
-//     });
-//     await mutateAll(/.*private\/templates.*/);
-//     return res;
-//   };
-
-//   const updateTemplate = async (
-//     templateId: string,
-//     data: TalerMerchantApi.TemplatePatchDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/templates/${templateId}`, {
-//       method: "PATCH",
-//       data,
-//     });
-//     await mutateAll(/.*private\/templates.*/);
-//     return res;
-//   };
-
-//   const deleteTemplate = async (
-//     templateId: string,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/templates/${templateId}`, {
-//       method: "DELETE",
-//     });
-//     await mutateAll(/.*private\/templates.*/);
-//     return res;
-//   };
-
-//   const createOrderFromTemplate = async (
-//     templateId: string,
-//     data: TalerMerchantApi.UsingTemplateDetails,
-//   ): Promise<
-//     HttpResponseOk<TalerMerchantApi.PostOrderResponse>
-//   > => {
-//     const res = await request<TalerMerchantApi.PostOrderResponse>(
-//       `/templates/${templateId}`,
-//       {
-//         method: "POST",
-//         data,
-//       },
-//     );
-//     await mutateAll(/.*private\/templates.*/);
-//     return res;
-//   };
-
-//   const testTemplateExist = async (
-//     templateId: string,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/templates/${templateId}`, { 
method: "GET", });
-//     return res;
-//   };
-
-
-//   return {
-//     createTemplate,
-//     updateTemplate,
-//     deleteTemplate,
-//     testTemplateExist,
-//     createOrderFromTemplate,
-//   };
-// }
-
-// export interface TemplateAPI {
-//   createTemplate: (
-//     data: TalerMerchantApi.TemplateAddDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   updateTemplate: (
-//     id: string,
-//     data: TalerMerchantApi.TemplatePatchDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   testTemplateExist: (
-//     id: string
-//   ) => Promise<HttpResponseOk<void>>;
-//   deleteTemplate: (id: string) => Promise<HttpResponseOk<void>>;
-//   createOrderFromTemplate: (
-//     id: string,
-//     data: TalerMerchantApi.UsingTemplateDetails,
-//   ) => Promise<HttpResponseOk<TalerMerchantApi.PostOrderResponse>>;
-// }
 
 export interface InstanceTemplateFilter {
-  //FIXME: add filter to the template list
-  position?: string;
 }
 
-export function useInstanceTemplates(
-  args?: InstanceTemplateFilter,
-  updatePosition?: (id: string) => void,
-): HttpResponsePaginated<
-  TalerMerchantApi.TemplateSummaryResponse,
-  TalerErrorDetail
-> {
-  const { templateFetcher } = useBackendInstanceRequest();
-
-  const [pageBefore, setPageBefore] = useState(1);
-  const [pageAfter, setPageAfter] = useState(1);
-
-  const totalAfter = pageAfter * PAGE_SIZE;
-  const totalBefore = args?.position ? pageBefore * PAGE_SIZE : 0;
-
-  /**
-   * FIXME: this can be cleaned up a little
-   *
-   * the logic of double query should be inside the orderFetch so from the 
hook perspective and cache
-   * is just one query and one error status
-   */
-  const {
-    data: beforeData,
-    error: beforeError,
-    isValidating: loadingBefore,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.TemplateSummaryResponse>,
-    RequestError<TalerErrorDetail>>(
-      [
-        `/private/templates`,
-        args?.position,
-        totalBefore,
-      ],
-      templateFetcher,
-    );
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.TemplateSummaryResponse>,
-    RequestError<TalerErrorDetail>
-  >([`/private/templates`, args?.position, -totalAfter], templateFetcher);
-
-  //this will save last result
-  const [lastBefore, setLastBefore] = useState<
-    HttpResponse<
-      TalerMerchantApi.TemplateSummaryResponse,
-      TalerErrorDetail
-    >
-  >({ loading: true });
+export function revalidateInstanceTemplates() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "listTemplates",
+    undefined,
+    { revalidate: true },
+  );
+}
+export function useInstanceTemplates() {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  const [offset, setOffset] = useState<string | undefined>();
+
+  async function fetcher([token, bid]: [AccessToken, string]) {
+    return await management.listTemplates(token, {
+      limit: PAGE_SIZE,
+      offset: bid,
+      order: "dec",
+    });
+  }
 
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      TalerMerchantApi.TemplateSummaryResponse,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-    if (beforeData) setLastBefore(beforeData);
-  }, [afterData, beforeData]);
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"listTemplates">,
+    TalerHttpError
+  >([session.token, offset, "listTemplates"], fetcher);
 
-  if (beforeError) return beforeError.cause;
-  if (afterError) return afterError.cause;
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
 
-  // if the query returns less that we ask, then we have reach the end or 
beginning
-  const isReachingEnd =
-    afterData && afterData.data.templates.length < totalAfter;
-  const isReachingStart = args?.position === undefined
-    ||
-    (beforeData && beforeData.data.templates.length < totalBefore);
+  return buildPaginatedResult(data.body.templates, offset, setOffset, (d) => 
d.template_id)
 
-  const pagination = {
-    isReachingEnd,
-    isReachingStart,
-    loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data.templates.length < MAX_RESULT_SIZE) {
-        setPageAfter(pageAfter + 1);
-      } else {
-        const from = 
`${afterData.data.templates[afterData.data.templates.length - 1]
-          .template_id
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
-    },
-    loadMorePrev: () => {
-      if (!beforeData || isReachingStart) return;
-      if (beforeData.data.templates.length < MAX_RESULT_SIZE) {
-        setPageBefore(pageBefore + 1);
-      } else if (beforeData) {
-        const from = 
`${beforeData.data.templates[beforeData.data.templates.length - 1]
-          .template_id
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
-    },
-  };
+}
 
-  // const templates = !afterData ? [] : (afterData || 
lastAfter).data.templates;
-  const templates =
-    !beforeData || !afterData
-      ? []
-      : (beforeData || lastBefore).data.templates
-        .slice()
-        .reverse()
-        .concat((afterData || lastAfter).data.templates);
-  if (loadingAfter || loadingBefore)
-    return { loading: true, data: { templates } };
-  if (beforeData && afterData) {
-    return { ok: true, data: { templates }, ...pagination };
-  }
-  return { loading: true };
+export function revalidateProductDetails() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === 
"getTemplateDetails",
+    undefined,
+    { revalidate: true },
+  );
 }
+export function useTemplateDetails(templateId: string) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
 
-export function useTemplateDetails(
-  templateId: string,
-): HttpResponse<
-  TalerMerchantApi.TemplateDetails,
-  TalerErrorDetail
-> {
-  const { templateFetcher } = useBackendInstanceRequest();
+  async function fetcher([tid, token]: [string, AccessToken]) {
+    return await management.getTemplateDetails(token, tid);
+  }
 
-  const { data, error, isValidating } = useSWR<
-    HttpResponseOk<TalerMerchantApi.TemplateDetails>,
-    RequestError<TalerErrorDetail>
-  >([`/private/templates/${templateId}`], templateFetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"getTemplateDetails">,
+    TalerHttpError
+  >([templateId, session.token, "getTemplateDetails"], fetcher);
 
-  if (isValidating) return { loading: true, data: data?.data };
-  if (data) {
-    return data;
-  }
-  if (error) return error.cause;
-  return { loading: true };
+  if (data) return data;
+  if (error) return error;
+  return undefined;
 }
diff --git a/packages/merchant-backoffice-ui/src/hooks/transfer.ts 
b/packages/merchant-backoffice-ui/src/hooks/transfer.ts
index af62af0fa..afabf775c 100644
--- a/packages/merchant-backoffice-ui/src/hooks/transfer.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/transfer.ts
@@ -14,173 +14,59 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
-import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+import { PAGE_SIZE } from "../utils/constants.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
-import _useSWR, { SWRHook } from "swr";
+import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } 
from "@gnu-taler/taler-util";
+import _useSWR, { SWRHook, mutate } from "swr";
+import { useSessionContext } from "../context/session.js";
+import { buildPaginatedResult } from "./webhooks.js";
+import { useState } from "preact/hooks";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// export function useTransferAPI(): TransferAPI {
-//   const mutateAll = useMatchMutate();
-//   const { request } = useBackendInstanceRequest();
-
-//   const informTransfer = async (
-//     data: TalerMerchantApi.TransferInformation,
-//   ): Promise<HttpResponseOk<{}>> => {
-//     const res = await request<{}>(`/private/transfers`, {
-//       method: "POST",
-//       data,
-//     });
-
-//     await mutateAll(/.*private\/transfers.*/);
-//     return res;
-//   };
-
-//   return { informTransfer };
-// }
-
-// export interface TransferAPI {
-//   informTransfer: (
-//     data: TalerMerchantApi.TransferInformation,
-//   ) => Promise<HttpResponseOk<{}>>;
-// }
-
 export interface InstanceTransferFilter {
   payto_uri?: string;
-  verified?: "yes" | "no";
+  verified?: boolean;
   position?: string;
 }
 
+export function revalidateInstanceTransfers() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "listWireTransfers",
+    undefined,
+    { revalidate: true },
+  );
+}
 export function useInstanceTransfers(
   args?: InstanceTransferFilter,
-  updatePosition?: (id: string) => void,
-): HttpResponsePaginated<
-  TalerMerchantApi.TransferList,
-  TalerErrorDetail
-> {
-  const { transferFetcher } = useBackendInstanceRequest();
-
-  const [pageBefore, setPageBefore] = useState(1);
-  const [pageAfter, setPageAfter] = useState(1);
-
-  const totalAfter = pageAfter * PAGE_SIZE;
-  const totalBefore = args?.position !== undefined ? pageBefore * PAGE_SIZE : 
0;
-
-  /**
-   * FIXME: this can be cleaned up a little
-   *
-   * the logic of double query should be inside the orderFetch so from the 
hook perspective and cache
-   * is just one query and one error status
-   */
-  const {
-    data: beforeData,
-    error: beforeError,
-    isValidating: loadingBefore,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.TransferList>,
-    RequestError<TalerErrorDetail>
-  >(
-    [
-      `/private/transfers`,
-      args?.payto_uri,
-      args?.verified,
-      args?.position,
-      totalBefore,
-    ],
-    transferFetcher,
-  );
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.TransferList>,
-    RequestError<TalerErrorDetail>
-  >(
-    [
-      `/private/transfers`,
-      args?.payto_uri,
-      args?.verified,
-      args?.position,
-      -totalAfter,
-    ],
-    transferFetcher,
-  );
-
-  //this will save last result
-  const [lastBefore, setLastBefore] = useState<
-    HttpResponse<
-      TalerMerchantApi.TransferList,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      TalerMerchantApi.TransferList,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-    if (beforeData) setLastBefore(beforeData);
-  }, [afterData, beforeData]);
+  updatePosition: (id: string | undefined) => void = (() => { }),
+) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  // const [offset, setOffset] = useState<string | undefined>(args?.position);
+
+  async function fetcher([token, o, p, v]: [AccessToken, string, string, 
boolean]) {
+    return await management.listWireTransfers(token, {
+      paytoURI: p,
+      verified: v,
+      limit: PAGE_SIZE,
+      offset: o,
+      order: "dec",
+    });
+  }
 
-  if (beforeError) return beforeError.cause;
-  if (afterError) return afterError.cause;
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"listWireTransfers">,
+    TalerHttpError
+  >([session.token, args?.position, args?.payto_uri, args?.verified, 
"listWireTransfers"], fetcher);
 
-  // if the query returns less that we ask, then we have reach the end or 
beginning
-  const isReachingEnd =
-    afterData && afterData.data.transfers.length < totalAfter;
-  const isReachingStart =
-    args?.position === undefined ||
-    (beforeData && beforeData.data.transfers.length < totalBefore);
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
 
-  const pagination = {
-    isReachingEnd,
-    isReachingStart,
-    loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data.transfers.length < MAX_RESULT_SIZE) {
-        setPageAfter(pageAfter + 1);
-      } else {
-        const from = 
`${afterData.data.transfers[afterData.data.transfers.length - 1]
-            .transfer_serial_id
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
-    },
-    loadMorePrev: () => {
-      if (!beforeData || isReachingStart) return;
-      if (beforeData.data.transfers.length < MAX_RESULT_SIZE) {
-        setPageBefore(pageBefore + 1);
-      } else if (beforeData) {
-        const from = 
`${beforeData.data.transfers[beforeData.data.transfers.length - 1]
-            .transfer_serial_id
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
-    },
-  };
+  return buildPaginatedResult(data.body.transfers, args?.position, 
updatePosition, (d) => String(d.transfer_serial_id))
 
-  const transfers =
-    !beforeData || !afterData
-      ? []
-      : (beforeData || lastBefore).data.transfers
-        .slice()
-        .reverse()
-        .concat((afterData || lastAfter).data.transfers);
-  if (loadingAfter || loadingBefore)
-    return { loading: true, data: { transfers } };
-  if (beforeData && afterData) {
-    return { ok: true, data: { transfers }, ...pagination };
-  }
-  return { loading: true };
 }
diff --git a/packages/merchant-backoffice-ui/src/hooks/webhooks.ts 
b/packages/merchant-backoffice-ui/src/hooks/webhooks.ts
index cc817b84e..5e2e08bcc 100644
--- a/packages/merchant-backoffice-ui/src/hooks/webhooks.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/webhooks.ts
@@ -14,165 +14,108 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
-import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+import { useState } from "preact/hooks";
+import { PAGE_SIZE } from "../utils/constants.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
-import _useSWR, { SWRHook } from "swr";
+import { AccessToken, OperationOk, TalerHttpError, 
TalerMerchantManagementResultByMethod } from "@gnu-taler/taler-util";
+import _useSWR, { SWRHook, mutate } from "swr";
+import { useSessionContext } from "../context/session.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// export function useWebhookAPI(): WebhookAPI {
-//   const mutateAll = useMatchMutate();
-//   const { request } = useBackendInstanceRequest();
-
-//   const createWebhook = async (
-//     data: TalerMerchantApi.WebhookAddDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/webhooks`, {
-//       method: "POST",
-//       data,
-//     });
-//     await mutateAll(/.*private\/webhooks.*/);
-//     return res;
-//   };
-
-//   const updateWebhook = async (
-//     webhookId: string,
-//     data: TalerMerchantApi.WebhookPatchDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/webhooks/${webhookId}`, {
-//       method: "PATCH",
-//       data,
-//     });
-//     await mutateAll(/.*private\/webhooks.*/);
-//     return res;
-//   };
-
-//   const deleteWebhook = async (
-//     webhookId: string,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/webhooks/${webhookId}`, {
-//       method: "DELETE",
-//     });
-//     await mutateAll(/.*private\/webhooks.*/);
-//     return res;
-//   };
-
-//   return { createWebhook, updateWebhook, deleteWebhook };
-// }
-
-// export interface WebhookAPI {
-//   createWebhook: (
-//     data: TalerMerchantApi.WebhookAddDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   updateWebhook: (
-//     id: string,
-//     data: TalerMerchantApi.WebhookPatchDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   deleteWebhook: (id: string) => Promise<HttpResponseOk<void>>;
-// }
-
 export interface InstanceWebhookFilter {
-  //FIXME: add filter to the webhook list
-  position?: string;
 }
 
-export function useInstanceWebhooks(
-  args?: InstanceWebhookFilter,
-  updatePosition?: (id: string) => void,
-): HttpResponsePaginated<
-  TalerMerchantApi.WebhookSummaryResponse,
-  TalerErrorDetail
-> {
-  const { webhookFetcher } = useBackendInstanceRequest();
-
-  const [pageBefore, setPageBefore] = useState(1);
-  const [pageAfter, setPageAfter] = useState(1);
-
-  const totalAfter = pageAfter * PAGE_SIZE;
-  const totalBefore = args?.position ? pageBefore * PAGE_SIZE : 0;
-
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.WebhookSummaryResponse>,
-    RequestError<TalerErrorDetail>
-
-  >([`/private/webhooks`, args?.position, -totalAfter], webhookFetcher);
-
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      TalerMerchantApi.WebhookSummaryResponse,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-  }, [afterData]);
-
-  if (afterError) return afterError.cause;
-
-  const isReachingEnd =
-    afterData && afterData.data.webhooks.length < totalAfter;
-  const isReachingStart = true;
-
-  const pagination = {
-    isReachingEnd,
-    isReachingStart,
-    loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data.webhooks.length < MAX_RESULT_SIZE) {
-        setPageAfter(pageAfter + 1);
-      } else {
-        const from = `${afterData.data.webhooks[afterData.data.webhooks.length 
- 1].webhook_id
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
+export function revalidateInstanceWebhooks() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "listWebhooks",
+    undefined,
+    { revalidate: true },
+  );
+}
+export function useInstanceWebhooks() {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  const [offset, setOffset] = useState<string | undefined>();
+
+  async function fetcher([token, bid]: [AccessToken, string]) {
+    return await management.listWebhooks(token, {
+      limit: 5,
+      offset: bid,
+      order: "dec",
+    });
+  }
+
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"listWebhooks">,
+    TalerHttpError
+  >([session.token, offset, "listWebhooks"], fetcher);
+
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
+
+  return buildPaginatedResult(data.body.webhooks, offset, setOffset, (d) => 
d.webhook_id)
+}
+
+type PaginatedResult<T> = OperationOk<T> & {
+  isLastPage: boolean;
+  isFirstPage: boolean;
+  loadNext(): void;
+  loadFirst(): void;
+}
+
+//TODO: consider sending this to web-util
+export function buildPaginatedResult<R, OffId>(data: R[], offset: OffId | 
undefined, setOffset: (o: OffId | undefined) => void, getId: (r: R) => OffId): 
PaginatedResult<R[]> {
+
+  const isLastPage = data.length <= PAGE_SIZE;
+  const isFirstPage = offset === undefined;
+
+  const result = structuredClone(data);
+  if (result.length == PAGE_SIZE + 1) {
+    result.pop();
+  }
+  return {
+    type: "ok",
+    body: result,
+    isLastPage,
+    isFirstPage,
+    loadNext: () => {
+      if (!result.length) return;
+      const id = getId(result[result.length - 1])
+      setOffset(id);
     },
-    loadMorePrev: () => {
-      return;
+    loadFirst: () => {
+      setOffset(undefined);
     },
   };
+}
 
-  const webhooks = !afterData ? [] : (afterData || lastAfter).data.webhooks;
 
-  if (loadingAfter) return { loading: true, data: { webhooks } };
-  if (afterData) {
-    return { ok: true, data: { webhooks }, ...pagination };
-  }
-  return { loading: true };
+export function revalidateWebhookDetails() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "getWebhookDetails",
+    undefined,
+    { revalidate: true },
+  );
 }
+export function useWebhookDetails(webhookId: string) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  async function fetcher([hookId, token]: [string, AccessToken]) {
+    return await management.getWebhookDetails(token, hookId);
+  }
+
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"getWebhookDetails">,
+    TalerHttpError
+  >([webhookId, session.token, "getWebhookDetails"], fetcher);
 
-export function useWebhookDetails(
-  webhookId: string,
-): HttpResponse<
-  TalerMerchantApi.WebhookDetails,
-  TalerErrorDetail
-> {
-  const { webhookFetcher } = useBackendInstanceRequest();
-
-  const { data, error, isValidating } = useSWR<
-    HttpResponseOk<TalerMerchantApi.WebhookDetails>,
-    RequestError<TalerErrorDetail>
-  >([`/private/webhooks/${webhookId}`], webhookFetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
-
-  if (isValidating) return { loading: true, data: data?.data };
   if (data) return data;
-  if (error) return error.cause;
-  return { loading: true };
+  if (error) return error;
+  return undefined;
 }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
index 35c9e6624..0ce126b76 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
@@ -46,7 +46,7 @@ export default function CreateValidator({ onConfirm, onBack 
}: Props): VNode {
       <CreatePage
         onBack={onBack}
         onCreate={(request: Entity) => {
-          return api.management.addAccount(state.token, request)
+          return api.management.addBankAccount(state.token, request)
             .then(() => {
               onConfirm()
             })
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
index 8de6c763e..ab63d0d5f 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
+import { HttpStatusCode, TalerError, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
 import {
   ErrorType,
   HttpError,
@@ -34,6 +34,7 @@ import { useInstanceBankAccounts } from 
"../../../../hooks/bank.js";
 import { Notification } from "../../../../utils/types.js";
 import { ListPage } from "./ListPage.js";
 import { useSessionContext } from "../../../../context/session.js";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 
 interface Props {
   onUnauthorized: () => VNode;
@@ -50,32 +51,21 @@ export default function ListOtpDevices({
   onSelect,
   onNotFound,
 }: Props): VNode {
-  const [position, setPosition] = useState<string | undefined>(undefined);
   const { i18n } = useTranslationContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { lib: api } = useMerchantApiContext();
   const { state } = useSessionContext();
-  const result = useInstanceBankAccounts({ position }, (id) => 
setPosition(id));
+  const result = useInstanceBankAccounts();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />
   }
 
   return (
     <Fragment>
       <NotificationCard notification={notif} />
-      {result.data.accounts.length < 1 &&
+      {result.result.length < 1 &&
         <NotificationCard notification={{
           type: "WARN",
           message: i18n.str`You need to associate a bank account to receive 
revenue.`,
@@ -83,7 +73,7 @@ export default function ListOtpDevices({
         }} />
       }
       <ListPage
-        devices={result.data.accounts}
+        devices={result.result}
         onLoadMoreBefore={
           result.isReachingStart ? result.loadMorePrev : undefined
         }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
index dc2e08d91..ff54c487a 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
@@ -83,7 +83,7 @@ export default function ProductList({
 
       <JumpToElementById
         testIfExist={async (id) => {
-          const resp = await lib.management.getProduct(state.token, id);
+          const resp = await lib.management.getProductDetails(state.token, id);
           return resp.type === "ok";
         }}
         onSelect={onSelect}
diff --git a/packages/taler-util/src/http-client/merchant.ts 
b/packages/taler-util/src/http-client/merchant.ts
index 5fd001555..c223b0a16 100644
--- a/packages/taler-util/src/http-client/merchant.ts
+++ b/packages/taler-util/src/http-client/merchant.ts
@@ -512,7 +512,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-accounts
    */
-  async addAccount(token: AccessToken | undefined, body: 
TalerMerchantApi.AccountAddDetails) {
+  async addBankAccount(token: AccessToken | undefined, body: 
TalerMerchantApi.AccountAddDetails) {
     const url = new URL(`private/accounts`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -540,7 +540,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private-accounts-$H_WIRE
    */
-  async updateAccount(
+  async updateBankAccount(
     token: AccessToken | undefined,
     wireAccount: string,
     body: TalerMerchantApi.AccountPatchDetails,
@@ -569,9 +569,11 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-accounts
    */
-  async listAccounts(token: AccessToken) {
+  async listBankAccounts(token: AccessToken, params?: PaginationParams) {
     const url = new URL(`private/accounts`, this.baseUrl);
 
+    // addMerchantPaginationParams(url, params);
+
     const headers: Record<string, string> = {}
     if (token) {
       headers.Authorization = makeBearerTokenAuthHeader(token)
@@ -594,7 +596,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-accounts-$H_WIRE
    */
-  async getAccount(token: AccessToken | undefined, wireAccount: string) {
+  async getBankAccountDetails(token: AccessToken | undefined, wireAccount: 
string) {
     const url = new URL(`private/accounts/${wireAccount}`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -619,7 +621,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-accounts-$H_WIRE
    */
-  async deleteAccount(token: AccessToken | undefined, wireAccount: string) {
+  async deleteBankAccount(token: AccessToken | undefined, wireAccount: string) 
{
     const url = new URL(`private/accounts/${wireAccount}`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -733,7 +735,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-products-$PRODUCT_ID
    */
-  async getProduct(token: AccessToken | undefined, productId: string) {
+  async getProductDetails(token: AccessToken | undefined, productId: string) {
     const url = new URL(`private/products/${productId}`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -902,7 +904,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-orders-$ORDER_ID
    */
-  async getOrder(
+  async getOrderDetails(
     token: AccessToken | undefined,
     orderId: string,
     params: TalerMerchantApi.GetOrderRequestParams = {},
@@ -1207,7 +1209,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-otp-devices
    */
-  async listOtpDevices(token: AccessToken | undefined,) {
+  async listOtpDevices(token: AccessToken | undefined, params?: 
PaginationParams) {
     const url = new URL(`private/otp-devices`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1231,7 +1233,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-otp-devices-$DEVICE_ID
    */
-  async getOtpDevice(
+  async getOtpDeviceDetails(
     token: AccessToken | undefined,
     deviceId: string,
     params: TalerMerchantApi.GetOtpDeviceRequestParams = {},
@@ -1350,7 +1352,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * https://docs.taler.net/core/api-merchant.html#inspecting-template
    */
-  async listTemplates(token: AccessToken | undefined,) {
+  async listTemplates(token: AccessToken | undefined, params?: 
PaginationParams) {
     const url = new URL(`private/templates`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1374,7 +1376,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-templates-$TEMPLATE_ID
    */
-  async getTemplate(token: AccessToken | undefined, templateId: string) {
+  async getTemplateDetails(token: AccessToken | undefined, templateId: string) 
{
     const url = new URL(`private/templates/${templateId}`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1520,7 +1522,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-webhooks
    */
-  async listWebhooks(token: AccessToken | undefined,) {
+  async listWebhooks(token: AccessToken | undefined, params?: 
PaginationParams) {
     const url = new URL(`private/webhooks`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1545,7 +1547,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-webhooks-$WEBHOOK_ID
    */
-  async getWebhook(token: AccessToken | undefined, webhookId: string) {
+  async getWebhookDetails(token: AccessToken | undefined, webhookId: string) {
     const url = new URL(`private/webhooks/${webhookId}`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1652,7 +1654,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-tokenfamilies
    */
-  async listTokenFamilies(token: AccessToken | undefined,) {
+  async listTokenFamilies(token: AccessToken | undefined, params?: 
PaginationParams) {
     const url = new URL(`private/tokenfamilies`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1677,7 +1679,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-tokenfamilies-$TOKEN_FAMILY_SLUG
    */
-  async getTokenFamily(token: AccessToken | undefined, tokenSlug: string) {
+  async getTokenFamilyDetails(token: AccessToken | undefined, tokenSlug: 
string) {
     const url = new URL(`private/tokenfamilies/${tokenSlug}`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1853,7 +1855,7 @@ export class TalerMerchantManagementHttpClient extends 
TalerMerchantInstanceHttp
   /**
    * https://docs.taler.net/core/api-merchant.html#get--management-instances
    */
-  async listInstances(token: AccessToken | undefined,) {
+  async listInstances(token: AccessToken | undefined, params?: 
PaginationParams) {
     const url = new URL(`management/instances`, this.baseUrl);
 
     const headers: Record<string, string> = {}

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