gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (1c3e9473f -> e1d86816a)


From: gnunet
Subject: [taler-wallet-core] branch master updated (1c3e9473f -> e1d86816a)
Date: Mon, 04 Sep 2023 19:18:05 +0200

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

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

    from 1c3e9473f -remove bogus logging
     new 241a37c88 add payto type
     new ff20c3e25 upgrade swr
     new e1d86816a backoffcie new version, lot of changes

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


Summary of changes:
 packages/merchant-backoffice-ui/package.json       |   6 +-
 .../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 ++---
 .../create/Create.stories.tsx                      |   2 +-
 .../paths/instance/accounts/create/CreatePage.tsx  | 175 +++++++++
 .../{webhooks => accounts}/create/index.tsx        |  18 +-
 .../{templates => accounts}/list/List.stories.tsx  |   2 +-
 .../{webhooks => accounts}/list/ListPage.tsx       |  12 +-
 .../src/paths/instance/accounts/list/Table.tsx     | 385 +++++++++++++++++++
 .../instance/{webhooks => accounts}/list/index.tsx |  28 +-
 .../update/Update.stories.tsx                      |   2 +-
 .../{webhooks => accounts}/update/UpdatePage.tsx   |  48 +--
 .../{templates => accounts}/update/index.tsx       |  27 +-
 .../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 +++++
 .../qr/Qr.stories.tsx => token/stories.tsx}        |   5 +-
 .../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 ++----
 .../create/Create.stories.tsx                      |   2 +-
 .../create/CreatePage.tsx                          | 149 ++------
 .../validators/create/CreatedSuccessfully.tsx      | 104 +++++
 .../{webhooks => validators}/create/index.tsx      |  23 +-
 .../list/List.stories.tsx                          |   2 +-
 .../{webhooks => validators}/list/ListPage.tsx     |  12 +-
 .../{webhooks => validators}/list/Table.tsx        |  46 +--
 .../{webhooks => validators}/list/index.tsx        |  27 +-
 .../update/Update.stories.tsx                      |   2 +-
 .../instance/validators/update/UpdatePage.tsx      | 185 +++++++++
 .../{webhooks => validators}/update/index.tsx      |  27 +-
 .../src/paths/instance/webhooks/list/Table.tsx     |   5 -
 .../src/paths/settings/index.tsx                   | 103 +++--
 .../merchant-backoffice-ui/src/schemas/index.ts    |   4 +-
 packages/taler-util/src/bitcoin.ts                 |   8 +-
 packages/taler-util/src/payto.ts                   |   6 +-
 .../src/wallet/ProviderAddPage.tsx                 |   7 +-
 pnpm-lock.yaml                                     |  20 +-
 94 files changed, 3438 insertions(+), 1870 deletions(-)
 rename 
packages/merchant-backoffice-ui/src/components/form/{InputSearchProduct.tsx => 
InputSearchOnList.tsx} (65%)
 create mode 100644 packages/merchant-backoffice-ui/src/hooks/bank.ts
 create mode 100644 packages/merchant-backoffice-ui/src/hooks/otp.ts
 copy packages/merchant-backoffice-ui/src/paths/instance/{webhooks => 
accounts}/create/Create.stories.tsx (96%)
 create mode 100644 
packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
 copy packages/merchant-backoffice-ui/src/paths/instance/{webhooks => 
accounts}/create/index.tsx (76%)
 copy packages/merchant-backoffice-ui/src/paths/instance/{templates => 
accounts}/list/List.stories.tsx (96%)
 copy packages/merchant-backoffice-ui/src/paths/instance/{webhooks => 
accounts}/list/ListPage.tsx (85%)
 create mode 100644 
packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx
 copy packages/merchant-backoffice-ui/src/paths/instance/{webhooks => 
accounts}/list/index.tsx (79%)
 copy packages/merchant-backoffice-ui/src/paths/instance/{templates => 
accounts}/update/Update.stories.tsx (96%)
 copy packages/merchant-backoffice-ui/src/paths/instance/{webhooks => 
accounts}/update/UpdatePage.tsx (64%)
 copy packages/merchant-backoffice-ui/src/paths/instance/{templates => 
accounts}/update/index.tsx (82%)
 rename 
packages/merchant-backoffice-ui/src/paths/instance/reserves/details/{TipInfo.tsx
 => RewardInfo.tsx} (77%)
 rename 
packages/merchant-backoffice-ui/src/paths/instance/reserves/list/{AutorizeTipModal.tsx
 => AutorizeRewardModal.tsx} (74%)
 create mode 100644 
packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx
 create mode 100644 
packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
 copy 
packages/merchant-backoffice-ui/src/paths/instance/{templates/qr/Qr.stories.tsx 
=> token/stories.tsx} (89%)
 copy packages/merchant-backoffice-ui/src/paths/instance/{webhooks => 
validators}/create/Create.stories.tsx (96%)
 copy packages/merchant-backoffice-ui/src/paths/instance/{templates => 
validators}/create/CreatePage.tsx (56%)
 create mode 100644 
packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatedSuccessfully.tsx
 copy packages/merchant-backoffice-ui/src/paths/instance/{webhooks => 
validators}/create/index.tsx (70%)
 copy packages/merchant-backoffice-ui/src/paths/instance/{templates => 
validators}/list/List.stories.tsx (96%)
 copy packages/merchant-backoffice-ui/src/paths/instance/{webhooks => 
validators}/list/ListPage.tsx (86%)
 copy packages/merchant-backoffice-ui/src/paths/instance/{webhooks => 
validators}/list/Table.tsx (80%)
 copy packages/merchant-backoffice-ui/src/paths/instance/{webhooks => 
validators}/list/index.tsx (81%)
 copy packages/merchant-backoffice-ui/src/paths/instance/{templates => 
validators}/update/Update.stories.tsx (96%)
 create mode 100644 
packages/merchant-backoffice-ui/src/paths/instance/validators/update/UpdatePage.tsx
 copy packages/merchant-backoffice-ui/src/paths/instance/{webhooks => 
validators}/update/index.tsx (81%)

diff --git a/packages/merchant-backoffice-ui/package.json 
b/packages/merchant-backoffice-ui/package.json
index 3a0c22adb..3d568a502 100644
--- a/packages/merchant-backoffice-ui/package.json
+++ b/packages/merchant-backoffice-ui/package.json
@@ -1,7 +1,7 @@
 {
   "private": true,
   "name": "@gnu-taler/merchant-backoffice-ui",
-  "version": "0.0.5",
+  "version": "0.1.0",
   "license": "AGPL-3.0-or-later",
   "type": "module",
   "scripts": {
@@ -41,7 +41,7 @@
     "preact": "10.11.3",
     "preact-router": "3.2.1",
     "qrcode-generator": "1.4.4",
-    "swr": "1.3.0",
+    "swr": "2.2.2",
     "yup": "^0.32.9"
   },
   "devDependencies": {
@@ -81,4 +81,4 @@
   "pogen": {
     "domain": "taler-merchant-backoffice"
   }
-}
+}
\ No newline at end of file
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/webhooks/create/Create.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx
similarity index 96%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/Create.stories.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx
index 4857ede97..3336c53a4 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/Create.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx
@@ -23,6 +23,6 @@ import { h, VNode, FunctionalComponent } from "preact";
 import { CreatePage as TestedComponent } from "./CreatePage.js";
 
 export default {
-  title: "Pages/Webhooks/Create",
+  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/webhooks/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
similarity index 76%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/index.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
index 924e6d9b8..7d33d25ce 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
@@ -27,15 +27,17 @@ import { MerchantBackend } from 
"../../../../declaration.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.Webhooks.WebhookAddDetails;
+export type Entity = MerchantBackend.BankAccounts.AccountAddDetails;
 interface Props {
   onBack?: () => void;
   onConfirm: () => void;
 }
 
-export default function CreateWebhook({ onConfirm, onBack }: Props): VNode {
-  const { createWebhook } = useWebhookAPI();
+export default function CreateValidator({ onConfirm, onBack }: Props): VNode {
+  const { createBankAccount } = useBankAccountAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { i18n } = useTranslationContext();
 
@@ -44,12 +46,14 @@ export default function CreateWebhook({ onConfirm, onBack 
}: Props): VNode {
       <NotificationCard notification={notif} />
       <CreatePage
         onBack={onBack}
-        onCreate={(request: MerchantBackend.Webhooks.WebhookAddDetails) => {
-          return createWebhook(request)
-            .then(() => onConfirm())
+        onCreate={(request: Entity) => {
+          return createBankAccount(request)
+            .then((d) => {
+              onConfirm()
+            })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not inform template`,
+                message: i18n.str`could not create device`,
                 type: "ERROR",
                 description: error.message,
               });
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/List.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx
similarity index 96%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/list/List.stories.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx
index 702e9ba4a..6b4b63735 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/List.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx
@@ -23,6 +23,6 @@ import { FunctionalComponent, h } from "preact";
 import { ListPage as TestedComponent } from "./ListPage.js";
 
 export default {
-  title: "Pages/Templates/List",
+  title: "Pages/Accounts/List",
   component: TestedComponent,
 };
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx
similarity index 85%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx
index 87e221e3c..24da755b9 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx
@@ -25,16 +25,16 @@ import { useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { CardTable } from "./Table.js";
 
 export interface Props {
-  webhooks: MerchantBackend.Webhooks.WebhookEntry[];
+  devices: MerchantBackend.BankAccounts.BankAccountEntry[];
   onLoadMoreBefore?: () => void;
   onLoadMoreAfter?: () => void;
   onCreate: () => void;
-  onDelete: (e: MerchantBackend.Webhooks.WebhookEntry) => void;
-  onSelect: (e: MerchantBackend.Webhooks.WebhookEntry) => void;
+  onDelete: (e: MerchantBackend.BankAccounts.BankAccountEntry) => void;
+  onSelect: (e: MerchantBackend.BankAccounts.BankAccountEntry) => void;
 }
 
 export function ListPage({
-  webhooks,
+  devices,
   onCreate,
   onDelete,
   onSelect,
@@ -47,9 +47,9 @@ export function ListPage({
   return (
     <section class="section is-main-section">
       <CardTable
-        webhooks={webhooks.map((o) => ({
+        accounts={devices.map((o) => ({
           ...o,
-          id: String(o.webhook_id),
+          id: String(o.h_wire),
         }))}
         onCreate={onCreate}
         onDelete={onDelete}
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/webhooks/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
similarity index 79%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
index a6f6f1511..9788ce0ec 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/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 {
-  useInstanceWebhooks,
-  useWebhookAPI,
-} from "../../../../hooks/webhooks.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;
@@ -45,7 +43,7 @@ interface Props {
   onSelect: (id: string) => void;
 }
 
-export default function ListWebhooks({
+export default function ListValidators({
   onUnauthorized,
   onLoadError,
   onCreate,
@@ -55,8 +53,8 @@ export default function ListWebhooks({
   const [position, setPosition] = useState<string | undefined>(undefined);
   const { i18n } = useTranslationContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const { deleteWebhook } = useWebhookAPI();
-  const result = useInstanceWebhooks({ position }, (id) => setPosition(id));
+  const { deleteBankAccount } = useBankAccountAPI();
+  const result = useInstanceBankAccounts({ position }, (id) => 
setPosition(id));
 
   if (result.loading) return <Loading />;
   if (!result.ok) {
@@ -78,26 +76,26 @@ export default function ListWebhooks({
       <NotificationCard notification={notif} />
 
       <ListPage
-        webhooks={result.data.webhooks}
+        devices={result.data.accounts}
         onLoadMoreBefore={
           result.isReachingStart ? result.loadMorePrev : undefined
         }
         onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
         onCreate={onCreate}
         onSelect={(e) => {
-          onSelect(e.webhook_id);
+          onSelect(e.h_wire);
         }}
-        onDelete={(e: MerchantBackend.Webhooks.WebhookEntry) =>
-          deleteWebhook(e.webhook_id)
+        onDelete={(e: MerchantBackend.BankAccounts.BankAccountEntry) =>
+          deleteBankAccount(e.h_wire)
             .then(() =>
               setNotif({
-                message: i18n.str`webhook delete successfully`,
+                message: i18n.str`bank account delete successfully`,
                 type: "SUCCESS",
               }),
             )
             .catch((error) =>
               setNotif({
-                message: i18n.str`could not delete the webhook`,
+                message: i18n.str`could not delete the bank account`,
                 type: "ERROR",
                 description: error.message,
               }),
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/Update.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx
similarity index 96%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/update/Update.stories.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx
index 8d07cb31f..fcb77b820 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/Update.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx
@@ -23,7 +23,7 @@ import { h, VNode, FunctionalComponent } from "preact";
 import { UpdatePage as TestedComponent } from "./UpdatePage.js";
 
 export default {
-  title: "Pages/Templates/Update",
+  title: "Pages/Validators/Update",
   component: TestedComponent,
   argTypes: {
     onUpdate: { action: "onUpdate" },
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
similarity index 64%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
index 76a23b6e5..802f593cf 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
@@ -28,31 +28,21 @@ import {
   FormProvider,
 } from "../../../../components/form/FormProvider.js";
 import { Input } from "../../../../components/form/Input.js";
-import { useBackendContext } from "../../../../context/backend.js";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
 
-type Entity = MerchantBackend.Webhooks.WebhookPatchDetails & WithId;
+type Entity = MerchantBackend.BankAccounts.AccountPatchDetails & WithId;
 
 interface Props {
   onUpdate: (d: Entity) => Promise<void>;
   onBack?: () => void;
-  webhook: Entity;
+  account: Entity;
 }
-const validMethod = ["GET", "POST", "PUT", "PATCH", "HEAD"];
-
-export function UpdatePage({ webhook, onUpdate, onBack }: Props): VNode {
+export function UpdatePage({ account, onUpdate, onBack }: Props): VNode {
   const { i18n } = useTranslationContext();
 
-  const [state, setState] = useState<Partial<Entity>>(webhook);
+  const [state, setState] = useState<Partial<Entity>>(account);
 
   const errors: FormErrors<Entity> = {
-    event_type: !state.event_type ? i18n.str`required` : undefined,
-    http_method: !state.http_method
-      ? i18n.str`required`
-      : !validMethod.includes(state.http_method)
-      ? i18n.str`should be one of '${validMethod.join(", ")}'`
-      : undefined,
-    url: !state.url ? i18n.str`required` : undefined,
   };
 
   const hasErrors = Object.keys(errors).some(
@@ -73,7 +63,7 @@ export function UpdatePage({ webhook, onUpdate, onBack }: 
Props): VNode {
               <div class="level-left">
                 <div class="level-item">
                   <span class="is-size-4">
-                    Webhook: <b>{webhook.id}</b>
+                    Account: <b>{account.id}</b>
                   </span>
                 </div>
               </div>
@@ -91,31 +81,9 @@ export function UpdatePage({ webhook, onUpdate, onBack }: 
Props): VNode {
                 errors={errors}
               >
                 <Input<Entity>
-                  name="event_type"
-                  label={i18n.str`Event`}
-                  tooltip={i18n.str`The event of the webhook: why the webhook 
is used`}
-                />
-                <Input<Entity>
-                  name="http_method"
-                  label={i18n.str`Method`}
-                  tooltip={i18n.str`Method used by the webhook`}
-                />
-                <Input<Entity>
-                  name="url"
-                  label={i18n.str`URL`}
-                  tooltip={i18n.str`URL of the webhook where the customer will 
be redirected`}
-                />
-                <Input<Entity>
-                  name="header_template"
-                  label={i18n.str`Header`}
-                  inputType="multiline"
-                  tooltip={i18n.str`Header template of the webhook`}
-                />
-                <Input<Entity>
-                  name="body_template"
-                  inputType="multiline"
-                  label={i18n.str`Body`}
-                  tooltip={i18n.str`Body template by the webhook`}
+                  name="credit_facade_url"
+                  label={i18n.str`Description`}
+                  tooltip={i18n.str`dddd`}
                 />
               </FormProvider>
 
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
similarity index 82%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
index 3adca45db..44dee7651 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
@@ -19,25 +19,22 @@
  * @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, WithId } from "../../../../declaration.js";
-import {
-  useTemplateAPI,
-  useTemplateDetails,
-} from "../../../../hooks/templates.js";
+import { useBankAccountAPI, useBankAccountDetails } from 
"../../../../hooks/bank.js";
 import { Notification } from "../../../../utils/types.js";
 import { UpdatePage } from "./UpdatePage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
 
-export type Entity = MerchantBackend.Template.TemplatePatchDetails & WithId;
+export type Entity = MerchantBackend.BankAccounts.AccountPatchDetails & WithId;
 
 interface Props {
   onBack?: () => void;
@@ -45,18 +42,18 @@ interface Props {
   onUnauthorized: () => VNode;
   onNotFound: () => VNode;
   onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
-  tid: string;
+  bid: string;
 }
-export default function UpdateTemplate({
-  tid,
+export default function UpdateValidator({
+  bid,
   onConfirm,
   onBack,
   onUnauthorized,
   onNotFound,
   onLoadError,
 }: Props): VNode {
-  const { updateTemplate } = useTemplateAPI();
-  const result = useTemplateDetails(tid);
+  const { updateBankAccount } = useBankAccountAPI();
+  const result = useBankAccountDetails(bid);
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
 
   const { i18n } = useTranslationContext();
@@ -80,14 +77,14 @@ export default function UpdateTemplate({
     <Fragment>
       <NotificationCard notification={notif} />
       <UpdatePage
-        template={{ ...result.data, id: tid }}
+        account={{ ...result.data, id: bid }}
         onBack={onBack}
         onUpdate={(data) => {
-          return updateTemplate(tid, data)
+          return updateBankAccount(bid, data)
             .then(onConfirm)
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not update template`,
+                message: i18n.str`could not update account`,
                 type: "ERROR",
                 description: error.message,
               });
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/templates/qr/Qr.stories.tsx
 b/packages/merchant-backoffice-ui/src/paths/instance/token/stories.tsx
similarity index 89%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/qr/Qr.stories.tsx
copy to packages/merchant-backoffice-ui/src/paths/instance/token/stories.tsx
index eb853c8ff..5f0f56f2d 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/Qr.stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/token/stories.tsx
@@ -19,9 +19,10 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { QrPage as TestedComponent } from "./QrPage.js";
+import { DetailPage as TestedComponent } from "./DetailPage.js";
 
 export default {
-  title: "Pages/Templates/QR",
+  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/webhooks/create/Create.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/Create.stories.tsx
similarity index 96%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/Create.stories.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/validators/create/Create.stories.tsx
index 4857ede97..56762db7b 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/Create.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/Create.stories.tsx
@@ -23,6 +23,6 @@ import { h, VNode, FunctionalComponent } from "preact";
 import { CreatePage as TestedComponent } from "./CreatePage.js";
 
 export default {
-  title: "Pages/Webhooks/Create",
+  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/webhooks/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/index.tsx
similarity index 70%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/index.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/validators/create/index.tsx
index 924e6d9b8..648846793 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/index.tsx
@@ -27,29 +27,38 @@ import { MerchantBackend } from 
"../../../../declaration.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.Webhooks.WebhookAddDetails;
+export type Entity = MerchantBackend.OTP.OtpDeviceAddDetails;
 interface Props {
   onBack?: () => void;
   onConfirm: () => void;
 }
 
-export default function CreateWebhook({ onConfirm, onBack }: Props): VNode {
-  const { createWebhook } = useWebhookAPI();
+export default function CreateValidator({ onConfirm, onBack }: Props): VNode {
+  const { createOtpDevice } = useOtpDeviceAPI();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { i18n } = useTranslationContext();
+  const [created, setCreated] = 
useState<MerchantBackend.OTP.OtpDeviceAddDetails | null>(null)
+
+  if (created) {
+    return <CreatedSuccessfully entity={created} onConfirm={onConfirm} />
+  }
 
   return (
     <>
       <NotificationCard notification={notif} />
       <CreatePage
         onBack={onBack}
-        onCreate={(request: MerchantBackend.Webhooks.WebhookAddDetails) => {
-          return createWebhook(request)
-            .then(() => onConfirm())
+        onCreate={(request: Entity) => {
+          return createOtpDevice(request)
+            .then((d) => {
+              setCreated(request)
+            })
             .catch((error) => {
               setNotif({
-                message: i18n.str`could not inform template`,
+                message: i18n.str`could not create device`,
                 type: "ERROR",
                 description: error.message,
               });
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/List.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/List.stories.tsx
similarity index 96%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/list/List.stories.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/validators/list/List.stories.tsx
index 702e9ba4a..3aa491c53 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/List.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/List.stories.tsx
@@ -23,6 +23,6 @@ import { FunctionalComponent, h } from "preact";
 import { ListPage as TestedComponent } from "./ListPage.js";
 
 export default {
-  title: "Pages/Templates/List",
+  title: "Pages/Validators/List",
   component: TestedComponent,
 };
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/ListPage.tsx
similarity index 86%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/validators/list/ListPage.tsx
index 87e221e3c..4efee9781 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/ListPage.tsx
@@ -25,16 +25,16 @@ import { useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { CardTable } from "./Table.js";
 
 export interface Props {
-  webhooks: MerchantBackend.Webhooks.WebhookEntry[];
+  devices: MerchantBackend.OTP.OtpDeviceEntry[];
   onLoadMoreBefore?: () => void;
   onLoadMoreAfter?: () => void;
   onCreate: () => void;
-  onDelete: (e: MerchantBackend.Webhooks.WebhookEntry) => void;
-  onSelect: (e: MerchantBackend.Webhooks.WebhookEntry) => void;
+  onDelete: (e: MerchantBackend.OTP.OtpDeviceEntry) => void;
+  onSelect: (e: MerchantBackend.OTP.OtpDeviceEntry) => void;
 }
 
 export function ListPage({
-  webhooks,
+  devices,
   onCreate,
   onDelete,
   onSelect,
@@ -47,9 +47,9 @@ export function ListPage({
   return (
     <section class="section is-main-section">
       <CardTable
-        webhooks={webhooks.map((o) => ({
+        devices={devices.map((o) => ({
           ...o,
-          id: String(o.webhook_id),
+          id: String(o.otp_device_id),
         }))}
         onCreate={onCreate}
         onDelete={onDelete}
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/webhooks/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/index.tsx
similarity index 81%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/validators/list/index.tsx
index a6f6f1511..8837c848b 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/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 {
-  useInstanceWebhooks,
-  useWebhookAPI,
-} from "../../../../hooks/webhooks.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;
@@ -45,7 +42,7 @@ interface Props {
   onSelect: (id: string) => void;
 }
 
-export default function ListWebhooks({
+export default function ListValidators({
   onUnauthorized,
   onLoadError,
   onCreate,
@@ -55,8 +52,8 @@ export default function ListWebhooks({
   const [position, setPosition] = useState<string | undefined>(undefined);
   const { i18n } = useTranslationContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const { deleteWebhook } = useWebhookAPI();
-  const result = useInstanceWebhooks({ position }, (id) => setPosition(id));
+  const { deleteOtpDevice } = useOtpDeviceAPI();
+  const result = useInstanceOtpDevices({ position }, (id) => setPosition(id));
 
   if (result.loading) return <Loading />;
   if (!result.ok) {
@@ -78,26 +75,26 @@ export default function ListWebhooks({
       <NotificationCard notification={notif} />
 
       <ListPage
-        webhooks={result.data.webhooks}
+        devices={result.data.otp_devices}
         onLoadMoreBefore={
           result.isReachingStart ? result.loadMorePrev : undefined
         }
         onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
         onCreate={onCreate}
         onSelect={(e) => {
-          onSelect(e.webhook_id);
+          onSelect(e.otp_device_id);
         }}
-        onDelete={(e: MerchantBackend.Webhooks.WebhookEntry) =>
-          deleteWebhook(e.webhook_id)
+        onDelete={(e: MerchantBackend.OTP.OtpDeviceEntry) =>
+          deleteOtpDevice(e.otp_device_id)
             .then(() =>
               setNotif({
-                message: i18n.str`webhook delete successfully`,
+                message: i18n.str`validator delete successfully`,
                 type: "SUCCESS",
               }),
             )
             .catch((error) =>
               setNotif({
-                message: i18n.str`could not delete the webhook`,
+                message: i18n.str`could not delete the validator`,
                 type: "ERROR",
                 description: error.message,
               }),
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/Update.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/Update.stories.tsx
similarity index 96%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/update/Update.stories.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/validators/update/Update.stories.tsx
index 8d07cb31f..fcb77b820 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/Update.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/Update.stories.tsx
@@ -23,7 +23,7 @@ import { h, VNode, FunctionalComponent } from "preact";
 import { UpdatePage as TestedComponent } from "./UpdatePage.js";
 
 export default {
-  title: "Pages/Templates/Update",
+  title: "Pages/Validators/Update",
   component: TestedComponent,
   argTypes: {
     onUpdate: { action: "onUpdate" },
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/webhooks/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx
similarity index 81%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx
index 3f723ed87..9a27ccfee 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx
@@ -29,15 +29,12 @@ import { useState } from "preact/hooks";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { MerchantBackend, WithId } from "../../../../declaration.js";
-import {
-  useWebhookAPI,
-  useWebhookDetails,
-} from "../../../../hooks/webhooks.js";
 import { Notification } from "../../../../utils/types.js";
 import { UpdatePage } from "./UpdatePage.js";
 import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { useOtpDeviceAPI, useOtpDeviceDetails } from 
"../../../../hooks/otp.js";
 
-export type Entity = MerchantBackend.Webhooks.WebhookPatchDetails & WithId;
+export type Entity = MerchantBackend.OTP.OtpDevicePatchDetails & WithId;
 
 interface Props {
   onBack?: () => void;
@@ -45,18 +42,18 @@ interface Props {
   onUnauthorized: () => VNode;
   onNotFound: () => VNode;
   onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
-  tid: string;
+  vid: string;
 }
-export default function UpdateWebhook({
-  tid,
+export default function UpdateValidator({
+  vid,
   onConfirm,
   onBack,
   onUnauthorized,
   onNotFound,
   onLoadError,
 }: Props): VNode {
-  const { updateWebhook } = useWebhookAPI();
-  const result = useWebhookDetails(tid);
+  const { updateOtpDevice } = useOtpDeviceAPI();
+  const result = useOtpDeviceDetails(vid);
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
 
   const { i18n } = useTranslationContext();
@@ -80,10 +77,16 @@ export default function UpdateWebhook({
     <Fragment>
       <NotificationCard notification={notif} />
       <UpdatePage
-        webhook={{ ...result.data, id: tid }}
+        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 updateWebhook(tid, data)
+          return updateOtpDevice(vid, data)
             .then(onConfirm)
             .catch((error) => {
               setNotif({
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-util/src/bitcoin.ts 
b/packages/taler-util/src/bitcoin.ts
index 8c22ba522..37b7ae6b9 100644
--- a/packages/taler-util/src/bitcoin.ts
+++ b/packages/taler-util/src/bitcoin.ts
@@ -69,10 +69,10 @@ export function generateFakeSegwitAddress(
     addr[0] === "t" && addr[1] == "b"
       ? "tb"
       : addr[0] === "b" && addr[1] == "c" && addr[2] === "r" && addr[3] == "t"
-      ? "bcrt"
-      : addr[0] === "b" && addr[1] == "c"
-      ? "bc"
-      : undefined;
+        ? "bcrt"
+        : addr[0] === "b" && addr[1] == "c"
+          ? "bc"
+          : undefined;
   if (prefix === undefined) throw new Error("unknown bitcoin net");
 
   const addr1 = segwit.default.encode(prefix, 0, first_part);
diff --git a/packages/taler-util/src/payto.ts b/packages/taler-util/src/payto.ts
index 2b0af4cc2..60c4ba838 100644
--- a/packages/taler-util/src/payto.ts
+++ b/packages/taler-util/src/payto.ts
@@ -24,7 +24,7 @@ export type PaytoUri =
   | PaytoUriBitcoin;
 
 export interface PaytoUriGeneric {
-  targetType: string;
+  targetType: PaytoType | string;
   targetPath: string;
   params: { [name: string]: string };
 }
@@ -55,6 +55,8 @@ export interface PaytoUriBitcoin extends PaytoUriGeneric {
 
 const paytoPfx = "payto://";
 
+export type PaytoType = "iban" | "bitcoin" | "x-taler-bank"
+
 export function buildPayto(
   type: "iban",
   iban: string,
@@ -71,7 +73,7 @@ export function buildPayto(
   account: string,
 ): PaytoUriTalerBank;
 export function buildPayto(
-  type: "iban" | "bitcoin" | "x-taler-bank",
+  type: PaytoType,
   first: string,
   second?: string,
 ): PaytoUriGeneric {
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 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5b733353b..9a389bf50 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,4 +1,4 @@
-lockfileVersion: '6.0'
+lockfileVersion: '6.1'
 
 settings:
   autoInstallPeers: true
@@ -452,8 +452,8 @@ importers:
         specifier: 1.4.4
         version: 1.4.4
       swr:
-        specifier: 1.3.0
-        version: 1.3.0(react@18.2.0)
+        specifier: 2.2.2
+        version: 2.2.2(react@18.2.0)
       yup:
         specifier: ^0.32.9
         version: 0.32.11
@@ -16350,22 +16350,24 @@ packages:
       stable: 0.1.8
     dev: true
 
-  /swr@1.3.0(react@18.2.0):
-    resolution: {integrity: 
sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==}
+  /swr@2.0.3(react@18.2.0):
+    resolution: {integrity: 
sha512-sGvQDok/AHEWTPfhUWXEHBVEXmgGnuahyhmRQbjl9XBYxT/MSlAzvXEKQpyM++bMPaI52vcWS2HiKNaW7+9OFw==}
+    engines: {pnpm: '7'}
     peerDependencies:
       react: ^16.11.0 || ^17.0.0 || ^18.0.0
     dependencies:
       react: 18.2.0
-    dev: false
+      use-sync-external-store: 1.2.0(react@18.2.0)
 
-  /swr@2.0.3(react@18.2.0):
-    resolution: {integrity: 
sha512-sGvQDok/AHEWTPfhUWXEHBVEXmgGnuahyhmRQbjl9XBYxT/MSlAzvXEKQpyM++bMPaI52vcWS2HiKNaW7+9OFw==}
-    engines: {pnpm: '7'}
+  /swr@2.2.2(react@18.2.0):
+    resolution: {integrity: 
sha512-CbR41AoMD4TQBQw9ic3GTXspgfM9Y8Mdhb5Ob4uIKXhWqnRLItwA5fpGvB7SmSw3+zEjb0PdhiEumtUvYoQ+bQ==}
     peerDependencies:
       react: ^16.11.0 || ^17.0.0 || ^18.0.0
     dependencies:
+      client-only: 0.0.1
       react: 18.2.0
       use-sync-external-store: 1.2.0(react@18.2.0)
+    dev: false
 
   /symbol-tree@3.2.4:
     resolution: {integrity: 
sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}

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