gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 03/03: backoffcie new version, lot of changes


From: gnunet
Subject: [taler-wallet-core] 03/03: backoffcie new version, lot of changes
Date: Mon, 04 Sep 2023 19:18:08 +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 e1d86816a7c07cb8ca2d54676d5cdbbe513f2ba7
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Sep 4 14:17:55 2023 -0300

    backoffcie new version, lot of changes
---
 .../merchant-backoffice-ui/src/Application.tsx     |  60 +--
 .../src/ApplicationReadyRoutes.tsx                 |  86 +++--
 .../merchant-backoffice-ui/src/InstanceRoutes.tsx  | 219 ++++++++---
 .../src/components/exception/login.tsx             |   4 +-
 .../src/components/form/InputDate.tsx              |  11 +-
 .../src/components/form/InputPaytoForm.tsx         | 305 ++++-----------
 ...nputSearchProduct.tsx => InputSearchOnList.tsx} |  97 +++--
 .../src/components/form/InputToggle.tsx            |   4 +-
 .../instance/DefaultInstanceFormFields.tsx         |  31 +-
 .../src/components/menu/SideBar.tsx                |  81 ++--
 .../src/components/menu/index.tsx                  |  58 ++-
 .../components/product/InventoryProductForm.tsx    |   8 +-
 .../src/components/product/ProductForm.tsx         |  18 +-
 .../merchant-backoffice-ui/src/context/backend.ts  |  52 +--
 .../merchant-backoffice-ui/src/declaration.d.ts    | 420 ++++++++++++---------
 .../merchant-backoffice-ui/src/hooks/backend.ts    | 111 +++---
 packages/merchant-backoffice-ui/src/hooks/bank.ts  | 217 +++++++++++
 packages/merchant-backoffice-ui/src/hooks/index.ts |  23 +-
 .../src/hooks/instance.test.ts                     |   6 +-
 .../merchant-backoffice-ui/src/hooks/instance.ts   |   5 +-
 packages/merchant-backoffice-ui/src/hooks/otp.ts   | 223 +++++++++++
 .../src/hooks/reserve.test.ts                      | 114 +++---
 .../merchant-backoffice-ui/src/hooks/reserves.ts   |  64 ++--
 packages/merchant-backoffice-ui/src/hooks/urls.ts  |  34 +-
 .../src/hooks/useSettings.ts                       |  37 +-
 .../src/paths/admin/create/CreatePage.tsx          |  96 ++---
 .../instance/accounts/create/Create.stories.tsx    |  28 ++
 .../paths/instance/accounts/create/CreatePage.tsx  | 175 +++++++++
 .../{transfers => accounts}/create/index.tsx       |  26 +-
 .../paths/instance/accounts/list/List.stories.tsx  |  28 ++
 .../src/paths/instance/accounts/list/ListPage.tsx  |  64 ++++
 .../src/paths/instance/accounts/list/Table.tsx     | 385 +++++++++++++++++++
 .../{templates => accounts}/list/index.tsx         |  38 +-
 .../{ => accounts}/update/Update.stories.tsx       |  31 +-
 .../paths/instance/accounts/update/UpdatePage.tsx  | 114 ++++++
 .../{templates/qr => accounts/update}/index.tsx    |  54 ++-
 .../src/paths/instance/details/DetailPage.tsx      |  10 +-
 .../src/paths/instance/details/stories.tsx         |   6 +-
 .../paths/instance/kyc/list/ListPage.stories.tsx   |   2 +-
 .../src/paths/instance/kyc/list/ListPage.tsx       |   6 +-
 .../instance/orders/create/Create.stories.tsx      |   7 +-
 .../paths/instance/orders/create/CreatePage.tsx    | 161 ++++++--
 .../src/paths/instance/orders/create/index.tsx     |   6 +-
 .../instance/orders/details/Detail.stories.tsx     |   2 -
 .../paths/instance/orders/details/DetailPage.tsx   |  28 +-
 .../src/paths/instance/orders/details/Timeline.tsx |   5 +-
 .../src/paths/instance/orders/list/ListPage.tsx    |  42 ++-
 .../src/paths/instance/orders/list/Table.tsx       |  27 +-
 .../src/paths/instance/orders/list/index.tsx       |  18 +-
 .../src/paths/instance/products/list/Table.tsx     |  62 +--
 .../src/paths/instance/products/list/index.tsx     |  48 ++-
 .../paths/instance/reserves/create/CreatePage.tsx  |  14 +-
 .../reserves/create/CreatedSuccessfully.tsx        |  22 +-
 .../src/paths/instance/reserves/create/index.tsx   |   8 +-
 .../paths/instance/reserves/details/DetailPage.tsx |  36 +-
 .../instance/reserves/details/Details.stories.tsx  |   6 +-
 .../details/{TipInfo.tsx => RewardInfo.tsx}        |  23 +-
 ...utorizeTipModal.tsx => AutorizeRewardModal.tsx} |  40 +-
 .../instance/reserves/list/CreatedSuccessfully.tsx |  18 +-
 .../paths/instance/reserves/list/List.stories.tsx  |   6 -
 .../src/paths/instance/reserves/list/Table.tsx     |  39 +-
 .../src/paths/instance/reserves/list/index.tsx     |  82 ++--
 .../paths/instance/templates/create/CreatePage.tsx | 127 ++-----
 .../src/paths/instance/templates/list/index.tsx    |  51 ++-
 .../src/paths/instance/templates/qr/QrPage.tsx     |  62 +--
 .../src/paths/instance/templates/qr/index.tsx      |   2 +-
 .../paths/instance/templates/update/UpdatePage.tsx | 121 +-----
 .../src/paths/instance/token/DetailPage.tsx        | 165 ++++++++
 .../src/paths/instance/token/index.tsx             |  90 +++++
 .../src/paths/instance/token/stories.tsx           |  28 ++
 .../src/paths/instance/transfers/create/index.tsx  |   3 +-
 .../src/paths/instance/transfers/list/Table.tsx    |  10 +-
 .../src/paths/instance/transfers/list/index.tsx    |   3 +-
 .../src/paths/instance/update/Update.stories.tsx   |   6 +-
 .../src/paths/instance/update/UpdatePage.tsx       | 118 ++----
 .../instance/validators/create/Create.stories.tsx  |  28 ++
 .../create/CreatePage.tsx                          | 149 ++------
 .../validators/create/CreatedSuccessfully.tsx      | 104 +++++
 .../{transfers => validators}/create/index.tsx     |  31 +-
 .../instance/validators/list/List.stories.tsx      |  28 ++
 .../paths/instance/validators/list/ListPage.tsx    |  64 ++++
 .../{webhooks => validators}/list/Table.tsx        |  46 +--
 .../{templates => validators}/list/index.tsx       |  37 +-
 .../{ => validators}/update/Update.stories.tsx     |  31 +-
 .../instance/validators/update/UpdatePage.tsx      | 185 +++++++++
 .../{templates/qr => validators/update}/index.tsx  |  56 ++-
 .../src/paths/instance/webhooks/list/Table.tsx     |   5 -
 .../src/paths/settings/index.tsx                   | 103 +++--
 .../merchant-backoffice-ui/src/schemas/index.ts    |   4 +-
 .../src/wallet/ProviderAddPage.tsx                 |   7 +-
 90 files changed, 3820 insertions(+), 1895 deletions(-)

diff --git a/packages/merchant-backoffice-ui/src/Application.tsx 
b/packages/merchant-backoffice-ui/src/Application.tsx
index f6a81ff8d..5e82821ae 100644
--- a/packages/merchant-backoffice-ui/src/Application.tsx
+++ b/packages/merchant-backoffice-ui/src/Application.tsx
@@ -19,19 +19,20 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { HttpStatusCode, LibtoolVersion } from "@gnu-taler/taler-util";
 import {
   ErrorType,
   TranslationProvider,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { route } from "preact-router";
-import { useMemo, useState } from "preact/hooks";
+import { useMemo } from "preact/hooks";
 import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes.js";
 import { Loading } from "./components/exception/loading.js";
 import {
-  NotificationCard,
-  NotYetReadyAppMenu,
+  NotConnectedAppMenu,
+  NotificationCard
 } from "./components/menu/index.js";
 import {
   BackendContextProvider,
@@ -41,23 +42,24 @@ import { ConfigContextProvider } from "./context/config.js";
 import { useBackendConfig } from "./hooks/backend.js";
 import { strings } from "./i18n/strings.js";
 import LoginPage from "./paths/login/index.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { Settings } from "./paths/settings/index.js";
 
 export function Application(): VNode {
   return (
-    // <FetchContextProvider>
     <BackendContextProvider>
       <TranslationProvider source={strings}>
         <ApplicationStatusRoutes />
       </TranslationProvider>
     </BackendContextProvider>
-    // </FetchContextProvider>
   );
 }
 
+/**
+ * Check connection testing against /config
+ * 
+ * @returns 
+ */
 function ApplicationStatusRoutes(): VNode {
-  const { updateLoginStatus, triedToLog } = useBackendContext();
+  const { url, updateLoginStatus, triedToLog } = useBackendContext();
   const result = useBackendConfig();
   const { i18n } = useTranslationContext();
 
@@ -71,19 +73,10 @@ function ApplicationStatusRoutes(): VNode {
     : { currency: "unknown", version: "unknown" };
   const ctx = useMemo(() => ({ currency, version }), [currency, version]);
 
-  const [showSettings, setShowSettings] = useState(false)
-
-  if (showSettings) {
-    return <Fragment>
-      <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} 
title="UI Settings" />
-      <Settings />
-    </Fragment>
-  }
-
   if (!triedToLog) {
     return (
       <Fragment>
-        <NotYetReadyAppMenu title="Welcome!" onShowSettings={() => 
setShowSettings(true)} />
+        <NotConnectedAppMenu title="Welcome!" />
         <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
       </Fragment>
     );
@@ -97,7 +90,7 @@ function ApplicationStatusRoutes(): VNode {
     ) {
       return (
         <Fragment>
-          <NotYetReadyAppMenu title="Login" onShowSettings={() => 
setShowSettings(true)} />
+          <NotConnectedAppMenu title="Login" />
           <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
         </Fragment>
       );
@@ -108,7 +101,7 @@ function ApplicationStatusRoutes(): VNode {
     ) {
       return (
         <Fragment>
-          <NotYetReadyAppMenu title="Error" onShowSettings={() => 
setShowSettings(true)} />
+          <NotConnectedAppMenu title="Error" />
           <NotificationCard
             notification={{
               message: i18n.str`Server not found`,
@@ -122,7 +115,7 @@ function ApplicationStatusRoutes(): VNode {
     }
     if (result.type === ErrorType.SERVER) {
       <Fragment>
-        <NotYetReadyAppMenu title="Error" onShowSettings={() => 
setShowSettings(true)} />
+        <NotConnectedAppMenu title="Error" />
         <NotificationCard
           notification={{
             message: i18n.str`Server response with an error code`,
@@ -135,7 +128,7 @@ function ApplicationStatusRoutes(): VNode {
     }
     if (result.type === ErrorType.UNREADABLE) {
       <Fragment>
-        <NotYetReadyAppMenu title="Error" onShowSettings={() => 
setShowSettings(true)} />
+        <NotConnectedAppMenu title="Error" />
         <NotificationCard
           notification={{
             message: i18n.str`Response from server is unreadable, http status: 
${result.status}`,
@@ -148,7 +141,7 @@ function ApplicationStatusRoutes(): VNode {
     }
     return (
       <Fragment>
-        <NotYetReadyAppMenu title="Error" onShowSettings={() => 
setShowSettings(true)} />
+        <NotConnectedAppMenu title="Error" />
         <NotificationCard
           notification={{
             message: i18n.str`Unexpected Error`,
@@ -161,6 +154,25 @@ function ApplicationStatusRoutes(): VNode {
     );
   }
 
+  const SUPPORTED_VERSION = "5:0:1"
+  if (!LibtoolVersion.compare(
+    SUPPORTED_VERSION,
+    result.data.version,
+  )?.compatible) {
+    return <Fragment>
+      <NotConnectedAppMenu title="Error" />
+      <NotificationCard
+        notification={{
+          message: i18n.str`Incompatible version`,
+          type: "ERROR",
+          description: i18n.str`Merchant backend server version 
${result.data.version} is not compatible with the supported version 
${SUPPORTED_VERSION}`,
+        }}
+      />
+      <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
+    </Fragment>
+
+  }
+
   return (
     <div class="has-navbar-fixed-top">
       <ConfigContextProvider value={ctx}>
diff --git a/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx 
b/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx
index 277c2b176..46dea98e3 100644
--- a/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx
+++ b/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx
@@ -22,7 +22,7 @@ import { ErrorType, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { createHashHistory } from "history";
 import { Fragment, h, VNode } from "preact";
 import { Router, Route, route } from "preact-router";
-import { useState } from "preact/hooks";
+import { useEffect, useState } from "preact/hooks";
 import {
   NotificationCard,
   NotYetReadyAppMenu,
@@ -35,52 +35,55 @@ import { INSTANCE_ID_LOOKUP } from "./utils/constants.js";
 import { HttpStatusCode } from "@gnu-taler/taler-util";
 import { Settings } from "./paths/settings/index.js";
 
+/**
+ * Check if admin against /management/instances
+ * @returns 
+ */
 export function ApplicationReadyRoutes(): VNode {
   const { i18n } = useTranslationContext();
+  const [unauthorized, setUnauthorized] = useState(false)
   const {
     url: backendURL,
-    updateLoginStatus,
-    clearAllTokens,
+    updateLoginStatus: updateLoginStatus2,
   } = useBackendContext();
 
+  function updateLoginStatus(url: string, token: string | undefined) {
+    console.log("updateing", url, token)
+    updateLoginStatus2(url, token)
+    setUnauthorized(false)
+  }
+
   const result = useBackendInstancesTestForAdmin();
 
   const clearTokenAndGoToRoot = () => {
-    clearAllTokens();
     route("/");
   };
   const [showSettings, setShowSettings] = useState(false)
+  // useEffect(() => {
+  //   setUnauthorized(FF)
+  // }, [FF])
+  const unauthorizedAdmin = !result.loading && !result.ok && result.type === 
ErrorType.CLIENT && result.status === HttpStatusCode.Unauthorized
 
   if (showSettings) {
     return <Fragment>
-      <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} 
title="UI Settings" onLogout={clearTokenAndGoToRoot} />
-      <Settings/>
+      <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} 
title="UI Settings" onLogout={clearTokenAndGoToRoot} isPasswordOk={false} />
+      <Settings />
     </Fragment>
   }
-  if (result.loading) return <NotYetReadyAppMenu onShowSettings={() => 
setShowSettings(true)} title="Loading..." />;
 
-  let admin = true;
-  let instanceNameByBackendURL;
+  if (result.loading) {
+    return <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} 
title="Loading..." isPasswordOk={false} />;
+  }
 
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    ) {
-      return (
-        <Fragment>
-          <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} 
title="Login" onLogout={clearTokenAndGoToRoot} />
-          <NotificationCard
-            notification={{
-              message: i18n.str`Access denied`,
-              description: i18n.str`Check your token is valid`,
-              type: "ERROR",
-            }}
-          />
-          <LoginPage onConfirm={updateLoginStatus} />
-        </Fragment>
-      );
-    }
+  let admin = result.ok || unauthorizedAdmin;
+  let instanceNameByBackendURL: string | undefined;
+
+  if (!admin) {
+    // * the testing against admin endpoint failed and it's not 
+    // an authorization problem
+    // * merchant backend will return this SPA under the main
+    // endpoint or /instance/<id> endpoint
+    // => trying to infer the instance id
     const path = new URL(backendURL).pathname;
     const match = INSTANCE_ID_LOOKUP.exec(path);
     if (!match || !match[1]) {
@@ -89,7 +92,7 @@ export function ApplicationReadyRoutes(): VNode {
       // does not match our pattern
       return (
         <Fragment>
-          <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} 
title="Error" onLogout={clearTokenAndGoToRoot} />
+          <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} 
title="Error" onLogout={clearTokenAndGoToRoot} isPasswordOk={false} />
           <NotificationCard
             notification={{
               message: i18n.str`Couldn't access the server.`,
@@ -102,10 +105,24 @@ export function ApplicationReadyRoutes(): VNode {
       );
     }
 
-    admin = false;
     instanceNameByBackendURL = match[1];
   }
 
+  console.log(unauthorized, unauthorizedAdmin)
+  if (unauthorized || unauthorizedAdmin) {
+    return <Fragment>
+      <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} 
title="Login" onLogout={clearTokenAndGoToRoot} isPasswordOk={false} />
+      <NotificationCard
+        notification={{
+          message: i18n.str`Access denied`,
+          description: i18n.str`Check your token is valid`,
+          type: "ERROR",
+        }}
+      />
+      <LoginPage onConfirm={updateLoginStatus} />
+    </Fragment>
+  }
+
   const history = createHashHistory();
   return (
     <Router history={history}>
@@ -113,6 +130,11 @@ export function ApplicationReadyRoutes(): VNode {
         default
         component={DefaultMainRoute}
         admin={admin}
+        onUnauthorized={() => setUnauthorized(true)}
+        onLoginPass={() => {
+          console.log("ahora si")
+          setUnauthorized(false)
+        }}
         instanceNameByBackendURL={instanceNameByBackendURL}
       />
     </Router>
@@ -122,6 +144,8 @@ export function ApplicationReadyRoutes(): VNode {
 function DefaultMainRoute({
   instance,
   admin,
+  onUnauthorized,
+  onLoginPass,
   instanceNameByBackendURL,
   url, //from preact-router
 }: any): VNode {
@@ -133,6 +157,8 @@ function DefaultMainRoute({
     <InstanceRoutes
       admin={admin}
       path={url}
+      onUnauthorized={onUnauthorized}
+      onLoginPass={onLoginPass}
       id={instanceName}
       setInstanceName={setInstanceName}
     />
diff --git a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx 
b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
index 1547442ea..4a4b3fee4 100644
--- a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
+++ b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
@@ -40,6 +40,7 @@ import {
 import { useInstanceKYCDetails } from "./hooks/instance.js";
 import InstanceCreatePage from "./paths/admin/create/index.js";
 import InstanceListPage from "./paths/admin/list/index.js";
+import TokenPage from "./paths/instance/token/index.js";
 import ListKYCPage from "./paths/instance/kyc/list/index.js";
 import OrderCreatePage from "./paths/instance/orders/create/index.js";
 import OrderDetailsPage from "./paths/instance/orders/details/index.js";
@@ -47,6 +48,9 @@ import OrderListPage from 
"./paths/instance/orders/list/index.js";
 import ProductCreatePage from "./paths/instance/products/create/index.js";
 import ProductListPage from "./paths/instance/products/list/index.js";
 import ProductUpdatePage from "./paths/instance/products/update/index.js";
+import BankAccountCreatePage from "./paths/instance/accounts/create/index.js";
+import BankAccountListPage from "./paths/instance/accounts/list/index.js";
+import BankAccountUpdatePage from "./paths/instance/accounts/update/index.js";
 import ReservesCreatePage from "./paths/instance/reserves/create/index.js";
 import ReservesDetailsPage from "./paths/instance/reserves/details/index.js";
 import ReservesListPage from "./paths/instance/reserves/list/index.js";
@@ -58,6 +62,9 @@ import TemplateUpdatePage from 
"./paths/instance/templates/update/index.js";
 import WebhookCreatePage from "./paths/instance/webhooks/create/index.js";
 import WebhookListPage from "./paths/instance/webhooks/list/index.js";
 import WebhookUpdatePage from "./paths/instance/webhooks/update/index.js";
+import ValidatorCreatePage from "./paths/instance/validators/create/index.js";
+import ValidatorListPage from "./paths/instance/validators/list/index.js";
+import ValidatorUpdatePage from "./paths/instance/validators/update/index.js";
 import TransferCreatePage from "./paths/instance/transfers/create/index.js";
 import TransferListPage from "./paths/instance/transfers/list/index.js";
 import InstanceUpdatePage, {
@@ -69,11 +76,16 @@ import NotFoundPage from "./paths/notfound/index.js";
 import { Notification } from "./utils/types.js";
 import { MerchantBackend } from "./declaration.js";
 import { Settings } from "./paths/settings/index.js";
+import { dateFormatForSettings, useSettings } from "./hooks/useSettings.js";
 
 export enum InstancePaths {
-  // details = '/',
   error = "/error",
-  update = "/update",
+  server = "/server",
+  token = "/token",
+
+  bank_list = "/bank",
+  bank_update = "/bank/:bid/update",
+  bank_new = "/bank/new",
 
   product_list = "/products",
   product_update = "/product/:pid/update",
@@ -102,11 +114,15 @@ export enum InstancePaths {
   webhooks_update = "/webhooks/:tid/update",
   webhooks_new = "/webhooks/new",
 
-  settings = "/settings",
+  validators_list = "/validators",
+  validators_update = "/validators/:vid/update",
+  validators_new = "/validators/new",
+
+  settings = "/inteface",
 }
 
 // eslint-disable-next-line @typescript-eslint/no-empty-function
-const noop = () => {};
+const noop = () => { };
 
 export enum AdminPaths {
   list_instances = "/instances",
@@ -118,6 +134,8 @@ export interface Props {
   id: string;
   admin?: boolean;
   path: string;
+  onUnauthorized: () => void;
+  onLoginPass: () => void;
   setInstanceName: (s: string) => void;
 }
 
@@ -125,40 +143,29 @@ export function InstanceRoutes({
   id,
   admin,
   path,
+  onUnauthorized,
+  onLoginPass,
   setInstanceName,
 }: Props): VNode {
-  const [_, updateDefaultToken] = useBackendDefaultToken();
+  const [defaultToken, updateDefaultToken] = useBackendDefaultToken();
   const [token, updateToken] = useBackendInstanceToken(id);
-  const {
-    updateLoginStatus: changeBackend,
-    addTokenCleaner,
-    clearAllTokens,
-  } = useBackendContext();
-  const cleaner = useCallback(() => {
-    updateToken(undefined);
-  }, [id]);
   const { i18n } = useTranslationContext();
 
   type GlobalNotifState = (Notification & { to: string }) | undefined;
   const [globalNotification, setGlobalNotification] =
     useState<GlobalNotifState>(undefined);
 
-  useEffect(() => {
-    addTokenCleaner(cleaner);
-  }, [addTokenCleaner, cleaner]);
-
   const changeToken = (token?: string) => {
     if (admin) {
       updateToken(token);
     } else {
       updateDefaultToken(token);
     }
+    onLoginPass()
   };
-  const updateLoginStatus = (url: string, token?: string) => {
-    changeBackend(url);
-    if (!token) return;
-    changeToken(token);
-  };
+  // const updateLoginStatus = (url: string, token?: string) => {
+  //   changeToken(token);
+  // };
 
   const value = useMemo(
     () => ({ id, token, admin, changeToken }),
@@ -192,18 +199,17 @@ export function InstanceRoutes({
     };
   }
 
-  const LoginPageAccessDenied = () => (
-    <Fragment>
-      <NotificationCard
-        notification={{
-          message: i18n.str`Access denied`,
-          description: i18n.str`The access token provided is invalid.`,
-          type: "ERROR",
-        }}
-      />
-      <LoginPage onConfirm={updateLoginStatus} />
-    </Fragment>
-  );
+  // const LoginPageAccessDeniend = onUnauthorized
+  const LoginPageAccessDenied = () => {
+    onUnauthorized()
+    return <NotificationCard
+      notification={{
+        message: i18n.str`Access denied`,
+        description: i18n.str`Redirecting to login page.`,
+        type: "ERROR",
+      }}
+    />
+  }
 
   function IfAdminCreateDefaultOr<T>(Next: FunctionComponent<any>) {
     return function IfAdminCreateDefaultOrImpl(props?: T) {
@@ -234,8 +240,10 @@ export function InstanceRoutes({
   }
 
   const clearTokenAndGoToRoot = () => {
-    clearAllTokens();
     route("/");
+    // clear all tokens
+    updateToken(undefined)
+    updateDefaultToken(undefined)
   };
 
   return (
@@ -244,11 +252,12 @@ export function InstanceRoutes({
         instance={id}
         admin={admin}
         onShowSettings={() => {
-          route("/settings")
+          route("/inteface")
         }}
         path={path}
         onLogout={clearTokenAndGoToRoot}
         setInstanceName={setInstanceName}
+        isPasswordOk={defaultToken !== undefined}
       />
       <KycBanner />
       <NotificationCard notification={globalNotification} />
@@ -308,7 +317,7 @@ export function InstanceRoutes({
          * Update instance page
          */}
         <Route
-          path={InstancePaths.update}
+          path={InstancePaths.server}
           component={InstanceUpdatePage}
           onBack={() => {
             route(`/`);
@@ -321,6 +330,19 @@ export function InstanceRoutes({
           onUnauthorized={LoginPageAccessDenied}
           onLoadError={ServerErrorRedirectTo(InstancePaths.error)}
         />
+        {/**
+         * Update instance page
+         */}
+        <Route
+          path={InstancePaths.token}
+          component={TokenPage}
+          onChange={() => {
+            route(`/`);
+          }}
+          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          onUnauthorized={LoginPageAccessDenied}
+          onLoadError={ServerErrorRedirectTo(InstancePaths.error)}
+        />
         {/**
          * Product pages
          */}
@@ -328,7 +350,7 @@ export function InstanceRoutes({
           path={InstancePaths.product_list}
           component={ProductListPage}
           onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.update)}
+          onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
           onCreate={() => {
             route(InstancePaths.product_new);
           }}
@@ -360,6 +382,45 @@ export function InstanceRoutes({
             route(InstancePaths.product_list);
           }}
         />
+        {/**
+         * Bank pages
+         */}
+        <Route
+          path={InstancePaths.bank_list}
+          component={BankAccountListPage}
+          onUnauthorized={LoginPageAccessDenied}
+          onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
+          onCreate={() => {
+            route(InstancePaths.bank_new);
+          }}
+          onSelect={(id: string) => {
+            route(InstancePaths.bank_update.replace(":bid", id));
+          }}
+          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+        />
+        <Route
+          path={InstancePaths.bank_update}
+          component={BankAccountUpdatePage}
+          onUnauthorized={LoginPageAccessDenied}
+          onLoadError={ServerErrorRedirectTo(InstancePaths.product_list)}
+          onConfirm={() => {
+            route(InstancePaths.bank_list);
+          }}
+          onBack={() => {
+            route(InstancePaths.bank_list);
+          }}
+          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+        />
+        <Route
+          path={InstancePaths.bank_new}
+          component={BankAccountCreatePage}
+          onConfirm={() => {
+            route(InstancePaths.bank_list);
+          }}
+          onBack={() => {
+            route(InstancePaths.bank_list);
+          }}
+        />
         {/**
          * Order pages
          */}
@@ -373,7 +434,7 @@ export function InstanceRoutes({
             route(InstancePaths.order_details.replace(":oid", id));
           }}
           onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.update)}
+          onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
           onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
         />
         <Route
@@ -389,8 +450,8 @@ export function InstanceRoutes({
         <Route
           path={InstancePaths.order_new}
           component={OrderCreatePage}
-          onConfirm={() => {
-            route(InstancePaths.order_list);
+          onConfirm={(orderId: string) => {
+            route(InstancePaths.order_details.replace(":oid", orderId));
           }}
           onBack={() => {
             route(InstancePaths.order_list);
@@ -404,7 +465,7 @@ export function InstanceRoutes({
           component={TransferListPage}
           onUnauthorized={LoginPageAccessDenied}
           onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.update)}
+          onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
           onCreate={() => {
             route(InstancePaths.transfers_new);
           }}
@@ -427,7 +488,7 @@ export function InstanceRoutes({
           component={WebhookListPage}
           onUnauthorized={LoginPageAccessDenied}
           onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.update)}
+          onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
           onCreate={() => {
             route(InstancePaths.webhooks_new);
           }}
@@ -458,6 +519,45 @@ export function InstanceRoutes({
             route(InstancePaths.webhooks_list);
           }}
         />
+        {/**
+         * Validator pages
+         */}
+        <Route
+          path={InstancePaths.validators_list}
+          component={ValidatorListPage}
+          onUnauthorized={LoginPageAccessDenied}
+          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
+          onCreate={() => {
+            route(InstancePaths.validators_new);
+          }}
+          onSelect={(id: string) => {
+            route(InstancePaths.validators_update.replace(":vid", id));
+          }}
+        />
+        <Route
+          path={InstancePaths.validators_update}
+          component={ValidatorUpdatePage}
+          onConfirm={() => {
+            route(InstancePaths.validators_list);
+          }}
+          onUnauthorized={LoginPageAccessDenied}
+          onLoadError={ServerErrorRedirectTo(InstancePaths.validators_list)}
+          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          onBack={() => {
+            route(InstancePaths.validators_list);
+          }}
+        />
+        <Route
+          path={InstancePaths.validators_new}
+          component={ValidatorCreatePage}
+          onConfirm={() => {
+            route(InstancePaths.validators_list);
+          }}
+          onBack={() => {
+            route(InstancePaths.validators_list);
+          }}
+        />
         {/**
          * Templates pages
          */}
@@ -466,7 +566,7 @@ export function InstanceRoutes({
           component={TemplateListPage}
           onUnauthorized={LoginPageAccessDenied}
           onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.update)}
+          onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
           onCreate={() => {
             route(InstancePaths.templates_new);
           }}
@@ -535,7 +635,7 @@ export function InstanceRoutes({
           component={ReservesListPage}
           onUnauthorized={LoginPageAccessDenied}
           onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.update)}
+          onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
           onSelect={(id: string) => {
             route(InstancePaths.reserves_details.replace(":rid", id));
           }}
@@ -590,7 +690,7 @@ function AdminInstanceUpdatePage({
   const { updateLoginStatus: changeBackend } = useBackendContext();
   const updateLoginStatus = (url: string, token?: string): void => {
     changeBackend(url);
-    if (token) changeToken(token);
+    changeToken(token);
   };
   const value = useMemo(
     () => ({ id, token, admin: true, changeToken }),
@@ -607,20 +707,20 @@ function AdminInstanceUpdatePage({
           const notif =
             error.type === ErrorType.TIMEOUT
               ? {
-                  message: i18n.str`The request to the backend take too long 
and was cancelled`,
-                  description: i18n.str`Diagnostic from ${error.info.url} is 
'${error.message}'`,
-                  type: "ERROR" as const,
-                }
+                message: i18n.str`The request to the backend take too long and 
was cancelled`,
+                description: i18n.str`Diagnostic from ${error.info.url} is 
'${error.message}'`,
+                type: "ERROR" as const,
+              }
               : {
-                  message: i18n.str`The backend reported a problem: HTTP 
status #${error.status}`,
-                  description: i18n.str`Diagnostic from ${error.info.url} is 
'${error.message}'`,
-                  details:
-                    error.type === ErrorType.CLIENT ||
+                message: i18n.str`The backend reported a problem: HTTP status 
#${error.status}`,
+                description: i18n.str`Diagnostic from ${error.info.url} is 
'${error.message}'`,
+                details:
+                  error.type === ErrorType.CLIENT ||
                     error.type === ErrorType.SERVER
-                      ? error.payload.detail
-                      : undefined,
-                  type: "ERROR" as const,
-                };
+                    ? error.payload.detail
+                    : undefined,
+                type: "ERROR" as const,
+              };
           return (
             <Fragment>
               <NotificationCard notification={notif} />
@@ -650,7 +750,8 @@ function AdminInstanceUpdatePage({
 function KycBanner(): VNode {
   const kycStatus = useInstanceKYCDetails();
   const { i18n } = useTranslationContext();
-  const today = format(new Date(), "yyyy-MM-dd");
+  const [settings] = useSettings();
+  const today = format(new Date(), dateFormatForSettings(settings));
   const [lastHide, setLastHide] = useLocalStorage("kyc-last-hide");
   const hasBeenHidden = today === lastHide;
   const needsToBeShown = kycStatus.ok && kycStatus.data.type === "redirect";
diff --git a/packages/merchant-backoffice-ui/src/components/exception/login.tsx 
b/packages/merchant-backoffice-ui/src/components/exception/login.tsx
index f2f94a7c5..4fa440fc7 100644
--- a/packages/merchant-backoffice-ui/src/components/exception/login.tsx
+++ b/packages/merchant-backoffice-ui/src/components/exception/login.tsx
@@ -93,7 +93,7 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
                     <input
                       class="input"
                       type="password"
-                      placeholder={"set new access token"}
+                      placeholder={"current access token"}
                       name="token"
                       onKeyPress={(e) =>
                         e.keyCode === 13
@@ -186,7 +186,7 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
                     <input
                       class="input"
                       type="password"
-                      placeholder={"set new access token"}
+                      placeholder={"current access token"}
                       name="token"
                       onKeyPress={(e) =>
                         e.keyCode === 13
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx
index 1f41c3564..a398629dc 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx
@@ -20,16 +20,18 @@
  */
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { format } from "date-fns";
-import { h, VNode } from "preact";
+import { ComponentChildren, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { DatePicker } from "../picker/DatePicker.js";
 import { InputProps, useField } from "./useField.js";
+import { dateFormatForSettings, useSettings } from 
"../../hooks/useSettings.js";
 
 export interface Props<T> extends InputProps<T> {
   readonly?: boolean;
   expand?: boolean;
   //FIXME: create separated components InputDate and InputTimestamp
   withTimestampSupport?: boolean;
+  side?: ComponentChildren;
 }
 
 export function InputDate<T>({
@@ -41,9 +43,11 @@ export function InputDate<T>({
   tooltip,
   expand,
   withTimestampSupport,
+  side,
 }: Props<keyof T>): VNode {
   const [opened, setOpened] = useState(false);
   const { i18n } = useTranslationContext();
+  const [settings] = useSettings()
 
   const { error, required, value, onChange } = useField<T>(name);
 
@@ -51,14 +55,14 @@ export function InputDate<T>({
   if (!value) {
     strValue = withTimestampSupport ? "unknown" : "";
   } else if (value instanceof Date) {
-    strValue = format(value, "yyyy/MM/dd");
+    strValue = format(value, dateFormatForSettings(settings));
   } else if (value.t_s) {
     strValue =
       value.t_s === "never"
         ? withTimestampSupport
           ? "never"
           : ""
-        : format(new Date(value.t_s * 1000), "yyyy/MM/dd");
+        : format(new Date(value.t_s * 1000), dateFormatForSettings(settings));
   }
 
   return (
@@ -142,6 +146,7 @@ export function InputDate<T>({
             </button>
           </span>
         )}
+        {side}
       </div>
       <DatePicker
         opened={opened}
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx
index 8d324660e..5cd69a0b3 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx
@@ -18,9 +18,9 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
+import { parsePaytoUri, PaytoUriGeneric, stringifyPaytoUri } from 
"@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, h, VNode } from "preact";
-import { useCallback, useState } from "preact/hooks";
 import { COUNTRY_TABLE } from "../../utils/constants.js";
 import { undefinedIfEmpty } from "../../utils/table.js";
 import { FormErrors, FormProvider } from "./FormProvider.js";
@@ -28,23 +28,23 @@ import { Input } from "./Input.js";
 import { InputGroup } from "./InputGroup.js";
 import { InputSelector } from "./InputSelector.js";
 import { InputProps, useField } from "./useField.js";
-import { InputWithAddon } from "./InputWithAddon.js";
-import { MerchantBackend } from "../../declaration.js";
+import { useEffect, useState } from "preact/hooks";
 
 export interface Props<T> extends InputProps<T> {
   isValid?: (e: any) => boolean;
 }
 
+// type Entity = PaytoUriGeneric
 // https://datatracker.ietf.org/doc/html/rfc8905
 type Entity = {
   // iban, bitcoin, x-taler-bank. it defined the format
   target: string;
   // path1 if the first field to be used
-  path1: string;
+  path1?: string;
   // path2 if the second field to be used, optional
   path2?: string;
-  // options of the payto uri
-  options: {
+  // params of the payto uri
+  params: {
     "receiver-name"?: string;
     sender?: string;
     message?: string;
@@ -52,13 +52,6 @@ type Entity = {
     instruction?: string;
     [name: string]: string | undefined;
   };
-  auth: {
-    type: "unset" | "basic" | "none";
-    url?: string;
-    username?: string;
-    password?: string;
-    repeat?: string;
-  };
 };
 
 function isEthereumAddress(address: string) {
@@ -171,14 +164,10 @@ const targets = [
   "bitcoin",
   "ethereum",
 ];
-const accountAuthType = ["none", "basic"];
 const noTargetValue = targets[0];
-const defaultTarget: Partial<Entity> = {
+const defaultTarget: Entity = {
   target: noTargetValue,
-  options: {},
-  auth: {
-    type: "unset" as const,
-  },
+  params: {},
 };
 
 export function InputPaytoForm<T>({
@@ -187,110 +176,91 @@ export function InputPaytoForm<T>({
   label,
   tooltip,
 }: Props<keyof T>): VNode {
-  const { value: paytos, onChange, required } = useField<T>(name);
-
-  const [value, valueHandler] = useState<Partial<Entity>>(defaultTarget);
+  const { value: initialValueStr, onChange } = useField<T>(name);
 
-  let payToPath;
-  if (value.target === "iban" && value.path1) {
-    payToPath = `/${value.path1.toUpperCase()}`;
-  } else if (value.path1) {
-    if (value.path2) {
-      payToPath = `/${value.path1}/${value.path2}`;
-    } else {
-      payToPath = `/${value.path1}`;
-    }
+  const initialPayto = parsePaytoUri(initialValueStr ?? "")
+  const paths = !initialPayto ? [] : initialPayto.targetPath.split("/")
+  const initialPath1 = paths.length >= 1 ? paths[0] : undefined;
+  const initialPath2 = paths.length >= 2 ? paths[1] : undefined;
+  const initial: Entity = initialPayto === undefined ? defaultTarget : {
+    target: initialPayto.targetType,
+    params: initialPayto.params,
+    path1: initialPath1,
+    path2: initialPath2,
   }
-  const { i18n } = useTranslationContext();
+  const [value, setValue] = useState<Partial<Entity>>(initial)
 
-  const ops = value.options ?? {};
-  const url = tryUrl(`payto://${value.target}${payToPath}`);
-  if (url) {
-    Object.keys(ops).forEach((opt_key) => {
-      const opt_value = ops[opt_key];
-      if (opt_value) url.searchParams.set(opt_key, opt_value);
-    });
-  }
-  const paytoURL = !url ? "" : url.href;
+  const { i18n } = useTranslationContext();
 
   const errors: FormErrors<Entity> = {
     target:
-      value.target === noTargetValue && !paytos.length
+      value.target === noTargetValue
         ? i18n.str`required`
         : undefined,
     path1: !value.path1
       ? i18n.str`required`
       : value.target === "iban"
-      ? validateIBAN(value.path1, i18n)
-      : value.target === "bitcoin"
-      ? validateBitcoin(value.path1, i18n)
-      : value.target === "ethereum"
-      ? validateEthereum(value.path1, i18n)
-      : undefined,
+        ? validateIBAN(value.path1, i18n)
+        : value.target === "bitcoin"
+          ? validateBitcoin(value.path1, i18n)
+          : value.target === "ethereum"
+            ? validateEthereum(value.path1, i18n)
+            : undefined,
     path2:
       value.target === "x-taler-bank"
         ? !value.path2
           ? i18n.str`required`
           : undefined
         : undefined,
-    options: undefinedIfEmpty({
-      "receiver-name": !value.options?.["receiver-name"]
+    params: undefinedIfEmpty({
+      "receiver-name": !value.params?.["receiver-name"]
         ? i18n.str`required`
         : undefined,
     }),
-    auth: !value.auth
-      ? undefined
-      : undefinedIfEmpty({
-          username:
-            value.auth.type === "basic" && !value.auth.username
-              ? i18n.str`required`
-              : undefined,
-          password:
-            value.auth.type === "basic" && !value.auth.password
-              ? i18n.str`required`
-              : undefined,
-          repeat:
-            value.auth.type === "basic" && !value.auth.repeat
-              ? i18n.str`required`
-              : value.auth.repeat !== value.auth.password
-              ? i18n.str`is not the same`
-              : undefined,
-        }),
   };
 
   const hasErrors = Object.keys(errors).some(
     (k) => (errors as any)[k] !== undefined,
   );
+  const str = hasErrors || !value.target ? undefined : stringifyPaytoUri({
+    targetType: value.target,
+    targetPath: value.path2 ? `${value.path1}/${value.path2}` : (value.path1 
?? ""),
+    params: value.params ?? {} as any,
+    isKnown: false,
+  })
+  useEffect(() => {
+    onChange(str as any)
+  }, [str])
 
-  const submit = useCallback((): void => {
-    const accounts: MerchantBackend.Instances.MerchantBankAccount[] = paytos;
-    const alreadyExists =
-      accounts.findIndex((x) => x.payto_uri === paytoURL) !== -1;
-    if (!alreadyExists) {
-      const newValue: MerchantBackend.Instances.MerchantBankAccount = {
-        payto_uri: paytoURL,
-      };
-      if (value.auth) {
-        if (value.auth.url) {
-          newValue.credit_facade_url = value.auth.url;
-        }
-        if (value.auth.type === "none") {
-          newValue.credit_facade_credentials = {
-            type: "none",
-          };
-        }
-        if (value.auth.type === "basic") {
-          newValue.credit_facade_credentials = {
-            type: "basic",
-            username: value.auth.username ?? "",
-            password: value.auth.password ?? "",
-          };
-        }
-      }
-      onChange([newValue, ...accounts] as any);
-    }
-    valueHandler(defaultTarget);
-  }, [value]);
+  // const submit = useCallback((): void => {
+  //   // const accounts: MerchantBackend.BankAccounts.AccountAddDetails[] = 
paytos;
+  //   // const alreadyExists =
+  //   //   accounts.findIndex((x) => x.payto_uri === paytoURL) !== -1;
+  //   // if (!alreadyExists) {
+  //   const newValue: MerchantBackend.BankAccounts.AccountAddDetails = {
+  //     payto_uri: paytoURL,
+  //   };
+  //   if (value.auth) {
+  //     if (value.auth.url) {
+  //       newValue.credit_facade_url = value.auth.url;
+  //     }
+  //     if (value.auth.type === "none") {
+  //       newValue.credit_facade_credentials = {
+  //         type: "none",
+  //       };
+  //     }
+  //     if (value.auth.type === "basic") {
+  //       newValue.credit_facade_credentials = {
+  //         type: "basic",
+  //         username: value.auth.username ?? "",
+  //         password: value.auth.password ?? "",
+  //       };
+  //     }
+  //   }
+  //   onChange(newValue as any);
+  //   // }
+  //   // valueHandler(defaultTarget);
+  // }, [value]);
 
   //FIXME: translating plural singular
   return (
@@ -299,11 +269,11 @@ export function InputPaytoForm<T>({
         name="tax"
         errors={errors}
         object={value}
-        valueHandler={valueHandler}
+        valueHandler={setValue}
       >
         <InputSelector<Entity>
           name="target"
-          label={i18n.str`Target type`}
+          label={i18n.str`Account type`}
           tooltip={i18n.str`Method to use for wire transfer`}
           values={targets}
           toStr={(v) => (v === noTargetValue ? i18n.str`Choose one...` : v)}
@@ -400,150 +370,15 @@ export function InputPaytoForm<T>({
         {value.target !== noTargetValue && (
           <Fragment>
             <Input
-              name="options.receiver-name"
+              name="params.receiver-name"
               label={i18n.str`Name`}
               tooltip={i18n.str`Bank account owner's name.`}
             />
-            <InputWithAddon
-              name="auth.url"
-              label={i18n.str`Account info URL`}
-              help="https://bank.com";
-              expand
-              tooltip={i18n.str`From where the merchant can download 
information about incoming wire transfers to this account`}
-            />
-            <InputSelector
-              name="auth.type"
-              label={i18n.str`Auth type`}
-              tooltip={i18n.str`Choose the authentication type for the account 
info URL`}
-              values={accountAuthType}
-              toStr={(str) => {
-                // if (str === "unset") {
-                //   return "Without change";
-                // }
-                if (str === "none") return "Without authentication";
-                return "Username and password";
-              }}
-            />
-            {value.auth?.type === "basic" ? (
-              <Fragment>
-                <Input
-                  name="auth.username"
-                  label={i18n.str`Username`}
-                  tooltip={i18n.str`Username to access the account 
information.`}
-                />
-                <Input
-                  name="auth.password"
-                  inputType="password"
-                  label={i18n.str`Password`}
-                  tooltip={i18n.str`Password to access the account 
information.`}
-                />
-                <Input
-                  name="auth.repeat"
-                  inputType="password"
-                  label={i18n.str`Repeat password`}
-                />
-              </Fragment>
-            ) : undefined}
-
-            {/* <InputWithAddon
-              name="options.credit_credentials"
-              label={i18n.str`Account info`}
-              inputType={showKey ? "text" : "password"}
-              help="From where the merchant can download information about 
incoming wire transfers to this account"
-              expand
-              tooltip={i18n.str`Useful to validate the purchase`}
-              fromStr={(v) => v.toUpperCase()}
-              addonAfter={
-                <span class="icon">
-                  {showKey ? (
-                    <i class="mdi mdi-eye" />
-                  ) : (
-                    <i class="mdi mdi-eye-off" />
-                  )}
-                </span>
-              }
-              side={
-                <span style={{ display: "flex" }}>
-                  <button
-                    data-tooltip={
-                      showKey
-                        ? i18n.str`show secret key`
-                        : i18n.str`hide secret key`
-                    }
-                    class="button is-info mr-3"
-                    onClick={(e) => {
-                      setShowKey(!showKey);
-                    }}
-                  >
-                    {showKey ? (
-                      <i18n.Translate>hide</i18n.Translate>
-                    ) : (
-                      <i18n.Translate>show</i18n.Translate>
-                    )}
-                  </button>
-                </span>
-              }
-            /> */}
           </Fragment>
         )}
-        {/**
-         * Show the values in the list
-         */}
-        <div class="field is-horizontal">
-          <div class="field-label is-normal" />
-          <div class="field-body" style={{ display: "block" }}>
-            {paytos.map(
-              (v: MerchantBackend.Instances.MerchantBankAccount, i: number) => 
(
-                <div
-                  key={i}
-                  class="tags has-addons mt-3 mb-0 mr-3"
-                  style={{ flexWrap: "nowrap" }}
-                >
-                  <span
-                    class="tag is-medium is-info mb-0"
-                    style={{ maxWidth: "90%" }}
-                  >
-                    {v.payto_uri}
-                  </span>
-                  <a
-                    class="tag is-medium is-danger is-delete mb-0"
-                    onClick={() => {
-                      onChange(paytos.filter((f: any) => f !== v) as any);
-                    }}
-                  />
-                </div>
-              ),
-            )}
-            {!paytos.length && i18n.str`No accounts yet.`}
-            {required && (
-              <span class="icon has-text-danger is-right">
-                <i class="mdi mdi-alert" />
-              </span>
-            )}
-          </div>
-        </div>
 
-        {value.target !== noTargetValue && (
-          <div class="buttons is-right mt-5">
-            <button
-              class="button is-info"
-              data-tooltip={i18n.str`add tax to the tax list`}
-              disabled={hasErrors}
-              onClick={submit}
-            >
-              <i18n.Translate>Add</i18n.Translate>
-            </button>
-          </div>
-        )}
       </FormProvider>
     </InputGroup>
   );
 }
 
-function tryUrl(s: string): URL | undefined {
-  try {
-    return new URL(s);
-  } catch (e) {
-    return undefined;
-  }
-}
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputSearchOnList.tsx
similarity index 65%
rename from 
packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx
rename to 
packages/merchant-backoffice-ui/src/components/form/InputSearchOnList.tsx
index 1c1fcb907..be5800d14 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputSearchOnList.tsx
@@ -22,32 +22,41 @@ import { useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import emptyImage from "../../assets/empty.png";
-import { MerchantBackend, WithId } from "../../declaration.js";
 import { FormErrors, FormProvider } from "./FormProvider.js";
 import { InputWithAddon } from "./InputWithAddon.js";
+import { TranslatedString } from "@gnu-taler/taler-util";
 
-type Entity = MerchantBackend.Products.ProductDetail & WithId;
+type Entity = {
+  id: string,
+  description: string;
+  image?: string;
+  extra?: string;
+};
 
-export interface Props {
-  selected?: Entity;
-  onChange: (p?: Entity) => void;
-  products: (MerchantBackend.Products.ProductDetail & WithId)[];
+export interface Props<T extends Entity> {
+  selected?: T;
+  onChange: (p?: T) => void;
+  label: TranslatedString;
+  list: T[];
+  withImage?: boolean;
 }
 
-interface ProductSearch {
+interface Search {
   name: string;
 }
 
-export function InputSearchProduct({
+export function InputSearchOnList<T extends Entity>({
   selected,
   onChange,
-  products,
-}: Props): VNode {
-  const [prodForm, setProdName] = useState<Partial<ProductSearch>>({
+  label,
+  list,
+  withImage,
+}: Props<T>): VNode {
+  const [nameForm, setNameForm] = useState<Partial<Search>>({
     name: "",
   });
 
-  const errors: FormErrors<ProductSearch> = {
+  const errors: FormErrors<Search> = {
     name: undefined,
   };
   const { i18n } = useTranslationContext();
@@ -55,15 +64,17 @@ export function InputSearchProduct({
   if (selected) {
     return (
       <article class="media">
-        <figure class="media-left">
-          <p class="image is-128x128">
-            <img src={selected.image ? selected.image : emptyImage} />
-          </p>
-        </figure>
+        {withImage &&
+          <figure class="media-left">
+            <p class="image is-128x128">
+              <img src={selected.image ? selected.image : emptyImage} />
+            </p>
+          </figure>
+        }
         <div class="media-content">
           <div class="content">
             <p class="media-meta">
-              <i18n.Translate>Product id</i18n.Translate>: <b>{selected.id}</b>
+              <i18n.Translate>ID</i18n.Translate>: <b>{selected.id}</b>
             </p>
             <p>
               <i18n.Translate>Description</i18n.Translate>:{" "}
@@ -84,15 +95,15 @@ export function InputSearchProduct({
   }
 
   return (
-    <FormProvider<ProductSearch>
+    <FormProvider<Search>
       errors={errors}
-      object={prodForm}
-      valueHandler={setProdName}
+      object={nameForm}
+      valueHandler={setNameForm}
     >
-      <InputWithAddon<ProductSearch>
+      <InputWithAddon<Search>
         name="name"
-        label={i18n.str`Product`}
-        tooltip={i18n.str`search products by it's description or id`}
+        label={label}
+        tooltip={i18n.str`enter description or id`}
         addonAfter={
           <span class="icon">
             <i class="mdi mdi-magnify" />
@@ -100,13 +111,14 @@ export function InputSearchProduct({
         }
       >
         <div>
-          <ProductList
-            name={prodForm.name}
-            list={products}
+          <DropdownList
+            name={nameForm.name}
+            list={list}
             onSelect={(p) => {
-              setProdName({ name: "" });
+              setNameForm({ name: "" });
               onChange(p);
             }}
+            withImage={!!withImage}
           />
         </div>
       </InputWithAddon>
@@ -114,13 +126,14 @@ export function InputSearchProduct({
   );
 }
 
-interface ProductListProps {
+interface DropdownListProps<T extends Entity> {
   name?: string;
-  onSelect: (p: MerchantBackend.Products.ProductDetail & WithId) => void;
-  list: (MerchantBackend.Products.ProductDetail & WithId)[];
+  onSelect: (p: T) => void;
+  list: T[];
+  withImage: boolean;
 }
 
-function ProductList({ name, onSelect, list }: ProductListProps) {
+function DropdownList<T extends Entity>({ name, onSelect, list, withImage }: 
DropdownListProps<T>) {
   const { i18n } = useTranslationContext();
   if (!name) {
     /* FIXME
@@ -149,7 +162,7 @@ function ProductList({ name, onSelect, list }: 
ProductListProps) {
           {!filtered.length ? (
             <div class="dropdown-item">
               <i18n.Translate>
-                no products found with that description
+                no match found with that description or id
               </i18n.Translate>
             </div>
           ) : (
@@ -161,18 +174,20 @@ function ProductList({ name, onSelect, list }: 
ProductListProps) {
                 style={{ cursor: "pointer" }}
               >
                 <article class="media">
-                  <div class="media-left">
-                    <div class="image" style={{ minWidth: 64 }}>
-                      <img
-                        src={p.image ? p.image : emptyImage}
-                        style={{ width: 64, height: 64 }}
-                      />
+                  {withImage &&
+                    <div class="media-left">
+                      <div class="image" style={{ minWidth: 64 }}>
+                        <img
+                          src={p.image ? p.image : emptyImage}
+                          style={{ width: 64, height: 64 }}
+                        />
+                      </div>
                     </div>
-                  </div>
+                  }
                   <div class="media-content">
                     <div class="content">
                       <p>
-                        <strong>{p.id}</strong> <small>{p.price}</small>
+                        <strong>{p.id}</strong> {p.extra !== undefined ? 
<small>{p.extra}</small> : undefined}
                         <br />
                         {p.description}
                       </p>
diff --git 
a/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx 
b/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx
index 61ddf3c84..f95dfcd05 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx
@@ -56,7 +56,7 @@ export function InputToggle<T>({
   return (
     <div class="field is-horizontal">
       <div class="field-label is-normal">
-        <label class="label" style={{ width: 200 }}>
+        <label class="label" >
           {label}
           {tooltip && (
             <span class="icon has-tooltip-right" data-tooltip={tooltip}>
@@ -65,7 +65,7 @@ export function InputToggle<T>({
           )}
         </label>
       </div>
-      <div class="field-body is-flex-grow-1">
+      <div class="field-body is-flex-grow-3">
         <div class="field">
           <p class={expand ? "control is-expanded" : "control"}>
             <label class="toggle" style={{ marginLeft: 4, marginTop: 0 }}>
diff --git 
a/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx
 
b/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx
index 24380ce98..b75dc83b3 100644
--- 
a/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx
+++ 
b/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx
@@ -24,14 +24,13 @@ import { Fragment, h, VNode } from "preact";
 import { useBackendContext } from "../../context/backend.js";
 import { Entity } from "../../paths/admin/create/CreatePage.js";
 import { Input } from "../form/Input.js";
-import { InputCurrency } from "../form/InputCurrency.js";
 import { InputDuration } from "../form/InputDuration.js";
 import { InputGroup } from "../form/InputGroup.js";
 import { InputImage } from "../form/InputImage.js";
 import { InputLocation } from "../form/InputLocation.js";
-import { InputPaytoForm } from "../form/InputPaytoForm.js";
-import { InputWithAddon } from "../form/InputWithAddon.js";
 import { InputSelector } from "../form/InputSelector.js";
+import { InputToggle } from "../form/InputToggle.js";
+import { InputWithAddon } from "../form/InputWithAddon.js";
 
 export function DefaultInstanceFormFields({
   readonlyId,
@@ -85,28 +84,10 @@ export function DefaultInstanceFormFields({
         tooltip={i18n.str`Logo image.`}
       />
 
-      <InputPaytoForm<Entity>
-        name="accounts"
-        label={i18n.str`Bank account`}
-        tooltip={i18n.str`URI specifying bank account for crediting revenue.`}
-      />
-
-      <InputCurrency<Entity>
-        name="default_max_deposit_fee"
-        label={i18n.str`Default max deposit fee`}
-        tooltip={i18n.str`Maximum deposit fees this merchant is willing to pay 
per order by default.`}
-      />
-
-      <InputCurrency<Entity>
-        name="default_max_wire_fee"
-        label={i18n.str`Default max wire fee`}
-        tooltip={i18n.str`Maximum wire fees this merchant is willing to pay 
per wire transfer by default.`}
-      />
-
-      <Input<Entity>
-        name="default_wire_fee_amortization"
-        label={i18n.str`Default wire fee amortization`}
-        tooltip={i18n.str`Number of orders excess wire transfer fees will be 
divided by to compute per order surcharge.`}
+      <InputToggle<Entity>
+        name="use_stefan"
+        label={i18n.str`Pay transaction fee`}
+        tooltip={i18n.str`Assume the cost of the transaction of let the user 
pay for it.`}
       />
 
       <InputGroup
diff --git a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx 
b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
index f3cf80b92..be2f8dde5 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
@@ -25,6 +25,7 @@ import { useBackendContext } from "../../context/backend.js";
 import { useConfigContext } from "../../context/config.js";
 import { useInstanceKYCDetails } from "../../hooks/instance.js";
 import { LangSelector } from "./LangSelector.js";
+import { useCredentialsChecker } from "../../hooks/backend.js";
 
 const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : 
undefined;
 const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;
@@ -36,6 +37,7 @@ interface Props {
   instance: string;
   admin?: boolean;
   mimic?: boolean;
+  isPasswordOk: boolean;
 }
 
 export function Sidebar({
@@ -45,6 +47,7 @@ export function Sidebar({
   onLogout,
   admin,
   mimic,
+  isPasswordOk
 }: Props): VNode {
   const config = useConfigContext();
   const backend = useBackendContext();
@@ -53,7 +56,7 @@ export function Sidebar({
   const needKYC = kycStatus.ok && kycStatus.data.type === "redirect";
 
   return (
-    <aside class="aside is-placed-left is-expanded">
+    <aside class="aside is-placed-left is-expanded" style={{ overflowY: 
"scroll" }}>
       {mobile && (
         <div
           class="footer"
@@ -78,10 +81,10 @@ export function Sidebar({
         </div>
       </div>
       <div class="menu is-menu-main">
-        {instance ? (
+        {isPasswordOk && instance ? (
           <Fragment>
             <ul class="menu-list">
-            <li>
+              <li>
                 <a href={"/orders"} class="has-icon">
                   <span class="icon">
                     <i class="mdi mdi-cash-register" />
@@ -104,7 +107,7 @@ export function Sidebar({
               <li>
                 <a href={"/transfers"} class="has-icon">
                   <span class="icon">
-                    <i class="mdi mdi-bank" />
+                    <i class="mdi mdi-arrow-left-right" />
                   </span>
                   <span class="menu-item-label">
                     <i18n.Translate>Transfers</i18n.Translate>
@@ -137,12 +140,22 @@ export function Sidebar({
             </p>
             <ul class="menu-list">
               <li>
-                <a href={"/update"} class="has-icon">
+                <a href={"/bank"} class="has-icon">
                   <span class="icon">
-                    <i class="mdi mdi-square-edit-outline" />
+                    <i class="mdi mdi-bank" />
+                  </span>
+                  <span class="menu-item-label">
+                    <i18n.Translate>Bank account</i18n.Translate>
+                  </span>
+                </a>
+              </li>
+              <li>
+                <a href={"/validators"} class="has-icon">
+                  <span class="icon">
+                    <i class="mdi mdi-lock" />
                   </span>
                   <span class="menu-item-label">
-                    <i18n.Translate>Account</i18n.Translate>
+                    <i18n.Translate>Validators</i18n.Translate>
                   </span>
                 </a>
               </li>
@@ -164,6 +177,26 @@ export function Sidebar({
                   </span>
                 </a>
               </li>
+              <li>
+                <a href={"/server"} class="has-icon">
+                  <span class="icon">
+                    <i class="mdi mdi-square-edit-outline" />
+                  </span>
+                  <span class="menu-item-label">
+                    <i18n.Translate>Server</i18n.Translate>
+                  </span>
+                </a>
+              </li>
+              <li>
+                <a href={"/token"} class="has-icon">
+                  <span class="icon">
+                    <i class="mdi mdi-security" />
+                  </span>
+                  <span class="menu-item-label">
+                    <i18n.Translate>Access token</i18n.Translate>
+                  </span>
+                </a>
+              </li>
             </ul>
           </Fragment>
         ) : undefined}
@@ -174,12 +207,12 @@ export function Sidebar({
           <li>
             <a class="has-icon is-state-info is-hoverable"
               onClick={(): void => onShowSettings()}
-              >
+            >
               <span class="icon">
                 <i class="mdi mdi-newspaper" />
               </span>
               <span class="menu-item-label">
-                <i18n.Translate>Settings</i18n.Translate>
+                <i18n.Translate>Interface</i18n.Translate>
               </span>
             </a>
           </li>
@@ -211,7 +244,7 @@ export function Sidebar({
               </span>
             </div>
           </li>
-          {admin && !mimic && (
+          {isPasswordOk && admin && !mimic && (
             <Fragment>
               <p class="menu-label">
                 <i18n.Translate>Instances</i18n.Translate>
@@ -238,19 +271,21 @@ export function Sidebar({
               </li>
             </Fragment>
           )}
-          <li>
-            <a
-              class="has-icon is-state-info is-hoverable"
-              onClick={(): void => onLogout()}
-            >
-              <span class="icon">
-                <i class="mdi mdi-logout default" />
-              </span>
-              <span class="menu-item-label">
-                <i18n.Translate>Log out</i18n.Translate>
-              </span>
-            </a>
-          </li>
+          {isPasswordOk &&
+            <li>
+              <a
+                class="has-icon is-state-info is-hoverable"
+                onClick={(): void => onLogout()}
+              >
+                <span class="icon">
+                  <i class="mdi mdi-logout default" />
+                </span>
+                <span class="menu-item-label">
+                  <i18n.Translate>Log out</i18n.Translate>
+                </span>
+              </a>
+            </li>
+          }
         </ul>
       </div>
     </aside>
diff --git a/packages/merchant-backoffice-ui/src/components/menu/index.tsx 
b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
index cdbae4ae0..cb318906f 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/index.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
@@ -24,7 +24,7 @@ import { Sidebar } from "./SideBar.js";
 
 function getInstanceTitle(path: string, id: string): string {
   switch (path) {
-    case InstancePaths.update:
+    case InstancePaths.server:
       return `${id}: Settings`;
     case InstancePaths.order_list:
       return `${id}: Orders`;
@@ -50,6 +50,12 @@ function getInstanceTitle(path: string, id: string): string {
       return `${id}: New webhook`;
     case InstancePaths.webhooks_update:
       return `${id}: Update webhook`;
+    case InstancePaths.validators_list:
+      return `${id}: Validators`;
+    case InstancePaths.validators_new:
+      return `${id}: New validator`;
+    case InstancePaths.validators_update:
+      return `${id}: Update validators`;
     case InstancePaths.templates_new:
       return `${id}: New template`;
     case InstancePaths.templates_update:
@@ -58,6 +64,10 @@ function getInstanceTitle(path: string, id: string): string {
       return `${id}: Templates`;
     case InstancePaths.templates_use:
       return `${id}: Use template`;
+    case InstancePaths.settings:
+      return `${id}: Interface`;
+    case InstancePaths.settings:
+      return `${id}: Interface`;
     default:
       return "";
   }
@@ -77,6 +87,7 @@ interface MenuProps {
   onLogout?: () => void;
   onShowSettings: () => void;
   setInstanceName: (s: string) => void;
+  isPasswordOk: boolean;
 }
 
 function WithTitle({
@@ -100,14 +111,15 @@ export function Menu({
   path,
   admin,
   setInstanceName,
+  isPasswordOk
 }: MenuProps): VNode {
   const [mobileOpen, setMobileOpen] = useState(false);
 
   const titleWithSubtitle = title
     ? title
     : !admin
-    ? getInstanceTitle(path, instance)
-    : getAdminTitle(path, instance);
+      ? getInstanceTitle(path, instance)
+      : getAdminTitle(path, instance);
   const adminInstance = instance === "default";
   const mimic = admin && !adminInstance;
   return (
@@ -129,14 +141,15 @@ export function Menu({
             mimic={mimic}
             instance={instance}
             mobile={mobileOpen}
+            isPasswordOk={isPasswordOk}
           />
         )}
 
         {mimic && (
           <nav class="level" style={{
             zIndex: 100,
-            position:"fixed",
-            width:"50%",
+            position: "fixed",
+            width: "50%",
             marginLeft: "20%"
           }}>
             <div class="level-item has-text-centered has-background-warning">
@@ -161,8 +174,9 @@ export function Menu({
 
 interface NotYetReadyAppMenuProps {
   title: string;
-  onLogout?: () => void;
   onShowSettings: () => void;
+  onLogout?: () => void;
+  isPasswordOk: boolean;
 }
 
 interface NotifProps {
@@ -181,8 +195,8 @@ export function NotificationCard({
               n.type === "ERROR"
                 ? "message is-danger"
                 : n.type === "WARN"
-                ? "message is-warning"
-                : "message is-info"
+                  ? "message is-warning"
+                  : "message is-info"
             }
           >
             <div class="message-header">
@@ -201,10 +215,36 @@ export function NotificationCard({
   );
 }
 
+interface NotConnectedAppMenuProps {
+  title: string;
+}
+export function NotConnectedAppMenu({
+  title,
+}: NotConnectedAppMenuProps): VNode {
+  const [mobileOpen, setMobileOpen] = useState(false);
+
+  useEffect(() => {
+    document.title = `Taler Backoffice: ${title}`;
+  }, [title]);
+
+  return (
+    <div
+      class={mobileOpen ? "has-aside-mobile-expanded" : ""}
+      onClick={() => setMobileOpen(false)}
+    >
+      <NavigationBar
+        onMobileMenu={() => setMobileOpen(!mobileOpen)}
+        title={title}
+      />
+    </div>
+  );
+}
+
 export function NotYetReadyAppMenu({
   onLogout,
   onShowSettings,
   title,
+  isPasswordOk
 }: NotYetReadyAppMenuProps): VNode {
   const [mobileOpen, setMobileOpen] = useState(false);
 
@@ -222,7 +262,7 @@ export function NotYetReadyAppMenu({
         title={title}
       />
       {onLogout && (
-        <Sidebar onShowSettings={onShowSettings} onLogout={onLogout} 
instance="" mobile={mobileOpen} />
+        <Sidebar onShowSettings={onShowSettings} onLogout={onLogout} 
instance="" mobile={mobileOpen} isPasswordOk={isPasswordOk} />
       )}
     </div>
   );
diff --git 
a/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
 
b/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
index b2ec4dd11..377d9c1ba 100644
--- 
a/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
+++ 
b/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
@@ -20,7 +20,7 @@ import { MerchantBackend, WithId } from 
"../../declaration.js";
 import { ProductMap } from "../../paths/instance/orders/create/CreatePage.js";
 import { FormErrors, FormProvider } from "../form/FormProvider.js";
 import { InputNumber } from "../form/InputNumber.js";
-import { InputSearchProduct } from "../form/InputSearchProduct.js";
+import { InputSearchOnList } from "../form/InputSearchOnList.js";
 
 type Form = {
   product: MerchantBackend.Products.ProductDetail & WithId;
@@ -95,10 +95,12 @@ export function InventoryProductForm({
 
   return (
     <FormProvider<Form> errors={errors} object={state} valueHandler={setState}>
-      <InputSearchProduct
+      <InputSearchOnList
+        label={i18n.str`Search product`}
         selected={state.product}
         onChange={(p) => setState((v) => ({ ...v, product: p }))}
-        products={inventory}
+        list={inventory}
+        withImage
       />
       {state.product && (
         <div class="columns mt-5">
diff --git 
a/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx 
b/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx
index 7956a9ea5..4cd90aa45 100644
--- a/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx
+++ b/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx
@@ -58,12 +58,12 @@ export function ProductForm({ onSubscribe, initial, 
alreadyExist }: Props) {
       !initial || initial.total_stock === -1
         ? undefined
         : {
-            current: initial.total_stock || 0,
-            lost: initial.total_lost || 0,
-            sold: initial.total_sold || 0,
-            address: initial.address,
-            nextRestock: initial.next_restock,
-          },
+          current: initial.total_stock || 0,
+          lost: initial.total_lost || 0,
+          sold: initial.total_sold || 0,
+          address: initial.address,
+          nextRestock: initial.next_restock,
+        },
   });
   let errors: FormErrors<Entity> = {};
 
@@ -148,15 +148,17 @@ export function ProductForm({ onSubscribe, initial, 
alreadyExist }: Props) {
           name="minimum_age"
           label={i18n.str`Age restricted`}
           tooltip={i18n.str`is this product restricted for customer below 
certain age?`}
+          help={i18n.str`can be overriden by the order configuration`}
         />
         <Input<Entity>
           name="unit"
-          label={i18n.str`Unit`}
+          label={i18n.str`Unit name`}
           tooltip={i18n.str`unit describing quantity of product sold (e.g. 2 
kilograms, 5 liters, 3 items, 5 meters) for customers`}
+          help={i18n.str`exajmple: kg, items or liters`}
         />
         <InputCurrency<Entity>
           name="price"
-          label={i18n.str`Price`}
+          label={i18n.str`Price per unit`}
           tooltip={i18n.str`sale price for customers, including taxes, for 
above units of the product`}
         />
         <InputStock
diff --git a/packages/merchant-backoffice-ui/src/context/backend.ts 
b/packages/merchant-backoffice-ui/src/context/backend.ts
index f7f8afea6..43e9e4d27 100644
--- a/packages/merchant-backoffice-ui/src/context/backend.ts
+++ b/packages/merchant-backoffice-ui/src/context/backend.ts
@@ -28,8 +28,8 @@ interface BackendContextType {
   token?: string;
   triedToLog: boolean;
   resetBackend: () => void;
-  clearAllTokens: () => void;
-  addTokenCleaner: (c: () => void) => void;
+  // clearAllTokens: () => void;
+  // addTokenCleaner: (c: () => void) => void;
   updateLoginStatus: (url: string, token?: string) => void;
   updateToken: (token?: string) => void;
 }
@@ -39,8 +39,8 @@ const BackendContext = createContext<BackendContextType>({
   token: undefined,
   triedToLog: false,
   resetBackend: () => null,
-  clearAllTokens: () => null,
-  addTokenCleaner: () => null,
+  // clearAllTokens: () => null,
+  // addTokenCleaner: () => null,
   updateLoginStatus: () => null,
   updateToken: () => null,
 });
@@ -56,30 +56,30 @@ function useBackendContextState(
     _updateToken(t);
   };
 
-  const tokenCleaner = useCallback(() => {
-    updateToken(undefined);
-  }, []);
-  const [cleaners, setCleaners] = useState([tokenCleaner]);
-  const addTokenCleaner = (c: () => void) => setCleaners((cs) => [...cs, c]);
-  const addTokenCleanerMemo = useCallback(
-    (c: () => void) => {
-      addTokenCleaner(c);
-    },
-    [tokenCleaner],
-  );
+  // const tokenCleaner = useCallback(() => {
+  //   updateToken(undefined);
+  // }, []);
+  // const [cleaners, setCleaners] = useState([tokenCleaner]);
+  // const addTokenCleaner = (c: () => void) => setCleaners((cs) => [...cs, 
c]);
+  // const addTokenCleanerMemo = useCallback(
+  //   (c: () => void) => {
+  //     addTokenCleaner(c);
+  //   },
+  //   [tokenCleaner],
+  // );
 
-  const clearAllTokens = () => {
-    cleaners.forEach((c) => c());
-    for (let i = 0; i < localStorage.length; i++) {
-      const k = localStorage.key(i);
-      if (k && /^backend-token/.test(k)) localStorage.removeItem(k);
-    }
-    resetBackend();
-  };
+  // const clearAllTokens = () => {
+  //   cleaners.forEach((c) => c());
+  //   for (let i = 0; i < localStorage.length; i++) {
+  //     const k = localStorage.key(i);
+  //     if (k && /^backend-token/.test(k)) localStorage.removeItem(k);
+  //   }
+  //   resetBackend();
+  // };
 
   const updateLoginStatus = (url: string, token?: string) => {
     changeBackend(url);
-    if (token) updateToken(token);
+    updateToken(token);
   };
 
   return {
@@ -88,9 +88,9 @@ function useBackendContextState(
     triedToLog,
     updateLoginStatus,
     resetBackend,
-    clearAllTokens,
+    // clearAllTokens,
     updateToken,
-    addTokenCleaner: addTokenCleanerMemo,
+    // addTokenCleaner: addTokenCleanerMemo,
   };
 }
 
diff --git a/packages/merchant-backoffice-ui/src/declaration.d.ts 
b/packages/merchant-backoffice-ui/src/declaration.d.ts
index db3122266..5ca9c1e09 100644
--- a/packages/merchant-backoffice-ui/src/declaration.d.ts
+++ b/packages/merchant-backoffice-ui/src/declaration.d.ts
@@ -25,6 +25,8 @@ type EddsaSignature = string;
 type WireTransferIdentifierRawP = string;
 type RelativeTime = Duration;
 type ImageDataUrl = string;
+type MerchantUserType = "business" | "individual";
+
 
 export interface WithId {
   id: string;
@@ -312,46 +314,8 @@ export namespace MerchantBackend {
       // header.
       token?: string;
     }
-    type FacadeCredentials = NoFacadeCredentials | BasicAuthFacadeCredentials;
-
-    interface NoFacadeCredentials {
-      type: "none";
-    }
-
-    interface BasicAuthFacadeCredentials {
-      type: "basic";
-
-      // Username to use to authenticate
-      username: string;
-
-      // Password to use to authenticate
-      password: string;
-    }
-
-    interface MerchantBankAccount {
-      // The payto:// URI where the wallet will send coins.
-      payto_uri: string;
-
-      // Optional base URL for a facade where the
-      // merchant backend can see incoming wire
-      // transfers to reconcile its accounting
-      // with that of the exchange. Used by
-      // taler-merchant-wirewatch.
-      credit_facade_url?: string;
-
-      // Credentials for accessing the credit facade.
-      credit_facade_credentials?: FacadeCredentials;
-    }
     //POST /private/instances
     interface InstanceConfigurationMessage {
-      // Bank accounts of the merchant.  A merchant may have
-      // multiple accounts, thus this is an array.  Note that by
-      // removing accounts from this list the respective account is set to
-      // inactive and thus unavailable for new contracts, but preserved
-      // in the database as existing offers and contracts may still refer
-      // to it.
-      accounts: MerchantBankAccount[];
-
       // Name of the merchant instance to create (will become $INSTANCE).
       id: string;
 
@@ -361,12 +325,16 @@ export namespace MerchantBackend {
       // Type of the user (business or individual).
       // Defaults to 'business'. Should become mandatory field
       // in the future, left as optional for API compatibility for now.
-      user_type?: string;
+      user_type?: MerchantUserType;
+
+      // Merchant email for customer contact.
+      email?: string;
+
+      // Merchant public website.
+      website?: string;
 
-      email: string;
-      website: string;
-      // An optional base64-encoded logo image
-      logo: ImageDataUrl;
+      // Merchant logo.
+      logo?: ImageDataUrl;
 
       // "Authentication" header required to authorize management access the 
instance.
       // Optional, if not given authentication will be disabled for
@@ -381,17 +349,10 @@ export namespace MerchantBackend {
       // (to be put into contracts).
       jurisdiction: Location;
 
-      // Maximum wire fee this instance is willing to pay.
-      // Can be overridden by the frontend on a per-order basis.
-      default_max_wire_fee: Amount;
-
-      // Default factor for wire fee amortization calculations.
-      // Can be overridden by the frontend on a per-order basis.
-      default_wire_fee_amortization: Integer;
-
-      // Maximum deposit fee (sum over all coins) this instance is willing to 
pay.
-      // Can be overridden by the frontend on a per-order basis.
-      default_max_deposit_fee: Amount;
+      // Use STEFAN curves to determine default fees?
+      // If false, no fees are allowed by default.
+      // Can always be overridden by the frontend on a per-order basis.
+      use_stefan: boolean;
 
       //  If the frontend does NOT specify an execution date, how long should
       // we tell the exchange to wait to aggregate transactions before
@@ -406,11 +367,6 @@ export namespace MerchantBackend {
 
     // PATCH /private/instances/$INSTANCE
     interface InstanceReconfigurationMessage {
-      // Bank accounts of the merchant.  A merchant may have
-      // multiple accounts, thus this is an array.  Note that removing
-      // URIs from this list deactivates the specified accounts
-      // (they will no longer be used for future contracts).
-      accounts: MerchantBankAccount[];
 
       // Merchant name corresponding to this instance.
       name: string;
@@ -418,7 +374,16 @@ export namespace MerchantBackend {
       // Type of the user (business or individual).
       // Defaults to 'business'. Should become mandatory field
       // in the future, left as optional for API compatibility for now.
-      user_type?: string;
+      user_type?: MerchantUserType;
+
+      // Merchant email for customer contact.
+      email?: string;
+
+      // Merchant public website.
+      website?: string;
+
+      // Merchant logo.
+      logo?: ImageDataUrl;
 
       // The merchant's physical address (to be put into contracts).
       address: Location;
@@ -427,17 +392,10 @@ export namespace MerchantBackend {
       // (to be put into contracts).
       jurisdiction: Location;
 
-      // Maximum wire fee this instance is willing to pay.
-      // Can be overridden by the frontend on a per-order basis.
-      default_max_wire_fee: Amount;
-
-      // Default factor for wire fee amortization calculations.
-      // Can be overridden by the frontend on a per-order basis.
-      default_wire_fee_amortization: Integer;
-
-      // Maximum deposit fee (sum over all coins) this instance is willing to 
pay.
-      // Can be overridden by the frontend on a per-order basis.
-      default_max_deposit_fee: Amount;
+      // Use STEFAN curves to determine default fees?
+      // If false, no fees are allowed by default.
+      // Can always be overridden by the frontend on a per-order basis.
+      use_stefan: boolean;
 
       //  If the frontend does NOT specify an execution date, how long should
       // we tell the exchange to wait to aggregate transactions before
@@ -460,7 +418,14 @@ export namespace MerchantBackend {
       // Merchant name corresponding to this instance.
       name: string;
 
-      deleted?: boolean;
+      // Type of the user ("business" or "individual").
+      user_type: MerchantUserType;
+
+      // Merchant public website.
+      website?: string;
+
+      // Merchant logo.
+      logo?: ImageDataUrl;
 
       // Merchant instance this response is about ($INSTANCE)
       id: string;
@@ -472,8 +437,63 @@ export namespace MerchantBackend {
       // specify the desired payment target in /order requests.  Note that
       // front-ends do not have to support wallets selecting payment targets.
       payment_targets: string[];
+
+      // Has this instance been deleted (but not purged)?
+      deleted: boolean;
     }
 
+    //GET /private/instances/$INSTANCE
+    interface QueryInstancesResponse {
+
+      // Merchant name corresponding to this instance.
+      name: string;
+      // Type of the user ("business" or "individual").
+      user_type: MerchantUserType;
+
+      // Merchant email for customer contact.
+      email?: string;
+
+      // Merchant public website.
+      website?: string;
+
+      // Merchant logo.
+      logo?: ImageDataUrl;
+
+      // Public key of the merchant/instance, in Crockford Base32 encoding.
+      merchant_pub: EddsaPublicKey;
+
+      // The merchant's physical address (to be put into contracts).
+      address: Location;
+
+      // The jurisdiction under which the merchant conducts its business
+      // (to be put into contracts).
+      jurisdiction: Location;
+
+      // Use STEFAN curves to determine default fees?
+      // If false, no fees are allowed by default.
+      // Can always be overridden by the frontend on a per-order basis.
+      use_stefan: boolean;
+
+      //  If the frontend does NOT specify an execution date, how long should
+      // we tell the exchange to wait to aggregate transactions before
+      // executing the wire transfer?  This delay is added to the current
+      // time when we generate the advisory execution time for the exchange.
+      default_wire_transfer_delay: RelativeTime;
+
+      // If the frontend does NOT specify a payment deadline, how long should
+      // offers we make be valid by default?
+      default_pay_delay: RelativeTime;
+
+      // Authentication configuration.
+      // Does not contain the token when token auth is configured.
+      auth: {
+        method: "external" | "token";
+      };
+    }
+    //   DELETE /private/instances/$INSTANCE
+  }
+
+  namespace KYC {
     //GET /private/instances/$INSTANCE/kyc
     interface AccountKycRedirects {
       // Array of pending KYCs.
@@ -513,56 +533,76 @@ export namespace MerchantBackend {
       exchange_http_status: number;
     }
 
-    //GET /private/instances/$INSTANCE
-    interface QueryInstancesResponse {
-      // The URI where the wallet will send coins.  A merchant may have
-      // multiple accounts, thus this is an array.
-      accounts: MerchantAccount[];
+  }
 
-      // Merchant name corresponding to this instance.
-      name: string;
+  namespace BankAccounts {
 
-      // Public key of the merchant/instance, in Crockford Base32 encoding.
-      merchant_pub: EddsaPublicKey;
+    interface AccountAddDetails {
 
-      // The merchant's physical address (to be put into contracts).
-      address: Location;
+      // payto:// URI of the account.
+      payto_uri: string;
 
-      // The jurisdiction under which the merchant conducts its business
-      // (to be put into contracts).
-      jurisdiction: Location;
+      // URL from where the merchant can download information
+      // about incoming wire transfers to this account.
+      credit_facade_url?: string;
 
-      // Maximum wire fee this instance is willing to pay.
-      // Can be overridden by the frontend on a per-order basis.
-      default_max_wire_fee: Amount;
+      // Credentials to use when accessing the credit facade.
+      // Never returned on a GET (as this may be somewhat
+      // sensitive data). Can be set in POST
+      // or PATCH requests to update (or delete) credentials.
+      // To really delete credentials, set them to the type: "none".
+      credit_facade_credentials?: FacadeCredentials;
 
-      // Default factor for wire fee amortization calculations.
-      // Can be overridden by the frontend on a per-order basis.
-      default_wire_fee_amortization: Integer;
+    }
 
-      // Maximum deposit fee (sum over all coins) this instance is willing to 
pay.
-      // Can be overridden by the frontend on a per-order basis.
-      default_max_deposit_fee: Amount;
+    type FacadeCredentials =
+      | NoFacadeCredentials
+      | BasicAuthFacadeCredentials;
 
-      //  If the frontend does NOT specify an execution date, how long should
-      // we tell the exchange to wait to aggregate transactions before
-      // executing the wire transfer?  This delay is added to the current
-      // time when we generate the advisory execution time for the exchange.
-      default_wire_transfer_delay: RelativeTime;
+    interface NoFacadeCredentials {
+      type: "none";
+    }
 
-      // If the frontend does NOT specify a payment deadline, how long should
-      // offers we make be valid by default?
-      default_pay_delay: RelativeTime;
+    interface BasicAuthFacadeCredentials {
+      type: "basic";
 
-      // Authentication configuration.
-      // Does not contain the token when token auth is configured.
-      auth: {
-        method: "external" | "token";
-        token?: string;
-      };
+      // Username to use to authenticate
+      username: string;
+
+      // Password to use to authenticate
+      password: string;
+    }
+
+    interface AccountAddResponse {
+      // Hash over the wire details (including over the salt).
+      h_wire: HashCode;
+
+      // Salt used to compute h_wire.
+      salt: HashCode;
+    }
+
+    interface AccountPatchDetails {
+
+      // URL from where the merchant can download information
+      // about incoming wire transfers to this account.
+      credit_facade_url?: string;
+
+      // Credentials to use when accessing the credit facade.
+      // Never returned on a GET (as this may be somewhat
+      // sensitive data). Can be set in POST
+      // or PATCH requests to update (or delete) credentials.
+      // To really delete credentials, set them to the type: "none".
+      credit_facade_credentials?: FacadeCredentials;
     }
 
-    interface MerchantAccount {
+
+    interface AccountsSummaryResponse {
+
+      // List of accounts that are known for the instance.
+      accounts: BankAccountEntry[];
+    }
+
+    interface BankAccountEntry {
       // payto:// URI of the account.
       payto_uri: string;
 
@@ -587,7 +627,6 @@ export namespace MerchantBackend {
       active: boolean;
     }
 
-    //   DELETE /private/instances/$INSTANCE
   }
 
   namespace Products {
@@ -957,6 +996,10 @@ export namespace MerchantBackend {
       // high entropy to prevent adversarial claims (like it is
       // if the backend auto-generates one). Default is 'true'.
       create_token?: boolean;
+
+      // OTP device ID to associate with the order.
+      // This parameter is optional.
+      otp_id?: string;
     }
     type Order = MinimalOrderDetail | ContractTerms;
 
@@ -1031,9 +1074,9 @@ export namespace MerchantBackend {
     }
   }
 
-  namespace Tips {
+  namespace Rewards {
     // GET /private/reserves
-    interface TippingReserveStatus {
+    interface RewardReserveStatus {
       // Array of all known reserves (possibly empty!)
       reserves: ReserveStatusEntry[];
     }
@@ -1057,7 +1100,7 @@ export namespace MerchantBackend {
       // Amount picked up so far.
       pickup_amount: Amount;
 
-      // Amount approved for tips that exceeds the pickup_amount.
+      // Amount approved for rewards that exceeds the pickup_amount.
       committed_amount: Amount;
 
       // Is this reserve active (false if it was deleted but not purged)
@@ -1068,7 +1111,7 @@ export namespace MerchantBackend {
       // Amount that the merchant promises to put into the reserve
       initial_balance: Amount;
 
-      // Exchange the merchant intends to use for tipping
+      // Exchange the merchant intends to use for reward
       exchange_url: string;
 
       // Desired wire method, for example "iban" or "x-taler-bank"
@@ -1081,30 +1124,30 @@ export namespace MerchantBackend {
       // Wire accounts of the exchange where to transfer the funds.
       accounts: WireAccount[];
     }
-    interface TipCreateRequest {
-      // Amount that the customer should be tipped
+    interface RewardCreateRequest {
+      // Amount that the customer should be reward
       amount: Amount;
 
-      // Justification for giving the tip
+      // Justification for giving the reward
       justification: string;
 
-      // URL that the user should be directed to after tipping,
-      // will be included in the tip_token.
+      // URL that the user should be directed to after rewarding,
+      // will be included in the reward_token.
       next_url: string;
     }
-    interface TipCreateConfirmation {
-      // Unique tip identifier for the tip that was created.
-      tip_id: HashCode;
+    interface RewardCreateConfirmation {
+      // Unique reward identifier for the reward that was created.
+      reward_id: HashCode;
 
-      // taler://tip URI for the tip
-      taler_tip_uri: string;
+      // taler://reward URI for the reward
+      taler_reward_uri: string;
 
       // URL that will directly trigger processing
-      // the tip when the browser is redirected to it
-      tip_status_url: string;
+      // the reward when the browser is redirected to it
+      reward_status_url: string;
 
-      // when does the tip expire
-      tip_expiration: Timestamp;
+      // when does the reward expire
+      reward_expiration: Timestamp;
     }
 
     interface ReserveDetail {
@@ -1124,12 +1167,12 @@ export namespace MerchantBackend {
       // Amount picked up so far.
       pickup_amount: Amount;
 
-      // Amount approved for tips that exceeds the pickup_amount.
+      // Amount approved for rewards that exceeds the pickup_amount.
       committed_amount: Amount;
 
-      // Array of all tips created by this reserves (possibly empty!).
+      // Array of all rewards created by this reserves (possibly empty!).
       // Only present if asked for explicitly.
-      tips?: TipStatusEntry[];
+      rewards?: RewardStatusEntry[];
 
       // Is this reserve active (false if it was deleted but not purged)?
       active: boolean;
@@ -1144,31 +1187,31 @@ export namespace MerchantBackend {
       exchange_url: string;
     }
 
-    interface TipStatusEntry {
-      // Unique identifier for the tip.
-      tip_id: HashCode;
+    interface RewardStatusEntry {
+      // Unique identifier for the reward.
+      reward_id: HashCode;
 
-      // Total amount of the tip that can be withdrawn.
+      // Total amount of the reward that can be withdrawn.
       total_amount: Amount;
 
-      // Human-readable reason for why the tip was granted.
+      // Human-readable reason for why the reward was granted.
       reason: string;
     }
 
-    interface TipDetails {
-      // Amount that we authorized for this tip.
+    interface RewardDetails {
+      // Amount that we authorized for this reward.
       total_authorized: Amount;
 
       // Amount that was picked up by the user already.
       total_picked_up: Amount;
 
-      // Human-readable reason given when authorizing the tip.
+      // Human-readable reason given when authorizing the reward.
       reason: string;
 
-      // Timestamp indicating when the tip is set to expire (may be in the 
past).
+      // Timestamp indicating when the reward is set to expire (may be in the 
past).
       expiration: Timestamp;
 
-      // Reserve public key from which the tip is funded.
+      // Reserve public key from which the reward is funded.
       reserve_pub: EddsaPublicKey;
 
       // Array showing the pickup operations of the wallet (possibly empty!).
@@ -1239,6 +1282,63 @@ export namespace MerchantBackend {
     }
   }
 
+  namespace OTP {
+    interface OtpDeviceAddDetails {
+      // Device ID to use.
+      otp_device_id: string;
+
+      // Human-readable description for the device.
+      otp_description: string;
+
+      // A base64-encoded key
+      otp_key: string;
+
+      // Algorithm for computing the POS confirmation.
+      otp_algorithm: Integer;
+
+      // Counter for counter-based OTP devices.
+      otp_ctr?: Integer;
+    }
+
+    interface OtpDevicePatchDetails {
+      // Human-readable description for the device.
+      otp_description: string;
+
+      // A base64-encoded key
+      otp_key: string | undefined;
+
+      // Algorithm for computing the POS confirmation.
+      otp_algorithm: Integer;
+
+      // Counter for counter-based OTP devices.
+      otp_ctr?: Integer;
+    }
+
+    interface OtpDeviceSummaryResponse {
+      // Array of devices that are present in our backend.
+      otp_devices: OtpDeviceEntry[];
+    }
+    interface OtpDeviceEntry {
+      // Device identifier.
+      otp_device_id: string;
+
+      // Human-readable description for the device.
+      device_description: string;
+    }
+
+    interface OtpDeviceDetails {
+      // Human-readable description for the device.
+      device_description: string;
+
+      // Algorithm for computing the POS confirmation.
+      otp_algorithm: Integer;
+
+      // Counter for counter-based OTP devices.
+      otp_ctr?: Integer;
+    }
+
+
+  }
   namespace Template {
     interface TemplateAddDetails {
       // Template ID to use.
@@ -1247,12 +1347,9 @@ export namespace MerchantBackend {
       // Human-readable description for the template.
       template_description: string;
 
-      // A base64-encoded key of the point-of-sale.
+      // OTP device ID.
       // This parameter is optional.
-      pos_key?: string;
-
-      // Algorithm for computing the POS confirmation, 0 for none.
-      pos_algorithm?: number;
+      otp_id?: string;
 
       // Additional information in a separate template.
       template_contract: TemplateContractDetails;
@@ -1276,12 +1373,9 @@ export namespace MerchantBackend {
       // Human-readable description for the template.
       template_description: string;
 
-      // A base64-encoded key of the point-of-sale.
+      // OTP device ID.
       // This parameter is optional.
-      pos_key?: string;
-
-      // Algorithm for computing the POS confirmation, 0 for none.
-      pos_algorithm?: Integer;
+      otp_id?: string;
 
       // Additional information in a separate template.
       template_contract: TemplateContractDetails;
@@ -1304,12 +1398,9 @@ export namespace MerchantBackend {
       // Human-readable description for the template.
       template_description: string;
 
-      // A base64-encoded key of the point-of-sale.
+      // OTP device ID.
       // This parameter is optional.
-      pos_key?: string;
-
-      // Algorithm for computing the POS confirmation, 0 for none.
-      pos_algorithm?: Integer;
+      otp_id?: string;
 
       // Additional information in a separate template.
       template_contract: TemplateContractDetails;
@@ -1424,21 +1515,6 @@ export namespace MerchantBackend {
     // Maximum total deposit fee accepted by the merchant for this contract
     max_fee: Amount;
 
-    // Maximum wire fee accepted by the merchant (customer share to be
-    // divided by the 'wire_fee_amortization' factor, and further reduced
-    // if deposit fees are below 'max_fee').  Default if missing is zero.
-    max_wire_fee: Amount;
-
-    // Over how many customer transactions does the merchant expect to
-    // amortize wire fees on average?  If the exchange's wire fee is
-    // above 'max_wire_fee', the difference is divided by this number
-    // to compute the expected customer's contribution to the wire fee.
-    // The customer's contribution may further be reduced by the difference
-    // between the 'max_fee' and the sum of the actual deposit fees.
-    // Optional, default value if missing is 1.  0 and negative values are
-    // invalid and also interpreted as 1.
-    wire_fee_amortization: number;
-
     // List of products that are part of the purchase (see Product).
     products: Product[];
 
diff --git a/packages/merchant-backoffice-ui/src/hooks/backend.ts 
b/packages/merchant-backoffice-ui/src/hooks/backend.ts
index 145a366f6..ecd34df6d 100644
--- a/packages/merchant-backoffice-ui/src/hooks/backend.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/backend.ts
@@ -33,8 +33,9 @@ import {
 } from "@gnu-taler/web-util/browser";
 import { useApiContext } from "@gnu-taler/web-util/browser";
 
+
 export function useMatchMutate(): (
-  re: RegExp,
+  re?: RegExp,
   value?: unknown,
 ) => Promise<any> {
   const { cache, mutate } = useSWRConfig();
@@ -45,13 +46,19 @@ export function useMatchMutate(): (
     );
   }
 
-  return function matchRegexMutate(re: RegExp, value?: unknown) {
-    const allKeys = Array.from(cache.keys());
-    const keys = allKeys.filter((key) => re.test(key));
-    const mutations = keys.map((key) => {
-      return mutate(key, value, true);
+  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,
     });
-    return Promise.all(mutations);
   };
 }
 
@@ -106,32 +113,32 @@ interface useBackendInstanceRequestType {
   ) => Promise<HttpResponseOk<T>>;
   fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
   reserveDetailFetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
-  tipsDetailFetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
-  multiFetcher: <T>(url: string[]) => Promise<HttpResponseOk<T>[]>;
+  rewardsDetailFetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
+  multiFetcher: <T>(params: [url: string[]]) => Promise<HttpResponseOk<T>[]>;
   orderFetcher: <T>(
-    endpoint: string,
-    paid?: YesOrNo,
-    refunded?: YesOrNo,
-    wired?: YesOrNo,
-    searchDate?: Date,
-    delta?: number,
+    params: [endpoint: string,
+      paid?: YesOrNo,
+      refunded?: YesOrNo,
+      wired?: YesOrNo,
+      searchDate?: Date,
+      delta?: number,]
   ) => Promise<HttpResponseOk<T>>;
   transferFetcher: <T>(
-    endpoint: string,
-    payto_uri?: string,
-    verified?: string,
-    position?: string,
-    delta?: number,
+    params: [endpoint: string,
+      payto_uri?: string,
+      verified?: string,
+      position?: string,
+      delta?: number,]
   ) => Promise<HttpResponseOk<T>>;
   templateFetcher: <T>(
-    endpoint: string,
-    position?: string,
-    delta?: number,
+    params: [endpoint: string,
+      position?: string,
+      delta?: number]
   ) => Promise<HttpResponseOk<T>>;
   webhookFetcher: <T>(
-    endpoint: string,
-    position?: string,
-    delta?: number,
+    params: [endpoint: string,
+      position?: string,
+      delta?: number]
   ) => Promise<HttpResponseOk<T>>;
 }
 interface useBackendBaseRequestType {
@@ -147,7 +154,7 @@ export function useCredentialsChecker() {
   const { request } = useApiContext();
   //check against instance details endpoint
   //while merchant backend doesn't have a login endpoint
-  return async function testLogin(
+  async function testLogin(
     instance: string,
     token: string,
   ): Promise<{
@@ -167,6 +174,7 @@ export function useCredentialsChecker() {
       return { valid: false, cause: ErrorType.UNEXPECTED };
     }
   };
+  return testLogin
 }
 
 /**
@@ -212,8 +220,9 @@ export function useBackendInstanceRequest(): 
useBackendInstanceRequestType {
 
   const multiFetcher = useCallback(
     function multiFetcherImpl<T>(
-      endpoints: string[],
+      args: [endpoints: string[]],
     ): Promise<HttpResponseOk<T>[]> {
+      const [endpoints] = args
       return Promise.all(
         endpoints.map((endpoint) =>
           requestHandler<T>(baseUrl, endpoint, { token }),
@@ -232,13 +241,14 @@ export function useBackendInstanceRequest(): 
useBackendInstanceRequestType {
 
   const orderFetcher = useCallback(
     function orderFetcherImpl<T>(
-      endpoint: string,
-      paid?: YesOrNo,
-      refunded?: YesOrNo,
-      wired?: YesOrNo,
-      searchDate?: Date,
-      delta?: number,
+      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
           ? (searchDate.getTime() / 1000) + 1
@@ -260,7 +270,7 @@ export function useBackendInstanceRequest(): 
useBackendInstanceRequestType {
     ): Promise<HttpResponseOk<T>> {
       return requestHandler<T>(baseUrl, endpoint, {
         params: {
-          tips: "yes",
+          rewards: "yes",
         },
         token,
       });
@@ -268,8 +278,8 @@ export function useBackendInstanceRequest(): 
useBackendInstanceRequestType {
     [baseUrl, token],
   );
 
-  const tipsDetailFetcher = useCallback(
-    function tipsDetailFetcherImpl<T>(
+  const rewardsDetailFetcher = useCallback(
+    function rewardsDetailFetcherImpl<T>(
       endpoint: string,
     ): Promise<HttpResponseOk<T>> {
       return requestHandler<T>(baseUrl, endpoint, {
@@ -284,12 +294,13 @@ export function useBackendInstanceRequest(): 
useBackendInstanceRequestType {
 
   const transferFetcher = useCallback(
     function transferFetcherImpl<T>(
-      endpoint: string,
-      payto_uri?: string,
-      verified?: string,
-      position?: string,
-      delta?: number,
+      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;
@@ -305,10 +316,11 @@ export function useBackendInstanceRequest(): 
useBackendInstanceRequestType {
 
   const templateFetcher = useCallback(
     function templateFetcherImpl<T>(
-      endpoint: string,
-      position?: string,
-      delta?: number,
+      args: [endpoint: string,
+        position?: string,
+        delta?: number,]
     ): Promise<HttpResponseOk<T>> {
+      const [endpoint, position, delta] = args
       const params: any = {};
       if (delta !== undefined) {
         params.limit = delta;
@@ -322,10 +334,11 @@ export function useBackendInstanceRequest(): 
useBackendInstanceRequestType {
 
   const webhookFetcher = useCallback(
     function webhookFetcherImpl<T>(
-      endpoint: string,
-      position?: string,
-      delta?: number,
+      args: [endpoint: string,
+        position?: string,
+        delta?: number,]
     ): Promise<HttpResponseOk<T>> {
+      const [endpoint, position, delta] = args
       const params: any = {};
       if (delta !== undefined) {
         params.limit = delta;
@@ -343,7 +356,7 @@ export function useBackendInstanceRequest(): 
useBackendInstanceRequestType {
     multiFetcher,
     orderFetcher,
     reserveDetailFetcher,
-    tipsDetailFetcher,
+    rewardsDetailFetcher,
     transferFetcher,
     templateFetcher,
     webhookFetcher,
diff --git a/packages/merchant-backoffice-ui/src/hooks/bank.ts 
b/packages/merchant-backoffice-ui/src/hooks/bank.ts
new file mode 100644
index 000000000..03b064646
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/hooks/bank.ts
@@ -0,0 +1,217 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+import {
+  HttpResponse,
+  HttpResponseOk,
+  HttpResponsePaginated,
+  RequestError,
+} from "@gnu-taler/web-util/browser";
+import { useEffect, useState } from "preact/hooks";
+import { MerchantBackend } from "../declaration.js";
+import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
+import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+
+// FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import _useSWR, { SWRHook } from "swr";
+const useSWR = _useSWR as unknown as SWRHook;
+
+// const MOCKED_ACCOUNTS: Record<string, 
MerchantBackend.BankAccounts.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: MerchantBackend.BankAccounts.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: MerchantBackend.BankAccounts.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: MerchantBackend.BankAccounts.AccountAddDetails,
+  ) => Promise<HttpResponseOk<void>>;
+  updateBankAccount: (
+    id: string,
+    data: MerchantBackend.BankAccounts.AccountPatchDetails,
+  ) => Promise<HttpResponseOk<void>>;
+  deleteBankAccount: (id: string) => Promise<HttpResponseOk<void>>;
+}
+
+export interface InstanceBankAccountFilter {
+}
+
+export function useInstanceBankAccounts(
+  args?: InstanceBankAccountFilter,
+  updatePosition?: (id: string) => void,
+): HttpResponsePaginated<
+  MerchantBackend.BankAccounts.AccountsSummaryResponse,
+  MerchantBackend.ErrorDetail
+> {
+  // return {
+  //   ok: true,
+  //   loadMore() { },
+  //   loadMorePrev() { },
+  //   data: {
+  //     accounts: Object.values(MOCKED_ACCOUNTS).map(e => ({
+  //       ...e,
+  //       active: true,
+  //     }))
+  //   }
+  // }
+  const { fetcher } = useBackendInstanceRequest();
+
+  const [pageAfter, setPageAfter] = useState(1);
+
+  const totalAfter = pageAfter * PAGE_SIZE;
+  const {
+    data: afterData,
+    error: afterError,
+    isValidating: loadingAfter,
+  } = useSWR<
+    HttpResponseOk<MerchantBackend.BankAccounts.AccountsSummaryResponse>,
+    RequestError<MerchantBackend.ErrorDetail>
+  >([`/private/accounts`], fetcher);
+
+  const [lastAfter, setLastAfter] = useState<
+    HttpResponse<
+      MerchantBackend.BankAccounts.AccountsSummaryResponse,
+      MerchantBackend.ErrorDetail
+    >
+  >({ 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;
+
+  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);
+      }
+    },
+    loadMorePrev: () => {
+    },
+  };
+
+  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 };
+}
+
+export function useBankAccountDetails(
+  h_wire: string,
+): HttpResponse<
+  MerchantBackend.BankAccounts.BankAccountEntry,
+  MerchantBackend.ErrorDetail
+> {
+  // return {
+  //   ok: true,
+  //   data: {
+  //     ...MOCKED_ACCOUNTS[h_wire],
+  //     active: true,
+  //   }
+  // }
+  const { fetcher } = useBackendInstanceRequest();
+
+  const { data, error, isValidating } = useSWR<
+    HttpResponseOk<MerchantBackend.BankAccounts.BankAccountEntry>,
+    RequestError<MerchantBackend.ErrorDetail>
+  >([`/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;
+  }
+  if (error) return error.cause;
+  return { loading: true };
+}
diff --git a/packages/merchant-backoffice-ui/src/hooks/index.ts 
b/packages/merchant-backoffice-ui/src/hooks/index.ts
index b77b9dea8..79b22304a 100644
--- a/packages/merchant-backoffice-ui/src/hooks/index.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/index.ts
@@ -19,9 +19,10 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { StateUpdater, useCallback, useState } from "preact/hooks";
+import { StateUpdater, useCallback, useEffect, useState } from "preact/hooks";
 import { ValueOrFunction } from "../utils/types.js";
 import { useMemoryStorage } from "@gnu-taler/web-util/browser";
+import { useMatchMutate } from "./backend.js";
 
 const calculateRootPath = () => {
   const rootPath =
@@ -56,8 +57,22 @@ export function useBackendDefaultToken(
 ): [string | undefined, ((d: string | undefined) => void)] {
   // uncomment for testing
   initialValue = "secret-token:secret" as string | undefined
-  const { update, value } = useMemoryStorage(`backend-token`, initialValue)
-  return [value, update];
+  const { update: setToken, value: token, reset } = 
useMemoryStorage(`backend-token`, initialValue)
+  const clearCache = useMatchMutate()
+  useEffect(() => {
+    clearCache()
+  }, [token])
+
+  function updateToken(
+    value: (string | undefined)
+  ): void {
+    if (value === undefined) {
+      reset()
+    } else {
+      setToken(value)
+    }
+  }
+  return [token, updateToken];
 }
 
 export function useBackendInstanceToken(
@@ -73,14 +88,12 @@ export function useBackendInstanceToken(
   function updateToken(
     value: (string | undefined)
   ): void {
-    console.log("seeting token", value)
     if (value === undefined) {
       reset()
     } else {
       setToken(value)
     }
   }
-  console.log("token", token)
 
   return [token, updateToken];
 }
diff --git a/packages/merchant-backoffice-ui/src/hooks/instance.test.ts 
b/packages/merchant-backoffice-ui/src/hooks/instance.test.ts
index f78de85dd..d15b3f6d7 100644
--- a/packages/merchant-backoffice-ui/src/hooks/instance.test.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/instance.test.ts
@@ -113,7 +113,7 @@ describe("instance api interaction with details", () => {
         name: "instance_name",
         auth: {
           method: "token",
-          token: "not-secret",
+          // token: "not-secret",
         },
       } as MerchantBackend.Instances.QueryInstancesResponse,
     });
@@ -154,7 +154,7 @@ describe("instance api interaction with details", () => {
               name: "instance_name",
               auth: {
                 method: "token",
-                token: "secret",
+                // token: "secret",
               },
             } as MerchantBackend.Instances.QueryInstancesResponse,
           });
@@ -190,7 +190,7 @@ describe("instance api interaction with details", () => {
         name: "instance_name",
         auth: {
           method: "token",
-          token: "not-secret",
+          // token: "not-secret",
         },
       } as MerchantBackend.Instances.QueryInstancesResponse,
     });
diff --git a/packages/merchant-backoffice-ui/src/hooks/instance.ts 
b/packages/merchant-backoffice-ui/src/hooks/instance.ts
index eae65d64c..32ed30c6f 100644
--- a/packages/merchant-backoffice-ui/src/hooks/instance.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/instance.ts
@@ -198,6 +198,7 @@ export function useInstanceDetails(): HttpResponse<
     revalidateOnFocus: false,
     revalidateOnReconnect: false,
     refreshWhenOffline: false,
+    revalidateIfStale: false,
     errorRetryCount: 0,
     errorRetryInterval: 1,
     shouldRetryOnError: false,
@@ -211,7 +212,7 @@ export function useInstanceDetails(): HttpResponse<
 
 type KYCStatus =
   | { type: "ok" }
-  | { type: "redirect"; status: MerchantBackend.Instances.AccountKycRedirects 
};
+  | { type: "redirect"; status: MerchantBackend.KYC.AccountKycRedirects };
 
 export function useInstanceKYCDetails(): HttpResponse<
   KYCStatus,
@@ -220,7 +221,7 @@ export function useInstanceKYCDetails(): HttpResponse<
   const { fetcher } = useBackendInstanceRequest();
 
   const { data, error } = useSWR<
-    HttpResponseOk<MerchantBackend.Instances.AccountKycRedirects>,
+    HttpResponseOk<MerchantBackend.KYC.AccountKycRedirects>,
     RequestError<MerchantBackend.ErrorDetail>
   >([`/private/kyc`], fetcher, {
     refreshInterval: 60 * 1000,
diff --git a/packages/merchant-backoffice-ui/src/hooks/otp.ts 
b/packages/merchant-backoffice-ui/src/hooks/otp.ts
new file mode 100644
index 000000000..3544b4881
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/hooks/otp.ts
@@ -0,0 +1,223 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+import {
+  HttpResponse,
+  HttpResponseOk,
+  HttpResponsePaginated,
+  RequestError,
+} from "@gnu-taler/web-util/browser";
+import { useEffect, useState } from "preact/hooks";
+import { MerchantBackend } from "../declaration.js";
+import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
+import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+
+// FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import _useSWR, { SWRHook } from "swr";
+const useSWR = _useSWR as unknown as SWRHook;
+
+const MOCKED_DEVICES: Record<string, MerchantBackend.OTP.OtpDeviceAddDetails> 
= {
+  "1": {
+    otp_description: "first device",
+    otp_algorithm: 1,
+    otp_device_id: "1",
+    otp_key: "123",
+  },
+  "2": {
+    otp_description: "second device",
+    otp_algorithm: 0,
+    otp_device_id: "2",
+    otp_key: "456",
+  }
+}
+
+export function useOtpDeviceAPI(): OtpDeviceAPI {
+  const mutateAll = useMatchMutate();
+  const { request } = useBackendInstanceRequest();
+
+  const createOtpDevice = async (
+    data: MerchantBackend.OTP.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: MerchantBackend.OTP.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: MerchantBackend.OTP.OtpDeviceAddDetails,
+  ) => Promise<HttpResponseOk<void>>;
+  updateOtpDevice: (
+    id: string,
+    data: MerchantBackend.OTP.OtpDevicePatchDetails,
+  ) => Promise<HttpResponseOk<void>>;
+  deleteOtpDevice: (id: string) => Promise<HttpResponseOk<void>>;
+}
+
+export interface InstanceOtpDeviceFilter {
+}
+
+export function useInstanceOtpDevices(
+  args?: InstanceOtpDeviceFilter,
+  updatePosition?: (id: string) => void,
+): HttpResponsePaginated<
+  MerchantBackend.OTP.OtpDeviceSummaryResponse,
+  MerchantBackend.ErrorDetail
+> {
+  // 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<MerchantBackend.OTP.OtpDeviceSummaryResponse>,
+    RequestError<MerchantBackend.ErrorDetail>
+  >([`/private/otp-devices`], fetcher);
+
+  const [lastAfter, setLastAfter] = useState<
+    HttpResponse<
+      MerchantBackend.OTP.OtpDeviceSummaryResponse,
+      MerchantBackend.ErrorDetail
+    >
+  >({ 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.otp_devices.length < totalAfter;
+  const isReachingStart = false;
+
+  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: () => {
+    },
+  };
+
+  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 useOtpDeviceDetails(
+  deviceId: string,
+): HttpResponse<
+  MerchantBackend.OTP.OtpDeviceDetails,
+  MerchantBackend.ErrorDetail
+> {
+  // 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();
+
+  const { data, error, isValidating } = useSWR<
+    HttpResponseOk<MerchantBackend.OTP.OtpDeviceDetails>,
+    RequestError<MerchantBackend.ErrorDetail>
+  >([`/private/otp-devices/${deviceId}`], 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 };
+}
diff --git a/packages/merchant-backoffice-ui/src/hooks/reserve.test.ts 
b/packages/merchant-backoffice-ui/src/hooks/reserve.test.ts
index d2831ecff..b3eecd754 100644
--- a/packages/merchant-backoffice-ui/src/hooks/reserve.test.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/reserve.test.ts
@@ -25,16 +25,16 @@ import {
   useInstanceReserves,
   useReserveDetails,
   useReservesAPI,
-  useTipDetails,
+  useRewardDetails,
 } from "./reserves.js";
 import { ApiMockEnvironment } from "./testing.js";
 import {
-  API_AUTHORIZE_TIP,
-  API_AUTHORIZE_TIP_FOR_RESERVE,
+  API_AUTHORIZE_REWARD,
+  API_AUTHORIZE_REWARD_FOR_RESERVE,
   API_CREATE_RESERVE,
   API_DELETE_RESERVE,
   API_GET_RESERVE_BY_ID,
-  API_GET_TIP_BY_ID,
+  API_GET_REWARD_BY_ID,
   API_LIST_RESERVES,
 } from "./urls.js";
 import * as tests from "@gnu-taler/web-util/testing";
@@ -48,7 +48,7 @@ describe("reserve api interaction with listing", () => {
         reserves: [
           {
             reserve_pub: "11",
-          } as MerchantBackend.Tips.ReserveStatusEntry,
+          } as MerchantBackend.Rewards.ReserveStatusEntry,
         ],
       },
     });
@@ -89,10 +89,10 @@ describe("reserve api interaction with listing", () => {
               reserves: [
                 {
                   reserve_pub: "11",
-                } as MerchantBackend.Tips.ReserveStatusEntry,
+                } as MerchantBackend.Rewards.ReserveStatusEntry,
                 {
                   reserve_pub: "22",
-                } as MerchantBackend.Tips.ReserveStatusEntry,
+                } as MerchantBackend.Rewards.ReserveStatusEntry,
               ],
             },
           });
@@ -115,10 +115,10 @@ describe("reserve api interaction with listing", () => {
             reserves: [
               {
                 reserve_pub: "11",
-              } as MerchantBackend.Tips.ReserveStatusEntry,
+              } as MerchantBackend.Rewards.ReserveStatusEntry,
               {
                 reserve_pub: "22",
-              } as MerchantBackend.Tips.ReserveStatusEntry,
+              } as MerchantBackend.Rewards.ReserveStatusEntry,
             ],
           });
         },
@@ -138,13 +138,13 @@ describe("reserve api interaction with listing", () => {
         reserves: [
           {
             reserve_pub: "11",
-          } as MerchantBackend.Tips.ReserveStatusEntry,
+          } as MerchantBackend.Rewards.ReserveStatusEntry,
           {
             reserve_pub: "22",
-          } as MerchantBackend.Tips.ReserveStatusEntry,
+          } as MerchantBackend.Rewards.ReserveStatusEntry,
           {
             reserve_pub: "33",
-          } as MerchantBackend.Tips.ReserveStatusEntry,
+          } as MerchantBackend.Rewards.ReserveStatusEntry,
         ],
       },
     });
@@ -182,10 +182,10 @@ describe("reserve api interaction with listing", () => {
               reserves: [
                 {
                   reserve_pub: "22",
-                } as MerchantBackend.Tips.ReserveStatusEntry,
+                } as MerchantBackend.Rewards.ReserveStatusEntry,
                 {
                   reserve_pub: "33",
-                } as MerchantBackend.Tips.ReserveStatusEntry,
+                } as MerchantBackend.Rewards.ReserveStatusEntry,
               ],
             },
           });
@@ -213,16 +213,16 @@ describe("reserve api interaction with listing", () => {
 });
 
 describe("reserve api interaction with details", () => {
-  it("should evict cache when adding a tip for a specific reserve", async () 
=> {
+  it("should evict cache when adding a reward for a specific reserve", async 
() => {
     const env = new ApiMockEnvironment();
 
     env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), {
       response: {
         accounts: [{ payto_uri: "payto://here" }],
-        tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }],
-      } as MerchantBackend.Tips.ReserveDetail,
+        rewards: [{ reason: "why?", reward_id: "id1", total_amount: "USD:10" 
}],
+      } as MerchantBackend.Rewards.ReserveDetail,
       qparam: {
-        tips: "yes",
+        rewards: "yes",
       },
     });
 
@@ -246,37 +246,37 @@ describe("reserve api interaction with details", () => {
           if (!query.ok) return;
           expect(query.data).deep.equals({
             accounts: [{ payto_uri: "payto://here" }],
-            tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }],
+            rewards: [{ reason: "why?", reward_id: "id1", total_amount: 
"USD:10" }],
           });
 
-          env.addRequestExpectation(API_AUTHORIZE_TIP_FOR_RESERVE("11"), {
+          env.addRequestExpectation(API_AUTHORIZE_REWARD_FOR_RESERVE("11"), {
             request: {
               amount: "USD:12",
               justification: "not",
               next_url: "http://taler.net";,
             },
             response: {
-              tip_id: "id2",
-              taler_tip_uri: "uri",
-              tip_expiration: { t_s: 1 },
-              tip_status_url: "url",
+              reward_id: "id2",
+              taler_reward_uri: "uri",
+              reward_expiration: { t_s: 1 },
+              reward_status_url: "url",
             },
           });
 
           env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), {
             response: {
               accounts: [{ payto_uri: "payto://here" }],
-              tips: [
-                { reason: "why?", tip_id: "id1", total_amount: "USD:10" },
-                { reason: "not", tip_id: "id2", total_amount: "USD:12" },
+              rewards: [
+                { reason: "why?", reward_id: "id1", total_amount: "USD:10" },
+                { reason: "not", reward_id: "id2", total_amount: "USD:12" },
               ],
-            } as MerchantBackend.Tips.ReserveDetail,
+            } as MerchantBackend.Rewards.ReserveDetail,
             qparam: {
-              tips: "yes",
+              rewards: "yes",
             },
           });
 
-          api.authorizeTipReserve("11", {
+          api.authorizeRewardReserve("11", {
             amount: "USD:12",
             justification: "not",
             next_url: "http://taler.net";,
@@ -294,9 +294,9 @@ describe("reserve api interaction with details", () => {
 
           expect(query.data).deep.equals({
             accounts: [{ payto_uri: "payto://here" }],
-            tips: [
-              { reason: "why?", tip_id: "id1", total_amount: "USD:10" },
-              { reason: "not", tip_id: "id2", total_amount: "USD:12" },
+            rewards: [
+              { reason: "why?", reward_id: "id1", total_amount: "USD:10" },
+              { reason: "not", reward_id: "id2", total_amount: "USD:12" },
             ],
           });
         },
@@ -308,16 +308,16 @@ describe("reserve api interaction with details", () => {
     expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
   });
 
-  it("should evict cache when adding a tip for a random reserve", async () => {
+  it("should evict cache when adding a reward for a random reserve", async () 
=> {
     const env = new ApiMockEnvironment();
 
     env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), {
       response: {
         accounts: [{ payto_uri: "payto://here" }],
-        tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }],
-      } as MerchantBackend.Tips.ReserveDetail,
+        rewards: [{ reason: "why?", reward_id: "id1", total_amount: "USD:10" 
}],
+      } as MerchantBackend.Rewards.ReserveDetail,
       qparam: {
-        tips: "yes",
+        rewards: "yes",
       },
     });
 
@@ -341,37 +341,37 @@ describe("reserve api interaction with details", () => {
           if (!query.ok) return;
           expect(query.data).deep.equals({
             accounts: [{ payto_uri: "payto://here" }],
-            tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }],
+            rewards: [{ reason: "why?", reward_id: "id1", total_amount: 
"USD:10" }],
           });
 
-          env.addRequestExpectation(API_AUTHORIZE_TIP, {
+          env.addRequestExpectation(API_AUTHORIZE_REWARD, {
             request: {
               amount: "USD:12",
               justification: "not",
               next_url: "http://taler.net";,
             },
             response: {
-              tip_id: "id2",
-              taler_tip_uri: "uri",
-              tip_expiration: { t_s: 1 },
-              tip_status_url: "url",
+              reward_id: "id2",
+              taler_reward_uri: "uri",
+              reward_expiration: { t_s: 1 },
+              reward_status_url: "url",
             },
           });
 
           env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), {
             response: {
               accounts: [{ payto_uri: "payto://here" }],
-              tips: [
-                { reason: "why?", tip_id: "id1", total_amount: "USD:10" },
-                { reason: "not", tip_id: "id2", total_amount: "USD:12" },
+              rewards: [
+                { reason: "why?", reward_id: "id1", total_amount: "USD:10" },
+                { reason: "not", reward_id: "id2", total_amount: "USD:12" },
               ],
-            } as MerchantBackend.Tips.ReserveDetail,
+            } as MerchantBackend.Rewards.ReserveDetail,
             qparam: {
-              tips: "yes",
+              rewards: "yes",
             },
           });
 
-          api.authorizeTip({
+          api.authorizeReward({
             amount: "USD:12",
             justification: "not",
             next_url: "http://taler.net";,
@@ -387,9 +387,9 @@ describe("reserve api interaction with details", () => {
 
           expect(query.data).deep.equals({
             accounts: [{ payto_uri: "payto://here" }],
-            tips: [
-              { reason: "why?", tip_id: "id1", total_amount: "USD:10" },
-              { reason: "not", tip_id: "id2", total_amount: "USD:12" },
+            rewards: [
+              { reason: "why?", reward_id: "id1", total_amount: "USD:10" },
+              { reason: "not", reward_id: "id2", total_amount: "USD:12" },
             ],
           });
         },
@@ -402,15 +402,15 @@ describe("reserve api interaction with details", () => {
   });
 });
 
-describe("reserve api interaction with tip details", () => {
-  it("should list tips", async () => {
+describe("reserve api interaction with reward details", () => {
+  it("should list rewards", async () => {
     const env = new ApiMockEnvironment();
 
-    env.addRequestExpectation(API_GET_TIP_BY_ID("11"), {
+    env.addRequestExpectation(API_GET_REWARD_BY_ID("11"), {
       response: {
         total_picked_up: "USD:12",
         reason: "not",
-      } as MerchantBackend.Tips.TipDetails,
+      } as MerchantBackend.Rewards.RewardDetails,
       qparam: {
         pickups: "yes",
       },
@@ -418,7 +418,7 @@ describe("reserve api interaction with tip details", () => {
 
     const hookBehavior = await tests.hookBehaveLikeThis(
       () => {
-        const query = useTipDetails("11");
+        const query = useRewardDetails("11");
         return { query };
       },
       {},
diff --git a/packages/merchant-backoffice-ui/src/hooks/reserves.ts 
b/packages/merchant-backoffice-ui/src/hooks/reserves.ts
index bb55b2474..b719bfbe6 100644
--- a/packages/merchant-backoffice-ui/src/hooks/reserves.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/reserves.ts
@@ -31,11 +31,11 @@ export function useReservesAPI(): ReserveMutateAPI {
   const { request } = useBackendInstanceRequest();
 
   const createReserve = async (
-    data: MerchantBackend.Tips.ReserveCreateRequest,
+    data: MerchantBackend.Rewards.ReserveCreateRequest,
   ): Promise<
-    HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation>
+    HttpResponseOk<MerchantBackend.Rewards.ReserveCreateConfirmation>
   > => {
-    const res = await request<MerchantBackend.Tips.ReserveCreateConfirmation>(
+    const res = await 
request<MerchantBackend.Rewards.ReserveCreateConfirmation>(
       `/private/reserves`,
       {
         method: "POST",
@@ -49,12 +49,12 @@ export function useReservesAPI(): ReserveMutateAPI {
     return res;
   };
 
-  const authorizeTipReserve = async (
+  const authorizeRewardReserve = async (
     pub: string,
-    data: MerchantBackend.Tips.TipCreateRequest,
-  ): Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => {
-    const res = await request<MerchantBackend.Tips.TipCreateConfirmation>(
-      `/private/reserves/${pub}/authorize-tip`,
+    data: MerchantBackend.Rewards.RewardCreateRequest,
+  ): Promise<HttpResponseOk<MerchantBackend.Rewards.RewardCreateConfirmation>> 
=> {
+    const res = await 
request<MerchantBackend.Rewards.RewardCreateConfirmation>(
+      `/private/reserves/${pub}/authorize-reward`,
       {
         method: "POST",
         data,
@@ -67,11 +67,11 @@ export function useReservesAPI(): ReserveMutateAPI {
     return res;
   };
 
-  const authorizeTip = async (
-    data: MerchantBackend.Tips.TipCreateRequest,
-  ): Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => {
-    const res = await request<MerchantBackend.Tips.TipCreateConfirmation>(
-      `/private/tips`,
+  const authorizeReward = async (
+    data: MerchantBackend.Rewards.RewardCreateRequest,
+  ): Promise<HttpResponseOk<MerchantBackend.Rewards.RewardCreateConfirmation>> 
=> {
+    const res = await 
request<MerchantBackend.Rewards.RewardCreateConfirmation>(
+      `/private/rewards`,
       {
         method: "POST",
         data,
@@ -97,33 +97,33 @@ export function useReservesAPI(): ReserveMutateAPI {
     return res;
   };
 
-  return { createReserve, authorizeTip, authorizeTipReserve, deleteReserve };
+  return { createReserve, authorizeReward, authorizeRewardReserve, 
deleteReserve };
 }
 
 export interface ReserveMutateAPI {
   createReserve: (
-    data: MerchantBackend.Tips.ReserveCreateRequest,
-  ) => Promise<HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation>>;
-  authorizeTipReserve: (
+    data: MerchantBackend.Rewards.ReserveCreateRequest,
+  ) => 
Promise<HttpResponseOk<MerchantBackend.Rewards.ReserveCreateConfirmation>>;
+  authorizeRewardReserve: (
     id: string,
-    data: MerchantBackend.Tips.TipCreateRequest,
-  ) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>;
-  authorizeTip: (
-    data: MerchantBackend.Tips.TipCreateRequest,
-  ) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>;
+    data: MerchantBackend.Rewards.RewardCreateRequest,
+  ) => 
Promise<HttpResponseOk<MerchantBackend.Rewards.RewardCreateConfirmation>>;
+  authorizeReward: (
+    data: MerchantBackend.Rewards.RewardCreateRequest,
+  ) => 
Promise<HttpResponseOk<MerchantBackend.Rewards.RewardCreateConfirmation>>;
   deleteReserve: (
     id: string,
   ) => Promise<HttpResponse<void, MerchantBackend.ErrorDetail>>;
 }
 
 export function useInstanceReserves(): HttpResponse<
-  MerchantBackend.Tips.TippingReserveStatus,
+  MerchantBackend.Rewards.RewardReserveStatus,
   MerchantBackend.ErrorDetail
 > {
   const { fetcher } = useBackendInstanceRequest();
 
   const { data, error, isValidating } = useSWR<
-    HttpResponseOk<MerchantBackend.Tips.TippingReserveStatus>,
+    HttpResponseOk<MerchantBackend.Rewards.RewardReserveStatus>,
     RequestError<MerchantBackend.ErrorDetail>
   >([`/private/reserves`], fetcher);
 
@@ -136,13 +136,13 @@ export function useInstanceReserves(): HttpResponse<
 export function useReserveDetails(
   reserveId: string,
 ): HttpResponse<
-  MerchantBackend.Tips.ReserveDetail,
+  MerchantBackend.Rewards.ReserveDetail,
   MerchantBackend.ErrorDetail
 > {
   const { reserveDetailFetcher } = useBackendInstanceRequest();
 
   const { data, error, isValidating } = useSWR<
-    HttpResponseOk<MerchantBackend.Tips.ReserveDetail>,
+    HttpResponseOk<MerchantBackend.Rewards.ReserveDetail>,
     RequestError<MerchantBackend.ErrorDetail>
   >([`/private/reserves/${reserveId}`], reserveDetailFetcher, {
     refreshInterval: 0,
@@ -158,15 +158,15 @@ export function useReserveDetails(
   return { loading: true };
 }
 
-export function useTipDetails(
-  tipId: string,
-): HttpResponse<MerchantBackend.Tips.TipDetails, MerchantBackend.ErrorDetail> {
-  const { tipsDetailFetcher } = useBackendInstanceRequest();
+export function useRewardDetails(
+  rewardId: string,
+): HttpResponse<MerchantBackend.Rewards.RewardDetails, 
MerchantBackend.ErrorDetail> {
+  const { rewardsDetailFetcher } = useBackendInstanceRequest();
 
   const { data, error, isValidating } = useSWR<
-    HttpResponseOk<MerchantBackend.Tips.TipDetails>,
+    HttpResponseOk<MerchantBackend.Rewards.RewardDetails>,
     RequestError<MerchantBackend.ErrorDetail>
-  >([`/private/tips/${tipId}`], tipsDetailFetcher, {
+  >([`/private/rewards/${rewardId}`], rewardsDetailFetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
diff --git a/packages/merchant-backoffice-ui/src/hooks/urls.ts 
b/packages/merchant-backoffice-ui/src/hooks/urls.ts
index 6b339c05a..00c5e95af 100644
--- a/packages/merchant-backoffice-ui/src/hooks/urls.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/urls.ts
@@ -139,15 +139,15 @@ export const API_DELETE_PRODUCT = (id: string): 
Query<unknown, unknown> => ({
 ////////////////////
 
 export const API_CREATE_RESERVE: Query<
-  MerchantBackend.Tips.ReserveCreateRequest,
-  MerchantBackend.Tips.ReserveCreateConfirmation
+  MerchantBackend.Rewards.ReserveCreateRequest,
+  MerchantBackend.Rewards.ReserveCreateConfirmation
 > = {
   method: "POST",
   url: "http://backend/instances/default/private/reserves";,
 };
 export const API_LIST_RESERVES: Query<
   unknown,
-  MerchantBackend.Tips.TippingReserveStatus
+  MerchantBackend.Rewards.RewardReserveStatus
 > = {
   method: "GET",
   url: "http://backend/instances/default/private/reserves";,
@@ -155,34 +155,34 @@ export const API_LIST_RESERVES: Query<
 
 export const API_GET_RESERVE_BY_ID = (
   pub: string,
-): Query<unknown, MerchantBackend.Tips.ReserveDetail> => ({
+): Query<unknown, MerchantBackend.Rewards.ReserveDetail> => ({
   method: "GET",
   url: `http://backend/instances/default/private/reserves/${pub}`,
 });
 
-export const API_GET_TIP_BY_ID = (
+export const API_GET_REWARD_BY_ID = (
   pub: string,
-): Query<unknown, MerchantBackend.Tips.TipDetails> => ({
+): Query<unknown, MerchantBackend.Rewards.RewardDetails> => ({
   method: "GET",
-  url: `http://backend/instances/default/private/tips/${pub}`,
+  url: `http://backend/instances/default/private/rewards/${pub}`,
 });
 
-export const API_AUTHORIZE_TIP_FOR_RESERVE = (
+export const API_AUTHORIZE_REWARD_FOR_RESERVE = (
   pub: string,
 ): Query<
-  MerchantBackend.Tips.TipCreateRequest,
-  MerchantBackend.Tips.TipCreateConfirmation
+  MerchantBackend.Rewards.RewardCreateRequest,
+  MerchantBackend.Rewards.RewardCreateConfirmation
 > => ({
   method: "POST",
-  url: 
`http://backend/instances/default/private/reserves/${pub}/authorize-tip`,
+  url: 
`http://backend/instances/default/private/reserves/${pub}/authorize-reward`,
 });
 
-export const API_AUTHORIZE_TIP: Query<
-  MerchantBackend.Tips.TipCreateRequest,
-  MerchantBackend.Tips.TipCreateConfirmation
+export const API_AUTHORIZE_REWARD: Query<
+  MerchantBackend.Rewards.RewardCreateRequest,
+  MerchantBackend.Rewards.RewardCreateConfirmation
 > = {
   method: "POST",
-  url: `http://backend/instances/default/private/tips`,
+  url: `http://backend/instances/default/private/rewards`,
 };
 
 export const API_DELETE_RESERVE = (id: string): Query<unknown, unknown> => ({
@@ -211,7 +211,7 @@ export const API_GET_INSTANCE_BY_ID = (
 
 export const API_GET_INSTANCE_KYC_BY_ID = (
   id: string,
-): Query<unknown, MerchantBackend.Instances.AccountKycRedirects> => ({
+): Query<unknown, MerchantBackend.KYC.AccountKycRedirects> => ({
   method: "GET",
   url: `http://backend/management/instances/${id}/kyc`,
 });
@@ -263,7 +263,7 @@ export const API_GET_CURRENT_INSTANCE: Query<
 
 export const API_GET_CURRENT_INSTANCE_KYC: Query<
   unknown,
-  MerchantBackend.Instances.AccountKycRedirects
+  MerchantBackend.KYC.AccountKycRedirects
 > = {
   method: "GET",
   url: `http://backend/instances/default/private/kyc`,
diff --git a/packages/merchant-backoffice-ui/src/hooks/useSettings.ts 
b/packages/merchant-backoffice-ui/src/hooks/useSettings.ts
index 5c0932f27..7dee9f896 100644
--- a/packages/merchant-backoffice-ui/src/hooks/useSettings.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/useSettings.ts
@@ -19,6 +19,9 @@ import {
   Codec,
   buildCodecForObject,
   codecForBoolean,
+  codecForConstString,
+  codecForEither,
+  codecForString,
 } from "@gnu-taler/taler-util";
 
 function parse_json_or_undefined<T>(str: string | undefined): T | undefined {
@@ -31,29 +34,49 @@ function parse_json_or_undefined<T>(str: string | 
undefined): T | undefined {
 }
 
 export interface Settings {
-  advanceOrderMode: boolean
+  advanceOrderMode: boolean;
+  dateFormat: "ymd" | "dmy" | "mdy";
 }
 
 const defaultSettings: Settings = {
   advanceOrderMode: false,
+  dateFormat: "ymd",
 }
 
 export const codecForSettings = (): Codec<Settings> =>
   buildCodecForObject<Settings>()
     .property("advanceOrderMode", codecForBoolean())
+    .property("dateFormat", codecForEither(
+      codecForConstString("ymd"),
+      codecForConstString("dmy"),
+      codecForConstString("mdy"),
+    ))
     .build("Settings");
 
 const SETTINGS_KEY = buildStorageKey("merchant-settings", codecForSettings());
 
 export function useSettings(): [
   Readonly<Settings>,
-  <T extends keyof Settings>(key: T, value: Settings[T]) => void,
+  (s: Settings) => void,
 ] {
-  const { value, update } = useLocalStorage(SETTINGS_KEY);
+  const { value, update } = useLocalStorage(SETTINGS_KEY, defaultSettings);
 
-  const parsed: Settings = value ?? defaultSettings;
-  function updateField<T extends keyof Settings>(k: T, v: Settings[T]) {
-    update({ ...parsed, [k]: v });
+  // const parsed: Settings = value ?? defaultSettings;
+  // function updateField<T extends keyof Settings>(k: T, v: Settings[T]) {
+  //   const next = { ...parsed, [k]: v }
+  //   update(next);
+  // }
+  return [value, update];
+}
+
+export function dateFormatForSettings(s: Settings): string {
+  switch (s.dateFormat) {
+    case "ymd": return "yyyy/MM/dd"
+    case "dmy": return "dd/MM/yyyy"
+    case "mdy": return "MM/dd/yyyy"
   }
-  return [parsed, updateField];
 }
+
+export function datetimeFormatForSettings(s: Settings): string {
+  return dateFormatForSettings(s) + " HH:mm:ss"
+}
\ No newline at end of file
diff --git 
a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx 
b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
index 14e2fcb46..a8108251d 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
@@ -19,7 +19,6 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { Amounts } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
@@ -29,9 +28,8 @@ import {
   FormProvider,
 } from "../../../components/form/FormProvider.js";
 import { DefaultInstanceFormFields } from 
"../../../components/instance/DefaultInstanceFormFields.js";
-import { SetTokenNewInstanceModal } from "../../../components/modal/index.js";
 import { MerchantBackend } from "../../../declaration.js";
-import { INSTANCE_ID_REGEX, PAYTO_REGEX } from "../../../utils/constants.js";
+import { INSTANCE_ID_REGEX } from "../../../utils/constants.js";
 import { undefinedIfEmpty } from "../../../utils/table.js";
 
 export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage & {
@@ -47,19 +45,19 @@ interface Props {
 function with_defaults(id?: string): Partial<Entity> {
   return {
     id,
-    accounts: [],
+    // accounts: [],
     user_type: "business",
+    use_stefan: false,
     default_pay_delay: { d_us: 2 * 1000 * 60 * 60 * 1000 }, // two hours
-    default_wire_fee_amortization: 1,
     default_wire_transfer_delay: { d_us: 1000 * 2 * 60 * 60 * 24 * 1000 }, // 
two days
   };
 }
 
 export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
   const [value, valueHandler] = useState(with_defaults(forceId));
-  const [isTokenSet, updateIsTokenSet] = useState<boolean>(false);
-  const [isTokenDialogActive, updateIsTokenDialogActive] =
-    useState<boolean>(false);
+  // const [isTokenSet, updateIsTokenSet] = useState<boolean>(false);
+  // const [isTokenDialogActive, updateIsTokenDialogActive] =
+  //   useState<boolean>(false);
 
   const { i18n } = useTranslationContext();
 
@@ -67,42 +65,24 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
     id: !value.id
       ? i18n.str`required`
       : !INSTANCE_ID_REGEX.test(value.id)
-      ? i18n.str`is not valid`
-      : undefined,
+        ? i18n.str`is not valid`
+        : undefined,
     name: !value.name ? i18n.str`required` : undefined,
     user_type: !value.user_type
       ? i18n.str`required`
       : value.user_type !== "business" && value.user_type !== "individual"
-      ? i18n.str`should be business or individual`
-      : undefined,
-    accounts:
-      !value.accounts || !value.accounts.length
-        ? i18n.str`required`
-        : undefinedIfEmpty(
-            value.accounts.map((p) => {
-              return !PAYTO_REGEX.test(p.payto_uri)
-                ? i18n.str`is not valid`
-                : undefined;
-            }),
-          ),
-    default_max_deposit_fee: !value.default_max_deposit_fee
-      ? i18n.str`required`
-      : !Amounts.parse(value.default_max_deposit_fee)
-      ? i18n.str`invalid format`
-      : undefined,
-    default_max_wire_fee: !value.default_max_wire_fee
-      ? i18n.str`required`
-      : !Amounts.parse(value.default_max_wire_fee)
-      ? i18n.str`invalid format`
-      : undefined,
-    default_wire_fee_amortization:
-      value.default_wire_fee_amortization === undefined
-        ? i18n.str`required`
-        : isNaN(value.default_wire_fee_amortization)
-        ? i18n.str`is not a number`
-        : value.default_wire_fee_amortization < 1
-        ? i18n.str`must be 1 or greater`
+        ? i18n.str`should be business or individual`
         : undefined,
+    // accounts:
+    //   !value.accounts || !value.accounts.length
+    //     ? i18n.str`required`
+    //     : undefinedIfEmpty(
+    //       value.accounts.map((p) => {
+    //         return !PAYTO_REGEX.test(p.payto_uri)
+    //           ? i18n.str`is not valid`
+    //           : undefined;
+    //       }),
+    //     ),
     default_pay_delay: !value.default_pay_delay
       ? i18n.str`required`
       : undefined,
@@ -129,12 +109,12 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
 
   const submit = (): Promise<void> => {
     // use conversion instead of this
-    const newToken = value.auth_token;
-    value.auth_token = undefined;
-    value.auth =
-      newToken === null || newToken === undefined
-        ? { method: "external" }
-        : { method: "token", token: `secret-token:${newToken}` };
+    // const newToken = value.auth_token;
+    // value.auth_token = undefined;
+    value.auth = { method: "external" }
+    // newToken === null || newToken === undefined
+    //   ? { method: "external" }
+    // : { method: "token", token: `secret-token:${newToken}` };
     if (!value.address) value.address = {};
     if (!value.jurisdiction) value.jurisdiction = {};
     // remove above use conversion
@@ -142,16 +122,16 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
     return onCreate(value as Entity);
   };
 
-  function updateToken(token: string | null) {
-    valueHandler((old) => ({
-      ...old,
-      auth_token: token === null ? undefined : token,
-    }));
-  }
+  // function updateToken(token: string | null) {
+  //   valueHandler((old) => ({
+  //     ...old,
+  //     auth_token: token === null ? undefined : token,
+  //   }));
+  // }
 
   return (
     <div>
-      <div class="columns">
+      {/* <div class="columns">
         <div class="column" />
         <div class="column is-four-fifths">
           {isTokenDialogActive && (
@@ -174,9 +154,9 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
           )}
         </div>
         <div class="column" />
-      </div>
+      </div> */}
 
-      <section class="hero is-hero-bar">
+      {/* <section class="hero is-hero-bar">
         <div class="hero-body">
           <div class="level">
             <div class="level-item has-text-centered">
@@ -186,8 +166,8 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
                     !isTokenSet
                       ? "button is-danger has-tooltip-bottom"
                       : !value.auth_token
-                      ? "button has-tooltip-bottom"
-                      : "button is-info has-tooltip-bottom"
+                        ? "button has-tooltip-bottom"
+                        : "button is-info has-tooltip-bottom"
                   }
                   data-tooltip={i18n.str`change authorization configuration`}
                   onClick={() => updateIsTokenDialogActive(true)}
@@ -228,7 +208,7 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
             </div>
           </div>
         </div>
-      </section>
+      </section> */}
 
       <section class="section is-main-section">
         <div class="columns">
@@ -250,7 +230,7 @@ export function CreatePage({ onCreate, onBack, forceId }: 
Props): VNode {
               )}
               <AsyncButton
                 onClick={submit}
-                disabled={!isTokenSet || hasErrors}
+                disabled={hasErrors}
                 data-tooltip={
                   hasErrors
                     ? i18n.str`Need to complete marked fields and choose 
authorization method`
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx
new file mode 100644
index 000000000..3336c53a4
--- /dev/null
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx
@@ -0,0 +1,28 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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 { h, VNode, FunctionalComponent } from "preact";
+import { CreatePage as TestedComponent } from "./CreatePage.js";
+
+export default {
+  title: "Pages/Accounts/Create",
+  component: TestedComponent,
+};
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
new file mode 100644
index 000000000..3ac510f63
--- /dev/null
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
@@ -0,0 +1,175 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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 { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
+import {
+  FormErrors,
+  FormProvider,
+} from "../../../../components/form/FormProvider.js";
+import { Input } from "../../../../components/form/Input.js";
+import { useBackendContext } from "../../../../context/backend.js";
+import { MerchantBackend } from "../../../../declaration.js";
+import { InputPaytoForm } from "../../../../components/form/InputPaytoForm.js";
+import { parsePayUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
+import { undefinedIfEmpty } from "../../../../utils/table.js";
+import { InputSelector } from "../../../../components/form/InputSelector.js";
+
+type Entity = MerchantBackend.BankAccounts.AccountAddDetails & { 
repeatPassword: string };
+
+interface Props {
+  onCreate: (d: Entity) => Promise<void>;
+  onBack?: () => void;
+}
+
+const accountAuthType = ["none", "basic"];
+
+function isValidURL(s: string): boolean {
+  try {
+    const u = new URL(s)
+    return true;
+  } catch (e) {
+    return false;
+  }
+}
+
+export function CreatePage({ onCreate, onBack }: Props): VNode {
+  const { i18n } = useTranslationContext();
+
+  const [state, setState] = useState<Partial<Entity>>({});
+  const errors: FormErrors<Entity> = {
+    payto_uri: !state.payto_uri ? i18n.str`required` : undefined,
+
+    credit_facade_credentials: !state.credit_facade_credentials
+      ? undefined
+      : undefinedIfEmpty({
+        username:
+          state.credit_facade_credentials.type === "basic" && 
!state.credit_facade_credentials.username
+            ? i18n.str`required`
+            : undefined,
+        password:
+          state.credit_facade_credentials.type === "basic" && 
!state.credit_facade_credentials.password
+            ? i18n.str`required`
+            : undefined,
+      }),
+    credit_facade_url: !state.credit_facade_url
+      ? undefined
+      : !isValidURL(state.credit_facade_url) ? i18n.str`not valid url`
+        : undefined,
+    repeatPassword:
+      !state.credit_facade_credentials
+        ? undefined
+        : state.credit_facade_credentials.type === "basic" && 
(!state.credit_facade_credentials.password || 
state.credit_facade_credentials.password !== state.repeatPassword)
+          ? i18n.str`is not the same`
+          : undefined,
+  };
+
+  const hasErrors = Object.keys(errors).some(
+    (k) => (errors as any)[k] !== undefined,
+  );
+
+  const submitForm = () => {
+    if (hasErrors) return Promise.reject();
+    delete state.repeatPassword
+    return onCreate(state as any);
+  };
+
+  return (
+    <div>
+      <section class="section is-main-section">
+        <div class="columns">
+          <div class="column" />
+          <div class="column is-four-fifths">
+            <FormProvider
+              object={state}
+              valueHandler={setState}
+              errors={errors}
+            >
+              <InputPaytoForm<Entity>
+                name="payto_uri"
+                label={i18n.str`Account`}
+              />
+              <Input<Entity>
+                name="credit_facade_url"
+                label={i18n.str`Account info URL`}
+                help="https://bank.com";
+                expand
+                tooltip={i18n.str`From where the merchant can download 
information about incoming wire transfers to this account`}
+              />
+              <InputSelector
+                name="credit_facade_credentials.type"
+                label={i18n.str`Auth type`}
+                tooltip={i18n.str`Choose the authentication type for the 
account info URL`}
+                values={accountAuthType}
+                toStr={(str) => {
+                  if (str === "none") return "Without authentication";
+                  return "Username and password";
+                }}
+              />
+              {state.credit_facade_credentials?.type === "basic" ? (
+                <Fragment>
+                  <Input
+                    name="credit_facade_credentials.username"
+                    label={i18n.str`Username`}
+                    tooltip={i18n.str`Username to access the account 
information.`}
+                  />
+                  <Input
+                    name="credit_facade_credentials.password"
+                    inputType="password"
+                    label={i18n.str`Password`}
+                    tooltip={i18n.str`Password to access the account 
information.`}
+                  />
+                  <Input
+                    name="repeatPassword"
+                    inputType="password"
+                    label={i18n.str`Repeat password`}
+                  />
+                </Fragment>
+              ) : undefined}
+            </FormProvider>
+
+            <div class="buttons is-right mt-5">
+              {onBack && (
+                <button class="button" onClick={onBack}>
+                  <i18n.Translate>Cancel</i18n.Translate>
+                </button>
+              )}
+              <AsyncButton
+                disabled={hasErrors}
+                data-tooltip={
+                  hasErrors
+                    ? i18n.str`Need to complete marked fields`
+                    : "confirm operation"
+                }
+                onClick={submitForm}
+              >
+                <i18n.Translate>Confirm</i18n.Translate>
+              </AsyncButton>
+            </div>
+          </div>
+          <div class="column" />
+        </div>
+      </section>
+    </div>
+  );
+}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
similarity index 69%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
index f218f4ead..7d33d25ce 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
@@ -24,38 +24,36 @@ import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import { useInstanceDetails } from "../../../../hooks/instance.js";
-import { useTransferAPI } from "../../../../hooks/transfer.js";
+import { useWebhookAPI } from "../../../../hooks/webhooks.js";
 import { Notification } from "../../../../utils/types.js";
 import { CreatePage } from "./CreatePage.js";
+import { useOtpDeviceAPI } from "../../../../hooks/otp.js";
+import { useBankAccountAPI } from "../../../../hooks/bank.js";
 
-export type Entity = MerchantBackend.Transfers.TransferInformation;
+export type Entity = MerchantBackend.BankAccounts.AccountAddDetails;
 interface Props {
   onBack?: () => void;
   onConfirm: () => void;
 }
 
-export default function CreateTransfer({ onConfirm, onBack }: Props): VNode {
-  const { informTransfer } = useTransferAPI();
+export default function CreateValidator({ onConfirm, onBack }: Props): VNode {
+  const { createBankAccount } = useBankAccountAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { i18n } = useTranslationContext();
-  const instance = useInstanceDetails();
-  const accounts = !instance.ok
-    ? []
-    : instance.data.accounts.map((a) => a.payto_uri);
 
   return (
     <>
       <NotificationCard notification={notif} />
       <CreatePage
         onBack={onBack}
-        accounts={accounts}
-        onCreate={(request: MerchantBackend.Transfers.TransferInformation) => {
-          return informTransfer(request)
-            .then(() => onConfirm())
+        onCreate={(request: Entity) => {
+          return createBankAccount(request)
+            .then((d) => {
+              onConfirm()
+            })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not inform transfer`,
+                message: i18n.str`could not create device`,
                 type: "ERROR",
                 description: error.message,
               });
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx
new file mode 100644
index 000000000..6b4b63735
--- /dev/null
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx
@@ -0,0 +1,28 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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 { FunctionalComponent, h } from "preact";
+import { ListPage as TestedComponent } from "./ListPage.js";
+
+export default {
+  title: "Pages/Accounts/List",
+  component: TestedComponent,
+};
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx
new file mode 100644
index 000000000..24da755b9
--- /dev/null
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx
@@ -0,0 +1,64 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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 { h, VNode } from "preact";
+import { MerchantBackend } from "../../../../declaration.js";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { CardTable } from "./Table.js";
+
+export interface Props {
+  devices: MerchantBackend.BankAccounts.BankAccountEntry[];
+  onLoadMoreBefore?: () => void;
+  onLoadMoreAfter?: () => void;
+  onCreate: () => void;
+  onDelete: (e: MerchantBackend.BankAccounts.BankAccountEntry) => void;
+  onSelect: (e: MerchantBackend.BankAccounts.BankAccountEntry) => void;
+}
+
+export function ListPage({
+  devices,
+  onCreate,
+  onDelete,
+  onSelect,
+  onLoadMoreBefore,
+  onLoadMoreAfter,
+}: Props): VNode {
+  const form = { payto_uri: "" };
+
+  const { i18n } = useTranslationContext();
+  return (
+    <section class="section is-main-section">
+      <CardTable
+        accounts={devices.map((o) => ({
+          ...o,
+          id: String(o.h_wire),
+        }))}
+        onCreate={onCreate}
+        onDelete={onDelete}
+        onSelect={onSelect}
+        onLoadMoreBefore={onLoadMoreBefore}
+        hasMoreBefore={!onLoadMoreBefore}
+        onLoadMoreAfter={onLoadMoreAfter}
+        hasMoreAfter={!onLoadMoreAfter}
+      />
+    </section>
+  );
+}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx
new file mode 100644
index 000000000..7d6db0782
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx
@@ -0,0 +1,385 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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 { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { StateUpdater, useState } from "preact/hooks";
+import { MerchantBackend } from "../../../../declaration.js";
+import { parsePaytoUri, PaytoType, PaytoUri, PaytoUriBitcoin, PaytoUriIBAN, 
PaytoUriTalerBank, PaytoUriUnknown } from "@gnu-taler/taler-util";
+
+type Entity = MerchantBackend.BankAccounts.BankAccountEntry;
+
+interface Props {
+  accounts: Entity[];
+  onDelete: (e: Entity) => void;
+  onSelect: (e: Entity) => void;
+  onCreate: () => void;
+  onLoadMoreBefore?: () => void;
+  hasMoreBefore?: boolean;
+  hasMoreAfter?: boolean;
+  onLoadMoreAfter?: () => void;
+}
+
+export function CardTable({
+  accounts,
+  onCreate,
+  onDelete,
+  onSelect,
+  onLoadMoreAfter,
+  onLoadMoreBefore,
+  hasMoreAfter,
+  hasMoreBefore,
+}: Props): VNode {
+  const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
+
+  const { i18n } = useTranslationContext();
+
+  return (
+    <div class="card has-table">
+      <header class="card-header">
+        <p class="card-header-title">
+          <span class="icon">
+            <i class="mdi mdi-newspaper" />
+          </span>
+          <i18n.Translate>Bank accounts</i18n.Translate>
+        </p>
+        <div class="card-header-icon" aria-label="more options">
+          <span
+            class="has-tooltip-left"
+            data-tooltip={i18n.str`add new accounts`}
+          >
+            <button class="button is-info" type="button" onClick={onCreate}>
+              <span class="icon is-small">
+                <i class="mdi mdi-plus mdi-36px" />
+              </span>
+            </button>
+          </span>
+        </div>
+      </header>
+      <div class="card-content">
+        <div class="b-table has-pagination">
+          <div class="table-wrapper has-mobile-cards">
+            {accounts.length > 0 ? (
+              <Table
+                accounts={accounts}
+                onDelete={onDelete}
+                onSelect={onSelect}
+                rowSelection={rowSelection}
+                rowSelectionHandler={rowSelectionHandler}
+                onLoadMoreAfter={onLoadMoreAfter}
+                onLoadMoreBefore={onLoadMoreBefore}
+                hasMoreAfter={hasMoreAfter}
+                hasMoreBefore={hasMoreBefore}
+              />
+            ) : (
+              <EmptyTable />
+            )}
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}
+interface TableProps {
+  rowSelection: string[];
+  accounts: Entity[];
+  onDelete: (e: Entity) => void;
+  onSelect: (e: Entity) => void;
+  rowSelectionHandler: StateUpdater<string[]>;
+  onLoadMoreBefore?: () => void;
+  hasMoreBefore?: boolean;
+  hasMoreAfter?: boolean;
+  onLoadMoreAfter?: () => void;
+}
+
+function toggleSelected<T>(id: T): (prev: T[]) => T[] {
+  return (prev: T[]): T[] =>
+    prev.indexOf(id) == -1 ? [...prev, id] : prev.filter((e) => e != id);
+}
+
+function Table({
+  accounts,
+  onLoadMoreAfter,
+  onDelete,
+  onSelect,
+  onLoadMoreBefore,
+  hasMoreAfter,
+  hasMoreBefore,
+}: TableProps): VNode {
+  const { i18n } = useTranslationContext();
+  const emptyList: Record<PaytoType | "unknown", { parsed: PaytoUri, acc: 
Entity }[]> = { "bitcoin": [], "x-taler-bank": [], "iban": [], "unknown": [], }
+  const accountsByType = accounts.reduce((prev, acc) => {
+    const parsed = parsePaytoUri(acc.payto_uri)
+    if (!parsed) return prev //skip
+    if (parsed.targetType !== "bitcoin" && parsed.targetType !== 
"x-taler-bank" && parsed.targetType !== "iban") {
+      prev["unknown"].push({ parsed, acc })
+    } else {
+      prev[parsed.targetType].push({ parsed, acc })
+    }
+    return prev
+  }, emptyList)
+
+  const bitcoinAccounts = accountsByType["bitcoin"]
+  const talerbankAccounts = accountsByType["x-taler-bank"]
+  const ibanAccounts = accountsByType["iban"]
+  const unkownAccounts = accountsByType["unknown"]
+
+
+  return (
+    <Fragment>
+
+      {bitcoinAccounts.length > 0 && <div class="table-container">
+        <p class="card-header-title"><i18n.Translate>Bitcoin type 
accounts</i18n.Translate></p>
+        <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
+          <thead>
+            <tr>
+              <th>
+                <i18n.Translate>Address</i18n.Translate>
+              </th>
+              <th>
+                <i18n.Translate>Sewgit 1</i18n.Translate>
+              </th>
+              <th>
+                <i18n.Translate>Sewgit 2</i18n.Translate>
+              </th>
+              <th />
+            </tr>
+          </thead>
+          <tbody>
+            {bitcoinAccounts.map(({ parsed, acc }, idx) => {
+              const ac = parsed as PaytoUriBitcoin
+              return (
+                <tr key={idx}>
+                  <td
+                    onClick={(): void => onSelect(acc)}
+                    style={{ cursor: "pointer" }}
+                  >
+                    {ac.targetPath}
+                  </td>
+                  <td
+                    onClick={(): void => onSelect(acc)}
+                    style={{ cursor: "pointer" }}
+                  >
+                    {ac.segwitAddrs[0]}
+                  </td>
+                  <td
+                    onClick={(): void => onSelect(acc)}
+                    style={{ cursor: "pointer" }}
+                  >
+                    {ac.segwitAddrs[1]}
+                  </td>
+                  <td class="is-actions-cell right-sticky">
+                    <div class="buttons is-right">
+                      <button
+                        class="button is-danger is-small has-tooltip-left"
+                        data-tooltip={i18n.str`delete selected accounts from 
the database`}
+                        onClick={() => onDelete(acc)}
+                      >
+                        Delete
+                      </button>
+                    </div>
+                  </td>
+                </tr>
+              );
+            })}
+          </tbody>
+        </table>
+      </div>}
+
+
+
+      {talerbankAccounts.length > 0 && <div class="table-container">
+        <p class="card-header-title"><i18n.Translate>Taler type 
accounts</i18n.Translate></p>
+        <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
+          <thead>
+            <tr>
+              <th>
+                <i18n.Translate>Host</i18n.Translate>
+              </th>
+              <th>
+                <i18n.Translate>Account name</i18n.Translate>
+              </th>
+              <th />
+            </tr>
+          </thead>
+          <tbody>
+            {talerbankAccounts.map(({ parsed, acc }, idx) => {
+              const ac = parsed as PaytoUriTalerBank
+              return (
+                <tr key={idx}>
+                  <td
+                    onClick={(): void => onSelect(acc)}
+                    style={{ cursor: "pointer" }}
+                  >
+                    {ac.host}
+                  </td>
+                  <td
+                    onClick={(): void => onSelect(acc)}
+                    style={{ cursor: "pointer" }}
+                  >
+                    {ac.account}
+                  </td>
+                  <td class="is-actions-cell right-sticky">
+                    <div class="buttons is-right">
+                      <button
+                        class="button is-danger is-small has-tooltip-left"
+                        data-tooltip={i18n.str`delete selected accounts from 
the database`}
+                        onClick={() => onDelete(acc)}
+                      >
+                        Delete
+                      </button>
+                    </div>
+                  </td>
+                </tr>
+              );
+            })}
+          </tbody>
+        </table>
+      </div>}
+
+      {ibanAccounts.length > 0 && <div class="table-container">
+        <p class="card-header-title"><i18n.Translate>IBAN type 
accounts</i18n.Translate></p>
+        <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
+          <thead>
+            <tr>
+              <th>
+                <i18n.Translate>Account name</i18n.Translate>
+              </th>
+              <th>
+                <i18n.Translate>IBAN</i18n.Translate>
+              </th>
+              <th>
+                <i18n.Translate>BIC</i18n.Translate>
+              </th>
+              <th />
+            </tr>
+          </thead>
+          <tbody>
+            {ibanAccounts.map(({ parsed, acc }, idx) => {
+              const ac = parsed as PaytoUriIBAN
+              return (
+                <tr key={idx}>
+                  <td
+                    onClick={(): void => onSelect(acc)}
+                    style={{ cursor: "pointer" }}
+                  >
+                    {ac.params["receiver-name"]}
+                  </td>
+                  <td
+                    onClick={(): void => onSelect(acc)}
+                    style={{ cursor: "pointer" }}
+                  >
+                    {ac.iban}
+                  </td>
+                  <td
+                    onClick={(): void => onSelect(acc)}
+                    style={{ cursor: "pointer" }}
+                  >
+                    {ac.bic ?? ""}
+                  </td>
+                  <td class="is-actions-cell right-sticky">
+                    <div class="buttons is-right">
+                      <button
+                        class="button is-danger is-small has-tooltip-left"
+                        data-tooltip={i18n.str`delete selected accounts from 
the database`}
+                        onClick={() => onDelete(acc)}
+                      >
+                        Delete
+                      </button>
+                    </div>
+                  </td>
+                </tr>
+              );
+            })}
+          </tbody>
+        </table>
+      </div>}
+
+      {unkownAccounts.length > 0 && <div class="table-container">
+        <p class="card-header-title"><i18n.Translate>Other type 
accounts</i18n.Translate></p>
+        <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
+          <thead>
+            <tr>
+              <th>
+                <i18n.Translate>Type</i18n.Translate>
+              </th>
+              <th>
+                <i18n.Translate>Path</i18n.Translate>
+              </th>
+              <th />
+            </tr>
+          </thead>
+          <tbody>
+            {unkownAccounts.map(({ parsed, acc }, idx) => {
+              const ac = parsed as PaytoUriUnknown
+              return (
+                <tr key={idx}>
+                  <td
+                    onClick={(): void => onSelect(acc)}
+                    style={{ cursor: "pointer" }}
+                  >
+                    {ac.targetType}
+                  </td>
+                  <td
+                    onClick={(): void => onSelect(acc)}
+                    style={{ cursor: "pointer" }}
+                  >
+                    {ac.targetPath}
+                  </td>
+                  <td class="is-actions-cell right-sticky">
+                    <div class="buttons is-right">
+                      <button
+                        class="button is-danger is-small has-tooltip-left"
+                        data-tooltip={i18n.str`delete selected accounts from 
the database`}
+                        onClick={() => onDelete(acc)}
+                      >
+                        Delete
+                      </button>
+                    </div>
+                  </td>
+                </tr>
+              );
+            })}
+          </tbody>
+        </table>
+      </div>}
+    </Fragment>
+
+  );
+}
+
+function EmptyTable(): VNode {
+  const { i18n } = useTranslationContext();
+  return (
+    <div class="content has-text-grey has-text-centered">
+      <p>
+        <span class="icon is-large">
+          <i class="mdi mdi-emoticon-sad mdi-48px" />
+        </span>
+      </p>
+      <p>
+        <i18n.Translate>
+          There is no accounts yet, add more pressing the + sign
+        </i18n.Translate>
+      </p>
+    </div>
+  );
+}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
similarity index 76%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
index 2f91298bf..9788ce0ec 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
@@ -19,23 +19,21 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { HttpStatusCode } from "@gnu-taler/taler-util";
 import {
   ErrorType,
   HttpError,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import {
-  useInstanceTemplates,
-  useTemplateAPI,
-} from "../../../../hooks/templates.js";
+import { useInstanceOtpDevices, useOtpDeviceAPI } from 
"../../../../hooks/otp.js";
 import { Notification } from "../../../../utils/types.js";
 import { ListPage } from "./ListPage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { useBankAccountAPI, useInstanceBankAccounts } from 
"../../../../hooks/bank.js";
 
 interface Props {
   onUnauthorized: () => VNode;
@@ -43,24 +41,20 @@ interface Props {
   onNotFound: () => VNode;
   onCreate: () => void;
   onSelect: (id: string) => void;
-  onNewOrder: (id: string) => void;
-  onQR: (id: string) => void;
 }
 
-export default function ListTemplates({
+export default function ListValidators({
   onUnauthorized,
   onLoadError,
   onCreate,
-  onQR,
   onSelect,
-  onNewOrder,
   onNotFound,
 }: Props): VNode {
   const [position, setPosition] = useState<string | undefined>(undefined);
   const { i18n } = useTranslationContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const { deleteTemplate } = useTemplateAPI();
-  const result = useInstanceTemplates({ position }, (id) => setPosition(id));
+  const { deleteBankAccount } = useBankAccountAPI();
+  const result = useInstanceBankAccounts({ position }, (id) => 
setPosition(id));
 
   if (result.loading) return <Loading />;
   if (!result.ok) {
@@ -82,32 +76,26 @@ export default function ListTemplates({
       <NotificationCard notification={notif} />
 
       <ListPage
-        templates={result.data.templates}
+        devices={result.data.accounts}
         onLoadMoreBefore={
           result.isReachingStart ? result.loadMorePrev : undefined
         }
         onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
         onCreate={onCreate}
         onSelect={(e) => {
-          onSelect(e.template_id);
-        }}
-        onNewOrder={(e) => {
-          onNewOrder(e.template_id);
-        }}
-        onQR={(e) => {
-          onQR(e.template_id);
+          onSelect(e.h_wire);
         }}
-        onDelete={(e: MerchantBackend.Template.TemplateEntry) =>
-          deleteTemplate(e.template_id)
+        onDelete={(e: MerchantBackend.BankAccounts.BankAccountEntry) =>
+          deleteBankAccount(e.h_wire)
             .then(() =>
               setNotif({
-                message: i18n.str`template delete successfully`,
+                message: i18n.str`bank account delete successfully`,
                 type: "SUCCESS",
               }),
             )
             .catch((error) =>
               setNotif({
-                message: i18n.str`could not delete the template`,
+                message: i18n.str`could not delete the bank account`,
                 type: "ERROR",
                 description: error.message,
               }),
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx
similarity index 58%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx
index 045c96c2c..fcb77b820 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx
@@ -23,39 +23,10 @@ import { h, VNode, FunctionalComponent } from "preact";
 import { UpdatePage as TestedComponent } from "./UpdatePage.js";
 
 export default {
-  title: "Pages/Instance/Update",
+  title: "Pages/Validators/Update",
   component: TestedComponent,
   argTypes: {
     onUpdate: { action: "onUpdate" },
     onBack: { action: "onBack" },
   },
 };
-
-function createExample<Props>(
-  Component: FunctionalComponent<Props>,
-  props: Partial<Props>,
-) {
-  const r = (args: any) => <Component {...args} />;
-  r.args = props;
-  return r;
-}
-
-export const Example = createExample(TestedComponent, {
-  selected: {
-    accounts: [],
-    name: "name",
-    auth: { method: "external" },
-    address: {},
-    jurisdiction: {},
-    default_max_deposit_fee: "TESTKUDOS:2",
-    default_max_wire_fee: "TESTKUDOS:1",
-    default_pay_delay: {
-      d_us: 1000 * 1000, //one second
-    },
-    default_wire_fee_amortization: 1,
-    default_wire_transfer_delay: {
-      d_us: 1000 * 1000, //one second
-    },
-    merchant_pub: "ASDWQEKASJDKSADJ",
-  },
-});
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
new file mode 100644
index 000000000..802f593cf
--- /dev/null
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
@@ -0,0 +1,114 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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 { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
+import {
+  FormErrors,
+  FormProvider,
+} from "../../../../components/form/FormProvider.js";
+import { Input } from "../../../../components/form/Input.js";
+import { MerchantBackend, WithId } from "../../../../declaration.js";
+
+type Entity = MerchantBackend.BankAccounts.AccountPatchDetails & WithId;
+
+interface Props {
+  onUpdate: (d: Entity) => Promise<void>;
+  onBack?: () => void;
+  account: Entity;
+}
+export function UpdatePage({ account, onUpdate, onBack }: Props): VNode {
+  const { i18n } = useTranslationContext();
+
+  const [state, setState] = useState<Partial<Entity>>(account);
+
+  const errors: FormErrors<Entity> = {
+  };
+
+  const hasErrors = Object.keys(errors).some(
+    (k) => (errors as any)[k] !== undefined,
+  );
+
+  const submitForm = () => {
+    if (hasErrors) return Promise.reject();
+    return onUpdate(state as any);
+  };
+
+  return (
+    <div>
+      <section class="section">
+        <section class="hero is-hero-bar">
+          <div class="hero-body">
+            <div class="level">
+              <div class="level-left">
+                <div class="level-item">
+                  <span class="is-size-4">
+                    Account: <b>{account.id}</b>
+                  </span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </section>
+        <hr />
+
+        <section class="section is-main-section">
+          <div class="columns">
+            <div class="column is-four-fifths">
+              <FormProvider
+                object={state}
+                valueHandler={setState}
+                errors={errors}
+              >
+                <Input<Entity>
+                  name="credit_facade_url"
+                  label={i18n.str`Description`}
+                  tooltip={i18n.str`dddd`}
+                />
+              </FormProvider>
+
+              <div class="buttons is-right mt-5">
+                {onBack && (
+                  <button class="button" onClick={onBack}>
+                    <i18n.Translate>Cancel</i18n.Translate>
+                  </button>
+                )}
+                <AsyncButton
+                  disabled={hasErrors}
+                  data-tooltip={
+                    hasErrors
+                      ? i18n.str`Need to complete marked fields`
+                      : "confirm operation"
+                  }
+                  onClick={submitForm}
+                >
+                  <i18n.Translate>Confirm</i18n.Translate>
+                </AsyncButton>
+              </div>
+            </div>
+          </div>
+        </section>
+      </section>
+    </div>
+  );
+}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
similarity index 64%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
index 1f74afc2b..44dee7651 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
@@ -19,43 +19,45 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { HttpStatusCode } from "@gnu-taler/taler-util";
 import {
   ErrorType,
   HttpError,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import {
-  useTemplateAPI,
-  useTemplateDetails,
-} from "../../../../hooks/templates.js";
+import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { useBankAccountAPI, useBankAccountDetails } from 
"../../../../hooks/bank.js";
 import { Notification } from "../../../../utils/types.js";
-import { QrPage } from "./QrPage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { UpdatePage } from "./UpdatePage.js";
+
+export type Entity = MerchantBackend.BankAccounts.AccountPatchDetails & WithId;
 
-export type Entity = MerchantBackend.Transfers.TransferInformation;
 interface Props {
   onBack?: () => void;
+  onConfirm: () => void;
   onUnauthorized: () => VNode;
   onNotFound: () => VNode;
   onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
-  tid: string;
+  bid: string;
 }
-
-export default function TemplateQrPage({
-  tid,
+export default function UpdateValidator({
+  bid,
+  onConfirm,
   onBack,
-  onLoadError,
-  onNotFound,
   onUnauthorized,
+  onNotFound,
+  onLoadError,
 }: Props): VNode {
-  const result = useTemplateDetails(tid);
+  const { updateBankAccount } = useBankAccountAPI();
+  const result = useBankAccountDetails(bid);
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
 
+  const { i18n } = useTranslationContext();
+
   if (result.loading) return <Loading />;
   if (!result.ok) {
     if (
@@ -72,9 +74,23 @@ export default function TemplateQrPage({
   }
 
   return (
-    <>
+    <Fragment>
       <NotificationCard notification={notif} />
-      <QrPage template={result.data} id={tid} onBack={onBack} />
-    </>
+      <UpdatePage
+        account={{ ...result.data, id: bid }}
+        onBack={onBack}
+        onUpdate={(data) => {
+          return updateBankAccount(bid, data)
+            .then(onConfirm)
+            .catch((error) => {
+              setNotif({
+                message: i18n.str`could not update account`,
+                type: "ERROR",
+                description: error.message,
+              });
+            });
+        }}
+      />
+    </Fragment>
   );
 }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx
index e5937ab7b..21dadb1e3 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx
@@ -36,14 +36,13 @@ interface Props {
 function convert(
   from: MerchantBackend.Instances.QueryInstancesResponse,
 ): Entity {
-  const { accounts: allAccounts, ...rest } = from;
-  const accounts = allAccounts.filter((a) => a.active);
   const defaults = {
     default_wire_fee_amortization: 1,
+    use_stefan: true,
     default_pay_delay: { d_us: 1000 * 60 * 60 * 1000 }, //one hour
     default_wire_transfer_delay: { d_us: 1000 * 60 * 60 * 2 * 1000 }, //two 
hours
   };
-  return { ...defaults, ...rest, accounts };
+  return { ...defaults, ...from };
 }
 
 export function DetailPage({ selected }: Props): VNode {
@@ -74,11 +73,6 @@ export function DetailPage({ selected }: Props): VNode {
           <div class="column is-6">
             <FormProvider<Entity> object={value} valueHandler={valueHandler}>
               <Input<Entity> name="name" readonly label={i18n.str`Name`} />
-              <Input<Entity>
-                name="accounts"
-                readonly
-                label={i18n.str`Account address`}
-              />
             </FormProvider>
           </div>
           <div class="column" />
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx
index 3a6e0fbfe..367fabce2 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx
@@ -51,17 +51,15 @@ function createExample<Props>(
 
 export const Example = createExample(TestedComponent, {
   selected: {
-    accounts: [],
     name: "name",
     auth: { method: "external" },
     address: {},
+    user_type: "business",
     jurisdiction: {},
-    default_max_deposit_fee: "TESTKUDOS:2",
-    default_max_wire_fee: "TESTKUDOS:1",
+    use_stefan: true,
     default_pay_delay: {
       d_us: 1000 * 1000, //one second
     },
-    default_wire_fee_amortization: 1,
     default_wire_transfer_delay: {
       d_us: 1000 * 1000, //one second
     },
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.stories.tsx
index 6f50ac830..d33f64ada 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.stories.tsx
@@ -54,5 +54,5 @@ export const Example = tests.createExample(TestedComponent, {
         payto_uri: "payto://iban/de123123123",
       },
     ],
-  } as MerchantBackend.Instances.AccountKycRedirects,
+  } as MerchantBackend.KYC.AccountKycRedirects,
 });
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx
index 67005d3cc..338081886 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx
@@ -24,7 +24,7 @@ import { h, VNode } from "preact";
 import { MerchantBackend } from "../../../../declaration.js";
 
 export interface Props {
-  status: MerchantBackend.Instances.AccountKycRedirects;
+  status: MerchantBackend.KYC.AccountKycRedirects;
 }
 
 export function ListPage({ status }: Props): VNode {
@@ -85,11 +85,11 @@ export function ListPage({ status }: Props): VNode {
   );
 }
 interface PendingTableProps {
-  entries: MerchantBackend.Instances.MerchantAccountKycRedirect[];
+  entries: MerchantBackend.KYC.MerchantAccountKycRedirect[];
 }
 
 interface TimedOutTableProps {
-  entries: MerchantBackend.Instances.ExchangeKycTimeout[];
+  entries: MerchantBackend.KYC.ExchangeKycTimeout[];
 }
 
 function PendingTable({ entries }: PendingTableProps): VNode {
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx
index fcf611c3c..bd9f65718 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx
@@ -42,12 +42,13 @@ function createExample<Props>(
 
 export const Example = createExample(TestedComponent, {
   instanceConfig: {
-    default_max_deposit_fee: "",
-    default_max_wire_fee: "",
     default_pay_delay: {
       d_us: 1000 * 1000 * 60 * 60, //one hour
     },
-    default_wire_fee_amortization: 1,
+    default_wire_transfer_delay: {
+      d_us: 1000 * 1000 * 60 * 60, //one hour
+    },
+    use_stefan: true,
   },
   instanceInventory: [
     {
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
index fa9347c6e..ea2cf849a 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
@@ -44,6 +44,7 @@ import { OrderCreateSchema as schema } from 
"../../../../schemas/index.js";
 import { rate } from "../../../../utils/amount.js";
 import { undefinedIfEmpty } from "../../../../utils/table.js";
 import { useSettings } from "../../../../hooks/useSettings.js";
+import { InputToggle } from "../../../../components/form/InputToggle.js";
 
 interface Props {
   onCreate: (d: MerchantBackend.Orders.PostOrderRequest) => void;
@@ -52,34 +53,38 @@ interface Props {
   instanceInventory: (MerchantBackend.Products.ProductDetail & WithId)[];
 }
 interface InstanceConfig {
-  default_max_wire_fee: string;
-  default_max_deposit_fee: string;
-  default_wire_fee_amortization: number;
+  use_stefan: boolean;
   default_pay_delay: Duration;
+  default_wire_transfer_delay: Duration;
 }
 
-function with_defaults(config: InstanceConfig): Partial<Entity> {
+function with_defaults(config: InstanceConfig, currency: string): 
Partial<Entity> {
   const defaultPayDeadline =
     !config.default_pay_delay || config.default_pay_delay.d_us === "forever"
       ? undefined
       : add(new Date(), {
         seconds: config.default_pay_delay.d_us / (1000 * 1000),
       });
+  const defaultWireDeadline =
+    !config.default_wire_transfer_delay || 
config.default_wire_transfer_delay.d_us === "forever"
+      ? undefined
+      : add(new Date(), {
+        seconds: config.default_wire_transfer_delay.d_us / (1000 * 1000),
+      });
 
   return {
     inventoryProducts: {},
     products: [],
     pricing: {},
     payments: {
-      max_wire_fee: config.default_max_wire_fee,
-      max_fee: config.default_max_deposit_fee,
-      wire_fee_amortization: config.default_wire_fee_amortization,
+      max_fee: undefined,
       pay_deadline: defaultPayDeadline,
       refund_deadline: defaultPayDeadline,
       createToken: true,
+      wire_transfer_deadline: defaultWireDeadline,
     },
     shipping: {},
-    extra: "",
+    extra: {},
   };
 }
 
@@ -107,8 +112,6 @@ interface Payments {
   wire_transfer_deadline?: Date;
   auto_refund_deadline?: Date;
   max_fee?: string;
-  max_wire_fee?: string;
-  wire_fee_amortization?: number;
   createToken: boolean;
   minimum_age?: number;
 }
@@ -118,7 +121,7 @@ interface Entity {
   pricing: Partial<Pricing>;
   payments: Partial<Payments>;
   shipping: Partial<Shipping>;
-  extra: string;
+  extra: Record<string, string>;
 }
 
 const stringIsValidJSON = (value: string) => {
@@ -136,8 +139,9 @@ export function CreatePage({
   instanceConfig,
   instanceInventory,
 }: Props): VNode {
-  const [value, valueHandler] = useState(with_defaults(instanceConfig));
   const config = useConfigContext();
+  const instance_default = with_defaults(instanceConfig, config.currency)
+  const [value, valueHandler] = useState(instance_default);
   const zero = Amounts.zeroOfCurrency(config.currency);
   const [settings] = useSettings()
   const inventoryList = Object.values(value.inventoryProducts || {});
@@ -160,10 +164,10 @@ export function CreatePage({
             ? i18n.str`must be greater than 0`
             : undefined,
     }),
-    extra:
-      value.extra && !stringIsValidJSON(value.extra)
-        ? i18n.str`not a valid json`
-        : undefined,
+    // extra:
+    //   value.extra && !stringIsValidJSON(value.extra)
+    //     ? i18n.str`not a valid json`
+    //     : undefined,
     payments: undefinedIfEmpty({
       refund_deadline: !value.payments?.refund_deadline
         ? undefined
@@ -202,6 +206,7 @@ export function CreatePage({
             )
               ? i18n.str`auto refund cannot be after refund deadline`
               : undefined,
+
     }),
     shipping: undefinedIfEmpty({
       delivery_date: !value.shipping?.delivery_date
@@ -225,7 +230,7 @@ export function CreatePage({
         amount: order.pricing.order_price,
         summary: order.pricing.summary,
         products: productList,
-        extra: value.extra,
+        extra: JSON.stringify(value.extra),
         pay_deadline: value.payments.pay_deadline
           ? {
             t_s: Math.floor(value.payments.pay_deadline.getTime() / 1000),
@@ -250,9 +255,7 @@ export function CreatePage({
             ),
           }
           : undefined,
-        wire_fee_amortization: value.payments.wire_fee_amortization as number,
         max_fee: value.payments.max_fee as string,
-        max_wire_fee: value.payments.max_wire_fee as string,
 
         delivery_date: value.shipping.delivery_date
           ? { t_s: value.shipping.delivery_date.getTime() / 1000 }
@@ -326,6 +329,8 @@ export function CreatePage({
   const totalAsString = Amounts.stringify(totalPrice.amount);
   const allProducts = productList.concat(inventoryList.map(asProduct));
 
+  const [newField, setNewField] = useState("")
+
   useEffect(() => {
     valueHandler((v) => {
       return {
@@ -486,16 +491,61 @@ export function CreatePage({
                     name="payments.pay_deadline"
                     label={i18n.str`Payment deadline`}
                     tooltip={i18n.str`Deadline for the customer to pay for the 
offer before it expires. Inventory products will be reserved until this 
deadline.`}
+                    side={
+                      <span>
+                        <button class="button" onClick={() => {
+                          valueHandler({
+                            ...value,
+                            payments: {
+                              ...(value.payments ?? {}),
+                              pay_deadline: 
instance_default.payments?.pay_deadline
+                            }
+                          })
+                        }}>
+                          <i18n.Translate>default</i18n.Translate>
+                        </button>
+                      </span>
+                    }
                   />
                   <InputDate
                     name="payments.refund_deadline"
                     label={i18n.str`Refund deadline`}
                     tooltip={i18n.str`Time until which the order can be 
refunded by the merchant.`}
+                    side={
+                      <span>
+                        <button class="button" onClick={() => {
+                          valueHandler({
+                            ...value,
+                            payments: {
+                              ...(value.payments ?? {}),
+                              refund_deadline: 
instance_default.payments?.refund_deadline
+                            }
+                          })
+                        }}>
+                          <i18n.Translate>default</i18n.Translate>
+                        </button>
+                      </span>
+                    }
                   />
                   <InputDate
                     name="payments.wire_transfer_deadline"
                     label={i18n.str`Wire transfer deadline`}
                     tooltip={i18n.str`Deadline for the exchange to make the 
wire transfer.`}
+                    side={
+                      <span>
+                        <button class="button" onClick={() => {
+                          valueHandler({
+                            ...value,
+                            payments: {
+                              ...(value.payments ?? {}),
+                              wire_transfer_deadline: 
instance_default.payments?.wire_transfer_deadline
+                            }
+                          })
+                        }}>
+                          <i18n.Translate>default</i18n.Translate>
+                        </button>
+                      </span>
+                    }
                   />
                   <InputDate
                     name="payments.auto_refund_deadline"
@@ -505,23 +555,13 @@ export function CreatePage({
 
                   <InputCurrency
                     name="payments.max_fee"
-                    label={i18n.str`Maximum deposit fee`}
-                    tooltip={i18n.str`Maximum deposit fees the merchant is 
willing to cover for this order. Higher deposit fees must be covered in full by 
the consumer.`}
+                    label={i18n.str`Maximum fee`}
+                    tooltip={i18n.str`Maximum fees the merchant is willing to 
cover for this order. Higher deposit fees must be covered in full by the 
consumer.`}
                   />
-                  <InputCurrency
-                    name="payments.max_wire_fee"
-                    label={i18n.str`Maximum wire fee`}
-                    tooltip={i18n.str`Maximum aggregate wire fees the merchant 
is willing to cover for this order. Wire fees exceeding this amount are to be 
covered by the customers.`}
-                  />
-                  <InputNumber
-                    name="payments.wire_fee_amortization"
-                    label={i18n.str`Wire fee amortization`}
-                    tooltip={i18n.str`Factor by which wire fees exceeding the 
above threshold are divided to determine the share of excess wire fees to be 
paid explicitly by the consumer.`}
-                  />
-                  <InputBoolean
+                  <InputToggle
                     name="payments.createToken"
                     label={i18n.str`Create token`}
-                    tooltip={i18n.str`Uncheck this option if the merchant 
backend generated an order ID with enough entropy to prevent adversarial 
claims.`}
+                    tooltip={i18n.str`If the order ID is easy to guess the 
token will prevent user to steal orders from others.`}
                   />
                   <InputNumber
                     name="payments.minimum_age"
@@ -530,7 +570,7 @@ export function CreatePage({
                     help={
                       minAgeByProducts > 0
                         ? i18n.str`Min age defined by the producs is 
${minAgeByProducts}`
-                        : undefined
+                        : i18n.str`No product with age restriction in this 
order`
                     }
                   />
                 </InputGroup>
@@ -542,12 +582,53 @@ export function CreatePage({
                   label={i18n.str`Additional information`}
                   tooltip={i18n.str`Custom information to be included in the 
contract for this order.`}
                 >
-                  <Input
-                    name="extra"
-                    inputType="multiline"
-                    label={`Value`}
-                    tooltip={i18n.str`You must enter a value in JavaScript 
Object Notation (JSON).`}
-                  />
+                  {Object.keys(value.extra ?? {}).map((key) => {
+
+                    return <Input
+                      name={`extra.${key}`}
+                      inputType="multiline"
+                      label={key}
+                      tooltip={i18n.str`You must enter a value in JavaScript 
Object Notation (JSON).`}
+                      side={
+                        <button class="button" onClick={(e) => {
+                          if (value.extra && value.extra[key] !== undefined) {
+                            console.log(value.extra)
+                            delete value.extra[key]
+                          }
+                          valueHandler({
+                            ...value,
+                          })
+                        }}>remove</button>
+                      }
+                    />
+                  })}
+                  <div class="field is-horizontal">
+                    <div class="field-label is-normal">
+                      <label class="label">
+                        <i18n.Translate>Custom field name</i18n.Translate>
+                        <span class="icon has-tooltip-right" 
data-tooltip={"new extra field"}>
+                          <i class="mdi mdi-information" />
+                        </span>
+                      </label>
+                    </div>
+                    <div class="field-body is-flex-grow-3">
+                      <div class="field">
+                        <p class="control">
+                          <input class="input " value={newField} onChange={(e) 
=> setNewField(e.currentTarget.value)} />
+                        </p>
+                      </div>
+                    </div>
+                    <button class="button" onClick={(e) => {
+                      setNewField("")
+                      valueHandler({
+                        ...value,
+                        extra: {
+                          ...(value.extra ?? {}),
+                          [newField]: ""
+                        }
+                      })
+                    }}>add</button>
+                  </div>
                 </InputGroup>
               }
             </FormProvider>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx
index ffefd5302..2474fd042 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx
@@ -38,7 +38,7 @@ export type Entity = {
 };
 interface Props {
   onBack?: () => void;
-  onConfirm: () => void;
+  onConfirm: (id: string) => void;
   onUnauthorized: () => VNode;
   onNotFound: () => VNode;
   onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
@@ -95,7 +95,9 @@ export default function OrderCreate({
         onBack={onBack}
         onCreate={(request: MerchantBackend.Orders.PostOrderRequest) => {
           createOrder(request)
-            .then(onConfirm)
+            .then((r) => {
+              return onConfirm(r.data.order_id)
+            })
             .catch((error) => {
               setNotif({
                 message: "could not create order",
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx
index e430ede56..6e73a01a5 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx
@@ -50,13 +50,11 @@ const defaultContractTerm = {
   auditors: [],
   exchanges: [],
   max_fee: "TESTKUDOS:1",
-  max_wire_fee: "TESTKUDOS:1",
   merchant: {} as any,
   merchant_base_url: "http://merchant.url/";,
   order_id: "2021.165-03GDFC26Y1NNG",
   products: [],
   summary: "text summary",
-  wire_fee_amortization: 1,
   wire_transfer_deadline: {
     t_s: "never",
   },
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
index 8965d41c9..e42adc2ff 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { AmountJson, Amounts } from "@gnu-taler/taler-util";
+import { AmountJson, Amounts, stringifyRefundUri } from 
"@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { format, formatDistance } from "date-fns";
 import { Fragment, h, VNode } from "preact";
@@ -38,6 +38,7 @@ import { MerchantBackend } from "../../../../declaration.js";
 import { mergeRefunds } from "../../../../utils/amount.js";
 import { RefundModal } from "../list/Table.js";
 import { Event, Timeline } from "./Timeline.js";
+import { dateFormatForSettings, datetimeFormatForSettings, useSettings } from 
"../../../../hooks/useSettings.js";
 
 type Entity = MerchantBackend.Orders.MerchantOrderStatusResponse;
 type CT = MerchantBackend.ContractTerms;
@@ -87,18 +88,6 @@ function ContractTerms({ value }: { value: CT }) {
           label={i18n.str`Max fee`}
           tooltip={i18n.str`maximum total deposit fee accepted by the merchant 
for this contract`}
         />
-        <Input<CT>
-          readonly
-          name="max_wire_fee"
-          label={i18n.str`Max wire fee`}
-          tooltip={i18n.str`maximum wire fee accepted by the merchant`}
-        />
-        <Input<CT>
-          readonly
-          name="wire_fee_amortization"
-          label={i18n.str`Wire fee amortization`}
-          tooltip={i18n.str`over how many customer transactions does the 
merchant expect to amortize wire fees on average`}
-        />
         <InputDate<CT>
           readonly
           name="timestamp"
@@ -204,6 +193,7 @@ function ClaimedPage({
 
   const [value, valueHandler] = useState<Partial<Claimed>>(order);
   const { i18n } = useTranslationContext();
+  const [settings] = useSettings()
 
   return (
     <div>
@@ -249,7 +239,7 @@ function ClaimedPage({
                           </b>{" "}
                           {format(
                             new Date(order.contract_terms.timestamp.t_s * 
1000),
-                            "yyyy-MM-dd HH:mm:ss",
+                            datetimeFormatForSettings(settings)
                           )}
                         </p>
                       </div>
@@ -427,9 +417,10 @@ function PaidPage({
 
   const [value, valueHandler] = useState<Partial<Paid>>(order);
   const { url } = useBackendContext();
-  const refundHost = url.replace(/.*:\/\//, ""); // remove protocol part
-  const proto = url.startsWith("http://";) ? "taler+http" : "taler";
-  const refundurl = 
`${proto}://refund/${refundHost}/${order.contract_terms.order_id}/`;
+  const refundurl = stringifyRefundUri({
+    merchantBaseUrl: url,
+    orderId: order.contract_terms.order_id
+  })
   const refundable =
     new Date().getTime() < order.contract_terms.refund_deadline.t_s * 1000;
   const { i18n } = useTranslationContext();
@@ -618,6 +609,7 @@ function UnpaidPage({
 }) {
   const [value, valueHandler] = useState<Partial<Unpaid>>(order);
   const { i18n } = useTranslationContext();
+  const [settings] = useSettings()
   return (
     <div>
       <section class="hero is-hero-bar">
@@ -666,7 +658,7 @@ function UnpaidPage({
                       ? "never"
                       : format(
                         new Date(order.creation_time.t_s * 1000),
-                        "yyyy-MM-dd HH:mm:ss",
+                        datetimeFormatForSettings(settings)
                       )}
                   </p>
                 </div>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx
index e68889a92..8c863f386 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx
@@ -16,6 +16,7 @@
 import { format } from "date-fns";
 import { h } from "preact";
 import { useEffect, useState } from "preact/hooks";
+import { datetimeFormatForSettings, useSettings } from 
"../../../../hooks/useSettings.js";
 
 interface Props {
   events: Event[];
@@ -30,7 +31,7 @@ export function Timeline({ events: e }: Props) {
   });
 
   events.sort((a, b) => a.when.getTime() - b.when.getTime());
-
+  const [settings] = useSettings();
   const [state, setState] = useState(events);
   useEffect(() => {
     const handle = setTimeout(() => {
@@ -104,7 +105,7 @@ export function Timeline({ events: e }: Props) {
               }
             })()}
             <div class="timeline-content">
-              {e.description !== "now" && <p class="heading">{format(e.when, 
"yyyy/MM/dd HH:mm:ss")}</p>}
+              {e.description !== "now" && <p class="heading">{format(e.when, 
datetimeFormatForSettings(settings))}</p>}
               <p>{e.description}</p>
             </div>
           </div>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
index 37770d273..c29a6fa6e 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
@@ -26,19 +26,24 @@ import { useState } from "preact/hooks";
 import { DatePicker } from "../../../../components/picker/DatePicker.js";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
 import { CardTable } from "./Table.js";
+import { dateFormatForSettings, useSettings } from 
"../../../../hooks/useSettings.js";
 
 export interface ListPageProps {
   errorOrderId: string | undefined;
 
   onShowAll: () => void;
+  onShowNotPaid: () => void;
   onShowPaid: () => void;
   onShowRefunded: () => void;
   onShowNotWired: () => void;
+  onShowWired: () => void;
   onCopyURL: (id: string) => void;
   isAllActive: string;
   isPaidActive: string;
+  isNotPaidActive: string;
   isRefundedActive: string;
   isNotWiredActive: string;
+  isWiredActive: string;
 
   jumpToDate?: Date;
   onSelectDate: (date?: Date) => void;
@@ -66,18 +71,23 @@ export function ListPage({
   onCopyURL,
   onShowAll,
   onShowPaid,
+  onShowNotPaid,
   onShowRefunded,
   onShowNotWired,
+  onShowWired,
   onSelectDate,
   isPaidActive,
   isRefundedActive,
   isNotWiredActive,
   onCreate,
+  isNotPaidActive,
+  isWiredActive,
 }: ListPageProps): VNode {
   const { i18n } = useTranslationContext();
   const dateTooltip = i18n.str`select date to show nearby orders`;
   const [pickDate, setPickDate] = useState(false);
   const [orderId, setOrderId] = useState<string>("");
+  const [settings] = useSettings();
 
   return (
     <section class="section is-main-section">
@@ -116,13 +126,13 @@ export function ListPage({
         <div class="column is-two-thirds">
           <div class="tabs" style={{ overflow: "inherit" }}>
             <ul>
-              <li class={isAllActive}>
+              <li class={isNotPaidActive}>
                 <div
                   class="has-tooltip-right"
-                  data-tooltip={i18n.str`remove all filters`}
+                  data-tooltip={i18n.str`only show paid orders`}
                 >
-                  <a onClick={onShowAll}>
-                    <i18n.Translate>All</i18n.Translate>
+                  <a onClick={onShowNotPaid}>
+                    <i18n.Translate>New</i18n.Translate>
                   </a>
                 </div>
               </li>
@@ -156,6 +166,26 @@ export function ListPage({
                   </a>
                 </div>
               </li>
+              <li class={isWiredActive}>
+                <div
+                  class="has-tooltip-left"
+                  data-tooltip={i18n.str`only show orders where customers 
paid, but wire payments from payment provider are still pending`}
+                >
+                  <a onClick={onShowWired}>
+                    <i18n.Translate>Completed</i18n.Translate>
+                  </a>
+                </div>
+              </li>
+              <li class={isAllActive}>
+                <div
+                  class="has-tooltip-right"
+                  data-tooltip={i18n.str`remove all filters`}
+                >
+                  <a onClick={onShowAll}>
+                    <i18n.Translate>All</i18n.Translate>
+                  </a>
+                </div>
+              </li>
             </ul>
           </div>
         </div>
@@ -180,8 +210,8 @@ export function ListPage({
                     class="input"
                     type="text"
                     readonly
-                    value={!jumpToDate ? "" : format(jumpToDate, "yyyy/MM/dd")}
-                    placeholder={i18n.str`date (YYYY/MM/DD)`}
+                    value={!jumpToDate ? "" : format(jumpToDate, 
dateFormatForSettings(settings))}
+                    placeholder={i18n.str`date 
(${dateFormatForSettings(settings)})`}
                     onClick={() => {
                       setPickDate(true);
                     }}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx
index 3c927033b..608c9b20d 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx
@@ -36,6 +36,7 @@ import { ConfirmModal } from 
"../../../../components/modal/index.js";
 import { useConfigContext } from "../../../../context/config.js";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
 import { mergeRefunds } from "../../../../utils/amount.js";
+import { datetimeFormatForSettings, useSettings } from 
"../../../../hooks/useSettings.js";
 
 type Entity = MerchantBackend.Orders.OrderHistoryEntry & WithId;
 interface Props {
@@ -136,6 +137,7 @@ function Table({
   hasMoreBefore,
 }: TableProps): VNode {
   const { i18n } = useTranslationContext();
+  const [settings] = useSettings();
   return (
     <div class="table-container">
       {onLoadMoreBefore && (
@@ -173,9 +175,9 @@ function Table({
                   {i.timestamp.t_s === "never"
                     ? "never"
                     : format(
-                        new Date(i.timestamp.t_s * 1000),
-                        "yyyy/MM/dd HH:mm:ss",
-                      )}
+                      new Date(i.timestamp.t_s * 1000),
+                      datetimeFormatForSettings(settings),
+                    )}
                 </td>
                 <td
                   onClick={(): void => onSelect(i)}
@@ -260,6 +262,7 @@ export function RefundModal({
 }: RefundModalProps): VNode {
   type State = { mainReason?: string; description?: string; refund?: string };
   const [form, setValue] = useState<State>({});
+  const [settings] = useSettings();
   const { i18n } = useTranslationContext();
   // const [errors, setErrors] = useState<FormErrors<State>>({});
 
@@ -281,8 +284,8 @@ export function RefundModal({
   const totalRefundable = !orderPrice
     ? Amounts.zeroOfCurrency(totalRefunded.currency)
     : refunds.length
-    ? Amounts.sub(orderPrice, totalRefunded).amount
-    : orderPrice;
+      ? Amounts.sub(orderPrice, totalRefunded).amount
+      : orderPrice;
 
   const isRefundable = Amounts.isNonZero(totalRefundable);
   const duplicatedText = i18n.str`duplicated`;
@@ -296,10 +299,10 @@ export function RefundModal({
     refund: !form.refund
       ? i18n.str`required`
       : !Amounts.parse(form.refund)
-      ? i18n.str`invalid format`
-      : Amounts.cmp(totalRefundable, Amounts.parse(form.refund)!) === -1
-      ? i18n.str`this value exceed the refundable amount`
-      : undefined,
+        ? i18n.str`invalid format`
+        : Amounts.cmp(totalRefundable, Amounts.parse(form.refund)!) === -1
+          ? i18n.str`this value exceed the refundable amount`
+          : undefined,
   };
   const hasErrors = Object.keys(errors).some(
     (k) => (errors as any)[k] !== undefined,
@@ -361,9 +364,9 @@ export function RefundModal({
                           {r.timestamp.t_s === "never"
                             ? "never"
                             : format(
-                                new Date(r.timestamp.t_s * 1000),
-                                "yyyy-MM-dd HH:mm:ss",
-                              )}
+                              new Date(r.timestamp.t_s * 1000),
+                              datetimeFormatForSettings(settings),
+                            )}
                         </td>
                         <td>{r.amount}</td>
                         <td>{r.reason}</td>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
index 6888eda58..48f77e3d3 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
@@ -55,7 +55,7 @@ export default function OrderList({
   onSelect,
   onNotFound,
 }: Props): VNode {
-  const [filter, setFilter] = useState<InstanceOrderFilter>({});
+  const [filter, setFilter] = useState<InstanceOrderFilter>({ paid: "no" });
   const [orderToBeRefunded, setOrderToBeRefunded] = useState<
     MerchantBackend.Orders.OrderHistoryEntry | undefined
   >(undefined);
@@ -88,13 +88,15 @@ export default function OrderList({
     return onLoadError(result);
   }
 
-  const isPaidActive = filter.paid === "yes" ? "is-active" : "";
+  const isNotPaidActive = filter.paid === "no" ? "is-active" : "";
+  const isPaidActive = filter.paid === "yes" && filter.wired === undefined ? 
"is-active" : "";
   const isRefundedActive = filter.refunded === "yes" ? "is-active" : "";
-  const isNotWiredActive = filter.wired === "no" ? "is-active" : "";
+  const isNotWiredActive = filter.wired === "no" && filter.paid === "yes" ? 
"is-active" : "";
+  const isWiredActive = filter.wired === "yes" ? "is-active" : "";
   const isAllActive =
     filter.paid === undefined &&
-    filter.refunded === undefined &&
-    filter.wired === undefined
+      filter.refunded === undefined &&
+      filter.wired === undefined
       ? "is-active"
       : "";
 
@@ -127,7 +129,9 @@ export default function OrderList({
         errorOrderId={errorOrderId}
         isAllActive={isAllActive}
         isNotWiredActive={isNotWiredActive}
+        isWiredActive={isWiredActive}
         isPaidActive={isPaidActive}
+        isNotPaidActive={isNotPaidActive}
         isRefundedActive={isRefundedActive}
         jumpToDate={filter.date}
         onCopyURL={(id) =>
@@ -137,9 +141,11 @@ export default function OrderList({
         onSearchOrderById={testIfOrderExistAndSelect}
         onSelectDate={setNewDate}
         onShowAll={() => setFilter({})}
+        onShowNotPaid={() => setFilter({ paid: "no" })}
         onShowPaid={() => setFilter({ paid: "yes" })}
         onShowRefunded={() => setFilter({ refunded: "yes" })}
-        onShowNotWired={() => setFilter({ wired: "no" })}
+        onShowNotWired={() => setFilter({ wired: "no", paid: "yes" })}
+        onShowWired={() => setFilter({ wired: "yes" })}
       />
 
       {orderToBeRefunded && (
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx
index 6bbb89dfa..cbfe1d573 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx
@@ -32,6 +32,7 @@ import {
 import { InputCurrency } from "../../../../components/form/InputCurrency.js";
 import { InputNumber } from "../../../../components/form/InputNumber.js";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { dateFormatForSettings, useSettings } from 
"../../../../hooks/useSettings.js";
 
 type Entity = MerchantBackend.Products.ProductDetail & WithId;
 
@@ -122,6 +123,7 @@ function Table({
   onDelete,
 }: TableProps): VNode {
   const { i18n } = useTranslationContext();
+  const [settings] = useSettings();
   return (
     <div class="table-container">
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -134,7 +136,7 @@ function Table({
               <i18n.Translate>Description</i18n.Translate>
             </th>
             <th>
-              <i18n.Translate>Sell</i18n.Translate>
+              <i18n.Translate>Price per unit</i18n.Translate>
             </th>
             <th>
               <i18n.Translate>Taxes</i18n.Translate>
@@ -156,10 +158,10 @@ function Table({
             const restStockInfo = !i.next_restock
               ? ""
               : i.next_restock.t_s === "never"
-              ? "never"
-              : `restock at ${format(
+                ? "never"
+                : `restock at ${format(
                   new Date(i.next_restock.t_s * 1000),
-                  "yyyy/MM/dd",
+                  dateFormatForSettings(settings),
                 )}`;
             let stockInfo: ComponentChildren = "";
             if (i.total_stock < 0) {
@@ -332,26 +334,35 @@ function FastProductWithInfiniteStockUpdateForm({
         />
       </FormProvider>
 
-      <div class="buttons is-right mt-5">
-        <button class="button" onClick={onCancel}>
-          <i18n.Translate>Cancel</i18n.Translate>
-        </button>
-        <span
-          class="has-tooltip-left"
-          data-tooltip={i18n.str`update product with new price`}
-        >
-          <button
-            class="button is-info"
-            onClick={() =>
-              onUpdate({
-                ...product,
-                price: value.price,
-              })
-            }
-          >
-            <i18n.Translate>Confirm</i18n.Translate>
+      <div class="buttons is-expanded">
+
+        <div class="buttons mt-5">
+
+          <button class="button " onClick={onCancel}>
+            <i18n.Translate>Clone</i18n.Translate>
           </button>
-        </span>
+        </div>
+        <div class="buttons is-right mt-5">
+          <button class="button" onClick={onCancel}>
+            <i18n.Translate>Cancel</i18n.Translate>
+          </button>
+          <span
+            class="has-tooltip-left"
+            data-tooltip={i18n.str`update product with new price`}
+          >
+            <button
+              class="button is-info"
+              onClick={() =>
+                onUpdate({
+                  ...product,
+                  price: value.price,
+                })
+              }
+            >
+              <i18n.Translate>Confirm update</i18n.Translate>
+            </button>
+          </span>
+        </div>
       </div>
     </Fragment>
   );
@@ -374,9 +385,8 @@ function FastProductWithManagedStockUpdateForm({
   const errors: FormErrors<FastProductUpdate> = {
     lost:
       currentStock + value.incoming < value.lost
-        ? `lost cannot be greater that current + incoming (max ${
-            currentStock + value.incoming
-          })`
+        ? `lost cannot be greater that current + incoming (max ${currentStock 
+ value.incoming
+        })`
         : 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 87efd1554..85c50e5ed 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
@@ -36,6 +36,7 @@ import {
 import { Notification } from "../../../../utils/types.js";
 import { CardTable } from "./Table.js";
 import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { ConfirmModal, DeleteModal } from 
"../../../../components/modal/index.js";
 
 interface Props {
   onUnauthorized: () => VNode;
@@ -53,6 +54,8 @@ export default function ProductList({
 }: Props): VNode {
   const result = useInstanceProducts();
   const { deleteProduct, updateProduct } = useProductAPI();
+  const [deleting, setDeleting] =
+    useState<MerchantBackend.Products.ProductDetail & WithId | null>(null);
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
 
   const { i18n } = useTranslationContext();
@@ -97,22 +100,43 @@ export default function ProductList({
         }
         onSelect={(product) => onSelect(product.id)}
         onDelete={(prod: MerchantBackend.Products.ProductDetail & WithId) =>
-          deleteProduct(prod.id)
-            .then(() =>
+          setDeleting(prod)
+        }
+      />
+
+      {deleting && (
+        <ConfirmModal
+          label={`Delete product`}
+          description={`Delete the product "${deleting.description}"`}
+          danger
+          active
+          onCancel={() => setDeleting(null)}
+          onConfirm={async (): Promise<void> => {
+            try {
+              await deleteProduct(deleting.id);
               setNotif({
-                message: i18n.str`product delete successfully`,
+                message: i18n.str`Product "${deleting.description}" (ID: 
${deleting.id}) has been deleted`,
                 type: "SUCCESS",
-              }),
-            )
-            .catch((error) =>
+              });
+            } catch (error) {
               setNotif({
-                message: i18n.str`could not delete the product`,
+                message: i18n.str`Failed to delete product`,
                 type: "ERROR",
-                description: error.message,
-              }),
-            )
-        }
-      />
+                description: error instanceof Error ? error.message : 
undefined,
+              });
+            }
+            setDeleting(null);
+          }}
+        >
+          <p>
+            If you delete the product named 
<b>&quot;{deleting.description}&quot;</b> (ID:{" "}
+            <b>{deleting.id}</b>), the stock and related information will be 
lost
+          </p>
+          <p class="warning">
+            Deleting an product <b>cannot be undone</b>.
+          </p>
+        </ConfirmModal>
+      )}
     </section>
   );
 }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx
index fccb20121..2201e75a5 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx
@@ -36,7 +36,7 @@ import {
   URL_REGEX,
 } from "../../../../utils/constants.js";
 
-type Entity = MerchantBackend.Tips.ReserveCreateRequest;
+type Entity = MerchantBackend.Rewards.ReserveCreateRequest;
 
 interface Props {
   onCreate: (d: Entity) => Promise<void>;
@@ -80,15 +80,15 @@ function ViewStep({
         initial_balance: !reserve.initial_balance
           ? "cannot be empty"
           : !(parseInt(reserve.initial_balance.split(":")[1], 10) > 0)
-          ? i18n.str`it should be greater than 0`
-          : undefined,
+            ? i18n.str`it should be greater than 0`
+            : undefined,
         exchange_url: !reserve.exchange_url
           ? i18n.str`cannot be empty`
           : !URL_REGEX.test(reserve.exchange_url)
-          ? i18n.str`must be a valid URL`
-          : !exchangeQueryError
-          ? undefined
-          : exchangeQueryError,
+            ? i18n.str`must be a valid URL`
+            : !exchangeQueryError
+              ? undefined
+              : exchangeQueryError,
       };
 
       const hasErrors = Object.keys(errors).some(
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
index 94fcdaff7..1d512c843 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
@@ -22,8 +22,8 @@ import { CreatedSuccessfully as Template } from 
"../../../../components/notifica
 import { MerchantBackend, WireAccount } from "../../../../declaration.js";
 
 type Entity = {
-  request: MerchantBackend.Tips.ReserveCreateRequest;
-  response: MerchantBackend.Tips.ReserveCreateConfirmation;
+  request: MerchantBackend.Rewards.ReserveCreateRequest;
+  response: MerchantBackend.Rewards.ReserveCreateConfirmation;
 };
 
 interface Props {
@@ -98,15 +98,15 @@ export function ShowAccountsOfReserveAsQRWithLink({
   const accountsInfo = !accounts
     ? []
     : accounts
-        .map((acc) => {
-          const p = parsePaytoUri(acc.payto_uri);
-          if (p) {
-            p.params["message"] = message;
-            p.params["amount"] = amount;
-          }
-          return p;
-        })
-        .filter(isNotUndefined);
+      .map((acc) => {
+        const p = parsePaytoUri(acc.payto_uri);
+        if (p) {
+          p.params["message"] = message;
+          p.params["amount"] = amount;
+        }
+        return p;
+      })
+      .filter(isNotUndefined);
 
   const links = accountsInfo.map((a) => stringifyPaytoUri(a));
 
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx
index 8a4fe1565..4bbaf1459 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx
@@ -39,9 +39,9 @@ export default function CreateReserve({ onBack, onConfirm }: 
Props): VNode {
 
   const [createdOk, setCreatedOk] = useState<
     | {
-        request: MerchantBackend.Tips.ReserveCreateRequest;
-        response: MerchantBackend.Tips.ReserveCreateConfirmation;
-      }
+      request: MerchantBackend.Rewards.ReserveCreateRequest;
+      response: MerchantBackend.Rewards.ReserveCreateConfirmation;
+    }
     | undefined
   >(undefined);
 
@@ -54,7 +54,7 @@ export default function CreateReserve({ onBack, onConfirm }: 
Props): VNode {
       <NotificationCard notification={notif} />
       <CreatePage
         onBack={onBack}
-        onCreate={(request: MerchantBackend.Tips.ReserveCreateRequest) => {
+        onCreate={(request: MerchantBackend.Rewards.ReserveCreateRequest) => {
           return createReserve(request)
             .then((r) => setCreatedOk({ request, response: r.data }))
             .catch((error) => {
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx
index b0173b5d3..d8840eeac 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx
@@ -36,11 +36,12 @@ import { InputDate } from 
"../../../../components/form/InputDate.js";
 import { TextField } from "../../../../components/form/TextField.js";
 import { SimpleModal } from "../../../../components/modal/index.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import { useTipDetails } from "../../../../hooks/reserves.js";
-import { TipInfo } from "./TipInfo.js";
+import { useRewardDetails } from "../../../../hooks/reserves.js";
+import { RewardInfo } from "./RewardInfo.js";
 import { ShowAccountsOfReserveAsQRWithLink } from 
"../create/CreatedSuccessfully.js";
+import { datetimeFormatForSettings, useSettings } from 
"../../../../hooks/useSettings.js";
 
-type Entity = MerchantBackend.Tips.ReserveDetail;
+type Entity = MerchantBackend.Rewards.ReserveDetail;
 type CT = MerchantBackend.ContractTerms;
 
 interface Props {
@@ -116,14 +117,14 @@ export function DetailPage({ id, selected, onBack }: 
Props): VNode {
                     <span class="icon">
                       <i class="mdi mdi-cash-register" />
                     </span>
-                    <i18n.Translate>Tips</i18n.Translate>
+                    <i18n.Translate>Rewards</i18n.Translate>
                   </p>
                 </header>
                 <div class="card-content">
                   <div class="b-table has-pagination">
                     <div class="table-wrapper has-mobile-cards">
-                      {selected.tips && selected.tips.length > 0 ? (
-                        <Table tips={selected.tips} />
+                      {selected.rewards && selected.rewards.length > 0 ? (
+                        <Table rewards={selected.rewards} />
                       ) : (
                         <EmptyTable />
                       )}
@@ -163,7 +164,7 @@ function EmptyTable(): VNode {
       </p>
       <p>
         <i18n.Translate>
-          No tips has been authorized from this reserve
+          No reward has been authorized from this reserve
         </i18n.Translate>
       </p>
     </div>
@@ -171,10 +172,10 @@ function EmptyTable(): VNode {
 }
 
 interface TableProps {
-  tips: MerchantBackend.Tips.TipStatusEntry[];
+  rewards: MerchantBackend.Rewards.RewardStatusEntry[];
 }
 
-function Table({ tips }: TableProps): VNode {
+function Table({ rewards }: TableProps): VNode {
   const { i18n } = useTranslationContext();
   return (
     <div class="table-container">
@@ -196,8 +197,8 @@ function Table({ tips }: TableProps): VNode {
           </tr>
         </thead>
         <tbody>
-          {tips.map((t, i) => {
-            return <TipRow id={t.tip_id} key={i} entry={t} />;
+          {rewards.map((t, i) => {
+            return <RewardRow id={t.reward_id} key={i} entry={t} />;
           })}
         </tbody>
       </table>
@@ -205,15 +206,16 @@ function Table({ tips }: TableProps): VNode {
   );
 }
 
-function TipRow({
+function RewardRow({
   id,
   entry,
 }: {
   id: string;
-  entry: MerchantBackend.Tips.TipStatusEntry;
+  entry: MerchantBackend.Rewards.RewardStatusEntry;
 }) {
   const [selected, setSelected] = useState(false);
-  const result = useTipDetails(id);
+  const result = useRewardDetails(id);
+  const [settings] = useSettings();
   if (result.loading) {
     return (
       <tr>
@@ -242,11 +244,11 @@ function TipRow({
     <Fragment>
       {selected && (
         <SimpleModal
-          description="tip"
+          description="reward"
           active
           onCancel={() => setSelected(false)}
         >
-          <TipInfo id={id} amount={info.total_authorized} entity={info} />
+          <RewardInfo id={id} amount={info.total_authorized} entity={info} />
         </SimpleModal>
       )}
       <tr>
@@ -256,7 +258,7 @@ function TipRow({
         <td onClick={onSelect}>
           {info.expiration.t_s === "never"
             ? "never"
-            : format(info.expiration.t_s * 1000, "yyyy/MM/dd HH:mm:ss")}
+            : format(info.expiration.t_s * 1000, 
datetimeFormatForSettings(settings))}
         </td>
       </tr>
     </Fragment>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx
index 2592e2c6e..41c715f20 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx
@@ -92,7 +92,7 @@ export const NotYetFunded = createExample(TestedComponent, {
   },
 });
 
-export const FundedWithEmptyTips = createExample(TestedComponent, {
+export const FundedWithEmptyRewards = createExample(TestedComponent, {
   id: "THISISTHERESERVEID",
   selected: {
     active: true,
@@ -115,10 +115,10 @@ export const FundedWithEmptyTips = 
createExample(TestedComponent, {
       },
     ],
     exchange_url: "http://exchange.taler/";,
-    tips: [
+    rewards: [
       {
         reason: "asdasd",
-        tip_id: "123",
+        reward_id: "123",
         total_amount: "TESTKUDOS:1",
       },
     ],
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/TipInfo.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/RewardInfo.tsx
similarity index 77%
rename from 
packages/merchant-backoffice-ui/src/paths/instance/reserves/details/TipInfo.tsx
rename to 
packages/merchant-backoffice-ui/src/paths/instance/reserves/details/RewardInfo.tsx
index 360d39aba..57a051ed7 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/TipInfo.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/RewardInfo.tsx
@@ -17,8 +17,10 @@ import { format } from "date-fns";
 import { Fragment, h, VNode } from "preact";
 import { useBackendContext } from "../../../../context/backend.js";
 import { MerchantBackend } from "../../../../declaration.js";
+import { datetimeFormatForSettings, useSettings } from 
"../../../../hooks/useSettings.js";
+import { stringifyRewardUri } from "@gnu-taler/taler-util";
 
-type Entity = MerchantBackend.Tips.TipDetails;
+type Entity = MerchantBackend.Rewards.RewardDetails;
 
 interface Props {
   id: string;
@@ -26,11 +28,10 @@ interface Props {
   amount: string;
 }
 
-export function TipInfo({ id, amount, entity }: Props): VNode {
-  const { url } = useBackendContext();
-  const tipHost = url.replace(/.*:\/\//, ""); // remove protocol part
-  const proto = url.startsWith("http://";) ? "taler+http" : "taler";
-  const tipURL = `${proto}://tip/${tipHost}/${id}`;
+export function RewardInfo({ id: merchantRewardId, amount, entity }: Props): 
VNode {
+  const { url: merchantBaseUrl } = useBackendContext();
+  const [settings] = useSettings();
+  const rewardURL = stringifyRewardUri({ merchantBaseUrl, merchantRewardId })
   return (
     <Fragment>
       <div class="field is-horizontal">
@@ -52,8 +53,8 @@ export function TipInfo({ id, amount, entity }: Props): VNode 
{
         <div class="field-body is-flex-grow-3">
           <div class="field" style={{ overflowWrap: "anywhere" }}>
             <p class="control">
-              <a target="_blank" rel="noreferrer" href={tipURL}>
-                {tipURL}
+              <a target="_blank" rel="noreferrer" href={rewardURL}>
+                {rewardURL}
               </a>
             </p>
           </div>
@@ -73,9 +74,9 @@ export function TipInfo({ id, amount, entity }: Props): VNode 
{
                   !entity.expiration || entity.expiration.t_s === "never"
                     ? "never"
                     : format(
-                        entity.expiration.t_s * 1000,
-                        "yyyy/MM/dd HH:mm:ss",
-                      )
+                      entity.expiration.t_s * 1000,
+                      datetimeFormatForSettings(settings),
+                    )
                 }
               />
             </p>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeRewardModal.tsx
similarity index 74%
rename from 
packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx
rename to 
packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeRewardModal.tsx
index 1882f50d3..e205ee621 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeRewardModal.tsx
@@ -34,32 +34,32 @@ import {
   ContinueModal,
 } from "../../../../components/modal/index.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import { AuthorizeTipSchema } from "../../../../schemas/index.js";
+import { AuthorizeRewardSchema } from "../../../../schemas/index.js";
 import { CreatedSuccessfully } from "./CreatedSuccessfully.js";
 
-interface AuthorizeTipModalProps {
+interface AuthorizeRewardModalProps {
   onCancel: () => void;
-  onConfirm: (value: MerchantBackend.Tips.TipCreateRequest) => void;
-  tipAuthorized?: {
-    response: MerchantBackend.Tips.TipCreateConfirmation;
-    request: MerchantBackend.Tips.TipCreateRequest;
+  onConfirm: (value: MerchantBackend.Rewards.RewardCreateRequest) => void;
+  rewardAuthorized?: {
+    response: MerchantBackend.Rewards.RewardCreateConfirmation;
+    request: MerchantBackend.Rewards.RewardCreateRequest;
   };
 }
 
-export function AuthorizeTipModal({
+export function AuthorizeRewardModal({
   onCancel,
   onConfirm,
-  tipAuthorized,
-}: AuthorizeTipModalProps): VNode {
+  rewardAuthorized,
+}: AuthorizeRewardModalProps): VNode {
   // const result = useOrderDetails(id)
-  type State = MerchantBackend.Tips.TipCreateRequest;
+  type State = MerchantBackend.Rewards.RewardCreateRequest;
   const [form, setValue] = useState<Partial<State>>({});
   const { i18n } = useTranslationContext();
 
   // const [errors, setErrors] = useState<FormErrors<State>>({})
   let errors: FormErrors<State> = {};
   try {
-    AuthorizeTipSchema.validateSync(form, { abortEarly: false });
+    AuthorizeRewardSchema.validateSync(form, { abortEarly: false });
   } catch (err) {
     if (err instanceof yup.ValidationError) {
       const yupErrors = err.inner as any[];
@@ -77,12 +77,12 @@ export function AuthorizeTipModal({
   const validateAndConfirm = () => {
     onConfirm(form as State);
   };
-  if (tipAuthorized) {
+  if (rewardAuthorized) {
     return (
-      <ContinueModal description="tip" active onConfirm={onCancel}>
+      <ContinueModal description="reward" active onConfirm={onCancel}>
         <CreatedSuccessfully
-          entity={tipAuthorized.response}
-          request={tipAuthorized.request}
+          entity={rewardAuthorized.response}
+          request={rewardAuthorized.request}
           onConfirm={onCancel}
         />
       </ContinueModal>
@@ -91,7 +91,7 @@ export function AuthorizeTipModal({
 
   return (
     <ConfirmModal
-      description="tip"
+      description="New reward"
       active
       onCancel={onCancel}
       disabled={hasErrors}
@@ -105,18 +105,18 @@ export function AuthorizeTipModal({
         <InputCurrency<State>
           name="amount"
           label={i18n.str`Amount`}
-          tooltip={i18n.str`amount of tip`}
+          tooltip={i18n.str`amount of reward`}
         />
         <Input<State>
           name="justification"
           label={i18n.str`Justification`}
           inputType="multiline"
-          tooltip={i18n.str`reason for the tip`}
+          tooltip={i18n.str`reason for the reward`}
         />
         <Input<State>
           name="next_url"
-          label={i18n.str`URL after tip`}
-          tooltip={i18n.str`URL to visit after tip payment`}
+          label={i18n.str`URL after reward`}
+          tooltip={i18n.str`URL to visit after reward payment`}
         />
       </FormProvider>
     </ConfirmModal>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx
index 643651b52..b78236bc7 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx
@@ -17,12 +17,13 @@ import { format } from "date-fns";
 import { Fragment, h, VNode } from "preact";
 import { CreatedSuccessfully as Template } from 
"../../../../components/notifications/CreatedSuccessfully.js";
 import { MerchantBackend } from "../../../../declaration.js";
+import { datetimeFormatForSettings, useSettings } from 
"../../../../hooks/useSettings.js";
 
-type Entity = MerchantBackend.Tips.TipCreateConfirmation;
+type Entity = MerchantBackend.Rewards.RewardCreateConfirmation;
 
 interface Props {
   entity: Entity;
-  request: MerchantBackend.Tips.TipCreateRequest;
+  request: MerchantBackend.Rewards.RewardCreateRequest;
   onConfirm: () => void;
   onCreateAnother?: () => void;
 }
@@ -33,6 +34,7 @@ export function CreatedSuccessfully({
   onConfirm,
   onCreateAnother,
 }: Props): VNode {
+  const [settings] = useSettings();
   return (
     <Fragment>
       <div class="field is-horizontal">
@@ -66,7 +68,7 @@ export function CreatedSuccessfully({
         <div class="field-body is-flex-grow-3">
           <div class="field">
             <p class="control">
-              <input readonly class="input" value={entity.tip_status_url} />
+              <input readonly class="input" value={entity.reward_status_url} />
             </p>
           </div>
         </div>
@@ -82,13 +84,13 @@ export function CreatedSuccessfully({
                 class="input"
                 readonly
                 value={
-                  !entity.tip_expiration ||
-                  entity.tip_expiration.t_s === "never"
+                  !entity.reward_expiration ||
+                    entity.reward_expiration.t_s === "never"
                     ? "never"
                     : format(
-                        entity.tip_expiration.t_s * 1000,
-                        "yyyy/MM/dd HH:mm:ss",
-                      )
+                      entity.reward_expiration.t_s * 1000,
+                      datetimeFormatForSettings(settings),
+                    )
                 }
               />
             </p>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx
index fe305f4fd..b070bbde3 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx
@@ -25,12 +25,6 @@ import { CardTable as TestedComponent } from "./Table.js";
 export default {
   title: "Pages/Reserve/List",
   component: TestedComponent,
-  argTypes: {
-    onCreate: { action: "onCreate" },
-    onDelete: { action: "onDelete" },
-    onNewTip: { action: "onNewTip" },
-    onSelect: { action: "onSelect" },
-  },
 };
 
 function createExample<Props>(
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx
index 1f229d7cb..795e7ec82 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx
@@ -23,12 +23,13 @@ import { useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { format } from "date-fns";
 import { Fragment, h, VNode } from "preact";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { datetimeFormatForSettings, useSettings } from 
"../../../../hooks/useSettings.js";
 
-type Entity = MerchantBackend.Tips.ReserveStatusEntry & WithId;
+type Entity = MerchantBackend.Rewards.ReserveStatusEntry & WithId;
 
 interface Props {
   instances: Entity[];
-  onNewTip: (id: Entity) => void;
+  onNewReward: (id: Entity) => void;
   onSelect: (id: Entity) => void;
   onDelete: (id: Entity) => void;
   onCreate: () => void;
@@ -38,7 +39,7 @@ export function CardTable({
   instances,
   onCreate,
   onSelect,
-  onNewTip,
+  onNewReward,
   onDelete,
 }: Props): VNode {
   const [withoutFunds, withFunds] = instances.reduce((prev, current) => {
@@ -70,7 +71,7 @@ export function CardTable({
               <div class="table-wrapper has-mobile-cards">
                 <TableWithoutFund
                   instances={withoutFunds}
-                  onNewTip={onNewTip}
+                  onNewReward={onNewReward}
                   onSelect={onSelect}
                   onDelete={onDelete}
                 />
@@ -108,7 +109,7 @@ export function CardTable({
               {withFunds.length > 0 ? (
                 <Table
                   instances={withFunds}
-                  onNewTip={onNewTip}
+                  onNewReward={onNewReward}
                   onSelect={onSelect}
                   onDelete={onDelete}
                 />
@@ -124,13 +125,14 @@ export function CardTable({
 }
 interface TableProps {
   instances: Entity[];
-  onNewTip: (id: Entity) => void;
+  onNewReward: (id: Entity) => void;
   onDelete: (id: Entity) => void;
   onSelect: (id: Entity) => void;
 }
 
-function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode 
{
+function Table({ instances, onNewReward, onSelect, onDelete }: TableProps): 
VNode {
   const { i18n } = useTranslationContext();
+  const [settings] = useSettings();
   return (
     <div class="table-container">
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -164,7 +166,7 @@ function Table({ instances, onNewTip, onSelect, onDelete }: 
TableProps): VNode {
                 >
                   {i.creation_time.t_s === "never"
                     ? "never"
-                    : format(i.creation_time.t_s * 1000, "yyyy/MM/dd 
HH:mm:ss")}
+                    : format(i.creation_time.t_s * 1000, 
datetimeFormatForSettings(settings))}
                 </td>
                 <td
                   onClick={(): void => onSelect(i)}
@@ -173,9 +175,9 @@ function Table({ instances, onNewTip, onSelect, onDelete }: 
TableProps): VNode {
                   {i.expiration_time.t_s === "never"
                     ? "never"
                     : format(
-                        i.expiration_time.t_s * 1000,
-                        "yyyy/MM/dd HH:mm:ss",
-                      )}
+                      i.expiration_time.t_s * 1000,
+                      datetimeFormatForSettings(settings),
+                    )}
                 </td>
                 <td
                   onClick={(): void => onSelect(i)}
@@ -207,11 +209,11 @@ function Table({ instances, onNewTip, onSelect, onDelete 
}: TableProps): VNode {
                     </button>
                     <button
                       class="button is-small is-info has-tooltip-left"
-                      data-tooltip={i18n.str`authorize new tip from selected 
reserve`}
+                      data-tooltip={i18n.str`authorize new reward from 
selected reserve`}
                       type="button"
-                      onClick={(): void => onNewTip(i)}
+                      onClick={(): void => onNewReward(i)}
                     >
-                      New Tip
+                      New Reward
                     </button>
                   </div>
                 </td>
@@ -249,6 +251,7 @@ function TableWithoutFund({
   onDelete,
 }: TableProps): VNode {
   const { i18n } = useTranslationContext();
+  const [settings] = useSettings();
   return (
     <div class="table-container">
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -276,7 +279,7 @@ function TableWithoutFund({
                 >
                   {i.creation_time.t_s === "never"
                     ? "never"
-                    : format(i.creation_time.t_s * 1000, "yyyy/MM/dd 
HH:mm:ss")}
+                    : format(i.creation_time.t_s * 1000, 
datetimeFormatForSettings(settings))}
                 </td>
                 <td
                   onClick={(): void => onSelect(i)}
@@ -285,9 +288,9 @@ function TableWithoutFund({
                   {i.expiration_time.t_s === "never"
                     ? "never"
                     : format(
-                        i.expiration_time.t_s * 1000,
-                        "yyyy/MM/dd HH:mm:ss",
-                      )}
+                      i.expiration_time.t_s * 1000,
+                      datetimeFormatForSettings(settings),
+                    )}
                 </td>
                 <td
                   onClick={(): void => onSelect(i)}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx
index 14387c2a9..b26ff0000 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx
@@ -34,9 +34,10 @@ import {
   useReservesAPI,
 } from "../../../../hooks/reserves.js";
 import { Notification } from "../../../../utils/types.js";
-import { AuthorizeTipModal } from "./AutorizeTipModal.js";
+import { AuthorizeRewardModal } from "./AutorizeRewardModal.js";
 import { CardTable } from "./Table.js";
 import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { ConfirmModal } from "../../../../components/modal/index.js";
 
 interface Props {
   onUnauthorized: () => VNode;
@@ -46,12 +47,12 @@ interface Props {
   onCreate: () => void;
 }
 
-interface TipConfirmation {
-  response: MerchantBackend.Tips.TipCreateConfirmation;
-  request: MerchantBackend.Tips.TipCreateRequest;
+interface RewardConfirmation {
+  response: MerchantBackend.Rewards.RewardCreateConfirmation;
+  request: MerchantBackend.Rewards.RewardCreateRequest;
 }
 
-export default function ListTips({
+export default function ListRewards({
   onUnauthorized,
   onLoadError,
   onNotFound,
@@ -59,14 +60,16 @@ export default function ListTips({
   onCreate,
 }: Props): VNode {
   const result = useInstanceReserves();
-  const { deleteReserve, authorizeTipReserve } = useReservesAPI();
+  const { deleteReserve, authorizeRewardReserve } = useReservesAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { i18n } = useTranslationContext();
-  const [reserveForTip, setReserveForTip] = useState<string | undefined>(
+  const [reserveForReward, setReserveForReward] = useState<string | undefined>(
     undefined,
   );
-  const [tipAuthorized, setTipAuthorized] = useState<
-    TipConfirmation | undefined
+  const [deleting, setDeleting] =
+    useState<MerchantBackend.Rewards.ReserveStatusEntry | null>(null);
+  const [rewardAuthorized, setRewardAuthorized] = useState<
+    RewardConfirmation | undefined
   >(undefined);
 
   if (result.loading) return <Loading />;
@@ -88,30 +91,30 @@ export default function ListTips({
     <section class="section is-main-section">
       <NotificationCard notification={notif} />
 
-      {reserveForTip && (
-        <AuthorizeTipModal
+      {reserveForReward && (
+        <AuthorizeRewardModal
           onCancel={() => {
-            setReserveForTip(undefined);
-            setTipAuthorized(undefined);
+            setReserveForReward(undefined);
+            setRewardAuthorized(undefined);
           }}
-          tipAuthorized={tipAuthorized}
+          rewardAuthorized={rewardAuthorized}
           onConfirm={async (request) => {
             try {
-              const response = await authorizeTipReserve(
-                reserveForTip,
+              const response = await authorizeRewardReserve(
+                reserveForReward,
                 request,
               );
-              setTipAuthorized({
+              setRewardAuthorized({
                 request,
                 response: response.data,
               });
             } catch (error) {
               setNotif({
-                message: i18n.str`could not create the tip`,
+                message: i18n.str`could not create the reward`,
                 type: "ERROR",
                 description: error instanceof Error ? error.message : 
undefined,
               });
-              setReserveForTip(undefined);
+              setReserveForReward(undefined);
             }
           }}
         />
@@ -122,10 +125,47 @@ export default function ListTips({
           .filter((r) => r.active)
           .map((o) => ({ ...o, id: o.reserve_pub }))}
         onCreate={onCreate}
-        onDelete={(reserve) => deleteReserve(reserve.reserve_pub)}
+        onDelete={(reserve) => {
+          setDeleting(reserve)
+        }}
         onSelect={(reserve) => onSelect(reserve.id)}
-        onNewTip={(reserve) => setReserveForTip(reserve.id)}
+        onNewReward={(reserve) => setReserveForReward(reserve.id)}
       />
+
+      {deleting && (
+        <ConfirmModal
+          label={`Delete reserve`}
+          description={`Delete the reserve`}
+          danger
+          active
+          onCancel={() => setDeleting(null)}
+          onConfirm={async (): Promise<void> => {
+            try {
+              await deleteReserve(deleting.reserve_pub);
+              setNotif({
+                message: i18n.str`Reserve for 
"${deleting.merchant_initial_amount}" (ID: ${deleting.reserve_pub}) has been 
deleted`,
+                type: "SUCCESS",
+              });
+            } catch (error) {
+              setNotif({
+                message: i18n.str`Failed to delete reserve`,
+                type: "ERROR",
+                description: error instanceof Error ? error.message : 
undefined,
+              });
+            }
+            setDeleting(null);
+          }}
+        >
+          <p>
+            If you delete the reserve for 
<b>&quot;{deleting.merchant_initial_amount}&quot;</b> you won't be able to 
create more rewards. <br />
+            Reserve ID: <b>{deleting.reserve_pub}</b>
+          </p>
+          <p class="warning">
+            Deleting an template <b>cannot be undone</b>.
+          </p>
+        </ConfirmModal>
+      )}
+
     </section>
   );
 }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
index e20b9bc27..8629d8dee 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
@@ -24,7 +24,7 @@ import {
   MerchantTemplateContractDetails,
 } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
 import {
@@ -35,17 +35,16 @@ import { Input } from 
"../../../../components/form/Input.js";
 import { InputCurrency } from "../../../../components/form/InputCurrency.js";
 import { InputDuration } from "../../../../components/form/InputDuration.js";
 import { InputNumber } from "../../../../components/form/InputNumber.js";
-import { InputSelector } from "../../../../components/form/InputSelector.js";
 import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
 import { useBackendContext } from "../../../../context/backend.js";
+import { useInstanceContext } from "../../../../context/instance.js";
 import { MerchantBackend } from "../../../../declaration.js";
 import {
-  isBase32RFC3548Charset,
-  randomBase32Key,
+  isBase32RFC3548Charset
 } from "../../../../utils/crypto.js";
 import { undefinedIfEmpty } from "../../../../utils/table.js";
-import { QR } from "../../../../components/exception/QR.js";
-import { useInstanceContext } from "../../../../context/instance.js";
+import { InputSearchOnList } from 
"../../../../components/form/InputSearchOnList.js";
+import { useInstanceOtpDevices } from "../../../../hooks/otp.js";
 
 type Entity = MerchantBackend.Template.TemplateAddDetails;
 
@@ -54,16 +53,11 @@ interface Props {
   onBack?: () => void;
 }
 
-const algorithms = [0, 1, 2];
-const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"];
-
 export function CreatePage({ onCreate, onBack }: Props): VNode {
   const { i18n } = useTranslationContext();
   const backend = useBackendContext();
-  const { id: instanceId } = useInstanceContext();
-  const issuer = new URL(backend.url).hostname;
+  const devices = useInstanceOtpDevices()
 
-  const [showKey, setShowKey] = useState(false);
   const [state, setState] = useState<Partial<Entity>>({
     template_contract: {
       minimum_age: 0,
@@ -78,7 +72,11 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
     : Amounts.parse(state.template_contract?.amount);
 
   const errors: FormErrors<Entity> = {
-    template_id: !state.template_id ? i18n.str`should not be empty` : 
undefined,
+    template_id: !state.template_id
+      ? i18n.str`should not be empty`
+      : !/[a-zA-Z0-9]*/.test(state.template_id)
+        ? i18n.str`no valid. only characters and numbers`
+        : undefined,
     template_description: !state.template_description
       ? i18n.str`should not be empty`
       : undefined,
@@ -104,15 +102,6 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
               ? i18n.str`to short`
               : undefined,
       } as Partial<MerchantTemplateContractDetails>),
-    pos_key: !state.pos_key
-      ? !state.pos_algorithm
-        ? undefined
-        : i18n.str`required`
-      : !isBase32RFC3548Charset(state.pos_key)
-        ? i18n.str`just letters and numbers from 2 to 7`
-        : state.pos_key.length !== 32
-          ? i18n.str`size of the key should be 32`
-          : undefined,
   };
 
   const hasErrors = Object.keys(errors).some(
@@ -124,7 +113,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
     return onCreate(state as any);
   };
 
-  const qrText = 
`otpauth://totp/${instanceId}/${state.template_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${state.pos_key}`;
+  const deviceList = !devices.ok ? [] : devices.data.otp_devices
 
   return (
     <div>
@@ -139,7 +128,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
             >
               <InputWithAddon<Entity>
                 name="template_id"
-                help={`${backend.url}/instances/templates/${state.template_id 
?? ""}`}
+                help={`${backend.url}/templates/${state.template_id ?? ""}`}
                 label={i18n.str`Identifier`}
                 tooltip={i18n.str`Name of the template in URLs.`}
               />
@@ -172,83 +161,21 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                 help=""
                 tooltip={i18n.str`How much time has the customer to complete 
the payment once the order was created.`}
               />
-              <InputSelector<Entity>
-                name="pos_algorithm"
-                label={i18n.str`Verification algorithm`}
-                tooltip={i18n.str`Algorithm to use to verify transaction in 
offline mode`}
-                values={algorithms}
-                toStr={(v) => algorithmsNames[v]}
-                fromStr={(v) => Number(v)}
+              <Input<Entity>
+                name="otp_id"
+                label={i18n.str`OTP device`}
+                readonly
+                tooltip={i18n.str`Use to verify transaction in offline mode.`}
+              />
+              <InputSearchOnList
+                label={i18n.str`Search device`}
+                onChange={(p) => setState((v) => ({ ...v, otp_id: p?.id }))}
+                list={deviceList.map(e => ({
+                  description: e.device_description,
+                  id: e.otp_device_id
+                }))}
               />
-              {state.pos_algorithm && state.pos_algorithm > 0 ? (
-                <Fragment>
-                  <InputWithAddon<Entity>
-                    name="pos_key"
-                    label={i18n.str`Point-of-sale key`}
-                    inputType={showKey ? "text" : "password"}
-                    help="Be sure to be very hard to guess or use the random 
generator"
-                    tooltip={i18n.str`Useful to validate the purchase`}
-                    fromStr={(v) => v.toUpperCase()}
-                    addonAfter={
-                      <span class="icon">
-                        {showKey ? (
-                          <i class="mdi mdi-eye" />
-                        ) : (
-                          <i class="mdi mdi-eye-off" />
-                        )}
-                      </span>
-                    }
-                    side={
-                      <span style={{ display: "flex" }}>
-                        <button
-                          data-tooltip={i18n.str`generate random secret key`}
-                          class="button is-info mr-3"
-                          onClick={(e) => {
-                            const pos_key = randomBase32Key();
-                            setState((s) => ({ ...s, pos_key }));
-                          }}
-                        >
-                          <i18n.Translate>random</i18n.Translate>
-                        </button>
-                        <button
-                          data-tooltip={
-                            showKey
-                              ? i18n.str`show secret key`
-                              : i18n.str`hide secret key`
-                          }
-                          class="button is-info mr-3"
-                          onClick={(e) => {
-                            setShowKey(!showKey);
-                          }}
-                        >
-                          {showKey ? (
-                            <i18n.Translate>hide</i18n.Translate>
-                          ) : (
-                            <i18n.Translate>show</i18n.Translate>
-                          )}
-                        </button>
-                      </span>
-                    }
-                  />
-                  {showKey && (
-                    <Fragment>
-                      <QR text={qrText} />
-                      <div
-                        style={{
-                          color: "grey",
-                          fontSize: "small",
-                          width: 200,
-                          textAlign: "center",
-                          margin: "auto",
-                          wordBreak: "break-all",
-                        }}
-                      >
-                        {qrText}
-                      </div>
-                    </Fragment>
-                  )}
-                </Fragment>
-              ) : undefined}
+
             </FormProvider>
 
             <div class="buttons is-right mt-5">
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
index 2f91298bf..3c9bb231c 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
@@ -36,6 +36,7 @@ import {
 import { Notification } from "../../../../utils/types.js";
 import { ListPage } from "./ListPage.js";
 import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { ConfirmModal } from "../../../../components/modal/index.js";
 
 interface Props {
   onUnauthorized: () => VNode;
@@ -61,6 +62,8 @@ export default function ListTemplates({
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { deleteTemplate } = useTemplateAPI();
   const result = useInstanceTemplates({ position }, (id) => setPosition(id));
+  const [deleting, setDeleting] =
+    useState<MerchantBackend.Template.TemplateEntry | null>(null);
 
   if (result.loading) return <Loading />;
   if (!result.ok) {
@@ -97,23 +100,45 @@ export default function ListTemplates({
         onQR={(e) => {
           onQR(e.template_id);
         }}
-        onDelete={(e: MerchantBackend.Template.TemplateEntry) =>
-          deleteTemplate(e.template_id)
-            .then(() =>
+        onDelete={(e: MerchantBackend.Template.TemplateEntry) => {
+          setDeleting(e)
+        }
+        }
+      />
+
+      {deleting && (
+        <ConfirmModal
+          label={`Delete template`}
+          description={`Delete the template 
"${deleting.template_description}"`}
+          danger
+          active
+          onCancel={() => setDeleting(null)}
+          onConfirm={async (): Promise<void> => {
+            try {
+              await deleteTemplate(deleting.template_id);
               setNotif({
-                message: i18n.str`template delete successfully`,
+                message: i18n.str`Template "${deleting.template_description}" 
(ID: ${deleting.template_id}) has been deleted`,
                 type: "SUCCESS",
-              }),
-            )
-            .catch((error) =>
+              });
+            } catch (error) {
               setNotif({
-                message: i18n.str`could not delete the template`,
+                message: i18n.str`Failed to delete template`,
                 type: "ERROR",
-                description: error.message,
-              }),
-            )
-        }
-      />
+                description: error instanceof Error ? error.message : 
undefined,
+              });
+            }
+            setDeleting(null);
+          }}
+        >
+          <p>
+            If you delete the template 
<b>&quot;{deleting.template_description}&quot;</b> (ID:{" "}
+            <b>{deleting.template_id}</b>) you may loose information
+          </p>
+          <p class="warning">
+            Deleting an template <b>cannot be undone</b>.
+          </p>
+        </ConfirmModal>
+      )}
     </Fragment>
   );
 }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
index 0f30efafd..c65cf6a19 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { HttpError, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { QR } from "../../../../components/exception/QR.js";
@@ -35,35 +35,32 @@ import { useConfigContext } from 
"../../../../context/config.js";
 import { useInstanceContext } from "../../../../context/instance.js";
 import { MerchantBackend } from "../../../../declaration.js";
 import { stringifyPayTemplateUri } from "@gnu-taler/taler-util";
+import { useOtpDeviceDetails } from "../../../../hooks/otp.js";
+import { Loading } from "../../../../components/exception/loading.js";
 
 type Entity = MerchantBackend.Template.UsingTemplateDetails;
 
 interface Props {
-  template: MerchantBackend.Template.TemplateDetails;
+  contract: MerchantBackend.Template.TemplateContractDetails;
   id: string;
   onBack?: () => void;
 }
 
-export function QrPage({ template, id: templateId, onBack }: Props): VNode {
+export function QrPage({ contract, id: templateId, onBack }: Props): VNode {
   const { i18n } = useTranslationContext();
   const { url: backendUrl } = useBackendContext();
   const { id: instanceId } = useInstanceContext();
   const config = useConfigContext();
-  const [setupTOTP, setSetupTOTP] = useState(false);
 
   const [state, setState] = useState<Partial<Entity>>({
-    amount: template.template_contract.amount,
-    summary: template.template_contract.summary,
+    amount: contract.amount,
+    summary: contract.summary,
   });
 
   const errors: FormErrors<Entity> = {};
 
-  const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
-  );
-
-  const fixedAmount = !!template.template_contract.amount;
-  const fixedSummary = !!template.template_contract.summary;
+  const fixedAmount = !!contract.amount;
+  const fixedSummary = !!contract.summary;
 
   const templateParams: Record<string, string> = {}
   if (!fixedAmount) {
@@ -89,40 +86,9 @@ export function QrPage({ template, id: templateId, onBack }: 
Props): VNode {
   const issuer = encodeURIComponent(
     `${new URL(backendUrl).host}/${instanceId}`,
   );
-  const oauthUri = !template.pos_algorithm
-    ? undefined
-    : template.pos_algorithm === 1
-      ? 
`otpauth://totp/${issuer}:${templateId}?secret=${template.pos_key}&issuer=${issuer}&algorithm=SHA1&digits=8&period=30`
-      : template.pos_algorithm === 2
-        ? 
`otpauth://totp/${issuer}:${templateId}?secret=${template.pos_key}&issuer=${issuer}&algorithm=SHA1&digits=8&period=30`
-        : undefined;
-
-  const keySlice = template.pos_key?.substring(0, 4);
-
-  const oauthUriWithoutSecret = !template.pos_algorithm
-    ? undefined
-    : template.pos_algorithm === 1
-      ? 
`otpauth://totp/${issuer}:${templateId}?secret=${keySlice}...&issuer=${issuer}&algorithm=SHA1&digits=8&period=30`
-      : template.pos_algorithm === 2
-        ? 
`otpauth://totp/${issuer}:${templateId}?secret=${keySlice}...&issuer=${issuer}&algorithm=SHA1&digits=8&period=30`
-        : undefined;
+
   return (
     <div>
-      {oauthUri && (
-        <ConfirmModal
-          description="Setup TOTP"
-          active={setupTOTP}
-          onCancel={() => {
-            setSetupTOTP(false);
-          }}
-        >
-          <p>Scan this qr code with your TOTP device</p>
-          <QR text={oauthUri} />
-          <pre style={{ textAlign: "center" }}>
-            <a href={oauthUri}>{oauthUriWithoutSecret}</a>
-          </pre>
-        </ConfirmModal>
-      )}
       <section class="section is-main-section">
         <div class="columns">
           <div class="column" />
@@ -176,14 +142,6 @@ export function QrPage({ template, id: templateId, onBack 
}: Props): VNode {
               >
                 <i18n.Translate>Print</i18n.Translate>
               </button>
-              {oauthUri && (
-                <button
-                  class="button is-info"
-                  onClick={() => setSetupTOTP(true)}
-                >
-                  <i18n.Translate>Setup TOTP</i18n.Translate>
-                </button>
-              )}
             </div>
           </div>
           <div class="column" />
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
index 1f74afc2b..7db7478f7 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
@@ -74,7 +74,7 @@ export default function TemplateQrPage({
   return (
     <>
       <NotificationCard notification={notif} />
-      <QrPage template={result.data} id={tid} onBack={onBack} />
+      <QrPage contract={result.data.template_contract} id={tid} 
onBack={onBack} />
     </>
   );
 }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
index 30e5502bb..30d47385c 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
@@ -61,10 +61,7 @@ const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d 
eTOTP-SHA1"];
 export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
   const { i18n } = useTranslationContext();
   const backend = useBackendContext();
-  const { id: instanceId } = useInstanceContext();
-  const issuer = new URL(backend.url).hostname;
 
-  const [showKey, setShowKey] = useState(false);
   const [state, setState] = useState<Partial<Entity>>(template);
 
   const parsedPrice = !state.template_contract?.amount
@@ -78,34 +75,25 @@ export function UpdatePage({ template, onUpdate, onBack }: 
Props): VNode {
     template_contract: !state.template_contract
       ? undefined
       : undefinedIfEmpty({
-          amount: !state.template_contract?.amount
-            ? undefined
-            : !parsedPrice
+        amount: !state.template_contract?.amount
+          ? undefined
+          : !parsedPrice
             ? i18n.str`not valid`
             : Amounts.isZero(parsedPrice)
-            ? i18n.str`must be greater than 0`
-            : undefined,
-          minimum_age:
-            state.template_contract.minimum_age < 0
-              ? i18n.str`should be greater that 0`
+              ? i18n.str`must be greater than 0`
               : undefined,
-          pay_duration: !state.template_contract.pay_duration
-            ? i18n.str`can't be empty`
-            : state.template_contract.pay_duration.d_us === "forever"
+        minimum_age:
+          state.template_contract.minimum_age < 0
+            ? i18n.str`should be greater that 0`
+            : undefined,
+        pay_duration: !state.template_contract.pay_duration
+          ? i18n.str`can't be empty`
+          : state.template_contract.pay_duration.d_us === "forever"
             ? undefined
             : state.template_contract.pay_duration.d_us < 1000 * 1000 // less 
than one second
-            ? i18n.str`to short`
-            : undefined,
-        } as Partial<MerchantTemplateContractDetails>),
-    pos_key: !state.pos_key
-      ? !state.pos_algorithm
-        ? undefined
-        : i18n.str`required`
-      : !isBase32RFC3548Charset(state.pos_key)
-      ? i18n.str`just letters and numbers from 2 to 7`
-      : state.pos_key.length !== 32
-      ? i18n.str`size of the key should be 32`
-      : undefined,
+              ? i18n.str`to short`
+              : undefined,
+      } as Partial<MerchantTemplateContractDetails>),
   };
 
   const hasErrors = Object.keys(errors).some(
@@ -117,7 +105,6 @@ export function UpdatePage({ template, onUpdate, onBack }: 
Props): VNode {
     return onUpdate(state as any);
   };
 
-  const qrText = 
`otpauth://totp/${instanceId}/${state.id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${state.pos_key}`;
 
   return (
     <div>
@@ -128,7 +115,7 @@ export function UpdatePage({ template, onUpdate, onBack }: 
Props): VNode {
               <div class="level-left">
                 <div class="level-item">
                   <span class="is-size-4">
-                    {backend.url}/instances/template/{template.id}
+                    {backend.url}/templates/{template.id}
                   </span>
                 </div>
               </div>
@@ -182,84 +169,6 @@ export function UpdatePage({ template, onUpdate, onBack }: 
Props): VNode {
                   help=""
                   tooltip={i18n.str`How much time has the customer to complete 
the payment once the order was created.`}
                 />
-                <InputSelector<Entity>
-                  name="pos_algorithm"
-                  label={i18n.str`Verification algorithm`}
-                  tooltip={i18n.str`Algorithm to use to verify transaction in 
offline mode`}
-                  values={algorithms}
-                  toStr={(v) => algorithmsNames[v]}
-                  fromStr={(v) => Number(v)}
-                />
-                {state.pos_algorithm && state.pos_algorithm > 0 ? (
-                  <Fragment>
-                    <InputWithAddon<Entity>
-                      name="pos_key"
-                      label={i18n.str`Point-of-sale key`}
-                      inputType={showKey ? "text" : "password"}
-                      help="Be sure to be very hard to guess or use the random 
generator"
-                      expand
-                      tooltip={i18n.str`Useful to validate the purchase`}
-                      fromStr={(v) => v.toUpperCase()}
-                      addonAfter={
-                        <span class="icon">
-                          {showKey ? (
-                            <i class="mdi mdi-eye" />
-                          ) : (
-                            <i class="mdi mdi-eye-off" />
-                          )}
-                        </span>
-                      }
-                      side={
-                        <span style={{ display: "flex" }}>
-                          <button
-                            data-tooltip={i18n.str`generate random secret key`}
-                            class="button is-info mr-3"
-                            onClick={(e) => {
-                              const pos_key = randomBase32Key();
-                              setState((s) => ({ ...s, pos_key }));
-                            }}
-                          >
-                            <i18n.Translate>random</i18n.Translate>
-                          </button>
-                          <button
-                            data-tooltip={
-                              showKey
-                                ? i18n.str`show secret key`
-                                : i18n.str`hide secret key`
-                            }
-                            class="button is-info mr-3"
-                            onClick={(e) => {
-                              setShowKey(!showKey);
-                            }}
-                          >
-                            {showKey ? (
-                              <i18n.Translate>hide</i18n.Translate>
-                            ) : (
-                              <i18n.Translate>show</i18n.Translate>
-                            )}
-                          </button>
-                        </span>
-                      }
-                    />
-                    {showKey && (
-                      <Fragment>
-                        <QR text={qrText} />
-                        <div
-                          style={{
-                            color: "grey",
-                            fontSize: "small",
-                            width: 200,
-                            textAlign: "center",
-                            margin: "auto",
-                            wordBreak: "break-all",
-                          }}
-                        >
-                          {qrText}
-                        </div>
-                      </Fragment>
-                    )}
-                  </Fragment>
-                ) : undefined}
               </FormProvider>
 
               <div class="buttons is-right mt-5">
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx
new file mode 100644
index 000000000..6ab2a2df6
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx
@@ -0,0 +1,165 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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 { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { AsyncButton } from "../../../components/exception/AsyncButton.js";
+import { FormProvider } from "../../../components/form/FormProvider.js";
+import { Input } from "../../../components/form/Input.js";
+import { useInstanceContext } from "../../../context/instance.js";
+
+interface Props {
+  instanceId: string;
+  currentToken: string | undefined;
+  onClearToken: () => void;
+  onNewToken: (s: string) => void;
+  onBack?: () => void;
+}
+
+export function DetailPage({ instanceId, currentToken: oldToken, onBack, 
onNewToken, onClearToken }: Props): VNode {
+  type State = { old_token: string; new_token: string; repeat_token: string };
+  const [form, setValue] = useState<Partial<State>>({
+    old_token: "",
+    new_token: "",
+    repeat_token: "",
+  });
+  const { i18n } = useTranslationContext();
+
+  const hasOldtoken = !!oldToken
+  const hasInputTheCorrectOldToken = hasOldtoken && oldToken !== 
form.old_token;
+  const errors = {
+    old_token: hasInputTheCorrectOldToken
+      ? i18n.str`is not the same as the current access token`
+      : undefined,
+    new_token: !form.new_token
+      ? i18n.str`cannot be empty`
+      : form.new_token === form.old_token
+        ? i18n.str`cannot be the same as the old token`
+        : undefined,
+    repeat_token:
+      form.new_token !== form.repeat_token
+        ? i18n.str`is not the same`
+        : undefined,
+  };
+
+  const hasErrors = Object.keys(errors).some(
+    (k) => (errors as any)[k] !== undefined,
+  );
+
+  const instance = useInstanceContext();
+
+  const text = i18n.str`You are updating the access token from instance with 
id ${instance.id}`;
+
+  async function submitForm() {
+    if (hasErrors) return;
+    onNewToken(form.new_token as any)
+  }
+
+  return (
+    <div>
+      <section class="section">
+        <section class="hero is-hero-bar">
+          <div class="hero-body">
+            <div class="level">
+              <div class="level-left">
+                <div class="level-item">
+                  <span class="is-size-4">
+                    Instace id: <b>{instanceId}</b>
+                  </span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </section>
+        <hr />
+
+        <div class="columns">
+          <div class="column" />
+          <div class="column is-four-fifths">
+            <FormProvider errors={errors} object={form} 
valueHandler={setValue}>
+              {hasOldtoken && (
+                <Input<State>
+                  name="old_token"
+                  label={i18n.str`Current access token`}
+                  tooltip={i18n.str`access token currently in use`}
+                  inputType="password"
+                />
+              )}
+              {!hasInputTheCorrectOldToken && <Fragment>
+                {hasOldtoken && <Fragment>
+                  <p>
+                    <i18n.Translate>
+                      Clearing the access token will mean public access to the 
instance.
+                    </i18n.Translate>
+                  </p>
+                  <div class="buttons is-right mt-5">
+                    <button
+                      disabled={!!hasInputTheCorrectOldToken}
+                      class="button"
+                      onClick={onClearToken}
+                    >
+                      <i18n.Translate>Clear token</i18n.Translate>
+                    </button>
+                  </div>
+                </Fragment>
+                }
+
+                <Input<State>
+                  name="new_token"
+                  label={i18n.str`New access token`}
+                  tooltip={i18n.str`next access token to be used`}
+                  inputType="password"
+                />
+                <Input<State>
+                  name="repeat_token"
+                  label={i18n.str`Repeat access token`}
+                  tooltip={i18n.str`confirm the same access token`}
+                  inputType="password"
+                />
+              </Fragment>}
+            </FormProvider>
+            <div class="buttons is-right mt-5">
+              {onBack && (
+                <button class="button" onClick={onBack}>
+                  <i18n.Translate>Cancel</i18n.Translate>
+                </button>
+              )}
+              <AsyncButton
+                disabled={hasErrors}
+                data-tooltip={
+                  hasErrors
+                    ? i18n.str`Need to complete marked fields`
+                    : "confirm operation"
+                }
+                onClick={submitForm}
+              >
+                <i18n.Translate>Confirm change</i18n.Translate>
+              </AsyncButton>
+            </div>
+          </div>
+          <div class="column" />
+        </div>
+
+      </section>
+    </div>
+  );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
new file mode 100644
index 000000000..d5910361b
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
@@ -0,0 +1,90 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { ErrorType, HttpError, useTranslationContext } from 
"@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import { Loading } from "../../../components/exception/loading.js";
+import { MerchantBackend } from "../../../declaration.js";
+import { useInstanceAPI, useInstanceDetails } from 
"../../../hooks/instance.js";
+import { DetailPage } from "./DetailPage.js";
+import { useInstanceContext } from "../../../context/instance.js";
+import { useState } from "preact/hooks";
+import { NotificationCard } from "../../../components/menu/index.js";
+import { Notification } from "../../../utils/types.js";
+import { useBackendContext } from "../../../context/backend.js";
+
+interface Props {
+  onUnauthorized: () => VNode;
+  onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
+  onChange: () => void;
+  onNotFound: () => VNode;
+}
+
+const PREFIX = "secret-token:"
+
+export default function Token({
+  onLoadError,
+  onChange,
+  onUnauthorized,
+  onNotFound,
+}: Props): VNode {
+  const { i18n } = useTranslationContext();
+
+  const [notif, setNotif] = useState<Notification | undefined>(undefined);
+  const { clearToken, setNewToken } = useInstanceAPI();
+  const { token: rootToken } = useBackendContext();
+  const { token: instanceToken, id, admin } = useInstanceContext();
+
+  const currentToken = !admin ? rootToken : instanceToken
+  const hasPrefix = currentToken !== undefined && 
currentToken.startsWith(PREFIX)
+  return (
+    <Fragment>
+      <NotificationCard notification={notif} />
+      <DetailPage
+        instanceId={id}
+        currentToken={hasPrefix ? currentToken.substring(PREFIX.length) : 
currentToken}
+        onClearToken={async (): Promise<void> => {
+          try {
+            await clearToken();
+            onChange();
+          } catch (error) {
+            if (error instanceof Error) {
+              setNotif({
+                message: i18n.str`Failed to clear token`,
+                type: "ERROR",
+                description: error.message,
+              });
+            }
+          }
+        }}
+        onNewToken={async (newToken): Promise<void> => {
+          try {
+            await setNewToken(`secret-token:${newToken}`);
+            onChange();
+          } catch (error) {
+            if (error instanceof Error) {
+              setNotif({
+                message: i18n.str`Failed to set new token`,
+                type: "ERROR",
+                description: error.message,
+              });
+            }
+          }
+        }}
+      />
+    </Fragment>
+  );
+}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/token/stories.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/token/stories.tsx
new file mode 100644
index 000000000..5f0f56f2d
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/token/stories.tsx
@@ -0,0 +1,28 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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 { DetailPage as TestedComponent } from "./DetailPage.js";
+
+export default {
+  title: "Pages/Token",
+  component: TestedComponent,
+};
+
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
index f218f4ead..25551a031 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
@@ -28,6 +28,7 @@ import { useInstanceDetails } from 
"../../../../hooks/instance.js";
 import { useTransferAPI } from "../../../../hooks/transfer.js";
 import { Notification } from "../../../../utils/types.js";
 import { CreatePage } from "./CreatePage.js";
+import { useBankAccountDetails, useInstanceBankAccounts } from 
"../../../../hooks/bank.js";
 
 export type Entity = MerchantBackend.Transfers.TransferInformation;
 interface Props {
@@ -39,7 +40,7 @@ export default function CreateTransfer({ onConfirm, onBack }: 
Props): VNode {
   const { informTransfer } = useTransferAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { i18n } = useTranslationContext();
-  const instance = useInstanceDetails();
+  const instance = useInstanceBankAccounts();
   const accounts = !instance.ok
     ? []
     : instance.data.accounts.map((a) => a.payto_uri);
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
index a2e93d598..1c464cbc7 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
@@ -24,6 +24,7 @@ import { format } from "date-fns";
 import { h, VNode } from "preact";
 import { StateUpdater, useState } from "preact/hooks";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { datetimeFormatForSettings, useSettings } from 
"../../../../hooks/useSettings.js";
 
 type Entity = MerchantBackend.Transfers.TransferDetails & WithId;
 
@@ -56,7 +57,7 @@ export function CardTable({
       <header class="card-header">
         <p class="card-header-title">
           <span class="icon">
-            <i class="mdi mdi-bank" />
+            <i class="mdi mdi-arrow-left-right" />
           </span>
           <i18n.Translate>Transfers</i18n.Translate>
         </p>
@@ -121,6 +122,7 @@ function Table({
   hasMoreBefore,
 }: TableProps): VNode {
   const { i18n } = useTranslationContext();
+  const [settings] = useSettings();
   return (
     <div class="table-container">
       {onLoadMoreBefore && (
@@ -175,9 +177,9 @@ function Table({
                     ? i.execution_time.t_s == "never"
                       ? i18n.str`never`
                       : format(
-                          i.execution_time.t_s * 1000,
-                          "yyyy/MM/dd HH:mm:ss",
-                        )
+                        i.execution_time.t_s * 1000,
+                        datetimeFormatForSettings(settings),
+                      )
                     : i18n.str`unknown`}
                 </td>
                 <td>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
index 29e860342..1bc1673ba 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
@@ -28,6 +28,7 @@ import { useInstanceDetails } from 
"../../../../hooks/instance.js";
 import { useInstanceTransfers } from "../../../../hooks/transfer.js";
 import { ListPage } from "./ListPage.js";
 import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { useInstanceBankAccounts } from "../../../../hooks/bank.js";
 
 interface Props {
   onUnauthorized: () => VNode;
@@ -51,7 +52,7 @@ export default function ListTransfer({
 
   const [position, setPosition] = useState<string | undefined>(undefined);
 
-  const instance = useInstanceDetails();
+  const instance = useInstanceBankAccounts();
   const accounts = !instance.ok
     ? []
     : instance.data.accounts.map((a) => a.payto_uri);
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx
index 045c96c2c..817a7025c 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx
@@ -42,17 +42,15 @@ function createExample<Props>(
 
 export const Example = createExample(TestedComponent, {
   selected: {
-    accounts: [],
     name: "name",
     auth: { method: "external" },
     address: {},
+    user_type: "business",
+    use_stefan: true,
     jurisdiction: {},
-    default_max_deposit_fee: "TESTKUDOS:2",
-    default_max_wire_fee: "TESTKUDOS:1",
     default_pay_delay: {
       d_us: 1000 * 1000, //one second
     },
-    default_wire_fee_amortization: 1,
     default_wire_transfer_delay: {
       d_us: 1000 * 1000, //one second
     },
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx
index 547b40f07..a1c608f15 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx
@@ -19,7 +19,6 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { Amounts } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
@@ -29,10 +28,8 @@ import {
   FormProvider,
 } from "../../../components/form/FormProvider.js";
 import { DefaultInstanceFormFields } from 
"../../../components/instance/DefaultInstanceFormFields.js";
-import { UpdateTokenModal } from "../../../components/modal/index.js";
 import { useInstanceContext } from "../../../context/instance.js";
 import { MerchantBackend } from "../../../declaration.js";
-import { PAYTO_REGEX } from "../../../utils/constants.js";
 import { undefinedIfEmpty } from "../../../utils/table.js";
 
 type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & {
@@ -53,23 +50,23 @@ interface Props {
 function convert(
   from: MerchantBackend.Instances.QueryInstancesResponse,
 ): Entity {
-  const { accounts: qAccounts, ...rest } = from;
-  const accounts = qAccounts
-    .filter((a) => a.active)
-    .map(
-      (a) =>
-        ({
-          payto_uri: a.payto_uri,
-          credit_facade_url: a.credit_facade_url,
-          credit_facade_credentials: a.credit_facade_credentials,
-        } as MerchantBackend.Instances.MerchantBankAccount),
-    );
+  const { ...rest } = from;
+  // const accounts = qAccounts
+  //   .filter((a) => a.active)
+  //   .map(
+  //     (a) =>
+  //     ({
+  //       payto_uri: a.payto_uri,
+  //       credit_facade_url: a.credit_facade_url,
+  //       credit_facade_credentials: a.credit_facade_credentials,
+  //     } as MerchantBackend.Instances.MerchantBankAccount),
+  //   );
   const defaults = {
-    default_wire_fee_amortization: 1,
+    use_stefan: false,
     default_pay_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 }, //two hours
     default_wire_transfer_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 * 2 }, 
//two hours
   };
-  return { ...defaults, ...rest, accounts };
+  return { ...defaults, ...rest };
 }
 
 function getTokenValuePart(t?: string): string | undefined {
@@ -85,21 +82,21 @@ export function UpdatePage({
   selected,
   onBack,
 }: Props): VNode {
-  const { id, token } = useInstanceContext();
-  const currentTokenValue = getTokenValuePart(token);
-
-  function updateToken(token: string | undefined | null) {
-    const value =
-      token && token.startsWith("secret-token:")
-        ? token.substring("secret-token:".length)
-        : token;
-
-    if (!token) {
-      onChangeAuth({ method: "external" });
-    } else {
-      onChangeAuth({ method: "token", token: `secret-token:${value}` });
-    }
-  }
+  const { id } = useInstanceContext();
+  // const currentTokenValue = getTokenValuePart(token);
+
+  // function updateToken(token: string | undefined | null) {
+  //   const value =
+  //     token && token.startsWith("secret-token:")
+  //       ? token.substring("secret-token:".length)
+  //       : token;
+
+  //   if (!token) {
+  //     onChangeAuth({ method: "external" });
+  //   } else {
+  //     onChangeAuth({ method: "token", token: `secret-token:${value}` });
+  //   }
+  // }
 
   const [value, valueHandler] = useState<Partial<Entity>>(convert(selected));
 
@@ -110,35 +107,7 @@ export function UpdatePage({
     user_type: !value.user_type
       ? i18n.str`required`
       : value.user_type !== "business" && value.user_type !== "individual"
-      ? i18n.str`should be business or individual`
-      : undefined,
-    accounts:
-      !value.accounts || !value.accounts.length
-        ? i18n.str`required`
-        : undefinedIfEmpty(
-            value.accounts.map((p) => {
-              return !PAYTO_REGEX.test(p.payto_uri)
-                ? i18n.str`is not valid`
-                : undefined;
-            }),
-          ),
-    default_max_deposit_fee: !value.default_max_deposit_fee
-      ? i18n.str`required`
-      : !Amounts.parse(value.default_max_deposit_fee)
-      ? i18n.str`invalid format`
-      : undefined,
-    default_max_wire_fee: !value.default_max_wire_fee
-      ? i18n.str`required`
-      : !Amounts.parse(value.default_max_wire_fee)
-      ? i18n.str`invalid format`
-      : undefined,
-    default_wire_fee_amortization:
-      value.default_wire_fee_amortization === undefined
-        ? i18n.str`required`
-        : isNaN(value.default_wire_fee_amortization)
-        ? i18n.str`is not a number`
-        : value.default_wire_fee_amortization < 1
-        ? i18n.str`must be 1 or greater`
+        ? i18n.str`should be business or individual`
         : undefined,
     default_pay_delay: !value.default_pay_delay
       ? i18n.str`required`
@@ -163,10 +132,11 @@ export function UpdatePage({
   const hasErrors = Object.keys(errors).some(
     (k) => (errors as any)[k] !== undefined,
   );
+
   const submit = async (): Promise<void> => {
     await onUpdate(value as Entity);
   };
-  const [active, setActive] = useState(false);
+  // const [active, setActive] = useState(false);
 
   return (
     <div>
@@ -181,7 +151,7 @@ export function UpdatePage({
                   </span>
                 </div>
               </div>
-              <div class="level-right">
+              {/* <div class="level-right">
                 <div class="level-item">
                   <h1 class="title">
                     <button
@@ -200,33 +170,11 @@ export function UpdatePage({
                     </button>
                   </h1>
                 </div>
-              </div>
+              </div> */}
             </div>
           </div>
         </section>
 
-        <div class="columns">
-          <div class="column" />
-          <div class="column is-four-fifths">
-            {active && (
-              <UpdateTokenModal
-                oldToken={currentTokenValue}
-                onCancel={() => {
-                  setActive(false);
-                }}
-                onClear={() => {
-                  updateToken(null);
-                  setActive(false);
-                }}
-                onConfirm={(newToken) => {
-                  updateToken(newToken);
-                  setActive(false);
-                }}
-              />
-            )}
-          </div>
-          <div class="column" />
-        </div>
         <hr />
 
         <div class="columns">
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/validators/create/Create.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/Create.stories.tsx
new file mode 100644
index 000000000..56762db7b
--- /dev/null
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/Create.stories.tsx
@@ -0,0 +1,28 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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 { h, VNode, FunctionalComponent } from "preact";
+import { CreatePage as TestedComponent } from "./CreatePage.js";
+
+export default {
+  title: "Pages/Validators/Create",
+  component: TestedComponent,
+};
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx
similarity index 56%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx
index e20b9bc27..bdc86d226 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx
@@ -19,10 +19,6 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import {
-  Amounts,
-  MerchantTemplateContractDetails,
-} from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
@@ -35,19 +31,15 @@ import { Input } from 
"../../../../components/form/Input.js";
 import { InputCurrency } from "../../../../components/form/InputCurrency.js";
 import { InputDuration } from "../../../../components/form/InputDuration.js";
 import { InputNumber } from "../../../../components/form/InputNumber.js";
-import { InputSelector } from "../../../../components/form/InputSelector.js";
-import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
 import { useBackendContext } from "../../../../context/backend.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import {
-  isBase32RFC3548Charset,
-  randomBase32Key,
-} from "../../../../utils/crypto.js";
-import { undefinedIfEmpty } from "../../../../utils/table.js";
+import { InputSelector } from "../../../../components/form/InputSelector.js";
+import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
+import { isBase32RFC3548Charset, randomBase32Key } from 
"../../../../utils/crypto.js";
 import { QR } from "../../../../components/exception/QR.js";
 import { useInstanceContext } from "../../../../context/instance.js";
 
-type Entity = MerchantBackend.Template.TemplateAddDetails;
+type Entity = MerchantBackend.OTP.OtpDeviceAddDetails;
 
 interface Props {
   onCreate: (d: Entity) => Promise<void>;
@@ -57,62 +49,32 @@ interface Props {
 const algorithms = [0, 1, 2];
 const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"];
 
+
 export function CreatePage({ onCreate, onBack }: Props): VNode {
   const { i18n } = useTranslationContext();
   const backend = useBackendContext();
-  const { id: instanceId } = useInstanceContext();
-  const issuer = new URL(backend.url).hostname;
 
-  const [showKey, setShowKey] = useState(false);
-  const [state, setState] = useState<Partial<Entity>>({
-    template_contract: {
-      minimum_age: 0,
-      pay_duration: {
-        d_us: 1000 * 1000 * 60 * 30, //30 min
-      },
-    },
-  });
+  const [state, setState] = useState<Partial<Entity>>({});
 
-  const parsedPrice = !state.template_contract?.amount
-    ? undefined
-    : Amounts.parse(state.template_contract?.amount);
+  const [showKey, setShowKey] = useState(false);
 
   const errors: FormErrors<Entity> = {
-    template_id: !state.template_id ? i18n.str`should not be empty` : 
undefined,
-    template_description: !state.template_description
-      ? i18n.str`should not be empty`
-      : undefined,
-    template_contract: !state.template_contract
-      ? undefined
-      : undefinedIfEmpty({
-        amount: !state.template_contract?.amount
-          ? undefined
-          : !parsedPrice
-            ? i18n.str`not valid`
-            : Amounts.isZero(parsedPrice)
-              ? i18n.str`must be greater than 0`
-              : undefined,
-        minimum_age:
-          state.template_contract.minimum_age < 0
-            ? i18n.str`should be greater that 0`
-            : undefined,
-        pay_duration: !state.template_contract.pay_duration
-          ? i18n.str`can't be empty`
-          : state.template_contract.pay_duration.d_us === "forever"
-            ? undefined
-            : state.template_contract.pay_duration.d_us < 1000 * 1000 //less 
than one second
-              ? i18n.str`to short`
-              : undefined,
-      } as Partial<MerchantTemplateContractDetails>),
-    pos_key: !state.pos_key
-      ? !state.pos_algorithm
-        ? undefined
-        : i18n.str`required`
-      : !isBase32RFC3548Charset(state.pos_key)
+    otp_device_id: !state.otp_device_id ? i18n.str`required`
+      : !/[a-zA-Z0-9]*/.test(state.otp_device_id)
+        ? i18n.str`no valid. only characters and numbers`
+        : undefined,
+    otp_algorithm: !state.otp_algorithm ? i18n.str`required` : undefined,
+    otp_key: !state.otp_key ? i18n.str`required` :
+      !isBase32RFC3548Charset(state.otp_key)
         ? i18n.str`just letters and numbers from 2 to 7`
-        : state.pos_key.length !== 32
+        : state.otp_key.length !== 32
           ? i18n.str`size of the key should be 32`
           : undefined,
+    otp_description: !state.otp_description ? i18n.str`required`
+      : !/[a-zA-Z0-9]*/.test(state.otp_description)
+        ? i18n.str`no valid. only characters and numbers`
+        : undefined,
+
   };
 
   const hasErrors = Object.keys(errors).some(
@@ -124,8 +86,6 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
     return onCreate(state as any);
   };
 
-  const qrText = 
`otpauth://totp/${instanceId}/${state.template_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${state.pos_key}`;
-
   return (
     <div>
       <section class="section is-main-section">
@@ -137,57 +97,32 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
               valueHandler={setState}
               errors={errors}
             >
-              <InputWithAddon<Entity>
-                name="template_id"
-                help={`${backend.url}/instances/templates/${state.template_id 
?? ""}`}
-                label={i18n.str`Identifier`}
-                tooltip={i18n.str`Name of the template in URLs.`}
-              />
               <Input<Entity>
-                name="template_description"
-                label={i18n.str`Description`}
-                help=""
-                tooltip={i18n.str`Describe what this template stands for`}
+                name="otp_device_id"
+                label={i18n.str`ID`}
+                tooltip={i18n.str`Internal id on the system`}
               />
-              <Input
-                name="template_contract.summary"
-                inputType="multiline"
-                label={i18n.str`Fixed summary`}
-                tooltip={i18n.str`If specified, this template will create 
order with the same summary`}
-              />
-              <InputCurrency
-                name="template_contract.amount"
-                label={i18n.str`Fixed price`}
-                tooltip={i18n.str`If specified, this template will create 
order with the same price`}
-              />
-              <InputNumber
-                name="template_contract.minimum_age"
-                label={i18n.str`Minimum age`}
-                help=""
-                tooltip={i18n.str`Is this contract restricted to some age?`}
-              />
-              <InputDuration
-                name="template_contract.pay_duration"
-                label={i18n.str`Payment timeout`}
-                help=""
-                tooltip={i18n.str`How much time has the customer to complete 
the payment once the order was created.`}
+              <Input<Entity>
+                name="otp_description"
+                label={i18n.str`Descripiton`}
+                tooltip={i18n.str`Useful to identify the device physically`}
               />
               <InputSelector<Entity>
-                name="pos_algorithm"
+                name="otp_algorithm"
                 label={i18n.str`Verification algorithm`}
                 tooltip={i18n.str`Algorithm to use to verify transaction in 
offline mode`}
                 values={algorithms}
                 toStr={(v) => algorithmsNames[v]}
                 fromStr={(v) => Number(v)}
               />
-              {state.pos_algorithm && state.pos_algorithm > 0 ? (
+              {state.otp_algorithm && state.otp_algorithm > 0 ? (
                 <Fragment>
                   <InputWithAddon<Entity>
-                    name="pos_key"
-                    label={i18n.str`Point-of-sale key`}
+                    name="otp_key"
+                    label={i18n.str`Device key`}
                     inputType={showKey ? "text" : "password"}
                     help="Be sure to be very hard to guess or use the random 
generator"
-                    tooltip={i18n.str`Useful to validate the purchase`}
+                    tooltip={i18n.str`Your device need to have exactly the 
same value`}
                     fromStr={(v) => v.toUpperCase()}
                     addonAfter={
                       <span class="icon">
@@ -204,8 +139,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                           data-tooltip={i18n.str`generate random secret key`}
                           class="button is-info mr-3"
                           onClick={(e) => {
-                            const pos_key = randomBase32Key();
-                            setState((s) => ({ ...s, pos_key }));
+                            setState((s) => ({ ...s, otp_key: 
randomBase32Key() }));
                           }}
                         >
                           <i18n.Translate>random</i18n.Translate>
@@ -230,23 +164,6 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                       </span>
                     }
                   />
-                  {showKey && (
-                    <Fragment>
-                      <QR text={qrText} />
-                      <div
-                        style={{
-                          color: "grey",
-                          fontSize: "small",
-                          width: 200,
-                          textAlign: "center",
-                          margin: "auto",
-                          wordBreak: "break-all",
-                        }}
-                      >
-                        {qrText}
-                      </div>
-                    </Fragment>
-                  )}
                 </Fragment>
               ) : undefined}
             </FormProvider>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatedSuccessfully.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatedSuccessfully.tsx
new file mode 100644
index 000000000..3ad3cb3a3
--- /dev/null
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatedSuccessfully.tsx
@@ -0,0 +1,104 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import { QR } from "../../../../components/exception/QR.js";
+import { CreatedSuccessfully as Template } from 
"../../../../components/notifications/CreatedSuccessfully.js";
+import { useBackendContext } from "../../../../context/backend.js";
+import { useInstanceContext } from "../../../../context/instance.js";
+import { MerchantBackend } from "../../../../declaration.js";
+
+type Entity = MerchantBackend.OTP.OtpDeviceAddDetails;
+
+interface Props {
+  entity: Entity;
+  onConfirm: () => void;
+}
+
+function isNotUndefined<X>(x: X | undefined): x is X {
+  return !!x;
+}
+
+export function CreatedSuccessfully({
+  entity,
+  onConfirm,
+}: Props): VNode {
+  const { i18n } = useTranslationContext();
+  const backend = useBackendContext();
+  const { id: instanceId } = useInstanceContext();
+  const issuer = new URL(backend.url).hostname;
+  const qrText = 
`otpauth://totp/${instanceId}/${entity.otp_device_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${entity.otp_key}`;
+  const qrTextSafe = 
`otpauth://totp/${instanceId}/${entity.otp_device_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${entity.otp_key.substring(0,
 6)}...`;
+
+  return (
+    <Template onConfirm={onConfirm} >
+      <p class="is-size-5">
+        <i18n.Translate>
+          You can scan the next QR code with your device or safe the key 
before continue.
+        </i18n.Translate>
+      </p>
+      <div class="field is-horizontal">
+        <div class="field-label is-normal">
+          <label class="label">ID</label>
+        </div>
+        <div class="field-body is-flex-grow-3">
+          <div class="field">
+            <p class="control">
+              <input
+                readonly
+                class="input"
+                value={entity.otp_device_id}
+              />
+            </p>
+          </div>
+        </div>
+      </div>
+      <div class="field is-horizontal">
+        <div class="field-label is-normal">
+          <label 
class="label"><i18n.Translate>Description</i18n.Translate></label>
+        </div>
+        <div class="field-body is-flex-grow-3">
+          <div class="field">
+            <p class="control">
+              <input
+                class="input"
+                readonly
+                value={entity.otp_description}
+              />
+            </p>
+          </div>
+        </div>
+      </div>
+      <QR
+        text={qrText}
+      />
+      <div
+        style={{
+          color: "grey",
+          fontSize: "small",
+          width: 200,
+          textAlign: "center",
+          margin: "auto",
+          wordBreak: "break-all",
+        }}
+      >
+        {qrTextSafe}
+      </div>
+    </Template>
+  );
+}
+
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/index.tsx
similarity index 67%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/validators/create/index.tsx
index f218f4ead..648846793 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/index.tsx
@@ -24,38 +24,41 @@ import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import { useInstanceDetails } from "../../../../hooks/instance.js";
-import { useTransferAPI } from "../../../../hooks/transfer.js";
+import { useWebhookAPI } from "../../../../hooks/webhooks.js";
 import { Notification } from "../../../../utils/types.js";
 import { CreatePage } from "./CreatePage.js";
+import { useOtpDeviceAPI } from "../../../../hooks/otp.js";
+import { CreatedSuccessfully } from "./CreatedSuccessfully.js";
 
-export type Entity = MerchantBackend.Transfers.TransferInformation;
+export type Entity = MerchantBackend.OTP.OtpDeviceAddDetails;
 interface Props {
   onBack?: () => void;
   onConfirm: () => void;
 }
 
-export default function CreateTransfer({ onConfirm, onBack }: Props): VNode {
-  const { informTransfer } = useTransferAPI();
+export default function CreateValidator({ onConfirm, onBack }: Props): VNode {
+  const { createOtpDevice } = useOtpDeviceAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { i18n } = useTranslationContext();
-  const instance = useInstanceDetails();
-  const accounts = !instance.ok
-    ? []
-    : instance.data.accounts.map((a) => a.payto_uri);
+  const [created, setCreated] = 
useState<MerchantBackend.OTP.OtpDeviceAddDetails | null>(null)
+
+  if (created) {
+    return <CreatedSuccessfully entity={created} onConfirm={onConfirm} />
+  }
 
   return (
     <>
       <NotificationCard notification={notif} />
       <CreatePage
         onBack={onBack}
-        accounts={accounts}
-        onCreate={(request: MerchantBackend.Transfers.TransferInformation) => {
-          return informTransfer(request)
-            .then(() => onConfirm())
+        onCreate={(request: Entity) => {
+          return createOtpDevice(request)
+            .then((d) => {
+              setCreated(request)
+            })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not inform transfer`,
+                message: i18n.str`could not create device`,
                 type: "ERROR",
                 description: error.message,
               });
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/validators/list/List.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/List.stories.tsx
new file mode 100644
index 000000000..3aa491c53
--- /dev/null
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/List.stories.tsx
@@ -0,0 +1,28 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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 { FunctionalComponent, h } from "preact";
+import { ListPage as TestedComponent } from "./ListPage.js";
+
+export default {
+  title: "Pages/Validators/List",
+  component: TestedComponent,
+};
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/validators/list/ListPage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/ListPage.tsx
new file mode 100644
index 000000000..4efee9781
--- /dev/null
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/ListPage.tsx
@@ -0,0 +1,64 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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 { h, VNode } from "preact";
+import { MerchantBackend } from "../../../../declaration.js";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { CardTable } from "./Table.js";
+
+export interface Props {
+  devices: MerchantBackend.OTP.OtpDeviceEntry[];
+  onLoadMoreBefore?: () => void;
+  onLoadMoreAfter?: () => void;
+  onCreate: () => void;
+  onDelete: (e: MerchantBackend.OTP.OtpDeviceEntry) => void;
+  onSelect: (e: MerchantBackend.OTP.OtpDeviceEntry) => void;
+}
+
+export function ListPage({
+  devices,
+  onCreate,
+  onDelete,
+  onSelect,
+  onLoadMoreBefore,
+  onLoadMoreAfter,
+}: Props): VNode {
+  const form = { payto_uri: "" };
+
+  const { i18n } = useTranslationContext();
+  return (
+    <section class="section is-main-section">
+      <CardTable
+        devices={devices.map((o) => ({
+          ...o,
+          id: String(o.otp_device_id),
+        }))}
+        onCreate={onCreate}
+        onDelete={onDelete}
+        onSelect={onSelect}
+        onLoadMoreBefore={onLoadMoreBefore}
+        hasMoreBefore={!onLoadMoreBefore}
+        onLoadMoreAfter={onLoadMoreAfter}
+        hasMoreAfter={!onLoadMoreAfter}
+      />
+    </section>
+  );
+}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/Table.tsx
similarity index 80%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/validators/list/Table.tsx
index fd7b08875..b639a6134 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/Table.tsx
@@ -24,10 +24,10 @@ import { h, VNode } from "preact";
 import { StateUpdater, useState } from "preact/hooks";
 import { MerchantBackend } from "../../../../declaration.js";
 
-type Entity = MerchantBackend.Webhooks.WebhookEntry;
+type Entity = MerchantBackend.OTP.OtpDeviceEntry;
 
 interface Props {
-  webhooks: Entity[];
+  devices: Entity[];
   onDelete: (e: Entity) => void;
   onSelect: (e: Entity) => void;
   onCreate: () => void;
@@ -38,7 +38,7 @@ interface Props {
 }
 
 export function CardTable({
-  webhooks,
+  devices,
   onCreate,
   onDelete,
   onSelect,
@@ -58,12 +58,12 @@ export function CardTable({
           <span class="icon">
             <i class="mdi mdi-newspaper" />
           </span>
-          <i18n.Translate>Webhooks</i18n.Translate>
+          <i18n.Translate>OTP Devices</i18n.Translate>
         </p>
         <div class="card-header-icon" aria-label="more options">
           <span
             class="has-tooltip-left"
-            data-tooltip={i18n.str`add new webhooks`}
+            data-tooltip={i18n.str`add new devices`}
           >
             <button class="button is-info" type="button" onClick={onCreate}>
               <span class="icon is-small">
@@ -76,14 +76,11 @@ export function CardTable({
       <div class="card-content">
         <div class="b-table has-pagination">
           <div class="table-wrapper has-mobile-cards">
-            {webhooks.length > 0 ? (
+            {devices.length > 0 ? (
               <Table
-                instances={webhooks}
+                instances={devices}
                 onDelete={onDelete}
                 onSelect={onSelect}
-                onNewOrder={(d) => {
-                  console.log("test", d);
-                }}
                 rowSelection={rowSelection}
                 rowSelectionHandler={rowSelectionHandler}
                 onLoadMoreAfter={onLoadMoreAfter}
@@ -104,7 +101,6 @@ interface TableProps {
   rowSelection: string[];
   instances: Entity[];
   onDelete: (e: Entity) => void;
-  onNewOrder: (e: Entity) => void;
   onSelect: (e: Entity) => void;
   rowSelectionHandler: StateUpdater<string[]>;
   onLoadMoreBefore?: () => void;
@@ -122,7 +118,6 @@ function Table({
   instances,
   onLoadMoreAfter,
   onDelete,
-  onNewOrder,
   onSelect,
   onLoadMoreBefore,
   hasMoreAfter,
@@ -134,11 +129,11 @@ function Table({
       {onLoadMoreBefore && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n.str`load more webhooks before the first one`}
+          data-tooltip={i18n.str`load more devices before the first one`}
           disabled={!hasMoreBefore}
           onClick={onLoadMoreBefore}
         >
-          <i18n.Translate>load newer webhooks</i18n.Translate>
+          <i18n.Translate>load newer devices</i18n.Translate>
         </button>
       )}
       <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -148,7 +143,7 @@ function Table({
               <i18n.Translate>ID</i18n.Translate>
             </th>
             <th>
-              <i18n.Translate>Event type</i18n.Translate>
+              <i18n.Translate>Description</i18n.Translate>
             </th>
             <th />
           </tr>
@@ -156,35 +151,28 @@ function Table({
         <tbody>
           {instances.map((i) => {
             return (
-              <tr key={i.webhook_id}>
+              <tr key={i.otp_device_id}>
                 <td
                   onClick={(): void => onSelect(i)}
                   style={{ cursor: "pointer" }}
                 >
-                  {i.webhook_id}
+                  {i.otp_device_id}
                 </td>
                 <td
                   onClick={(): void => onSelect(i)}
                   style={{ cursor: "pointer" }}
                 >
-                  {i.event_type}
+                  {i.otp_device_id}
                 </td>
                 <td class="is-actions-cell right-sticky">
                   <div class="buttons is-right">
                     <button
                       class="button is-danger is-small has-tooltip-left"
-                      data-tooltip={i18n.str`delete selected webhook from the 
database`}
+                      data-tooltip={i18n.str`delete selected devices from the 
database`}
                       onClick={() => onDelete(i)}
                     >
                       Delete
                     </button>
-                    {/* <button
-                      class="button is-info is-small has-tooltip-left"
-                      data-tooltip={i18n.str`test webhook`}
-                      onClick={() => onNewOrder(i)}
-                    >
-                      Test
-                    </button> */}
                   </div>
                 </td>
               </tr>
@@ -195,11 +183,11 @@ function Table({
       {onLoadMoreAfter && (
         <button
           class="button is-fullwidth"
-          data-tooltip={i18n.str`load more webhooks after the last one`}
+          data-tooltip={i18n.str`load more devices after the last one`}
           disabled={!hasMoreAfter}
           onClick={onLoadMoreAfter}
         >
-          <i18n.Translate>load older webhooks</i18n.Translate>
+          <i18n.Translate>load older devices</i18n.Translate>
         </button>
       )}
     </div>
@@ -217,7 +205,7 @@ function EmptyTable(): VNode {
       </p>
       <p>
         <i18n.Translate>
-          There is no webhooks yet, add more pressing the + sign
+          There is no devices yet, add more pressing the + sign
         </i18n.Translate>
       </p>
     </div>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/index.tsx
similarity index 76%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/validators/list/index.tsx
index 2f91298bf..8837c848b 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/index.tsx
@@ -19,23 +19,20 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { HttpStatusCode } from "@gnu-taler/taler-util";
 import {
   ErrorType,
   HttpError,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { MerchantBackend } from "../../../../declaration.js";
-import {
-  useInstanceTemplates,
-  useTemplateAPI,
-} from "../../../../hooks/templates.js";
+import { useInstanceOtpDevices, useOtpDeviceAPI } from 
"../../../../hooks/otp.js";
 import { Notification } from "../../../../utils/types.js";
 import { ListPage } from "./ListPage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
 
 interface Props {
   onUnauthorized: () => VNode;
@@ -43,24 +40,20 @@ interface Props {
   onNotFound: () => VNode;
   onCreate: () => void;
   onSelect: (id: string) => void;
-  onNewOrder: (id: string) => void;
-  onQR: (id: string) => void;
 }
 
-export default function ListTemplates({
+export default function ListValidators({
   onUnauthorized,
   onLoadError,
   onCreate,
-  onQR,
   onSelect,
-  onNewOrder,
   onNotFound,
 }: Props): VNode {
   const [position, setPosition] = useState<string | undefined>(undefined);
   const { i18n } = useTranslationContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const { deleteTemplate } = useTemplateAPI();
-  const result = useInstanceTemplates({ position }, (id) => setPosition(id));
+  const { deleteOtpDevice } = useOtpDeviceAPI();
+  const result = useInstanceOtpDevices({ position }, (id) => setPosition(id));
 
   if (result.loading) return <Loading />;
   if (!result.ok) {
@@ -82,32 +75,26 @@ export default function ListTemplates({
       <NotificationCard notification={notif} />
 
       <ListPage
-        templates={result.data.templates}
+        devices={result.data.otp_devices}
         onLoadMoreBefore={
           result.isReachingStart ? result.loadMorePrev : undefined
         }
         onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
         onCreate={onCreate}
         onSelect={(e) => {
-          onSelect(e.template_id);
-        }}
-        onNewOrder={(e) => {
-          onNewOrder(e.template_id);
-        }}
-        onQR={(e) => {
-          onQR(e.template_id);
+          onSelect(e.otp_device_id);
         }}
-        onDelete={(e: MerchantBackend.Template.TemplateEntry) =>
-          deleteTemplate(e.template_id)
+        onDelete={(e: MerchantBackend.OTP.OtpDeviceEntry) =>
+          deleteOtpDevice(e.otp_device_id)
             .then(() =>
               setNotif({
-                message: i18n.str`template delete successfully`,
+                message: i18n.str`validator delete successfully`,
                 type: "SUCCESS",
               }),
             )
             .catch((error) =>
               setNotif({
-                message: i18n.str`could not delete the template`,
+                message: i18n.str`could not delete the validator`,
                 type: "ERROR",
                 description: error.message,
               }),
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/Update.stories.tsx
similarity index 58%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/validators/update/Update.stories.tsx
index 045c96c2c..fcb77b820 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/Update.stories.tsx
@@ -23,39 +23,10 @@ import { h, VNode, FunctionalComponent } from "preact";
 import { UpdatePage as TestedComponent } from "./UpdatePage.js";
 
 export default {
-  title: "Pages/Instance/Update",
+  title: "Pages/Validators/Update",
   component: TestedComponent,
   argTypes: {
     onUpdate: { action: "onUpdate" },
     onBack: { action: "onBack" },
   },
 };
-
-function createExample<Props>(
-  Component: FunctionalComponent<Props>,
-  props: Partial<Props>,
-) {
-  const r = (args: any) => <Component {...args} />;
-  r.args = props;
-  return r;
-}
-
-export const Example = createExample(TestedComponent, {
-  selected: {
-    accounts: [],
-    name: "name",
-    auth: { method: "external" },
-    address: {},
-    jurisdiction: {},
-    default_max_deposit_fee: "TESTKUDOS:2",
-    default_max_wire_fee: "TESTKUDOS:1",
-    default_pay_delay: {
-      d_us: 1000 * 1000, //one second
-    },
-    default_wire_fee_amortization: 1,
-    default_wire_transfer_delay: {
-      d_us: 1000 * 1000, //one second
-    },
-    merchant_pub: "ASDWQEKASJDKSADJ",
-  },
-});
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/validators/update/UpdatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/UpdatePage.tsx
new file mode 100644
index 000000000..585c12e11
--- /dev/null
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/UpdatePage.tsx
@@ -0,0 +1,185 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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 { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
+import {
+  FormErrors,
+  FormProvider,
+} from "../../../../components/form/FormProvider.js";
+import { Input } from "../../../../components/form/Input.js";
+import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { InputSelector } from "../../../../components/form/InputSelector.js";
+import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
+import { randomBase32Key } from "../../../../utils/crypto.js";
+
+type Entity = MerchantBackend.OTP.OtpDevicePatchDetails & WithId;
+
+interface Props {
+  onUpdate: (d: Entity) => Promise<void>;
+  onBack?: () => void;
+  device: Entity;
+}
+const algorithms = [0, 1, 2];
+const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"];
+export function UpdatePage({ device, onUpdate, onBack }: Props): VNode {
+  const { i18n } = useTranslationContext();
+
+  const [state, setState] = useState<Partial<Entity>>(device);
+  const [showKey, setShowKey] = useState(false);
+
+  const errors: FormErrors<Entity> = {
+  };
+
+  const hasErrors = Object.keys(errors).some(
+    (k) => (errors as any)[k] !== undefined,
+  );
+
+  const submitForm = () => {
+    if (hasErrors) return Promise.reject();
+    return onUpdate(state as any);
+  };
+
+  return (
+    <div>
+      <section class="section">
+        <section class="hero is-hero-bar">
+          <div class="hero-body">
+            <div class="level">
+              <div class="level-left">
+                <div class="level-item">
+                  <span class="is-size-4">
+                    Device: <b>{device.id}</b>
+                  </span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </section>
+        <hr />
+
+        <section class="section is-main-section">
+          <div class="columns">
+            <div class="column is-four-fifths">
+              <FormProvider
+                object={state}
+                valueHandler={setState}
+                errors={errors}
+              >
+                <Input<Entity>
+                  name="otp_description"
+                  label={i18n.str`Description`}
+                  tooltip={i18n.str`dddd`}
+                />
+                <InputSelector<Entity>
+                  name="otp_algorithm"
+                  label={i18n.str`Verification algorithm`}
+                  tooltip={i18n.str`Algorithm to use to verify transaction in 
offline mode`}
+                  values={algorithms}
+                  toStr={(v) => algorithmsNames[v]}
+                  fromStr={(v) => Number(v)}
+                />
+                {state.otp_algorithm && state.otp_algorithm > 0 ? (
+                  <Fragment>
+                    <InputWithAddon<Entity>
+                      name="otp_key"
+                      label={i18n.str`Device key`}
+                      readonly={state.otp_key === undefined}
+                      inputType={showKey ? "text" : "password"}
+                      help={state.otp_key === undefined ? "Not modified" : "Be 
sure to be very hard to guess or use the random generator"}
+                      tooltip={i18n.str`Your device need to have exactly the 
same value`}
+                      fromStr={(v) => v.toUpperCase()}
+                      addonAfter={
+                        <span class="icon">
+                          {showKey ? (
+                            <i class="mdi mdi-eye" />
+                          ) : (
+                            <i class="mdi mdi-eye-off" />
+                          )}
+                        </span>
+                      }
+                      side={
+                        state.otp_key === undefined ? <button
+
+                          onClick={(e) => {
+                            setState((s) => ({ ...s, otp_key: "" }));
+                          }}
+                          class="button">change key</button> :
+                          <span style={{ display: "flex" }}>
+                            <button
+                              data-tooltip={i18n.str`generate random secret 
key`}
+                              class="button is-info mr-3"
+                              onClick={(e) => {
+                                setState((s) => ({ ...s, otp_key: 
randomBase32Key() }));
+                              }}
+                            >
+                              <i18n.Translate>random</i18n.Translate>
+                            </button>
+                            <button
+                              data-tooltip={
+                                showKey
+                                  ? i18n.str`show secret key`
+                                  : i18n.str`hide secret key`
+                              }
+                              class="button is-info mr-3"
+                              onClick={(e) => {
+                                setShowKey(!showKey);
+                              }}
+                            >
+                              {showKey ? (
+                                <i18n.Translate>hide</i18n.Translate>
+                              ) : (
+                                <i18n.Translate>show</i18n.Translate>
+                              )}
+                            </button>
+                          </span>
+                      }
+                    />
+                  </Fragment>
+                ) : undefined}              </FormProvider>
+
+              <div class="buttons is-right mt-5">
+                {onBack && (
+                  <button class="button" onClick={onBack}>
+                    <i18n.Translate>Cancel</i18n.Translate>
+                  </button>
+                )}
+                <AsyncButton
+                  disabled={hasErrors}
+                  data-tooltip={
+                    hasErrors
+                      ? i18n.str`Need to complete marked fields`
+                      : "confirm operation"
+                  }
+                  onClick={submitForm}
+                >
+                  <i18n.Translate>Confirm</i18n.Translate>
+                </AsyncButton>
+              </div>
+            </div>
+          </div>
+        </section>
+      </section>
+    </div>
+  );
+}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx
similarity index 62%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx
index 1f74afc2b..9a27ccfee 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx
@@ -28,34 +28,36 @@ import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import {
-  useTemplateAPI,
-  useTemplateDetails,
-} from "../../../../hooks/templates.js";
+import { MerchantBackend, WithId } from "../../../../declaration.js";
 import { Notification } from "../../../../utils/types.js";
-import { QrPage } from "./QrPage.js";
+import { UpdatePage } from "./UpdatePage.js";
 import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { useOtpDeviceAPI, useOtpDeviceDetails } from 
"../../../../hooks/otp.js";
+
+export type Entity = MerchantBackend.OTP.OtpDevicePatchDetails & WithId;
 
-export type Entity = MerchantBackend.Transfers.TransferInformation;
 interface Props {
   onBack?: () => void;
+  onConfirm: () => void;
   onUnauthorized: () => VNode;
   onNotFound: () => VNode;
   onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
-  tid: string;
+  vid: string;
 }
-
-export default function TemplateQrPage({
-  tid,
+export default function UpdateValidator({
+  vid,
+  onConfirm,
   onBack,
-  onLoadError,
-  onNotFound,
   onUnauthorized,
+  onNotFound,
+  onLoadError,
 }: Props): VNode {
-  const result = useTemplateDetails(tid);
+  const { updateOtpDevice } = useOtpDeviceAPI();
+  const result = useOtpDeviceDetails(vid);
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
 
+  const { i18n } = useTranslationContext();
+
   if (result.loading) return <Loading />;
   if (!result.ok) {
     if (
@@ -72,9 +74,29 @@ export default function TemplateQrPage({
   }
 
   return (
-    <>
+    <Fragment>
       <NotificationCard notification={notif} />
-      <QrPage template={result.data} id={tid} onBack={onBack} />
-    </>
+      <UpdatePage
+        device={{
+          id: vid,
+          otp_algorithm: result.data.otp_algorithm,
+          otp_description: result.data.device_description,
+          otp_key: undefined,
+          otp_ctr: result.data.otp_ctr
+        }}
+        onBack={onBack}
+        onUpdate={(data) => {
+          return updateOtpDevice(vid, data)
+            .then(onConfirm)
+            .catch((error) => {
+              setNotif({
+                message: i18n.str`could not update template`,
+                type: "ERROR",
+                description: error.message,
+              });
+            });
+        }}
+      />
+    </Fragment>
   );
 }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
index fd7b08875..124ced1f1 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
@@ -81,9 +81,6 @@ export function CardTable({
                 instances={webhooks}
                 onDelete={onDelete}
                 onSelect={onSelect}
-                onNewOrder={(d) => {
-                  console.log("test", d);
-                }}
                 rowSelection={rowSelection}
                 rowSelectionHandler={rowSelectionHandler}
                 onLoadMoreAfter={onLoadMoreAfter}
@@ -104,7 +101,6 @@ interface TableProps {
   rowSelection: string[];
   instances: Entity[];
   onDelete: (e: Entity) => void;
-  onNewOrder: (e: Entity) => void;
   onSelect: (e: Entity) => void;
   rowSelectionHandler: StateUpdater<string[]>;
   onLoadMoreBefore?: () => void;
@@ -122,7 +118,6 @@ function Table({
   instances,
   onLoadMoreAfter,
   onDelete,
-  onNewOrder,
   onSelect,
   onLoadMoreBefore,
   hasMoreAfter,
diff --git a/packages/merchant-backoffice-ui/src/paths/settings/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/settings/index.tsx
index 128450553..0d514f2df 100644
--- a/packages/merchant-backoffice-ui/src/paths/settings/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/settings/index.tsx
@@ -1,10 +1,10 @@
-import { VNode, h } from "preact";
-import { LangSelector } from "../../components/menu/LangSelector.js";
 import { useLang, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { VNode, h } from "preact";
+import { FormErrors, FormProvider } from 
"../../components/form/FormProvider.js";
+import { InputSelector } from "../../components/form/InputSelector.js";
 import { InputToggle } from "../../components/form/InputToggle.js";
+import { LangSelector } from "../../components/menu/LangSelector.js";
 import { Settings, useSettings } from "../../hooks/useSettings.js";
-import { FormErrors, FormProvider } from 
"../../components/form/FormProvider.js";
-import { useState } from "preact/hooks";
 
 function getBrowserLang(): string | undefined {
   if (typeof window === "undefined") return undefined;
@@ -24,7 +24,11 @@ export function Settings(): VNode {
 
   function valueHandler(s: (d: Partial<Settings>) => Partial<Settings>): void {
     const next = s(value)
-    updateValue("advanceOrderMode", next.advanceOrderMode ?? false)
+    const v: Settings = {
+      advanceOrderMode: next.advanceOrderMode ?? false,
+      dateFormat: next.dateFormat ?? "ymd"
+    }
+    updateValue(v)
   }
 
   return <div>
@@ -32,41 +36,64 @@ export function Settings(): VNode {
       <div class="columns">
         <div class="column" />
         <div class="column is-four-fifths">
-          <div class="field is-horizontal">
-            <div class="field-label is-normal">
-              <label class="label" style={{ width: 200 }}>
-                <i18n.Translate>Language</i18n.Translate>
-                <span class="icon has-tooltip-right" data-tooltip={"Force 
language setting instance of taking the browser"}>
-                  <i class="mdi mdi-information" />
-                </span>
-              </label>
-            </div>
-            <div class="field has-addons">
-              <LangSelector />
-              &nbsp;
-              {borwserLang !== undefined && <button
-                data-tooltip={i18n.str`generate random secret key`}
-                class="button is-info mr-3"
-                onClick={(e) => {
-                  update(borwserLang.substring(0, 2))
+          <div>
+
+            <FormProvider<Settings>
+              name="settings"
+              errors={errors}
+              object={value}
+              valueHandler={valueHandler}
+            >
+              <div class="field is-horizontal">
+                <div class="field-label is-normal">
+                  <label class="label">
+                    <i18n.Translate>Language</i18n.Translate>
+                    <span class="icon has-tooltip-right" data-tooltip={"Force 
language setting instance of taking the browser"}>
+                      <i class="mdi mdi-information" />
+                    </span>
+                  </label>
+                </div>
+                <div class="field field-body has-addons is-flex-grow-3">
+                  <LangSelector />
+                  &nbsp;
+                  {borwserLang !== undefined && <button
+                    data-tooltip={i18n.str`generate random secret key`}
+                    class="button is-info mr-2"
+                    onClick={(e) => {
+                      update(borwserLang.substring(0, 2))
+                    }}
+                  >
+                    <i18n.Translate>Set default</i18n.Translate>
+                  </button>}
+                </div>
+              </div>
+              <InputToggle<Settings>
+                label={i18n.str`Advance order creation`}
+                tooltip={i18n.str`Shows more options in the order creation 
form`}
+                name="advanceOrderMode"
+              />
+              <InputSelector<Settings>
+                name="dateFormat"
+                label={i18n.str`Date format`}
+                expand={true}
+                help={
+                  value.dateFormat === "dmy" ? "31/12/2001" : value.dateFormat 
=== "mdy" ? "12/31/2001" : value.dateFormat === "ymd" ? "2001/12/31" : ""
+                }
+                toStr={(e) => {
+                  if (e === "ymd") return "year month day"
+                  if (e === "mdy") return "month day year"
+                  if (e === "dmy") return "day month year"
+                  return "choose one"
                 }}
-              >
-                <i18n.Translate>Set default</i18n.Translate>
-              </button>}
-            </div>
+                values={[
+                  "ymd",
+                  "mdy",
+                  "dmy",
+                ]}
+                tooltip={i18n.str`how the date is going to be displayed`}
+              />
+            </FormProvider>
           </div>
-          <FormProvider<Settings>
-            name="settings"
-            errors={errors}
-            object={value}
-            valueHandler={valueHandler}
-          >
-            <InputToggle<Settings>
-              label={i18n.str`Advance order creation`}
-              tooltip={i18n.str`Shows more options in the order creation form`}
-              name="advanceOrderMode"
-            />
-          </FormProvider>
 
 
         </div>
diff --git a/packages/merchant-backoffice-ui/src/schemas/index.ts 
b/packages/merchant-backoffice-ui/src/schemas/index.ts
index 149761c55..4be77595b 100644
--- a/packages/merchant-backoffice-ui/src/schemas/index.ts
+++ b/packages/merchant-backoffice-ui/src/schemas/index.ts
@@ -123,7 +123,7 @@ export const InstanceSchema = yup.object().shape({
 export const InstanceUpdateSchema = InstanceSchema.clone().omit(["id"]);
 export const InstanceCreateSchema = InstanceSchema.clone();
 
-export const AuthorizeTipSchema = yup.object().shape({
+export const AuthorizeRewardSchema = yup.object().shape({
   justification: yup.string().required(),
   amount: yup
     .string()
@@ -161,7 +161,7 @@ export const OrderCreateSchema = yup.object().shape({
           currencyGreaterThan0,
         ),
     }),
-  extra: yup.string().test("extra", "is not a JSON format", stringIsValidJSON),
+  // extra: yup.object().test("extra", "is not a JSON format", 
stringIsValidJSON),
   payments: yup
     .object()
     .required()
diff --git a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
index b32eb831a..6ade0718a 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
@@ -16,7 +16,6 @@
 
 import {
   Amounts,
-  BackupBackupProviderTerms,
   canonicalizeBaseUrl,
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
@@ -41,6 +40,12 @@ interface Props {
   onBack: () => Promise<void>;
 }
 
+interface BackupBackupProviderTerms {
+  annual_fee: string;
+  storage_limit_in_megabytes: number;
+  supported_protocol_version: string;
+}
+
 export function ProviderAddPage({ onBack }: Props): VNode {
   const [verifying, setVerifying] = useState<
     | { url: string; name: string; provider: BackupBackupProviderTerms }

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