summaryrefslogtreecommitdiff
path: root/packages/backend/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/backend/src')
-rw-r--r--packages/backend/src/assets/empty.pngbin0 -> 2785 bytes
-rw-r--r--packages/backend/src/assets/icons/android-chrome-192x192.pngbin0 -> 14058 bytes
-rw-r--r--packages/backend/src/assets/icons/android-chrome-512x512.pngbin0 -> 51484 bytes
-rw-r--r--packages/backend/src/assets/icons/apple-touch-icon.pngbin0 -> 12746 bytes
-rw-r--r--packages/backend/src/assets/icons/favicon-16x16.pngbin0 -> 626 bytes
-rw-r--r--packages/backend/src/assets/icons/favicon-32x32.pngbin0 -> 1487 bytes
-rw-r--r--packages/backend/src/assets/icons/languageicon.svg48
-rw-r--r--packages/backend/src/assets/icons/mstile-150x150.pngbin0 -> 9050 bytes
-rw-r--r--packages/backend/src/context/backend.ts82
-rw-r--r--packages/backend/src/context/config.ts32
-rw-r--r--packages/backend/src/context/fetch.ts40
-rw-r--r--packages/backend/src/context/instance.ts35
-rw-r--r--packages/backend/src/context/listener.ts35
-rw-r--r--packages/backend/src/context/translation.ts59
-rw-r--r--packages/backend/src/css/pure-min.css973
-rw-r--r--packages/backend/src/css/style.css62
-rw-r--r--packages/backend/src/custom.d.ts40
-rw-r--r--packages/backend/src/declaration.d.ts1384
-rw-r--r--packages/backend/src/hooks/async.ts76
-rw-r--r--packages/backend/src/hooks/backend.ts262
-rw-r--r--packages/backend/src/hooks/index.ts110
-rw-r--r--packages/backend/src/hooks/instance.ts187
-rw-r--r--packages/backend/src/hooks/listener.ts68
-rw-r--r--packages/backend/src/hooks/notification.ts43
-rw-r--r--packages/backend/src/hooks/notifications.ts48
-rw-r--r--packages/backend/src/hooks/order.ts217
-rw-r--r--packages/backend/src/hooks/product.ts199
-rw-r--r--packages/backend/src/hooks/tips.ts159
-rw-r--r--packages/backend/src/hooks/transfer.ts150
-rw-r--r--packages/backend/src/i18n/de.po1057
-rw-r--r--packages/backend/src/i18n/en.po1057
-rw-r--r--packages/backend/src/i18n/es.po1065
-rw-r--r--packages/backend/src/i18n/fr.po1057
-rw-r--r--packages/backend/src/i18n/index.tsx203
-rw-r--r--packages/backend/src/i18n/it.po1057
-rw-r--r--packages/backend/src/i18n/poheader27
-rw-r--r--packages/backend/src/i18n/strings-prelude19
-rw-r--r--packages/backend/src/i18n/strings.ts3445
-rw-r--r--packages/backend/src/i18n/sv.po1057
-rw-r--r--packages/backend/src/i18n/taler-merchant-backoffice.pot1054
-rw-r--r--packages/backend/src/index.tsx67
-rw-r--r--packages/backend/src/pages/DepletedTip.stories.tsx40
-rw-r--r--packages/backend/src/pages/DepletedTip.tsx45
-rw-r--r--packages/backend/src/pages/OfferRefund.stories.tsx40
-rw-r--r--packages/backend/src/pages/OfferRefund.tsx103
-rw-r--r--packages/backend/src/pages/OfferTip.stories.tsx40
-rw-r--r--packages/backend/src/pages/OfferTip.tsx113
-rw-r--r--packages/backend/src/pages/RequestPayment.stories.tsx40
-rw-r--r--packages/backend/src/pages/RequestPayment.tsx125
-rw-r--r--packages/backend/src/pages/ShowOrderDetails.stories.tsx40
-rw-r--r--packages/backend/src/pages/ShowOrderDetails.tsx125
-rw-r--r--packages/backend/src/utils/amount.ts69
-rw-r--r--packages/backend/src/utils/constants.ts47
-rw-r--r--packages/backend/src/utils/table.ts37
-rw-r--r--packages/backend/src/utils/types.ts31
55 files changed, 16369 insertions, 0 deletions
diff --git a/packages/backend/src/assets/empty.png b/packages/backend/src/assets/empty.png
new file mode 100644
index 0000000..5120d31
--- /dev/null
+++ b/packages/backend/src/assets/empty.png
Binary files differ
diff --git a/packages/backend/src/assets/icons/android-chrome-192x192.png b/packages/backend/src/assets/icons/android-chrome-192x192.png
new file mode 100644
index 0000000..93ebe2e
--- /dev/null
+++ b/packages/backend/src/assets/icons/android-chrome-192x192.png
Binary files differ
diff --git a/packages/backend/src/assets/icons/android-chrome-512x512.png b/packages/backend/src/assets/icons/android-chrome-512x512.png
new file mode 100644
index 0000000..52d1623
--- /dev/null
+++ b/packages/backend/src/assets/icons/android-chrome-512x512.png
Binary files differ
diff --git a/packages/backend/src/assets/icons/apple-touch-icon.png b/packages/backend/src/assets/icons/apple-touch-icon.png
new file mode 100644
index 0000000..254e4bb
--- /dev/null
+++ b/packages/backend/src/assets/icons/apple-touch-icon.png
Binary files differ
diff --git a/packages/backend/src/assets/icons/favicon-16x16.png b/packages/backend/src/assets/icons/favicon-16x16.png
new file mode 100644
index 0000000..e81177d
--- /dev/null
+++ b/packages/backend/src/assets/icons/favicon-16x16.png
Binary files differ
diff --git a/packages/backend/src/assets/icons/favicon-32x32.png b/packages/backend/src/assets/icons/favicon-32x32.png
new file mode 100644
index 0000000..40e9b5b
--- /dev/null
+++ b/packages/backend/src/assets/icons/favicon-32x32.png
Binary files differ
diff --git a/packages/backend/src/assets/icons/languageicon.svg b/packages/backend/src/assets/icons/languageicon.svg
new file mode 100644
index 0000000..22d58da
--- /dev/null
+++ b/packages/backend/src/assets/icons/languageicon.svg
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 2411.2 2794" style="enable-background:new 0 0 2411.2 2794;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+ .st1{fill-rule:evenodd;clip-rule:evenodd;}
+ .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
+</style>
+<g id="Layer_2">
+</g>
+<g id="Layer_x5F_1_x5F_1">
+ <g>
+ <polygon points="1204.6,359.2 271.8,30 271.8,2060.1 1204.6,1758.3 "/>
+ <polygon class="st0" points="1182.2,358.1 2150.6,29 2150.6,2059 1182.2,1757.3 "/>
+ <polygon class="st0" points="30,2415.4 1182.2,2031.4 1182.2,357.9 30,742 "/>
+ <polygon points="1707.2,2440.7 1870.5,2709.4 1956.6,2459.8 "/>
+ <g>
+ <path d="M421.7,934.8c-6.1-6,8,49.1,27.6,68.9c34.8,35.1,61.9,39.6,76.4,40.2c32,1.3,71.5-8,94.9-17.8
+ c22.7-9.7,62.4-30,77.5-59.6c3.2-6.3,11.9-17,6.4-43.2c-4.2-20.2-17-27.3-32.7-26.2c-15.7,1.1-63.2,13.7-86.1,20.8
+ c-23,7-70.3,21.4-90.9,25.8C474.3,948.2,429,941.7,421.7,934.8z"/>
+ <path d="M1003.1,1593.7c-9.1-3.3-196.9-81.1-223.6-93.9c-21.8-10.5-75.2-33.1-100.4-43.3c70.8-109.2,115.5-191.6,121.5-204.1
+ c11-23,86-169.6,87.7-178.7c1.7-9.1,3.8-42.9,2.2-51c-1.7-8.2-29.1,7.6-66.4,20.2c-37.4,12.6-108.4,58.8-135.8,64.6
+ c-27.5,5.7-115.5,39.1-160.5,54c-45,14.9-130.2,40.9-165.2,50.4c-35.1,9.5-65.7,10.2-85.3,16.2c0,0,2.6,27.5,7.8,35.7
+ c5.2,8.2,23.7,28.4,45.3,34.1c21.6,5.7,57.3,3.4,73.6-0.3c16.3-3.8,44.4-17.5,48.2-23.6c3.8-6.1-2-24.9,4.5-30.6
+ c6.5-5.6,92.2-25.7,124.6-35.4c32.4-10,156.3-52.6,173.1-50.5c-5.3,17.7-105,215.1-137.1,274c-32.1,58.9-218.6,318-258.3,363.6
+ c-30.1,34.7-103.2,123.5-128.5,143.6c6.4,1.8,51.6-2.1,59.9-7.2c51.3-31.6,136.9-138.1,164.4-170.5
+ c81.9-96,153.8-196.8,210.8-283.4h0.1c11.1,4.6,100.9,77.8,124.4,94c23.4,16.2,115.9,67.8,136,76.4c20,8.7,97.1,44.2,100.3,32.2
+ C1029.4,1668,1012.2,1597.1,1003.1,1593.7z"/>
+ </g>
+ <path class="st1" d="M569,2572c18,11,35,20,54,29c38,19,81,39,122,54c56,21,112,38,168,51c31,7,65,13,98,18c3,0,92,11,110,11h90
+ c35-3,68-5,103-10c28-4,59-9,89-16c22-5,45-10,67-17c21-6,45-14,68-22c15-5,31-12,47-18c13-6,29-13,44-19c18-8,39-19,59-29
+ c16-8,34-18,51-28c13-7,43-30,59-30c18,0,30,16,30,30c0,29-39,38-57,51c-19,13-42,23-62,34c-40,21-81,39-120,54
+ c-51,19-107,37-157,49c-19,4-38,9-57,12c-10,2-114,18-143,18h-132c-35-3-72-7-107-12c-31-5-64-11-95-18c-24-5-50-12-73-19
+ c-40-11-79-25-117-40c-69-26-141-60-209-105c-12-8-13-16-13-25c0-15,11-29,29-29C531,2546,563,2569,569,2572z"/>
+ <path class="st1" d="M1151,2009L61,2372V764l1090-363V2009z M1212,354v1680c-1,5-3,10-7,15c-2,3-6,7-9,8c-25,10-1151,388-1166,388
+ c-12,0-23-8-29-21c0-1-1-2-1-4V739c2-5,3-12,7-16c8-11,22-13,31-16c17-6,1126-378,1142-378C1190,329,1212,336,1212,354z"/>
+ <path class="st1" d="M2120,2017l-907-282V380l907-308V2017z M2181,32v2023c-1,23-17,33-32,33c-13,0-107-32-123-37
+ c-126-39-253-78-378-117c-28-9-57-18-84-27c-24-7-50-15-74-23c-107-33-216-66-323-102c-4-1-14-15-14-18V351c2-5,4-11,9-15
+ c8-9,351-123,486-168c36-13,487-168,501-168C2167,0,2181,13,2181,32z"/>
+ <polygon points="2411.2,2440.7 1199.5,2054.5 1204.6,373.2 2411.2,757.2 "/>
+ <g>
+ <path class="st2" d="M1800.3,1124.6L1681.4,1412l218.6,66.3L1800.3,1124.6z M1729,853.2l156.1,47.3l284.4,1025l-160.3-48.7
+ l-57.6-210.4L1620.2,1566l-71.3,171.4l-160.4-48.7L1729,853.2z"/>
+ </g>
+ </g>
+</g>
+</svg>
diff --git a/packages/backend/src/assets/icons/mstile-150x150.png b/packages/backend/src/assets/icons/mstile-150x150.png
new file mode 100644
index 0000000..9cfb889
--- /dev/null
+++ b/packages/backend/src/assets/icons/mstile-150x150.png
Binary files differ
diff --git a/packages/backend/src/context/backend.ts b/packages/backend/src/context/backend.ts
new file mode 100644
index 0000000..a920d6f
--- /dev/null
+++ b/packages/backend/src/context/backend.ts
@@ -0,0 +1,82 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { createContext, h, VNode } from 'preact'
+import { useCallback, useContext, useState } from 'preact/hooks'
+import { useBackendDefaultToken, useBackendURL } from '../hooks';
+
+interface BackendContextType {
+ url: string;
+ token?: string;
+ triedToLog: boolean;
+ resetBackend: () => void;
+ clearAllTokens: () => void;
+ addTokenCleaner: (c: () => void) => void;
+ updateLoginStatus: (url: string, token?: string) => void;
+}
+
+const BackendContext = createContext<BackendContextType>({
+ url: '',
+ token: undefined,
+ triedToLog: false,
+ resetBackend: () => null,
+ clearAllTokens: () => null,
+ addTokenCleaner: () => null,
+ updateLoginStatus: () => null,
+})
+
+function useBackendContextState(defaultUrl?: string): BackendContextType {
+ const [url, triedToLog, changeBackend, resetBackend] = useBackendURL(defaultUrl);
+ const [token, _updateToken] = useBackendDefaultToken();
+ const updateToken = (t?: string) => {
+ _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 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);
+ };
+
+
+ return { url, token, triedToLog, updateLoginStatus, resetBackend, clearAllTokens, addTokenCleaner: addTokenCleanerMemo }
+}
+
+export const BackendContextProvider = ({ children, defaultUrl }: { children: any, defaultUrl?: string }): VNode => {
+ const value = useBackendContextState(defaultUrl)
+
+ return h(BackendContext.Provider, { value, children });
+}
+
+export const useBackendContext = (): BackendContextType => useContext(BackendContext);
diff --git a/packages/backend/src/context/config.ts b/packages/backend/src/context/config.ts
new file mode 100644
index 0000000..5cd7723
--- /dev/null
+++ b/packages/backend/src/context/config.ts
@@ -0,0 +1,32 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { createContext } from 'preact'
+import { useContext } from 'preact/hooks'
+
+interface Type {
+ currency: string;
+ version: string;
+}
+const Context = createContext<Type>(null!)
+
+export const ConfigContextProvider = Context.Provider
+export const useConfigContext = (): Type => useContext(Context);
diff --git a/packages/backend/src/context/fetch.ts b/packages/backend/src/context/fetch.ts
new file mode 100644
index 0000000..52a4f9c
--- /dev/null
+++ b/packages/backend/src/context/fetch.ts
@@ -0,0 +1,40 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, createContext, VNode, ComponentChildren } from 'preact'
+import { useContext } from 'preact/hooks'
+import useSWR, { trigger, useSWRInfinite, cache, mutate } from 'swr';
+
+interface Type {
+ useSWR: typeof useSWR,
+ useSWRInfinite: typeof useSWRInfinite,
+}
+
+const Context = createContext<Type>({} as any)
+
+export const useFetchContext = (): Type => useContext(Context);
+export const FetchContextProvider = ({ children }: { children: ComponentChildren }): VNode => {
+ return h(Context.Provider, { value: { useSWR, useSWRInfinite }, children });
+}
+
+export const FetchContextProviderTesting = ({ children, data }: { children: ComponentChildren, data:any }): VNode => {
+ return h(Context.Provider, { value: { useSWR: () => data, useSWRInfinite }, children });
+}
diff --git a/packages/backend/src/context/instance.ts b/packages/backend/src/context/instance.ts
new file mode 100644
index 0000000..fecf364
--- /dev/null
+++ b/packages/backend/src/context/instance.ts
@@ -0,0 +1,35 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { createContext } from 'preact'
+import { useContext } from 'preact/hooks'
+
+interface Type {
+ id: string;
+ token?: string;
+ admin?: boolean;
+ changeToken: (t?:string) => void;
+}
+
+const Context = createContext<Type>({} as any)
+
+export const InstanceContextProvider = Context.Provider
+export const useInstanceContext = (): Type => useContext(Context);
diff --git a/packages/backend/src/context/listener.ts b/packages/backend/src/context/listener.ts
new file mode 100644
index 0000000..659db0a
--- /dev/null
+++ b/packages/backend/src/context/listener.ts
@@ -0,0 +1,35 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { createContext } from 'preact'
+import { useContext } from 'preact/hooks'
+
+interface Type {
+ id: string;
+ token?: string;
+ admin?: boolean;
+ changeToken: (t?:string) => void;
+}
+
+const Context = createContext<Type>({} as any)
+
+export const ListenerContextProvider = Context.Provider
+export const useListenerContext = (): Type => useContext(Context);
diff --git a/packages/backend/src/context/translation.ts b/packages/backend/src/context/translation.ts
new file mode 100644
index 0000000..952a1e3
--- /dev/null
+++ b/packages/backend/src/context/translation.ts
@@ -0,0 +1,59 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { createContext, h, VNode } from 'preact'
+import { useContext, useEffect } from 'preact/hooks'
+import { useLang } from '../hooks'
+import * as jedLib from "jed";
+import { strings } from "../i18n/strings";
+
+interface Type {
+ lang: string;
+ handler: any;
+ changeLanguage: (l: string) => void;
+}
+const initial = {
+ lang: 'en',
+ handler: null,
+ changeLanguage: () => {
+ // do not change anything
+ }
+}
+const Context = createContext<Type>(initial)
+
+interface Props {
+ initial?: string,
+ children: any,
+ forceLang?: string
+}
+
+export const TranslationProvider = ({ initial, children, forceLang }: Props): VNode => {
+ const [lang, changeLanguage] = useLang(initial)
+ useEffect(() => {
+ if (forceLang) {
+ changeLanguage(forceLang)
+ }
+ })
+ const handler = new jedLib.Jed(strings[lang]);
+ return h(Context.Provider, { value: { lang, handler, changeLanguage }, children });
+}
+
+export const useTranslationContext = (): Type => useContext(Context); \ No newline at end of file
diff --git a/packages/backend/src/css/pure-min.css b/packages/backend/src/css/pure-min.css
new file mode 100644
index 0000000..77217b5
--- /dev/null
+++ b/packages/backend/src/css/pure-min.css
@@ -0,0 +1,973 @@
+/*!
+ Pure v2.0.3
+ Copyright 2013 Yahoo!
+ Licensed under the BSD License.
+ https://github.com/pure-cs s/pure/blob/master/LICENSE.md
+*/
+/*!
+ normalize.cs s v | MIT License | git.io/normalize
+ Copyright (c) Nicolas Gallagher and Jonathan Neal
+*/
+/*! normalize.cs s v8.0.1 | MIT License | github.com/necolas/normalize.cs s */
+
+.talerbar {
+ text-align: center;
+}
+
+html {
+ line-height: 1.15;
+ -webkit-text-size-adjust: 100%;
+}
+body {
+ margin: 0;
+}
+main {
+ display: block;
+}
+h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
+}
+hr {
+ -webkit-box-sizing: content-box;
+ box-sizing: content-box;
+ height: 0;
+ overflow: visible;
+}
+pre {
+ font-family: monospace, monospace;
+ font-size: 1em;
+}
+a {
+ background-color: transparent;
+}
+abbr[title] {
+ border-bottom: none;
+ text-decoration: underline;
+ -webkit-text-decoration: underline dotted;
+ text-decoration: underline dotted;
+}
+b,
+strong {
+ font-weight: bolder;
+}
+code,
+kbd,
+samp {
+ font-family: monospace, monospace;
+ font-size: 1em;
+}
+small {
+ font-size: 80%;
+}
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+sub {
+ bottom: -0.25em;
+}
+sup {
+ top: -0.5em;
+}
+img {
+ border-style: none;
+}
+button,
+input,
+optgroup,
+select,
+textarea {
+ font-family: inherit;
+ font-size: 100%;
+ line-height: 1.15;
+ margin: 0;
+}
+button,
+input {
+ overflow: visible;
+}
+button,
+select {
+ text-transform: none;
+}
+[type="button"],
+[type="reset"],
+[type="submit"],
+button {
+ -webkit-appearance: button;
+}
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner,
+button::-moz-focus-inner {
+ border-style: none;
+ padding: 0;
+}
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring,
+button:-moz-focusring {
+ outline: 1px dotted ButtonText;
+}
+fieldset {
+ padding: 0.35em 0.75em 0.625em;
+}
+legend {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ color: inherit;
+ display: table;
+ max-width: 100%;
+ padding: 0;
+ white-space: normal;
+}
+progress {
+ vertical-align: baseline;
+}
+textarea {
+ overflow: auto;
+}
+[type="checkbox"],
+[type="radio"] {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 0;
+}
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+ height: auto;
+}
+[type="search"] {
+ -webkit-appearance: textfield;
+ outline-offset: -2px;
+}
+[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+::-webkit-file-upload-button {
+ -webkit-appearance: button;
+ font: inherit;
+}
+details {
+ display: block;
+}
+summary {
+ display: list-item;
+}
+template {
+ display: none;
+}
+[hidden] {
+ display: none;
+}
+html {
+ font-family: sans-serif;
+}
+.hidden,
+[hidden] {
+ display: none !important;
+}
+.pure-img {
+ max-width: 100%;
+ height: auto;
+ display: block;
+}
+.pure-g {
+ letter-spacing: -0.31em;
+ text-rendering: optimizespeed;
+ font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif;
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-orient: horizontal;
+ -webkit-box-direction: normal;
+ -ms-flex-flow: row wrap;
+ flex-flow: row wrap;
+ -ms-flex-line-pack: start;
+ align-content: flex-start;
+}
+@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
+ table .pure-g {
+ display: block;
+ }
+}
+.opera-only :-o-prefocus,
+.pure-g {
+ word-spacing: -0.43em;
+}
+.pure-u {
+ display: inline-block;
+ letter-spacing: normal;
+ word-spacing: normal;
+ vertical-align: top;
+ text-rendering: auto;
+}
+.pure-g [class*="pure-u"] {
+ font-family: sans-serif;
+}
+.pure-u-1,
+.pure-u-1-1,
+.pure-u-1-12,
+.pure-u-1-2,
+.pure-u-1-24,
+.pure-u-1-3,
+.pure-u-1-4,
+.pure-u-1-5,
+.pure-u-1-6,
+.pure-u-1-8,
+.pure-u-10-24,
+.pure-u-11-12,
+.pure-u-11-24,
+.pure-u-12-24,
+.pure-u-13-24,
+.pure-u-14-24,
+.pure-u-15-24,
+.pure-u-16-24,
+.pure-u-17-24,
+.pure-u-18-24,
+.pure-u-19-24,
+.pure-u-2-24,
+.pure-u-2-3,
+.pure-u-2-5,
+.pure-u-20-24,
+.pure-u-21-24,
+.pure-u-22-24,
+.pure-u-23-24,
+.pure-u-24-24,
+.pure-u-3-24,
+.pure-u-3-4,
+.pure-u-3-5,
+.pure-u-3-8,
+.pure-u-4-24,
+.pure-u-4-5,
+.pure-u-5-12,
+.pure-u-5-24,
+.pure-u-5-5,
+.pure-u-5-6,
+.pure-u-5-8,
+.pure-u-6-24,
+.pure-u-7-12,
+.pure-u-7-24,
+.pure-u-7-8,
+.pure-u-8-24,
+.pure-u-9-24 {
+ display: inline-block;
+ letter-spacing: normal;
+ word-spacing: normal;
+ vertical-align: top;
+ text-rendering: auto;
+}
+.pure-u-1-24 {
+ width: 4.1667%;
+}
+.pure-u-1-12,
+.pure-u-2-24 {
+ width: 8.3333%;
+}
+.pure-u-1-8,
+.pure-u-3-24 {
+ width: 12.5%;
+}
+.pure-u-1-6,
+.pure-u-4-24 {
+ width: 16.6667%;
+}
+.pure-u-1-5 {
+ width: 20%;
+}
+.pure-u-5-24 {
+ width: 20.8333%;
+}
+.pure-u-1-4,
+.pure-u-6-24 {
+ width: 25%;
+}
+.pure-u-7-24 {
+ width: 29.1667%;
+}
+.pure-u-1-3,
+.pure-u-8-24 {
+ width: 33.3333%;
+}
+.pure-u-3-8,
+.pure-u-9-24 {
+ width: 37.5%;
+}
+.pure-u-2-5 {
+ width: 40%;
+}
+.pure-u-10-24,
+.pure-u-5-12 {
+ width: 41.6667%;
+}
+.pure-u-11-24 {
+ width: 45.8333%;
+}
+.pure-u-1-2,
+.pure-u-12-24 {
+ width: 50%;
+}
+.pure-u-13-24 {
+ width: 54.1667%;
+}
+.pure-u-14-24,
+.pure-u-7-12 {
+ width: 58.3333%;
+}
+.pure-u-3-5 {
+ width: 60%;
+}
+.pure-u-15-24,
+.pure-u-5-8 {
+ width: 62.5%;
+}
+.pure-u-16-24,
+.pure-u-2-3 {
+ width: 66.6667%;
+}
+.pure-u-17-24 {
+ width: 70.8333%;
+}
+.pure-u-18-24,
+.pure-u-3-4 {
+ width: 75%;
+}
+.pure-u-19-24 {
+ width: 79.1667%;
+}
+.pure-u-4-5 {
+ width: 80%;
+}
+.pure-u-20-24,
+.pure-u-5-6 {
+ width: 83.3333%;
+}
+.pure-u-21-24,
+.pure-u-7-8 {
+ width: 87.5%;
+}
+.pure-u-11-12,
+.pure-u-22-24 {
+ width: 91.6667%;
+}
+.pure-u-23-24 {
+ width: 95.8333%;
+}
+.pure-u-1,
+.pure-u-1-1,
+.pure-u-24-24,
+.pure-u-5-5 {
+ width: 100%;
+}
+.pure-button {
+ display: inline-block;
+ line-height: normal;
+ white-space: nowrap;
+ vertical-align: middle;
+ text-align: center;
+ cursor: pointer;
+ -webkit-user-drag: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.pure-button::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+.pure-button-group {
+ letter-spacing: -0.31em;
+ text-rendering: optimizespeed;
+}
+.opera-only :-o-prefocus,
+.pure-button-group {
+ word-spacing: -0.43em;
+}
+.pure-button-group .pure-button {
+ letter-spacing: normal;
+ word-spacing: normal;
+ vertical-align: top;
+ text-rendering: auto;
+}
+.pure-button {
+ font-family: inherit;
+ font-size: 100%;
+ padding: 0.5em 1em;
+ color: rgba(0, 0, 0, 0.8);
+ border: none transparent;
+ background-color: #e6e6e6;
+ text-decoration: none;
+ border-radius: 2px;
+}
+.pure-button-hover,
+.pure-button:focus,
+.pure-button:hover {
+ background-image: -webkit-gradient(
+ linear,
+ left top,
+ left bottom,
+ from(transparent),
+ color-stop(40%, rgba(0, 0, 0, 0.05)),
+ to(rgba(0, 0, 0, 0.1))
+ );
+ background-image: linear-gradient(
+ transparent,
+ rgba(0, 0, 0, 0.05) 40%,
+ rgba(0, 0, 0, 0.1)
+ );
+}
+.pure-button:focus {
+ outline: 0;
+}
+.pure-button-active,
+.pure-button:active {
+ -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset,
+ 0 0 6px rgba(0, 0, 0, 0.2) inset;
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset,
+ 0 0 6px rgba(0, 0, 0, 0.2) inset;
+ border-color: #000;
+}
+.pure-button-disabled,
+.pure-button-disabled:active,
+.pure-button-disabled:focus,
+.pure-button-disabled:hover,
+.pure-button[disabled] {
+ border: none;
+ background-image: none;
+ opacity: 0.4;
+ cursor: not-allowed;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ pointer-events: none;
+}
+.pure-button-hidden {
+ display: none;
+}
+.pure-button-primary,
+.pure-button-selected,
+a.pure-button-primary,
+a.pure-button-selected {
+ background-color: #0078e7;
+ color: #fff;
+}
+.pure-button-group .pure-button {
+ margin: 0;
+ border-radius: 0;
+ border-right: 1px solid rgba(0, 0, 0, 0.2);
+}
+.pure-button-group .pure-button:first-child {
+ border-top-left-radius: 2px;
+ border-bottom-left-radius: 2px;
+}
+.pure-button-group .pure-button:last-child {
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-right: none;
+}
+.pure-form input[type="color"],
+.pure-form input[type="date"],
+.pure-form input[type="datetime-local"],
+.pure-form input[type="datetime"],
+.pure-form input[type="email"],
+.pure-form input[type="month"],
+.pure-form input[type="number"],
+.pure-form input[type="password"],
+.pure-form input[type="search"],
+.pure-form input[type="tel"],
+.pure-form input[type="text"],
+.pure-form input[type="time"],
+.pure-form input[type="url"],
+.pure-form input[type="week"],
+.pure-form select,
+.pure-form textarea {
+ padding: 0.5em 0.6em;
+ display: inline-block;
+ border: 1px solid #ccc;
+ -webkit-box-shadow: inset 0 1px 3px #ddd;
+ box-shadow: inset 0 1px 3px #ddd;
+ border-radius: 4px;
+ vertical-align: middle;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.pure-form input:not([type]) {
+ padding: 0.5em 0.6em;
+ display: inline-block;
+ border: 1px solid #ccc;
+ -webkit-box-shadow: inset 0 1px 3px #ddd;
+ box-shadow: inset 0 1px 3px #ddd;
+ border-radius: 4px;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.pure-form input[type="color"] {
+ padding: 0.2em 0.5em;
+}
+.pure-form input[type="color"]:focus,
+.pure-form input[type="date"]:focus,
+.pure-form input[type="datetime-local"]:focus,
+.pure-form input[type="datetime"]:focus,
+.pure-form input[type="email"]:focus,
+.pure-form input[type="month"]:focus,
+.pure-form input[type="number"]:focus,
+.pure-form input[type="password"]:focus,
+.pure-form input[type="search"]:focus,
+.pure-form input[type="tel"]:focus,
+.pure-form input[type="text"]:focus,
+.pure-form input[type="time"]:focus,
+.pure-form input[type="url"]:focus,
+.pure-form input[type="week"]:focus,
+.pure-form select:focus,
+.pure-form textarea:focus {
+ outline: 0;
+ border-color: #129fea;
+}
+.pure-form input:not([type]):focus {
+ outline: 0;
+ border-color: #129fea;
+}
+.pure-form input[type="checkbox"]:focus,
+.pure-form input[type="file"]:focus,
+.pure-form input[type="radio"]:focus {
+ outline: thin solid #129fea;
+ outline: 1px auto #129fea;
+}
+.pure-form .pure-checkbox,
+.pure-form .pure-radio {
+ margin: 0.5em 0;
+ display: block;
+}
+.pure-form input[type="color"][disabled],
+.pure-form input[type="date"][disabled],
+.pure-form input[type="datetime-local"][disabled],
+.pure-form input[type="datetime"][disabled],
+.pure-form input[type="email"][disabled],
+.pure-form input[type="month"][disabled],
+.pure-form input[type="number"][disabled],
+.pure-form input[type="password"][disabled],
+.pure-form input[type="search"][disabled],
+.pure-form input[type="tel"][disabled],
+.pure-form input[type="text"][disabled],
+.pure-form input[type="time"][disabled],
+.pure-form input[type="url"][disabled],
+.pure-form input[type="week"][disabled],
+.pure-form select[disabled],
+.pure-form textarea[disabled] {
+ cursor: not-allowed;
+ background-color: #eaeded;
+ color: #cad2d3;
+}
+.pure-form input:not([type])[disabled] {
+ cursor: not-allowed;
+ background-color: #eaeded;
+ color: #cad2d3;
+}
+.pure-form input[readonly],
+.pure-form select[readonly],
+.pure-form textarea[readonly] {
+ background-color: #eee;
+ color: #777;
+ border-color: #ccc;
+}
+.pure-form input:focus:invalid,
+.pure-form select:focus:invalid,
+.pure-form textarea:focus:invalid {
+ color: #b94a48;
+ border-color: #e9322d;
+}
+.pure-form input[type="checkbox"]:focus:invalid:focus,
+.pure-form input[type="file"]:focus:invalid:focus,
+.pure-form input[type="radio"]:focus:invalid:focus {
+ outline-color: #e9322d;
+}
+.pure-form select {
+ height: 2.25em;
+ border: 1px solid #ccc;
+ background-color: #fff;
+}
+.pure-form select[multiple] {
+ height: auto;
+}
+.pure-form label {
+ margin: 0.5em 0 0.2em;
+}
+.pure-form fieldset {
+ margin: 0;
+ padding: 0.35em 0 0.75em;
+ border: 0;
+}
+.pure-form legend {
+ display: block;
+ width: 100%;
+ padding: 0.3em 0;
+ margin-bottom: 0.3em;
+ color: #333;
+ border-bottom: 1px solid #e5e5e5;
+}
+.pure-form-stacked input[type="color"],
+.pure-form-stacked input[type="date"],
+.pure-form-stacked input[type="datetime-local"],
+.pure-form-stacked input[type="datetime"],
+.pure-form-stacked input[type="email"],
+.pure-form-stacked input[type="file"],
+.pure-form-stacked input[type="month"],
+.pure-form-stacked input[type="number"],
+.pure-form-stacked input[type="password"],
+.pure-form-stacked input[type="search"],
+.pure-form-stacked input[type="tel"],
+.pure-form-stacked input[type="text"],
+.pure-form-stacked input[type="time"],
+.pure-form-stacked input[type="url"],
+.pure-form-stacked input[type="week"],
+.pure-form-stacked label,
+.pure-form-stacked select,
+.pure-form-stacked textarea {
+ display: block;
+ margin: 0.25em 0;
+}
+.pure-form-stacked input:not([type]) {
+ display: block;
+ margin: 0.25em 0;
+}
+.pure-form-aligned input,
+.pure-form-aligned select,
+.pure-form-aligned textarea,
+.pure-form-message-inline {
+ display: inline-block;
+ vertical-align: middle;
+}
+.pure-form-aligned textarea {
+ vertical-align: top;
+}
+.pure-form-aligned .pure-control-group {
+ margin-bottom: 0.5em;
+}
+.pure-form-aligned .pure-control-group label {
+ text-align: right;
+ display: inline-block;
+ vertical-align: middle;
+ width: 10em;
+ margin: 0 1em 0 0;
+}
+.pure-form-aligned .pure-controls {
+ margin: 1.5em 0 0 11em;
+}
+.pure-form .pure-input-rounded,
+.pure-form input.pure-input-rounded {
+ border-radius: 2em;
+ padding: 0.5em 1em;
+}
+.pure-form .pure-group fieldset {
+ margin-bottom: 10px;
+}
+.pure-form .pure-group input,
+.pure-form .pure-group textarea {
+ display: block;
+ padding: 10px;
+ margin: 0 0 -1px;
+ border-radius: 0;
+ position: relative;
+ top: -1px;
+}
+.pure-form .pure-group input:focus,
+.pure-form .pure-group textarea:focus {
+ z-index: 3;
+}
+.pure-form .pure-group input:first-child,
+.pure-form .pure-group textarea:first-child {
+ top: 1px;
+ border-radius: 4px 4px 0 0;
+ margin: 0;
+}
+.pure-form .pure-group input:first-child:last-child,
+.pure-form .pure-group textarea:first-child:last-child {
+ top: 1px;
+ border-radius: 4px;
+ margin: 0;
+}
+.pure-form .pure-group input:last-child,
+.pure-form .pure-group textarea:last-child {
+ top: -2px;
+ border-radius: 0 0 4px 4px;
+ margin: 0;
+}
+.pure-form .pure-group button {
+ margin: 0.35em 0;
+}
+.pure-form .pure-input-1 {
+ width: 100%;
+}
+.pure-form .pure-input-3-4 {
+ width: 75%;
+}
+.pure-form .pure-input-2-3 {
+ width: 66%;
+}
+.pure-form .pure-input-1-2 {
+ width: 50%;
+}
+.pure-form .pure-input-1-3 {
+ width: 33%;
+}
+.pure-form .pure-input-1-4 {
+ width: 25%;
+}
+.pure-form-message-inline {
+ display: inline-block;
+ padding-left: 0.3em;
+ color: #666;
+ vertical-align: middle;
+ font-size: 0.875em;
+}
+.pure-form-message {
+ display: block;
+ color: #666;
+ font-size: 0.875em;
+}
+@media only screen and (max-width: 480px) {
+ .pure-form button[type="submit"] {
+ margin: 0.7em 0 0;
+ }
+ .pure-form input:not([type]),
+ .pure-form input[type="color"],
+ .pure-form input[type="date"],
+ .pure-form input[type="datetime-local"],
+ .pure-form input[type="datetime"],
+ .pure-form input[type="email"],
+ .pure-form input[type="month"],
+ .pure-form input[type="number"],
+ .pure-form input[type="password"],
+ .pure-form input[type="search"],
+ .pure-form input[type="tel"],
+ .pure-form input[type="text"],
+ .pure-form input[type="time"],
+ .pure-form input[type="url"],
+ .pure-form input[type="week"],
+ .pure-form label {
+ margin-bottom: 0.3em;
+ display: block;
+ }
+ .pure-group input:not([type]),
+ .pure-group input[type="color"],
+ .pure-group input[type="date"],
+ .pure-group input[type="datetime-local"],
+ .pure-group input[type="datetime"],
+ .pure-group input[type="email"],
+ .pure-group input[type="month"],
+ .pure-group input[type="number"],
+ .pure-group input[type="password"],
+ .pure-group input[type="search"],
+ .pure-group input[type="tel"],
+ .pure-group input[type="text"],
+ .pure-group input[type="time"],
+ .pure-group input[type="url"],
+ .pure-group input[type="week"] {
+ margin-bottom: 0;
+ }
+ .pure-form-aligned .pure-control-group label {
+ margin-bottom: 0.3em;
+ text-align: left;
+ display: block;
+ width: 100%;
+ }
+ .pure-form-aligned .pure-controls {
+ margin: 1.5em 0 0 0;
+ }
+ .pure-form-message,
+ .pure-form-message-inline {
+ display: block;
+ font-size: 0.75em;
+ padding: 0.2em 0 0.8em;
+ }
+}
+.pure-menu {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.pure-menu-fixed {
+ position: fixed;
+ left: 0;
+ top: 0;
+ z-index: 3;
+}
+.pure-menu-item,
+.pure-menu-list {
+ position: relative;
+}
+.pure-menu-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+.pure-menu-item {
+ padding: 0;
+ margin: 0;
+ height: 100%;
+}
+.pure-menu-heading,
+.pure-menu-link {
+ display: block;
+ text-decoration: none;
+ white-space: nowrap;
+}
+.pure-menu-horizontal {
+ width: 100%;
+ white-space: nowrap;
+}
+.pure-menu-horizontal .pure-menu-list {
+ display: inline-block;
+}
+.pure-menu-horizontal .pure-menu-heading,
+.pure-menu-horizontal .pure-menu-item,
+.pure-menu-horizontal .pure-menu-separator {
+ display: inline-block;
+ vertical-align: middle;
+}
+.pure-menu-item .pure-menu-item {
+ display: block;
+}
+.pure-menu-children {
+ display: none;
+ position: absolute;
+ left: 100%;
+ top: 0;
+ margin: 0;
+ padding: 0;
+ z-index: 3;
+}
+.pure-menu-horizontal .pure-menu-children {
+ left: 0;
+ top: auto;
+ width: inherit;
+}
+.pure-menu-active > .pure-menu-children,
+.pure-menu-allow-hover:hover > .pure-menu-children {
+ display: block;
+ position: absolute;
+}
+.pure-menu-has-children > .pure-menu-link:after {
+ padding-left: 0.5em;
+ content: "\25B8";
+ font-size: small;
+}
+.pure-menu-horizontal .pure-menu-has-children > .pure-menu-link:after {
+ content: "\25BE";
+}
+.pure-menu-scrollable {
+ overflow-y: scroll;
+ overflow-x: hidden;
+}
+.pure-menu-scrollable .pure-menu-list {
+ display: block;
+}
+.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list {
+ display: inline-block;
+}
+.pure-menu-horizontal.pure-menu-scrollable {
+ white-space: nowrap;
+ overflow-y: hidden;
+ overflow-x: auto;
+ padding: 0.5em 0;
+}
+.pure-menu-horizontal .pure-menu-children .pure-menu-separator,
+.pure-menu-separator {
+ background-color: #ccc;
+ height: 1px;
+ margin: 0.3em 0;
+}
+.pure-menu-horizontal .pure-menu-separator {
+ width: 1px;
+ height: 1.3em;
+ margin: 0 0.3em;
+}
+.pure-menu-horizontal .pure-menu-children .pure-menu-separator {
+ display: block;
+ width: auto;
+}
+.pure-menu-heading {
+ text-transform: uppercase;
+ color: #565d64;
+}
+.pure-menu-link {
+ color: #777;
+}
+.pure-menu-children {
+ background-color: #fff;
+}
+.pure-menu-disabled,
+.pure-menu-heading,
+.pure-menu-link {
+ padding: 0.5em 1em;
+}
+.pure-menu-disabled {
+ opacity: 0.5;
+}
+.pure-menu-disabled .pure-menu-link:hover {
+ background-color: transparent;
+}
+.pure-menu-active > .pure-menu-link,
+.pure-menu-link:focus,
+.pure-menu-link:hover {
+ background-color: #eee;
+}
+.pure-menu-selected > .pure-menu-link,
+.pure-menu-selected > .pure-menu-link:visited {
+ color: #000;
+}
+.pure-table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ empty-cells: show;
+ border: 1px solid #cbcbcb;
+}
+.pure-table caption {
+ color: #000;
+ font: italic 85%/1 arial, sans-serif;
+ padding: 1em 0;
+ text-align: center;
+}
+.pure-table td,
+.pure-table th {
+ border-left: 1px solid #cbcbcb;
+ border-width: 0 0 0 1px;
+ font-size: inherit;
+ margin: 0;
+ overflow: visible;
+ padding: 0.5em 1em;
+}
+.pure-table thead {
+ background-color: #e0e0e0;
+ color: #000;
+ text-align: left;
+ vertical-align: bottom;
+}
+.pure-table td {
+ background-color: transparent;
+}
+.pure-table-odd td {
+ background-color: #f2f2f2;
+}
+.pure-table-striped tr:nth-child(2n-1) td {
+ background-color: #f2f2f2;
+}
+.pure-table-bordered td {
+ border-bottom: 1px solid #cbcbcb;
+}
+.pure-table-bordered tbody > tr:last-child > td {
+ border-bottom-width: 0;
+}
+.pure-table-horizontal td,
+.pure-table-horizontal th {
+ border-width: 0 0 1px 0;
+ border-bottom: 1px solid #cbcbcb;
+}
+.pure-table-horizontal tbody > tr:last-child > td {
+ border-bottom-width: 0;
+}
diff --git a/packages/backend/src/css/style.css b/packages/backend/src/css/style.css
new file mode 100644
index 0000000..e03b632
--- /dev/null
+++ b/packages/backend/src/css/style.css
@@ -0,0 +1,62 @@
+/*!
+ Pure v2.0.3
+ Copyright 2013 Yahoo!
+ Licensed under the BSD License.
+ https://github.com/pure-ss/pure/blob/master/LICENSE.md
+*/
+/*!
+ normalize.cs v | MIT License | git.io/normalize
+ Copyright (c) Nicolas Gallagher and Jonathan Neal
+*/
+/*! normalize.ss v8.0.1 | MIT License | github.com/necolas/normalize.cs */
+
+.talerbar {
+ text-align: center;
+}
+.tt {
+ font-family: "Lucida Console", Monaco, monospace;
+}
+.content {
+ overflow-x: auto;
+ padding-left: 15%;
+ padding-right: 15%;
+}
+.qr {
+ margin: auto;
+ text-align: center;
+}
+.qrtext {
+ width: max-content;
+ margin: auto;
+ transition: font-size 0.2s;
+ font-family: "Lucida Console", Monaco, monospace;
+ font-size: 0.5em;
+}
+.qrtext:hover {
+ font-size: 1em;
+}
+.talerbar {
+ margin: 0;
+ bottom: 0;
+ background-color: #033;
+ color: white;
+ width: 100%;
+ padding: 1em;
+ overflow: auto;
+}
+body {
+ overflow-y: scroll;
+}
+@media (min-width: 500px) {
+ .content {
+ padding-bottom: 2em;
+ margin-right: 1em;
+ overflow-y: auto;
+ }
+}
+#main a:link,
+#main a:visited,
+#main a:hover,
+#main a:active {
+ color: black;
+} \ No newline at end of file
diff --git a/packages/backend/src/custom.d.ts b/packages/backend/src/custom.d.ts
new file mode 100644
index 0000000..d270500
--- /dev/null
+++ b/packages/backend/src/custom.d.ts
@@ -0,0 +1,40 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/>
+ */
+declare module '*.po' {
+ const content: any;
+ export default content;
+}
+declare module 'jed' {
+ const x: any;
+ export = x;
+}
+declare module "*.jpeg" {
+ const content: any;
+ export default content;
+}
+declare module "*.png" {
+ const content: any;
+ export default content;
+}
+declare module '*.svg' {
+ const content: any;
+ export default content;
+}
+
+declare module '*.scss' {
+ const content: Record<string, string>;
+ export default content;
+}
diff --git a/packages/backend/src/declaration.d.ts b/packages/backend/src/declaration.d.ts
new file mode 100644
index 0000000..1722a3d
--- /dev/null
+++ b/packages/backend/src/declaration.d.ts
@@ -0,0 +1,1384 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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)
+*/
+
+
+type HashCode = string;
+type EddsaPublicKey = string;
+type EddsaSignature = string;
+type WireTransferIdentifierRawP = string;
+type RelativeTime = Duration;
+type ImageDataUrl = string;
+
+export interface WithId {
+ id: string
+}
+
+interface Timestamp {
+ // Milliseconds since epoch, or the special
+ // value "forever" to represent an event that will
+ // never happen.
+ t_ms: number | "never";
+}
+interface Duration {
+ // Duration in milliseconds or "forever"
+ // to represent an infinite duration.
+ d_ms: number | "forever";
+}
+
+interface WithId {
+ id: string;
+}
+
+type Amount = string;
+type UUID = string;
+type Integer = number;
+
+export namespace ExchangeBackend {
+ interface WireResponse {
+
+ // Master public key of the exchange, must match the key returned in /keys.
+ master_public_key: EddsaPublicKey;
+
+ // Array of wire accounts operated by the exchange for
+ // incoming wire transfers.
+ accounts: WireAccount[];
+
+ // Object mapping names of wire methods (i.e. "sepa" or "x-taler-bank")
+ // to wire fees.
+ fees: { method: AggregateTransferFee };
+ }
+ interface WireAccount {
+ // payto:// URI identifying the account and wire method
+ payto_uri: string;
+
+ // Signature using the exchange's offline key
+ // with purpose TALER_SIGNATURE_MASTER_WIRE_DETAILS.
+ master_sig: EddsaSignature;
+ }
+ interface AggregateTransferFee {
+ // Per transfer wire transfer fee.
+ wire_fee: Amount;
+
+ // Per transfer closing fee.
+ closing_fee: Amount;
+
+ // What date (inclusive) does this fee go into effect?
+ // The different fees must cover the full time period in which
+ // any of the denomination keys are valid without overlap.
+ start_date: Timestamp;
+
+ // What date (exclusive) does this fee stop going into effect?
+ // The different fees must cover the full time period in which
+ // any of the denomination keys are valid without overlap.
+ end_date: Timestamp;
+
+ // Signature of TALER_MasterWireFeePS with
+ // purpose TALER_SIGNATURE_MASTER_WIRE_FEES.
+ sig: EddsaSignature;
+ }
+
+}
+export namespace MerchantBackend {
+ interface ErrorDetail {
+
+ // Numeric error code unique to the condition.
+ // The other arguments are specific to the error value reported here.
+ code: number;
+
+ // Human-readable description of the error, i.e. "missing parameter", "commitment violation", ...
+ // Should give a human-readable hint about the error's nature. Optional, may change without notice!
+ hint?: string;
+
+ // Optional detail about the specific input value that failed. May change without notice!
+ detail?: string;
+
+ // Name of the parameter that was bogus (if applicable).
+ parameter?: string;
+
+ // Path to the argument that was bogus (if applicable).
+ path?: string;
+
+ // Offset of the argument that was bogus (if applicable).
+ offset?: string;
+
+ // Index of the argument that was bogus (if applicable).
+ index?: string;
+
+ // Name of the object that was bogus (if applicable).
+ object?: string;
+
+ // Name of the currency than was problematic (if applicable).
+ currency?: string;
+
+ // Expected type (if applicable).
+ type_expected?: string;
+
+ // Type that was provided instead (if applicable).
+ type_actual?: string;
+ }
+
+
+ // Delivery location, loosely modeled as a subset of
+ // ISO20022's PostalAddress25.
+ interface Tax {
+ // the name of the tax
+ name: string;
+
+ // amount paid in tax
+ tax: Amount;
+ }
+
+ interface Auditor {
+ // official name
+ name: string;
+
+ // Auditor's public key
+ auditor_pub: EddsaPublicKey;
+
+ // Base URL of the auditor
+ url: string;
+ }
+ interface Exchange {
+ // the exchange's base URL
+ url: string;
+
+ // master public key of the exchange
+ master_pub: EddsaPublicKey;
+ }
+
+ interface Product {
+ // merchant-internal identifier for the product.
+ product_id?: string;
+
+ // Human-readable product description.
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized descriptions
+ description_i18n?: { [lang_tag: string]: string };
+
+ // The number of units of the product to deliver to the customer.
+ quantity: Integer;
+
+ // The unit in which the product is measured (liters, kilograms, packages, etc.)
+ unit: string;
+
+ // The price of the product; this is the total price for quantity times unit of this product.
+ price: Amount;
+
+ // An optional base64-encoded product image
+ image: ImageDataUrl;
+
+ // a list of taxes paid by the merchant for this product. Can be empty.
+ taxes: Tax[];
+
+ // time indicating when this product should be delivered
+ delivery_date?: Timestamp;
+ }
+ interface Merchant {
+ // label for a location with the business address of the merchant
+ address: Location;
+
+ // the merchant's legal name of business
+ name: string;
+
+ // label for a location that denotes the jurisdiction for disputes.
+ // Some of the typical fields for a location (such as a street address) may be absent.
+ jurisdiction: Location;
+ }
+
+ interface VersionResponse {
+ // libtool-style representation of the Merchant protocol version, see
+ // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+ // The format is "current:revision:age".
+ version: string;
+
+ // Name of the protocol.
+ name: "taler-merchant";
+
+ // Currency supported by this backend.
+ currency: string;
+
+ }
+ interface Location {
+ // Nation with its own government.
+ country?: string;
+
+ // Identifies a subdivision of a country such as state, region, county.
+ country_subdivision?: string;
+
+ // Identifies a subdivision within a country sub-division.
+ district?: string;
+
+ // Name of a built-up area, with defined boundaries, and a local government.
+ town?: string;
+
+ // Specific location name within the town.
+ town_location?: string;
+
+ // Identifier consisting of a group of letters and/or numbers that
+ // is added to a postal address to assist the sorting of mail.
+ post_code?: string;
+
+ // Name of a street or thoroughfare.
+ street?: string;
+
+ // Name of the building or house.
+ building_name?: string;
+
+ // Number that identifies the position of a building on a street.
+ building_number?: string;
+
+ // Free-form address lines, should not exceed 7 elements.
+ address_lines?: string[];
+ }
+ namespace Instances {
+
+ //POST /private/instances/$INSTANCE/auth
+ interface InstanceAuthConfigurationMessage {
+ // Type of authentication.
+ // "external": The mechant backend does not do
+ // any authentication checks. Instead an API
+ // gateway must do the authentication.
+ // "token": The merchant checks an auth token.
+ // See "token" for details.
+ method: "external" | "token";
+
+ // For method "external", this field is mandatory.
+ // The token MUST begin with the string "secret-token:".
+ // After the auth token has been set (with method "token"),
+ // the value must be provided in a "Authorization: Bearer $token"
+ // header.
+ token?: string;
+
+ }
+ //POST /private/instances
+ interface InstanceConfigurationMessage {
+ // The URI where the wallet will send coins. A merchant may have
+ // multiple accounts, thus this is an array. Note that by
+ // removing URIs 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.
+ payto_uris: string[];
+
+ // Name of the merchant instance to create (will become $INSTANCE).
+ id: string;
+
+ // Merchant name corresponding to this instance.
+ name: string;
+
+ // "Authentication" header required to authorize management access the instance.
+ // Optional, if not given authentication will be disabled for
+ // this instance (hopefully authentication checks are still
+ // done by some reverse proxy).
+ auth: InstanceAuthConfigurationMessage;
+
+ // 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;
+
+ // 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;
+
+ // 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;
+
+ }
+
+ // PATCH /private/instances/$INSTANCE
+ interface InstanceReconfigurationMessage {
+ // The URI where the wallet will send coins. A merchant may have
+ // multiple accounts, thus this is an array. Note that by
+ // removing URIs from this list
+ payto_uris: string[];
+
+ // Merchant name corresponding to this instance.
+ name: string;
+
+ // 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;
+
+ // 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;
+
+ // 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;
+
+ }
+
+ // GET /private/instances
+ interface InstancesResponse {
+ // List of instances that are present in the backend (see Instance)
+ instances: Instance[];
+ }
+
+ interface Instance {
+ // Merchant name corresponding to this instance.
+ name: string;
+
+ deleted?: boolean;
+
+ // Merchant instance this response is about ($INSTANCE)
+ id: string;
+
+ // Public key of the merchant/instance, in Crockford Base32 encoding.
+ merchant_pub: EddsaPublicKey;
+
+ // List of the payment targets supported by this instance. Clients can
+ // specify the desired payment target in /order requests. Note that
+ // front-ends do not have to support wallets selecting payment targets.
+ payment_targets: string[];
+
+ }
+
+ //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;
+
+ // 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;
+
+ // 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;
+
+ // 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";
+ };
+ }
+
+ interface MerchantAccount {
+
+ // payto:// URI of the account.
+ payto_uri: string;
+
+ // Hash over the wire details (including over the salt)
+ h_wire: HashCode;
+
+ // salt used to compute h_wire
+ salt: HashCode;
+
+ // true if this account is active,
+ // false if it is historic.
+ active: boolean;
+ }
+
+ // DELETE /private/instances/$INSTANCE
+
+
+ }
+
+ namespace Products {
+ // POST /private/products
+ interface ProductAddDetail {
+
+ // product ID to use.
+ product_id: string;
+
+ // Human-readable product description.
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized descriptions
+ description_i18n: { [lang_tag: string]: string };
+
+ // unit in which the product is measured (liters, kilograms, packages, etc.)
+ unit: string;
+
+ // The price for one unit of the product. Zero is used
+ // to imply that this product is not sold separately, or
+ // that the price is not fixed, and must be supplied by the
+ // front-end. If non-zero, this price MUST include applicable
+ // taxes.
+ price: Amount;
+
+ // An optional base64-encoded product image
+ image: ImageDataUrl;
+
+ // a list of taxes paid by the merchant for one unit of this product
+ taxes: Tax[];
+
+ // Number of units of the product in stock in sum in total,
+ // including all existing sales ever. Given in product-specific
+ // units.
+ // A value of -1 indicates "infinite" (i.e. for "electronic" books).
+ total_stock: Integer;
+
+ // Identifies where the product is in stock.
+ address: Location;
+
+ // Identifies when we expect the next restocking to happen.
+ next_restock?: Timestamp;
+
+ }
+ // PATCH /private/products/$PRODUCT_ID
+ interface ProductPatchDetail {
+
+ // Human-readable product description.
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized descriptions
+ description_i18n: { [lang_tag: string]: string };
+
+ // unit in which the product is measured (liters, kilograms, packages, etc.)
+ unit: string;
+
+ // The price for one unit of the product. Zero is used
+ // to imply that this product is not sold separately, or
+ // that the price is not fixed, and must be supplied by the
+ // front-end. If non-zero, this price MUST include applicable
+ // taxes.
+ price: Amount;
+
+ // An optional base64-encoded product image
+ image: ImageDataUrl;
+
+ // a list of taxes paid by the merchant for one unit of this product
+ taxes: Tax[];
+
+ // Number of units of the product in stock in sum in total,
+ // including all existing sales ever. Given in product-specific
+ // units.
+ // A value of -1 indicates "infinite" (i.e. for "electronic" books).
+ total_stock: Integer;
+
+ // Number of units of the product that were lost (spoiled, stolen, etc.)
+ total_lost: Integer;
+
+ // Identifies where the product is in stock.
+ address: Location;
+
+ // Identifies when we expect the next restocking to happen.
+ next_restock?: Timestamp;
+
+ }
+
+ // GET /private/products
+ interface InventorySummaryResponse {
+ // List of products that are present in the inventory
+ products: InventoryEntry[];
+ }
+ interface InventoryEntry {
+ // Product identifier, as found in the product.
+ product_id: string;
+
+ }
+
+ // GET /private/products/$PRODUCT_ID
+ interface ProductDetail {
+
+ // Human-readable product description.
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized descriptions
+ description_i18n: { [lang_tag: string]: string };
+
+ // unit in which the product is measured (liters, kilograms, packages, etc.)
+ unit: string;
+
+ // The price for one unit of the product. Zero is used
+ // to imply that this product is not sold separately, or
+ // that the price is not fixed, and must be supplied by the
+ // front-end. If non-zero, this price MUST include applicable
+ // taxes.
+ price: Amount;
+
+ // An optional base64-encoded product image
+ image: ImageDataUrl;
+
+ // a list of taxes paid by the merchant for one unit of this product
+ taxes: Tax[];
+
+ // Number of units of the product in stock in sum in total,
+ // including all existing sales ever. Given in product-specific
+ // units.
+ // A value of -1 indicates "infinite" (i.e. for "electronic" books).
+ total_stock: Integer;
+
+ // Number of units of the product that have already been sold.
+ total_sold: Integer;
+
+ // Number of units of the product that were lost (spoiled, stolen, etc.)
+ total_lost: Integer;
+
+ // Identifies where the product is in stock.
+ address: Location;
+
+ // Identifies when we expect the next restocking to happen.
+ next_restock?: Timestamp;
+
+ }
+
+ // POST /private/products/$PRODUCT_ID/lock
+ interface LockRequest {
+
+ // UUID that identifies the frontend performing the lock
+ // It is suggested that clients use a timeflake for this,
+ // see https://github.com/anthonynsimon/timeflake
+ lock_uuid: UUID;
+
+ // How long does the frontend intend to hold the lock
+ duration: RelativeTime;
+
+ // How many units should be locked?
+ quantity: Integer;
+
+ }
+
+ // DELETE /private/products/$PRODUCT_ID
+
+ }
+
+ namespace Orders {
+
+ type MerchantOrderStatusResponse = CheckPaymentPaidResponse |
+ CheckPaymentClaimedResponse |
+ CheckPaymentUnpaidResponse;
+ interface CheckPaymentPaidResponse {
+ // The customer paid for this contract.
+ order_status: "paid";
+
+ // Was the payment refunded (even partially)?
+ refunded: boolean;
+
+ // True if there are any approved refunds that the wallet has
+ // not yet obtained.
+ refund_pending: boolean;
+
+ // Did the exchange wire us the funds?
+ wired: boolean;
+
+ // Total amount the exchange deposited into our bank account
+ // for this contract, excluding fees.
+ deposit_total: Amount;
+
+ // Numeric error code indicating errors the exchange
+ // encountered tracking the wire transfer for this purchase (before
+ // we even got to specific coin issues).
+ // 0 if there were no issues.
+ exchange_ec: number;
+
+ // HTTP status code returned by the exchange when we asked for
+ // information to track the wire transfer for this purchase.
+ // 0 if there were no issues.
+ exchange_hc: number;
+
+ // Total amount that was refunded, 0 if refunded is false.
+ refund_amount: Amount;
+
+ // Contract terms.
+ contract_terms: ContractTerms;
+
+ // The wire transfer status from the exchange for this order if
+ // available, otherwise empty array.
+ wire_details: TransactionWireTransfer[];
+
+ // Reports about trouble obtaining wire transfer details,
+ // empty array if no trouble were encountered.
+ wire_reports: TransactionWireReport[];
+
+ // The refund details for this order. One entry per
+ // refunded coin; empty array if there are no refunds.
+ refund_details: RefundDetails[];
+
+ // Status URL, can be used as a redirect target for the browser
+ // to show the order QR code / trigger the wallet.
+ order_status_url: string;
+ }
+ interface CheckPaymentClaimedResponse {
+ // A wallet claimed the order, but did not yet pay for the contract.
+ order_status: "claimed";
+
+ // Contract terms.
+ contract_terms: ContractTerms;
+
+ }
+ interface CheckPaymentUnpaidResponse {
+ // The order was neither claimed nor paid.
+ order_status: "unpaid";
+
+ // when was the order created
+ creation_time: Timestamp;
+
+ // Order summary text.
+ summary: string;
+
+ // Total amount of the order (to be paid by the customer).
+ total_amount: Amount;
+
+ // URI that the wallet must process to complete the payment.
+ taler_pay_uri: string;
+
+ // Alternative order ID which was paid for already in the same session.
+ // Only given if the same product was purchased before in the same session.
+ already_paid_order_id?: string;
+
+ // Fulfillment URL of an already paid order. Only given if under this
+ // session an already paid order with a fulfillment URL exists.
+ already_paid_fulfillment_url?: string;
+
+ // Status URL, can be used as a redirect target for the browser
+ // to show the order QR code / trigger the wallet.
+ order_status_url: string;
+
+ // We do we NOT return the contract terms here because they may not
+ // exist in case the wallet did not yet claim them.
+ }
+ interface RefundDetails {
+ // Reason given for the refund.
+ reason: string;
+
+ // When was the refund approved.
+ timestamp: Timestamp;
+
+ // Total amount that was refunded (minus a refund fee).
+ amount: Amount;
+ }
+ interface TransactionWireTransfer {
+ // Responsible exchange.
+ exchange_url: string;
+
+ // 32-byte wire transfer identifier.
+ wtid: Base32;
+
+ // Execution time of the wire transfer.
+ execution_time: Timestamp;
+
+ // Total amount that has been wire transferred
+ // to the merchant.
+ amount: Amount;
+
+ // Was this transfer confirmed by the merchant via the
+ // POST /transfers API, or is it merely claimed by the exchange?
+ confirmed: boolean;
+ }
+ interface TransactionWireReport {
+ // Numerical error code.
+ code: number;
+
+ // Human-readable error description.
+ hint: string;
+
+ // Numerical error code from the exchange.
+ exchange_ec: number;
+
+ // HTTP status code received from the exchange.
+ exchange_hc: number;
+
+ // Public key of the coin for which we got the exchange error.
+ coin_pub: CoinPublicKey;
+ }
+
+ interface OrderHistory {
+ // timestamp-sorted array of all orders matching the query.
+ // The order of the sorting depends on the sign of delta.
+ orders: OrderHistoryEntry[];
+ }
+ interface OrderHistoryEntry {
+
+ // order ID of the transaction related to this entry.
+ order_id: string;
+
+ // row ID of the order in the database
+ row_id: number;
+
+ // when the order was created
+ timestamp: Timestamp;
+
+ // the amount of money the order is for
+ amount: Amount;
+
+ // the summary of the order
+ summary: string;
+
+ // whether some part of the order is refundable,
+ // that is the refund deadline has not yet expired
+ // and the total amount refunded so far is below
+ // the value of the original transaction.
+ refundable: boolean;
+
+ // whether the order has been paid or not
+ paid: boolean;
+ }
+
+ interface PostOrderRequest {
+ // The order must at least contain the minimal
+ // order detail, but can override all
+ order: Order;
+
+ // if set, the backend will then set the refund deadline to the current
+ // time plus the specified delay. If it's not set, refunds will not be
+ // possible.
+ refund_delay?: RelativeTime;
+
+ // specifies the payment target preferred by the client. Can be used
+ // to select among the various (active) wire methods supported by the instance.
+ payment_target?: string;
+
+ // specifies that some products are to be included in the
+ // order from the inventory. For these inventory management
+ // is performed (so the products must be in stock) and
+ // details are completed from the product data of the backend.
+ inventory_products?: MinimalInventoryProduct[];
+
+ // Specifies a lock identifier that was used to
+ // lock a product in the inventory. Only useful if
+ // manage_inventory is set. Used in case a frontend
+ // reserved quantities of the individual products while
+ // the shopping card was being built. Multiple UUIDs can
+ // be used in case different UUIDs were used for different
+ // products (i.e. in case the user started with multiple
+ // shopping sessions that were combined during checkout).
+ lock_uuids?: UUID[];
+
+ // Should a token for claiming the order be generated?
+ // False can make sense if the ORDER_ID is sufficiently
+ // high entropy to prevent adversarial claims (like it is
+ // if the backend auto-generates one). Default is 'true'.
+ create_token?: boolean;
+
+ }
+ type Order = MinimalOrderDetail | ContractTerms;
+
+ interface MinimalOrderDetail {
+ // Amount to be paid by the customer
+ amount: Amount;
+
+ // Short summary of the order
+ summary: string;
+
+ // URL that will show that the order was successful after
+ // it has been paid for. Optional. When POSTing to the
+ // merchant, the placeholder "${ORDER_ID}" will be
+ // replaced with the actual order ID (useful if the
+ // order ID is generated server-side and needs to be
+ // in the URL).
+ fulfillment_url?: string;
+ }
+
+ interface MinimalInventoryProduct {
+ // Which product is requested (here mandatory!)
+ product_id: string;
+
+ // How many units of the product are requested
+ quantity: Integer;
+ }
+ interface PostOrderResponse {
+ // Order ID of the response that was just created
+ order_id: string;
+
+ // Token that authorizes the wallet to claim the order.
+ // Provided only if "create_token" was set to 'true'
+ // in the request.
+ token?: ClaimToken;
+ }
+ interface OutOfStockResponse {
+
+ // Product ID of an out-of-stock item
+ product_id: string;
+
+ // Requested quantity
+ requested_quantity: Integer;
+
+ // Available quantity (must be below requested_quanitity)
+ available_quantity: Integer;
+
+ // When do we expect the product to be again in stock?
+ // Optional, not given if unknown.
+ restock_expected?: Timestamp;
+ }
+
+ interface ForgetRequest {
+
+ // Array of valid JSON paths to forgettable fields in the order's
+ // contract terms.
+ fields: string[];
+ }
+ interface RefundRequest {
+ // Amount to be refunded
+ refund: Amount;
+
+ // Human-readable refund justification
+ reason: string;
+ }
+ interface MerchantRefundResponse {
+
+ // URL (handled by the backend) that the wallet should access to
+ // trigger refund processing.
+ // taler://refund/...
+ taler_refund_uri: string;
+
+ // Contract hash that a client may need to authenticate an
+ // HTTP request to obtain the above URI in a wallet-friendly way.
+ h_contract: HashCode;
+ }
+
+ }
+
+ namespace Tips {
+
+ // GET /private/reserves
+ interface TippingReserveStatus {
+ // Array of all known reserves (possibly empty!)
+ reserves: ReserveStatusEntry[];
+ }
+ interface ReserveStatusEntry {
+ // Public key of the reserve
+ reserve_pub: EddsaPublicKey;
+
+ // Timestamp when it was established
+ creation_time: Timestamp;
+
+ // Timestamp when it expires
+ expiration_time: Timestamp;
+
+ // Initial amount as per reserve creation call
+ merchant_initial_amount: Amount;
+
+ // Initial amount as per exchange, 0 if exchange did
+ // not confirm reserve creation yet.
+ exchange_initial_amount: Amount;
+
+ // Amount picked up so far.
+ pickup_amount: Amount;
+
+ // Amount approved for tips that exceeds the pickup_amount.
+ committed_amount: Amount;
+
+ // Is this reserve active (false if it was deleted but not purged)
+ active: boolean;
+ }
+
+ interface ReserveCreateRequest {
+ // Amount that the merchant promises to put into the reserve
+ initial_balance: Amount;
+
+ // Exchange the merchant intends to use for tipping
+ exchange_url: string;
+
+ // Desired wire method, for example "iban" or "x-taler-bank"
+ wire_method: string;
+ }
+ interface ReserveCreateConfirmation {
+ // Public key identifying the reserve
+ reserve_pub: EddsaPublicKey;
+
+ // Wire account of the exchange where to transfer the funds
+ payto_uri: string;
+ }
+ interface TipCreateRequest {
+ // Amount that the customer should be tipped
+ amount: Amount;
+
+ // Justification for giving the tip
+ justification: string;
+
+ // URL that the user should be directed to after tipping,
+ // will be included in the tip_token.
+ next_url: string;
+ }
+ interface TipCreateConfirmation {
+ // Unique tip identifier for the tip that was created.
+ tip_id: HashCode;
+
+ // taler://tip URI for the tip
+ taler_tip_uri: string;
+
+ // URL that will directly trigger processing
+ // the tip when the browser is redirected to it
+ tip_status_url: string;
+
+ // when does the tip expire
+ tip_expiration: Timestamp;
+ }
+
+ interface ReserveDetail {
+ // Timestamp when it was established.
+ creation_time: Timestamp;
+
+ // Timestamp when it expires.
+ expiration_time: Timestamp;
+
+ // Initial amount as per reserve creation call.
+ merchant_initial_amount: Amount;
+
+ // Initial amount as per exchange, 0 if exchange did
+ // not confirm reserve creation yet.
+ exchange_initial_amount: Amount;
+
+ // Amount picked up so far.
+ pickup_amount: Amount;
+
+ // Amount approved for tips that exceeds the pickup_amount.
+ committed_amount: Amount;
+
+ // Array of all tips created by this reserves (possibly empty!).
+ // Only present if asked for explicitly.
+ tips?: TipStatusEntry[];
+
+ // Is this reserve active (false if it was deleted but not purged)?
+ active: boolean;
+
+ // URI to use to fill the reserve, can be NULL
+ // if the reserve is inactive or was already filled
+ payto_uri: string;
+
+ // URL of the exchange hosting the reserve,
+ // NULL if the reserve is inactive
+ exchange_url: string;
+
+ }
+
+ interface TipStatusEntry {
+
+ // Unique identifier for the tip.
+ tip_id: HashCode;
+
+ // Total amount of the tip that can be withdrawn.
+ total_amount: Amount;
+
+ // Human-readable reason for why the tip was granted.
+ reason: string;
+ }
+
+ interface TipDetails {
+ // Amount that we authorized for this tip.
+ total_authorized: Amount;
+
+ // Amount that was picked up by the user already.
+ total_picked_up: Amount;
+
+ // Human-readable reason given when authorizing the tip.
+ reason: string;
+
+ // Timestamp indicating when the tip is set to expire (may be in the past).
+ expiration: Timestamp;
+
+ // Reserve public key from which the tip is funded.
+ reserve_pub: EddsaPublicKey;
+
+ // Array showing the pickup operations of the wallet (possibly empty!).
+ // Only present if asked for explicitly.
+ pickups?: PickupDetail[];
+ }
+ interface PickupDetail {
+ // Unique identifier for the pickup operation.
+ pickup_id: HashCode;
+
+ // Number of planchets involved.
+ num_planchets: Integer;
+
+ // Total amount requested for this pickup_id.
+ requested_amount: Amount;
+ }
+
+ }
+
+ namespace Transfers {
+
+ interface TransferList {
+ // list of all the transfers that fit the filter that we know
+ transfers: TransferDetails[];
+ }
+ interface TransferDetails {
+ // how much was wired to the merchant (minus fees)
+ credit_amount: Amount;
+
+ // raw wire transfer identifier identifying the wire transfer (a base32-encoded value)
+ wtid: string;
+
+ // target account that received the wire transfer
+ payto_uri: string;
+
+ // base URL of the exchange that made the wire transfer
+ exchange_url: string;
+
+ // Serial number identifying the transfer in the merchant backend.
+ // Used for filgering via offset.
+ transfer_serial_id: number;
+
+ // Time of the execution of the wire transfer by the exchange, according to the exchange
+ // Only provided if we did get an answer from the exchange.
+ execution_time?: Timestamp;
+
+ // True if we checked the exchange's answer and are happy with it.
+ // False if we have an answer and are unhappy, missing if we
+ // do not have an answer from the exchange.
+ verified?: boolean;
+
+ // True if the merchant uses the POST /transfers API to confirm
+ // that this wire transfer took place (and it is thus not
+ // something merely claimed by the exchange).
+ confirmed?: boolean;
+ }
+
+ interface TransferInformation {
+ // how much was wired to the merchant (minus fees)
+ credit_amount: Amount;
+
+ // raw wire transfer identifier identifying the wire transfer (a base32-encoded value)
+ wtid: WireTransferIdentifierRawP;
+
+ // target account that received the wire transfer
+ payto_uri: string;
+
+ // base URL of the exchange that made the wire transfer
+ exchange_url: string;
+ }
+ interface MerchantTrackTransferResponse {
+ // Total amount transferred
+ total: Amount;
+
+ // Applicable wire fee that was charged
+ wire_fee: Amount;
+
+ // Time of the execution of the wire transfer by the exchange, according to the exchange
+ execution_time: Timestamp;
+
+ // details about the deposits
+ deposits_sums: MerchantTrackTransferDetail[];
+ }
+ interface MerchantTrackTransferDetail {
+ // Business activity associated with the wire transferred amount
+ // deposit_value.
+ order_id: string;
+
+ // The total amount the exchange paid back for order_id.
+ deposit_value: Amount;
+
+ // applicable fees for the deposit
+ deposit_fee: Amount;
+ }
+
+ type ExchangeConflictDetails = WireFeeConflictDetails | TrackTransferConflictDetails
+ // Note: this is not the full 'proof' of missbehavior, as
+ // the bogus message from the exchange with a signature
+ // over the 'different' wire fee is missing.
+ //
+ // This information is NOT provided by the current implementation,
+ // because this would be quite expensive to generate and is
+ // hardly needed _here_. Once we add automated reports for
+ // the Taler auditor, we need to generate this data anyway
+ // and should probably return it here as well.
+ interface WireFeeConflictDetails {
+ // Numerical error code:
+ code: "TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_BAD_WIRE_FEE";
+
+ // Text describing the issue for humans.
+ hint: string;
+
+
+ // Wire fee (wrongly) charged by the exchange, breaking the
+ // contract affirmed by the exchange_sig.
+ wire_fee: Amount;
+
+ // Timestamp of the wire transfer
+ execution_time: Timestamp;
+
+ // The expected wire fee (as signed by the exchange)
+ expected_wire_fee: Amount;
+
+ // Expected closing fee (needed to verify signature)
+ expected_closing_fee: Amount;
+
+ // Start date of the expected fee structure
+ start_date: Timestamp;
+
+ // End date of the expected fee structure
+ end_date: Timestamp;
+
+ // Signature of the exchange affirming the expected fee structure
+ master_sig: EddsaSignature;
+
+ // Master public key of the exchange
+ master_pub: EddsaPublicKey;
+ }
+ interface TrackTransferConflictDetails {
+ // Numerical error code
+ code: "TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_REPORTS";
+
+ // Text describing the issue for humans.
+ hint: string;
+
+ // Offset in the exchange_transfer where the
+ // exchange's response fails to match the exchange_deposit_proof.
+ conflict_offset: number;
+
+ // The response from the exchange which tells us when the
+ // coin was returned to us, except that it does not match
+ // the expected value of the coin.
+ //
+ // This field is NOT provided by the current implementation,
+ // because this would be quite expensive to generate and is
+ // hardly needed _here_. Once we add automated reports for
+ // the Taler auditor, we need to generate this data anyway
+ // and should probably return it here as well.
+ // exchange_transfer?: TrackTransferResponse;
+
+ // Public key of the exchange used to sign the response to
+ // our deposit request.
+ deposit_exchange_pub: EddsaPublicKey;
+
+ // Signature of the exchange signing the (conflicting) response.
+ // Signs over a struct TALER_DepositConfirmationPS.
+ deposit_exchange_sig: EddsaSignature;
+
+ // Hash of the merchant's bank account the wire transfer went to
+ h_wire: HashCode;
+
+ // Hash of the contract terms with the conflicting deposit.
+ h_contract_terms: HashCode;
+
+ // At what time the exchange received the deposit. Needed
+ // to verify the \exchange_sig\.
+ deposit_timestamp: Timestamp;
+
+ // At what time the refund possibility expired (needed to verify exchange_sig).
+ refund_deadline: Timestamp;
+
+ // Public key of the coin for which we have conflicting information.
+ coin_pub: EddsaPublicKey;
+
+ // Amount the exchange counted the coin for in the transfer.
+ amount_with_fee: Amount;
+
+ // Expected value of the coin.
+ coin_value: Amount;
+
+ // Expected deposit fee of the coin.
+ coin_fee: Amount;
+
+ // Expected deposit fee of the coin.
+ deposit_fee: Amount;
+
+ }
+
+ // interface TrackTransferProof {
+ // // signature from the exchange made with purpose
+ // // TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT
+ // exchange_sig: EddsaSignature;
+
+ // // public EdDSA key of the exchange that was used to generate the signature.
+ // // Should match one of the exchange's signing keys from /keys. Again given
+ // // explicitly as the client might otherwise be confused by clock skew as to
+ // // which signing key was used.
+ // exchange_pub: EddsaSignature;
+
+ // // hash of the wire details (identical for all deposits)
+ // // Needed to check the exchange_sig
+ // h_wire: HashCode;
+ // }
+
+ }
+
+
+ interface ContractTerms {
+ // Human-readable description of the whole purchase
+ summary: string;
+
+ // Map from IETF BCP 47 language tags to localized summaries
+ summary_i18n?: { [lang_tag: string]: string };
+
+ // Unique, free-form identifier for the proposal.
+ // Must be unique within a merchant instance.
+ // For merchants that do not store proposals in their DB
+ // before the customer paid for them, the order_id can be used
+ // by the frontend to restore a proposal from the information
+ // encoded in it (such as a short product identifier and timestamp).
+ order_id: string;
+
+ // Total price for the transaction.
+ // The exchange will subtract deposit fees from that amount
+ // before transferring it to the merchant.
+ amount: Amount;
+
+ // The URL for this purchase. Every time is is visited, the merchant
+ // will send back to the customer the same proposal. Clearly, this URL
+ // can be bookmarked and shared by users.
+ fulfillment_url?: string;
+
+ // 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[];
+
+ // Time when this contract was generated
+ timestamp: Timestamp;
+
+ // After this deadline has passed, no refunds will be accepted.
+ refund_deadline: Timestamp;
+
+ // After this deadline, the merchant won't accept payments for the contact
+ pay_deadline: Timestamp;
+
+ // Transfer deadline for the exchange. Must be in the
+ // deposit permissions of coins used to pay for this order.
+ wire_transfer_deadline: Timestamp;
+
+ // Merchant's public key used to sign this proposal; this information
+ // is typically added by the backend Note that this can be an ephemeral key.
+ merchant_pub: EddsaPublicKey;
+
+ // Base URL of the (public!) merchant backend API.
+ // Must be an absolute URL that ends with a slash.
+ merchant_base_url: string;
+
+ // More info about the merchant, see below
+ merchant: Merchant;
+
+ // The hash of the merchant instance's wire details.
+ h_wire: HashCode;
+
+ // Wire transfer method identifier for the wire method associated with h_wire.
+ // The wallet may only select exchanges via a matching auditor if the
+ // exchange also supports this wire method.
+ // The wire transfer fees must be added based on this wire transfer method.
+ wire_method: string;
+
+ // Any exchanges audited by these auditors are accepted by the merchant.
+ auditors: Auditor[];
+
+ // Exchanges that the merchant accepts even if it does not accept any auditors that audit them.
+ exchanges: Exchange[];
+
+ // Delivery location for (all!) products.
+ delivery_location?: Location;
+
+ // Time indicating when the order should be delivered.
+ // May be overwritten by individual products.
+ delivery_date?: Timestamp;
+
+ // Nonce generated by the wallet and echoed by the merchant
+ // in this field when the proposal is generated.
+ nonce: string;
+
+ // Specifies for how long the wallet should try to get an
+ // automatic refund for the purchase. If this field is
+ // present, the wallet should wait for a few seconds after
+ // the purchase and then automatically attempt to obtain
+ // a refund. The wallet should probe until "delay"
+ // after the payment was successful (i.e. via long polling
+ // or via explicit requests with exponential back-off).
+ //
+ // In particular, if the wallet is offline
+ // at that time, it MUST repeat the request until it gets
+ // one response from the merchant after the delay has expired.
+ // If the refund is granted, the wallet MUST automatically
+ // recover the payment. This is used in case a merchant
+ // knows that it might be unable to satisfy the contract and
+ // desires for the wallet to attempt to get the refund without any
+ // customer interaction. Note that it is NOT an error if the
+ // merchant does not grant a refund.
+ auto_refund?: RelativeTime;
+
+ // Extra data that is only interpreted by the merchant frontend.
+ // Useful when the merchant needs to store extra information on a
+ // contract without storing it separately in their database.
+ extra?: any;
+ }
+
+}
diff --git a/packages/backend/src/hooks/async.ts b/packages/backend/src/hooks/async.ts
new file mode 100644
index 0000000..fd55004
--- /dev/null
+++ b/packages/backend/src/hooks/async.ts
@@ -0,0 +1,76 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { useState } from "preact/hooks";
+import { cancelPendingRequest } from "./backend";
+
+export interface Options {
+ slowTolerance: number,
+}
+
+export interface AsyncOperationApi<T> {
+ request: (...a: any) => void,
+ cancel: () => void,
+ data: T | undefined,
+ isSlow: boolean,
+ isLoading: boolean,
+ error: string | undefined
+}
+
+export function useAsync<T>(fn?: (...args: any) => Promise<T>, { slowTolerance: tooLong }: Options = { slowTolerance: 1000 }): AsyncOperationApi<T> {
+ const [data, setData] = useState<T | undefined>(undefined);
+ const [isLoading, setLoading] = useState<boolean>(false);
+ const [error, setError] = useState<any>(undefined);
+ const [isSlow, setSlow] = useState(false)
+
+ const request = async (...args: any) => {
+ if (!fn) return;
+ setLoading(true);
+
+ const handler = setTimeout(() => {
+ setSlow(true)
+ }, tooLong)
+
+ try {
+ const result = await fn(...args);
+ setData(result);
+ } catch (error) {
+ setError(error);
+ }
+ setLoading(false);
+ setSlow(false)
+ clearTimeout(handler)
+ };
+
+ function cancel() {
+ cancelPendingRequest()
+ setLoading(false);
+ setSlow(false)
+ }
+
+ return {
+ request,
+ cancel,
+ data,
+ isSlow,
+ isLoading,
+ error
+ };
+}
diff --git a/packages/backend/src/hooks/backend.ts b/packages/backend/src/hooks/backend.ts
new file mode 100644
index 0000000..96b8f71
--- /dev/null
+++ b/packages/backend/src/hooks/backend.ts
@@ -0,0 +1,262 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { mutate, cache } from 'swr';
+import axios, { AxiosError, AxiosResponse } from 'axios'
+import { MerchantBackend } from '../declaration';
+import { useBackendContext } from '../context/backend';
+import { useEffect, useState } from 'preact/hooks';
+import { DEFAULT_REQUEST_TIMEOUT } from '../utils/constants';
+
+export function mutateAll(re: RegExp, value?: unknown): Array<Promise<any>> {
+ return cache.keys().filter(key => {
+ return re.test(key)
+ }).map(key => {
+ return mutate(key, value)
+ })
+}
+
+export type HttpResponse<T> = HttpResponseOk<T> | HttpResponseLoading<T> | HttpError;
+export type HttpResponsePaginated<T> = HttpResponseOkPaginated<T> | HttpResponseLoading<T> | HttpError;
+
+export interface RequestInfo {
+ url: string;
+ hasToken: boolean;
+ params: unknown;
+ data: unknown;
+}
+
+interface HttpResponseLoading<T> {
+ ok?: false;
+ loading: true;
+ clientError?: false;
+ serverError?: false;
+
+ data?: T;
+}
+export interface HttpResponseOk<T> {
+ ok: true;
+ loading?: false;
+ clientError?: false;
+ serverError?: false;
+
+ data: T;
+ info?: RequestInfo;
+}
+
+export type HttpResponseOkPaginated<T> = HttpResponseOk<T> & WithPagination
+
+export interface WithPagination {
+ loadMore: () => void;
+ loadMorePrev: () => void;
+ isReachingEnd?: boolean;
+ isReachingStart?: boolean;
+}
+
+export type HttpError = HttpResponseClientError | HttpResponseServerError | HttpResponseUnexpectedError;
+export interface SwrError {
+ info: unknown,
+ status: number,
+ message: string,
+}
+export interface HttpResponseServerError {
+ ok?: false;
+ loading?: false;
+ clientError?: false;
+ serverError: true;
+
+ error?: MerchantBackend.ErrorDetail;
+ status: number;
+ message: string;
+ info?: RequestInfo;
+}
+interface HttpResponseClientError {
+ ok?: false;
+ loading?: false;
+ clientError: true;
+ serverError?: false;
+
+ info?: RequestInfo;
+ isUnauthorized: boolean;
+ isNotfound: boolean;
+ status: number;
+ error?: MerchantBackend.ErrorDetail;
+ message: string;
+
+}
+
+interface HttpResponseUnexpectedError {
+ ok?: false;
+ loading?: false;
+ clientError?: false;
+ serverError?: false;
+
+ info?: RequestInfo;
+ status?: number;
+ error: unknown;
+ message: string;
+}
+
+type Methods = 'get' | 'post' | 'patch' | 'delete' | 'put';
+
+interface RequestOptions {
+ method?: Methods;
+ token?: string;
+ data?: unknown;
+ params?: unknown;
+}
+
+function buildRequestOk<T>(res: AxiosResponse<T>, url: string, hasToken: boolean): HttpResponseOk<T> {
+ return {
+ ok: true, data: res.data, info: {
+ params: res.config.params,
+ data: res.config.data,
+ url,
+ hasToken,
+ }
+ }
+}
+
+// function buildResponse<T>(data?: T, error?: MerchantBackend.ErrorDetail, isValidating?: boolean): HttpResponse<T> {
+// if (isValidating) return {loading: true}
+// if (error) return buildRequestFailed()
+// }
+
+function buildRequestFailed(ex: AxiosError<MerchantBackend.ErrorDetail>, url: string, hasToken: boolean): HttpResponseClientError | HttpResponseServerError | HttpResponseUnexpectedError {
+ const status = ex.response?.status
+
+ const info: RequestInfo = {
+ data: ex.request?.data,
+ params: ex.request?.params,
+ url,
+ hasToken,
+ };
+
+ if (status && status >= 400 && status < 500) {
+ const error: HttpResponseClientError = {
+ clientError: true,
+ isNotfound: status === 404,
+ isUnauthorized: status === 401,
+ status,
+ info,
+ message: ex.response?.data?.hint || ex.message,
+ error: ex.response?.data
+ }
+ return error
+ }
+ if (status && status >= 500 && status < 600) {
+ const error: HttpResponseServerError = {
+ serverError: true,
+ status,
+ info,
+ message: `${ex.response?.data?.hint} (code ${ex.response?.data?.code})` || ex.message,
+ error: ex.response?.data
+ }
+ return error;
+ }
+
+ const error: HttpResponseUnexpectedError = {
+ info,
+ status,
+ error: ex,
+ message: ex.message
+ }
+
+ return error
+}
+
+
+const CancelToken = axios.CancelToken;
+let source = CancelToken.source();
+
+export function cancelPendingRequest() {
+ source.cancel('canceled by the user')
+ source = CancelToken.source()
+}
+
+let removeAxiosCancelToken = false
+/**
+ * Jest mocking seems to break when using the cancelToken property.
+ * Using this workaround when testing while finding the correct solution
+ */
+export function setAxiosRequestAsTestingEnvironment() {
+ removeAxiosCancelToken = true
+}
+
+export async function request<T>(url: string, options: RequestOptions = {}): Promise<HttpResponseOk<T>> {
+ const headers = options.token ? { Authorization: `Bearer ${options.token}` } : undefined
+
+ try {
+ const res = await axios({
+ url,
+ responseType: 'json',
+ headers,
+ cancelToken: !removeAxiosCancelToken? source.token : undefined,
+ method: options.method || 'get',
+ data: options.data,
+ params: options.params,
+ timeout: DEFAULT_REQUEST_TIMEOUT * 1000,
+ })
+ return buildRequestOk<T>(res, url, !!options.token)
+ } catch (e) {
+ const error = buildRequestFailed(e, url, !!options.token)
+ throw error
+ }
+
+}
+
+export function fetcher<T>(url: string, token: string, backend: string): Promise<HttpResponseOk<T>> {
+ return request<T>(`${backend}${url}`, { token })
+}
+
+export function useBackendInstancesTestForAdmin(): HttpResponse<MerchantBackend.Instances.InstancesResponse> {
+ const { url, token } = useBackendContext()
+
+ type Type = MerchantBackend.Instances.InstancesResponse;
+
+ const [result, setResult] = useState<HttpResponse<Type>>({ loading: true })
+
+ useEffect(() => {
+ request<Type>(`${url}/management/instances`, { token })
+ .then(data => setResult(data))
+ .catch(error => setResult(error))
+ }, [url, token])
+
+
+ return result
+}
+
+
+export function useBackendConfig(): HttpResponse<MerchantBackend.VersionResponse> {
+ const { url, token } = useBackendContext()
+
+ type Type = MerchantBackend.VersionResponse;
+
+ const [result, setResult] = useState<HttpResponse<Type>>({ loading: true })
+
+ useEffect(() => {
+ request<Type>(`${url}/config`, { token })
+ .then(data => setResult(data))
+ .catch(error => setResult(error))
+ }, [url, token])
+
+ return result
+}
diff --git a/packages/backend/src/hooks/index.ts b/packages/backend/src/hooks/index.ts
new file mode 100644
index 0000000..19d672a
--- /dev/null
+++ b/packages/backend/src/hooks/index.ts
@@ -0,0 +1,110 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { StateUpdater, useCallback, useState } from "preact/hooks";
+import { ValueOrFunction } from '../utils/types';
+
+
+const calculateRootPath = () => {
+ const rootPath = typeof window !== undefined ? window.location.origin + window.location.pathname : '/'
+ return rootPath
+}
+
+export function useBackendURL(url?: string): [string, boolean, StateUpdater<string>, () => void] {
+ const [value, setter] = useNotNullLocalStorage('backend-url', url || calculateRootPath())
+ const [triedToLog, setTriedToLog] = useLocalStorage('tried-login')
+
+ const checkedSetter = (v: ValueOrFunction<string>) => {
+ setTriedToLog('yes')
+ return setter(p => (v instanceof Function ? v(p) : v).replace(/\/$/, ''))
+ }
+
+ const resetBackend = () => {
+ setTriedToLog(undefined)
+ }
+ return [value, !!triedToLog, checkedSetter, resetBackend]
+}
+
+export function useBackendDefaultToken(): [string | undefined, StateUpdater<string | undefined>] {
+ return useLocalStorage('backend-token')
+}
+
+export function useBackendInstanceToken(id: string): [string | undefined, StateUpdater<string | undefined>] {
+ const [token, setToken] = useLocalStorage(`backend-token-${id}`)
+ const [defaultToken, defaultSetToken] = useBackendDefaultToken()
+
+ // instance named 'default' use the default token
+ if (id === 'default') {
+ return [defaultToken, defaultSetToken]
+ }
+
+ return [token, setToken]
+}
+
+export function useLang(initial?: string): [string, StateUpdater<string>] {
+ const browserLang = typeof window !== "undefined" ? navigator.language || (navigator as any).userLanguage : undefined;
+ const defaultLang = (browserLang || initial || 'en').substring(0, 2)
+ return useNotNullLocalStorage('lang-preference', defaultLang)
+}
+
+export function useLocalStorage(key: string, initialValue?: string): [string | undefined, StateUpdater<string | undefined>] {
+ const [storedValue, setStoredValue] = useState<string | undefined>((): string | undefined => {
+ return typeof window !== "undefined" ? window.localStorage.getItem(key) || initialValue : initialValue;
+ });
+
+ const setValue = (value?: string | ((val?: string) => string | undefined)) => {
+ setStoredValue(p => {
+ const toStore = value instanceof Function ? value(p) : value
+ if (typeof window !== "undefined") {
+ if (!toStore) {
+ window.localStorage.removeItem(key)
+ } else {
+ window.localStorage.setItem(key, toStore);
+ }
+ }
+ return toStore
+ })
+ };
+
+ return [storedValue, setValue];
+}
+
+export function useNotNullLocalStorage(key: string, initialValue: string): [string, StateUpdater<string>] {
+ const [storedValue, setStoredValue] = useState<string>((): string => {
+ return typeof window !== "undefined" ? window.localStorage.getItem(key) || initialValue : initialValue;
+ });
+
+ const setValue = (value: string | ((val: string) => string)) => {
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
+ setStoredValue(valueToStore);
+ if (typeof window !== "undefined") {
+ if (!valueToStore) {
+ window.localStorage.removeItem(key)
+ } else {
+ window.localStorage.setItem(key, valueToStore);
+ }
+ }
+ };
+
+ return [storedValue, setValue];
+}
+
+
diff --git a/packages/backend/src/hooks/instance.ts b/packages/backend/src/hooks/instance.ts
new file mode 100644
index 0000000..14ab8de
--- /dev/null
+++ b/packages/backend/src/hooks/instance.ts
@@ -0,0 +1,187 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { MerchantBackend } from '../declaration';
+import { useBackendContext } from '../context/backend';
+import { fetcher, HttpError, HttpResponse, HttpResponseOk, request, SwrError } from './backend';
+import useSWR, { mutate } from 'swr';
+import { useInstanceContext } from '../context/instance';
+
+
+interface InstanceAPI {
+ updateInstance: (data: MerchantBackend.Instances.InstanceReconfigurationMessage) => Promise<void>;
+ deleteInstance: () => Promise<void>;
+ clearToken: () => Promise<void>;
+ setNewToken: (token: string) => Promise<void>;
+}
+
+export function useManagementAPI(instanceId: string) : InstanceAPI {
+ const { url, token } = useBackendContext()
+
+ const updateInstance = async (instance: MerchantBackend.Instances.InstanceReconfigurationMessage): Promise<void> => {
+ await request(`${url}/management/instances/${instanceId}`, {
+ method: 'patch',
+ token,
+ data: instance
+ })
+
+ mutate([`/private/`, token, url], null)
+ };
+
+ const deleteInstance = async (): Promise<void> => {
+ await request(`${url}/management/instances/${instanceId}`, {
+ method: 'delete',
+ token,
+ })
+
+ mutate([`/private/`, token, url], null)
+ }
+
+ const clearToken = async (): Promise<void> => {
+ await request(`${url}/management/instances/${instanceId}/auth`, {
+ method: 'post',
+ token,
+ data: { method: 'external' }
+ })
+
+ mutate([`/private/`, token, url], null)
+ }
+
+ const setNewToken = async (newToken: string): Promise<void> => {
+ await request(`${url}/management/instances/${instanceId}/auth`, {
+ method: 'post',
+ token,
+ data: { method: 'token', token: newToken }
+ })
+
+ mutate([`/private/`, token, url], null)
+ }
+
+ return { updateInstance, deleteInstance, setNewToken, clearToken }
+}
+
+export function useInstanceAPI(): InstanceAPI {
+ const { url: baseUrl, token: adminToken } = useBackendContext()
+ const { token: instanceToken, id, admin } = useInstanceContext()
+
+ const { url, token } = !admin ? {
+ url: baseUrl, token: adminToken
+ } : {
+ url: `${baseUrl}/instances/${id}`, token: instanceToken
+ };
+
+ const updateInstance = async (instance: MerchantBackend.Instances.InstanceReconfigurationMessage): Promise<void> => {
+ await request(`${url}/private/`, {
+ method: 'patch',
+ token,
+ data: instance
+ })
+
+ if (adminToken) mutate(['/private/instances', adminToken, baseUrl], null)
+ mutate([`/private/`, token, url], null)
+ };
+
+ const deleteInstance = async (): Promise<void> => {
+ await request(`${url}/private/`, {
+ method: 'delete',
+ token: adminToken,
+ })
+
+ if (adminToken) mutate(['/private/instances', adminToken, baseUrl], null)
+ mutate([`/private/`, token, url], null)
+ }
+
+ const clearToken = async (): Promise<void> => {
+ await request(`${url}/private/auth`, {
+ method: 'post',
+ token,
+ data: { method: 'external' }
+ })
+
+ mutate([`/private/`, token, url], null)
+ }
+
+ const setNewToken = async (newToken: string): Promise<void> => {
+ await request(`${url}/private/auth`, {
+ method: 'post',
+ token,
+ data: { method: 'token', token: newToken }
+ })
+
+ mutate([`/private/`, token, url], null)
+ }
+
+ return { updateInstance, deleteInstance, setNewToken, clearToken }
+}
+
+
+export function useInstanceDetails(): HttpResponse<MerchantBackend.Instances.QueryInstancesResponse> {
+ const { url: baseUrl, token: baseToken } = useBackendContext();
+ const { token: instanceToken, id, admin } = useInstanceContext();
+
+ const { url, token } = !admin ? {
+ url: baseUrl, token: baseToken
+ } : {
+ url: `${baseUrl}/instances/${id}`, token: instanceToken
+ }
+
+ const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Instances.QueryInstancesResponse>, HttpError>([`/private/`, token, url], fetcher, {
+ refreshInterval:0,
+ refreshWhenHidden: false,
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ errorRetryCount: 0,
+ errorRetryInterval: 1,
+ shouldRetryOnError: false,
+ })
+
+ if (isValidating) return {loading:true, data: data?.data}
+ if (data) return data
+ if (error) return error
+ return {loading: true}
+}
+
+export function useManagedInstanceDetails(instanceId: string): HttpResponse<MerchantBackend.Instances.QueryInstancesResponse> {
+ const { url, token } = useBackendContext();
+
+ const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Instances.QueryInstancesResponse>, HttpError>([`/management/instances/${instanceId}`, token, url], fetcher, {
+ refreshInterval:0,
+ refreshWhenHidden: false,
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ errorRetryCount: 0,
+ errorRetryInterval: 1,
+ shouldRetryOnError: false,
+ })
+
+ if (isValidating) return {loading:true, data: data?.data}
+ if (data) return data
+ if (error) return error
+ return {loading: true}
+}
+
+export function useBackendInstances(): HttpResponse<MerchantBackend.Instances.InstancesResponse> {
+ const { url } = useBackendContext()
+ const { token } = useInstanceContext();
+
+ const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Instances.InstancesResponse>, HttpError>(['/management/instances', token, url], fetcher)
+
+ if (isValidating) return {loading:true, data: data?.data}
+ if (data) return data
+ if (error) return error
+ return {loading: true}
+}
diff --git a/packages/backend/src/hooks/listener.ts b/packages/backend/src/hooks/listener.ts
new file mode 100644
index 0000000..231ed6c
--- /dev/null
+++ b/packages/backend/src/hooks/listener.ts
@@ -0,0 +1,68 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { useState } from "preact/hooks";
+
+/**
+ * returns subscriber and activator
+ * subscriber will receive a method (listener) that will be call when the activator runs.
+ * the result of calling the listener will be sent to @action
+ *
+ * @param action from <T> to <R>
+ * @returns activator and subscriber, undefined activator means that there is not subscriber
+ */
+
+export function useListener<T, R = any>(action: (r: T) => Promise<R>): [undefined | (() => Promise<R>), (listener?: () => T) => void] {
+ type RunnerHandler = { toBeRan?: () => Promise<R>; };
+ const [state, setState] = useState<RunnerHandler>({});
+
+ /**
+ * subscriber will receive a method that will be call when the activator runs
+ *
+ * @param listener function to be run when the activator runs
+ */
+ const subscriber = (listener?: () => T) => {
+ if (listener) {
+ setState({
+ toBeRan: () => {
+ const whatWeGetFromTheListener = listener();
+ return action(whatWeGetFromTheListener);
+ }
+ });
+ } else {
+ setState({
+ toBeRan: undefined
+ })
+ }
+ };
+
+ /**
+ * activator will call runner if there is someone subscribed
+ */
+ const activator = state.toBeRan ? async () => {
+ if (state.toBeRan) {
+ return state.toBeRan();
+ }
+ return Promise.reject();
+ } : undefined;
+
+ return [activator, subscriber];
+}
diff --git a/packages/backend/src/hooks/notification.ts b/packages/backend/src/hooks/notification.ts
new file mode 100644
index 0000000..d1dfbff
--- /dev/null
+++ b/packages/backend/src/hooks/notification.ts
@@ -0,0 +1,43 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { useCallback, useState } from "preact/hooks";
+import { Notification } from '../utils/types';
+
+interface Result {
+ notification?: Notification;
+ pushNotification: (n: Notification) => void;
+ removeNotification: () => void;
+}
+
+export function useNotification(): Result {
+ const [notification, setNotifications] = useState<Notification|undefined>(undefined)
+
+ const pushNotification = useCallback((n: Notification): void => {
+ setNotifications(n)
+ },[])
+
+ const removeNotification = useCallback(() => {
+ setNotifications(undefined)
+ },[])
+
+ return { notification, pushNotification, removeNotification }
+}
diff --git a/packages/backend/src/hooks/notifications.ts b/packages/backend/src/hooks/notifications.ts
new file mode 100644
index 0000000..1c0c373
--- /dev/null
+++ b/packages/backend/src/hooks/notifications.ts
@@ -0,0 +1,48 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { useState } from "preact/hooks";
+import { Notification } from '../utils/types';
+
+interface Result {
+ notifications: Notification[];
+ pushNotification: (n: Notification) => void;
+ removeNotification: (n: Notification) => void;
+}
+
+type NotificationWithDate = Notification & { since: Date }
+
+export function useNotifications(initial: Notification[] = [], timeout = 3000): Result {
+ const [notifications, setNotifications] = useState<(NotificationWithDate)[]>(initial.map(i => ({...i, since: new Date() })))
+
+ const pushNotification = (n: Notification): void => {
+ const entry = { ...n, since: new Date() }
+ setNotifications(ns => [...ns, entry])
+ if (n.type !== 'ERROR') setTimeout(() => {
+ setNotifications(ns => ns.filter(x => x.since !== entry.since))
+ }, timeout)
+ }
+
+ const removeNotification = (notif: Notification) => {
+ setNotifications((ns: NotificationWithDate[]) => ns.filter(n => n !== notif))
+ }
+ return { notifications, pushNotification, removeNotification }
+}
diff --git a/packages/backend/src/hooks/order.ts b/packages/backend/src/hooks/order.ts
new file mode 100644
index 0000000..a0d7916
--- /dev/null
+++ b/packages/backend/src/hooks/order.ts
@@ -0,0 +1,217 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { useEffect, useState } from 'preact/hooks';
+import useSWR from 'swr';
+import { useBackendContext } from '../context/backend';
+import { useInstanceContext } from '../context/instance';
+import { MerchantBackend } from '../declaration';
+import { MAX_RESULT_SIZE, PAGE_SIZE } from '../utils/constants';
+import { fetcher, HttpError, HttpResponse, HttpResponseOk, HttpResponsePaginated, mutateAll, request } from './backend';
+
+export interface OrderAPI {
+ //FIXME: add OutOfStockResponse on 410
+ createOrder: (data: MerchantBackend.Orders.PostOrderRequest) => Promise<HttpResponseOk<MerchantBackend.Orders.PostOrderResponse>>;
+ forgetOrder: (id: string, data: MerchantBackend.Orders.ForgetRequest) => Promise<HttpResponseOk<void>>;
+ refundOrder: (id: string, data: MerchantBackend.Orders.RefundRequest) => Promise<HttpResponseOk<MerchantBackend.Orders.MerchantRefundResponse>>;
+ deleteOrder: (id: string) => Promise<HttpResponseOk<void>>;
+ getPaymentURL: (id: string) => Promise<HttpResponseOk<string>>;
+}
+
+type YesOrNo = 'yes' | 'no';
+
+
+export function orderFetcher<T>(url: string, token: string, backend: string, paid?: YesOrNo, refunded?: YesOrNo, wired?: YesOrNo, searchDate?: Date, delta?: number): Promise<HttpResponseOk<T>> {
+ const date_ms = delta && delta < 0 && searchDate ? searchDate.getTime() + 1 : searchDate?.getTime()
+ const params: any = {}
+ if (paid !== undefined) params.paid = paid
+ if (delta !== undefined) params.delta = delta
+ if (refunded !== undefined) params.refunded = refunded
+ if (wired !== undefined) params.wired = wired
+ if (date_ms !== undefined) params.date_ms = date_ms
+ return request<T>(`${backend}${url}`, { token, params })
+}
+
+
+export function useOrderAPI(): OrderAPI {
+ const { url: baseUrl, token: adminToken } = useBackendContext()
+ const { token: instanceToken, id, admin } = useInstanceContext()
+
+ const { url, token } = !admin ? {
+ url: baseUrl, token: adminToken
+ } : {
+ url: `${baseUrl}/instances/${id}`, token: instanceToken
+ }
+
+ const createOrder = async (data: MerchantBackend.Orders.PostOrderRequest): Promise<HttpResponseOk<MerchantBackend.Orders.PostOrderResponse>> => {
+ const res = await request<MerchantBackend.Orders.PostOrderResponse>(`${url}/private/orders`, {
+ method: 'post',
+ token,
+ data
+ })
+ await mutateAll(/@"\/private\/orders"@/)
+ return res
+ }
+ const refundOrder = async (orderId: string, data: MerchantBackend.Orders.RefundRequest): Promise<HttpResponseOk<MerchantBackend.Orders.MerchantRefundResponse>> => {
+ mutateAll(/@"\/private\/orders"@/)
+ return request<MerchantBackend.Orders.MerchantRefundResponse>(`${url}/private/orders/${orderId}/refund`, {
+ method: 'post',
+ token,
+ data
+ })
+
+ // return res
+ }
+
+ const forgetOrder = async (orderId: string, data: MerchantBackend.Orders.ForgetRequest): Promise<HttpResponseOk<void>> => {
+ mutateAll(/@"\/private\/orders"@/)
+ return request(`${url}/private/orders/${orderId}/forget`, {
+ method: 'patch',
+ token,
+ data
+ })
+
+ }
+ const deleteOrder = async (orderId: string): Promise<HttpResponseOk<void>> => {
+ mutateAll(/@"\/private\/orders"@/)
+ return request(`${url}/private/orders/${orderId}`, {
+ method: 'delete',
+ token
+ })
+ }
+
+ const getPaymentURL = async (orderId: string): Promise<HttpResponseOk<string>> => {
+ return request<MerchantBackend.Orders.MerchantOrderStatusResponse>(`${url}/private/orders/${orderId}`, {
+ method: 'get',
+ token
+ }).then((res) => {
+ const url = res.data.order_status === "unpaid" ? res.data.taler_pay_uri : res.data.contract_terms.fulfillment_url
+ const response: HttpResponseOk<string> = res as any
+ response.data = url || ''
+ return response
+ })
+ }
+
+ return { createOrder, forgetOrder, deleteOrder, refundOrder, getPaymentURL }
+}
+
+export function useOrderDetails(oderId: string): HttpResponse<MerchantBackend.Orders.MerchantOrderStatusResponse> {
+ const { url: baseUrl, token: baseToken } = useBackendContext();
+ const { token: instanceToken, id, admin } = useInstanceContext();
+
+ const { url, token } = !admin ? {
+ url: baseUrl, token: baseToken
+ } : {
+ url: `${baseUrl}/instances/${id}`, token: instanceToken
+ };
+
+ const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Orders.MerchantOrderStatusResponse>, HttpError>([`/private/orders/${oderId}`, token, url], 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
+ return { loading: true }
+}
+
+export interface InstanceOrderFilter {
+ paid?: YesOrNo;
+ refunded?: YesOrNo;
+ wired?: YesOrNo;
+ date?: Date;
+}
+
+export function useInstanceOrders(args?: InstanceOrderFilter, updateFilter?: (d: Date) => void): HttpResponsePaginated<MerchantBackend.Orders.OrderHistory> {
+ const { url: baseUrl, token: baseToken } = useBackendContext();
+ const { token: instanceToken, id, admin } = useInstanceContext();
+
+ const { url, token } = !admin ? {
+ url: baseUrl, token: baseToken
+ } : {
+ url: `${baseUrl}/instances/${id}`, token: instanceToken
+ }
+
+ const [pageBefore, setPageBefore] = useState(1)
+ const [pageAfter, setPageAfter] = useState(1)
+
+ const totalAfter = pageAfter * PAGE_SIZE;
+ const totalBefore = args?.date ? pageBefore * PAGE_SIZE : 0;
+
+ /**
+ * FIXME: this can be cleaned up a little
+ *
+ * the logic of double query should be inside the orderFetch so from the hook perspective and cache
+ * is just one query and one error status
+ */
+ const { data: beforeData, error: beforeError, isValidating: loadingBefore } = useSWR<HttpResponseOk<MerchantBackend.Orders.OrderHistory>, HttpError>(
+ [`/private/orders`, token, url, args?.paid, args?.refunded, args?.wired, args?.date, totalBefore],
+ orderFetcher,
+ )
+ const { data: afterData, error: afterError, isValidating: loadingAfter } = useSWR<HttpResponseOk<MerchantBackend.Orders.OrderHistory>, HttpError>(
+ [`/private/orders`, token, url, args?.paid, args?.refunded, args?.wired, args?.date, -totalAfter],
+ orderFetcher,
+ )
+
+ //this will save last result
+ const [lastBefore, setLastBefore] = useState<HttpResponse<MerchantBackend.Orders.OrderHistory>>({ loading: true })
+ const [lastAfter, setLastAfter] = useState<HttpResponse<MerchantBackend.Orders.OrderHistory>>({ loading: true })
+ useEffect(() => {
+ if (afterData) setLastAfter(afterData)
+ if (beforeData) setLastBefore(beforeData)
+ }, [afterData, beforeData])
+
+ // this has problems when there are some ids missing
+
+ if (beforeError) return beforeError
+ if (afterError) return afterError
+
+
+ const pagination = {
+ isReachingEnd: afterData && afterData.data.orders.length < totalAfter,
+ isReachingStart: (!args?.date) || (beforeData && beforeData.data.orders.length < totalBefore),
+ loadMore: () => {
+ if (!afterData) return
+ if (afterData.data.orders.length < MAX_RESULT_SIZE) {
+ setPageAfter(pageAfter + 1)
+ } else {
+ const from = afterData.data.orders[afterData.data.orders.length - 1].timestamp.t_ms
+ if (from && updateFilter) updateFilter(new Date(from))
+ }
+ },
+ loadMorePrev: () => {
+ if (!beforeData) return
+ if (beforeData.data.orders.length < MAX_RESULT_SIZE) {
+ setPageBefore(pageBefore + 1)
+ } else if (beforeData) {
+ const from = beforeData.data.orders[beforeData.data.orders.length - 1].timestamp.t_ms
+ if (from && updateFilter) updateFilter(new Date(from))
+ }
+ },
+ }
+
+ const orders = !beforeData || !afterData ? [] : (beforeData || lastBefore).data.orders.slice().reverse().concat((afterData || lastAfter).data.orders)
+ if (loadingAfter || loadingBefore) return { loading: true, data: { orders } }
+ if (beforeData && afterData) {
+ return { ok: true, data: { orders }, ...pagination }
+ }
+ return { loading: true }
+
+}
+
diff --git a/packages/backend/src/hooks/product.ts b/packages/backend/src/hooks/product.ts
new file mode 100644
index 0000000..edba27b
--- /dev/null
+++ b/packages/backend/src/hooks/product.ts
@@ -0,0 +1,199 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { useEffect } from 'preact/hooks';
+import useSWR, { trigger, useSWRInfinite, cache, mutate } from 'swr';
+import { useBackendContext } from '../context/backend';
+// import { useFetchContext } from '../context/fetch';
+import { useInstanceContext } from '../context/instance';
+import { MerchantBackend, WithId } from '../declaration';
+import { fetcher, HttpError, HttpResponse, HttpResponseOk, mutateAll, request } from './backend';
+
+export interface ProductAPI {
+ createProduct: (data: MerchantBackend.Products.ProductAddDetail) => Promise<void>;
+ updateProduct: (id: string, data: MerchantBackend.Products.ProductPatchDetail) => Promise<void>;
+ deleteProduct: (id: string) => Promise<void>;
+ lockProduct: (id: string, data: MerchantBackend.Products.LockRequest) => Promise<void>;
+}
+
+
+export function useProductAPI(): ProductAPI {
+ const { url: baseUrl, token: adminToken } = useBackendContext();
+ const { token: instanceToken, id, admin } = useInstanceContext();
+
+ const { url, token } = !admin ? {
+ url: baseUrl, token: adminToken
+ } : {
+ url: `${baseUrl}/instances/${id}`, token: instanceToken
+ };
+
+
+ const createProduct = async (data: MerchantBackend.Products.ProductAddDetail): Promise<void> => {
+ await request(`${url}/private/products`, {
+ method: 'post',
+ token,
+ data
+ });
+
+ await mutateAll(/@"\/private\/products"@/, null);
+ };
+
+ const updateProduct = async (productId: string, data: MerchantBackend.Products.ProductPatchDetail): Promise<void> => {
+ const r = await request(`${url}/private/products/${productId}`, {
+ method: 'patch',
+ token,
+ data
+ });
+
+ /**
+ * There is some inconsistency in how the cache is evicted.
+ * I'm keeping this for later inspection
+ */
+
+ // -- Clear all cache
+ // -- This seems to work always but is bad
+
+ // const keys = [...cache.keys()]
+ // console.log(keys)
+ // cache.clear()
+ // await Promise.all(keys.map(k => trigger(k)))
+
+ // -- From the keys to the cache trigger
+ // -- An intermediate step
+
+ // const keys = [
+ // [`/private/products`, token, url],
+ // [`/private/products/${productId}`, token, url],
+ // ]
+ // cache.clear()
+ // const f: string[][] = keys.map(k => cache.serializeKey(k))
+ // console.log(f)
+ // const m = flat(f)
+ // console.log(m)
+ // await Promise.all(m.map(k => trigger(k, true)))
+
+ // await Promise.all(keys.map(k => mutate(k)))
+
+ // -- This is how is supposed to be use
+
+ // await mutate([`/private/products`, token, url])
+ // await mutate([`/private/products/${productId}`, token, url])
+
+ // await mutateAll(/@"\/private\/products"@/);
+ await mutateAll(/@"\/private\/products\/.*"@/);
+ // return true
+ // return r
+
+ // -- FIXME: why this un-break the tests?
+ return Promise.resolve()
+ };
+
+ const deleteProduct = async (productId: string): Promise<void> => {
+ await request(`${url}/private/products/${productId}`, {
+ method: 'delete',
+ token,
+ });
+
+ await mutateAll(/@"\/private\/products"@/);
+ };
+
+ const lockProduct = async (productId: string, data: MerchantBackend.Products.LockRequest): Promise<void> => {
+ await request(`${url}/private/products/${productId}/lock`, {
+ method: 'post',
+ token,
+ data
+ });
+
+ await mutateAll(/@"\/private\/products"@/);
+ };
+
+ return { createProduct, updateProduct, deleteProduct, lockProduct };
+}
+
+
+export function useInstanceProducts(): HttpResponse<(MerchantBackend.Products.ProductDetail & WithId)[]> {
+ const { url: baseUrl, token: baseToken } = useBackendContext();
+ const { token: instanceToken, id, admin } = useInstanceContext();
+ // const { useSWR, useSWRInfinite } = useFetchContext();
+
+ const { url, token } = !admin ? {
+ url: baseUrl, token: baseToken
+ } : {
+ url: `${baseUrl}/instances/${id}`, token: instanceToken
+ };
+
+ const { data: list, error: listError, isValidating: listLoading } = useSWR<HttpResponseOk<MerchantBackend.Products.InventorySummaryResponse>, HttpError>([`/private/products`, token, url], fetcher, {
+ refreshInterval: 0,
+ refreshWhenHidden: false,
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ });
+
+ const { data: products, error: productError, setSize, size } = useSWRInfinite<HttpResponseOk<MerchantBackend.Products.ProductDetail>, HttpError>((pageIndex: number) => {
+ if (!list?.data || !list.data.products.length || listError || listLoading) return null
+ return [`/private/products/${list.data.products[pageIndex].product_id}`, token, url]
+ }, fetcher, {
+ revalidateAll: true,
+ })
+
+ useEffect(() => {
+ if (list?.data && list.data.products.length > 0) {
+ setSize(list.data.products.length)
+ }
+ }, [list?.data.products.length, listLoading])
+
+
+ if (listLoading) return { loading: true, data: [] }
+ if (listError) return listError
+ if (productError) return productError
+ if (list?.data && list.data.products.length === 0) {
+ return { ok: true, data: [] }
+ }
+ if (products) {
+ const dataWithId = products.map((d) => {
+ //take the id from the queried url
+ return ({ ...d.data, id: d.info?.url.replace(/.*\/private\/products\//, '') || '' })
+ })
+ return { ok: true, data: dataWithId }
+ }
+ return { loading: true }
+}
+
+export function useProductDetails(productId: string): HttpResponse<MerchantBackend.Products.ProductDetail> {
+ const { url: baseUrl, token: baseToken } = useBackendContext();
+ const { token: instanceToken, id, admin } = useInstanceContext();
+
+ const { url, token } = !admin ? {
+ url: baseUrl, token: baseToken
+ } : {
+ url: `${baseUrl}/instances/${id}`, token: instanceToken
+ };
+
+ const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Products.ProductDetail>, HttpError>(
+ [`/private/products/${productId}`, token, url], 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
+ return { loading: true }
+}
diff --git a/packages/backend/src/hooks/tips.ts b/packages/backend/src/hooks/tips.ts
new file mode 100644
index 0000000..345e1fa
--- /dev/null
+++ b/packages/backend/src/hooks/tips.ts
@@ -0,0 +1,159 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 useSWR from 'swr';
+import { useBackendContext } from '../context/backend';
+import { useInstanceContext } from '../context/instance';
+import { MerchantBackend } from '../declaration';
+import { fetcher, HttpError, HttpResponse, HttpResponseOk, mutateAll, request } from './backend';
+
+
+export function useReservesAPI(): ReserveMutateAPI {
+ const { url: baseUrl, token: adminToken } = useBackendContext();
+ const { token: instanceToken, id, admin } = useInstanceContext();
+
+ const { url, token } = !admin ? {
+ url: baseUrl, token: adminToken
+ } : {
+ url: `${baseUrl}/instances/${id}`, token: instanceToken
+ };
+
+ const createReserve = async (data: MerchantBackend.Tips.ReserveCreateRequest): Promise<HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation>> => {
+ const res = await request<MerchantBackend.Tips.ReserveCreateConfirmation>(`${url}/private/reserves`, {
+ method: 'post',
+ token,
+ data
+ });
+
+ await mutateAll(/@"\/private\/reserves"@/);
+
+ return res
+ };
+
+ const authorizeTipReserve = async (pub: string, data: MerchantBackend.Tips.TipCreateRequest): Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => {
+ const res = await request<MerchantBackend.Tips.TipCreateConfirmation>(`${url}/private/reserves/${pub}/authorize-tip`, {
+ method: 'post',
+ token,
+ data
+ });
+ await mutateAll(/@"\/private\/reserves"@/);
+
+ return res
+ };
+
+ const authorizeTip = async (data: MerchantBackend.Tips.TipCreateRequest): Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => {
+ const res = await request<MerchantBackend.Tips.TipCreateConfirmation>(`${url}/private/tips`, {
+ method: 'post',
+ token,
+ data
+ });
+
+ await mutateAll(/@"\/private\/reserves"@/);
+
+ return res
+ };
+
+ const deleteReserve = async (pub: string): Promise<HttpResponse<void>> => {
+ const res = await request<void>(`${url}/private/reserves/${pub}`, {
+ method: 'delete',
+ token,
+ });
+
+ await mutateAll(/@"\/private\/reserves"@/);
+
+ return res
+ };
+
+
+ return { createReserve, authorizeTip, authorizeTipReserve, deleteReserve };
+}
+
+export interface ReserveMutateAPI {
+ createReserve: (data: MerchantBackend.Tips.ReserveCreateRequest) => Promise<HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation>>;
+ authorizeTipReserve: (id: string, data: MerchantBackend.Tips.TipCreateRequest) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>;
+ authorizeTip: (data: MerchantBackend.Tips.TipCreateRequest) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>;
+ deleteReserve: (id: string) => Promise<HttpResponse<void>>;
+}
+
+export function useInstanceTips(): HttpResponse<MerchantBackend.Tips.TippingReserveStatus> {
+ const { url: baseUrl, token: baseToken } = useBackendContext();
+ const { token: instanceToken, id, admin } = useInstanceContext();
+
+ const { url, token } = !admin ? {
+ url: baseUrl, token: baseToken
+ } : {
+ url: `${baseUrl}/instances/${id}`, token: instanceToken
+ }
+
+ const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Tips.TippingReserveStatus>, HttpError>([`/private/reserves`, token, url], fetcher)
+
+ if (isValidating) return { loading: true, data: data?.data }
+ if (data) return data
+ if (error) return error
+ return { loading: true }
+}
+
+
+export function useReserveDetails(reserveId: string): HttpResponse<MerchantBackend.Tips.ReserveDetail> {
+ const { url: baseUrl } = useBackendContext();
+ const { token, id: instanceId, admin } = useInstanceContext();
+
+ const url = !admin ? baseUrl : `${baseUrl}/instances/${instanceId}`
+
+ const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Tips.ReserveDetail>, HttpError>([`/private/reserves/${reserveId}`, token, url], reserveDetailFetcher, {
+ 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
+ return { loading: true }
+}
+
+export function useTipDetails(tipId: string): HttpResponse<MerchantBackend.Tips.TipDetails> {
+ const { url: baseUrl } = useBackendContext();
+ const { token, id: instanceId, admin } = useInstanceContext();
+
+ const url = !admin ? baseUrl : `${baseUrl}/instances/${instanceId}`
+
+ const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Tips.TipDetails>, HttpError>([`/private/tips/${tipId}`, token, url], tipsDetailFetcher, {
+ 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
+ return { loading: true }
+}
+
+export function reserveDetailFetcher<T>(url: string, token: string, backend: string): Promise<HttpResponseOk<T>> {
+ return request<T>(`${backend}${url}`, { token, params: {
+ tips: 'yes'
+ } })
+}
+
+export function tipsDetailFetcher<T>(url: string, token: string, backend: string): Promise<HttpResponseOk<T>> {
+ return request<T>(`${backend}${url}`, { token, params: {
+ pickups: 'yes'
+ } })
+}
diff --git a/packages/backend/src/hooks/transfer.ts b/packages/backend/src/hooks/transfer.ts
new file mode 100644
index 0000000..02e041a
--- /dev/null
+++ b/packages/backend/src/hooks/transfer.ts
@@ -0,0 +1,150 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { MerchantBackend } from '../declaration';
+import { useBackendContext } from '../context/backend';
+import { request, mutateAll, HttpResponse, HttpError, HttpResponseOk, HttpResponsePaginated } from './backend';
+import useSWR from 'swr';
+import { useInstanceContext } from '../context/instance';
+import { MAX_RESULT_SIZE, PAGE_SIZE } from '../utils/constants';
+import { useEffect, useState } from 'preact/hooks';
+
+async function transferFetcher<T>(url: string, token: string, backend: string, payto_uri?: string, verified?: string, position?: string, delta?: number): Promise<HttpResponseOk<T>> {
+ const params: any = {}
+ if (payto_uri !== undefined) params.payto_uri = payto_uri
+ if (verified !== undefined) params.verified = verified
+ if (delta !== undefined) {
+ // if (delta > 0) {
+ // params.after = searchDate?.getTime()
+ // } else {
+ // params.before = searchDate?.getTime()
+ // }
+ params.limit = delta
+ }
+ if (position !== undefined) params.offset = position
+
+ return request<T>(`${backend}${url}`, { token, params })
+}
+
+export function useTransferAPI(): TransferAPI {
+ const { url: baseUrl, token: adminToken } = useBackendContext();
+ const { token: instanceToken, id, admin } = useInstanceContext();
+
+ const { url, token } = !admin ? {
+ url: baseUrl, token: adminToken
+ } : {
+ url: `${baseUrl}/instances/${id}`, token: instanceToken
+ };
+
+ const informTransfer = async (data: MerchantBackend.Transfers.TransferInformation): Promise<HttpResponseOk<MerchantBackend.Transfers.MerchantTrackTransferResponse>> => {
+ mutateAll(/@"\/private\/transfers"@/);
+
+ return request<MerchantBackend.Transfers.MerchantTrackTransferResponse>(`${url}/private/transfers`, {
+ method: 'post',
+ token,
+ data
+ });
+ };
+
+ return { informTransfer };
+}
+
+export interface TransferAPI {
+ informTransfer: (data: MerchantBackend.Transfers.TransferInformation) => Promise<HttpResponseOk<MerchantBackend.Transfers.MerchantTrackTransferResponse>>;
+}
+
+export interface InstanceTransferFilter {
+ payto_uri?: string;
+ verified?: 'yes' | 'no';
+ position?: string;
+}
+
+
+export function useInstanceTransfers(args?: InstanceTransferFilter, updatePosition?: (id: string) => void): HttpResponsePaginated<MerchantBackend.Transfers.TransferList> {
+ const { url: baseUrl, token: baseToken } = useBackendContext();
+ const { token: instanceToken, id, admin } = useInstanceContext();
+
+ const { url, token } = !admin ? {
+ url: baseUrl, token: baseToken
+ } : {
+ url: `${baseUrl}/instances/${id}`, token: instanceToken
+ }
+
+ const [pageBefore, setPageBefore] = useState(1)
+ const [pageAfter, setPageAfter] = useState(1)
+
+ const totalAfter = pageAfter * PAGE_SIZE;
+ const totalBefore = args?.position !== undefined ? pageBefore * PAGE_SIZE : 0;
+
+ /**
+ * FIXME: this can be cleaned up a little
+ *
+ * the logic of double query should be inside the orderFetch so from the hook perspective and cache
+ * is just one query and one error status
+ */
+ const { data: beforeData, error: beforeError, isValidating: loadingBefore } = useSWR<HttpResponseOk<MerchantBackend.Transfers.TransferList>, HttpError>(
+ [`/private/transfers`, token, url, args?.payto_uri, args?.verified, args?.position, totalBefore],
+ transferFetcher,
+ )
+ const { data: afterData, error: afterError, isValidating: loadingAfter } = useSWR<HttpResponseOk<MerchantBackend.Transfers.TransferList>, HttpError>(
+ [`/private/transfers`, token, url, args?.payto_uri, args?.verified, args?.position, -totalAfter],
+ transferFetcher,
+ )
+
+ //this will save last result
+ const [lastBefore, setLastBefore] = useState<HttpResponse<MerchantBackend.Transfers.TransferList>>({ loading: true })
+ const [lastAfter, setLastAfter] = useState<HttpResponse<MerchantBackend.Transfers.TransferList>>({ loading: true })
+ useEffect(() => {
+ if (afterData) setLastAfter(afterData)
+ if (beforeData) setLastBefore(beforeData)
+ }, [afterData, beforeData])
+
+ // this has problems when there are some ids missing
+
+ if (beforeError) return beforeError
+ if (afterError) return afterError
+
+ const pagination = {
+ isReachingEnd: afterData && afterData.data.transfers.length < totalAfter,
+ isReachingStart: (!args?.position) || (beforeData && beforeData.data.transfers.length < totalBefore),
+ loadMore: () => {
+ if (!afterData) return
+ if (afterData.data.transfers.length < MAX_RESULT_SIZE) {
+ setPageAfter(pageAfter + 1)
+ } else {
+ const from = ""+afterData.data.transfers[afterData.data.transfers.length - 1].transfer_serial_id
+ if (from && updatePosition) updatePosition(from)
+ }
+ },
+ loadMorePrev: () => {
+ if (!beforeData) return
+ if (beforeData.data.transfers.length < MAX_RESULT_SIZE) {
+ setPageBefore(pageBefore + 1)
+ } else if (beforeData) {
+ const from = ""+beforeData.data.transfers[beforeData.data.transfers.length - 1].transfer_serial_id
+ if (from && updatePosition) updatePosition(from)
+ }
+ },
+ }
+
+ const transfers = !beforeData || !afterData ? [] : (beforeData || lastBefore).data.transfers.slice().reverse().concat((afterData || lastAfter).data.transfers)
+ if (loadingAfter || loadingBefore) return { loading: true, data: { transfers } }
+ if (beforeData && afterData) {
+ return { ok: true, data: { transfers }, ...pagination }
+ }
+ return { loading: true }
+}
+
+
diff --git a/packages/backend/src/i18n/de.po b/packages/backend/src/i18n/de.po
new file mode 100644
index 0000000..6b35bd0
--- /dev/null
+++ b/packages/backend/src/i18n/de.po
@@ -0,0 +1,1057 @@
+# This file is part of TALER
+# (C) 2016 GNUnet e.V.
+#
+# 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.
+#
+# 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
+# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Taler Wallet\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-11-23 00:00+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:299
+#, c-format
+msgid "Access denied"
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:300
+#, c-format
+msgid "Check your token is valid"
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:72
+#, c-format
+msgid "Couldn't access the server."
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:73
+#, c-format
+msgid "Could not infer instance id from url %1$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:109
+#, c-format
+msgid "HTTP status #%1$s: Server reported a problem"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:110
+#, c-format
+msgid "Got message: \"%1$s\" from: %2$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:127
+#, c-format
+msgid "No default instance"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:128
+#, c-format
+msgid ""
+"in order to use merchant backoffice, you should create the default instance"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:288
+#, c-format
+msgid "Server reported a problem: HTTP status #%1$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:289
+#, c-format
+msgid "Got message: %1$s from: %2$s"
+msgstr ""
+
+#: src/components/exception/login.tsx:46
+#, c-format
+msgid "Login required"
+msgstr ""
+
+#: src/components/exception/login.tsx:49
+#, c-format
+msgid ""
+"Please enter your auth token. Token should have \"secret-token:\" and start "
+"with Bearer or ApiKey"
+msgstr ""
+
+#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53
+#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115
+#: src/paths/instance/orders/create/CreatePage.tsx:325
+#: src/paths/instance/products/create/CreatePage.tsx:51
+#: src/paths/instance/products/list/Table.tsx:174
+#: src/paths/instance/products/list/Table.tsx:228
+#: src/paths/instance/products/update/UpdatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:134
+#, c-format
+msgid "Confirm"
+msgstr ""
+
+#: src/components/form/InputArray.tsx:72
+#, c-format
+msgid "The value %1$s is invalid for a payment url"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:67
+#: src/paths/instance/orders/list/index.tsx:123
+#, c-format
+msgid "pick a date"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:81
+#, c-format
+msgid "clear"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:83
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "never"
+msgstr ""
+
+#: src/components/form/InputImage.tsx:80
+#, c-format
+msgid "Image should be smaller than 1 MB"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:28
+#, c-format
+msgid "Country"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:30
+#: src/paths/admin/create/CreatePage.tsx:99
+#: src/paths/instance/transfers/list/Table.tsx:124
+#: src/paths/instance/update/UpdatePage.tsx:118
+#, c-format
+msgid "Address"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:34
+#, c-format
+msgid "Building number"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:35
+#, c-format
+msgid "Building name"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:36
+#, c-format
+msgid "Street"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:37
+#, c-format
+msgid "Post code"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:38
+#, c-format
+msgid "Town location"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:39
+#, c-format
+msgid "Town"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:40
+#, c-format
+msgid "District"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:41
+#, c-format
+msgid "Country subdivision"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:59
+#, c-format
+msgid "Product id"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:60
+#: src/components/product/ProductForm.tsx:99
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122
+#: src/paths/instance/orders/list/Table.tsx:227
+#: src/paths/instance/products/list/Table.tsx:86
+#, c-format
+msgid "Description"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:73
+#: src/components/form/InputTaxes.tsx:81
+#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110
+#: src/paths/instance/details/DetailPage.tsx:76
+#: src/paths/instance/update/UpdatePage.tsx:106
+#, c-format
+msgid "Name"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:102
+#, c-format
+msgid "loading..."
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:108
+#, c-format
+msgid "no products found"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:116
+#, c-format
+msgid "no results"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:33
+#, c-format
+msgid "Deleting"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:34
+#, c-format
+msgid "Changing"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:60
+#, c-format
+msgid "Manage token"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:83
+#, c-format
+msgid "Update"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:100
+#: src/paths/instance/orders/create/CreatePage.tsx:252
+#: src/paths/instance/orders/create/CreatePage.tsx:273
+#, c-format
+msgid "Remove"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52
+#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114
+#: src/paths/instance/orders/create/CreatePage.tsx:324
+#: src/paths/instance/products/create/CreatePage.tsx:50
+#: src/paths/instance/products/list/Table.tsx:166
+#: src/paths/instance/products/list/Table.tsx:218
+#: src/paths/instance/products/update/UpdatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:88
+#: src/paths/instance/update/UpdatePage.tsx:133
+#, c-format
+msgid "Cancel"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:91
+#, c-format
+msgid "Manage stock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:93
+#, c-format
+msgid "Infinite"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:105
+#, c-format
+msgid "lost cannot be greater that current + incoming (max %1$s)"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:111
+#, c-format
+msgid "current stock will change from %1$s to %2$s"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:112
+#, c-format
+msgid "current stock will stay at %1$s"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:129
+#: src/paths/instance/products/list/Table.tsx:204
+#, c-format
+msgid "Incoming"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:130
+#: src/paths/instance/products/list/Table.tsx:205
+#, c-format
+msgid "Lost"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:142
+#, c-format
+msgid "Current"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:145
+#, c-format
+msgid "without stock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:150
+#, c-format
+msgid "Next restock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:152
+#, c-format
+msgid "Delivery address"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:73
+#, c-format
+msgid "this product has no taxes"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:77
+#: src/paths/instance/orders/details/DetailPage.tsx:145
+#: src/paths/instance/orders/details/DetailPage.tsx:296
+#: src/paths/instance/orders/list/Table.tsx:116
+#: src/paths/instance/transfers/create/CreatePage.tsx:84
+#, c-format
+msgid "Amount"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:78
+#, c-format
+msgid "currency and value separated with colon"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:84
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:78
+#, c-format
+msgid "Add"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:53
+#, c-format
+msgid "Instance"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:59
+#, c-format
+msgid "Settings"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:65
+#: src/paths/instance/orders/list/Table.tsx:60
+#, c-format
+msgid "Orders"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:71
+#: src/paths/instance/orders/create/CreatePage.tsx:258
+#: src/paths/instance/products/list/Table.tsx:48
+#, c-format
+msgid "Products"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:77
+#: src/paths/instance/transfers/list/Table.tsx:65
+#, c-format
+msgid "Transfers"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:87
+#, c-format
+msgid "Connection"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57
+#, c-format
+msgid "Instances"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:116
+#, c-format
+msgid "New"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:122
+#, c-format
+msgid "List"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:129
+#, c-format
+msgid "Log out"
+msgstr ""
+
+#: src/components/modal/index.tsx:74
+#, c-format
+msgid "Clear"
+msgstr ""
+
+#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111
+#, c-format
+msgid "should be the same"
+msgstr ""
+
+#: src/components/modal/index.tsx:111
+#, c-format
+msgid "cannot be the same as before"
+msgstr ""
+
+#: src/components/modal/index.tsx:114
+#, c-format
+msgid ""
+"You are updating the authorization token from instance %1$s with id %2$s"
+msgstr ""
+
+#: src/components/modal/index.tsx:124
+#, c-format
+msgid "Old token"
+msgstr ""
+
+#: src/components/modal/index.tsx:125
+#, c-format
+msgid "New token"
+msgstr ""
+
+#: src/components/modal/index.tsx:127
+#, c-format
+msgid "Clearing the auth token will mean public access to the instance"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:96
+#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109
+#: src/paths/instance/transfers/list/Table.tsx:122
+#, c-format
+msgid "ID"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:98
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121
+#: src/paths/instance/products/list/Table.tsx:85
+#, c-format
+msgid "Image"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:100
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123
+#, c-format
+msgid "Unit"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:101
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124
+#: src/paths/instance/products/list/Table.tsx:162
+#: src/paths/instance/products/list/Table.tsx:214
+#, c-format
+msgid "Price"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:103
+#: src/paths/instance/products/list/Table.tsx:90
+#, c-format
+msgid "Stock"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:105
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128
+#: src/paths/instance/products/list/Table.tsx:88
+#, c-format
+msgid "Taxes"
+msgstr ""
+
+#: src/index.tsx:75
+#, c-format
+msgid "Server not found"
+msgstr ""
+
+#: src/index.tsx:85
+#, c-format
+msgid "Couldn't access the server"
+msgstr ""
+
+#: src/index.tsx:87 src/index.tsx:99
+#, c-format
+msgid "Got message %1$s from %2$s"
+msgstr ""
+
+#: src/index.tsx:97
+#, c-format
+msgid "Unexpected Error"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:108
+#, c-format
+msgid "Auth token"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:91
+#: src/paths/instance/details/DetailPage.tsx:77
+#: src/paths/instance/update/UpdatePage.tsx:110
+#, c-format
+msgid "Account address"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:93
+#: src/paths/instance/update/UpdatePage.tsx:112
+#, c-format
+msgid "Default max deposit fee"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:95
+#: src/paths/instance/update/UpdatePage.tsx:114
+#, c-format
+msgid "Default max wire fee"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:97
+#: src/paths/instance/update/UpdatePage.tsx:116
+#, c-format
+msgid "Default wire fee amortization"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:103
+#: src/paths/instance/update/UpdatePage.tsx:122
+#, c-format
+msgid "Jurisdiction"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:107
+#: src/paths/instance/update/UpdatePage.tsx:126
+#, c-format
+msgid "Default pay delay"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:109
+#: src/paths/instance/update/UpdatePage.tsx:128
+#, c-format
+msgid "Default wire transfer delay"
+msgstr ""
+
+#: src/paths/admin/create/index.tsx:58
+#, c-format
+msgid "could not create instance"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131
+#: src/paths/instance/transfers/list/Table.tsx:71
+#, c-format
+msgid "Delete"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:128
+#, c-format
+msgid "Edit"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:149
+#: src/paths/instance/products/list/Table.tsx:245
+#, c-format
+msgid "There is no instances yet, add more pressing the + sign"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:237
+#, c-format
+msgid "Inventory products"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:286
+#, c-format
+msgid "Total price"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:287
+#, c-format
+msgid "Total tax"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:289
+#: src/paths/instance/orders/create/CreatePage.tsx:297
+#, c-format
+msgid "Order price"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:295
+#, c-format
+msgid "Net"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:300
+#: src/paths/instance/orders/details/DetailPage.tsx:144
+#: src/paths/instance/orders/details/DetailPage.tsx:295
+#: src/paths/instance/orders/list/Table.tsx:117
+#, c-format
+msgid "Summary"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:302
+#, c-format
+msgid "Payments options"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:303
+#, c-format
+msgid "Auto refund deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:304
+#, c-format
+msgid "Refund deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:305
+#, c-format
+msgid "Pay deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:307
+#, c-format
+msgid "Delivery date"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:308
+#, c-format
+msgid "Location"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:312
+#, c-format
+msgid "Max fee"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:313
+#, c-format
+msgid "Max wire fee"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:314
+#, c-format
+msgid "Wire fee amortization"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:315
+#, c-format
+msgid "Fullfilment url"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:318
+#, c-format
+msgid "Extra information"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:44
+#, c-format
+msgid "select a product first"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:51
+#, c-format
+msgid "should be greater than 0"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:58
+#, c-format
+msgid ""
+"cannot be greater than current stock and quantity previously added. max: %1$s"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:64
+#, c-format
+msgid "cannot be greater than current stock %1$s"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:76
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126
+#, c-format
+msgid "Quantity"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:92
+#: src/paths/instance/orders/details/DetailPage.tsx:235
+#: src/paths/instance/orders/details/DetailPage.tsx:333
+#, c-format
+msgid "Order"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:93
+#, c-format
+msgid "claimed"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:110
+#: src/paths/instance/orders/details/DetailPage.tsx:261
+#: src/paths/instance/orders/list/Table.tsx:136
+#, c-format
+msgid "copy url"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:126
+#: src/paths/instance/orders/details/DetailPage.tsx:349
+#, c-format
+msgid "pay at"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:127
+#: src/paths/instance/orders/details/DetailPage.tsx:350
+#, c-format
+msgid "created at"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:138
+#: src/paths/instance/orders/details/DetailPage.tsx:289
+#, c-format
+msgid "Timeline"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:142
+#: src/paths/instance/orders/details/DetailPage.tsx:293
+#, c-format
+msgid "Payment details"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:146
+#: src/paths/instance/orders/details/DetailPage.tsx:299
+#: src/paths/instance/orders/details/DetailPage.tsx:363
+#, c-format
+msgid "Order status"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:156
+#: src/paths/instance/orders/details/DetailPage.tsx:308
+#, c-format
+msgid "Product list"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:236
+#, c-format
+msgid "paid"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:238
+#, c-format
+msgid "wired"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:241
+#, c-format
+msgid "refunded"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:258
+#, c-format
+msgid "refund"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:297
+#, c-format
+msgid "Refunded amount"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:298
+#, c-format
+msgid "Deposit total"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:336
+#, c-format
+msgid "unpaid"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:364
+#, c-format
+msgid "Order status URL"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:365
+#, c-format
+msgid "Pay URI"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:383
+#, c-format
+msgid ""
+"Unknown order status. This is an error, please contact the administrator."
+msgstr ""
+
+#: src/paths/instance/orders/details/index.tsx:56
+#: src/paths/instance/orders/list/index.tsx:147
+#, c-format
+msgid "refund created successfully"
+msgstr ""
+
+#: src/paths/instance/orders/details/index.tsx:59
+#: src/paths/instance/orders/list/index.tsx:150
+#, c-format
+msgid "could not create the refund"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:111
+#, c-format
+msgid "load newer orders"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:115
+#, c-format
+msgid "Date"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:131
+#: src/paths/instance/orders/list/Table.tsx:223
+#, c-format
+msgid "Refund"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:145
+#, c-format
+msgid "load older orders"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:154
+#, c-format
+msgid "No orders has been found"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:202
+#, c-format
+msgid "date"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:203
+#, c-format
+msgid "amount"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:204
+#, c-format
+msgid "reason"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:224
+#, c-format
+msgid "Max refundable:"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "Reason"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "duplicated"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "requested by the customer"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "other"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:91
+#, c-format
+msgid "go to order id"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:107
+#, c-format
+msgid "Paid"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:108
+#, c-format
+msgid "Refunded"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:109
+#, c-format
+msgid "Not wired"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:110
+#, c-format
+msgid "All"
+msgstr ""
+
+#: src/paths/instance/products/create/index.tsx:48
+#: src/paths/instance/products/update/index.tsx:64
+#, c-format
+msgid "could not create product"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:87
+#, c-format
+msgid "Sell"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:89
+#, c-format
+msgid "Profit"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:91
+#, c-format
+msgid "Sold"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:59
+#, c-format
+msgid "product updated successfully"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:62
+#, c-format
+msgid "could not update the product"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:70
+#, c-format
+msgid "product delete successfully"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:73
+#, c-format
+msgid "could not delete the product"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:59
+#, c-format
+msgid "Tips"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:111
+#, c-format
+msgid "Committed amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:112
+#, c-format
+msgid "Exchange initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:113
+#, c-format
+msgid "Merchant initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:148
+#, c-format
+msgid "There is no tips yet, add more pressing the + sign"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:50
+#: src/paths/instance/transfers/create/CreatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:56
+#, c-format
+msgid "cannot be empty"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:51
+#, c-format
+msgid "check the id, doest look valid"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:52
+#, c-format
+msgid "should have 52 characters, current %1$s"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:57
+#, c-format
+msgid "URL doesn't have the right format"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:74
+#, c-format
+msgid "Transfer ID"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:76
+#, c-format
+msgid "Account Address"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:82
+#: src/paths/instance/transfers/list/Table.tsx:125
+#, c-format
+msgid "Exchange URL"
+msgstr ""
+
+#: src/paths/instance/transfers/create/index.tsx:49
+#, c-format
+msgid "could not inform transfer"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:118
+#, c-format
+msgid "load newer transfers"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:123
+#, c-format
+msgid "Credit"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:126
+#, c-format
+msgid "Confirmed"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:127
+#: src/paths/instance/transfers/list/index.tsx:60
+#, c-format
+msgid "Verified"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:128
+#, c-format
+msgid "Executed at"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "yes"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "no"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "unknown"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:145
+#, c-format
+msgid "load older transfers"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:154
+#, c-format
+msgid "There is no transfer yet, add more pressing the + sign"
+msgstr ""
diff --git a/packages/backend/src/i18n/en.po b/packages/backend/src/i18n/en.po
new file mode 100644
index 0000000..6b35bd0
--- /dev/null
+++ b/packages/backend/src/i18n/en.po
@@ -0,0 +1,1057 @@
+# This file is part of TALER
+# (C) 2016 GNUnet e.V.
+#
+# 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.
+#
+# 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
+# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Taler Wallet\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-11-23 00:00+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:299
+#, c-format
+msgid "Access denied"
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:300
+#, c-format
+msgid "Check your token is valid"
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:72
+#, c-format
+msgid "Couldn't access the server."
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:73
+#, c-format
+msgid "Could not infer instance id from url %1$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:109
+#, c-format
+msgid "HTTP status #%1$s: Server reported a problem"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:110
+#, c-format
+msgid "Got message: \"%1$s\" from: %2$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:127
+#, c-format
+msgid "No default instance"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:128
+#, c-format
+msgid ""
+"in order to use merchant backoffice, you should create the default instance"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:288
+#, c-format
+msgid "Server reported a problem: HTTP status #%1$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:289
+#, c-format
+msgid "Got message: %1$s from: %2$s"
+msgstr ""
+
+#: src/components/exception/login.tsx:46
+#, c-format
+msgid "Login required"
+msgstr ""
+
+#: src/components/exception/login.tsx:49
+#, c-format
+msgid ""
+"Please enter your auth token. Token should have \"secret-token:\" and start "
+"with Bearer or ApiKey"
+msgstr ""
+
+#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53
+#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115
+#: src/paths/instance/orders/create/CreatePage.tsx:325
+#: src/paths/instance/products/create/CreatePage.tsx:51
+#: src/paths/instance/products/list/Table.tsx:174
+#: src/paths/instance/products/list/Table.tsx:228
+#: src/paths/instance/products/update/UpdatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:134
+#, c-format
+msgid "Confirm"
+msgstr ""
+
+#: src/components/form/InputArray.tsx:72
+#, c-format
+msgid "The value %1$s is invalid for a payment url"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:67
+#: src/paths/instance/orders/list/index.tsx:123
+#, c-format
+msgid "pick a date"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:81
+#, c-format
+msgid "clear"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:83
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "never"
+msgstr ""
+
+#: src/components/form/InputImage.tsx:80
+#, c-format
+msgid "Image should be smaller than 1 MB"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:28
+#, c-format
+msgid "Country"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:30
+#: src/paths/admin/create/CreatePage.tsx:99
+#: src/paths/instance/transfers/list/Table.tsx:124
+#: src/paths/instance/update/UpdatePage.tsx:118
+#, c-format
+msgid "Address"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:34
+#, c-format
+msgid "Building number"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:35
+#, c-format
+msgid "Building name"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:36
+#, c-format
+msgid "Street"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:37
+#, c-format
+msgid "Post code"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:38
+#, c-format
+msgid "Town location"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:39
+#, c-format
+msgid "Town"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:40
+#, c-format
+msgid "District"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:41
+#, c-format
+msgid "Country subdivision"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:59
+#, c-format
+msgid "Product id"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:60
+#: src/components/product/ProductForm.tsx:99
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122
+#: src/paths/instance/orders/list/Table.tsx:227
+#: src/paths/instance/products/list/Table.tsx:86
+#, c-format
+msgid "Description"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:73
+#: src/components/form/InputTaxes.tsx:81
+#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110
+#: src/paths/instance/details/DetailPage.tsx:76
+#: src/paths/instance/update/UpdatePage.tsx:106
+#, c-format
+msgid "Name"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:102
+#, c-format
+msgid "loading..."
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:108
+#, c-format
+msgid "no products found"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:116
+#, c-format
+msgid "no results"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:33
+#, c-format
+msgid "Deleting"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:34
+#, c-format
+msgid "Changing"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:60
+#, c-format
+msgid "Manage token"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:83
+#, c-format
+msgid "Update"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:100
+#: src/paths/instance/orders/create/CreatePage.tsx:252
+#: src/paths/instance/orders/create/CreatePage.tsx:273
+#, c-format
+msgid "Remove"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52
+#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114
+#: src/paths/instance/orders/create/CreatePage.tsx:324
+#: src/paths/instance/products/create/CreatePage.tsx:50
+#: src/paths/instance/products/list/Table.tsx:166
+#: src/paths/instance/products/list/Table.tsx:218
+#: src/paths/instance/products/update/UpdatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:88
+#: src/paths/instance/update/UpdatePage.tsx:133
+#, c-format
+msgid "Cancel"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:91
+#, c-format
+msgid "Manage stock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:93
+#, c-format
+msgid "Infinite"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:105
+#, c-format
+msgid "lost cannot be greater that current + incoming (max %1$s)"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:111
+#, c-format
+msgid "current stock will change from %1$s to %2$s"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:112
+#, c-format
+msgid "current stock will stay at %1$s"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:129
+#: src/paths/instance/products/list/Table.tsx:204
+#, c-format
+msgid "Incoming"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:130
+#: src/paths/instance/products/list/Table.tsx:205
+#, c-format
+msgid "Lost"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:142
+#, c-format
+msgid "Current"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:145
+#, c-format
+msgid "without stock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:150
+#, c-format
+msgid "Next restock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:152
+#, c-format
+msgid "Delivery address"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:73
+#, c-format
+msgid "this product has no taxes"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:77
+#: src/paths/instance/orders/details/DetailPage.tsx:145
+#: src/paths/instance/orders/details/DetailPage.tsx:296
+#: src/paths/instance/orders/list/Table.tsx:116
+#: src/paths/instance/transfers/create/CreatePage.tsx:84
+#, c-format
+msgid "Amount"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:78
+#, c-format
+msgid "currency and value separated with colon"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:84
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:78
+#, c-format
+msgid "Add"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:53
+#, c-format
+msgid "Instance"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:59
+#, c-format
+msgid "Settings"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:65
+#: src/paths/instance/orders/list/Table.tsx:60
+#, c-format
+msgid "Orders"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:71
+#: src/paths/instance/orders/create/CreatePage.tsx:258
+#: src/paths/instance/products/list/Table.tsx:48
+#, c-format
+msgid "Products"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:77
+#: src/paths/instance/transfers/list/Table.tsx:65
+#, c-format
+msgid "Transfers"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:87
+#, c-format
+msgid "Connection"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57
+#, c-format
+msgid "Instances"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:116
+#, c-format
+msgid "New"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:122
+#, c-format
+msgid "List"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:129
+#, c-format
+msgid "Log out"
+msgstr ""
+
+#: src/components/modal/index.tsx:74
+#, c-format
+msgid "Clear"
+msgstr ""
+
+#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111
+#, c-format
+msgid "should be the same"
+msgstr ""
+
+#: src/components/modal/index.tsx:111
+#, c-format
+msgid "cannot be the same as before"
+msgstr ""
+
+#: src/components/modal/index.tsx:114
+#, c-format
+msgid ""
+"You are updating the authorization token from instance %1$s with id %2$s"
+msgstr ""
+
+#: src/components/modal/index.tsx:124
+#, c-format
+msgid "Old token"
+msgstr ""
+
+#: src/components/modal/index.tsx:125
+#, c-format
+msgid "New token"
+msgstr ""
+
+#: src/components/modal/index.tsx:127
+#, c-format
+msgid "Clearing the auth token will mean public access to the instance"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:96
+#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109
+#: src/paths/instance/transfers/list/Table.tsx:122
+#, c-format
+msgid "ID"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:98
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121
+#: src/paths/instance/products/list/Table.tsx:85
+#, c-format
+msgid "Image"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:100
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123
+#, c-format
+msgid "Unit"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:101
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124
+#: src/paths/instance/products/list/Table.tsx:162
+#: src/paths/instance/products/list/Table.tsx:214
+#, c-format
+msgid "Price"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:103
+#: src/paths/instance/products/list/Table.tsx:90
+#, c-format
+msgid "Stock"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:105
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128
+#: src/paths/instance/products/list/Table.tsx:88
+#, c-format
+msgid "Taxes"
+msgstr ""
+
+#: src/index.tsx:75
+#, c-format
+msgid "Server not found"
+msgstr ""
+
+#: src/index.tsx:85
+#, c-format
+msgid "Couldn't access the server"
+msgstr ""
+
+#: src/index.tsx:87 src/index.tsx:99
+#, c-format
+msgid "Got message %1$s from %2$s"
+msgstr ""
+
+#: src/index.tsx:97
+#, c-format
+msgid "Unexpected Error"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:108
+#, c-format
+msgid "Auth token"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:91
+#: src/paths/instance/details/DetailPage.tsx:77
+#: src/paths/instance/update/UpdatePage.tsx:110
+#, c-format
+msgid "Account address"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:93
+#: src/paths/instance/update/UpdatePage.tsx:112
+#, c-format
+msgid "Default max deposit fee"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:95
+#: src/paths/instance/update/UpdatePage.tsx:114
+#, c-format
+msgid "Default max wire fee"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:97
+#: src/paths/instance/update/UpdatePage.tsx:116
+#, c-format
+msgid "Default wire fee amortization"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:103
+#: src/paths/instance/update/UpdatePage.tsx:122
+#, c-format
+msgid "Jurisdiction"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:107
+#: src/paths/instance/update/UpdatePage.tsx:126
+#, c-format
+msgid "Default pay delay"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:109
+#: src/paths/instance/update/UpdatePage.tsx:128
+#, c-format
+msgid "Default wire transfer delay"
+msgstr ""
+
+#: src/paths/admin/create/index.tsx:58
+#, c-format
+msgid "could not create instance"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131
+#: src/paths/instance/transfers/list/Table.tsx:71
+#, c-format
+msgid "Delete"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:128
+#, c-format
+msgid "Edit"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:149
+#: src/paths/instance/products/list/Table.tsx:245
+#, c-format
+msgid "There is no instances yet, add more pressing the + sign"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:237
+#, c-format
+msgid "Inventory products"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:286
+#, c-format
+msgid "Total price"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:287
+#, c-format
+msgid "Total tax"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:289
+#: src/paths/instance/orders/create/CreatePage.tsx:297
+#, c-format
+msgid "Order price"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:295
+#, c-format
+msgid "Net"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:300
+#: src/paths/instance/orders/details/DetailPage.tsx:144
+#: src/paths/instance/orders/details/DetailPage.tsx:295
+#: src/paths/instance/orders/list/Table.tsx:117
+#, c-format
+msgid "Summary"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:302
+#, c-format
+msgid "Payments options"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:303
+#, c-format
+msgid "Auto refund deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:304
+#, c-format
+msgid "Refund deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:305
+#, c-format
+msgid "Pay deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:307
+#, c-format
+msgid "Delivery date"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:308
+#, c-format
+msgid "Location"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:312
+#, c-format
+msgid "Max fee"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:313
+#, c-format
+msgid "Max wire fee"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:314
+#, c-format
+msgid "Wire fee amortization"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:315
+#, c-format
+msgid "Fullfilment url"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:318
+#, c-format
+msgid "Extra information"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:44
+#, c-format
+msgid "select a product first"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:51
+#, c-format
+msgid "should be greater than 0"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:58
+#, c-format
+msgid ""
+"cannot be greater than current stock and quantity previously added. max: %1$s"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:64
+#, c-format
+msgid "cannot be greater than current stock %1$s"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:76
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126
+#, c-format
+msgid "Quantity"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:92
+#: src/paths/instance/orders/details/DetailPage.tsx:235
+#: src/paths/instance/orders/details/DetailPage.tsx:333
+#, c-format
+msgid "Order"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:93
+#, c-format
+msgid "claimed"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:110
+#: src/paths/instance/orders/details/DetailPage.tsx:261
+#: src/paths/instance/orders/list/Table.tsx:136
+#, c-format
+msgid "copy url"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:126
+#: src/paths/instance/orders/details/DetailPage.tsx:349
+#, c-format
+msgid "pay at"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:127
+#: src/paths/instance/orders/details/DetailPage.tsx:350
+#, c-format
+msgid "created at"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:138
+#: src/paths/instance/orders/details/DetailPage.tsx:289
+#, c-format
+msgid "Timeline"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:142
+#: src/paths/instance/orders/details/DetailPage.tsx:293
+#, c-format
+msgid "Payment details"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:146
+#: src/paths/instance/orders/details/DetailPage.tsx:299
+#: src/paths/instance/orders/details/DetailPage.tsx:363
+#, c-format
+msgid "Order status"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:156
+#: src/paths/instance/orders/details/DetailPage.tsx:308
+#, c-format
+msgid "Product list"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:236
+#, c-format
+msgid "paid"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:238
+#, c-format
+msgid "wired"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:241
+#, c-format
+msgid "refunded"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:258
+#, c-format
+msgid "refund"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:297
+#, c-format
+msgid "Refunded amount"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:298
+#, c-format
+msgid "Deposit total"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:336
+#, c-format
+msgid "unpaid"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:364
+#, c-format
+msgid "Order status URL"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:365
+#, c-format
+msgid "Pay URI"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:383
+#, c-format
+msgid ""
+"Unknown order status. This is an error, please contact the administrator."
+msgstr ""
+
+#: src/paths/instance/orders/details/index.tsx:56
+#: src/paths/instance/orders/list/index.tsx:147
+#, c-format
+msgid "refund created successfully"
+msgstr ""
+
+#: src/paths/instance/orders/details/index.tsx:59
+#: src/paths/instance/orders/list/index.tsx:150
+#, c-format
+msgid "could not create the refund"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:111
+#, c-format
+msgid "load newer orders"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:115
+#, c-format
+msgid "Date"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:131
+#: src/paths/instance/orders/list/Table.tsx:223
+#, c-format
+msgid "Refund"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:145
+#, c-format
+msgid "load older orders"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:154
+#, c-format
+msgid "No orders has been found"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:202
+#, c-format
+msgid "date"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:203
+#, c-format
+msgid "amount"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:204
+#, c-format
+msgid "reason"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:224
+#, c-format
+msgid "Max refundable:"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "Reason"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "duplicated"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "requested by the customer"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "other"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:91
+#, c-format
+msgid "go to order id"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:107
+#, c-format
+msgid "Paid"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:108
+#, c-format
+msgid "Refunded"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:109
+#, c-format
+msgid "Not wired"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:110
+#, c-format
+msgid "All"
+msgstr ""
+
+#: src/paths/instance/products/create/index.tsx:48
+#: src/paths/instance/products/update/index.tsx:64
+#, c-format
+msgid "could not create product"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:87
+#, c-format
+msgid "Sell"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:89
+#, c-format
+msgid "Profit"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:91
+#, c-format
+msgid "Sold"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:59
+#, c-format
+msgid "product updated successfully"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:62
+#, c-format
+msgid "could not update the product"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:70
+#, c-format
+msgid "product delete successfully"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:73
+#, c-format
+msgid "could not delete the product"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:59
+#, c-format
+msgid "Tips"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:111
+#, c-format
+msgid "Committed amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:112
+#, c-format
+msgid "Exchange initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:113
+#, c-format
+msgid "Merchant initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:148
+#, c-format
+msgid "There is no tips yet, add more pressing the + sign"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:50
+#: src/paths/instance/transfers/create/CreatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:56
+#, c-format
+msgid "cannot be empty"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:51
+#, c-format
+msgid "check the id, doest look valid"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:52
+#, c-format
+msgid "should have 52 characters, current %1$s"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:57
+#, c-format
+msgid "URL doesn't have the right format"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:74
+#, c-format
+msgid "Transfer ID"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:76
+#, c-format
+msgid "Account Address"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:82
+#: src/paths/instance/transfers/list/Table.tsx:125
+#, c-format
+msgid "Exchange URL"
+msgstr ""
+
+#: src/paths/instance/transfers/create/index.tsx:49
+#, c-format
+msgid "could not inform transfer"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:118
+#, c-format
+msgid "load newer transfers"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:123
+#, c-format
+msgid "Credit"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:126
+#, c-format
+msgid "Confirmed"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:127
+#: src/paths/instance/transfers/list/index.tsx:60
+#, c-format
+msgid "Verified"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:128
+#, c-format
+msgid "Executed at"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "yes"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "no"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "unknown"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:145
+#, c-format
+msgid "load older transfers"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:154
+#, c-format
+msgid "There is no transfer yet, add more pressing the + sign"
+msgstr ""
diff --git a/packages/backend/src/i18n/es.po b/packages/backend/src/i18n/es.po
new file mode 100644
index 0000000..9075d46
--- /dev/null
+++ b/packages/backend/src/i18n/es.po
@@ -0,0 +1,1065 @@
+# This file is part of TALER
+# (C) 2016 GNUnet e.V.
+#
+# 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.
+#
+# 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
+# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Taler Wallet\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-11-23 00:00+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:299
+#, c-format
+msgid "Access denied"
+msgstr "Acceso denegado"
+
+#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:300
+#, c-format
+msgid "Check your token is valid"
+msgstr "Verifica que el token sea valido"
+
+#: src/ApplicationReadyRoutes.tsx:72
+#, c-format
+msgid "Couldn't access the server."
+msgstr "No se pudo acceder al servidor"
+
+#: src/ApplicationReadyRoutes.tsx:73
+#, c-format
+msgid "Could not infer instance id from url %1$s"
+msgstr "No se pudo inferir el id de la instancia con la url %1$s"
+
+#: src/InstanceRoutes.tsx:109
+#, c-format
+msgid "HTTP status #%1$s: Server reported a problem"
+msgstr "HTTP status #%1$s: Servidor reporto un problema"
+
+#: src/InstanceRoutes.tsx:110
+#, fuzzy, c-format
+msgid "Got message: \"%1$s\" from: %2$s"
+msgstr "Recivimos el mensaje %1$s desde %2$s"
+
+#: src/InstanceRoutes.tsx:127
+#, c-format
+msgid "No default instance"
+msgstr "Sin instancia default"
+
+#: src/InstanceRoutes.tsx:128
+#, c-format
+msgid ""
+"in order to use merchant backoffice, you should create the default instance"
+msgstr "para usar el merchant backoffice, debería crear la instancia default"
+
+#: src/InstanceRoutes.tsx:288
+#, c-format
+msgid "Server reported a problem: HTTP status #%1$s"
+msgstr "Servidir reporto un problema: HTTP status #%1$s"
+
+#: src/InstanceRoutes.tsx:289
+#, fuzzy, c-format
+msgid "Got message: %1$s from: %2$s"
+msgstr "Recivimos el mensaje %1$s desde %2$s"
+
+#: src/components/exception/login.tsx:46
+#, c-format
+msgid "Login required"
+msgstr "Login necesario"
+
+#: src/components/exception/login.tsx:49
+#, c-format
+msgid ""
+"Please enter your auth token. Token should have \"secret-token:\" and start "
+"with Bearer or ApiKey"
+msgstr ""
+"Por favor ingrese su token de autorización. El token debe tener \"secret-"
+"token\" y comenzar con Bearer o ApiKey"
+
+#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53
+#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115
+#: src/paths/instance/orders/create/CreatePage.tsx:325
+#: src/paths/instance/products/create/CreatePage.tsx:51
+#: src/paths/instance/products/list/Table.tsx:174
+#: src/paths/instance/products/list/Table.tsx:228
+#: src/paths/instance/products/update/UpdatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:134
+#, c-format
+msgid "Confirm"
+msgstr "Confirmar"
+
+#: src/components/form/InputArray.tsx:72
+#, c-format
+msgid "The value %1$s is invalid for a payment url"
+msgstr "El valor %1$s es invalido para una URL de pago"
+
+#: src/components/form/InputDate.tsx:67
+#: src/paths/instance/orders/list/index.tsx:123
+#, c-format
+msgid "pick a date"
+msgstr "elegir una fecha"
+
+#: src/components/form/InputDate.tsx:81
+#, fuzzy, c-format
+msgid "clear"
+msgstr "Limpiar"
+
+#: src/components/form/InputDate.tsx:83
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "never"
+msgstr "nunca"
+
+#: src/components/form/InputImage.tsx:80
+#, c-format
+msgid "Image should be smaller than 1 MB"
+msgstr "La imagen debe ser mas chica que 1 MB"
+
+#: src/components/form/InputLocation.tsx:28
+#, c-format
+msgid "Country"
+msgstr "País"
+
+#: src/components/form/InputLocation.tsx:30
+#: src/paths/admin/create/CreatePage.tsx:99
+#: src/paths/instance/transfers/list/Table.tsx:124
+#: src/paths/instance/update/UpdatePage.tsx:118
+#, c-format
+msgid "Address"
+msgstr "Dirección"
+
+#: src/components/form/InputLocation.tsx:34
+#, c-format
+msgid "Building number"
+msgstr "Número de edificio"
+
+#: src/components/form/InputLocation.tsx:35
+#, c-format
+msgid "Building name"
+msgstr "Nombre de edificio"
+
+#: src/components/form/InputLocation.tsx:36
+#, c-format
+msgid "Street"
+msgstr "Calle"
+
+#: src/components/form/InputLocation.tsx:37
+#, c-format
+msgid "Post code"
+msgstr "Código postal"
+
+#: src/components/form/InputLocation.tsx:38
+#, fuzzy, c-format
+msgid "Town location"
+msgstr "Ubicación de ciudad"
+
+#: src/components/form/InputLocation.tsx:39
+#, c-format
+msgid "Town"
+msgstr "Ciudad"
+
+#: src/components/form/InputLocation.tsx:40
+#, c-format
+msgid "District"
+msgstr "Distrito"
+
+#: src/components/form/InputLocation.tsx:41
+#, c-format
+msgid "Country subdivision"
+msgstr "Provincia"
+
+#: src/components/form/InputSearchProduct.tsx:59
+#, fuzzy, c-format
+msgid "Product id"
+msgstr "Id de producto"
+
+#: src/components/form/InputSearchProduct.tsx:60
+#: src/components/product/ProductForm.tsx:99
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122
+#: src/paths/instance/orders/list/Table.tsx:227
+#: src/paths/instance/products/list/Table.tsx:86
+#, c-format
+msgid "Description"
+msgstr "Descripcion"
+
+#: src/components/form/InputSearchProduct.tsx:73
+#: src/components/form/InputTaxes.tsx:81
+#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110
+#: src/paths/instance/details/DetailPage.tsx:76
+#: src/paths/instance/update/UpdatePage.tsx:106
+#, c-format
+msgid "Name"
+msgstr "Nombre"
+
+#: src/components/form/InputSearchProduct.tsx:102
+#, c-format
+msgid "loading..."
+msgstr "Cargando..."
+
+#: src/components/form/InputSearchProduct.tsx:108
+#, c-format
+msgid "no products found"
+msgstr "No se encontraron productos"
+
+#: src/components/form/InputSearchProduct.tsx:116
+#, c-format
+msgid "no results"
+msgstr "Sin resultados"
+
+#: src/components/form/InputSecured.tsx:33
+#, c-format
+msgid "Deleting"
+msgstr "Borrando"
+
+#: src/components/form/InputSecured.tsx:34
+#, c-format
+msgid "Changing"
+msgstr "Cambiando"
+
+#: src/components/form/InputSecured.tsx:60
+#, c-format
+msgid "Manage token"
+msgstr "Administrar token"
+
+#: src/components/form/InputSecured.tsx:83
+#, c-format
+msgid "Update"
+msgstr "Actualizar"
+
+#: src/components/form/InputSecured.tsx:100
+#: src/paths/instance/orders/create/CreatePage.tsx:252
+#: src/paths/instance/orders/create/CreatePage.tsx:273
+#, c-format
+msgid "Remove"
+msgstr "Eliminar"
+
+#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52
+#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114
+#: src/paths/instance/orders/create/CreatePage.tsx:324
+#: src/paths/instance/products/create/CreatePage.tsx:50
+#: src/paths/instance/products/list/Table.tsx:166
+#: src/paths/instance/products/list/Table.tsx:218
+#: src/paths/instance/products/update/UpdatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:88
+#: src/paths/instance/update/UpdatePage.tsx:133
+#, c-format
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: src/components/form/InputStock.tsx:91
+#, c-format
+msgid "Manage stock"
+msgstr "Administrar stock"
+
+#: src/components/form/InputStock.tsx:93
+#, c-format
+msgid "Infinite"
+msgstr "Inifinito"
+
+#: src/components/form/InputStock.tsx:105
+#, fuzzy, c-format
+msgid "lost cannot be greater that current + incoming (max %1$s)"
+msgstr "no puede ser mayor al stock actual %1$s"
+
+#: src/components/form/InputStock.tsx:111
+#, c-format
+msgid "current stock will change from %1$s to %2$s"
+msgstr "stock actual cambiará desde %1$s a %2$s"
+
+#: src/components/form/InputStock.tsx:112
+#, c-format
+msgid "current stock will stay at %1$s"
+msgstr "stock actual seguirá en %1$s"
+
+#: src/components/form/InputStock.tsx:129
+#: src/paths/instance/products/list/Table.tsx:204
+#, c-format
+msgid "Incoming"
+msgstr "Ingresando"
+
+#: src/components/form/InputStock.tsx:130
+#: src/paths/instance/products/list/Table.tsx:205
+#, c-format
+msgid "Lost"
+msgstr "Perdido"
+
+#: src/components/form/InputStock.tsx:142
+#, c-format
+msgid "Current"
+msgstr "Actual"
+
+#: src/components/form/InputStock.tsx:145
+#, c-format
+msgid "without stock"
+msgstr "sin stock"
+
+#: src/components/form/InputStock.tsx:150
+#, c-format
+msgid "Next restock"
+msgstr "Próximo reabastecimiento"
+
+#: src/components/form/InputStock.tsx:152
+#, c-format
+msgid "Delivery address"
+msgstr "Dirección de entrega"
+
+#: src/components/form/InputTaxes.tsx:73
+#, c-format
+msgid "this product has no taxes"
+msgstr "este producto no tiene impuestos"
+
+#: src/components/form/InputTaxes.tsx:77
+#: src/paths/instance/orders/details/DetailPage.tsx:145
+#: src/paths/instance/orders/details/DetailPage.tsx:296
+#: src/paths/instance/orders/list/Table.tsx:116
+#: src/paths/instance/transfers/create/CreatePage.tsx:84
+#, c-format
+msgid "Amount"
+msgstr "Monto"
+
+#: src/components/form/InputTaxes.tsx:78
+#, c-format
+msgid "currency and value separated with colon"
+msgstr "Moneda y valor separado por dos puntos"
+
+#: src/components/form/InputTaxes.tsx:84
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:78
+#, c-format
+msgid "Add"
+msgstr "Agregar"
+
+#: src/components/menu/SideBar.tsx:53
+#, c-format
+msgid "Instance"
+msgstr "Instancia"
+
+#: src/components/menu/SideBar.tsx:59
+#, c-format
+msgid "Settings"
+msgstr "Configuración"
+
+#: src/components/menu/SideBar.tsx:65
+#: src/paths/instance/orders/list/Table.tsx:60
+#, fuzzy, c-format
+msgid "Orders"
+msgstr "Ordenes"
+
+#: src/components/menu/SideBar.tsx:71
+#: src/paths/instance/orders/create/CreatePage.tsx:258
+#: src/paths/instance/products/list/Table.tsx:48
+#, c-format
+msgid "Products"
+msgstr "Productos"
+
+#: src/components/menu/SideBar.tsx:77
+#: src/paths/instance/transfers/list/Table.tsx:65
+#, c-format
+msgid "Transfers"
+msgstr "Transferencias"
+
+#: src/components/menu/SideBar.tsx:87
+#, fuzzy, c-format
+msgid "Connection"
+msgstr "Conexión"
+
+#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57
+#, c-format
+msgid "Instances"
+msgstr "Instancias"
+
+#: src/components/menu/SideBar.tsx:116
+#, fuzzy, c-format
+msgid "New"
+msgstr "Nuevo"
+
+#: src/components/menu/SideBar.tsx:122
+#, c-format
+msgid "List"
+msgstr "Lista"
+
+#: src/components/menu/SideBar.tsx:129
+#, c-format
+msgid "Log out"
+msgstr "Salir"
+
+#: src/components/modal/index.tsx:74
+#, c-format
+msgid "Clear"
+msgstr "Limpiar"
+
+#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111
+#, c-format
+msgid "should be the same"
+msgstr "deberían ser iguales"
+
+#: src/components/modal/index.tsx:111
+#, c-format
+msgid "cannot be the same as before"
+msgstr "no puede ser igual al anterior"
+
+#: src/components/modal/index.tsx:114
+#, c-format
+msgid ""
+"You are updating the authorization token from instance %1$s with id %2$s"
+msgstr ""
+"Está actualizando el token de autorización para la instancia %1$s con id %2$s"
+
+#: src/components/modal/index.tsx:124
+#, c-format
+msgid "Old token"
+msgstr "Viejo token"
+
+#: src/components/modal/index.tsx:125
+#, c-format
+msgid "New token"
+msgstr "Nuevo token"
+
+#: src/components/modal/index.tsx:127
+#, c-format
+msgid "Clearing the auth token will mean public access to the instance"
+msgstr ""
+"Limpiar el token de autorización significa acceso publico a la instancia"
+
+#: src/components/product/ProductForm.tsx:96
+#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109
+#: src/paths/instance/transfers/list/Table.tsx:122
+#, c-format
+msgid "ID"
+msgstr "ID"
+
+#: src/components/product/ProductForm.tsx:98
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121
+#: src/paths/instance/products/list/Table.tsx:85
+#, c-format
+msgid "Image"
+msgstr "Imagen"
+
+#: src/components/product/ProductForm.tsx:100
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123
+#, c-format
+msgid "Unit"
+msgstr "Unidad"
+
+#: src/components/product/ProductForm.tsx:101
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124
+#: src/paths/instance/products/list/Table.tsx:162
+#: src/paths/instance/products/list/Table.tsx:214
+#, c-format
+msgid "Price"
+msgstr "Precio"
+
+#: src/components/product/ProductForm.tsx:103
+#: src/paths/instance/products/list/Table.tsx:90
+#, c-format
+msgid "Stock"
+msgstr "Stock"
+
+#: src/components/product/ProductForm.tsx:105
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128
+#: src/paths/instance/products/list/Table.tsx:88
+#, c-format
+msgid "Taxes"
+msgstr "Impuesto"
+
+#: src/index.tsx:75
+#, c-format
+msgid "Server not found"
+msgstr "Servidor no encontrado"
+
+#: src/index.tsx:85
+#, c-format
+msgid "Couldn't access the server"
+msgstr "No se pudo aceder al servidor"
+
+#: src/index.tsx:87 src/index.tsx:99
+#, c-format
+msgid "Got message %1$s from %2$s"
+msgstr "Recivimos el mensaje %1$s desde %2$s"
+
+#: src/index.tsx:97
+#, c-format
+msgid "Unexpected Error"
+msgstr "Error inesperado"
+
+#: src/paths/admin/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:108
+#, c-format
+msgid "Auth token"
+msgstr "Token de autorización"
+
+#: src/paths/admin/create/CreatePage.tsx:91
+#: src/paths/instance/details/DetailPage.tsx:77
+#: src/paths/instance/update/UpdatePage.tsx:110
+#, c-format
+msgid "Account address"
+msgstr "Dirección de cuenta"
+
+#: src/paths/admin/create/CreatePage.tsx:93
+#: src/paths/instance/update/UpdatePage.tsx:112
+#, c-format
+msgid "Default max deposit fee"
+msgstr "Impuesto máximo de deposito por omisión"
+
+#: src/paths/admin/create/CreatePage.tsx:95
+#: src/paths/instance/update/UpdatePage.tsx:114
+#, c-format
+msgid "Default max wire fee"
+msgstr "Impuesto máximo de transferencia por omisión"
+
+#: src/paths/admin/create/CreatePage.tsx:97
+#: src/paths/instance/update/UpdatePage.tsx:116
+#, c-format
+msgid "Default wire fee amortization"
+msgstr "Amortización de impuesto de transferencia por omisión"
+
+#: src/paths/admin/create/CreatePage.tsx:103
+#: src/paths/instance/update/UpdatePage.tsx:122
+#, c-format
+msgid "Jurisdiction"
+msgstr "Jurisdicción"
+
+#: src/paths/admin/create/CreatePage.tsx:107
+#: src/paths/instance/update/UpdatePage.tsx:126
+#, c-format
+msgid "Default pay delay"
+msgstr "Retrazo de pago por omisión"
+
+#: src/paths/admin/create/CreatePage.tsx:109
+#: src/paths/instance/update/UpdatePage.tsx:128
+#, c-format
+msgid "Default wire transfer delay"
+msgstr "Retrazo de transferencia por omisión"
+
+#: src/paths/admin/create/index.tsx:58
+#, c-format
+msgid "could not create instance"
+msgstr "no se pudo crear la instancia"
+
+#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131
+#: src/paths/instance/transfers/list/Table.tsx:71
+#, fuzzy, c-format
+msgid "Delete"
+msgstr "Borrando"
+
+#: src/paths/admin/list/Table.tsx:128
+#, c-format
+msgid "Edit"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:149
+#: src/paths/instance/products/list/Table.tsx:245
+#, c-format
+msgid "There is no instances yet, add more pressing the + sign"
+msgstr "No hay instancias todavían, agregue mas presionando el signo +"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:237
+#, c-format
+msgid "Inventory products"
+msgstr "Productos de inventario"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:286
+#, c-format
+msgid "Total price"
+msgstr "Precio total"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:287
+#, c-format
+msgid "Total tax"
+msgstr "Impuesto total"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:289
+#: src/paths/instance/orders/create/CreatePage.tsx:297
+#, c-format
+msgid "Order price"
+msgstr "Precio de la orden"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:295
+#, fuzzy, c-format
+msgid "Net"
+msgstr "Neto"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:300
+#: src/paths/instance/orders/details/DetailPage.tsx:144
+#: src/paths/instance/orders/details/DetailPage.tsx:295
+#: src/paths/instance/orders/list/Table.tsx:117
+#, c-format
+msgid "Summary"
+msgstr "Resumen"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:302
+#, c-format
+msgid "Payments options"
+msgstr "Opciones de pago"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:303
+#, c-format
+msgid "Auto refund deadline"
+msgstr "Plazo de reembolso automático"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:304
+#, c-format
+msgid "Refund deadline"
+msgstr "Plazo de reembolso"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:305
+#, c-format
+msgid "Pay deadline"
+msgstr "Plazo de pago"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:307
+#, c-format
+msgid "Delivery date"
+msgstr "Fecha de entrega"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:308
+#, fuzzy, c-format
+msgid "Location"
+msgstr "Ubicación"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:312
+#, c-format
+msgid "Max fee"
+msgstr "Impuesto máximo"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:313
+#, c-format
+msgid "Max wire fee"
+msgstr "Impuesto de transferencia máximo"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:314
+#, c-format
+msgid "Wire fee amortization"
+msgstr "Amortización de impuesto de transferencia"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:315
+#, c-format
+msgid "Fullfilment url"
+msgstr "URL de completitud"
+
+#: src/paths/instance/orders/create/CreatePage.tsx:318
+#, c-format
+msgid "Extra information"
+msgstr "Información extra"
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:44
+#, c-format
+msgid "select a product first"
+msgstr "seleccione un producto primero"
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:51
+#, fuzzy, c-format
+msgid "should be greater than 0"
+msgstr "La imagen debe ser mas chica que 1 MB"
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:58
+#, c-format
+msgid ""
+"cannot be greater than current stock and quantity previously added. max: %1$s"
+msgstr ""
+"no puede ser mayor al stock actual y la cantidad previamente agregada. "
+"máximo: %1$s"
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:64
+#, c-format
+msgid "cannot be greater than current stock %1$s"
+msgstr "no puede ser mayor al stock actual %1$s"
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:76
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126
+#, c-format
+msgid "Quantity"
+msgstr "Cantidad"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:92
+#: src/paths/instance/orders/details/DetailPage.tsx:235
+#: src/paths/instance/orders/details/DetailPage.tsx:333
+#, c-format
+msgid "Order"
+msgstr "Orden"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:93
+#, c-format
+msgid "claimed"
+msgstr "reclamado"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:110
+#: src/paths/instance/orders/details/DetailPage.tsx:261
+#: src/paths/instance/orders/list/Table.tsx:136
+#, c-format
+msgid "copy url"
+msgstr "copiar url"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:126
+#: src/paths/instance/orders/details/DetailPage.tsx:349
+#, c-format
+msgid "pay at"
+msgstr "pagar en"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:127
+#: src/paths/instance/orders/details/DetailPage.tsx:350
+#, c-format
+msgid "created at"
+msgstr "creado"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:138
+#: src/paths/instance/orders/details/DetailPage.tsx:289
+#, c-format
+msgid "Timeline"
+msgstr "Cronología"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:142
+#: src/paths/instance/orders/details/DetailPage.tsx:293
+#, c-format
+msgid "Payment details"
+msgstr "Detalles de pago"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:146
+#: src/paths/instance/orders/details/DetailPage.tsx:299
+#: src/paths/instance/orders/details/DetailPage.tsx:363
+#, fuzzy, c-format
+msgid "Order status"
+msgstr "Estado de orden"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:156
+#: src/paths/instance/orders/details/DetailPage.tsx:308
+#, fuzzy, c-format
+msgid "Product list"
+msgstr "Lista de producto"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:236
+#, c-format
+msgid "paid"
+msgstr "pagados"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:238
+#, c-format
+msgid "wired"
+msgstr "transferido"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:241
+#, c-format
+msgid "refunded"
+msgstr "reembolzado"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:258
+#, c-format
+msgid "refund"
+msgstr "reembolzar"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:297
+#, c-format
+msgid "Refunded amount"
+msgstr "Monto reembolzado"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:298
+#, c-format
+msgid "Deposit total"
+msgstr "Total depositado"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:336
+#, c-format
+msgid "unpaid"
+msgstr "impago"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:364
+#, c-format
+msgid "Order status URL"
+msgstr "URL de estado de orden"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:365
+#, c-format
+msgid "Pay URI"
+msgstr "URI de pago"
+
+#: src/paths/instance/orders/details/DetailPage.tsx:383
+#, c-format
+msgid ""
+"Unknown order status. This is an error, please contact the administrator."
+msgstr ""
+"Estado de orden desconocido. Esto es un error, por favor contacte a su "
+"administrador"
+
+#: src/paths/instance/orders/details/index.tsx:56
+#: src/paths/instance/orders/list/index.tsx:147
+#, c-format
+msgid "refund created successfully"
+msgstr "reembolzo creado satisfactoriamente"
+
+#: src/paths/instance/orders/details/index.tsx:59
+#: src/paths/instance/orders/list/index.tsx:150
+#, fuzzy, c-format
+msgid "could not create the refund"
+msgstr "No se pudo aceder al servidor"
+
+#: src/paths/instance/orders/list/Table.tsx:111
+#, c-format
+msgid "load newer orders"
+msgstr "cargar nuevas ordenes"
+
+#: src/paths/instance/orders/list/Table.tsx:115
+#, c-format
+msgid "Date"
+msgstr "Fecha"
+
+#: src/paths/instance/orders/list/Table.tsx:131
+#: src/paths/instance/orders/list/Table.tsx:223
+#, c-format
+msgid "Refund"
+msgstr "Reembolzar"
+
+#: src/paths/instance/orders/list/Table.tsx:145
+#, c-format
+msgid "load older orders"
+msgstr "cargar viejas ordenes"
+
+#: src/paths/instance/orders/list/Table.tsx:154
+#, c-format
+msgid "No orders has been found"
+msgstr "No se enconraron ordenes"
+
+#: src/paths/instance/orders/list/Table.tsx:202
+#, c-format
+msgid "date"
+msgstr "fecha"
+
+#: src/paths/instance/orders/list/Table.tsx:203
+#, c-format
+msgid "amount"
+msgstr "monto"
+
+#: src/paths/instance/orders/list/Table.tsx:204
+#, c-format
+msgid "reason"
+msgstr "razón"
+
+#: src/paths/instance/orders/list/Table.tsx:224
+#, c-format
+msgid "Max refundable:"
+msgstr "Máximo reembolzable:"
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "Reason"
+msgstr "Razón"
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "duplicated"
+msgstr "duplicado"
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "requested by the customer"
+msgstr "pedido por el consumidor"
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "other"
+msgstr "otro"
+
+#: src/paths/instance/orders/list/index.tsx:91
+#, c-format
+msgid "go to order id"
+msgstr "ir a id de orden"
+
+#: src/paths/instance/orders/list/index.tsx:107
+#, c-format
+msgid "Paid"
+msgstr "Pagado"
+
+#: src/paths/instance/orders/list/index.tsx:108
+#, fuzzy, c-format
+msgid "Refunded"
+msgstr "Reembolzado"
+
+#: src/paths/instance/orders/list/index.tsx:109
+#, fuzzy, c-format
+msgid "Not wired"
+msgstr "No transferido"
+
+#: src/paths/instance/orders/list/index.tsx:110
+#, c-format
+msgid "All"
+msgstr "Todo"
+
+#: src/paths/instance/products/create/index.tsx:48
+#: src/paths/instance/products/update/index.tsx:64
+#, c-format
+msgid "could not create product"
+msgstr "no se pudo crear el producto"
+
+#: src/paths/instance/products/list/Table.tsx:87
+#, c-format
+msgid "Sell"
+msgstr "Venta"
+
+#: src/paths/instance/products/list/Table.tsx:89
+#, c-format
+msgid "Profit"
+msgstr "Ganancia"
+
+#: src/paths/instance/products/list/Table.tsx:91
+#, c-format
+msgid "Sold"
+msgstr "Vendido"
+
+#: src/paths/instance/products/list/index.tsx:59
+#, c-format
+msgid "product updated successfully"
+msgstr "producto actualizado correctamente"
+
+#: src/paths/instance/products/list/index.tsx:62
+#, c-format
+msgid "could not update the product"
+msgstr "no se pudo actualizar el producto"
+
+#: src/paths/instance/products/list/index.tsx:70
+#, c-format
+msgid "product delete successfully"
+msgstr "producto fue eliminado correctamente"
+
+#: src/paths/instance/products/list/index.tsx:73
+#, c-format
+msgid "could not delete the product"
+msgstr "no se pudo eliminar el producto"
+
+#: src/paths/instance/tips/list/Table.tsx:59
+#, c-format
+msgid "Tips"
+msgstr "Propinas"
+
+#: src/paths/instance/tips/list/Table.tsx:111
+#, c-format
+msgid "Committed amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:112
+#, c-format
+msgid "Exchange initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:113
+#, c-format
+msgid "Merchant initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:148
+#, c-format
+msgid "There is no tips yet, add more pressing the + sign"
+msgstr "No hay propinas todavía, agregar mas presionando el signo +"
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:50
+#: src/paths/instance/transfers/create/CreatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:56
+#, c-format
+msgid "cannot be empty"
+msgstr "no puede ser vacío"
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:51
+#, c-format
+msgid "check the id, doest look valid"
+msgstr "verificar el id, no parece válido"
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:52
+#, c-format
+msgid "should have 52 characters, current %1$s"
+msgstr "debería tener 52 caracteres, actualmente %1$s"
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:57
+#, c-format
+msgid "URL doesn't have the right format"
+msgstr "La URL no tiene el formato correcto"
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:74
+#, fuzzy, c-format
+msgid "Transfer ID"
+msgstr "Transferencias"
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:76
+#, fuzzy, c-format
+msgid "Account Address"
+msgstr "Dirección de cuenta"
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:82
+#: src/paths/instance/transfers/list/Table.tsx:125
+#, c-format
+msgid "Exchange URL"
+msgstr "URL del Exchange"
+
+#: src/paths/instance/transfers/create/index.tsx:49
+#, fuzzy, c-format
+msgid "could not inform transfer"
+msgstr "no se pudo crear la instancia"
+
+#: src/paths/instance/transfers/list/Table.tsx:118
+#, fuzzy, c-format
+msgid "load newer transfers"
+msgstr "cargar nuevas ordenes"
+
+#: src/paths/instance/transfers/list/Table.tsx:123
+#, c-format
+msgid "Credit"
+msgstr "Crédito"
+
+#: src/paths/instance/transfers/list/Table.tsx:126
+#, fuzzy, c-format
+msgid "Confirmed"
+msgstr "Confirmar"
+
+#: src/paths/instance/transfers/list/Table.tsx:127
+#: src/paths/instance/transfers/list/index.tsx:60
+#, c-format
+msgid "Verified"
+msgstr "Verificado"
+
+#: src/paths/instance/transfers/list/Table.tsx:128
+#, fuzzy, c-format
+msgid "Executed at"
+msgstr "creado"
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "yes"
+msgstr "si"
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "no"
+msgstr "no"
+
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "unknown"
+msgstr "desconocido"
+
+#: src/paths/instance/transfers/list/Table.tsx:145
+#, fuzzy, c-format
+msgid "load older transfers"
+msgstr "cargar viejas transferencias"
+
+#: src/paths/instance/transfers/list/Table.tsx:154
+#, c-format
+msgid "There is no transfer yet, add more pressing the + sign"
+msgstr "No hay transferencias todavía, agregar mas presionando el signo +"
diff --git a/packages/backend/src/i18n/fr.po b/packages/backend/src/i18n/fr.po
new file mode 100644
index 0000000..6b35bd0
--- /dev/null
+++ b/packages/backend/src/i18n/fr.po
@@ -0,0 +1,1057 @@
+# This file is part of TALER
+# (C) 2016 GNUnet e.V.
+#
+# 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.
+#
+# 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
+# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Taler Wallet\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-11-23 00:00+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:299
+#, c-format
+msgid "Access denied"
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:300
+#, c-format
+msgid "Check your token is valid"
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:72
+#, c-format
+msgid "Couldn't access the server."
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:73
+#, c-format
+msgid "Could not infer instance id from url %1$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:109
+#, c-format
+msgid "HTTP status #%1$s: Server reported a problem"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:110
+#, c-format
+msgid "Got message: \"%1$s\" from: %2$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:127
+#, c-format
+msgid "No default instance"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:128
+#, c-format
+msgid ""
+"in order to use merchant backoffice, you should create the default instance"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:288
+#, c-format
+msgid "Server reported a problem: HTTP status #%1$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:289
+#, c-format
+msgid "Got message: %1$s from: %2$s"
+msgstr ""
+
+#: src/components/exception/login.tsx:46
+#, c-format
+msgid "Login required"
+msgstr ""
+
+#: src/components/exception/login.tsx:49
+#, c-format
+msgid ""
+"Please enter your auth token. Token should have \"secret-token:\" and start "
+"with Bearer or ApiKey"
+msgstr ""
+
+#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53
+#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115
+#: src/paths/instance/orders/create/CreatePage.tsx:325
+#: src/paths/instance/products/create/CreatePage.tsx:51
+#: src/paths/instance/products/list/Table.tsx:174
+#: src/paths/instance/products/list/Table.tsx:228
+#: src/paths/instance/products/update/UpdatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:134
+#, c-format
+msgid "Confirm"
+msgstr ""
+
+#: src/components/form/InputArray.tsx:72
+#, c-format
+msgid "The value %1$s is invalid for a payment url"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:67
+#: src/paths/instance/orders/list/index.tsx:123
+#, c-format
+msgid "pick a date"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:81
+#, c-format
+msgid "clear"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:83
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "never"
+msgstr ""
+
+#: src/components/form/InputImage.tsx:80
+#, c-format
+msgid "Image should be smaller than 1 MB"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:28
+#, c-format
+msgid "Country"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:30
+#: src/paths/admin/create/CreatePage.tsx:99
+#: src/paths/instance/transfers/list/Table.tsx:124
+#: src/paths/instance/update/UpdatePage.tsx:118
+#, c-format
+msgid "Address"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:34
+#, c-format
+msgid "Building number"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:35
+#, c-format
+msgid "Building name"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:36
+#, c-format
+msgid "Street"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:37
+#, c-format
+msgid "Post code"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:38
+#, c-format
+msgid "Town location"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:39
+#, c-format
+msgid "Town"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:40
+#, c-format
+msgid "District"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:41
+#, c-format
+msgid "Country subdivision"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:59
+#, c-format
+msgid "Product id"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:60
+#: src/components/product/ProductForm.tsx:99
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122
+#: src/paths/instance/orders/list/Table.tsx:227
+#: src/paths/instance/products/list/Table.tsx:86
+#, c-format
+msgid "Description"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:73
+#: src/components/form/InputTaxes.tsx:81
+#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110
+#: src/paths/instance/details/DetailPage.tsx:76
+#: src/paths/instance/update/UpdatePage.tsx:106
+#, c-format
+msgid "Name"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:102
+#, c-format
+msgid "loading..."
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:108
+#, c-format
+msgid "no products found"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:116
+#, c-format
+msgid "no results"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:33
+#, c-format
+msgid "Deleting"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:34
+#, c-format
+msgid "Changing"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:60
+#, c-format
+msgid "Manage token"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:83
+#, c-format
+msgid "Update"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:100
+#: src/paths/instance/orders/create/CreatePage.tsx:252
+#: src/paths/instance/orders/create/CreatePage.tsx:273
+#, c-format
+msgid "Remove"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52
+#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114
+#: src/paths/instance/orders/create/CreatePage.tsx:324
+#: src/paths/instance/products/create/CreatePage.tsx:50
+#: src/paths/instance/products/list/Table.tsx:166
+#: src/paths/instance/products/list/Table.tsx:218
+#: src/paths/instance/products/update/UpdatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:88
+#: src/paths/instance/update/UpdatePage.tsx:133
+#, c-format
+msgid "Cancel"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:91
+#, c-format
+msgid "Manage stock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:93
+#, c-format
+msgid "Infinite"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:105
+#, c-format
+msgid "lost cannot be greater that current + incoming (max %1$s)"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:111
+#, c-format
+msgid "current stock will change from %1$s to %2$s"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:112
+#, c-format
+msgid "current stock will stay at %1$s"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:129
+#: src/paths/instance/products/list/Table.tsx:204
+#, c-format
+msgid "Incoming"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:130
+#: src/paths/instance/products/list/Table.tsx:205
+#, c-format
+msgid "Lost"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:142
+#, c-format
+msgid "Current"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:145
+#, c-format
+msgid "without stock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:150
+#, c-format
+msgid "Next restock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:152
+#, c-format
+msgid "Delivery address"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:73
+#, c-format
+msgid "this product has no taxes"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:77
+#: src/paths/instance/orders/details/DetailPage.tsx:145
+#: src/paths/instance/orders/details/DetailPage.tsx:296
+#: src/paths/instance/orders/list/Table.tsx:116
+#: src/paths/instance/transfers/create/CreatePage.tsx:84
+#, c-format
+msgid "Amount"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:78
+#, c-format
+msgid "currency and value separated with colon"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:84
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:78
+#, c-format
+msgid "Add"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:53
+#, c-format
+msgid "Instance"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:59
+#, c-format
+msgid "Settings"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:65
+#: src/paths/instance/orders/list/Table.tsx:60
+#, c-format
+msgid "Orders"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:71
+#: src/paths/instance/orders/create/CreatePage.tsx:258
+#: src/paths/instance/products/list/Table.tsx:48
+#, c-format
+msgid "Products"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:77
+#: src/paths/instance/transfers/list/Table.tsx:65
+#, c-format
+msgid "Transfers"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:87
+#, c-format
+msgid "Connection"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57
+#, c-format
+msgid "Instances"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:116
+#, c-format
+msgid "New"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:122
+#, c-format
+msgid "List"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:129
+#, c-format
+msgid "Log out"
+msgstr ""
+
+#: src/components/modal/index.tsx:74
+#, c-format
+msgid "Clear"
+msgstr ""
+
+#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111
+#, c-format
+msgid "should be the same"
+msgstr ""
+
+#: src/components/modal/index.tsx:111
+#, c-format
+msgid "cannot be the same as before"
+msgstr ""
+
+#: src/components/modal/index.tsx:114
+#, c-format
+msgid ""
+"You are updating the authorization token from instance %1$s with id %2$s"
+msgstr ""
+
+#: src/components/modal/index.tsx:124
+#, c-format
+msgid "Old token"
+msgstr ""
+
+#: src/components/modal/index.tsx:125
+#, c-format
+msgid "New token"
+msgstr ""
+
+#: src/components/modal/index.tsx:127
+#, c-format
+msgid "Clearing the auth token will mean public access to the instance"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:96
+#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109
+#: src/paths/instance/transfers/list/Table.tsx:122
+#, c-format
+msgid "ID"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:98
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121
+#: src/paths/instance/products/list/Table.tsx:85
+#, c-format
+msgid "Image"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:100
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123
+#, c-format
+msgid "Unit"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:101
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124
+#: src/paths/instance/products/list/Table.tsx:162
+#: src/paths/instance/products/list/Table.tsx:214
+#, c-format
+msgid "Price"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:103
+#: src/paths/instance/products/list/Table.tsx:90
+#, c-format
+msgid "Stock"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:105
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128
+#: src/paths/instance/products/list/Table.tsx:88
+#, c-format
+msgid "Taxes"
+msgstr ""
+
+#: src/index.tsx:75
+#, c-format
+msgid "Server not found"
+msgstr ""
+
+#: src/index.tsx:85
+#, c-format
+msgid "Couldn't access the server"
+msgstr ""
+
+#: src/index.tsx:87 src/index.tsx:99
+#, c-format
+msgid "Got message %1$s from %2$s"
+msgstr ""
+
+#: src/index.tsx:97
+#, c-format
+msgid "Unexpected Error"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:108
+#, c-format
+msgid "Auth token"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:91
+#: src/paths/instance/details/DetailPage.tsx:77
+#: src/paths/instance/update/UpdatePage.tsx:110
+#, c-format
+msgid "Account address"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:93
+#: src/paths/instance/update/UpdatePage.tsx:112
+#, c-format
+msgid "Default max deposit fee"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:95
+#: src/paths/instance/update/UpdatePage.tsx:114
+#, c-format
+msgid "Default max wire fee"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:97
+#: src/paths/instance/update/UpdatePage.tsx:116
+#, c-format
+msgid "Default wire fee amortization"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:103
+#: src/paths/instance/update/UpdatePage.tsx:122
+#, c-format
+msgid "Jurisdiction"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:107
+#: src/paths/instance/update/UpdatePage.tsx:126
+#, c-format
+msgid "Default pay delay"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:109
+#: src/paths/instance/update/UpdatePage.tsx:128
+#, c-format
+msgid "Default wire transfer delay"
+msgstr ""
+
+#: src/paths/admin/create/index.tsx:58
+#, c-format
+msgid "could not create instance"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131
+#: src/paths/instance/transfers/list/Table.tsx:71
+#, c-format
+msgid "Delete"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:128
+#, c-format
+msgid "Edit"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:149
+#: src/paths/instance/products/list/Table.tsx:245
+#, c-format
+msgid "There is no instances yet, add more pressing the + sign"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:237
+#, c-format
+msgid "Inventory products"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:286
+#, c-format
+msgid "Total price"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:287
+#, c-format
+msgid "Total tax"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:289
+#: src/paths/instance/orders/create/CreatePage.tsx:297
+#, c-format
+msgid "Order price"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:295
+#, c-format
+msgid "Net"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:300
+#: src/paths/instance/orders/details/DetailPage.tsx:144
+#: src/paths/instance/orders/details/DetailPage.tsx:295
+#: src/paths/instance/orders/list/Table.tsx:117
+#, c-format
+msgid "Summary"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:302
+#, c-format
+msgid "Payments options"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:303
+#, c-format
+msgid "Auto refund deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:304
+#, c-format
+msgid "Refund deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:305
+#, c-format
+msgid "Pay deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:307
+#, c-format
+msgid "Delivery date"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:308
+#, c-format
+msgid "Location"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:312
+#, c-format
+msgid "Max fee"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:313
+#, c-format
+msgid "Max wire fee"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:314
+#, c-format
+msgid "Wire fee amortization"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:315
+#, c-format
+msgid "Fullfilment url"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:318
+#, c-format
+msgid "Extra information"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:44
+#, c-format
+msgid "select a product first"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:51
+#, c-format
+msgid "should be greater than 0"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:58
+#, c-format
+msgid ""
+"cannot be greater than current stock and quantity previously added. max: %1$s"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:64
+#, c-format
+msgid "cannot be greater than current stock %1$s"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:76
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126
+#, c-format
+msgid "Quantity"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:92
+#: src/paths/instance/orders/details/DetailPage.tsx:235
+#: src/paths/instance/orders/details/DetailPage.tsx:333
+#, c-format
+msgid "Order"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:93
+#, c-format
+msgid "claimed"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:110
+#: src/paths/instance/orders/details/DetailPage.tsx:261
+#: src/paths/instance/orders/list/Table.tsx:136
+#, c-format
+msgid "copy url"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:126
+#: src/paths/instance/orders/details/DetailPage.tsx:349
+#, c-format
+msgid "pay at"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:127
+#: src/paths/instance/orders/details/DetailPage.tsx:350
+#, c-format
+msgid "created at"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:138
+#: src/paths/instance/orders/details/DetailPage.tsx:289
+#, c-format
+msgid "Timeline"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:142
+#: src/paths/instance/orders/details/DetailPage.tsx:293
+#, c-format
+msgid "Payment details"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:146
+#: src/paths/instance/orders/details/DetailPage.tsx:299
+#: src/paths/instance/orders/details/DetailPage.tsx:363
+#, c-format
+msgid "Order status"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:156
+#: src/paths/instance/orders/details/DetailPage.tsx:308
+#, c-format
+msgid "Product list"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:236
+#, c-format
+msgid "paid"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:238
+#, c-format
+msgid "wired"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:241
+#, c-format
+msgid "refunded"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:258
+#, c-format
+msgid "refund"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:297
+#, c-format
+msgid "Refunded amount"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:298
+#, c-format
+msgid "Deposit total"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:336
+#, c-format
+msgid "unpaid"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:364
+#, c-format
+msgid "Order status URL"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:365
+#, c-format
+msgid "Pay URI"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:383
+#, c-format
+msgid ""
+"Unknown order status. This is an error, please contact the administrator."
+msgstr ""
+
+#: src/paths/instance/orders/details/index.tsx:56
+#: src/paths/instance/orders/list/index.tsx:147
+#, c-format
+msgid "refund created successfully"
+msgstr ""
+
+#: src/paths/instance/orders/details/index.tsx:59
+#: src/paths/instance/orders/list/index.tsx:150
+#, c-format
+msgid "could not create the refund"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:111
+#, c-format
+msgid "load newer orders"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:115
+#, c-format
+msgid "Date"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:131
+#: src/paths/instance/orders/list/Table.tsx:223
+#, c-format
+msgid "Refund"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:145
+#, c-format
+msgid "load older orders"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:154
+#, c-format
+msgid "No orders has been found"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:202
+#, c-format
+msgid "date"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:203
+#, c-format
+msgid "amount"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:204
+#, c-format
+msgid "reason"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:224
+#, c-format
+msgid "Max refundable:"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "Reason"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "duplicated"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "requested by the customer"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "other"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:91
+#, c-format
+msgid "go to order id"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:107
+#, c-format
+msgid "Paid"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:108
+#, c-format
+msgid "Refunded"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:109
+#, c-format
+msgid "Not wired"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:110
+#, c-format
+msgid "All"
+msgstr ""
+
+#: src/paths/instance/products/create/index.tsx:48
+#: src/paths/instance/products/update/index.tsx:64
+#, c-format
+msgid "could not create product"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:87
+#, c-format
+msgid "Sell"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:89
+#, c-format
+msgid "Profit"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:91
+#, c-format
+msgid "Sold"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:59
+#, c-format
+msgid "product updated successfully"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:62
+#, c-format
+msgid "could not update the product"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:70
+#, c-format
+msgid "product delete successfully"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:73
+#, c-format
+msgid "could not delete the product"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:59
+#, c-format
+msgid "Tips"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:111
+#, c-format
+msgid "Committed amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:112
+#, c-format
+msgid "Exchange initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:113
+#, c-format
+msgid "Merchant initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:148
+#, c-format
+msgid "There is no tips yet, add more pressing the + sign"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:50
+#: src/paths/instance/transfers/create/CreatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:56
+#, c-format
+msgid "cannot be empty"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:51
+#, c-format
+msgid "check the id, doest look valid"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:52
+#, c-format
+msgid "should have 52 characters, current %1$s"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:57
+#, c-format
+msgid "URL doesn't have the right format"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:74
+#, c-format
+msgid "Transfer ID"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:76
+#, c-format
+msgid "Account Address"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:82
+#: src/paths/instance/transfers/list/Table.tsx:125
+#, c-format
+msgid "Exchange URL"
+msgstr ""
+
+#: src/paths/instance/transfers/create/index.tsx:49
+#, c-format
+msgid "could not inform transfer"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:118
+#, c-format
+msgid "load newer transfers"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:123
+#, c-format
+msgid "Credit"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:126
+#, c-format
+msgid "Confirmed"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:127
+#: src/paths/instance/transfers/list/index.tsx:60
+#, c-format
+msgid "Verified"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:128
+#, c-format
+msgid "Executed at"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "yes"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "no"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "unknown"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:145
+#, c-format
+msgid "load older transfers"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:154
+#, c-format
+msgid "There is no transfer yet, add more pressing the + sign"
+msgstr ""
diff --git a/packages/backend/src/i18n/index.tsx b/packages/backend/src/i18n/index.tsx
new file mode 100644
index 0000000..63c8e19
--- /dev/null
+++ b/packages/backend/src/i18n/index.tsx
@@ -0,0 +1,203 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/>
+ */
+
+/**
+ * Translation helpers for React components and template literals.
+ */
+
+/**
+ * Imports
+ */
+import { ComponentChild, ComponentChildren, h, Fragment, VNode } from "preact";
+
+import { useTranslationContext } from "../context/translation";
+
+export function useTranslator() {
+ const ctx = useTranslationContext();
+ const jed = ctx.handler
+ return function str(stringSeq: TemplateStringsArray, ...values: any[]): string {
+ const s = toI18nString(stringSeq);
+ if (!s) return s
+ const tr = jed
+ .translate(s)
+ .ifPlural(1, s)
+ .fetch(...values);
+ return tr;
+ }
+}
+
+
+/**
+ * Convert template strings to a msgid
+ */
+ function toI18nString(stringSeq: ReadonlyArray<string>): string {
+ let s = "";
+ for (let i = 0; i < stringSeq.length; i++) {
+ s += stringSeq[i];
+ if (i < stringSeq.length - 1) {
+ s += `%${i + 1}$s`;
+ }
+ }
+ return s;
+}
+
+
+interface TranslateSwitchProps {
+ target: number;
+ children: ComponentChildren;
+}
+
+function stringifyChildren(children: ComponentChildren): string {
+ let n = 1;
+ const ss = (children instanceof Array ? children : [children]).map((c) => {
+ if (typeof c === "string") {
+ return c;
+ }
+ return `%${n++}$s`;
+ });
+ const s = ss.join("").replace(/ +/g, " ").trim();
+ return s;
+}
+
+interface TranslateProps {
+ children: ComponentChildren;
+ /**
+ * Component that the translated element should be wrapped in.
+ * Defaults to "div".
+ */
+ wrap?: any;
+
+ /**
+ * Props to give to the wrapped component.
+ */
+ wrapProps?: any;
+}
+
+function getTranslatedChildren(
+ translation: string,
+ children: ComponentChildren,
+): ComponentChild[] {
+ const tr = translation.split(/%(\d+)\$s/);
+ const childArray = children instanceof Array ? children : [children];
+ // Merge consecutive string children.
+ const placeholderChildren = Array<ComponentChild>();
+ for (let i = 0; i < childArray.length; i++) {
+ const x = childArray[i];
+ if (x === undefined) {
+ continue;
+ } else if (typeof x === "string") {
+ continue;
+ } else {
+ placeholderChildren.push(x);
+ }
+ }
+ const result = Array<ComponentChild>();
+ for (let i = 0; i < tr.length; i++) {
+ if (i % 2 == 0) {
+ // Text
+ result.push(tr[i]);
+ } else {
+ const childIdx = Number.parseInt(tr[i],10) - 1;
+ result.push(placeholderChildren[childIdx]);
+ }
+ }
+ return result;
+}
+
+/**
+ * Translate text node children of this component.
+ * If a child component might produce a text node, it must be wrapped
+ * in a another non-text element.
+ *
+ * Example:
+ * ```
+ * <Translate>
+ * Hello. Your score is <span><PlayerScore player={player} /></span>
+ * </Translate>
+ * ```
+ */
+export function Translate({ children }: TranslateProps): VNode {
+ const s = stringifyChildren(children);
+ const ctx = useTranslationContext()
+ const translation: string = ctx.handler.ngettext(s, s, 1);
+ const result = getTranslatedChildren(translation, children)
+ return <Fragment>{result}</Fragment>;
+}
+
+/**
+ * Switch translation based on singular or plural based on the target prop.
+ * Should only contain TranslateSingular and TransplatePlural as children.
+ *
+ * Example:
+ * ```
+ * <TranslateSwitch target={n}>
+ * <TranslateSingular>I have {n} apple.</TranslateSingular>
+ * <TranslatePlural>I have {n} apples.</TranslatePlural>
+ * </TranslateSwitch>
+ * ```
+ */
+export function TranslateSwitch({ children, target }: TranslateSwitchProps) {
+ let singular: VNode<TranslationPluralProps> | undefined;
+ let plural: VNode<TranslationPluralProps> | undefined;
+ // const children = this.props.children;
+ if (children) {
+ (children instanceof Array ? children : [children]).forEach((child: any) => {
+ if (child.type === TranslatePlural) {
+ plural = child;
+ }
+ if (child.type === TranslateSingular) {
+ singular = child;
+ }
+ });
+ }
+ if (!singular || !plural) {
+ console.error("translation not found");
+ return h("span", {}, ["translation not found"]);
+ }
+ singular.props.target = target;
+ plural.props.target = target;
+ // We're looking up the translation based on the
+ // singular, even if we must use the plural form.
+ return singular;
+}
+
+interface TranslationPluralProps {
+ children: ComponentChildren;
+ target: number;
+}
+
+/**
+ * See [[TranslateSwitch]].
+ */
+export function TranslatePlural({ children, target }: TranslationPluralProps): VNode {
+ const s = stringifyChildren(children);
+ const ctx = useTranslationContext()
+ const translation = ctx.handler.ngettext(s, s, 1);
+ const result = getTranslatedChildren(translation, children);
+ return <Fragment>{result}</Fragment>;
+}
+
+/**
+ * See [[TranslateSwitch]].
+ */
+export function TranslateSingular({ children, target }: TranslationPluralProps): VNode {
+ const s = stringifyChildren(children);
+ const ctx = useTranslationContext()
+ const translation = ctx.handler.ngettext(s, s, target);
+ const result = getTranslatedChildren(translation, children);
+ return <Fragment>{result}</Fragment>;
+
+}
diff --git a/packages/backend/src/i18n/it.po b/packages/backend/src/i18n/it.po
new file mode 100644
index 0000000..6b35bd0
--- /dev/null
+++ b/packages/backend/src/i18n/it.po
@@ -0,0 +1,1057 @@
+# This file is part of TALER
+# (C) 2016 GNUnet e.V.
+#
+# 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.
+#
+# 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
+# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Taler Wallet\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-11-23 00:00+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:299
+#, c-format
+msgid "Access denied"
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:300
+#, c-format
+msgid "Check your token is valid"
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:72
+#, c-format
+msgid "Couldn't access the server."
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:73
+#, c-format
+msgid "Could not infer instance id from url %1$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:109
+#, c-format
+msgid "HTTP status #%1$s: Server reported a problem"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:110
+#, c-format
+msgid "Got message: \"%1$s\" from: %2$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:127
+#, c-format
+msgid "No default instance"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:128
+#, c-format
+msgid ""
+"in order to use merchant backoffice, you should create the default instance"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:288
+#, c-format
+msgid "Server reported a problem: HTTP status #%1$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:289
+#, c-format
+msgid "Got message: %1$s from: %2$s"
+msgstr ""
+
+#: src/components/exception/login.tsx:46
+#, c-format
+msgid "Login required"
+msgstr ""
+
+#: src/components/exception/login.tsx:49
+#, c-format
+msgid ""
+"Please enter your auth token. Token should have \"secret-token:\" and start "
+"with Bearer or ApiKey"
+msgstr ""
+
+#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53
+#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115
+#: src/paths/instance/orders/create/CreatePage.tsx:325
+#: src/paths/instance/products/create/CreatePage.tsx:51
+#: src/paths/instance/products/list/Table.tsx:174
+#: src/paths/instance/products/list/Table.tsx:228
+#: src/paths/instance/products/update/UpdatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:134
+#, c-format
+msgid "Confirm"
+msgstr ""
+
+#: src/components/form/InputArray.tsx:72
+#, c-format
+msgid "The value %1$s is invalid for a payment url"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:67
+#: src/paths/instance/orders/list/index.tsx:123
+#, c-format
+msgid "pick a date"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:81
+#, c-format
+msgid "clear"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:83
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "never"
+msgstr ""
+
+#: src/components/form/InputImage.tsx:80
+#, c-format
+msgid "Image should be smaller than 1 MB"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:28
+#, c-format
+msgid "Country"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:30
+#: src/paths/admin/create/CreatePage.tsx:99
+#: src/paths/instance/transfers/list/Table.tsx:124
+#: src/paths/instance/update/UpdatePage.tsx:118
+#, c-format
+msgid "Address"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:34
+#, c-format
+msgid "Building number"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:35
+#, c-format
+msgid "Building name"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:36
+#, c-format
+msgid "Street"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:37
+#, c-format
+msgid "Post code"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:38
+#, c-format
+msgid "Town location"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:39
+#, c-format
+msgid "Town"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:40
+#, c-format
+msgid "District"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:41
+#, c-format
+msgid "Country subdivision"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:59
+#, c-format
+msgid "Product id"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:60
+#: src/components/product/ProductForm.tsx:99
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122
+#: src/paths/instance/orders/list/Table.tsx:227
+#: src/paths/instance/products/list/Table.tsx:86
+#, c-format
+msgid "Description"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:73
+#: src/components/form/InputTaxes.tsx:81
+#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110
+#: src/paths/instance/details/DetailPage.tsx:76
+#: src/paths/instance/update/UpdatePage.tsx:106
+#, c-format
+msgid "Name"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:102
+#, c-format
+msgid "loading..."
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:108
+#, c-format
+msgid "no products found"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:116
+#, c-format
+msgid "no results"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:33
+#, c-format
+msgid "Deleting"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:34
+#, c-format
+msgid "Changing"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:60
+#, c-format
+msgid "Manage token"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:83
+#, c-format
+msgid "Update"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:100
+#: src/paths/instance/orders/create/CreatePage.tsx:252
+#: src/paths/instance/orders/create/CreatePage.tsx:273
+#, c-format
+msgid "Remove"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52
+#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114
+#: src/paths/instance/orders/create/CreatePage.tsx:324
+#: src/paths/instance/products/create/CreatePage.tsx:50
+#: src/paths/instance/products/list/Table.tsx:166
+#: src/paths/instance/products/list/Table.tsx:218
+#: src/paths/instance/products/update/UpdatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:88
+#: src/paths/instance/update/UpdatePage.tsx:133
+#, c-format
+msgid "Cancel"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:91
+#, c-format
+msgid "Manage stock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:93
+#, c-format
+msgid "Infinite"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:105
+#, c-format
+msgid "lost cannot be greater that current + incoming (max %1$s)"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:111
+#, c-format
+msgid "current stock will change from %1$s to %2$s"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:112
+#, c-format
+msgid "current stock will stay at %1$s"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:129
+#: src/paths/instance/products/list/Table.tsx:204
+#, c-format
+msgid "Incoming"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:130
+#: src/paths/instance/products/list/Table.tsx:205
+#, c-format
+msgid "Lost"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:142
+#, c-format
+msgid "Current"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:145
+#, c-format
+msgid "without stock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:150
+#, c-format
+msgid "Next restock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:152
+#, c-format
+msgid "Delivery address"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:73
+#, c-format
+msgid "this product has no taxes"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:77
+#: src/paths/instance/orders/details/DetailPage.tsx:145
+#: src/paths/instance/orders/details/DetailPage.tsx:296
+#: src/paths/instance/orders/list/Table.tsx:116
+#: src/paths/instance/transfers/create/CreatePage.tsx:84
+#, c-format
+msgid "Amount"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:78
+#, c-format
+msgid "currency and value separated with colon"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:84
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:78
+#, c-format
+msgid "Add"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:53
+#, c-format
+msgid "Instance"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:59
+#, c-format
+msgid "Settings"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:65
+#: src/paths/instance/orders/list/Table.tsx:60
+#, c-format
+msgid "Orders"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:71
+#: src/paths/instance/orders/create/CreatePage.tsx:258
+#: src/paths/instance/products/list/Table.tsx:48
+#, c-format
+msgid "Products"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:77
+#: src/paths/instance/transfers/list/Table.tsx:65
+#, c-format
+msgid "Transfers"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:87
+#, c-format
+msgid "Connection"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57
+#, c-format
+msgid "Instances"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:116
+#, c-format
+msgid "New"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:122
+#, c-format
+msgid "List"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:129
+#, c-format
+msgid "Log out"
+msgstr ""
+
+#: src/components/modal/index.tsx:74
+#, c-format
+msgid "Clear"
+msgstr ""
+
+#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111
+#, c-format
+msgid "should be the same"
+msgstr ""
+
+#: src/components/modal/index.tsx:111
+#, c-format
+msgid "cannot be the same as before"
+msgstr ""
+
+#: src/components/modal/index.tsx:114
+#, c-format
+msgid ""
+"You are updating the authorization token from instance %1$s with id %2$s"
+msgstr ""
+
+#: src/components/modal/index.tsx:124
+#, c-format
+msgid "Old token"
+msgstr ""
+
+#: src/components/modal/index.tsx:125
+#, c-format
+msgid "New token"
+msgstr ""
+
+#: src/components/modal/index.tsx:127
+#, c-format
+msgid "Clearing the auth token will mean public access to the instance"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:96
+#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109
+#: src/paths/instance/transfers/list/Table.tsx:122
+#, c-format
+msgid "ID"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:98
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121
+#: src/paths/instance/products/list/Table.tsx:85
+#, c-format
+msgid "Image"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:100
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123
+#, c-format
+msgid "Unit"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:101
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124
+#: src/paths/instance/products/list/Table.tsx:162
+#: src/paths/instance/products/list/Table.tsx:214
+#, c-format
+msgid "Price"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:103
+#: src/paths/instance/products/list/Table.tsx:90
+#, c-format
+msgid "Stock"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:105
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128
+#: src/paths/instance/products/list/Table.tsx:88
+#, c-format
+msgid "Taxes"
+msgstr ""
+
+#: src/index.tsx:75
+#, c-format
+msgid "Server not found"
+msgstr ""
+
+#: src/index.tsx:85
+#, c-format
+msgid "Couldn't access the server"
+msgstr ""
+
+#: src/index.tsx:87 src/index.tsx:99
+#, c-format
+msgid "Got message %1$s from %2$s"
+msgstr ""
+
+#: src/index.tsx:97
+#, c-format
+msgid "Unexpected Error"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:108
+#, c-format
+msgid "Auth token"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:91
+#: src/paths/instance/details/DetailPage.tsx:77
+#: src/paths/instance/update/UpdatePage.tsx:110
+#, c-format
+msgid "Account address"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:93
+#: src/paths/instance/update/UpdatePage.tsx:112
+#, c-format
+msgid "Default max deposit fee"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:95
+#: src/paths/instance/update/UpdatePage.tsx:114
+#, c-format
+msgid "Default max wire fee"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:97
+#: src/paths/instance/update/UpdatePage.tsx:116
+#, c-format
+msgid "Default wire fee amortization"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:103
+#: src/paths/instance/update/UpdatePage.tsx:122
+#, c-format
+msgid "Jurisdiction"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:107
+#: src/paths/instance/update/UpdatePage.tsx:126
+#, c-format
+msgid "Default pay delay"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:109
+#: src/paths/instance/update/UpdatePage.tsx:128
+#, c-format
+msgid "Default wire transfer delay"
+msgstr ""
+
+#: src/paths/admin/create/index.tsx:58
+#, c-format
+msgid "could not create instance"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131
+#: src/paths/instance/transfers/list/Table.tsx:71
+#, c-format
+msgid "Delete"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:128
+#, c-format
+msgid "Edit"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:149
+#: src/paths/instance/products/list/Table.tsx:245
+#, c-format
+msgid "There is no instances yet, add more pressing the + sign"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:237
+#, c-format
+msgid "Inventory products"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:286
+#, c-format
+msgid "Total price"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:287
+#, c-format
+msgid "Total tax"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:289
+#: src/paths/instance/orders/create/CreatePage.tsx:297
+#, c-format
+msgid "Order price"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:295
+#, c-format
+msgid "Net"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:300
+#: src/paths/instance/orders/details/DetailPage.tsx:144
+#: src/paths/instance/orders/details/DetailPage.tsx:295
+#: src/paths/instance/orders/list/Table.tsx:117
+#, c-format
+msgid "Summary"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:302
+#, c-format
+msgid "Payments options"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:303
+#, c-format
+msgid "Auto refund deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:304
+#, c-format
+msgid "Refund deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:305
+#, c-format
+msgid "Pay deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:307
+#, c-format
+msgid "Delivery date"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:308
+#, c-format
+msgid "Location"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:312
+#, c-format
+msgid "Max fee"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:313
+#, c-format
+msgid "Max wire fee"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:314
+#, c-format
+msgid "Wire fee amortization"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:315
+#, c-format
+msgid "Fullfilment url"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:318
+#, c-format
+msgid "Extra information"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:44
+#, c-format
+msgid "select a product first"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:51
+#, c-format
+msgid "should be greater than 0"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:58
+#, c-format
+msgid ""
+"cannot be greater than current stock and quantity previously added. max: %1$s"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:64
+#, c-format
+msgid "cannot be greater than current stock %1$s"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:76
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126
+#, c-format
+msgid "Quantity"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:92
+#: src/paths/instance/orders/details/DetailPage.tsx:235
+#: src/paths/instance/orders/details/DetailPage.tsx:333
+#, c-format
+msgid "Order"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:93
+#, c-format
+msgid "claimed"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:110
+#: src/paths/instance/orders/details/DetailPage.tsx:261
+#: src/paths/instance/orders/list/Table.tsx:136
+#, c-format
+msgid "copy url"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:126
+#: src/paths/instance/orders/details/DetailPage.tsx:349
+#, c-format
+msgid "pay at"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:127
+#: src/paths/instance/orders/details/DetailPage.tsx:350
+#, c-format
+msgid "created at"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:138
+#: src/paths/instance/orders/details/DetailPage.tsx:289
+#, c-format
+msgid "Timeline"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:142
+#: src/paths/instance/orders/details/DetailPage.tsx:293
+#, c-format
+msgid "Payment details"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:146
+#: src/paths/instance/orders/details/DetailPage.tsx:299
+#: src/paths/instance/orders/details/DetailPage.tsx:363
+#, c-format
+msgid "Order status"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:156
+#: src/paths/instance/orders/details/DetailPage.tsx:308
+#, c-format
+msgid "Product list"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:236
+#, c-format
+msgid "paid"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:238
+#, c-format
+msgid "wired"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:241
+#, c-format
+msgid "refunded"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:258
+#, c-format
+msgid "refund"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:297
+#, c-format
+msgid "Refunded amount"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:298
+#, c-format
+msgid "Deposit total"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:336
+#, c-format
+msgid "unpaid"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:364
+#, c-format
+msgid "Order status URL"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:365
+#, c-format
+msgid "Pay URI"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:383
+#, c-format
+msgid ""
+"Unknown order status. This is an error, please contact the administrator."
+msgstr ""
+
+#: src/paths/instance/orders/details/index.tsx:56
+#: src/paths/instance/orders/list/index.tsx:147
+#, c-format
+msgid "refund created successfully"
+msgstr ""
+
+#: src/paths/instance/orders/details/index.tsx:59
+#: src/paths/instance/orders/list/index.tsx:150
+#, c-format
+msgid "could not create the refund"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:111
+#, c-format
+msgid "load newer orders"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:115
+#, c-format
+msgid "Date"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:131
+#: src/paths/instance/orders/list/Table.tsx:223
+#, c-format
+msgid "Refund"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:145
+#, c-format
+msgid "load older orders"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:154
+#, c-format
+msgid "No orders has been found"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:202
+#, c-format
+msgid "date"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:203
+#, c-format
+msgid "amount"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:204
+#, c-format
+msgid "reason"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:224
+#, c-format
+msgid "Max refundable:"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "Reason"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "duplicated"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "requested by the customer"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "other"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:91
+#, c-format
+msgid "go to order id"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:107
+#, c-format
+msgid "Paid"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:108
+#, c-format
+msgid "Refunded"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:109
+#, c-format
+msgid "Not wired"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:110
+#, c-format
+msgid "All"
+msgstr ""
+
+#: src/paths/instance/products/create/index.tsx:48
+#: src/paths/instance/products/update/index.tsx:64
+#, c-format
+msgid "could not create product"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:87
+#, c-format
+msgid "Sell"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:89
+#, c-format
+msgid "Profit"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:91
+#, c-format
+msgid "Sold"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:59
+#, c-format
+msgid "product updated successfully"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:62
+#, c-format
+msgid "could not update the product"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:70
+#, c-format
+msgid "product delete successfully"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:73
+#, c-format
+msgid "could not delete the product"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:59
+#, c-format
+msgid "Tips"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:111
+#, c-format
+msgid "Committed amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:112
+#, c-format
+msgid "Exchange initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:113
+#, c-format
+msgid "Merchant initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:148
+#, c-format
+msgid "There is no tips yet, add more pressing the + sign"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:50
+#: src/paths/instance/transfers/create/CreatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:56
+#, c-format
+msgid "cannot be empty"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:51
+#, c-format
+msgid "check the id, doest look valid"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:52
+#, c-format
+msgid "should have 52 characters, current %1$s"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:57
+#, c-format
+msgid "URL doesn't have the right format"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:74
+#, c-format
+msgid "Transfer ID"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:76
+#, c-format
+msgid "Account Address"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:82
+#: src/paths/instance/transfers/list/Table.tsx:125
+#, c-format
+msgid "Exchange URL"
+msgstr ""
+
+#: src/paths/instance/transfers/create/index.tsx:49
+#, c-format
+msgid "could not inform transfer"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:118
+#, c-format
+msgid "load newer transfers"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:123
+#, c-format
+msgid "Credit"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:126
+#, c-format
+msgid "Confirmed"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:127
+#: src/paths/instance/transfers/list/index.tsx:60
+#, c-format
+msgid "Verified"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:128
+#, c-format
+msgid "Executed at"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "yes"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "no"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "unknown"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:145
+#, c-format
+msgid "load older transfers"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:154
+#, c-format
+msgid "There is no transfer yet, add more pressing the + sign"
+msgstr ""
diff --git a/packages/backend/src/i18n/poheader b/packages/backend/src/i18n/poheader
new file mode 100644
index 0000000..ee3fcd7
--- /dev/null
+++ b/packages/backend/src/i18n/poheader
@@ -0,0 +1,27 @@
+# This file is part of GNU Taler
+# (C) 2021 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/>
+
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Taler Wallet\n"
+"Report-Msgid-Bugs-To: taler@gnu.org\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
diff --git a/packages/backend/src/i18n/strings-prelude b/packages/backend/src/i18n/strings-prelude
new file mode 100644
index 0000000..cca13af
--- /dev/null
+++ b/packages/backend/src/i18n/strings-prelude
@@ -0,0 +1,19 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/>
+ */
+
+/*eslint quote-props: ["error", "consistent"]*/
+export const strings: {[s: string]: any} = {};
+
diff --git a/packages/backend/src/i18n/strings.ts b/packages/backend/src/i18n/strings.ts
new file mode 100644
index 0000000..63e9694
--- /dev/null
+++ b/packages/backend/src/i18n/strings.ts
@@ -0,0 +1,3445 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/>
+ */
+
+/*eslint quote-props: ["error", "consistent"]*/
+export const strings: {[s: string]: any} = {};
+
+strings['de'] = {
+ "domain": "messages",
+ "locale_data": {
+ "messages": {
+ "": {
+ "domain": "messages",
+ "plural_forms": "nplurals=2; plural=(n != 1);",
+ "lang": ""
+ },
+ "Access denied": [
+ ""
+ ],
+ "Check your token is valid": [
+ ""
+ ],
+ "Couldn't access the server.": [
+ ""
+ ],
+ "Could not infer instance id from url %1$s": [
+ ""
+ ],
+ "HTTP status #%1$s: Server reported a problem": [
+ ""
+ ],
+ "Got message: \"%1$s\" from: %2$s": [
+ ""
+ ],
+ "No default instance": [
+ ""
+ ],
+ "in order to use merchant backoffice, you should create the default instance": [
+ ""
+ ],
+ "Server reported a problem: HTTP status #%1$s": [
+ ""
+ ],
+ "Got message: %1$s from: %2$s": [
+ ""
+ ],
+ "Login required": [
+ ""
+ ],
+ "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [
+ ""
+ ],
+ "Confirm": [
+ ""
+ ],
+ "The value %1$s is invalid for a payment url": [
+ ""
+ ],
+ "pick a date": [
+ ""
+ ],
+ "clear": [
+ ""
+ ],
+ "never": [
+ ""
+ ],
+ "Image should be smaller than 1 MB": [
+ ""
+ ],
+ "Country": [
+ ""
+ ],
+ "Address": [
+ ""
+ ],
+ "Building number": [
+ ""
+ ],
+ "Building name": [
+ ""
+ ],
+ "Street": [
+ ""
+ ],
+ "Post code": [
+ ""
+ ],
+ "Town location": [
+ ""
+ ],
+ "Town": [
+ ""
+ ],
+ "District": [
+ ""
+ ],
+ "Country subdivision": [
+ ""
+ ],
+ "Product id": [
+ ""
+ ],
+ "Description": [
+ ""
+ ],
+ "Name": [
+ ""
+ ],
+ "loading...": [
+ ""
+ ],
+ "no products found": [
+ ""
+ ],
+ "no results": [
+ ""
+ ],
+ "Deleting": [
+ ""
+ ],
+ "Changing": [
+ ""
+ ],
+ "Manage token": [
+ ""
+ ],
+ "Update": [
+ ""
+ ],
+ "Remove": [
+ ""
+ ],
+ "Cancel": [
+ ""
+ ],
+ "Manage stock": [
+ ""
+ ],
+ "Infinite": [
+ ""
+ ],
+ "lost cannot be greater that current + incoming (max %1$s)": [
+ ""
+ ],
+ "current stock will change from %1$s to %2$s": [
+ ""
+ ],
+ "current stock will stay at %1$s": [
+ ""
+ ],
+ "Incoming": [
+ ""
+ ],
+ "Lost": [
+ ""
+ ],
+ "Current": [
+ ""
+ ],
+ "without stock": [
+ ""
+ ],
+ "Next restock": [
+ ""
+ ],
+ "Delivery address": [
+ ""
+ ],
+ "this product has no taxes": [
+ ""
+ ],
+ "Amount": [
+ ""
+ ],
+ "currency and value separated with colon": [
+ ""
+ ],
+ "Add": [
+ ""
+ ],
+ "Instance": [
+ ""
+ ],
+ "Settings": [
+ ""
+ ],
+ "Orders": [
+ ""
+ ],
+ "Products": [
+ ""
+ ],
+ "Transfers": [
+ ""
+ ],
+ "Connection": [
+ ""
+ ],
+ "Instances": [
+ ""
+ ],
+ "New": [
+ ""
+ ],
+ "List": [
+ ""
+ ],
+ "Log out": [
+ ""
+ ],
+ "Clear": [
+ ""
+ ],
+ "should be the same": [
+ ""
+ ],
+ "cannot be the same as before": [
+ ""
+ ],
+ "You are updating the authorization token from instance %1$s with id %2$s": [
+ ""
+ ],
+ "Old token": [
+ ""
+ ],
+ "New token": [
+ ""
+ ],
+ "Clearing the auth token will mean public access to the instance": [
+ ""
+ ],
+ "ID": [
+ ""
+ ],
+ "Image": [
+ ""
+ ],
+ "Unit": [
+ ""
+ ],
+ "Price": [
+ ""
+ ],
+ "Stock": [
+ ""
+ ],
+ "Taxes": [
+ ""
+ ],
+ "Server not found": [
+ ""
+ ],
+ "Couldn't access the server": [
+ ""
+ ],
+ "Got message %1$s from %2$s": [
+ ""
+ ],
+ "Unexpected Error": [
+ ""
+ ],
+ "Auth token": [
+ ""
+ ],
+ "Account address": [
+ ""
+ ],
+ "Default max deposit fee": [
+ ""
+ ],
+ "Default max wire fee": [
+ ""
+ ],
+ "Default wire fee amortization": [
+ ""
+ ],
+ "Jurisdiction": [
+ ""
+ ],
+ "Default pay delay": [
+ ""
+ ],
+ "Default wire transfer delay": [
+ ""
+ ],
+ "could not create instance": [
+ ""
+ ],
+ "Delete": [
+ ""
+ ],
+ "Edit": [
+ ""
+ ],
+ "There is no instances yet, add more pressing the + sign": [
+ ""
+ ],
+ "Inventory products": [
+ ""
+ ],
+ "Total price": [
+ ""
+ ],
+ "Total tax": [
+ ""
+ ],
+ "Order price": [
+ ""
+ ],
+ "Net": [
+ ""
+ ],
+ "Summary": [
+ ""
+ ],
+ "Payments options": [
+ ""
+ ],
+ "Auto refund deadline": [
+ ""
+ ],
+ "Refund deadline": [
+ ""
+ ],
+ "Pay deadline": [
+ ""
+ ],
+ "Delivery date": [
+ ""
+ ],
+ "Location": [
+ ""
+ ],
+ "Max fee": [
+ ""
+ ],
+ "Max wire fee": [
+ ""
+ ],
+ "Wire fee amortization": [
+ ""
+ ],
+ "Fullfilment url": [
+ ""
+ ],
+ "Extra information": [
+ ""
+ ],
+ "select a product first": [
+ ""
+ ],
+ "should be greater than 0": [
+ ""
+ ],
+ "cannot be greater than current stock and quantity previously added. max: %1$s": [
+ ""
+ ],
+ "cannot be greater than current stock %1$s": [
+ ""
+ ],
+ "Quantity": [
+ ""
+ ],
+ "Order": [
+ ""
+ ],
+ "claimed": [
+ ""
+ ],
+ "copy url": [
+ ""
+ ],
+ "pay at": [
+ ""
+ ],
+ "created at": [
+ ""
+ ],
+ "Timeline": [
+ ""
+ ],
+ "Payment details": [
+ ""
+ ],
+ "Order status": [
+ ""
+ ],
+ "Product list": [
+ ""
+ ],
+ "paid": [
+ ""
+ ],
+ "wired": [
+ ""
+ ],
+ "refunded": [
+ ""
+ ],
+ "refund": [
+ ""
+ ],
+ "Refunded amount": [
+ ""
+ ],
+ "Deposit total": [
+ ""
+ ],
+ "unpaid": [
+ ""
+ ],
+ "Order status URL": [
+ ""
+ ],
+ "Pay URI": [
+ ""
+ ],
+ "Unknown order status. This is an error, please contact the administrator.": [
+ ""
+ ],
+ "refund created successfully": [
+ ""
+ ],
+ "could not create the refund": [
+ ""
+ ],
+ "load newer orders": [
+ ""
+ ],
+ "Date": [
+ ""
+ ],
+ "Refund": [
+ ""
+ ],
+ "load older orders": [
+ ""
+ ],
+ "No orders has been found": [
+ ""
+ ],
+ "date": [
+ ""
+ ],
+ "amount": [
+ ""
+ ],
+ "reason": [
+ ""
+ ],
+ "Max refundable:": [
+ ""
+ ],
+ "Reason": [
+ ""
+ ],
+ "duplicated": [
+ ""
+ ],
+ "requested by the customer": [
+ ""
+ ],
+ "other": [
+ ""
+ ],
+ "go to order id": [
+ ""
+ ],
+ "Paid": [
+ ""
+ ],
+ "Refunded": [
+ ""
+ ],
+ "Not wired": [
+ ""
+ ],
+ "All": [
+ ""
+ ],
+ "could not create product": [
+ ""
+ ],
+ "Sell": [
+ ""
+ ],
+ "Profit": [
+ ""
+ ],
+ "Sold": [
+ ""
+ ],
+ "product updated successfully": [
+ ""
+ ],
+ "could not update the product": [
+ ""
+ ],
+ "product delete successfully": [
+ ""
+ ],
+ "could not delete the product": [
+ ""
+ ],
+ "Tips": [
+ ""
+ ],
+ "Committed amount": [
+ ""
+ ],
+ "Exchange initial amount": [
+ ""
+ ],
+ "Merchant initial amount": [
+ ""
+ ],
+ "There is no tips yet, add more pressing the + sign": [
+ ""
+ ],
+ "cannot be empty": [
+ ""
+ ],
+ "check the id, doest look valid": [
+ ""
+ ],
+ "should have 52 characters, current %1$s": [
+ ""
+ ],
+ "URL doesn't have the right format": [
+ ""
+ ],
+ "Transfer ID": [
+ ""
+ ],
+ "Account Address": [
+ ""
+ ],
+ "Exchange URL": [
+ ""
+ ],
+ "could not inform transfer": [
+ ""
+ ],
+ "load newer transfers": [
+ ""
+ ],
+ "Credit": [
+ ""
+ ],
+ "Confirmed": [
+ ""
+ ],
+ "Verified": [
+ ""
+ ],
+ "Executed at": [
+ ""
+ ],
+ "yes": [
+ ""
+ ],
+ "no": [
+ ""
+ ],
+ "unknown": [
+ ""
+ ],
+ "load older transfers": [
+ ""
+ ],
+ "There is no transfer yet, add more pressing the + sign": [
+ ""
+ ]
+ }
+ }
+};
+
+strings['en'] = {
+ "domain": "messages",
+ "locale_data": {
+ "messages": {
+ "": {
+ "domain": "messages",
+ "plural_forms": "nplurals=2; plural=(n != 1);",
+ "lang": ""
+ },
+ "Access denied": [
+ ""
+ ],
+ "Check your token is valid": [
+ ""
+ ],
+ "Couldn't access the server.": [
+ ""
+ ],
+ "Could not infer instance id from url %1$s": [
+ ""
+ ],
+ "HTTP status #%1$s: Server reported a problem": [
+ ""
+ ],
+ "Got message: \"%1$s\" from: %2$s": [
+ ""
+ ],
+ "No default instance": [
+ ""
+ ],
+ "in order to use merchant backoffice, you should create the default instance": [
+ ""
+ ],
+ "Server reported a problem: HTTP status #%1$s": [
+ ""
+ ],
+ "Got message: %1$s from: %2$s": [
+ ""
+ ],
+ "Login required": [
+ ""
+ ],
+ "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [
+ ""
+ ],
+ "Confirm": [
+ ""
+ ],
+ "The value %1$s is invalid for a payment url": [
+ ""
+ ],
+ "pick a date": [
+ ""
+ ],
+ "clear": [
+ ""
+ ],
+ "never": [
+ ""
+ ],
+ "Image should be smaller than 1 MB": [
+ ""
+ ],
+ "Country": [
+ ""
+ ],
+ "Address": [
+ ""
+ ],
+ "Building number": [
+ ""
+ ],
+ "Building name": [
+ ""
+ ],
+ "Street": [
+ ""
+ ],
+ "Post code": [
+ ""
+ ],
+ "Town location": [
+ ""
+ ],
+ "Town": [
+ ""
+ ],
+ "District": [
+ ""
+ ],
+ "Country subdivision": [
+ ""
+ ],
+ "Product id": [
+ ""
+ ],
+ "Description": [
+ ""
+ ],
+ "Name": [
+ ""
+ ],
+ "loading...": [
+ ""
+ ],
+ "no products found": [
+ ""
+ ],
+ "no results": [
+ ""
+ ],
+ "Deleting": [
+ ""
+ ],
+ "Changing": [
+ ""
+ ],
+ "Manage token": [
+ ""
+ ],
+ "Update": [
+ ""
+ ],
+ "Remove": [
+ ""
+ ],
+ "Cancel": [
+ ""
+ ],
+ "Manage stock": [
+ ""
+ ],
+ "Infinite": [
+ ""
+ ],
+ "lost cannot be greater that current + incoming (max %1$s)": [
+ ""
+ ],
+ "current stock will change from %1$s to %2$s": [
+ ""
+ ],
+ "current stock will stay at %1$s": [
+ ""
+ ],
+ "Incoming": [
+ ""
+ ],
+ "Lost": [
+ ""
+ ],
+ "Current": [
+ ""
+ ],
+ "without stock": [
+ ""
+ ],
+ "Next restock": [
+ ""
+ ],
+ "Delivery address": [
+ ""
+ ],
+ "this product has no taxes": [
+ ""
+ ],
+ "Amount": [
+ ""
+ ],
+ "currency and value separated with colon": [
+ ""
+ ],
+ "Add": [
+ ""
+ ],
+ "Instance": [
+ ""
+ ],
+ "Settings": [
+ ""
+ ],
+ "Orders": [
+ ""
+ ],
+ "Products": [
+ ""
+ ],
+ "Transfers": [
+ ""
+ ],
+ "Connection": [
+ ""
+ ],
+ "Instances": [
+ ""
+ ],
+ "New": [
+ ""
+ ],
+ "List": [
+ ""
+ ],
+ "Log out": [
+ ""
+ ],
+ "Clear": [
+ ""
+ ],
+ "should be the same": [
+ ""
+ ],
+ "cannot be the same as before": [
+ ""
+ ],
+ "You are updating the authorization token from instance %1$s with id %2$s": [
+ ""
+ ],
+ "Old token": [
+ ""
+ ],
+ "New token": [
+ ""
+ ],
+ "Clearing the auth token will mean public access to the instance": [
+ ""
+ ],
+ "ID": [
+ ""
+ ],
+ "Image": [
+ ""
+ ],
+ "Unit": [
+ ""
+ ],
+ "Price": [
+ ""
+ ],
+ "Stock": [
+ ""
+ ],
+ "Taxes": [
+ ""
+ ],
+ "Server not found": [
+ ""
+ ],
+ "Couldn't access the server": [
+ ""
+ ],
+ "Got message %1$s from %2$s": [
+ ""
+ ],
+ "Unexpected Error": [
+ ""
+ ],
+ "Auth token": [
+ ""
+ ],
+ "Account address": [
+ ""
+ ],
+ "Default max deposit fee": [
+ ""
+ ],
+ "Default max wire fee": [
+ ""
+ ],
+ "Default wire fee amortization": [
+ ""
+ ],
+ "Jurisdiction": [
+ ""
+ ],
+ "Default pay delay": [
+ ""
+ ],
+ "Default wire transfer delay": [
+ ""
+ ],
+ "could not create instance": [
+ ""
+ ],
+ "Delete": [
+ ""
+ ],
+ "Edit": [
+ ""
+ ],
+ "There is no instances yet, add more pressing the + sign": [
+ ""
+ ],
+ "Inventory products": [
+ ""
+ ],
+ "Total price": [
+ ""
+ ],
+ "Total tax": [
+ ""
+ ],
+ "Order price": [
+ ""
+ ],
+ "Net": [
+ ""
+ ],
+ "Summary": [
+ ""
+ ],
+ "Payments options": [
+ ""
+ ],
+ "Auto refund deadline": [
+ ""
+ ],
+ "Refund deadline": [
+ ""
+ ],
+ "Pay deadline": [
+ ""
+ ],
+ "Delivery date": [
+ ""
+ ],
+ "Location": [
+ ""
+ ],
+ "Max fee": [
+ ""
+ ],
+ "Max wire fee": [
+ ""
+ ],
+ "Wire fee amortization": [
+ ""
+ ],
+ "Fullfilment url": [
+ ""
+ ],
+ "Extra information": [
+ ""
+ ],
+ "select a product first": [
+ ""
+ ],
+ "should be greater than 0": [
+ ""
+ ],
+ "cannot be greater than current stock and quantity previously added. max: %1$s": [
+ ""
+ ],
+ "cannot be greater than current stock %1$s": [
+ ""
+ ],
+ "Quantity": [
+ ""
+ ],
+ "Order": [
+ ""
+ ],
+ "claimed": [
+ ""
+ ],
+ "copy url": [
+ ""
+ ],
+ "pay at": [
+ ""
+ ],
+ "created at": [
+ ""
+ ],
+ "Timeline": [
+ ""
+ ],
+ "Payment details": [
+ ""
+ ],
+ "Order status": [
+ ""
+ ],
+ "Product list": [
+ ""
+ ],
+ "paid": [
+ ""
+ ],
+ "wired": [
+ ""
+ ],
+ "refunded": [
+ ""
+ ],
+ "refund": [
+ ""
+ ],
+ "Refunded amount": [
+ ""
+ ],
+ "Deposit total": [
+ ""
+ ],
+ "unpaid": [
+ ""
+ ],
+ "Order status URL": [
+ ""
+ ],
+ "Pay URI": [
+ ""
+ ],
+ "Unknown order status. This is an error, please contact the administrator.": [
+ ""
+ ],
+ "refund created successfully": [
+ ""
+ ],
+ "could not create the refund": [
+ ""
+ ],
+ "load newer orders": [
+ ""
+ ],
+ "Date": [
+ ""
+ ],
+ "Refund": [
+ ""
+ ],
+ "load older orders": [
+ ""
+ ],
+ "No orders has been found": [
+ ""
+ ],
+ "date": [
+ ""
+ ],
+ "amount": [
+ ""
+ ],
+ "reason": [
+ ""
+ ],
+ "Max refundable:": [
+ ""
+ ],
+ "Reason": [
+ ""
+ ],
+ "duplicated": [
+ ""
+ ],
+ "requested by the customer": [
+ ""
+ ],
+ "other": [
+ ""
+ ],
+ "go to order id": [
+ ""
+ ],
+ "Paid": [
+ ""
+ ],
+ "Refunded": [
+ ""
+ ],
+ "Not wired": [
+ ""
+ ],
+ "All": [
+ ""
+ ],
+ "could not create product": [
+ ""
+ ],
+ "Sell": [
+ ""
+ ],
+ "Profit": [
+ ""
+ ],
+ "Sold": [
+ ""
+ ],
+ "product updated successfully": [
+ ""
+ ],
+ "could not update the product": [
+ ""
+ ],
+ "product delete successfully": [
+ ""
+ ],
+ "could not delete the product": [
+ ""
+ ],
+ "Tips": [
+ ""
+ ],
+ "Committed amount": [
+ ""
+ ],
+ "Exchange initial amount": [
+ ""
+ ],
+ "Merchant initial amount": [
+ ""
+ ],
+ "There is no tips yet, add more pressing the + sign": [
+ ""
+ ],
+ "cannot be empty": [
+ ""
+ ],
+ "check the id, doest look valid": [
+ ""
+ ],
+ "should have 52 characters, current %1$s": [
+ ""
+ ],
+ "URL doesn't have the right format": [
+ ""
+ ],
+ "Transfer ID": [
+ ""
+ ],
+ "Account Address": [
+ ""
+ ],
+ "Exchange URL": [
+ ""
+ ],
+ "could not inform transfer": [
+ ""
+ ],
+ "load newer transfers": [
+ ""
+ ],
+ "Credit": [
+ ""
+ ],
+ "Confirmed": [
+ ""
+ ],
+ "Verified": [
+ ""
+ ],
+ "Executed at": [
+ ""
+ ],
+ "yes": [
+ ""
+ ],
+ "no": [
+ ""
+ ],
+ "unknown": [
+ ""
+ ],
+ "load older transfers": [
+ ""
+ ],
+ "There is no transfer yet, add more pressing the + sign": [
+ ""
+ ]
+ }
+ }
+};
+
+strings['es'] = {
+ "domain": "messages",
+ "locale_data": {
+ "messages": {
+ "": {
+ "domain": "messages",
+ "plural_forms": "nplurals=2; plural=(n != 1);",
+ "lang": ""
+ },
+ "Access denied": [
+ "Acceso denegado"
+ ],
+ "Check your token is valid": [
+ "Verifica que el token sea valido"
+ ],
+ "Couldn't access the server.": [
+ "No se pudo acceder al servidor"
+ ],
+ "Could not infer instance id from url %1$s": [
+ "No se pudo inferir el id de la instancia con la url %1$s"
+ ],
+ "HTTP status #%1$s: Server reported a problem": [
+ "HTTP status #%1$s: Servidor reporto un problema"
+ ],
+ "Got message: \"%1$s\" from: %2$s": [
+ "Recivimos el mensaje %1$s desde %2$s"
+ ],
+ "No default instance": [
+ "Sin instancia default"
+ ],
+ "in order to use merchant backoffice, you should create the default instance": [
+ "para usar el merchant backoffice, debería crear la instancia default"
+ ],
+ "Server reported a problem: HTTP status #%1$s": [
+ "Servidir reporto un problema: HTTP status #%1$s"
+ ],
+ "Got message: %1$s from: %2$s": [
+ "Recivimos el mensaje %1$s desde %2$s"
+ ],
+ "Login required": [
+ "Login necesario"
+ ],
+ "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [
+ "Por favor ingrese su token de autorización. El token debe tener \"secret-token\" y comenzar con Bearer o ApiKey"
+ ],
+ "Confirm": [
+ "Confirmar"
+ ],
+ "The value %1$s is invalid for a payment url": [
+ "El valor %1$s es invalido para una URL de pago"
+ ],
+ "pick a date": [
+ "elegir una fecha"
+ ],
+ "clear": [
+ "Limpiar"
+ ],
+ "never": [
+ "nunca"
+ ],
+ "Image should be smaller than 1 MB": [
+ "La imagen debe ser mas chica que 1 MB"
+ ],
+ "Country": [
+ "País"
+ ],
+ "Address": [
+ "Dirección"
+ ],
+ "Building number": [
+ "Número de edificio"
+ ],
+ "Building name": [
+ "Nombre de edificio"
+ ],
+ "Street": [
+ "Calle"
+ ],
+ "Post code": [
+ "Código postal"
+ ],
+ "Town location": [
+ "Ubicación de ciudad"
+ ],
+ "Town": [
+ "Ciudad"
+ ],
+ "District": [
+ "Distrito"
+ ],
+ "Country subdivision": [
+ "Provincia"
+ ],
+ "Product id": [
+ "Id de producto"
+ ],
+ "Description": [
+ "Descripcion"
+ ],
+ "Name": [
+ "Nombre"
+ ],
+ "loading...": [
+ "Cargando..."
+ ],
+ "no products found": [
+ "No se encontraron productos"
+ ],
+ "no results": [
+ "Sin resultados"
+ ],
+ "Deleting": [
+ "Borrando"
+ ],
+ "Changing": [
+ "Cambiando"
+ ],
+ "Manage token": [
+ "Administrar token"
+ ],
+ "Update": [
+ "Actualizar"
+ ],
+ "Remove": [
+ "Eliminar"
+ ],
+ "Cancel": [
+ "Cancelar"
+ ],
+ "Manage stock": [
+ "Administrar stock"
+ ],
+ "Infinite": [
+ "Inifinito"
+ ],
+ "lost cannot be greater that current + incoming (max %1$s)": [
+ "no puede ser mayor al stock actual %1$s"
+ ],
+ "current stock will change from %1$s to %2$s": [
+ "stock actual cambiará desde %1$s a %2$s"
+ ],
+ "current stock will stay at %1$s": [
+ "stock actual seguirá en %1$s"
+ ],
+ "Incoming": [
+ "Ingresando"
+ ],
+ "Lost": [
+ "Perdido"
+ ],
+ "Current": [
+ "Actual"
+ ],
+ "without stock": [
+ "sin stock"
+ ],
+ "Next restock": [
+ "Próximo reabastecimiento"
+ ],
+ "Delivery address": [
+ "Dirección de entrega"
+ ],
+ "this product has no taxes": [
+ "este producto no tiene impuestos"
+ ],
+ "Amount": [
+ "Monto"
+ ],
+ "currency and value separated with colon": [
+ "Moneda y valor separado por dos puntos"
+ ],
+ "Add": [
+ "Agregar"
+ ],
+ "Instance": [
+ "Instancia"
+ ],
+ "Settings": [
+ "Configuración"
+ ],
+ "Orders": [
+ "Ordenes"
+ ],
+ "Products": [
+ "Productos"
+ ],
+ "Transfers": [
+ "Transferencias"
+ ],
+ "Connection": [
+ "Conexión"
+ ],
+ "Instances": [
+ "Instancias"
+ ],
+ "New": [
+ "Nuevo"
+ ],
+ "List": [
+ "Lista"
+ ],
+ "Log out": [
+ "Salir"
+ ],
+ "Clear": [
+ "Limpiar"
+ ],
+ "should be the same": [
+ "deberían ser iguales"
+ ],
+ "cannot be the same as before": [
+ "no puede ser igual al anterior"
+ ],
+ "You are updating the authorization token from instance %1$s with id %2$s": [
+ "Está actualizando el token de autorización para la instancia %1$s con id %2$s"
+ ],
+ "Old token": [
+ "Viejo token"
+ ],
+ "New token": [
+ "Nuevo token"
+ ],
+ "Clearing the auth token will mean public access to the instance": [
+ "Limpiar el token de autorización significa acceso publico a la instancia"
+ ],
+ "ID": [
+ "ID"
+ ],
+ "Image": [
+ "Imagen"
+ ],
+ "Unit": [
+ "Unidad"
+ ],
+ "Price": [
+ "Precio"
+ ],
+ "Stock": [
+ "Stock"
+ ],
+ "Taxes": [
+ "Impuesto"
+ ],
+ "Server not found": [
+ "Servidor no encontrado"
+ ],
+ "Couldn't access the server": [
+ "No se pudo aceder al servidor"
+ ],
+ "Got message %1$s from %2$s": [
+ "Recivimos el mensaje %1$s desde %2$s"
+ ],
+ "Unexpected Error": [
+ "Error inesperado"
+ ],
+ "Auth token": [
+ "Token de autorización"
+ ],
+ "Account address": [
+ "Dirección de cuenta"
+ ],
+ "Default max deposit fee": [
+ "Impuesto máximo de deposito por omisión"
+ ],
+ "Default max wire fee": [
+ "Impuesto máximo de transferencia por omisión"
+ ],
+ "Default wire fee amortization": [
+ "Amortización de impuesto de transferencia por omisión"
+ ],
+ "Jurisdiction": [
+ "Jurisdicción"
+ ],
+ "Default pay delay": [
+ "Retrazo de pago por omisión"
+ ],
+ "Default wire transfer delay": [
+ "Retrazo de transferencia por omisión"
+ ],
+ "could not create instance": [
+ "no se pudo crear la instancia"
+ ],
+ "Delete": [
+ "Borrando"
+ ],
+ "Edit": [
+ ""
+ ],
+ "There is no instances yet, add more pressing the + sign": [
+ "No hay instancias todavían, agregue mas presionando el signo +"
+ ],
+ "Inventory products": [
+ "Productos de inventario"
+ ],
+ "Total price": [
+ "Precio total"
+ ],
+ "Total tax": [
+ "Impuesto total"
+ ],
+ "Order price": [
+ "Precio de la orden"
+ ],
+ "Net": [
+ "Neto"
+ ],
+ "Summary": [
+ "Resumen"
+ ],
+ "Payments options": [
+ "Opciones de pago"
+ ],
+ "Auto refund deadline": [
+ "Plazo de reembolso automático"
+ ],
+ "Refund deadline": [
+ "Plazo de reembolso"
+ ],
+ "Pay deadline": [
+ "Plazo de pago"
+ ],
+ "Delivery date": [
+ "Fecha de entrega"
+ ],
+ "Location": [
+ "Ubicación"
+ ],
+ "Max fee": [
+ "Impuesto máximo"
+ ],
+ "Max wire fee": [
+ "Impuesto de transferencia máximo"
+ ],
+ "Wire fee amortization": [
+ "Amortización de impuesto de transferencia"
+ ],
+ "Fullfilment url": [
+ "URL de completitud"
+ ],
+ "Extra information": [
+ "Información extra"
+ ],
+ "select a product first": [
+ "seleccione un producto primero"
+ ],
+ "should be greater than 0": [
+ "La imagen debe ser mas chica que 1 MB"
+ ],
+ "cannot be greater than current stock and quantity previously added. max: %1$s": [
+ "no puede ser mayor al stock actual y la cantidad previamente agregada. máximo: %1$s"
+ ],
+ "cannot be greater than current stock %1$s": [
+ "no puede ser mayor al stock actual %1$s"
+ ],
+ "Quantity": [
+ "Cantidad"
+ ],
+ "Order": [
+ "Orden"
+ ],
+ "claimed": [
+ "reclamado"
+ ],
+ "copy url": [
+ "copiar url"
+ ],
+ "pay at": [
+ "pagar en"
+ ],
+ "created at": [
+ "creado"
+ ],
+ "Timeline": [
+ "Cronología"
+ ],
+ "Payment details": [
+ "Detalles de pago"
+ ],
+ "Order status": [
+ "Estado de orden"
+ ],
+ "Product list": [
+ "Lista de producto"
+ ],
+ "paid": [
+ "pagados"
+ ],
+ "wired": [
+ "transferido"
+ ],
+ "refunded": [
+ "reembolzado"
+ ],
+ "refund": [
+ "reembolzar"
+ ],
+ "Refunded amount": [
+ "Monto reembolzado"
+ ],
+ "Deposit total": [
+ "Total depositado"
+ ],
+ "unpaid": [
+ "impago"
+ ],
+ "Order status URL": [
+ "URL de estado de orden"
+ ],
+ "Pay URI": [
+ "URI de pago"
+ ],
+ "Unknown order status. This is an error, please contact the administrator.": [
+ "Estado de orden desconocido. Esto es un error, por favor contacte a su administrador"
+ ],
+ "refund created successfully": [
+ "reembolzo creado satisfactoriamente"
+ ],
+ "could not create the refund": [
+ "No se pudo aceder al servidor"
+ ],
+ "load newer orders": [
+ "cargar nuevas ordenes"
+ ],
+ "Date": [
+ "Fecha"
+ ],
+ "Refund": [
+ "Reembolzar"
+ ],
+ "load older orders": [
+ "cargar viejas ordenes"
+ ],
+ "No orders has been found": [
+ "No se enconraron ordenes"
+ ],
+ "date": [
+ "fecha"
+ ],
+ "amount": [
+ "monto"
+ ],
+ "reason": [
+ "razón"
+ ],
+ "Max refundable:": [
+ "Máximo reembolzable:"
+ ],
+ "Reason": [
+ "Razón"
+ ],
+ "duplicated": [
+ "duplicado"
+ ],
+ "requested by the customer": [
+ "pedido por el consumidor"
+ ],
+ "other": [
+ "otro"
+ ],
+ "go to order id": [
+ "ir a id de orden"
+ ],
+ "Paid": [
+ "Pagado"
+ ],
+ "Refunded": [
+ "Reembolzado"
+ ],
+ "Not wired": [
+ "No transferido"
+ ],
+ "All": [
+ "Todo"
+ ],
+ "could not create product": [
+ "no se pudo crear el producto"
+ ],
+ "Sell": [
+ "Venta"
+ ],
+ "Profit": [
+ "Ganancia"
+ ],
+ "Sold": [
+ "Vendido"
+ ],
+ "product updated successfully": [
+ "producto actualizado correctamente"
+ ],
+ "could not update the product": [
+ "no se pudo actualizar el producto"
+ ],
+ "product delete successfully": [
+ "producto fue eliminado correctamente"
+ ],
+ "could not delete the product": [
+ "no se pudo eliminar el producto"
+ ],
+ "Tips": [
+ "Propinas"
+ ],
+ "Committed amount": [
+ ""
+ ],
+ "Exchange initial amount": [
+ ""
+ ],
+ "Merchant initial amount": [
+ ""
+ ],
+ "There is no tips yet, add more pressing the + sign": [
+ "No hay propinas todavía, agregar mas presionando el signo +"
+ ],
+ "cannot be empty": [
+ "no puede ser vacío"
+ ],
+ "check the id, doest look valid": [
+ "verificar el id, no parece válido"
+ ],
+ "should have 52 characters, current %1$s": [
+ "debería tener 52 caracteres, actualmente %1$s"
+ ],
+ "URL doesn't have the right format": [
+ "La URL no tiene el formato correcto"
+ ],
+ "Transfer ID": [
+ "Transferencias"
+ ],
+ "Account Address": [
+ "Dirección de cuenta"
+ ],
+ "Exchange URL": [
+ "URL del Exchange"
+ ],
+ "could not inform transfer": [
+ "no se pudo crear la instancia"
+ ],
+ "load newer transfers": [
+ "cargar nuevas ordenes"
+ ],
+ "Credit": [
+ "Crédito"
+ ],
+ "Confirmed": [
+ "Confirmar"
+ ],
+ "Verified": [
+ "Verificado"
+ ],
+ "Executed at": [
+ "creado"
+ ],
+ "yes": [
+ "si"
+ ],
+ "no": [
+ "no"
+ ],
+ "unknown": [
+ "desconocido"
+ ],
+ "load older transfers": [
+ "cargar viejas transferencias"
+ ],
+ "There is no transfer yet, add more pressing the + sign": [
+ "No hay transferencias todavía, agregar mas presionando el signo +"
+ ]
+ }
+ }
+};
+
+strings['fr'] = {
+ "domain": "messages",
+ "locale_data": {
+ "messages": {
+ "": {
+ "domain": "messages",
+ "plural_forms": "nplurals=2; plural=(n != 1);",
+ "lang": ""
+ },
+ "Access denied": [
+ ""
+ ],
+ "Check your token is valid": [
+ ""
+ ],
+ "Couldn't access the server.": [
+ ""
+ ],
+ "Could not infer instance id from url %1$s": [
+ ""
+ ],
+ "HTTP status #%1$s: Server reported a problem": [
+ ""
+ ],
+ "Got message: \"%1$s\" from: %2$s": [
+ ""
+ ],
+ "No default instance": [
+ ""
+ ],
+ "in order to use merchant backoffice, you should create the default instance": [
+ ""
+ ],
+ "Server reported a problem: HTTP status #%1$s": [
+ ""
+ ],
+ "Got message: %1$s from: %2$s": [
+ ""
+ ],
+ "Login required": [
+ ""
+ ],
+ "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [
+ ""
+ ],
+ "Confirm": [
+ ""
+ ],
+ "The value %1$s is invalid for a payment url": [
+ ""
+ ],
+ "pick a date": [
+ ""
+ ],
+ "clear": [
+ ""
+ ],
+ "never": [
+ ""
+ ],
+ "Image should be smaller than 1 MB": [
+ ""
+ ],
+ "Country": [
+ ""
+ ],
+ "Address": [
+ ""
+ ],
+ "Building number": [
+ ""
+ ],
+ "Building name": [
+ ""
+ ],
+ "Street": [
+ ""
+ ],
+ "Post code": [
+ ""
+ ],
+ "Town location": [
+ ""
+ ],
+ "Town": [
+ ""
+ ],
+ "District": [
+ ""
+ ],
+ "Country subdivision": [
+ ""
+ ],
+ "Product id": [
+ ""
+ ],
+ "Description": [
+ ""
+ ],
+ "Name": [
+ ""
+ ],
+ "loading...": [
+ ""
+ ],
+ "no products found": [
+ ""
+ ],
+ "no results": [
+ ""
+ ],
+ "Deleting": [
+ ""
+ ],
+ "Changing": [
+ ""
+ ],
+ "Manage token": [
+ ""
+ ],
+ "Update": [
+ ""
+ ],
+ "Remove": [
+ ""
+ ],
+ "Cancel": [
+ ""
+ ],
+ "Manage stock": [
+ ""
+ ],
+ "Infinite": [
+ ""
+ ],
+ "lost cannot be greater that current + incoming (max %1$s)": [
+ ""
+ ],
+ "current stock will change from %1$s to %2$s": [
+ ""
+ ],
+ "current stock will stay at %1$s": [
+ ""
+ ],
+ "Incoming": [
+ ""
+ ],
+ "Lost": [
+ ""
+ ],
+ "Current": [
+ ""
+ ],
+ "without stock": [
+ ""
+ ],
+ "Next restock": [
+ ""
+ ],
+ "Delivery address": [
+ ""
+ ],
+ "this product has no taxes": [
+ ""
+ ],
+ "Amount": [
+ ""
+ ],
+ "currency and value separated with colon": [
+ ""
+ ],
+ "Add": [
+ ""
+ ],
+ "Instance": [
+ ""
+ ],
+ "Settings": [
+ ""
+ ],
+ "Orders": [
+ ""
+ ],
+ "Products": [
+ ""
+ ],
+ "Transfers": [
+ ""
+ ],
+ "Connection": [
+ ""
+ ],
+ "Instances": [
+ ""
+ ],
+ "New": [
+ ""
+ ],
+ "List": [
+ ""
+ ],
+ "Log out": [
+ ""
+ ],
+ "Clear": [
+ ""
+ ],
+ "should be the same": [
+ ""
+ ],
+ "cannot be the same as before": [
+ ""
+ ],
+ "You are updating the authorization token from instance %1$s with id %2$s": [
+ ""
+ ],
+ "Old token": [
+ ""
+ ],
+ "New token": [
+ ""
+ ],
+ "Clearing the auth token will mean public access to the instance": [
+ ""
+ ],
+ "ID": [
+ ""
+ ],
+ "Image": [
+ ""
+ ],
+ "Unit": [
+ ""
+ ],
+ "Price": [
+ ""
+ ],
+ "Stock": [
+ ""
+ ],
+ "Taxes": [
+ ""
+ ],
+ "Server not found": [
+ ""
+ ],
+ "Couldn't access the server": [
+ ""
+ ],
+ "Got message %1$s from %2$s": [
+ ""
+ ],
+ "Unexpected Error": [
+ ""
+ ],
+ "Auth token": [
+ ""
+ ],
+ "Account address": [
+ ""
+ ],
+ "Default max deposit fee": [
+ ""
+ ],
+ "Default max wire fee": [
+ ""
+ ],
+ "Default wire fee amortization": [
+ ""
+ ],
+ "Jurisdiction": [
+ ""
+ ],
+ "Default pay delay": [
+ ""
+ ],
+ "Default wire transfer delay": [
+ ""
+ ],
+ "could not create instance": [
+ ""
+ ],
+ "Delete": [
+ ""
+ ],
+ "Edit": [
+ ""
+ ],
+ "There is no instances yet, add more pressing the + sign": [
+ ""
+ ],
+ "Inventory products": [
+ ""
+ ],
+ "Total price": [
+ ""
+ ],
+ "Total tax": [
+ ""
+ ],
+ "Order price": [
+ ""
+ ],
+ "Net": [
+ ""
+ ],
+ "Summary": [
+ ""
+ ],
+ "Payments options": [
+ ""
+ ],
+ "Auto refund deadline": [
+ ""
+ ],
+ "Refund deadline": [
+ ""
+ ],
+ "Pay deadline": [
+ ""
+ ],
+ "Delivery date": [
+ ""
+ ],
+ "Location": [
+ ""
+ ],
+ "Max fee": [
+ ""
+ ],
+ "Max wire fee": [
+ ""
+ ],
+ "Wire fee amortization": [
+ ""
+ ],
+ "Fullfilment url": [
+ ""
+ ],
+ "Extra information": [
+ ""
+ ],
+ "select a product first": [
+ ""
+ ],
+ "should be greater than 0": [
+ ""
+ ],
+ "cannot be greater than current stock and quantity previously added. max: %1$s": [
+ ""
+ ],
+ "cannot be greater than current stock %1$s": [
+ ""
+ ],
+ "Quantity": [
+ ""
+ ],
+ "Order": [
+ ""
+ ],
+ "claimed": [
+ ""
+ ],
+ "copy url": [
+ ""
+ ],
+ "pay at": [
+ ""
+ ],
+ "created at": [
+ ""
+ ],
+ "Timeline": [
+ ""
+ ],
+ "Payment details": [
+ ""
+ ],
+ "Order status": [
+ ""
+ ],
+ "Product list": [
+ ""
+ ],
+ "paid": [
+ ""
+ ],
+ "wired": [
+ ""
+ ],
+ "refunded": [
+ ""
+ ],
+ "refund": [
+ ""
+ ],
+ "Refunded amount": [
+ ""
+ ],
+ "Deposit total": [
+ ""
+ ],
+ "unpaid": [
+ ""
+ ],
+ "Order status URL": [
+ ""
+ ],
+ "Pay URI": [
+ ""
+ ],
+ "Unknown order status. This is an error, please contact the administrator.": [
+ ""
+ ],
+ "refund created successfully": [
+ ""
+ ],
+ "could not create the refund": [
+ ""
+ ],
+ "load newer orders": [
+ ""
+ ],
+ "Date": [
+ ""
+ ],
+ "Refund": [
+ ""
+ ],
+ "load older orders": [
+ ""
+ ],
+ "No orders has been found": [
+ ""
+ ],
+ "date": [
+ ""
+ ],
+ "amount": [
+ ""
+ ],
+ "reason": [
+ ""
+ ],
+ "Max refundable:": [
+ ""
+ ],
+ "Reason": [
+ ""
+ ],
+ "duplicated": [
+ ""
+ ],
+ "requested by the customer": [
+ ""
+ ],
+ "other": [
+ ""
+ ],
+ "go to order id": [
+ ""
+ ],
+ "Paid": [
+ ""
+ ],
+ "Refunded": [
+ ""
+ ],
+ "Not wired": [
+ ""
+ ],
+ "All": [
+ ""
+ ],
+ "could not create product": [
+ ""
+ ],
+ "Sell": [
+ ""
+ ],
+ "Profit": [
+ ""
+ ],
+ "Sold": [
+ ""
+ ],
+ "product updated successfully": [
+ ""
+ ],
+ "could not update the product": [
+ ""
+ ],
+ "product delete successfully": [
+ ""
+ ],
+ "could not delete the product": [
+ ""
+ ],
+ "Tips": [
+ ""
+ ],
+ "Committed amount": [
+ ""
+ ],
+ "Exchange initial amount": [
+ ""
+ ],
+ "Merchant initial amount": [
+ ""
+ ],
+ "There is no tips yet, add more pressing the + sign": [
+ ""
+ ],
+ "cannot be empty": [
+ ""
+ ],
+ "check the id, doest look valid": [
+ ""
+ ],
+ "should have 52 characters, current %1$s": [
+ ""
+ ],
+ "URL doesn't have the right format": [
+ ""
+ ],
+ "Transfer ID": [
+ ""
+ ],
+ "Account Address": [
+ ""
+ ],
+ "Exchange URL": [
+ ""
+ ],
+ "could not inform transfer": [
+ ""
+ ],
+ "load newer transfers": [
+ ""
+ ],
+ "Credit": [
+ ""
+ ],
+ "Confirmed": [
+ ""
+ ],
+ "Verified": [
+ ""
+ ],
+ "Executed at": [
+ ""
+ ],
+ "yes": [
+ ""
+ ],
+ "no": [
+ ""
+ ],
+ "unknown": [
+ ""
+ ],
+ "load older transfers": [
+ ""
+ ],
+ "There is no transfer yet, add more pressing the + sign": [
+ ""
+ ]
+ }
+ }
+};
+
+strings['it'] = {
+ "domain": "messages",
+ "locale_data": {
+ "messages": {
+ "": {
+ "domain": "messages",
+ "plural_forms": "nplurals=2; plural=(n != 1);",
+ "lang": ""
+ },
+ "Access denied": [
+ ""
+ ],
+ "Check your token is valid": [
+ ""
+ ],
+ "Couldn't access the server.": [
+ ""
+ ],
+ "Could not infer instance id from url %1$s": [
+ ""
+ ],
+ "HTTP status #%1$s: Server reported a problem": [
+ ""
+ ],
+ "Got message: \"%1$s\" from: %2$s": [
+ ""
+ ],
+ "No default instance": [
+ ""
+ ],
+ "in order to use merchant backoffice, you should create the default instance": [
+ ""
+ ],
+ "Server reported a problem: HTTP status #%1$s": [
+ ""
+ ],
+ "Got message: %1$s from: %2$s": [
+ ""
+ ],
+ "Login required": [
+ ""
+ ],
+ "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [
+ ""
+ ],
+ "Confirm": [
+ ""
+ ],
+ "The value %1$s is invalid for a payment url": [
+ ""
+ ],
+ "pick a date": [
+ ""
+ ],
+ "clear": [
+ ""
+ ],
+ "never": [
+ ""
+ ],
+ "Image should be smaller than 1 MB": [
+ ""
+ ],
+ "Country": [
+ ""
+ ],
+ "Address": [
+ ""
+ ],
+ "Building number": [
+ ""
+ ],
+ "Building name": [
+ ""
+ ],
+ "Street": [
+ ""
+ ],
+ "Post code": [
+ ""
+ ],
+ "Town location": [
+ ""
+ ],
+ "Town": [
+ ""
+ ],
+ "District": [
+ ""
+ ],
+ "Country subdivision": [
+ ""
+ ],
+ "Product id": [
+ ""
+ ],
+ "Description": [
+ ""
+ ],
+ "Name": [
+ ""
+ ],
+ "loading...": [
+ ""
+ ],
+ "no products found": [
+ ""
+ ],
+ "no results": [
+ ""
+ ],
+ "Deleting": [
+ ""
+ ],
+ "Changing": [
+ ""
+ ],
+ "Manage token": [
+ ""
+ ],
+ "Update": [
+ ""
+ ],
+ "Remove": [
+ ""
+ ],
+ "Cancel": [
+ ""
+ ],
+ "Manage stock": [
+ ""
+ ],
+ "Infinite": [
+ ""
+ ],
+ "lost cannot be greater that current + incoming (max %1$s)": [
+ ""
+ ],
+ "current stock will change from %1$s to %2$s": [
+ ""
+ ],
+ "current stock will stay at %1$s": [
+ ""
+ ],
+ "Incoming": [
+ ""
+ ],
+ "Lost": [
+ ""
+ ],
+ "Current": [
+ ""
+ ],
+ "without stock": [
+ ""
+ ],
+ "Next restock": [
+ ""
+ ],
+ "Delivery address": [
+ ""
+ ],
+ "this product has no taxes": [
+ ""
+ ],
+ "Amount": [
+ ""
+ ],
+ "currency and value separated with colon": [
+ ""
+ ],
+ "Add": [
+ ""
+ ],
+ "Instance": [
+ ""
+ ],
+ "Settings": [
+ ""
+ ],
+ "Orders": [
+ ""
+ ],
+ "Products": [
+ ""
+ ],
+ "Transfers": [
+ ""
+ ],
+ "Connection": [
+ ""
+ ],
+ "Instances": [
+ ""
+ ],
+ "New": [
+ ""
+ ],
+ "List": [
+ ""
+ ],
+ "Log out": [
+ ""
+ ],
+ "Clear": [
+ ""
+ ],
+ "should be the same": [
+ ""
+ ],
+ "cannot be the same as before": [
+ ""
+ ],
+ "You are updating the authorization token from instance %1$s with id %2$s": [
+ ""
+ ],
+ "Old token": [
+ ""
+ ],
+ "New token": [
+ ""
+ ],
+ "Clearing the auth token will mean public access to the instance": [
+ ""
+ ],
+ "ID": [
+ ""
+ ],
+ "Image": [
+ ""
+ ],
+ "Unit": [
+ ""
+ ],
+ "Price": [
+ ""
+ ],
+ "Stock": [
+ ""
+ ],
+ "Taxes": [
+ ""
+ ],
+ "Server not found": [
+ ""
+ ],
+ "Couldn't access the server": [
+ ""
+ ],
+ "Got message %1$s from %2$s": [
+ ""
+ ],
+ "Unexpected Error": [
+ ""
+ ],
+ "Auth token": [
+ ""
+ ],
+ "Account address": [
+ ""
+ ],
+ "Default max deposit fee": [
+ ""
+ ],
+ "Default max wire fee": [
+ ""
+ ],
+ "Default wire fee amortization": [
+ ""
+ ],
+ "Jurisdiction": [
+ ""
+ ],
+ "Default pay delay": [
+ ""
+ ],
+ "Default wire transfer delay": [
+ ""
+ ],
+ "could not create instance": [
+ ""
+ ],
+ "Delete": [
+ ""
+ ],
+ "Edit": [
+ ""
+ ],
+ "There is no instances yet, add more pressing the + sign": [
+ ""
+ ],
+ "Inventory products": [
+ ""
+ ],
+ "Total price": [
+ ""
+ ],
+ "Total tax": [
+ ""
+ ],
+ "Order price": [
+ ""
+ ],
+ "Net": [
+ ""
+ ],
+ "Summary": [
+ ""
+ ],
+ "Payments options": [
+ ""
+ ],
+ "Auto refund deadline": [
+ ""
+ ],
+ "Refund deadline": [
+ ""
+ ],
+ "Pay deadline": [
+ ""
+ ],
+ "Delivery date": [
+ ""
+ ],
+ "Location": [
+ ""
+ ],
+ "Max fee": [
+ ""
+ ],
+ "Max wire fee": [
+ ""
+ ],
+ "Wire fee amortization": [
+ ""
+ ],
+ "Fullfilment url": [
+ ""
+ ],
+ "Extra information": [
+ ""
+ ],
+ "select a product first": [
+ ""
+ ],
+ "should be greater than 0": [
+ ""
+ ],
+ "cannot be greater than current stock and quantity previously added. max: %1$s": [
+ ""
+ ],
+ "cannot be greater than current stock %1$s": [
+ ""
+ ],
+ "Quantity": [
+ ""
+ ],
+ "Order": [
+ ""
+ ],
+ "claimed": [
+ ""
+ ],
+ "copy url": [
+ ""
+ ],
+ "pay at": [
+ ""
+ ],
+ "created at": [
+ ""
+ ],
+ "Timeline": [
+ ""
+ ],
+ "Payment details": [
+ ""
+ ],
+ "Order status": [
+ ""
+ ],
+ "Product list": [
+ ""
+ ],
+ "paid": [
+ ""
+ ],
+ "wired": [
+ ""
+ ],
+ "refunded": [
+ ""
+ ],
+ "refund": [
+ ""
+ ],
+ "Refunded amount": [
+ ""
+ ],
+ "Deposit total": [
+ ""
+ ],
+ "unpaid": [
+ ""
+ ],
+ "Order status URL": [
+ ""
+ ],
+ "Pay URI": [
+ ""
+ ],
+ "Unknown order status. This is an error, please contact the administrator.": [
+ ""
+ ],
+ "refund created successfully": [
+ ""
+ ],
+ "could not create the refund": [
+ ""
+ ],
+ "load newer orders": [
+ ""
+ ],
+ "Date": [
+ ""
+ ],
+ "Refund": [
+ ""
+ ],
+ "load older orders": [
+ ""
+ ],
+ "No orders has been found": [
+ ""
+ ],
+ "date": [
+ ""
+ ],
+ "amount": [
+ ""
+ ],
+ "reason": [
+ ""
+ ],
+ "Max refundable:": [
+ ""
+ ],
+ "Reason": [
+ ""
+ ],
+ "duplicated": [
+ ""
+ ],
+ "requested by the customer": [
+ ""
+ ],
+ "other": [
+ ""
+ ],
+ "go to order id": [
+ ""
+ ],
+ "Paid": [
+ ""
+ ],
+ "Refunded": [
+ ""
+ ],
+ "Not wired": [
+ ""
+ ],
+ "All": [
+ ""
+ ],
+ "could not create product": [
+ ""
+ ],
+ "Sell": [
+ ""
+ ],
+ "Profit": [
+ ""
+ ],
+ "Sold": [
+ ""
+ ],
+ "product updated successfully": [
+ ""
+ ],
+ "could not update the product": [
+ ""
+ ],
+ "product delete successfully": [
+ ""
+ ],
+ "could not delete the product": [
+ ""
+ ],
+ "Tips": [
+ ""
+ ],
+ "Committed amount": [
+ ""
+ ],
+ "Exchange initial amount": [
+ ""
+ ],
+ "Merchant initial amount": [
+ ""
+ ],
+ "There is no tips yet, add more pressing the + sign": [
+ ""
+ ],
+ "cannot be empty": [
+ ""
+ ],
+ "check the id, doest look valid": [
+ ""
+ ],
+ "should have 52 characters, current %1$s": [
+ ""
+ ],
+ "URL doesn't have the right format": [
+ ""
+ ],
+ "Transfer ID": [
+ ""
+ ],
+ "Account Address": [
+ ""
+ ],
+ "Exchange URL": [
+ ""
+ ],
+ "could not inform transfer": [
+ ""
+ ],
+ "load newer transfers": [
+ ""
+ ],
+ "Credit": [
+ ""
+ ],
+ "Confirmed": [
+ ""
+ ],
+ "Verified": [
+ ""
+ ],
+ "Executed at": [
+ ""
+ ],
+ "yes": [
+ ""
+ ],
+ "no": [
+ ""
+ ],
+ "unknown": [
+ ""
+ ],
+ "load older transfers": [
+ ""
+ ],
+ "There is no transfer yet, add more pressing the + sign": [
+ ""
+ ]
+ }
+ }
+};
+
+strings['sv'] = {
+ "domain": "messages",
+ "locale_data": {
+ "messages": {
+ "": {
+ "domain": "messages",
+ "plural_forms": "nplurals=2; plural=(n != 1);",
+ "lang": ""
+ },
+ "Access denied": [
+ ""
+ ],
+ "Check your token is valid": [
+ ""
+ ],
+ "Couldn't access the server.": [
+ ""
+ ],
+ "Could not infer instance id from url %1$s": [
+ ""
+ ],
+ "HTTP status #%1$s: Server reported a problem": [
+ ""
+ ],
+ "Got message: \"%1$s\" from: %2$s": [
+ ""
+ ],
+ "No default instance": [
+ ""
+ ],
+ "in order to use merchant backoffice, you should create the default instance": [
+ ""
+ ],
+ "Server reported a problem: HTTP status #%1$s": [
+ ""
+ ],
+ "Got message: %1$s from: %2$s": [
+ ""
+ ],
+ "Login required": [
+ ""
+ ],
+ "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [
+ ""
+ ],
+ "Confirm": [
+ ""
+ ],
+ "The value %1$s is invalid for a payment url": [
+ ""
+ ],
+ "pick a date": [
+ ""
+ ],
+ "clear": [
+ ""
+ ],
+ "never": [
+ ""
+ ],
+ "Image should be smaller than 1 MB": [
+ ""
+ ],
+ "Country": [
+ ""
+ ],
+ "Address": [
+ ""
+ ],
+ "Building number": [
+ ""
+ ],
+ "Building name": [
+ ""
+ ],
+ "Street": [
+ ""
+ ],
+ "Post code": [
+ ""
+ ],
+ "Town location": [
+ ""
+ ],
+ "Town": [
+ ""
+ ],
+ "District": [
+ ""
+ ],
+ "Country subdivision": [
+ ""
+ ],
+ "Product id": [
+ ""
+ ],
+ "Description": [
+ ""
+ ],
+ "Name": [
+ ""
+ ],
+ "loading...": [
+ ""
+ ],
+ "no products found": [
+ ""
+ ],
+ "no results": [
+ ""
+ ],
+ "Deleting": [
+ ""
+ ],
+ "Changing": [
+ ""
+ ],
+ "Manage token": [
+ ""
+ ],
+ "Update": [
+ ""
+ ],
+ "Remove": [
+ ""
+ ],
+ "Cancel": [
+ ""
+ ],
+ "Manage stock": [
+ ""
+ ],
+ "Infinite": [
+ ""
+ ],
+ "lost cannot be greater that current + incoming (max %1$s)": [
+ ""
+ ],
+ "current stock will change from %1$s to %2$s": [
+ ""
+ ],
+ "current stock will stay at %1$s": [
+ ""
+ ],
+ "Incoming": [
+ ""
+ ],
+ "Lost": [
+ ""
+ ],
+ "Current": [
+ ""
+ ],
+ "without stock": [
+ ""
+ ],
+ "Next restock": [
+ ""
+ ],
+ "Delivery address": [
+ ""
+ ],
+ "this product has no taxes": [
+ ""
+ ],
+ "Amount": [
+ ""
+ ],
+ "currency and value separated with colon": [
+ ""
+ ],
+ "Add": [
+ ""
+ ],
+ "Instance": [
+ ""
+ ],
+ "Settings": [
+ ""
+ ],
+ "Orders": [
+ ""
+ ],
+ "Products": [
+ ""
+ ],
+ "Transfers": [
+ ""
+ ],
+ "Connection": [
+ ""
+ ],
+ "Instances": [
+ ""
+ ],
+ "New": [
+ ""
+ ],
+ "List": [
+ ""
+ ],
+ "Log out": [
+ ""
+ ],
+ "Clear": [
+ ""
+ ],
+ "should be the same": [
+ ""
+ ],
+ "cannot be the same as before": [
+ ""
+ ],
+ "You are updating the authorization token from instance %1$s with id %2$s": [
+ ""
+ ],
+ "Old token": [
+ ""
+ ],
+ "New token": [
+ ""
+ ],
+ "Clearing the auth token will mean public access to the instance": [
+ ""
+ ],
+ "ID": [
+ ""
+ ],
+ "Image": [
+ ""
+ ],
+ "Unit": [
+ ""
+ ],
+ "Price": [
+ ""
+ ],
+ "Stock": [
+ ""
+ ],
+ "Taxes": [
+ ""
+ ],
+ "Server not found": [
+ ""
+ ],
+ "Couldn't access the server": [
+ ""
+ ],
+ "Got message %1$s from %2$s": [
+ ""
+ ],
+ "Unexpected Error": [
+ ""
+ ],
+ "Auth token": [
+ ""
+ ],
+ "Account address": [
+ ""
+ ],
+ "Default max deposit fee": [
+ ""
+ ],
+ "Default max wire fee": [
+ ""
+ ],
+ "Default wire fee amortization": [
+ ""
+ ],
+ "Jurisdiction": [
+ ""
+ ],
+ "Default pay delay": [
+ ""
+ ],
+ "Default wire transfer delay": [
+ ""
+ ],
+ "could not create instance": [
+ ""
+ ],
+ "Delete": [
+ ""
+ ],
+ "Edit": [
+ ""
+ ],
+ "There is no instances yet, add more pressing the + sign": [
+ ""
+ ],
+ "Inventory products": [
+ ""
+ ],
+ "Total price": [
+ ""
+ ],
+ "Total tax": [
+ ""
+ ],
+ "Order price": [
+ ""
+ ],
+ "Net": [
+ ""
+ ],
+ "Summary": [
+ ""
+ ],
+ "Payments options": [
+ ""
+ ],
+ "Auto refund deadline": [
+ ""
+ ],
+ "Refund deadline": [
+ ""
+ ],
+ "Pay deadline": [
+ ""
+ ],
+ "Delivery date": [
+ ""
+ ],
+ "Location": [
+ ""
+ ],
+ "Max fee": [
+ ""
+ ],
+ "Max wire fee": [
+ ""
+ ],
+ "Wire fee amortization": [
+ ""
+ ],
+ "Fullfilment url": [
+ ""
+ ],
+ "Extra information": [
+ ""
+ ],
+ "select a product first": [
+ ""
+ ],
+ "should be greater than 0": [
+ ""
+ ],
+ "cannot be greater than current stock and quantity previously added. max: %1$s": [
+ ""
+ ],
+ "cannot be greater than current stock %1$s": [
+ ""
+ ],
+ "Quantity": [
+ ""
+ ],
+ "Order": [
+ ""
+ ],
+ "claimed": [
+ ""
+ ],
+ "copy url": [
+ ""
+ ],
+ "pay at": [
+ ""
+ ],
+ "created at": [
+ ""
+ ],
+ "Timeline": [
+ ""
+ ],
+ "Payment details": [
+ ""
+ ],
+ "Order status": [
+ ""
+ ],
+ "Product list": [
+ ""
+ ],
+ "paid": [
+ ""
+ ],
+ "wired": [
+ ""
+ ],
+ "refunded": [
+ ""
+ ],
+ "refund": [
+ ""
+ ],
+ "Refunded amount": [
+ ""
+ ],
+ "Deposit total": [
+ ""
+ ],
+ "unpaid": [
+ ""
+ ],
+ "Order status URL": [
+ ""
+ ],
+ "Pay URI": [
+ ""
+ ],
+ "Unknown order status. This is an error, please contact the administrator.": [
+ ""
+ ],
+ "refund created successfully": [
+ ""
+ ],
+ "could not create the refund": [
+ ""
+ ],
+ "load newer orders": [
+ ""
+ ],
+ "Date": [
+ ""
+ ],
+ "Refund": [
+ ""
+ ],
+ "load older orders": [
+ ""
+ ],
+ "No orders has been found": [
+ ""
+ ],
+ "date": [
+ ""
+ ],
+ "amount": [
+ ""
+ ],
+ "reason": [
+ ""
+ ],
+ "Max refundable:": [
+ ""
+ ],
+ "Reason": [
+ ""
+ ],
+ "duplicated": [
+ ""
+ ],
+ "requested by the customer": [
+ ""
+ ],
+ "other": [
+ ""
+ ],
+ "go to order id": [
+ ""
+ ],
+ "Paid": [
+ ""
+ ],
+ "Refunded": [
+ ""
+ ],
+ "Not wired": [
+ ""
+ ],
+ "All": [
+ ""
+ ],
+ "could not create product": [
+ ""
+ ],
+ "Sell": [
+ ""
+ ],
+ "Profit": [
+ ""
+ ],
+ "Sold": [
+ ""
+ ],
+ "product updated successfully": [
+ ""
+ ],
+ "could not update the product": [
+ ""
+ ],
+ "product delete successfully": [
+ ""
+ ],
+ "could not delete the product": [
+ ""
+ ],
+ "Tips": [
+ ""
+ ],
+ "Committed amount": [
+ ""
+ ],
+ "Exchange initial amount": [
+ ""
+ ],
+ "Merchant initial amount": [
+ ""
+ ],
+ "There is no tips yet, add more pressing the + sign": [
+ ""
+ ],
+ "cannot be empty": [
+ ""
+ ],
+ "check the id, doest look valid": [
+ ""
+ ],
+ "should have 52 characters, current %1$s": [
+ ""
+ ],
+ "URL doesn't have the right format": [
+ ""
+ ],
+ "Transfer ID": [
+ ""
+ ],
+ "Account Address": [
+ ""
+ ],
+ "Exchange URL": [
+ ""
+ ],
+ "could not inform transfer": [
+ ""
+ ],
+ "load newer transfers": [
+ ""
+ ],
+ "Credit": [
+ ""
+ ],
+ "Confirmed": [
+ ""
+ ],
+ "Verified": [
+ ""
+ ],
+ "Executed at": [
+ ""
+ ],
+ "yes": [
+ ""
+ ],
+ "no": [
+ ""
+ ],
+ "unknown": [
+ ""
+ ],
+ "load older transfers": [
+ ""
+ ],
+ "There is no transfer yet, add more pressing the + sign": [
+ ""
+ ]
+ }
+ }
+};
+
diff --git a/packages/backend/src/i18n/sv.po b/packages/backend/src/i18n/sv.po
new file mode 100644
index 0000000..6b35bd0
--- /dev/null
+++ b/packages/backend/src/i18n/sv.po
@@ -0,0 +1,1057 @@
+# This file is part of TALER
+# (C) 2016 GNUnet e.V.
+#
+# 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.
+#
+# 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
+# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Taler Wallet\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-11-23 00:00+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:299
+#, c-format
+msgid "Access denied"
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:300
+#, c-format
+msgid "Check your token is valid"
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:72
+#, c-format
+msgid "Couldn't access the server."
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:73
+#, c-format
+msgid "Could not infer instance id from url %1$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:109
+#, c-format
+msgid "HTTP status #%1$s: Server reported a problem"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:110
+#, c-format
+msgid "Got message: \"%1$s\" from: %2$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:127
+#, c-format
+msgid "No default instance"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:128
+#, c-format
+msgid ""
+"in order to use merchant backoffice, you should create the default instance"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:288
+#, c-format
+msgid "Server reported a problem: HTTP status #%1$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:289
+#, c-format
+msgid "Got message: %1$s from: %2$s"
+msgstr ""
+
+#: src/components/exception/login.tsx:46
+#, c-format
+msgid "Login required"
+msgstr ""
+
+#: src/components/exception/login.tsx:49
+#, c-format
+msgid ""
+"Please enter your auth token. Token should have \"secret-token:\" and start "
+"with Bearer or ApiKey"
+msgstr ""
+
+#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53
+#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115
+#: src/paths/instance/orders/create/CreatePage.tsx:325
+#: src/paths/instance/products/create/CreatePage.tsx:51
+#: src/paths/instance/products/list/Table.tsx:174
+#: src/paths/instance/products/list/Table.tsx:228
+#: src/paths/instance/products/update/UpdatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:134
+#, c-format
+msgid "Confirm"
+msgstr ""
+
+#: src/components/form/InputArray.tsx:72
+#, c-format
+msgid "The value %1$s is invalid for a payment url"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:67
+#: src/paths/instance/orders/list/index.tsx:123
+#, c-format
+msgid "pick a date"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:81
+#, c-format
+msgid "clear"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:83
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "never"
+msgstr ""
+
+#: src/components/form/InputImage.tsx:80
+#, c-format
+msgid "Image should be smaller than 1 MB"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:28
+#, c-format
+msgid "Country"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:30
+#: src/paths/admin/create/CreatePage.tsx:99
+#: src/paths/instance/transfers/list/Table.tsx:124
+#: src/paths/instance/update/UpdatePage.tsx:118
+#, c-format
+msgid "Address"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:34
+#, c-format
+msgid "Building number"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:35
+#, c-format
+msgid "Building name"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:36
+#, c-format
+msgid "Street"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:37
+#, c-format
+msgid "Post code"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:38
+#, c-format
+msgid "Town location"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:39
+#, c-format
+msgid "Town"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:40
+#, c-format
+msgid "District"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:41
+#, c-format
+msgid "Country subdivision"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:59
+#, c-format
+msgid "Product id"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:60
+#: src/components/product/ProductForm.tsx:99
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122
+#: src/paths/instance/orders/list/Table.tsx:227
+#: src/paths/instance/products/list/Table.tsx:86
+#, c-format
+msgid "Description"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:73
+#: src/components/form/InputTaxes.tsx:81
+#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110
+#: src/paths/instance/details/DetailPage.tsx:76
+#: src/paths/instance/update/UpdatePage.tsx:106
+#, c-format
+msgid "Name"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:102
+#, c-format
+msgid "loading..."
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:108
+#, c-format
+msgid "no products found"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:116
+#, c-format
+msgid "no results"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:33
+#, c-format
+msgid "Deleting"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:34
+#, c-format
+msgid "Changing"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:60
+#, c-format
+msgid "Manage token"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:83
+#, c-format
+msgid "Update"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:100
+#: src/paths/instance/orders/create/CreatePage.tsx:252
+#: src/paths/instance/orders/create/CreatePage.tsx:273
+#, c-format
+msgid "Remove"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52
+#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114
+#: src/paths/instance/orders/create/CreatePage.tsx:324
+#: src/paths/instance/products/create/CreatePage.tsx:50
+#: src/paths/instance/products/list/Table.tsx:166
+#: src/paths/instance/products/list/Table.tsx:218
+#: src/paths/instance/products/update/UpdatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:88
+#: src/paths/instance/update/UpdatePage.tsx:133
+#, c-format
+msgid "Cancel"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:91
+#, c-format
+msgid "Manage stock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:93
+#, c-format
+msgid "Infinite"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:105
+#, c-format
+msgid "lost cannot be greater that current + incoming (max %1$s)"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:111
+#, c-format
+msgid "current stock will change from %1$s to %2$s"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:112
+#, c-format
+msgid "current stock will stay at %1$s"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:129
+#: src/paths/instance/products/list/Table.tsx:204
+#, c-format
+msgid "Incoming"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:130
+#: src/paths/instance/products/list/Table.tsx:205
+#, c-format
+msgid "Lost"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:142
+#, c-format
+msgid "Current"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:145
+#, c-format
+msgid "without stock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:150
+#, c-format
+msgid "Next restock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:152
+#, c-format
+msgid "Delivery address"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:73
+#, c-format
+msgid "this product has no taxes"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:77
+#: src/paths/instance/orders/details/DetailPage.tsx:145
+#: src/paths/instance/orders/details/DetailPage.tsx:296
+#: src/paths/instance/orders/list/Table.tsx:116
+#: src/paths/instance/transfers/create/CreatePage.tsx:84
+#, c-format
+msgid "Amount"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:78
+#, c-format
+msgid "currency and value separated with colon"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:84
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:78
+#, c-format
+msgid "Add"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:53
+#, c-format
+msgid "Instance"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:59
+#, c-format
+msgid "Settings"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:65
+#: src/paths/instance/orders/list/Table.tsx:60
+#, c-format
+msgid "Orders"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:71
+#: src/paths/instance/orders/create/CreatePage.tsx:258
+#: src/paths/instance/products/list/Table.tsx:48
+#, c-format
+msgid "Products"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:77
+#: src/paths/instance/transfers/list/Table.tsx:65
+#, c-format
+msgid "Transfers"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:87
+#, c-format
+msgid "Connection"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57
+#, c-format
+msgid "Instances"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:116
+#, c-format
+msgid "New"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:122
+#, c-format
+msgid "List"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:129
+#, c-format
+msgid "Log out"
+msgstr ""
+
+#: src/components/modal/index.tsx:74
+#, c-format
+msgid "Clear"
+msgstr ""
+
+#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111
+#, c-format
+msgid "should be the same"
+msgstr ""
+
+#: src/components/modal/index.tsx:111
+#, c-format
+msgid "cannot be the same as before"
+msgstr ""
+
+#: src/components/modal/index.tsx:114
+#, c-format
+msgid ""
+"You are updating the authorization token from instance %1$s with id %2$s"
+msgstr ""
+
+#: src/components/modal/index.tsx:124
+#, c-format
+msgid "Old token"
+msgstr ""
+
+#: src/components/modal/index.tsx:125
+#, c-format
+msgid "New token"
+msgstr ""
+
+#: src/components/modal/index.tsx:127
+#, c-format
+msgid "Clearing the auth token will mean public access to the instance"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:96
+#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109
+#: src/paths/instance/transfers/list/Table.tsx:122
+#, c-format
+msgid "ID"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:98
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121
+#: src/paths/instance/products/list/Table.tsx:85
+#, c-format
+msgid "Image"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:100
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123
+#, c-format
+msgid "Unit"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:101
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124
+#: src/paths/instance/products/list/Table.tsx:162
+#: src/paths/instance/products/list/Table.tsx:214
+#, c-format
+msgid "Price"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:103
+#: src/paths/instance/products/list/Table.tsx:90
+#, c-format
+msgid "Stock"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:105
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128
+#: src/paths/instance/products/list/Table.tsx:88
+#, c-format
+msgid "Taxes"
+msgstr ""
+
+#: src/index.tsx:75
+#, c-format
+msgid "Server not found"
+msgstr ""
+
+#: src/index.tsx:85
+#, c-format
+msgid "Couldn't access the server"
+msgstr ""
+
+#: src/index.tsx:87 src/index.tsx:99
+#, c-format
+msgid "Got message %1$s from %2$s"
+msgstr ""
+
+#: src/index.tsx:97
+#, c-format
+msgid "Unexpected Error"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:108
+#, c-format
+msgid "Auth token"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:91
+#: src/paths/instance/details/DetailPage.tsx:77
+#: src/paths/instance/update/UpdatePage.tsx:110
+#, c-format
+msgid "Account address"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:93
+#: src/paths/instance/update/UpdatePage.tsx:112
+#, c-format
+msgid "Default max deposit fee"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:95
+#: src/paths/instance/update/UpdatePage.tsx:114
+#, c-format
+msgid "Default max wire fee"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:97
+#: src/paths/instance/update/UpdatePage.tsx:116
+#, c-format
+msgid "Default wire fee amortization"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:103
+#: src/paths/instance/update/UpdatePage.tsx:122
+#, c-format
+msgid "Jurisdiction"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:107
+#: src/paths/instance/update/UpdatePage.tsx:126
+#, c-format
+msgid "Default pay delay"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:109
+#: src/paths/instance/update/UpdatePage.tsx:128
+#, c-format
+msgid "Default wire transfer delay"
+msgstr ""
+
+#: src/paths/admin/create/index.tsx:58
+#, c-format
+msgid "could not create instance"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131
+#: src/paths/instance/transfers/list/Table.tsx:71
+#, c-format
+msgid "Delete"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:128
+#, c-format
+msgid "Edit"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:149
+#: src/paths/instance/products/list/Table.tsx:245
+#, c-format
+msgid "There is no instances yet, add more pressing the + sign"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:237
+#, c-format
+msgid "Inventory products"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:286
+#, c-format
+msgid "Total price"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:287
+#, c-format
+msgid "Total tax"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:289
+#: src/paths/instance/orders/create/CreatePage.tsx:297
+#, c-format
+msgid "Order price"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:295
+#, c-format
+msgid "Net"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:300
+#: src/paths/instance/orders/details/DetailPage.tsx:144
+#: src/paths/instance/orders/details/DetailPage.tsx:295
+#: src/paths/instance/orders/list/Table.tsx:117
+#, c-format
+msgid "Summary"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:302
+#, c-format
+msgid "Payments options"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:303
+#, c-format
+msgid "Auto refund deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:304
+#, c-format
+msgid "Refund deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:305
+#, c-format
+msgid "Pay deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:307
+#, c-format
+msgid "Delivery date"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:308
+#, c-format
+msgid "Location"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:312
+#, c-format
+msgid "Max fee"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:313
+#, c-format
+msgid "Max wire fee"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:314
+#, c-format
+msgid "Wire fee amortization"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:315
+#, c-format
+msgid "Fullfilment url"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:318
+#, c-format
+msgid "Extra information"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:44
+#, c-format
+msgid "select a product first"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:51
+#, c-format
+msgid "should be greater than 0"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:58
+#, c-format
+msgid ""
+"cannot be greater than current stock and quantity previously added. max: %1$s"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:64
+#, c-format
+msgid "cannot be greater than current stock %1$s"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:76
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126
+#, c-format
+msgid "Quantity"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:92
+#: src/paths/instance/orders/details/DetailPage.tsx:235
+#: src/paths/instance/orders/details/DetailPage.tsx:333
+#, c-format
+msgid "Order"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:93
+#, c-format
+msgid "claimed"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:110
+#: src/paths/instance/orders/details/DetailPage.tsx:261
+#: src/paths/instance/orders/list/Table.tsx:136
+#, c-format
+msgid "copy url"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:126
+#: src/paths/instance/orders/details/DetailPage.tsx:349
+#, c-format
+msgid "pay at"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:127
+#: src/paths/instance/orders/details/DetailPage.tsx:350
+#, c-format
+msgid "created at"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:138
+#: src/paths/instance/orders/details/DetailPage.tsx:289
+#, c-format
+msgid "Timeline"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:142
+#: src/paths/instance/orders/details/DetailPage.tsx:293
+#, c-format
+msgid "Payment details"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:146
+#: src/paths/instance/orders/details/DetailPage.tsx:299
+#: src/paths/instance/orders/details/DetailPage.tsx:363
+#, c-format
+msgid "Order status"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:156
+#: src/paths/instance/orders/details/DetailPage.tsx:308
+#, c-format
+msgid "Product list"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:236
+#, c-format
+msgid "paid"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:238
+#, c-format
+msgid "wired"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:241
+#, c-format
+msgid "refunded"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:258
+#, c-format
+msgid "refund"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:297
+#, c-format
+msgid "Refunded amount"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:298
+#, c-format
+msgid "Deposit total"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:336
+#, c-format
+msgid "unpaid"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:364
+#, c-format
+msgid "Order status URL"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:365
+#, c-format
+msgid "Pay URI"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:383
+#, c-format
+msgid ""
+"Unknown order status. This is an error, please contact the administrator."
+msgstr ""
+
+#: src/paths/instance/orders/details/index.tsx:56
+#: src/paths/instance/orders/list/index.tsx:147
+#, c-format
+msgid "refund created successfully"
+msgstr ""
+
+#: src/paths/instance/orders/details/index.tsx:59
+#: src/paths/instance/orders/list/index.tsx:150
+#, c-format
+msgid "could not create the refund"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:111
+#, c-format
+msgid "load newer orders"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:115
+#, c-format
+msgid "Date"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:131
+#: src/paths/instance/orders/list/Table.tsx:223
+#, c-format
+msgid "Refund"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:145
+#, c-format
+msgid "load older orders"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:154
+#, c-format
+msgid "No orders has been found"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:202
+#, c-format
+msgid "date"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:203
+#, c-format
+msgid "amount"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:204
+#, c-format
+msgid "reason"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:224
+#, c-format
+msgid "Max refundable:"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "Reason"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "duplicated"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "requested by the customer"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "other"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:91
+#, c-format
+msgid "go to order id"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:107
+#, c-format
+msgid "Paid"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:108
+#, c-format
+msgid "Refunded"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:109
+#, c-format
+msgid "Not wired"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:110
+#, c-format
+msgid "All"
+msgstr ""
+
+#: src/paths/instance/products/create/index.tsx:48
+#: src/paths/instance/products/update/index.tsx:64
+#, c-format
+msgid "could not create product"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:87
+#, c-format
+msgid "Sell"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:89
+#, c-format
+msgid "Profit"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:91
+#, c-format
+msgid "Sold"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:59
+#, c-format
+msgid "product updated successfully"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:62
+#, c-format
+msgid "could not update the product"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:70
+#, c-format
+msgid "product delete successfully"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:73
+#, c-format
+msgid "could not delete the product"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:59
+#, c-format
+msgid "Tips"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:111
+#, c-format
+msgid "Committed amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:112
+#, c-format
+msgid "Exchange initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:113
+#, c-format
+msgid "Merchant initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:148
+#, c-format
+msgid "There is no tips yet, add more pressing the + sign"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:50
+#: src/paths/instance/transfers/create/CreatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:56
+#, c-format
+msgid "cannot be empty"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:51
+#, c-format
+msgid "check the id, doest look valid"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:52
+#, c-format
+msgid "should have 52 characters, current %1$s"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:57
+#, c-format
+msgid "URL doesn't have the right format"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:74
+#, c-format
+msgid "Transfer ID"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:76
+#, c-format
+msgid "Account Address"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:82
+#: src/paths/instance/transfers/list/Table.tsx:125
+#, c-format
+msgid "Exchange URL"
+msgstr ""
+
+#: src/paths/instance/transfers/create/index.tsx:49
+#, c-format
+msgid "could not inform transfer"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:118
+#, c-format
+msgid "load newer transfers"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:123
+#, c-format
+msgid "Credit"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:126
+#, c-format
+msgid "Confirmed"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:127
+#: src/paths/instance/transfers/list/index.tsx:60
+#, c-format
+msgid "Verified"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:128
+#, c-format
+msgid "Executed at"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "yes"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "no"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "unknown"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:145
+#, c-format
+msgid "load older transfers"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:154
+#, c-format
+msgid "There is no transfer yet, add more pressing the + sign"
+msgstr ""
diff --git a/packages/backend/src/i18n/taler-merchant-backoffice.pot b/packages/backend/src/i18n/taler-merchant-backoffice.pot
new file mode 100644
index 0000000..21fd863
--- /dev/null
+++ b/packages/backend/src/i18n/taler-merchant-backoffice.pot
@@ -0,0 +1,1054 @@
+# This file is part of GNU Taler
+# (C) 2021 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/>
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Taler Wallet\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-11-23 00:00+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:299
+#, c-format
+msgid "Access denied"
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118
+#: src/InstanceRoutes.tsx:300
+#, c-format
+msgid "Check your token is valid"
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:72
+#, c-format
+msgid "Couldn't access the server."
+msgstr ""
+
+#: src/ApplicationReadyRoutes.tsx:73
+#, c-format
+msgid "Could not infer instance id from url %1$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:109
+#, c-format
+msgid "HTTP status #%1$s: Server reported a problem"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:110
+#, c-format
+msgid "Got message: \"%1$s\" from: %2$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:127
+#, c-format
+msgid "No default instance"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:128
+#, c-format
+msgid ""
+"in order to use merchant backoffice, you should create the default instance"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:288
+#, c-format
+msgid "Server reported a problem: HTTP status #%1$s"
+msgstr ""
+
+#: src/InstanceRoutes.tsx:289
+#, c-format
+msgid "Got message: %1$s from: %2$s"
+msgstr ""
+
+#: src/components/exception/login.tsx:46
+#, c-format
+msgid "Login required"
+msgstr ""
+
+#: src/components/exception/login.tsx:49
+#, c-format
+msgid ""
+"Please enter your auth token. Token should have \"secret-token:\" and start "
+"with Bearer or ApiKey"
+msgstr ""
+
+#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53
+#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115
+#: src/paths/instance/orders/create/CreatePage.tsx:325
+#: src/paths/instance/products/create/CreatePage.tsx:51
+#: src/paths/instance/products/list/Table.tsx:174
+#: src/paths/instance/products/list/Table.tsx:228
+#: src/paths/instance/products/update/UpdatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:134
+#, c-format
+msgid "Confirm"
+msgstr ""
+
+#: src/components/form/InputArray.tsx:72
+#, c-format
+msgid "The value %1$s is invalid for a payment url"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:67
+#: src/paths/instance/orders/list/index.tsx:123
+#, c-format
+msgid "pick a date"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:81
+#, c-format
+msgid "clear"
+msgstr ""
+
+#: src/components/form/InputDate.tsx:83
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "never"
+msgstr ""
+
+#: src/components/form/InputImage.tsx:80
+#, c-format
+msgid "Image should be smaller than 1 MB"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:28
+#, c-format
+msgid "Country"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:30
+#: src/paths/admin/create/CreatePage.tsx:99
+#: src/paths/instance/transfers/list/Table.tsx:124
+#: src/paths/instance/update/UpdatePage.tsx:118
+#, c-format
+msgid "Address"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:34
+#, c-format
+msgid "Building number"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:35
+#, c-format
+msgid "Building name"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:36
+#, c-format
+msgid "Street"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:37
+#, c-format
+msgid "Post code"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:38
+#, c-format
+msgid "Town location"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:39
+#, c-format
+msgid "Town"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:40
+#, c-format
+msgid "District"
+msgstr ""
+
+#: src/components/form/InputLocation.tsx:41
+#, c-format
+msgid "Country subdivision"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:59
+#, c-format
+msgid "Product id"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:60
+#: src/components/product/ProductForm.tsx:99
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122
+#: src/paths/instance/orders/list/Table.tsx:227
+#: src/paths/instance/products/list/Table.tsx:86
+#, c-format
+msgid "Description"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:73
+#: src/components/form/InputTaxes.tsx:81
+#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110
+#: src/paths/instance/details/DetailPage.tsx:76
+#: src/paths/instance/update/UpdatePage.tsx:106
+#, c-format
+msgid "Name"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:102
+#, c-format
+msgid "loading..."
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:108
+#, c-format
+msgid "no products found"
+msgstr ""
+
+#: src/components/form/InputSearchProduct.tsx:116
+#, c-format
+msgid "no results"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:33
+#, c-format
+msgid "Deleting"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:34
+#, c-format
+msgid "Changing"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:60
+#, c-format
+msgid "Manage token"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:83
+#, c-format
+msgid "Update"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:100
+#: src/paths/instance/orders/create/CreatePage.tsx:252
+#: src/paths/instance/orders/create/CreatePage.tsx:273
+#, c-format
+msgid "Remove"
+msgstr ""
+
+#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52
+#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114
+#: src/paths/instance/orders/create/CreatePage.tsx:324
+#: src/paths/instance/products/create/CreatePage.tsx:50
+#: src/paths/instance/products/list/Table.tsx:166
+#: src/paths/instance/products/list/Table.tsx:218
+#: src/paths/instance/products/update/UpdatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:88
+#: src/paths/instance/update/UpdatePage.tsx:133
+#, c-format
+msgid "Cancel"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:91
+#, c-format
+msgid "Manage stock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:93
+#, c-format
+msgid "Infinite"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:105
+#, c-format
+msgid "lost cannot be greater that current + incoming (max %1$s)"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:111
+#, c-format
+msgid "current stock will change from %1$s to %2$s"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:112
+#, c-format
+msgid "current stock will stay at %1$s"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:129
+#: src/paths/instance/products/list/Table.tsx:204
+#, c-format
+msgid "Incoming"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:130
+#: src/paths/instance/products/list/Table.tsx:205
+#, c-format
+msgid "Lost"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:142
+#, c-format
+msgid "Current"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:145
+#, c-format
+msgid "without stock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:150
+#, c-format
+msgid "Next restock"
+msgstr ""
+
+#: src/components/form/InputStock.tsx:152
+#, c-format
+msgid "Delivery address"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:73
+#, c-format
+msgid "this product has no taxes"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:77
+#: src/paths/instance/orders/details/DetailPage.tsx:145
+#: src/paths/instance/orders/details/DetailPage.tsx:296
+#: src/paths/instance/orders/list/Table.tsx:116
+#: src/paths/instance/transfers/create/CreatePage.tsx:84
+#, c-format
+msgid "Amount"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:78
+#, c-format
+msgid "currency and value separated with colon"
+msgstr ""
+
+#: src/components/form/InputTaxes.tsx:84
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:78
+#, c-format
+msgid "Add"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:53
+#, c-format
+msgid "Instance"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:59
+#, c-format
+msgid "Settings"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:65
+#: src/paths/instance/orders/list/Table.tsx:60
+#, c-format
+msgid "Orders"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:71
+#: src/paths/instance/orders/create/CreatePage.tsx:258
+#: src/paths/instance/products/list/Table.tsx:48
+#, c-format
+msgid "Products"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:77
+#: src/paths/instance/transfers/list/Table.tsx:65
+#, c-format
+msgid "Transfers"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:87
+#, c-format
+msgid "Connection"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57
+#, c-format
+msgid "Instances"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:116
+#, c-format
+msgid "New"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:122
+#, c-format
+msgid "List"
+msgstr ""
+
+#: src/components/menu/SideBar.tsx:129
+#, c-format
+msgid "Log out"
+msgstr ""
+
+#: src/components/modal/index.tsx:74
+#, c-format
+msgid "Clear"
+msgstr ""
+
+#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111
+#, c-format
+msgid "should be the same"
+msgstr ""
+
+#: src/components/modal/index.tsx:111
+#, c-format
+msgid "cannot be the same as before"
+msgstr ""
+
+#: src/components/modal/index.tsx:114
+#, c-format
+msgid ""
+"You are updating the authorization token from instance %1$s with id %2$s"
+msgstr ""
+
+#: src/components/modal/index.tsx:124
+#, c-format
+msgid "Old token"
+msgstr ""
+
+#: src/components/modal/index.tsx:125
+#, c-format
+msgid "New token"
+msgstr ""
+
+#: src/components/modal/index.tsx:127
+#, c-format
+msgid "Clearing the auth token will mean public access to the instance"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:96
+#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109
+#: src/paths/instance/transfers/list/Table.tsx:122
+#, c-format
+msgid "ID"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:98
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121
+#: src/paths/instance/products/list/Table.tsx:85
+#, c-format
+msgid "Image"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:100
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123
+#, c-format
+msgid "Unit"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:101
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124
+#: src/paths/instance/products/list/Table.tsx:162
+#: src/paths/instance/products/list/Table.tsx:214
+#, c-format
+msgid "Price"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:103
+#: src/paths/instance/products/list/Table.tsx:90
+#, c-format
+msgid "Stock"
+msgstr ""
+
+#: src/components/product/ProductForm.tsx:105
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128
+#: src/paths/instance/products/list/Table.tsx:88
+#, c-format
+msgid "Taxes"
+msgstr ""
+
+#: src/index.tsx:75
+#, c-format
+msgid "Server not found"
+msgstr ""
+
+#: src/index.tsx:85
+#, c-format
+msgid "Couldn't access the server"
+msgstr ""
+
+#: src/index.tsx:87 src/index.tsx:99
+#, c-format
+msgid "Got message %1$s from %2$s"
+msgstr ""
+
+#: src/index.tsx:97
+#, c-format
+msgid "Unexpected Error"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:89
+#: src/paths/instance/update/UpdatePage.tsx:108
+#, c-format
+msgid "Auth token"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:91
+#: src/paths/instance/details/DetailPage.tsx:77
+#: src/paths/instance/update/UpdatePage.tsx:110
+#, c-format
+msgid "Account address"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:93
+#: src/paths/instance/update/UpdatePage.tsx:112
+#, c-format
+msgid "Default max deposit fee"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:95
+#: src/paths/instance/update/UpdatePage.tsx:114
+#, c-format
+msgid "Default max wire fee"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:97
+#: src/paths/instance/update/UpdatePage.tsx:116
+#, c-format
+msgid "Default wire fee amortization"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:103
+#: src/paths/instance/update/UpdatePage.tsx:122
+#, c-format
+msgid "Jurisdiction"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:107
+#: src/paths/instance/update/UpdatePage.tsx:126
+#, c-format
+msgid "Default pay delay"
+msgstr ""
+
+#: src/paths/admin/create/CreatePage.tsx:109
+#: src/paths/instance/update/UpdatePage.tsx:128
+#, c-format
+msgid "Default wire transfer delay"
+msgstr ""
+
+#: src/paths/admin/create/index.tsx:58
+#, c-format
+msgid "could not create instance"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131
+#: src/paths/instance/transfers/list/Table.tsx:71
+#, c-format
+msgid "Delete"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:128
+#, c-format
+msgid "Edit"
+msgstr ""
+
+#: src/paths/admin/list/Table.tsx:149
+#: src/paths/instance/products/list/Table.tsx:245
+#, c-format
+msgid "There is no instances yet, add more pressing the + sign"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:237
+#, c-format
+msgid "Inventory products"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:286
+#, c-format
+msgid "Total price"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:287
+#, c-format
+msgid "Total tax"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:289
+#: src/paths/instance/orders/create/CreatePage.tsx:297
+#, c-format
+msgid "Order price"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:295
+#, c-format
+msgid "Net"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:300
+#: src/paths/instance/orders/details/DetailPage.tsx:144
+#: src/paths/instance/orders/details/DetailPage.tsx:295
+#: src/paths/instance/orders/list/Table.tsx:117
+#, c-format
+msgid "Summary"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:302
+#, c-format
+msgid "Payments options"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:303
+#, c-format
+msgid "Auto refund deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:304
+#, c-format
+msgid "Refund deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:305
+#, c-format
+msgid "Pay deadline"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:307
+#, c-format
+msgid "Delivery date"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:308
+#, c-format
+msgid "Location"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:312
+#, c-format
+msgid "Max fee"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:313
+#, c-format
+msgid "Max wire fee"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:314
+#, c-format
+msgid "Wire fee amortization"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:315
+#, c-format
+msgid "Fullfilment url"
+msgstr ""
+
+#: src/paths/instance/orders/create/CreatePage.tsx:318
+#, c-format
+msgid "Extra information"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:44
+#, c-format
+msgid "select a product first"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:51
+#, c-format
+msgid "should be greater than 0"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:58
+#, c-format
+msgid ""
+"cannot be greater than current stock and quantity previously added. max: %1$s"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:64
+#, c-format
+msgid "cannot be greater than current stock %1$s"
+msgstr ""
+
+#: src/paths/instance/orders/create/InventoryProductForm.tsx:76
+#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126
+#, c-format
+msgid "Quantity"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:92
+#: src/paths/instance/orders/details/DetailPage.tsx:235
+#: src/paths/instance/orders/details/DetailPage.tsx:333
+#, c-format
+msgid "Order"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:93
+#, c-format
+msgid "claimed"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:110
+#: src/paths/instance/orders/details/DetailPage.tsx:261
+#: src/paths/instance/orders/list/Table.tsx:136
+#, c-format
+msgid "copy url"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:126
+#: src/paths/instance/orders/details/DetailPage.tsx:349
+#, c-format
+msgid "pay at"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:127
+#: src/paths/instance/orders/details/DetailPage.tsx:350
+#, c-format
+msgid "created at"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:138
+#: src/paths/instance/orders/details/DetailPage.tsx:289
+#, c-format
+msgid "Timeline"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:142
+#: src/paths/instance/orders/details/DetailPage.tsx:293
+#, c-format
+msgid "Payment details"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:146
+#: src/paths/instance/orders/details/DetailPage.tsx:299
+#: src/paths/instance/orders/details/DetailPage.tsx:363
+#, c-format
+msgid "Order status"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:156
+#: src/paths/instance/orders/details/DetailPage.tsx:308
+#, c-format
+msgid "Product list"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:236
+#, c-format
+msgid "paid"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:238
+#, c-format
+msgid "wired"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:241
+#, c-format
+msgid "refunded"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:258
+#, c-format
+msgid "refund"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:297
+#, c-format
+msgid "Refunded amount"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:298
+#, c-format
+msgid "Deposit total"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:336
+#, c-format
+msgid "unpaid"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:364
+#, c-format
+msgid "Order status URL"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:365
+#, c-format
+msgid "Pay URI"
+msgstr ""
+
+#: src/paths/instance/orders/details/DetailPage.tsx:383
+#, c-format
+msgid ""
+"Unknown order status. This is an error, please contact the administrator."
+msgstr ""
+
+#: src/paths/instance/orders/details/index.tsx:56
+#: src/paths/instance/orders/list/index.tsx:147
+#, c-format
+msgid "refund created successfully"
+msgstr ""
+
+#: src/paths/instance/orders/details/index.tsx:59
+#: src/paths/instance/orders/list/index.tsx:150
+#, c-format
+msgid "could not create the refund"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:111
+#, c-format
+msgid "load newer orders"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:115
+#, c-format
+msgid "Date"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:131
+#: src/paths/instance/orders/list/Table.tsx:223
+#, c-format
+msgid "Refund"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:145
+#, c-format
+msgid "load older orders"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:154
+#, c-format
+msgid "No orders has been found"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:202
+#, c-format
+msgid "date"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:203
+#, c-format
+msgid "amount"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:204
+#, c-format
+msgid "reason"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:224
+#, c-format
+msgid "Max refundable:"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "Reason"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "duplicated"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "requested by the customer"
+msgstr ""
+
+#: src/paths/instance/orders/list/Table.tsx:226
+#, c-format
+msgid "other"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:91
+#, c-format
+msgid "go to order id"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:107
+#, c-format
+msgid "Paid"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:108
+#, c-format
+msgid "Refunded"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:109
+#, c-format
+msgid "Not wired"
+msgstr ""
+
+#: src/paths/instance/orders/list/index.tsx:110
+#, c-format
+msgid "All"
+msgstr ""
+
+#: src/paths/instance/products/create/index.tsx:48
+#: src/paths/instance/products/update/index.tsx:64
+#, c-format
+msgid "could not create product"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:87
+#, c-format
+msgid "Sell"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:89
+#, c-format
+msgid "Profit"
+msgstr ""
+
+#: src/paths/instance/products/list/Table.tsx:91
+#, c-format
+msgid "Sold"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:59
+#, c-format
+msgid "product updated successfully"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:62
+#, c-format
+msgid "could not update the product"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:70
+#, c-format
+msgid "product delete successfully"
+msgstr ""
+
+#: src/paths/instance/products/list/index.tsx:73
+#, c-format
+msgid "could not delete the product"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:59
+#, c-format
+msgid "Tips"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:111
+#, c-format
+msgid "Committed amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:112
+#, c-format
+msgid "Exchange initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:113
+#, c-format
+msgid "Merchant initial amount"
+msgstr ""
+
+#: src/paths/instance/tips/list/Table.tsx:148
+#, c-format
+msgid "There is no tips yet, add more pressing the + sign"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:50
+#: src/paths/instance/transfers/create/CreatePage.tsx:54
+#: src/paths/instance/transfers/create/CreatePage.tsx:55
+#: src/paths/instance/transfers/create/CreatePage.tsx:56
+#, c-format
+msgid "cannot be empty"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:51
+#, c-format
+msgid "check the id, doest look valid"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:52
+#, c-format
+msgid "should have 52 characters, current %1$s"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:57
+#, c-format
+msgid "URL doesn't have the right format"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:74
+#, c-format
+msgid "Transfer ID"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:76
+#, c-format
+msgid "Account Address"
+msgstr ""
+
+#: src/paths/instance/transfers/create/CreatePage.tsx:82
+#: src/paths/instance/transfers/list/Table.tsx:125
+#, c-format
+msgid "Exchange URL"
+msgstr ""
+
+#: src/paths/instance/transfers/create/index.tsx:49
+#, c-format
+msgid "could not inform transfer"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:118
+#, c-format
+msgid "load newer transfers"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:123
+#, c-format
+msgid "Credit"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:126
+#, c-format
+msgid "Confirmed"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:127
+#: src/paths/instance/transfers/list/index.tsx:60
+#, c-format
+msgid "Verified"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:128
+#, c-format
+msgid "Executed at"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "yes"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:138
+#: src/paths/instance/transfers/list/Table.tsx:139
+#, c-format
+msgid "no"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:140
+#, c-format
+msgid "unknown"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:145
+#, c-format
+msgid "load older transfers"
+msgstr ""
+
+#: src/paths/instance/transfers/list/Table.tsx:154
+#, c-format
+msgid "There is no transfer yet, add more pressing the + sign"
+msgstr ""
diff --git a/packages/backend/src/index.tsx b/packages/backend/src/index.tsx
new file mode 100644
index 0000000..0babb26
--- /dev/null
+++ b/packages/backend/src/index.tsx
@@ -0,0 +1,67 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, Fragment } from 'preact';
+import { BackendContextProvider } from './context/backend';
+import { TranslationProvider } from './context/translation';
+// import { Page as RequestPayment } from './RequestPayment';
+import "./css/pure-min.css"
+import { Route, Router } from 'preact-router';
+// import OfferTip from './pages/OfferTip';
+// import {OfferRefund} from './pages/OfferRefund';
+// import DepletedTip from './pages/DepletedTip';
+// import RequestPayment from './pages/RequestPayment';
+// import ShowOrderDetails from './pages/ShowOrderDetails';
+
+export default function Application(): VNode {
+ return (
+ // <FetchContextProvider>
+ <BackendContextProvider>
+ <TranslationProvider>
+ <ApplicationStatusRoutes />
+ </TranslationProvider>
+ </BackendContextProvider>
+ // </FetchContextProvider>
+ );
+}
+
+function Footer() {
+ return <div class="talerbar">
+ <p>You can learn more about GNU Taler on our <a href="https://taler.net/">website</a>.<br />
+ Copyright &copy; 2014&mdash;2021 Taler Systems SA</p>
+ </div>
+}
+
+function ApplicationStatusRoutes(): VNode {
+ return <Fragment>
+ <Router>
+ {/* <Route path="offer_tip" component={OfferTip} />
+ <Route path="offer_refund" component={OfferRefund} />
+ <Route path="depleted_tip" component={DepletedTip} />
+ <Route path="request_payment" component={RequestPayment} />
+ <Route path="show_order_details" component={ShowOrderDetails} /> */}
+ <Route default component={() => <div>
+ hello!
+ </div>} />
+ </Router>
+ <Footer />
+ </Fragment>
+}
diff --git a/packages/backend/src/pages/DepletedTip.stories.tsx b/packages/backend/src/pages/DepletedTip.stories.tsx
new file mode 100644
index 0000000..c20f6dc
--- /dev/null
+++ b/packages/backend/src/pages/DepletedTip.stories.tsx
@@ -0,0 +1,40 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { DepletedTip as TestedComponent } from './DepletedTip';
+
+
+export default {
+ title: 'DepletedTip',
+ component: TestedComponent,
+ argTypes: {
+ },
+};
+
+function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
+ const r = (args: any) => <Component {...args} />
+ r.args = props
+ return r
+}
+
+export const Example = createExample(TestedComponent, {
+});
diff --git a/packages/backend/src/pages/DepletedTip.tsx b/packages/backend/src/pages/DepletedTip.tsx
new file mode 100644
index 0000000..f9cf9bb
--- /dev/null
+++ b/packages/backend/src/pages/DepletedTip.tsx
@@ -0,0 +1,45 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+import { h, render, VNode } from 'preact';
+
+export function DepletedTip(): VNode {
+ return <div>
+ You have already collected this tip.
+ </div>
+}
+
+export function Title(): VNode {
+ return <title>Status of your tip</title>
+}
+
+
+export function mountIntoBody(): void {
+ try {
+ const params = new URL(window.location.href).searchParams
+ render(<DepletedTip
+ // taler_refund_uri={params.get('taler_refund_uri') || undefined}
+ />, document.body);
+ } catch (e) {
+ console.error("got error", e);
+ document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;
+ }
+}
+
diff --git a/packages/backend/src/pages/OfferRefund.stories.tsx b/packages/backend/src/pages/OfferRefund.stories.tsx
new file mode 100644
index 0000000..f1b2f3a
--- /dev/null
+++ b/packages/backend/src/pages/OfferRefund.stories.tsx
@@ -0,0 +1,40 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { OfferRefund as TestedComponent } from './OfferRefund';
+
+
+export default {
+ title: 'OfferRefund',
+ component: TestedComponent,
+ argTypes: {
+ },
+};
+
+function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
+ const r = (args: any) => <Component {...args} />
+ r.args = props
+ return r
+}
+
+export const Example = createExample(TestedComponent, {
+});
diff --git a/packages/backend/src/pages/OfferRefund.tsx b/packages/backend/src/pages/OfferRefund.tsx
new file mode 100644
index 0000000..6c6b77e
--- /dev/null
+++ b/packages/backend/src/pages/OfferRefund.tsx
@@ -0,0 +1,103 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { render, h, VNode } from 'preact';
+import { useEffect } from 'preact/hooks';
+
+export function OfferRefund(): VNode {
+ useEffect(() => {
+ const checkUrl = new URL("{{& order_status_url }}");
+ checkUrl.searchParams.set("await_refund_obtained", "yes");
+ const delayMs = 500;
+ function check() {
+ let retried = false;
+ function retryOnce() {
+ if (!retried) {
+ retried = true;
+ check();
+ }
+ }
+ const req = new XMLHttpRequest();
+ req.onreadystatechange = function () {
+ if (req.readyState === XMLHttpRequest.DONE) {
+ if (req.status === 200) {
+ try {
+ const resp = JSON.parse(req.responseText);
+ if (!resp.refund_pending) {
+ window.location.reload(true);
+ }
+ } catch (e) {
+ console.error("could not parse response:", e);
+ }
+ }
+ setTimeout(retryOnce, delayMs);
+ }
+ };
+ req.onerror = function () {
+ setTimeout(retryOnce, delayMs);
+ }
+ req.open("GET", checkUrl.href);
+ req.send();
+ }
+
+ setTimeout(check, delayMs);
+ })
+ return <section id="main" class="content">
+ <h1 >Collect Taler refund</h1>
+ <div class="taler-installed-hide">
+ <p>
+ Scan this QR code with your Taler mobile wallet:
+ </p>
+ <div class="qr">
+ {/* {{{taler_refund_qrcode_svg}}} */}
+ </div>
+ <p>
+ <button onClick={() => {
+ window.location.href = '{{taler_refund_uri}}'
+ }}>
+ Or open your Taller wallet
+ </button>
+ </p>
+ <p>
+ <a href="https://wallet.taler.net/">Don't have a Taler wallet yet? Install it!</a>
+ </p>
+ </div>
+ <hr />
+ </section>
+}
+
+export function Title(): VNode {
+ return <title>Refund available for {`{order_summary}`}</title>
+}
+
+export function mountIntoBody(): void {
+ try {
+ const params = new URL(window.location.href).searchParams
+ render(<OfferRefund
+ // taler_refund_uri={params.get('taler_refund_uri') || undefined}
+ />, document.body);
+ } catch (e) {
+ console.error("got error", e);
+ document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;
+ }
+}
+
+
+
diff --git a/packages/backend/src/pages/OfferTip.stories.tsx b/packages/backend/src/pages/OfferTip.stories.tsx
new file mode 100644
index 0000000..09bffb8
--- /dev/null
+++ b/packages/backend/src/pages/OfferTip.stories.tsx
@@ -0,0 +1,40 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { OfferTip as TestedComponent } from './OfferTip';
+
+
+export default {
+ title: 'OfferTip',
+ component: TestedComponent,
+ argTypes: {
+ },
+};
+
+function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
+ const r = (args: any) => <Component {...args} />
+ r.args = props
+ return r
+}
+
+export const Example = createExample(TestedComponent, {
+});
diff --git a/packages/backend/src/pages/OfferTip.tsx b/packages/backend/src/pages/OfferTip.tsx
new file mode 100644
index 0000000..fd14354
--- /dev/null
+++ b/packages/backend/src/pages/OfferTip.tsx
@@ -0,0 +1,113 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { render, h, VNode, Fragment } from 'preact';
+import { useEffect } from 'preact/hooks';
+import { styled } from "@linaria/react"
+import "../css/pure-min.css"
+import "../css/style.css"
+
+interface Props {
+ taler_refund_uri?: string,
+ tip_status_url?: string,
+ taler_tip_qrcode_svg?: string,
+}
+export function OfferTip({ taler_refund_uri, tip_status_url, taler_tip_qrcode_svg }: Props): VNode {
+ useEffect(() => {
+ const delayMs = 500;
+ function check() {
+ let retried = false;
+ function retryOnce() {
+ if (!retried) {
+ retried = true;
+ check();
+ }
+ }
+ const req = new XMLHttpRequest();
+ req.onreadystatechange = function () {
+ if (req.readyState === XMLHttpRequest.DONE) {
+ if (req.status === 410) {
+ window.location.reload(true);
+ }
+ setTimeout(retryOnce, delayMs);
+ }
+ };
+ req.onerror = function () {
+ setTimeout(retryOnce, delayMs);
+ }
+ req.open("GET", taler_refund_uri || '');
+ req.send();
+ }
+
+ setTimeout(check, delayMs);
+ })
+ return <Fragment>
+ <section id="main" class="content">
+ <h1>Collect Taler tip</h1>
+ <div class="taler-installed-hide">
+ <p>
+ Scan this QR code with your Taler mobile wallet:
+ </p>
+ <div class="qr">
+ {taler_tip_qrcode_svg}
+ </div>
+ <p>
+ <button onClick={() => {
+ window.location.href = tip_status_url || '#'
+ }}>
+ Or open your Taller wallet
+ </button>
+ </p>
+ <p>
+ <a href="https://wallet.taler.net/">Don't have a Taler wallet yet? Install it!</a>
+ </p>
+ </div>
+ <hr />
+ </section>
+ <Footer />
+ </Fragment>
+}
+
+function Footer() {
+ return <div class="talerbar">
+ <p>You can learn more about GNU Taler on our <a href="https://taler.net/">website</a>.<br />
+ Copyright &copy; 2014&mdash;2021 Taler Systems SA</p>
+ </div>
+}
+
+
+export function Title(): VNode {
+ return <title>Tip available</title>
+}
+
+export function mountIntoBody(): void {
+ try {
+ const params = new URL(window.location.href).searchParams
+ render(<OfferTip
+ taler_refund_uri={params.get('taler_refund_uri') || undefined}
+ taler_tip_qrcode_svg={params.get('taler_tip_qrcode_svg') || undefined}
+ tip_status_url={params.get('tip_status_url') || undefined}
+ />, document.body);
+ } catch (e) {
+ console.error("got error", e);
+ document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;
+ }
+}
+
diff --git a/packages/backend/src/pages/RequestPayment.stories.tsx b/packages/backend/src/pages/RequestPayment.stories.tsx
new file mode 100644
index 0000000..1aed570
--- /dev/null
+++ b/packages/backend/src/pages/RequestPayment.stories.tsx
@@ -0,0 +1,40 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { RequestPayment as TestedComponent } from './RequestPayment';
+
+
+export default {
+ title: 'RequestPayment',
+ component: TestedComponent,
+ argTypes: {
+ },
+};
+
+function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
+ const r = (args: any) => <Component {...args} />
+ r.args = props
+ return r
+}
+
+export const Example = createExample(TestedComponent, {
+});
diff --git a/packages/backend/src/pages/RequestPayment.tsx b/packages/backend/src/pages/RequestPayment.tsx
new file mode 100644
index 0000000..d1fb8d4
--- /dev/null
+++ b/packages/backend/src/pages/RequestPayment.tsx
@@ -0,0 +1,125 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+import { h, render, VNode } from 'preact';
+import { useEffect } from 'preact/hooks';
+
+export function RequestPayment(): VNode {
+ useEffect(() => {
+ const longpollDelayMs = 60000;
+ const checkUrl = new URL("{{& order_status_url }}");
+ checkUrl.searchParams.set("timeout_ms", longpollDelayMs.toString());
+ function check() {
+ let retried = false;
+ function retryOnce() {
+ if (!retried) {
+ retried = true;
+ check();
+ }
+ }
+ const req = new XMLHttpRequest();
+ req.onreadystatechange = function () {
+ if (req.readyState === XMLHttpRequest.DONE) {
+ if (req.status === 200) {
+ try {
+ const resp = JSON.parse(req.responseText);
+ if (resp.fulfillment_url) {
+ window.location.replace(resp.fulfillment_url);
+ }
+ } catch (e) {
+ console.error("could not parse response:", e);
+ }
+ }
+ if (req.status === 202) {
+ try {
+ const resp = JSON.parse(req.responseText);
+ if (resp.fulfillment_url) {
+ window.location.replace(resp.fulfillment_url);
+ }
+ } catch (e) {
+ console.error("could not parse response:", e);
+ }
+ }
+ if (req.status === 402) {
+ try {
+ const resp = JSON.parse(req.responseText);
+ if (resp.already_paid_order_id && resp.fulfillment_url) {
+ window.location.replace(resp.fulfillment_url);
+ }
+ } catch (e) {
+ console.error("could not parse response:", e);
+ }
+ }
+ setTimeout(retryOnce, 500);
+ }
+ };
+ req.onerror = function () {
+ setTimeout(retryOnce, 500);
+ }
+ req.ontimeout = function () {
+ setTimeout(retryOnce, 500);
+ }
+ req.timeout = longpollDelayMs;
+ req.open("GET", checkUrl.href);
+ req.send();
+ }
+ setTimeout(check, 500);
+ })
+ return <section id="main" class="content">
+ <h1>Pay with Taler</h1>
+ <div class="taler-installed-hide">
+ <p>
+ Scan this QR code with your mobile wallet:
+ </p>
+ <div class="qr">
+ {/* {{{taler_pay_qrcode_svg}}} */}
+ </div>
+ <p>
+ <button onClick={() => {
+ window.location.href = '{{taler_refund_uri}}'
+ }}>
+ Or open your Taller wallet
+ </button>
+ </p>
+ <p>
+ <a href="https://wallet.taler.net/">Don't have a Taler wallet yet? Install it!</a>
+ </p>
+ </div>
+ <hr />
+ </section>
+
+}
+export function Title(): VNode {
+ return <title>Payment requested for {`{order_summary}`}</title>
+}
+
+export function mountIntoBody(): void {
+ try {
+ const params = new URL(window.location.href).searchParams
+ render(<RequestPayment
+ // taler_refund_uri={params.get('taler_refund_uri') || undefined}
+ />, document.body);
+ } catch (e) {
+ console.error("got error", e);
+ document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;
+ }
+}
+
+
diff --git a/packages/backend/src/pages/ShowOrderDetails.stories.tsx b/packages/backend/src/pages/ShowOrderDetails.stories.tsx
new file mode 100644
index 0000000..88fa393
--- /dev/null
+++ b/packages/backend/src/pages/ShowOrderDetails.stories.tsx
@@ -0,0 +1,40 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { h, VNode, FunctionalComponent } from 'preact';
+import { ShowOrderDetails as TestedComponent } from './ShowOrderDetails';
+
+
+export default {
+ title: 'ShowOrderDetails',
+ component: TestedComponent,
+ argTypes: {
+ },
+};
+
+function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
+ const r = (args: any) => <Component {...args} />
+ r.args = props
+ return r
+}
+
+export const Example = createExample(TestedComponent, {
+});
diff --git a/packages/backend/src/pages/ShowOrderDetails.tsx b/packages/backend/src/pages/ShowOrderDetails.tsx
new file mode 100644
index 0000000..4aae556
--- /dev/null
+++ b/packages/backend/src/pages/ShowOrderDetails.tsx
@@ -0,0 +1,125 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { Fragment, render, h, VNode } from 'preact';
+import { useEffect } from 'preact/hooks';
+
+export function ShowOrderDetails(): VNode {
+ useEffect(() => {
+ const longpollDelayMs = 60000;
+ const checkUrl = new URL("{{& order_status_url }}");
+ checkUrl.searchParams.set("timeout_ms", longpollDelayMs.toString());
+ function check() {
+ let retried = false;
+ function retryOnce() {
+
+ if (!retried) {
+ retried = true;
+ check();
+ }
+ }
+ const req = new XMLHttpRequest();
+ req.onreadystatechange = function () {
+ if (req.readyState === XMLHttpRequest.DONE) {
+ if (req.status === 200) {
+ try {
+ const resp = JSON.parse(req.responseText);
+ if (resp.fulfillment_url) {
+ window.location.replace(resp.fulfillment_url);
+ }
+ } catch (e) {
+ console.error("could not parse response:", e);
+ }
+ }
+ if (req.status === 202) {
+ try {
+ const resp = JSON.parse(req.responseText);
+ if (resp.fulfillment_url) {
+ window.location.replace(resp.fulfillment_url);
+ }
+ } catch (e) {
+ console.error("could not parse response:", e);
+ }
+ }
+ if (req.status === 402) {
+ try {
+ const resp = JSON.parse(req.responseText);
+ if (resp.already_paid_order_id && resp.fulfillment_url) {
+ window.location.replace(resp.fulfillment_url);
+ }
+ } catch (e) {
+ console.error("could not parse response:", e);
+ }
+ }
+ setTimeout(retryOnce, 500);
+ }
+ };
+ req.onerror = function () {
+ setTimeout(retryOnce, 500);
+ }
+ req.ontimeout = function () {
+ setTimeout(retryOnce, 500);
+ }
+ req.timeout = longpollDelayMs;
+ req.open("GET", checkUrl.href);
+ req.send();
+ }
+ setTimeout(check, 500);
+ })
+ return <Fragment>
+ <h1>Order details</h1>
+
+ <div>
+ This is the default status page for your order for <b>{`{ order_summary }`}</b>.
+ </div>
+
+
+ <h2>Refund status</h2>
+ <div>
+ The merchant has granted you refunds on the purchase of <b>{`{ refund_amount }`}</b>.
+ </div>
+
+ <h2>Full contract details</h2>
+
+ {/* <!-- FIXME #6459: expand the contract JSON in all its glory here --> */}
+ <pre>
+ {/* {{{contract_terms!stringify }}} */}
+ </pre>
+
+ </Fragment>
+
+}
+
+function Title(): VNode {
+ return <title>Status of your order for {`{order_summary}`}</title>
+}
+
+export function mountIntoBody(): void {
+ try {
+ const params = new URL(window.location.href).searchParams
+ render(<ShowOrderDetails
+ // taler_refund_uri={params.get('taler_refund_uri') || undefined}
+ />, document.body);
+ } catch (e) {
+ console.error("got error", e);
+ document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;
+ }
+}
+
diff --git a/packages/backend/src/utils/amount.ts b/packages/backend/src/utils/amount.ts
new file mode 100644
index 0000000..062ddaf
--- /dev/null
+++ b/packages/backend/src/utils/amount.ts
@@ -0,0 +1,69 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { amountFractionalBase, AmountJson, Amounts } from "@gnu-taler/taler-util";
+import { MerchantBackend } from "../declaration";
+
+/**
+ * sums two prices,
+ * @param one
+ * @param two
+ * @returns
+ */
+const sumPrices = (one: string, two: string) => {
+ const [currency, valueOne] = one.split(':')
+ const [, valueTwo] = two.split(':')
+ return `${currency}:${parseInt(valueOne, 10) + parseInt(valueTwo, 10)}`
+}
+
+/**
+ * merge refund with the same description and a difference less than one minute
+ * @param prev list of refunds that will hold the merged refunds
+ * @param cur new refund to add to the list
+ * @returns list with the new refund, may be merged with the last
+ */
+export function mergeRefunds(prev: MerchantBackend.Orders.RefundDetails[], cur: MerchantBackend.Orders.RefundDetails) {
+ let tail;
+
+ if (prev.length === 0 || //empty list
+ cur.timestamp.t_ms === 'never' || //current doesnt have timestamp
+ (tail = prev[prev.length - 1]).timestamp.t_ms === 'never' || // last doesnt have timestamp
+ cur.reason !== tail.reason || //different reason
+ Math.abs(cur.timestamp.t_ms - tail.timestamp.t_ms) > 1000 * 60) {//more than 1 minute difference
+
+ prev.push(cur)
+ return prev
+ }
+
+ prev[prev.length - 1] = {
+ ...tail,
+ amount: sumPrices(tail.amount, cur.amount)
+ }
+
+ return prev
+}
+
+export const rate = (one: string, two: string) => {
+ const a = Amounts.parseOrThrow(one)
+ const b = Amounts.parseOrThrow(two)
+ const af = toFloat(a)
+ const bf = toFloat(b)
+ if (bf === 0) return 0
+ return af / bf
+}
+
+function toFloat(amount: AmountJson) {
+ return amount.value + (amount.fraction / amountFractionalBase);
+}
diff --git a/packages/backend/src/utils/constants.ts b/packages/backend/src/utils/constants.ts
new file mode 100644
index 0000000..37c46e4
--- /dev/null
+++ b/packages/backend/src/utils/constants.ts
@@ -0,0 +1,47 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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)
+*/
+
+//https://tools.ietf.org/html/rfc8905
+export const PAYTO_REGEX = /^payto:\/\/[a-zA-Z][a-zA-Z0-9-.]+(\/[a-zA-Z0-9\-\.\~\(\)@_%:!$&'*+,;=]*)*\??((amount|receiver-name|sender-name|instruction|message)=[a-zA-Z0-9\-\.\~\(\)@_%:!$'*+,;=]*&?)*$/
+export const PAYTO_WIRE_METHOD_LOOKUP = /payto:\/\/([a-zA-Z][a-zA-Z0-9-.]+)\/.*/
+
+export const AMOUNT_REGEX = /^[a-zA-Z][a-zA-Z]*:[0-9][0-9,]*\.?[0-9,]*$/
+
+export const INSTANCE_ID_LOOKUP = /^\/instances\/([^/]*)\/?$/
+
+export const AMOUNT_ZERO_REGEX = /^[a-zA-Z][a-zA-Z]*:0$/
+
+export const CROCKFORD_BASE32_REGEX = /^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]+[*~$=U]*$/
+
+export const URL_REGEX = /^((https?:)(\/\/\/?)([\w]*(?::[\w]*)?@)?([\d\w\.-]+)(?::(\d+))?)\/$/
+
+// how much rows we add every time user hit load more
+export const PAGE_SIZE = 20
+// how bigger can be the result set
+// after this threshold, load more with move the cursor
+export const MAX_RESULT_SIZE = PAGE_SIZE * 2 - 1;
+
+// how much we will wait for all request, in seconds
+export const DEFAULT_REQUEST_TIMEOUT = 10;
+
+export const MAX_IMAGE_SIZE = 1024 * 1024;
+
+export const INSTANCE_ID_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9_.@-]+$/
diff --git a/packages/backend/src/utils/table.ts b/packages/backend/src/utils/table.ts
new file mode 100644
index 0000000..3d713a6
--- /dev/null
+++ b/packages/backend/src/utils/table.ts
@@ -0,0 +1,37 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { WithId } from "../declaration";
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+export interface Actions<T extends WithId> {
+ element: T;
+ type: 'DELETE' | 'UPDATE';
+}
+
+function notEmpty<TValue>(value: TValue | null | undefined): value is TValue {
+ return value !== null && value !== undefined;
+}
+
+export function buildActions<T extends WithId>(intances: T[], selected: string[], action: 'DELETE'): Actions<T>[] {
+ return selected.map(id => intances.find(i => i.id === id))
+ .filter(notEmpty)
+ .map(id => ({ element: id, type: action }))
+}
diff --git a/packages/backend/src/utils/types.ts b/packages/backend/src/utils/types.ts
new file mode 100644
index 0000000..9e49d39
--- /dev/null
+++ b/packages/backend/src/utils/types.ts
@@ -0,0 +1,31 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { VNode } from "preact"
+
+export interface KeyValue {
+ [key: string]: string;
+}
+
+export interface Notification {
+ message: string;
+ description?: string | VNode;
+ type: MessageType;
+}
+
+export type ValueOrFunction<T> = T | ((p: T) => T)
+export type MessageType = 'INFO' | 'WARN' | 'ERROR' | 'SUCCESS'
+