gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (863e6db05 -> 867d2ca76)


From: gnunet
Subject: [taler-wallet-core] branch master updated (863e6db05 -> 867d2ca76)
Date: Fri, 10 Mar 2023 05:27:41 +0100

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

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

    from 863e6db05 update translations
     new 034f2c6dc dev mode: check exchange cache
     new 0fb6c098d support http and https
     new 2291d460e add payTemplate, fix missing ampersand
     new 8ddc551cc remove webui from login url, ad qr for template, fix navbar 
size,
     new f40487806 fix: summary empty string also means that summary is required
     new 867d2ca76 fix encoded uri, add pay template cta

The 6 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/dev.mjs            |   1 +
 .../merchant-backoffice-ui/src/Application.tsx     |  22 +--
 .../merchant-backoffice-ui/src/InstanceRoutes.tsx  |  15 +++
 .../src/components/exception/login.tsx             |  12 +-
 .../src/components/menu/NavigationBar.tsx          |   2 +-
 .../src/paths/admin/list/View.tsx                  |  96 +++++++------
 .../paths/instance/templates/create/CreatePage.tsx |   8 +-
 .../src/paths/instance/templates/list/ListPage.tsx |   3 +
 .../src/paths/instance/templates/list/Table.tsx    |  12 ++
 .../src/paths/instance/templates/list/index.tsx    |   5 +
 .../{use/Use.stories.tsx => qr/Qr.stories.tsx}     |   4 +-
 .../templates/{use/UsePage.tsx => qr/QrPage.tsx}   |  75 ++++++-----
 .../paths/instance/templates/{use => qr}/index.tsx |  24 +---
 .../paths/instance/templates/update/UpdatePage.tsx |   8 +-
 packages/taler-util/src/taleruri.ts                |  12 +-
 packages/taler-wallet-core/src/wallet.ts           |   4 +-
 .../src/NavigationBar.tsx                          |   2 +-
 .../src/cta/Payment/index.ts                       |   2 +-
 .../src/cta/Payment/test.ts                        |   2 +-
 .../src/cta/PaymentTemplate/index.ts               |  37 ++++-
 .../src/cta/PaymentTemplate/state.ts               | 128 ++++++++++++++++--
 .../src/cta/PaymentTemplate/stories.tsx            |   2 +-
 .../src/cta/PaymentTemplate/test.ts                |   6 +-
 .../src/cta/PaymentTemplate/views.tsx              |  57 +++++++-
 .../taler-wallet-webextension/src/mui/handlers.ts  |   5 +
 .../src/platform/chrome.ts                         |  36 ++++-
 .../src/popup/TalerActionFound.tsx                 | 149 +++++++++++----------
 .../src/wallet/Application.tsx                     |  33 +++--
 .../src/wallet/DeveloperPage.tsx                   |  37 +++++
 .../taler-wallet-webextension/src/wxBackend.ts     |  25 ++--
 packages/web-util/src/live-reload.ts               |   3 +-
 packages/web-util/src/serve.ts                     |   6 +-
 32 files changed, 573 insertions(+), 260 deletions(-)
 copy 
packages/merchant-backoffice-ui/src/paths/instance/templates/{use/Use.stories.tsx
 => qr/Qr.stories.tsx} (89%)
 copy 
packages/merchant-backoffice-ui/src/paths/instance/templates/{use/UsePage.tsx 
=> qr/QrPage.tsx} (64%)
 copy packages/merchant-backoffice-ui/src/paths/instance/templates/{use => 
qr}/index.tsx (75%)

diff --git a/packages/merchant-backoffice-ui/dev.mjs 
b/packages/merchant-backoffice-ui/dev.mjs
index 2f31d8e79..6cf7f0954 100755
--- a/packages/merchant-backoffice-ui/dev.mjs
+++ b/packages/merchant-backoffice-ui/dev.mjs
@@ -25,6 +25,7 @@ serve({
   folder: './dist',
   port: 8080,
   source: './src',
+  insecure: true,
   development: true,
   onUpdate: async () => esbuild.build(buildConfig)
 })
diff --git a/packages/merchant-backoffice-ui/src/Application.tsx 
b/packages/merchant-backoffice-ui/src/Application.tsx
index 84536396d..ed1fc69f3 100644
--- a/packages/merchant-backoffice-ui/src/Application.tsx
+++ b/packages/merchant-backoffice-ui/src/Application.tsx
@@ -23,7 +23,7 @@ import {
   TranslationProvider,
   useTranslationContext,
 } from "@gnu-taler/web-util/lib/index.browser";
-import { h, VNode } from "preact";
+import { Fragment, h, VNode } from "preact";
 import { route } from "preact-router";
 import { useMemo } from "preact/hooks";
 import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes.js";
@@ -70,24 +70,24 @@ function ApplicationStatusRoutes(): VNode {
 
   if (!triedToLog) {
     return (
-      <div id="app">
+      <Fragment>
         <NotYetReadyAppMenu title="Welcome!" />
         <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
-      </div>
+      </Fragment>
     );
   }
 
   if (result.clientError && result.isUnauthorized)
     return (
-      <div id="app">
+      <Fragment>
         <NotYetReadyAppMenu title="Login" />
         <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
-      </div>
+      </Fragment>
     );
 
   if (result.clientError && result.isNotfound)
     return (
-      <div id="app">
+      <Fragment>
         <NotYetReadyAppMenu title="Error" />
         <NotificationCard
           notification={{
@@ -97,12 +97,12 @@ function ApplicationStatusRoutes(): VNode {
           }}
         />
         <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
-      </div>
+      </Fragment>
     );
 
   if (result.serverError)
     return (
-      <div id="app">
+      <Fragment>
         <NotYetReadyAppMenu title="Error" />
         <NotificationCard
           notification={{
@@ -112,14 +112,14 @@ function ApplicationStatusRoutes(): VNode {
           }}
         />
         <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
-      </div>
+      </Fragment>
     );
 
   if (result.loading) return <Loading />;
 
   if (!result.ok)
     return (
-      <div id="app">
+      <Fragment>
         <NotYetReadyAppMenu title="Error" />
         <NotificationCard
           notification={{
@@ -129,7 +129,7 @@ function ApplicationStatusRoutes(): VNode {
           }}
         />
         <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
-      </div>
+      </Fragment>
     );
 
   return (
diff --git a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx 
b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
index c8f22f583..b911483a7 100644
--- a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
+++ b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
@@ -52,6 +52,7 @@ import ReservesDetailsPage from 
"./paths/instance/reserves/details/index.js";
 import ReservesListPage from "./paths/instance/reserves/list/index.js";
 import TemplateCreatePage from "./paths/instance/templates/create/index.js";
 import TemplateUsePage from "./paths/instance/templates/use/index.js";
+import TemplateQrPage from "./paths/instance/templates/qr/index.js";
 import TemplateListPage from "./paths/instance/templates/list/index.js";
 import TemplateUpdatePage from "./paths/instance/templates/update/index.js";
 import WebhookCreatePage from "./paths/instance/webhooks/create/index.js";
@@ -94,6 +95,7 @@ export enum InstancePaths {
   templates_update = "/templates/:tid/update",
   templates_new = "/templates/new",
   templates_use = "/templates/:tid/use",
+  templates_qr = "/templates/:tid/qr",
 
   webhooks_list = "/webhooks",
   webhooks_update = "/webhooks/:tid/update",
@@ -465,6 +467,9 @@ export function InstanceRoutes({
           onNewOrder={(id: string) => {
             route(InstancePaths.templates_use.replace(":tid", id));
           }}
+          onQR={(id: string) => {
+            route(InstancePaths.templates_qr.replace(":tid", id));
+          }}
           onSelect={(id: string) => {
             route(InstancePaths.templates_update.replace(":tid", id));
           }}
@@ -505,6 +510,16 @@ export function InstanceRoutes({
             route(InstancePaths.templates_list);
           }}
         />
+        <Route
+          path={InstancePaths.templates_qr}
+          component={TemplateQrPage}
+          onUnauthorized={LoginPageAccessDenied}
+          onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)}
+          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          onBack={() => {
+            route(InstancePaths.templates_list);
+          }}
+        />
 
         {/**
          * reserves pages
diff --git a/packages/merchant-backoffice-ui/src/components/exception/login.tsx 
b/packages/merchant-backoffice-ui/src/components/exception/login.tsx
index 552e76ed6..9a0411642 100644
--- a/packages/merchant-backoffice-ui/src/components/exception/login.tsx
+++ b/packages/merchant-backoffice-ui/src/components/exception/login.tsx
@@ -42,6 +42,14 @@ function normalizeToken(r: string | undefined): string | 
undefined {
   return r ? `secret-token:${encodeURIComponent(r)}` : undefined;
 }
 
+function cleanUp(s: string): string {
+  let result = s;
+  if (result.indexOf("webui/") !== -1) {
+    result = result.substring(0, result.indexOf("webui/"));
+  }
+  return result;
+}
+
 export function LoginModal({ onConfirm, withMessage }: Props): VNode {
   const { url: backendUrl, token: baseToken } = useBackendContext();
   const { admin, token: instanceToken } = useInstanceContext();
@@ -50,11 +58,11 @@ export function LoginModal({ onConfirm, withMessage }: 
Props): VNode {
   );
   const [token, setToken] = useState(currentToken);
 
-  const [url, setURL] = useState(backendUrl);
+  const [url, setURL] = useState(cleanUp(backendUrl));
   const { i18n } = useTranslationContext();
 
   return (
-    <div class="columns is-centered">
+    <div class="columns is-centered" style={{ margin: "auto" }}>
       <div class="column is-two-thirds ">
         <div class="modal-card" style={{ width: "100%", margin: 0 }}>
           <header
diff --git 
a/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx 
b/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx
index 8bd85d0ef..9624a2c38 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx
@@ -61,7 +61,7 @@ export function NavigationBar({ onMobileMenu, title }: 
Props): VNode {
           class="navbar-start is-justify-content-center is-flex-grow-1"
           href="https://taler.net";
         >
-          <img src={logo} style={{ height: 50, margin: 10 }} />
+          <img src={logo} style={{ height: 35, margin: 10 }} />
         </a>
         <div class="navbar-end">
           <div class="navbar-item" style={{ paddingTop: 4, paddingBottom: 4 }}>
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx 
b/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx
index 7376a88cb..fd1b8dc05 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx
@@ -57,56 +57,54 @@ export function View({
     : instances;
 
   return (
-    <div id="app">
-      <section class="section is-main-section">
-        <div class="columns">
-          <div class="column is-two-thirds">
-            <div class="tabs" style={{ overflow: "inherit" }}>
-              <ul>
-                <li class={showIsActive}>
-                  <div
-                    class="has-tooltip-right"
-                    data-tooltip={i18n.str`Only show active instances`}
-                  >
-                    <a onClick={() => setShow("active")}>
-                      <i18n.Translate>Active</i18n.Translate>
-                    </a>
-                  </div>
-                </li>
-                <li class={showIsDeleted}>
-                  <div
-                    class="has-tooltip-right"
-                    data-tooltip={i18n.str`Only show deleted instances`}
-                  >
-                    <a onClick={() => setShow("deleted")}>
-                      <i18n.Translate>Deleted</i18n.Translate>
-                    </a>
-                  </div>
-                </li>
-                <li class={showAll}>
-                  <div
-                    class="has-tooltip-right"
-                    data-tooltip={i18n.str`Show all instances`}
-                  >
-                    <a onClick={() => setShow(null)}>
-                      <i18n.Translate>All</i18n.Translate>
-                    </a>
-                  </div>
-                </li>
-              </ul>
-            </div>
+    <section class="section is-main-section">
+      <div class="columns">
+        <div class="column is-two-thirds">
+          <div class="tabs" style={{ overflow: "inherit" }}>
+            <ul>
+              <li class={showIsActive}>
+                <div
+                  class="has-tooltip-right"
+                  data-tooltip={i18n.str`Only show active instances`}
+                >
+                  <a onClick={() => setShow("active")}>
+                    <i18n.Translate>Active</i18n.Translate>
+                  </a>
+                </div>
+              </li>
+              <li class={showIsDeleted}>
+                <div
+                  class="has-tooltip-right"
+                  data-tooltip={i18n.str`Only show deleted instances`}
+                >
+                  <a onClick={() => setShow("deleted")}>
+                    <i18n.Translate>Deleted</i18n.Translate>
+                  </a>
+                </div>
+              </li>
+              <li class={showAll}>
+                <div
+                  class="has-tooltip-right"
+                  data-tooltip={i18n.str`Show all instances`}
+                >
+                  <a onClick={() => setShow(null)}>
+                    <i18n.Translate>All</i18n.Translate>
+                  </a>
+                </div>
+              </li>
+            </ul>
           </div>
         </div>
-        <CardTableActive
-          instances={showingInstances}
-          onDelete={onDelete}
-          onPurge={onPurge}
-          setInstanceName={setInstanceName}
-          onUpdate={onUpdate}
-          selected={selected}
-          onCreate={onCreate}
-        />
-      </section>
-    </div>
+      </div>
+      <CardTableActive
+        instances={showingInstances}
+        onDelete={onDelete}
+        onPurge={onPurge}
+        setInstanceName={setInstanceName}
+        onUpdate={onUpdate}
+        selected={selected}
+        onCreate={onCreate}
+      />
+    </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 b23c52362..22f86002a 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
@@ -114,13 +114,13 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
               <Input
                 name="template_contract.summary"
                 inputType="multiline"
-                label={i18n.str`Order summary`}
-                tooltip={i18n.str`Title of the order to be shown to the 
customer`}
+                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`Order price`}
-                tooltip={i18n.str`Order price`}
+                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"
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
index 8482f7f52..708720818 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
@@ -32,6 +32,7 @@ export interface Props {
   onDelete: (e: MerchantBackend.Template.TemplateEntry) => void;
   onSelect: (e: MerchantBackend.Template.TemplateEntry) => void;
   onNewOrder: (e: MerchantBackend.Template.TemplateEntry) => void;
+  onQR: (e: MerchantBackend.Template.TemplateEntry) => void;
 }
 
 export function ListPage({
@@ -40,6 +41,7 @@ export function ListPage({
   onDelete,
   onSelect,
   onNewOrder,
+  onQR,
   onLoadMoreBefore,
   onLoadMoreAfter,
 }: Props): VNode {
@@ -53,6 +55,7 @@ export function ListPage({
           ...o,
           id: String(o.template_id),
         }))}
+        onQR={onQR}
         onCreate={onCreate}
         onDelete={onDelete}
         onSelect={onSelect}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx
index 57d328d39..700c332d7 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx
@@ -31,6 +31,7 @@ interface Props {
   onDelete: (e: Entity) => void;
   onSelect: (e: Entity) => void;
   onNewOrder: (e: Entity) => void;
+  onQR: (e: Entity) => void;
   onCreate: () => void;
   onLoadMoreBefore?: () => void;
   hasMoreBefore?: boolean;
@@ -43,6 +44,7 @@ export function CardTable({
   onCreate,
   onDelete,
   onSelect,
+  onQR,
   onNewOrder,
   onLoadMoreAfter,
   onLoadMoreBefore,
@@ -84,6 +86,7 @@ export function CardTable({
                 onDelete={onDelete}
                 onSelect={onSelect}
                 onNewOrder={onNewOrder}
+                onQR={onQR}
                 rowSelection={rowSelection}
                 rowSelectionHandler={rowSelectionHandler}
                 onLoadMoreAfter={onLoadMoreAfter}
@@ -105,6 +108,7 @@ interface TableProps {
   instances: Entity[];
   onDelete: (e: Entity) => void;
   onNewOrder: (e: Entity) => void;
+  onQR: (e: Entity) => void;
   onSelect: (e: Entity) => void;
   rowSelectionHandler: StateUpdater<string[]>;
   onLoadMoreBefore?: () => void;
@@ -123,6 +127,7 @@ function Table({
   onLoadMoreAfter,
   onDelete,
   onNewOrder,
+  onQR,
   onSelect,
   onLoadMoreBefore,
   hasMoreAfter,
@@ -185,6 +190,13 @@ function Table({
                     >
                       New order
                     </button>
+                    <button
+                      class="button is-info is-small has-tooltip-left"
+                      data-tooltip={i18n.str`create qr code for the template`}
+                      onClick={() => onQR(i)}
+                    >
+                      QR
+                    </button>
                   </div>
                 </td>
               </tr>
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 0b7c191bd..ea8f4e7e3 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
@@ -42,12 +42,14 @@ interface Props {
   onCreate: () => void;
   onSelect: (id: string) => void;
   onNewOrder: (id: string) => void;
+  onQR: (id: string) => void;
 }
 
 export default function ListTemplates({
   onUnauthorized,
   onLoadError,
   onCreate,
+  onQR,
   onSelect,
   onNewOrder,
   onNotFound,
@@ -80,6 +82,9 @@ export default function ListTemplates({
         onNewOrder={(e) => {
           onNewOrder(e.template_id);
         }}
+        onQR={(e) => {
+          onQR(e.template_id);
+        }}
         onDelete={(e: MerchantBackend.Template.TemplateEntry) =>
           deleteTemplate(e.template_id)
             .then(() =>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/Use.stories.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/Qr.stories.tsx
similarity index 89%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/use/Use.stories.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/templates/qr/Qr.stories.tsx
index 13576d94d..eb853c8ff 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/Use.stories.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/Qr.stories.tsx
@@ -19,9 +19,9 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { UsePage as TestedComponent } from "./UsePage.js";
+import { QrPage as TestedComponent } from "./QrPage.js";
 
 export default {
-  title: "Pages/Templates/Create",
+  title: "Pages/Templates/QR",
   component: TestedComponent,
 };
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
similarity index 64%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
index a63469763..756909d15 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
@@ -19,59 +19,68 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { buildPayto, classifyTalerUri } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
+import { QR } from "../../../../components/exception/QR.js";
 import {
   FormErrors,
   FormProvider,
 } from "../../../../components/form/FormProvider.js";
 import { Input } from "../../../../components/form/Input.js";
 import { InputCurrency } from "../../../../components/form/InputCurrency.js";
+import { useBackendContext } from "../../../../context/backend.js";
+import { useConfigContext } from "../../../../context/config.js";
 import { MerchantBackend } from "../../../../declaration.js";
 
 type Entity = MerchantBackend.Template.UsingTemplateDetails;
 
 interface Props {
   template: MerchantBackend.Template.TemplateDetails;
-  onCreateOrder: (d: Entity) => Promise<void>;
+  id: string;
   onBack?: () => void;
 }
 
-export function UsePage({ template, onCreateOrder, onBack }: Props): VNode {
+export function QrPage({ template, id: templateId, onBack }: Props): VNode {
   const { i18n } = useTranslationContext();
+  const { url: backendUrl } = useBackendContext();
+  const config = useConfigContext();
 
   const [state, setState] = useState<Partial<Entity>>({
     amount: template.template_contract.amount,
     summary: template.template_contract.summary,
   });
 
-  const errors: FormErrors<Entity> = {
-    amount:
-      !template.template_contract.amount && !state.amount
-        ? i18n.str`Amount is required`
-        : undefined,
-    summary:
-      !template.template_contract.summary && !state.summary
-        ? i18n.str`Order summary is required`
-        : undefined,
-  };
+  const errors: FormErrors<Entity> = {};
 
   const hasErrors = Object.keys(errors).some(
     (k) => (errors as any)[k] !== undefined,
   );
 
-  const submitForm = () => {
-    if (hasErrors) return Promise.reject();
-    if (template.template_contract.amount) {
-      delete state.amount;
-    }
-    if (template.template_contract.summary) {
-      delete state.summary;
+  const fixedAmount = !!template.template_contract.amount;
+  const fixedSummary = !!template.template_contract.summary;
+
+  const params = new URLSearchParams();
+  if (!fixedAmount) {
+    if (state.amount) {
+      params.append("amount", state.amount);
+    } else {
+      params.append("amount", config.currency);
     }
-    return onCreateOrder(state as any);
-  };
+  }
+  if (!fixedSummary) {
+    params.append("summary", state.summary ?? "");
+  }
+
+  const paramsStr = fixedAmount && fixedSummary ? "" : "?" + params.toString();
+  const merchantURL = new URL(backendUrl);
+
+  const talerProto =
+    merchantURL.protocol === "http:" ? "taler+http:" : "taler:";
+
+  const payTemplateUri = 
`${talerProto}//pay-template/${merchantURL.hostname}/${templateId}${paramsStr}`;
 
   return (
     <div>
@@ -87,14 +96,14 @@ export function UsePage({ template, onCreateOrder, onBack 
}: Props): VNode {
               <InputCurrency<Entity>
                 name="amount"
                 label={i18n.str`Amount`}
-                readonly={!!template.template_contract.amount}
+                readonly={fixedAmount}
                 tooltip={i18n.str`Amount of the order`}
               />
               <Input<Entity>
                 name="summary"
                 inputType="multiline"
+                readonly={fixedSummary}
                 label={i18n.str`Order summary`}
-                readonly={!!template.template_contract.summary}
                 tooltip={i18n.str`Title of the order to be shown to the 
customer`}
               />
             </FormProvider>
@@ -105,22 +114,20 @@ export function UsePage({ template, onCreateOrder, onBack 
}: Props): VNode {
                   <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>
+              <button class="button is-info" onClick={onBack}>
+                <i18n.Translate>Print</i18n.Translate>
+              </button>
             </div>
           </div>
           <div class="column" />
         </div>
       </section>
+      <section>
+        <pre>
+          <a href={payTemplateUri}>{payTemplateUri}</a>
+        </pre>
+        <QR text={payTemplateUri} />
+      </section>
     </div>
   );
 }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
similarity index 75%
copy from 
packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx
copy to 
packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
index d5fa6d39d..97d25b700 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
@@ -33,21 +33,19 @@ import {
   useTemplateDetails,
 } from "../../../../hooks/templates.js";
 import { Notification } from "../../../../utils/types.js";
-import { UsePage } from "./UsePage.js";
+import { QrPage } from "./QrPage.js";
 
 export type Entity = MerchantBackend.Transfers.TransferInformation;
 interface Props {
   onBack?: () => void;
-  onOrderCreated: (id: string) => void;
   onUnauthorized: () => VNode;
   onNotFound: () => VNode;
   onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
   tid: string;
 }
 
-export default function TemplateUsePage({
+export default function TemplateQrPage({
   tid,
-  onOrderCreated,
   onBack,
   onLoadError,
   onNotFound,
@@ -66,23 +64,7 @@ export default function TemplateUsePage({
   return (
     <>
       <NotificationCard notification={notif} />
-      <UsePage
-        template={result.data}
-        onBack={onBack}
-        onCreateOrder={(
-          request: MerchantBackend.Template.UsingTemplateDetails,
-        ) => {
-          return createOrderFromTemplate(tid, request)
-            .then((res) => onOrderCreated(res.data.order_id))
-            .catch((error) => {
-              setNotif({
-                message: i18n.str`could not create order from template`,
-                type: "ERROR",
-                description: error.message,
-              });
-            });
-        }}
-      />
+      <QrPage template={result.data} 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 5bd9bd38d..eba212517 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
@@ -123,13 +123,13 @@ export function UpdatePage({ template, onUpdate, onBack 
}: Props): VNode {
                 <Input
                   name="template_contract.summary"
                   inputType="multiline"
-                  label={i18n.str`Order summary`}
-                  tooltip={i18n.str`Title of the order to be shown to the 
customer`}
+                  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`Order price`}
-                  tooltip={i18n.str`total product price added up`}
+                  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"
diff --git a/packages/taler-util/src/taleruri.ts 
b/packages/taler-util/src/taleruri.ts
index 2aa9cb030..4d55d4c98 100644
--- a/packages/taler-util/src/taleruri.ts
+++ b/packages/taler-util/src/taleruri.ts
@@ -97,6 +97,7 @@ export function parseWithdrawUri(s: string): 
WithdrawUriResult | undefined {
 export enum TalerUriType {
   TalerPay = "taler-pay",
   TalerTemplate = "taler-template",
+  TalerPayTemplate = "taler-pay-template",
   TalerWithdraw = "taler-withdraw",
   TalerTip = "taler-tip",
   TalerRefund = "taler-refund",
@@ -129,10 +130,10 @@ export function classifyTalerUri(s: string): TalerUriType 
{
     return TalerUriType.TalerPay;
   }
   if (sl.startsWith("taler://pay-template/")) {
-    return TalerUriType.TalerPay;
+    return TalerUriType.TalerPayTemplate;
   }
   if (sl.startsWith("taler+http://pay-template/";)) {
-    return TalerUriType.TalerPay;
+    return TalerUriType.TalerPayTemplate;
   }
   if (sl.startsWith("taler://tip/")) {
     return TalerUriType.TalerTip;
@@ -277,13 +278,14 @@ export function constructPayUri(
     result += `:${url.port}`;
   }
   result += `${url.pathname}${orderId}/${sessionId}`;
-  let queryPart = "";
+  const qp = new URLSearchParams();
   if (claimToken) {
-    queryPart += `c=${claimToken}`;
+    qp.append("c", claimToken);
   }
   if (noncePriv) {
-    queryPart += `n=${noncePriv}`;
+    qp.append("n", noncePriv);
   }
+  const queryPart = qp.toString();
   if (queryPart) {
     result += "?" + queryPart;
   }
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index 8614fd7e3..6197f000e 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -1183,14 +1183,14 @@ async function dispatchRequestInternal<Op extends 
WalletApiOperation>(
         throw Error("invalid taler-template URI");
       }
       if (
-        url.templateParams.amount &&
+        url.templateParams.amount !== undefined &&
         typeof url.templateParams.amount === "string"
       ) {
         templateDetails.amount =
           req.templateParams.amount ?? url.templateParams.amount;
       }
       if (
-        url.templateParams.summary &&
+        url.templateParams.summary !== undefined &&
         typeof url.templateParams.summary === "string"
       ) {
         templateDetails.summary =
diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx 
b/packages/taler-wallet-webextension/src/NavigationBar.tsx
index fb6f280c3..e8ee4f475 100644
--- a/packages/taler-wallet-webextension/src/NavigationBar.tsx
+++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx
@@ -117,7 +117,7 @@ export const Pages = {
 
   cta: pageDefinition<{ action: string }>("/cta/:action"),
   ctaPay: "/cta/pay",
-  ctaPayTemplate: "/cta/payTemplate",
+  ctaPayTemplate: "/cta/pay/template",
   ctaRecovery: "/cta/recovery",
   ctaRefund: "/cta/refund",
   ctaTips: "/cta/tip",
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/index.ts 
b/packages/taler-wallet-webextension/src/cta/Payment/index.ts
index e844c1706..c9bead89c 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/index.ts
@@ -30,7 +30,7 @@ import { useComponentState } from "./state.js";
 import { BaseView } from "./views.js";
 
 export interface Props {
-  talerPayUri?: string;
+  talerPayUri: string;
   goToWalletManualWithdraw: (amount?: string) => Promise<void>;
   cancel: () => Promise<void>;
   onSuccess: (tx: string) => Promise<void>;
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/test.ts 
b/packages/taler-wallet-webextension/src/cta/Payment/test.ts
index e92eb78c0..f4b63955d 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/test.ts
@@ -41,7 +41,7 @@ describe("Payment CTA states", () => {
   it("should tell the user that the URI is missing", async () => {
     const { handler, TestingContext } = createWalletApiMock();
     const props = {
-      talerPayUri: undefined,
+      talerPayUri: "",
       cancel: nullFunction,
       goToWalletManualWithdraw: nullFunction,
       onSuccess: nullFunction,
diff --git 
a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/index.ts 
b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/index.ts
index 2cdc8d2e1..f5a8c8814 100644
--- a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/index.ts
@@ -20,12 +20,25 @@ import { ErrorAlert } from "../../context/alert.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
 import { ReadyView } from "./views.js";
+import { PaymentPage } from "../Payment/index.js";
+import {
+  AmountFieldHandler,
+  ButtonHandler,
+  TextFieldHandler,
+} from "../../mui/handlers.js";
 
 export interface Props {
-  talerTemplateUri?: string;
+  talerTemplateUri: string;
+  goToWalletManualWithdraw: (amount?: string) => Promise<void>;
+  cancel: () => Promise<void>;
+  onSuccess: (tx: string) => Promise<void>;
 }
 
-export type State = State.Loading | State.LoadingUriError | State.Ready;
+export type State =
+  | State.Loading
+  | State.LoadingUriError
+  | State.OrderReady
+  | State.FillTemplate;
 
 export namespace State {
   export interface Loading {
@@ -37,16 +50,30 @@ export namespace State {
     error: ErrorAlert;
   }
 
-  export interface Ready {
-    status: "ready";
+  export interface FillTemplate {
+    status: "fill-template";
     error: undefined;
+    currency: string;
+    amount?: AmountFieldHandler;
+    summary?: TextFieldHandler;
+    onCreate: ButtonHandler;
+  }
+
+  export interface OrderReady {
+    status: "order-ready";
+    error: undefined;
+    talerPayUri: string;
+    onSuccess: (tx: string) => Promise<void>;
+    cancel: () => Promise<void>;
+    goToWalletManualWithdraw: () => Promise<void>;
   }
 }
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
   error: ErrorAlertView,
-  ready: ReadyView,
+  "fill-template": ReadyView,
+  "order-ready": PaymentPage,
 };
 
 export const PaymentTemplatePage = compose(
diff --git 
a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts 
b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts
index f5e6dee61..abcf040b7 100644
--- a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts
@@ -14,27 +14,56 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { Amounts } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { alertFromError } from "../../context/alert.js";
+import { useState } from "preact/hooks";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
+import { AmountFieldHandler, TextFieldHandler } from "../../mui/handlers.js";
 import { Props, State } from "./index.js";
 
-export function useComponentState({ talerTemplateUri }: Props): State {
-  // const { pushAlertOnError } = useAlertContext();
+export function useComponentState({
+  talerTemplateUri,
+  cancel,
+  goToWalletManualWithdraw,
+  onSuccess,
+}: Props): State {
   const api = useBackendContext();
   const { i18n } = useTranslationContext();
+  const { safely } = useAlertContext();
+
+  const url = talerTemplateUri ? new URL(talerTemplateUri) : undefined;
+
+  const amountParam = !url
+    ? undefined
+    : url.searchParams.get("amount") ?? undefined;
+  const summaryParam = !url
+    ? undefined
+    : url.searchParams.get("summary") ?? undefined;
+
+  const parsedAmount = !amountParam ? undefined : Amounts.parse(amountParam);
+  const currency = parsedAmount ? parsedAmount.currency : amountParam;
+
+  const initialAmount =
+    parsedAmount ?? (currency ? Amounts.zeroOfCurrency(currency) : undefined);
+  const [amount, setAmount] = useState(initialAmount);
+  const [summary, setSummary] = useState(summaryParam);
+  const [newOrder, setNewOrder] = useState("");
 
   const hook = useAsyncAsHook(async () => {
     if (!talerTemplateUri) throw Error("ERROR_NO-URI-FOR-PAYMENT-TEMPLATE");
-    const payStatus = await api.wallet.call(
-      WalletApiOperation.PreparePayForTemplate,
-      {
-        talerPayTemplateUri: talerTemplateUri,
-        templateParams: {},
-      },
-    );
+    let payStatus;
+    if (!amountParam && !summaryParam) {
+      payStatus = await api.wallet.call(
+        WalletApiOperation.PreparePayForTemplate,
+        {
+          talerPayTemplateUri: talerTemplateUri,
+          templateParams: {},
+        },
+      );
+    }
     const balance = await api.wallet.call(WalletApiOperation.GetBalances, {});
     return { payStatus, balance, uri: talerTemplateUri };
   }, []);
@@ -56,8 +85,85 @@ export function useComponentState({ talerTemplateUri }: 
Props): State {
     };
   }
 
+  if (hook.response.payStatus) {
+    return {
+      status: "order-ready",
+      error: undefined,
+      cancel,
+      goToWalletManualWithdraw,
+      onSuccess,
+      talerPayUri: hook.response.payStatus.talerUri!,
+    };
+  }
+
+  if (newOrder) {
+    return {
+      status: "order-ready",
+      error: undefined,
+      cancel,
+      goToWalletManualWithdraw,
+      onSuccess,
+      talerPayUri: newOrder,
+    };
+  }
+
+  async function createOrder() {
+    try {
+      const templateParams: Record<string, string> = {};
+      if (amount) {
+        templateParams["amount"] = Amounts.stringify(amount);
+      }
+      if (summary) {
+        templateParams["summary"] = summary;
+      }
+      const payStatus = await api.wallet.call(
+        WalletApiOperation.PreparePayForTemplate,
+        {
+          talerPayTemplateUri: talerTemplateUri,
+          templateParams,
+        },
+      );
+      setNewOrder(payStatus.talerUri!);
+    } catch (e) {}
+  }
+  const errors = undefinedIfEmpty({
+    amount: amount && Amounts.isZero(amount) ? i18n.str`required` : undefined,
+    summary: !summary ? i18n.str`required` : undefined,
+  });
   return {
-    status: "ready",
+    status: "fill-template",
     error: undefined,
+    currency: currency!, //currency is always not null
+    amount:
+      amount !== undefined
+        ? ({
+            onInput: (a) => {
+              setAmount(a);
+            },
+            value: amount,
+            error: errors?.amount,
+          } as AmountFieldHandler)
+        : undefined,
+    summary:
+      summary !== undefined
+        ? ({
+            onInput: (t) => {
+              setSummary(t);
+            },
+            value: summary,
+            error: errors?.summary,
+          } as TextFieldHandler)
+        : undefined,
+    onCreate: {
+      onClick: errors
+        ? undefined
+        : safely(createOrder, i18n.str`Could not create order`),
+    },
   };
 }
+
+function undefinedIfEmpty<T extends object>(obj: T): T | undefined {
+  return Object.keys(obj).some((k) => (obj as any)[k] !== undefined)
+    ? obj
+    : undefined;
+}
diff --git 
a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/stories.tsx 
b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/stories.tsx
index 32a080959..93421eaa3 100644
--- a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/stories.tsx
@@ -29,6 +29,6 @@ export default {
 };
 
 export const PaymentPossible = tests.createExample(ReadyView, {
-  status: "ready",
+  status: "fill-template",
   error: undefined,
 });
diff --git a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/test.ts 
b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/test.ts
index d4c65e008..72fbb6853 100644
--- a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/test.ts
@@ -21,6 +21,7 @@
 
 import { expect } from "chai";
 import { tests } from "../../../../web-util/src/index.browser.js";
+import { nullFunction } from "../../mui/handlers.js";
 import { createWalletApiMock } from "../../test-utils.js";
 import { useComponentState } from "./state.js";
 
@@ -28,7 +29,10 @@ describe("Order template CTA states", () => {
   it("should tell the user that the URI is missing", async () => {
     const { handler, TestingContext } = createWalletApiMock();
     const props = {
-      talerTemplateUri: undefined,
+      talerTemplateUri: "",
+      cancel: nullFunction,
+      goToWalletManualWithdraw: nullFunction,
+      onSuccess: nullFunction,
     };
 
     const hookBehavior = await tests.hookBehaveLikeThis(
diff --git 
a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx 
b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx
index d3f893c7e..9f4c0f28c 100644
--- a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx
@@ -15,15 +15,64 @@
  */
 
 import { Fragment, h, VNode } from "preact";
+import { AmountField } from "../../components/AmountField.js";
+import { Part } from "../../components/Part.js";
 import { useTranslationContext } from "../../context/translation.js";
+import { Button } from "../../mui/Button.js";
+import { TextField } from "../../mui/TextField.js";
 import { State } from "./index.js";
 
-export function ReadyView({ status }: State.Ready): VNode {
+export function ReadyView({
+  currency,
+  amount,
+  summary,
+  onCreate,
+}: State.FillTemplate): VNode {
   const { i18n } = useTranslationContext();
 
+  console.log("is summary", !!summary);
   return (
-    <div>
-      <i18n.Translate>Not yet implemented</i18n.Translate>
-    </div>
+    <Fragment>
+      <section style={{ textAlign: "left" }}>
+        {/* <Part
+          title={
+            <div
+              style={{
+                display: "flex",
+                alignItems: "center",
+              }}
+            >
+              <i18n.Translate>Merchant</i18n.Translate>
+            </div>
+          }
+          text={<ExchangeDetails exchange={exchangeUrl} />}
+          kind="neutral"
+          big
+        /> */}
+        {!amount ? undefined : (
+          <p>
+            <AmountField label={i18n.str`Amount`} handler={amount} />
+          </p>
+        )}
+        {!summary ? undefined : (
+          <p>
+            <TextField
+              label="Summary"
+              variant="filled"
+              required
+              fullWidth
+              error={summary.error}
+              value={summary.value}
+              onChange={summary.onInput}
+            />
+          </p>
+        )}
+      </section>
+      <section>
+        <Button onClick={onCreate.onClick} variant="contained" color="success">
+          <i18n.Translate>Review order</i18n.Translate>
+        </Button>
+      </section>
+    </Fragment>
   );
 }
diff --git a/packages/taler-wallet-webextension/src/mui/handlers.ts 
b/packages/taler-wallet-webextension/src/mui/handlers.ts
index 61786742f..0bc00ca45 100644
--- a/packages/taler-wallet-webextension/src/mui/handlers.ts
+++ b/packages/taler-wallet-webextension/src/mui/handlers.ts
@@ -56,6 +56,11 @@ export const nullFunction = async function (): Promise<void> 
{
   //do nothing
 } as SafeHandler<void>;
 
+//FIXME: UI button should required SafeHandler but
+//useStateComponent should not be required to create SafeHandlers
+//so this need to be splitted in two:
+// * ButtonHandlerUI =>  with i18n
+// * ButtonHandlerLogic => without i18n
 export interface ButtonHandler {
   onClick?: SafeHandler<void>;
   // error?: TalerError;
diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts 
b/packages/taler-wallet-webextension/src/platform/chrome.ts
index beb65b2d0..4b0bdbfb7 100644
--- a/packages/taler-wallet-webextension/src/platform/chrome.ts
+++ b/packages/taler-wallet-webextension/src/platform/chrome.ts
@@ -241,41 +241,63 @@ function openWalletURIFromPopup(maybeTalerUri: string): 
void {
     : maybeTalerUri;
   const uriType = classifyTalerUri(talerUri);
 
+  encodeURIComponent;
   let url: string | undefined = undefined;
   switch (uriType) {
     case TalerUriType.TalerWithdraw:
       url = chrome.runtime.getURL(
-        `static/wallet.html#/cta/withdraw?talerWithdrawUri=${talerUri}`,
+        
`static/wallet.html#/cta/withdraw?talerWithdrawUri=${encodeURIComponent(
+          talerUri,
+        )}`,
       );
       break;
     case TalerUriType.TalerRecovery:
       url = chrome.runtime.getURL(
-        `static/wallet.html#/cta/recovery?talerRecoveryUri=${talerUri}`,
+        
`static/wallet.html#/cta/recovery?talerRecoveryUri=${encodeURIComponent(
+          talerUri,
+        )}`,
       );
       break;
     case TalerUriType.TalerPay:
       url = chrome.runtime.getURL(
-        `static/wallet.html#/cta/pay?talerPayUri=${talerUri}`,
+        `static/wallet.html#/cta/pay?talerPayUri=${encodeURIComponent(
+          talerUri,
+        )}`,
       );
       break;
     case TalerUriType.TalerTip:
       url = chrome.runtime.getURL(
-        `static/wallet.html#/cta/tip?talerTipUri=${talerUri}`,
+        `static/wallet.html#/cta/tip?talerTipUri=${encodeURIComponent(
+          talerUri,
+        )}`,
       );
       break;
     case TalerUriType.TalerRefund:
       url = chrome.runtime.getURL(
-        `static/wallet.html#/cta/refund?talerRefundUri=${talerUri}`,
+        `static/wallet.html#/cta/refund?talerRefundUri=${encodeURIComponent(
+          talerUri,
+        )}`,
       );
       break;
     case TalerUriType.TalerPayPull:
       url = chrome.runtime.getURL(
-        `static/wallet.html#/cta/invoice/pay?talerPayPullUri=${talerUri}`,
+        
`static/wallet.html#/cta/invoice/pay?talerPayPullUri=${encodeURIComponent(
+          talerUri,
+        )}`,
       );
       break;
     case TalerUriType.TalerPayPush:
       url = chrome.runtime.getURL(
-        `static/wallet.html#/cta/transfer/pickup?talerPayPushUri=${talerUri}`,
+        
`static/wallet.html#/cta/transfer/pickup?talerPayPushUri=${encodeURIComponent(
+          talerUri,
+        )}`,
+      );
+      break;
+    case TalerUriType.TalerPayTemplate:
+      url = chrome.runtime.getURL(
+        
`static/wallet.html#/cta/pay/template?talerPayTemplateUri=${encodeURIComponent(
+          talerUri,
+        )}`,
       );
       break;
     case TalerUriType.Unknown:
diff --git a/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx 
b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx
index 5c435a9a5..205e42d20 100644
--- a/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx
+++ b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx
@@ -31,6 +31,86 @@ export interface Props {
   onDismiss: () => Promise<void>;
 }
 
+function ContentByUriType({
+  type,
+  onConfirm,
+}: {
+  type: TalerUriType;
+  onConfirm: () => Promise<void>;
+}) {
+  const { i18n } = useTranslationContext();
+  switch (type) {
+    case TalerUriType.TalerWithdraw:
+      return (
+        <div>
+          <p>
+            <i18n.Translate>This page has a withdrawal action.</i18n.Translate>
+          </p>
+          <Button variant="contained" color="success" onClick={onConfirm}>
+            <i18n.Translate>Open withdraw page</i18n.Translate>
+          </Button>
+        </div>
+      );
+
+    case TalerUriType.TalerPayTemplate:
+    case TalerUriType.TalerPay:
+      return (
+        <div>
+          <p>
+            <i18n.Translate>This page has pay action.</i18n.Translate>
+          </p>
+          <Button variant="contained" color="success" onClick={onConfirm}>
+            <i18n.Translate>Open pay page</i18n.Translate>
+          </Button>
+        </div>
+      );
+    case TalerUriType.TalerTip:
+      return (
+        <div>
+          <p>
+            <i18n.Translate>This page has a tip action.</i18n.Translate>
+          </p>
+          <Button variant="contained" color="success" onClick={onConfirm}>
+            <i18n.Translate>Open tip page</i18n.Translate>
+          </Button>
+        </div>
+      );
+
+    case TalerUriType.TalerRefund:
+      return (
+        <div>
+          <p>
+            <i18n.Translate>This page has a refund action.</i18n.Translate>
+          </p>
+          <Button variant="contained" color="success" onClick={onConfirm}>
+            <i18n.Translate>Open refund page</i18n.Translate>
+          </Button>
+        </div>
+      );
+
+    case TalerUriType.TalerDevExperiment:
+    case TalerUriType.TalerTemplate:
+    case TalerUriType.TalerPayPull:
+    case TalerUriType.TalerPayPush:
+    case TalerUriType.TalerRecovery:
+    case TalerUriType.Unknown:
+      return (
+        <div>
+          <p>
+            <i18n.Translate>
+              This page has a malformed taler uri.
+            </i18n.Translate>
+          </p>
+        </div>
+      );
+
+    default: {
+      const error: never = type;
+      return null;
+    }
+  }
+}
+
 export function TalerActionFound({ url, onDismiss }: Props): VNode {
   const uriType = classifyTalerUri(url);
   const { i18n } = useTranslationContext();
@@ -43,74 +123,7 @@ export function TalerActionFound({ url, onDismiss }: 
Props): VNode {
         <Title>
           <i18n.Translate>Taler Action</i18n.Translate>
         </Title>
-        {uriType === TalerUriType.TalerPay && (
-          <div>
-            <p>
-              <i18n.Translate>This page has pay action.</i18n.Translate>
-            </p>
-            <Button
-              variant="contained"
-              color="success"
-              onClick={redirectToWallet}
-            >
-              <i18n.Translate>Open pay page</i18n.Translate>
-            </Button>
-          </div>
-        )}
-        {uriType === TalerUriType.TalerWithdraw && (
-          <div>
-            <p>
-              <i18n.Translate>
-                This page has a withdrawal action.
-              </i18n.Translate>
-            </p>
-            <Button
-              variant="contained"
-              color="success"
-              onClick={redirectToWallet}
-            >
-              <i18n.Translate>Open withdraw page</i18n.Translate>
-            </Button>
-          </div>
-        )}
-        {uriType === TalerUriType.TalerTip && (
-          <div>
-            <p>
-              <i18n.Translate>This page has a tip action.</i18n.Translate>
-            </p>
-            <Button
-              variant="contained"
-              color="success"
-              onClick={redirectToWallet}
-            >
-              <i18n.Translate>Open tip page</i18n.Translate>
-            </Button>
-          </div>
-        )}
-        {uriType === TalerUriType.TalerRefund && (
-          <div>
-            <p>
-              <i18n.Translate>This page has a refund action.</i18n.Translate>
-            </p>
-            <Button
-              variant="contained"
-              color="success"
-              onClick={redirectToWallet}
-            >
-              <i18n.Translate>Open refund page</i18n.Translate>
-            </Button>
-          </div>
-        )}
-        {uriType === TalerUriType.Unknown && (
-          <div>
-            <p>
-              <i18n.Translate>
-                This page has a malformed taler uri.
-              </i18n.Translate>
-            </p>
-            <p>{url}</p>
-          </div>
-        )}
+        <ContentByUriType type={uriType} onConfirm={redirectToWallet} />
       </section>
       <footer>
         <div />
diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx 
b/packages/taler-wallet-webextension/src/wallet/Application.tsx
index f29c169d0..fc8ddb804 100644
--- a/packages/taler-wallet-webextension/src/wallet/Application.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx
@@ -289,7 +289,7 @@ export function Application(): VNode {
               component={({ talerPayUri }: { talerPayUri: string }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash payment`}>
                   <PaymentPage
-                    talerPayUri={talerPayUri}
+                    talerPayUri={decodeURIComponent(talerPayUri)}
                     goToWalletManualWithdraw={(amount?: string) =>
                       redirectTo(Pages.receiveCash({ amount }))
                     }
@@ -302,14 +302,23 @@ export function Application(): VNode {
               )}
             />
             <Route
-              path={Pages.ctaPay}
+              path={Pages.ctaPayTemplate}
               component={({
-                talerTemplateUri,
+                talerPayTemplateUri,
               }: {
-                talerTemplateUri: string;
+                talerPayTemplateUri: string;
               }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash payment`}>
-                  <PaymentTemplatePage talerTemplateUri={talerTemplateUri} />
+                  <PaymentTemplatePage
+                    talerTemplateUri={decodeURIComponent(talerPayTemplateUri)}
+                    goToWalletManualWithdraw={(amount?: string) =>
+                      redirectTo(Pages.receiveCash({ amount }))
+                    }
+                    cancel={() => redirectTo(Pages.balance)}
+                    onSuccess={(tid: string) =>
+                      redirectTo(Pages.balanceTransaction({ tid }))
+                    }
+                  />
                 </CallToActionTemplate>
               )}
             />
@@ -318,7 +327,7 @@ export function Application(): VNode {
               component={({ talerRefundUri }: { talerRefundUri: string }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash refund`}>
                   <RefundPage
-                    talerRefundUri={talerRefundUri}
+                    talerRefundUri={decodeURIComponent(talerRefundUri)}
                     cancel={() => redirectTo(Pages.balance)}
                     onSuccess={(tid: string) =>
                       redirectTo(Pages.balanceTransaction({ tid }))
@@ -332,7 +341,7 @@ export function Application(): VNode {
               component={({ talerTipUri }: { talerTipUri: string }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash tip`}>
                   <TipPage
-                    talerTipUri={talerTipUri}
+                    talerTipUri={decodeURIComponent(talerTipUri)}
                     onCancel={() => redirectTo(Pages.balance)}
                     onSuccess={(tid: string) =>
                       redirectTo(Pages.balanceTransaction({ tid }))
@@ -350,7 +359,7 @@ export function Application(): VNode {
               }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash 
withdrawal`}>
                   <WithdrawPageFromURI
-                    talerWithdrawUri={talerWithdrawUri}
+                    talerWithdrawUri={decodeURIComponent(talerWithdrawUri)}
                     cancel={() => redirectTo(Pages.balance)}
                     onSuccess={(tid: string) =>
                       redirectTo(Pages.balanceTransaction({ tid }))
@@ -385,7 +394,7 @@ export function Application(): VNode {
                 <CallToActionTemplate title={i18n.str`Digital cash deposit`}>
                   <DepositPageCTA
                     amountStr={amount}
-                    talerDepositUri={talerDepositUri}
+                    talerDepositUri={decodeURIComponent(talerDepositUri)}
                     cancel={() => redirectTo(Pages.balance)}
                     onSuccess={(tid: string) =>
                       redirectTo(Pages.balanceTransaction({ tid }))
@@ -427,7 +436,7 @@ export function Application(): VNode {
               component={({ talerPayPullUri }: { talerPayPullUri: string }) => 
(
                 <CallToActionTemplate title={i18n.str`Digital cash invoice`}>
                   <InvoicePayPage
-                    talerPayPullUri={talerPayPullUri}
+                    talerPayPullUri={decodeURIComponent(talerPayPullUri)}
                     goToWalletManualWithdraw={(amount?: string) =>
                       redirectTo(Pages.receiveCash({ amount }))
                     }
@@ -444,7 +453,7 @@ export function Application(): VNode {
               component={({ talerPayPushUri }: { talerPayPushUri: string }) => 
(
                 <CallToActionTemplate title={i18n.str`Digital cash transfer`}>
                   <TransferPickupPage
-                    talerPayPushUri={talerPayPushUri}
+                    talerPayPushUri={decodeURIComponent(talerPayPushUri)}
                     onClose={() => redirectTo(Pages.balance)}
                     onSuccess={(tid: string) =>
                       redirectTo(Pages.balanceTransaction({ tid }))
@@ -462,7 +471,7 @@ export function Application(): VNode {
               }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash recovery`}>
                   <RecoveryPage
-                    talerRecoveryUri={talerRecoveryUri}
+                    talerRecoveryUri={decodeURIComponent(talerRecoveryUri)}
                     onCancel={() => redirectTo(Pages.balance)}
                     onSuccess={() => redirectTo(Pages.backup)}
                   />
diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
index ee0986076..2db2041d4 100644
--- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
@@ -171,6 +171,7 @@ export function View({
       [exchange_name: string]: CalculatedCoinfInfo[];
     },
   );
+  const exchanges = Object.keys(money_by_exchange);
 
   const [tagName, setTagName] = useState("");
   const [logLevel, setLogLevel] = useState("info");
@@ -317,6 +318,42 @@ export function View({
           >
             <i18n.Translate>Remove ext+taler:// handler</i18n.Translate>
           </Button>
+        </Grid>
+        <Grid item>
+          <Button
+            variant="contained"
+            onClick={async () => {
+              const result = await Promise.all(
+                exchanges.map(async (ex) => {
+                  const oldKeys = JSON.stringify(
+                    await (await fetch(`${ex}keys`)).json(),
+                  );
+                  const oldWire = JSON.stringify(
+                    await (await fetch(`${ex}wire`)).json(),
+                  );
+                  const newKeys = JSON.stringify(
+                    await (
+                      await fetch(`${ex}keys`, { cache: "no-cache" })
+                    ).json(),
+                  );
+                  const newWire = JSON.stringify(
+                    await (
+                      await fetch(`${ex}wire`, { cache: "no-cache" })
+                    ).json(),
+                  );
+                  return oldKeys !== newKeys || newWire !== oldWire;
+                }),
+              );
+              const ex = exchanges.filter((e, i) => result[i]);
+              if (!ex.length) {
+                alert("no exchange was outdated");
+              } else {
+                alert(`found some exchange out of date: ${result.join(", ")}`);
+              }
+            }}
+          >
+            <i18n.Translate>Clear exchange key cache</i18n.Translate>
+          </Button>
         </Grid>{" "}
       </Grid>
       <Paper style={{ padding: 10, margin: 10 }}>
diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts 
b/packages/taler-wallet-webextension/src/wxBackend.ts
index cca07941a..3655c5dbc 100644
--- a/packages/taler-wallet-webextension/src/wxBackend.ts
+++ b/packages/taler-wallet-webextension/src/wxBackend.ts
@@ -337,38 +337,44 @@ function parseTalerUriAndRedirect(tabId: number, 
maybeTalerUri: string): void {
     case TalerUriType.TalerWithdraw:
       return platform.redirectTabToWalletPage(
         tabId,
-        `/cta/withdraw?talerWithdrawUri=${talerUri}`,
+        `/cta/withdraw?talerWithdrawUri=${encodeURIComponent(talerUri)}`,
       );
     case TalerUriType.TalerPay:
       return platform.redirectTabToWalletPage(
         tabId,
-        `/cta/pay?talerPayUri=${talerUri}`,
+        `/cta/pay?talerPayUri=${encodeURIComponent(talerUri)}`,
       );
     case TalerUriType.TalerTip:
       return platform.redirectTabToWalletPage(
         tabId,
-        `/cta/tip?talerTipUri=${talerUri}`,
+        `/cta/tip?talerTipUri=${encodeURIComponent(talerUri)}`,
       );
     case TalerUriType.TalerRefund:
       return platform.redirectTabToWalletPage(
         tabId,
-        `/cta/refund?talerRefundUri=${talerUri}`,
+        `/cta/refund?talerRefundUri=${encodeURIComponent(talerUri)}`,
       );
     case TalerUriType.TalerPayPull:
       return platform.redirectTabToWalletPage(
         tabId,
-        `/cta/invoice/pay?talerPayPullUri=${talerUri}`,
+        `/cta/invoice/pay?talerPayPullUri=${encodeURIComponent(talerUri)}`,
       );
     case TalerUriType.TalerPayPush:
       return platform.redirectTabToWalletPage(
         tabId,
-        `/cta/transfer/pickup?talerPayPushUri=${talerUri}`,
+        `/cta/transfer/pickup?talerPayPushUri=${encodeURIComponent(talerUri)}`,
       );
     case TalerUriType.TalerRecovery:
       return platform.redirectTabToWalletPage(
         tabId,
-        `/cta/transfer/recovery?talerBackupUri=${talerUri}`,
+        
`/cta/transfer/recovery?talerBackupUri=${encodeURIComponent(talerUri)}`,
       );
+    case TalerUriType.TalerPayTemplate:
+      return platform.redirectTabToWalletPage(
+        tabId,
+        
`/cta/pay/template?talerPayTemplateUri=${encodeURIComponent(talerUri)}`,
+      );
+      return;
     case TalerUriType.Unknown:
       logger.warn(
         `Response with HTTP 402 the Taler header but could not classify 
${talerUri}`,
@@ -379,10 +385,7 @@ function parseTalerUriAndRedirect(tabId: number, 
maybeTalerUri: string): void {
       logger.warn("not implemented");
       return;
     case TalerUriType.TalerTemplate:
-      return platform.redirectTabToWalletPage(
-        tabId,
-        `/cta/template?talerTemplateUri=${talerUri}`,
-      );
+      logger.warn("not implemented");
       return;
     default: {
       const error: never = uriType;
diff --git a/packages/web-util/src/live-reload.ts 
b/packages/web-util/src/live-reload.ts
index 74d542956..48093088a 100644
--- a/packages/web-util/src/live-reload.ts
+++ b/packages/web-util/src/live-reload.ts
@@ -1,7 +1,8 @@
 /* eslint-disable no-undef */
 
 function setupLiveReload(): void {
-  const ws = new WebSocket("wss://localhost:8080/ws");
+  const protocol = window.location.protocol === "http:" ? "ws:" : "wss:";
+  const ws = new WebSocket(`${protocol}://localhost:8080/ws`);
 
   ws.addEventListener("message", (message) => {
     try {
diff --git a/packages/web-util/src/serve.ts b/packages/web-util/src/serve.ts
index 34982c656..597303ba2 100644
--- a/packages/web-util/src/serve.ts
+++ b/packages/web-util/src/serve.ts
@@ -2,6 +2,7 @@ import { Logger } from "@gnu-taler/taler-util";
 import chokidar from "chokidar";
 import express from "express";
 import https from "https";
+import http from "http";
 import { parse } from "url";
 import WebSocket from "ws";
 
@@ -30,6 +31,7 @@ export async function serve(opts: {
   port: number;
   source?: string;
   development?: boolean;
+  insecure?: boolean;
   examplesLocationJs?: string;
   examplesLocationCss?: string;
   onUpdate?: () => Promise<void>;
@@ -37,7 +39,9 @@ export async function serve(opts: {
   const app = express();
 
   app.use(PATHS.APP, express.static(opts.folder));
-  const server = https.createServer(httpServerOptions, app);
+  const server = opts.insecure
+    ? http.createServer(app)
+    : https.createServer(httpServerOptions, app);
   logger.info(`serving ${opts.folder} on ${opts.port}`);
   logger.info(`  ${PATHS.APP}: application`);
   logger.info(`  ${PATHS.EXAMPLE}: examples`);

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