aboutsummaryrefslogtreecommitdiff
path: root/packages/merchant-backend-ui
diff options
context:
space:
mode:
Diffstat (limited to 'packages/merchant-backend-ui')
-rw-r--r--packages/merchant-backend-ui/.gitignore9
-rw-r--r--packages/merchant-backend-ui/.storybook/.babelrc25
-rw-r--r--packages/merchant-backend-ui/.storybook/main.js82
-rw-r--r--packages/merchant-backend-ui/.storybook/preview.js73
-rw-r--r--packages/merchant-backend-ui/README.md30
-rwxr-xr-xpackages/merchant-backend-ui/contrib/po2ts42
-rw-r--r--packages/merchant-backend-ui/copyleft-header.js15
-rw-r--r--packages/merchant-backend-ui/package.json144
-rw-r--r--packages/merchant-backend-ui/render-examples.ts83
-rw-r--r--packages/merchant-backend-ui/rollup.config.js112
-rw-r--r--packages/merchant-backend-ui/src/assets/empty.pngbin0 -> 2785 bytes
-rw-r--r--packages/merchant-backend-ui/src/assets/icons/android-chrome-192x192.pngbin0 -> 14058 bytes
-rw-r--r--packages/merchant-backend-ui/src/assets/icons/android-chrome-512x512.pngbin0 -> 51484 bytes
-rw-r--r--packages/merchant-backend-ui/src/assets/icons/apple-touch-icon.pngbin0 -> 12746 bytes
-rw-r--r--packages/merchant-backend-ui/src/assets/icons/favicon-16x16.pngbin0 -> 626 bytes
-rw-r--r--packages/merchant-backend-ui/src/assets/icons/favicon-32x32.pngbin0 -> 1487 bytes
-rw-r--r--packages/merchant-backend-ui/src/assets/icons/languageicon.svg48
-rw-r--r--packages/merchant-backend-ui/src/assets/icons/mstile-150x150.pngbin0 -> 9050 bytes
-rw-r--r--packages/merchant-backend-ui/src/components/Footer.tsx32
-rw-r--r--packages/merchant-backend-ui/src/components/QR.tsx41
-rw-r--r--packages/merchant-backend-ui/src/context/backend.ts82
-rw-r--r--packages/merchant-backend-ui/src/context/config.ts32
-rw-r--r--packages/merchant-backend-ui/src/context/fetch.ts40
-rw-r--r--packages/merchant-backend-ui/src/context/instance.ts35
-rw-r--r--packages/merchant-backend-ui/src/context/listener.ts35
-rw-r--r--packages/merchant-backend-ui/src/context/translation.ts59
-rw-r--r--packages/merchant-backend-ui/src/css/pure-min.css973
-rw-r--r--packages/merchant-backend-ui/src/css/style.css61
-rw-r--r--packages/merchant-backend-ui/src/custom.d.ts40
-rw-r--r--packages/merchant-backend-ui/src/declaration.d.ts1384
-rw-r--r--packages/merchant-backend-ui/src/hooks/async.ts76
-rw-r--r--packages/merchant-backend-ui/src/hooks/backend.ts262
-rw-r--r--packages/merchant-backend-ui/src/hooks/index.ts110
-rw-r--r--packages/merchant-backend-ui/src/hooks/instance.ts187
-rw-r--r--packages/merchant-backend-ui/src/hooks/listener.ts68
-rw-r--r--packages/merchant-backend-ui/src/hooks/notification.ts43
-rw-r--r--packages/merchant-backend-ui/src/hooks/notifications.ts48
-rw-r--r--packages/merchant-backend-ui/src/hooks/order.ts217
-rw-r--r--packages/merchant-backend-ui/src/hooks/product.ts223
-rw-r--r--packages/merchant-backend-ui/src/hooks/tips.ts159
-rw-r--r--packages/merchant-backend-ui/src/hooks/transfer.ts150
-rw-r--r--packages/merchant-backend-ui/src/i18n/de.po1057
-rw-r--r--packages/merchant-backend-ui/src/i18n/en.po1057
-rw-r--r--packages/merchant-backend-ui/src/i18n/es.po1065
-rw-r--r--packages/merchant-backend-ui/src/i18n/fr.po1057
-rw-r--r--packages/merchant-backend-ui/src/i18n/index.tsx203
-rw-r--r--packages/merchant-backend-ui/src/i18n/it.po1057
-rw-r--r--packages/merchant-backend-ui/src/i18n/poheader27
-rw-r--r--packages/merchant-backend-ui/src/i18n/strings-prelude19
-rw-r--r--packages/merchant-backend-ui/src/i18n/strings.ts3445
-rw-r--r--packages/merchant-backend-ui/src/i18n/sv.po1057
-rw-r--r--packages/merchant-backend-ui/src/i18n/taler-merchant-backoffice.pot1054
-rw-r--r--packages/merchant-backend-ui/src/index.tsx61
-rw-r--r--packages/merchant-backend-ui/src/pages/DepletedTip.stories.tsx40
-rw-r--r--packages/merchant-backend-ui/src/pages/DepletedTip.tsx60
-rw-r--r--packages/merchant-backend-ui/src/pages/OfferRefund.stories.tsx45
-rw-r--r--packages/merchant-backend-ui/src/pages/OfferRefund.tsx154
-rw-r--r--packages/merchant-backend-ui/src/pages/OfferTip.stories.tsx45
-rw-r--r--packages/merchant-backend-ui/src/pages/OfferTip.tsx141
-rw-r--r--packages/merchant-backend-ui/src/pages/RequestPayment.stories.tsx45
-rw-r--r--packages/merchant-backend-ui/src/pages/RequestPayment.tsx196
-rw-r--r--packages/merchant-backend-ui/src/pages/ShowOrderDetails.examples.ts219
-rw-r--r--packages/merchant-backend-ui/src/pages/ShowOrderDetails.stories.tsx49
-rw-r--r--packages/merchant-backend-ui/src/pages/ShowOrderDetails.tsx551
-rw-r--r--packages/merchant-backend-ui/src/styled/index.tsx178
-rw-r--r--packages/merchant-backend-ui/src/utils/amount.ts69
-rw-r--r--packages/merchant-backend-ui/src/utils/constants.ts47
-rw-r--r--packages/merchant-backend-ui/src/utils/table.ts37
-rw-r--r--packages/merchant-backend-ui/src/utils/types.ts31
-rw-r--r--packages/merchant-backend-ui/tests/__mocks__/browserMocks.ts42
-rw-r--r--packages/merchant-backend-ui/tests/__mocks__/fileMocks.ts24
-rw-r--r--packages/merchant-backend-ui/tests/__mocks__/fileTransformer.js31
-rw-r--r--packages/merchant-backend-ui/tests/__mocks__/setupTests.ts28
-rw-r--r--packages/merchant-backend-ui/tests/funcitons/regex.test.ts87
-rw-r--r--packages/merchant-backend-ui/tests/util.ts62
-rw-r--r--packages/merchant-backend-ui/tsconfig.back.json23
-rw-r--r--packages/merchant-backend-ui/tsconfig.json61
77 files changed, 18444 insertions, 0 deletions
diff --git a/packages/merchant-backend-ui/.gitignore b/packages/merchant-backend-ui/.gitignore
new file mode 100644
index 000000000..a6ee22df2
--- /dev/null
+++ b/packages/merchant-backend-ui/.gitignore
@@ -0,0 +1,9 @@
+/build
+/size-plugin.json
+/storybook-static
+/docs
+/single
+/coverage
+/dist
+/.rollup.cache
+/.linaria-cache
diff --git a/packages/merchant-backend-ui/.storybook/.babelrc b/packages/merchant-backend-ui/.storybook/.babelrc
new file mode 100644
index 000000000..610b6f339
--- /dev/null
+++ b/packages/merchant-backend-ui/.storybook/.babelrc
@@ -0,0 +1,25 @@
+/*
+ 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)
+ */
+{
+ "presets": [
+ "preact-cli/babel"
+ ]
+} \ No newline at end of file
diff --git a/packages/merchant-backend-ui/.storybook/main.js b/packages/merchant-backend-ui/.storybook/main.js
new file mode 100644
index 000000000..5497a6510
--- /dev/null
+++ b/packages/merchant-backend-ui/.storybook/main.js
@@ -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)
+*/
+
+
+module.exports = {
+ "stories": [
+ "../src/**/*.stories.mdx",
+ "../src/**/*.stories.@(js|jsx|ts|tsx)"
+ ],
+ "addons": [
+ "@storybook/preset-scss",
+ "@storybook/addon-a11y",
+ "@storybook/addon-essentials" //docs, control, actions, viewpot, toolbar, background
+ ],
+ // sb does not yet support new jsx transform by default
+ // https://github.com/storybookjs/storybook/issues/12881
+ // https://github.com/storybookjs/storybook/issues/12952
+ babel: async (options) => ({
+ ...options,
+ presets: [
+ ...options.presets,
+ [
+ '@babel/preset-react', {
+ runtime: 'automatic',
+ },
+ 'preset-react-jsx-transform'
+ ],
+ "@linaria",
+ ],
+ }),
+ webpackFinal: (config) => {
+ // should be removed after storybook 6.3
+ // https://github.com/storybookjs/storybook/issues/12853#issuecomment-821576113
+ config.resolve.alias = {
+ react: "preact/compat",
+ "react-dom": "preact/compat",
+ };
+
+ // we need to add @linaria loader AFTER the babel-loader
+ // https://github.com/callstack/linaria/blob/master/docs/BUNDLERS_INTEGRATION.md#webpack
+ config.module.rules[0] = {
+ ...(config.module.rules[0]),
+ loader: undefined, // Disable the predefined babel-loader on the rule
+ use: [
+ {
+ ...(config.module.rules[0].use[0]),
+ loader: 'babel-loader',
+ },
+ {
+ loader: '@linaria/webpack-loader',
+ options: {
+ sourceMap: true, //always true since this is dev
+ babelOptions: {
+ presets: config.module.rules[0].use[0].options.presets,
+ }
+ // Pass the current babel options to linaria's babel instance
+ }
+ }
+ ]
+ };
+
+ return config;
+ },
+} \ No newline at end of file
diff --git a/packages/merchant-backend-ui/.storybook/preview.js b/packages/merchant-backend-ui/.storybook/preview.js
new file mode 100644
index 000000000..a9cc4c39a
--- /dev/null
+++ b/packages/merchant-backend-ui/.storybook/preview.js
@@ -0,0 +1,73 @@
+/*
+ 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 { ConfigContextProvider } from '../src/context/config'
+import { InstanceContextProvider } from '../src/context/instance'
+import { TranslationProvider } from '../src/context/translation'
+import { BackendContextProvider } from '../src/context/backend'
+import { h } from 'preact';
+
+const mockConfig = {
+ backendURL: 'http://demo.taler.net',
+ currency: 'TESTKUDOS'
+}
+
+const mockInstance = {
+ id: 'instance-id',
+ token: 'instance-token',
+ admin: false,
+}
+
+const mockBackend = {
+ url: 'http://merchant.url',
+ token: 'default-token',
+ triedToLog: false,
+}
+
+export const parameters = {
+ controls: { expanded: true },
+ // actions: { argTypesRegex: "^on.*" },
+}
+
+export const globalTypes = {
+ locale: {
+ name: 'Locale',
+ description: 'Internationalization locale',
+ defaultValue: 'en',
+ toolbar: {
+ icon: 'globe',
+ items: [
+ { value: 'en', right: 'πŸ‡ΊπŸ‡Έ', title: 'English' },
+ { value: 'es', right: 'πŸ‡ͺπŸ‡Έ', title: 'Spanish' },
+ ],
+ },
+ },
+};
+
+export const decorators = [
+ (Story, { globals }) => <TranslationProvider initial='en' forceLang={globals.locale}>
+ <Story />
+ </TranslationProvider>,
+ (Story) => <ConfigContextProvider value={mockConfig}>
+ <Story />
+ </ConfigContextProvider>,
+ (Story) => <InstanceContextProvider value={mockInstance}>
+ <Story />
+ </InstanceContextProvider>,
+ (Story) => <BackendContextProvider defaultUrl={mockBackend.url}>
+ <Story />
+ </BackendContextProvider>,
+];
diff --git a/packages/merchant-backend-ui/README.md b/packages/merchant-backend-ui/README.md
new file mode 100644
index 000000000..16cbd0b9a
--- /dev/null
+++ b/packages/merchant-backend-ui/README.md
@@ -0,0 +1,30 @@
+Merchant Backend pages
+
+# Description
+
+This project generate 5 templates for the merchant backend:
+
+ * DepletedTip
+ * OfferRefund
+ * OfferTip
+ * RequestPayment
+ * ShowOrderDetails
+
+This pages are to be serve from the merchant-backend service and will be queried for browser that may or may not have javascript enabled, so we are going to do server side rendering.
+The merchant-backend service is currently supporting mustache library for server side rendering.
+We also want the be able to create a more interactive design if the browser have javascript enabled, so the pages will be serve with all the infromation in the html but also in javascript.
+
+In this scenario, we are using jsx to build the template of the page that will be build-time rendered into the mustache template. This template can the be deployed into a merchant-backend that will complete the information before send it to the browser.
+
+# Building
+
+The building process can be executed with `pnpm build`
+
+# Testing
+
+This project is using a javascript implementation of mustache that can be executed with the command `pnpm render-examples`.
+This script will take the pages previously built in `dist/pages` directory and the examples definition in the `src/pages/[exampleName].examples.ts` files and render a to-be-sent-to-the-user page like the merchant would do.
+This examples will be saved invidivualy into directory `dist/examples` and should be opened with your testing browser.
+Testing should be done with javascript enabled and javascript disabled, both should look ok.
+
+
diff --git a/packages/merchant-backend-ui/contrib/po2ts b/packages/merchant-backend-ui/contrib/po2ts
new file mode 100755
index 000000000..a135da61b
--- /dev/null
+++ b/packages/merchant-backend-ui/contrib/po2ts
@@ -0,0 +1,42 @@
+#!/usr/bin/env node
+/*
+ This file is part of GNU Taler
+ (C) 2020 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/>
+ */
+
+/**
+ * Convert a <lang>.po file into a JavaScript / TypeScript expression.
+ */
+
+const po2json = require("po2json");
+
+const filename = process.argv[2];
+
+if (!filename) {
+ console.error("error: missing filename");
+ process.exit(1);
+}
+
+const m = filename.match(/([a-zA-Z0-9-_]+).po/);
+
+if (!m) {
+ console.error("error: unexpected filename (expected <lang>.po)");
+ process.exit(1);
+}
+
+const lang = m[1];
+const pojson = po2json.parseFileSync(filename, { format: "jed1.x", fuzzy: true });
+const s =
+ "strings['" + lang + "'] = " + JSON.stringify(pojson, null, " ") + ";\n";
+console.log(s);
diff --git a/packages/merchant-backend-ui/copyleft-header.js b/packages/merchant-backend-ui/copyleft-header.js
new file mode 100644
index 000000000..0794cb839
--- /dev/null
+++ b/packages/merchant-backend-ui/copyleft-header.js
@@ -0,0 +1,15 @@
+/*
+ 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/>
+ */
diff --git a/packages/merchant-backend-ui/package.json b/packages/merchant-backend-ui/package.json
new file mode 100644
index 000000000..4a30def96
--- /dev/null
+++ b/packages/merchant-backend-ui/package.json
@@ -0,0 +1,144 @@
+{
+ "private": true,
+ "name": "merchant-backend",
+ "version": "0.0.4",
+ "license": "MIT",
+ "scripts": {
+ "build": "rollup -c",
+ "dev": "rollup -c -w",
+ "render-examples": "ts-node -O '{\"module\": \"commonjs\"}' -T render-examples.ts dist/pages dist/examples",
+ "lint-check": "eslint '{src,tests}/**/*.{js,jsx,ts,tsx}'",
+ "lint-fix": "eslint --fix '{src,tests}/**/*.{js,jsx,ts,tsx}'",
+ "test": "jest ./tests",
+ "dev-test": "jest ./tests --watch",
+ "typedoc": "typedoc src",
+ "clean": "rimraf build storybook-static docs single dist",
+ "serve-dist": "sirv --port ${PORT:=8080} --cors --single dist",
+ "build-storybook": "build-storybook",
+ "storybook": "start-storybook -p 6006"
+ },
+ "engines": {
+ "node": ">=12",
+ "pnpm": ">=5"
+ },
+ "eslintConfig": {
+ "parser": "@typescript-eslint/parser",
+ "extends": [
+ "preact",
+ "plugin:@typescript-eslint/recommended"
+ ],
+ "plugins": [
+ "header"
+ ],
+ "rules": {
+ "header/header": [
+ 2,
+ "copyleft-header.js"
+ ]
+ },
+ "ignorePatterns": [
+ "build/"
+ ]
+ },
+ "dependencies": {
+ "@gnu-taler/taler-util": "workspace:*",
+ "axios": "^0.21.1",
+ "date-fns": "^2.21.1",
+ "history": "4.10.1",
+ "jed": "^1.1.1",
+ "preact": "^10.5.13",
+ "preact-router": "^3.2.1",
+ "qrcode-generator": "^1.4.4",
+ "swr": "^0.5.5",
+ "yup": "^0.32.9"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.13.16",
+ "@babel/plugin-transform-react-jsx-source": "^7.12.13",
+ "@creativebulma/bulma-tooltip": "^1.2.0",
+ "@gnu-taler/pogen": "^0.0.5",
+ "@linaria/babel-preset": "^3.0.0-beta.4",
+ "@linaria/core": "^3.0.0-beta.4",
+ "@linaria/react": "^3.0.0-beta.7",
+ "@linaria/rollup": "^3.0.0-beta.7",
+ "@linaria/shaker": "^3.0.0-beta.7",
+ "@linaria/webpack-loader": "^3.0.0-beta.7",
+ "@rollup/plugin-alias": "^3.1.5",
+ "@rollup/plugin-babel": "^5.3.0",
+ "@rollup/plugin-commonjs": "^20.0.0",
+ "@rollup/plugin-html": "^0.2.3",
+ "@rollup/plugin-image": "^2.1.1",
+ "@rollup/plugin-json": "^4.1.0",
+ "@rollup/plugin-replace": "^3.0.0",
+ "@rollup/plugin-typescript": "^8.2.5",
+ "@storybook/addon-a11y": "^6.2.9",
+ "@storybook/addon-actions": "^6.2.9",
+ "@storybook/addon-essentials": "^6.2.9",
+ "@storybook/addon-links": "^6.2.9",
+ "@storybook/preact": "^6.2.9",
+ "@storybook/preset-scss": "^1.0.3",
+ "@testing-library/preact": "^2.0.1",
+ "@testing-library/preact-hooks": "^1.1.0",
+ "@types/enzyme": "^3.10.8",
+ "@types/history": "^4.7.8",
+ "@types/jest": "^26.0.23",
+ "@types/mocha": "^8.2.2",
+ "@types/mustache": "^4.1.2",
+ "@typescript-eslint/eslint-plugin": "^4.22.0",
+ "@typescript-eslint/parser": "^4.22.0",
+ "babel-loader": "^8.2.2",
+ "base64-inline-loader": "^1.1.1",
+ "bulma": "^0.9.2",
+ "bulma-checkbox": "^1.1.1",
+ "bulma-radio": "^1.1.1",
+ "bulma-responsive-tables": "^1.2.3",
+ "bulma-switch-control": "^1.1.1",
+ "bulma-timeline": "^3.0.4",
+ "bulma-upload-control": "^1.2.0",
+ "dotenv": "^8.2.0",
+ "enzyme": "^3.11.0",
+ "enzyme-adapter-preact-pure": "^3.1.0",
+ "eslint": "^7.25.0",
+ "eslint-config-preact": "^1.1.4",
+ "eslint-plugin-header": "^3.1.1",
+ "html-webpack-inline-chunk-plugin": "^1.1.1",
+ "html-webpack-inline-source-plugin": "0.0.10",
+ "html-webpack-skip-assets-plugin": "^1.0.1",
+ "inline-chunk-html-plugin": "^1.1.1",
+ "jest": "^26.6.3",
+ "jest-preset-preact": "^4.0.2",
+ "mustache": "^4.2.0",
+ "po2json": "^0.4.5",
+ "preact-cli": "^3.0.5",
+ "preact-render-to-json": "^3.6.6",
+ "preact-render-to-string": "^5.1.19",
+ "rimraf": "^3.0.2",
+ "rollup": "^2.56.3",
+ "rollup-plugin-bundle-html": "^0.2.2",
+ "rollup-plugin-css-only": "^3.1.0",
+ "sass": "^1.32.13",
+ "sass-loader": "10.1.1",
+ "script-ext-html-webpack-plugin": "^2.1.5",
+ "sirv-cli": "^1.0.11",
+ "tslib": "^2.3.1",
+ "typedoc": "^0.20.36",
+ "typescript": "^4.2.4"
+ },
+ "jest": {
+ "preset": "jest-preset-preact",
+ "transformIgnorePatterns": [
+ "node_modules/.pnpm/(?!(@gnu-taler\\+taler-util))",
+ "\\.pnp\\.[^\\/]+$"
+ ],
+ "setupFiles": [
+ "<rootDir>/tests/__mocks__/browserMocks.ts",
+ "<rootDir>/tests/__mocks__/setupTests.ts"
+ ],
+ "moduleNameMapper": {
+ "\\.(css|less)$": "identity-obj-proxy"
+ },
+ "transform": {
+ "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|po)$": "<rootDir>/tests/__mocks__/fileTransformer.js"
+ }
+ }
+}
diff --git a/packages/merchant-backend-ui/render-examples.ts b/packages/merchant-backend-ui/render-examples.ts
new file mode 100644
index 000000000..47300ab8f
--- /dev/null
+++ b/packages/merchant-backend-ui/render-examples.ts
@@ -0,0 +1,83 @@
+/*
+ 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 mustache from "mustache";
+import fs from "fs";
+import { format, formatDuration, intervalToDuration } from "date-fns";
+
+/**
+ * This script will emulate what the merchant backend will do when being requested
+ *
+*/
+
+const sourceDirectory = process.argv[2]
+const destDirectory = process.argv[3]
+
+if (!sourceDirectory || !destDirectory) {
+ console.log('usage: render-mustache <source-directory> <dest-directory>')
+ process.exit(1);
+}
+
+if (!fs.existsSync(destDirectory)) {
+ fs.mkdirSync(destDirectory);
+}
+
+/**
+ * Load all the html files
+ */
+const files = fs.readdirSync(sourceDirectory).filter(f => /.html/.test(f))
+
+files.forEach(file => {
+ const html = fs.readFileSync(`${sourceDirectory}/${file}`, 'utf8')
+
+ const testName = file.replace('.html', '')
+ if (testName !== 'ShowOrderDetails') return;
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
+ const { exampleData } = require(`./src/pages/${testName}.examples`)
+
+ Object.keys(exampleData).forEach(exampleName => {
+ const example = exampleData[exampleName]
+
+ //enhance the example with more information
+ example.contract_terms_json = () => JSON.stringify(example.contract_terms);
+ example.contract_terms.timestamp_str = () => example.contract_terms.timestamp && format(example.contract_terms.timestamp.t_s, 'dd MMM yyyy HH:mm:ss');
+
+ example.contract_terms.hasProducts = () => example.contract_terms.products?.length > 0;
+ example.contract_terms.hasAuditors = () => example.contract_terms.auditors?.length > 0;
+ example.contract_terms.hasExchanges = () => example.contract_terms.exchanges?.length > 0;
+
+ example.contract_terms.products.forEach(p => {
+ p.delivery_date_str = () => p.delivery_date && format(p.delivery_date.t_s, 'dd MM yyyy HH:mm:ss')
+ p.hasTaxes = () => p.taxes?.length > 0
+ })
+ example.contract_terms.has_delivery_info = () => example.contract_terms.delivery_date || example.contract_terms.delivery_location
+
+ example.contract_terms.delivery_date_str = () => example.contract_terms.delivery_date && format(example.contract_terms.delivery_date.t_s, 'dd MM yyyy HH:mm:ss')
+ example.contract_terms.pay_deadline_str = () => example.contract_terms.pay_deadline && format(example.contract_terms.pay_deadline.t_s, 'dd MM yyyy HH:mm:ss')
+ example.contract_terms.wire_transfer_deadline_str = () => example.contract_terms.wire_transfer_deadline && format(example.contract_terms.wire_transfer_deadline.t_s, 'dd MM yyyy HH:mm:ss')
+ example.contract_terms.refund_deadline_str = () => example.contract_terms.refund_deadline && format(example.contract_terms.refund_deadline.t_s, 'dd MM yyyy HH:mm:ss')
+ example.contract_terms.auto_refund_str = () => example.contract_terms.auto_refund && formatDuration(intervalToDuration({ start: 0, end: example.contract_terms.auto_refund.d_us }))
+
+ const output = mustache.render(html, example);
+
+ fs.writeFileSync(`${destDirectory}/${testName}.${exampleName}.html`, output)
+ })
+})
diff --git a/packages/merchant-backend-ui/rollup.config.js b/packages/merchant-backend-ui/rollup.config.js
new file mode 100644
index 000000000..f5227ba74
--- /dev/null
+++ b/packages/merchant-backend-ui/rollup.config.js
@@ -0,0 +1,112 @@
+/*
+ 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/>
+ */
+
+// rollup.config.js
+import linaria from '@linaria/rollup';
+import nodeResolve from "@rollup/plugin-node-resolve";
+import alias from "@rollup/plugin-alias";
+import image from '@rollup/plugin-image';
+import json from "@rollup/plugin-json";
+import ts from "@rollup/plugin-typescript";
+import replace from "@rollup/plugin-replace";
+import css from 'rollup-plugin-css-only';
+import html from '@rollup/plugin-html';
+import commonjs from "@rollup/plugin-commonjs";
+
+
+
+const template = async ({
+ files,
+}) => {
+ const scripts = (files.js || []).map(({ code }) => `<script>${code}</script>`).join('\n');
+ const css = (files.css || []).map(({ source }) => `<style>${source}</style>`).join('\n');
+ const ssr = (files.js || []).map(({ code }) => code).join('\n');
+ const page = new Function(`${ssr}; return page.buildTimeRendering();`)()
+ return `
+<!doctype html>
+<html>
+ <head>
+ ${page.head}
+ ${css}
+ </head>
+ <script id="built_time_data">
+ </script>
+ <body>
+ ${page.body}
+ ${scripts}
+ <script>page.mount()</script>
+ </body>
+</html>`;
+};
+
+const makePlugins = (name) => [
+ alias({
+ entries: [
+ { find: 'react', replacement: 'preact/compat' },
+ { find: 'react-dom', replacement: 'preact/compat' }
+ ]
+ }),
+
+ replace({
+ "process.env.NODE_ENV": JSON.stringify("production"),
+ preventAssignment: true,
+ }),
+
+ commonjs({
+ include: [/node_modules/, /dist/],
+ extensions: [".js"],
+ ignoreGlobal: true,
+ sourceMap: true,
+ }),
+
+ nodeResolve({
+ browser: true,
+ preferBuiltins: true,
+ }),
+
+ json(),
+ image(),
+
+ linaria({
+ sourceMap: process.env.NODE_ENV !== 'production',
+ }),
+ css(),
+ ts({
+ sourceMap: false,
+ outputToFilesystem: false,
+ }),
+ html({ template, fileName: name }),
+];
+
+
+const pageDefinition = (name) => ({
+ input: `src/pages/${name}.tsx`,
+ output: {
+ file: `dist/pages/${name}.js`,
+ format: "iife",
+ exports: 'named',
+ name: 'page',
+ },
+ plugins: makePlugins(`${name}.html`),
+});
+
+export default [
+ pageDefinition("OfferTip"),
+ pageDefinition("OfferRefund"),
+ pageDefinition("DepletedTip"),
+ pageDefinition("RequestPayment"),
+ pageDefinition("ShowOrderDetails"),
+]
diff --git a/packages/merchant-backend-ui/src/assets/empty.png b/packages/merchant-backend-ui/src/assets/empty.png
new file mode 100644
index 000000000..5120d3138
--- /dev/null
+++ b/packages/merchant-backend-ui/src/assets/empty.png
Binary files differ
diff --git a/packages/merchant-backend-ui/src/assets/icons/android-chrome-192x192.png b/packages/merchant-backend-ui/src/assets/icons/android-chrome-192x192.png
new file mode 100644
index 000000000..93ebe2e2c
--- /dev/null
+++ b/packages/merchant-backend-ui/src/assets/icons/android-chrome-192x192.png
Binary files differ
diff --git a/packages/merchant-backend-ui/src/assets/icons/android-chrome-512x512.png b/packages/merchant-backend-ui/src/assets/icons/android-chrome-512x512.png
new file mode 100644
index 000000000..52d1623ea
--- /dev/null
+++ b/packages/merchant-backend-ui/src/assets/icons/android-chrome-512x512.png
Binary files differ
diff --git a/packages/merchant-backend-ui/src/assets/icons/apple-touch-icon.png b/packages/merchant-backend-ui/src/assets/icons/apple-touch-icon.png
new file mode 100644
index 000000000..254e4bb4d
--- /dev/null
+++ b/packages/merchant-backend-ui/src/assets/icons/apple-touch-icon.png
Binary files differ
diff --git a/packages/merchant-backend-ui/src/assets/icons/favicon-16x16.png b/packages/merchant-backend-ui/src/assets/icons/favicon-16x16.png
new file mode 100644
index 000000000..e81177dcb
--- /dev/null
+++ b/packages/merchant-backend-ui/src/assets/icons/favicon-16x16.png
Binary files differ
diff --git a/packages/merchant-backend-ui/src/assets/icons/favicon-32x32.png b/packages/merchant-backend-ui/src/assets/icons/favicon-32x32.png
new file mode 100644
index 000000000..40e9b5b47
--- /dev/null
+++ b/packages/merchant-backend-ui/src/assets/icons/favicon-32x32.png
Binary files differ
diff --git a/packages/merchant-backend-ui/src/assets/icons/languageicon.svg b/packages/merchant-backend-ui/src/assets/icons/languageicon.svg
new file mode 100644
index 000000000..22d58da65
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/assets/icons/mstile-150x150.png b/packages/merchant-backend-ui/src/assets/icons/mstile-150x150.png
new file mode 100644
index 000000000..9cfb889be
--- /dev/null
+++ b/packages/merchant-backend-ui/src/assets/icons/mstile-150x150.png
Binary files differ
diff --git a/packages/merchant-backend-ui/src/components/Footer.tsx b/packages/merchant-backend-ui/src/components/Footer.tsx
new file mode 100644
index 000000000..5f2957800
--- /dev/null
+++ b/packages/merchant-backend-ui/src/components/Footer.tsx
@@ -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 { h, VNode } from 'preact';
+import { FooterBar } from '../styled';
+
+export function Footer(): VNode {
+ return <FooterBar>
+ <p>
+ <a href="https://taler.net/">Learn more about GNU Taler on our website.</a>
+ <p>Copyright &copy; 2014&mdash;2021 Taler Systems SA</p>
+ </p>
+ </FooterBar>
+}
+
diff --git a/packages/merchant-backend-ui/src/components/QR.tsx b/packages/merchant-backend-ui/src/components/QR.tsx
new file mode 100644
index 000000000..29c9920bf
--- /dev/null
+++ b/packages/merchant-backend-ui/src/components/QR.tsx
@@ -0,0 +1,41 @@
+/*
+ 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 { h, VNode } from "preact";
+ import { useEffect, useRef } from "preact/hooks";
+ import qrcode from "qrcode-generator";
+
+export function createSVG(text:string):string {
+ const qr = qrcode(0, 'L');
+ qr.addData(text);
+ qr.make();
+ return qr.createSvgTag({
+ scalable: true,
+ margin: 0
+ });
+}
+
+ export function QR({ text }: { text: string; }):VNode {
+ const divRef = useRef<HTMLDivElement>(null);
+ useEffect(() => {
+ divRef.current.innerHTML = createSVG(text)
+ });
+
+ return <div style={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
+ <div style={{ width: '50%', minWidth: 200, maxWidth: 300 }} ref={divRef} />
+ </div>;
+ }
+ \ No newline at end of file
diff --git a/packages/merchant-backend-ui/src/context/backend.ts b/packages/merchant-backend-ui/src/context/backend.ts
new file mode 100644
index 000000000..a920d6ffc
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/context/config.ts b/packages/merchant-backend-ui/src/context/config.ts
new file mode 100644
index 000000000..5cd772380
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/context/fetch.ts b/packages/merchant-backend-ui/src/context/fetch.ts
new file mode 100644
index 000000000..52a4f9c8d
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/context/instance.ts b/packages/merchant-backend-ui/src/context/instance.ts
new file mode 100644
index 000000000..fecf36426
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/context/listener.ts b/packages/merchant-backend-ui/src/context/listener.ts
new file mode 100644
index 000000000..659db0a03
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/context/translation.ts b/packages/merchant-backend-ui/src/context/translation.ts
new file mode 100644
index 000000000..952a1e325
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/css/pure-min.css b/packages/merchant-backend-ui/src/css/pure-min.css
new file mode 100644
index 000000000..77217b520
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/css/style.css b/packages/merchant-backend-ui/src/css/style.css
new file mode 100644
index 000000000..f24dbaa87
--- /dev/null
+++ b/packages/merchant-backend-ui/src/css/style.css
@@ -0,0 +1,61 @@
+/*!
+ 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;
+ 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/merchant-backend-ui/src/custom.d.ts b/packages/merchant-backend-ui/src/custom.d.ts
new file mode 100644
index 000000000..d2705003b
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/declaration.d.ts b/packages/merchant-backend-ui/src/declaration.d.ts
new file mode 100644
index 000000000..74b0a5011
--- /dev/null
+++ b/packages/merchant-backend-ui/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_s: number | "never";
+}
+interface Duration {
+ // Duration in milliseconds or "forever"
+ // to represent an infinite duration.
+ d_us: 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: TalerProtocolTimestamp;
+
+ // 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: TalerProtocolTimestamp;
+
+ // 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: TalerProtocolTimestamp;
+
+ // 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: TalerProtocolTimestamp;
+
+ // After this deadline has passed, no refunds will be accepted.
+ refund_deadline: TalerProtocolTimestamp;
+
+ // After this deadline, the merchant won't accept payments for the contact
+ pay_deadline: TalerProtocolTimestamp;
+
+ // Transfer deadline for the exchange. Must be in the
+ // deposit permissions of coins used to pay for this order.
+ wire_transfer_deadline: TalerProtocolTimestamp;
+
+ // 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?: TalerProtocolTimestamp;
+
+ // 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/merchant-backend-ui/src/hooks/async.ts b/packages/merchant-backend-ui/src/hooks/async.ts
new file mode 100644
index 000000000..fd550043b
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/hooks/backend.ts b/packages/merchant-backend-ui/src/hooks/backend.ts
new file mode 100644
index 000000000..96b8f7139
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/hooks/index.ts b/packages/merchant-backend-ui/src/hooks/index.ts
new file mode 100644
index 000000000..19d672ad3
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/hooks/instance.ts b/packages/merchant-backend-ui/src/hooks/instance.ts
new file mode 100644
index 000000000..14ab8de9c
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/hooks/listener.ts b/packages/merchant-backend-ui/src/hooks/listener.ts
new file mode 100644
index 000000000..231ed6c87
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/hooks/notification.ts b/packages/merchant-backend-ui/src/hooks/notification.ts
new file mode 100644
index 000000000..d1dfbff2c
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/hooks/notifications.ts b/packages/merchant-backend-ui/src/hooks/notifications.ts
new file mode 100644
index 000000000..1c0c37308
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/hooks/order.ts b/packages/merchant-backend-ui/src/hooks/order.ts
new file mode 100644
index 000000000..4a17eac30
--- /dev/null
+++ b/packages/merchant-backend-ui/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_s
+ 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_s
+ 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/merchant-backend-ui/src/hooks/product.ts b/packages/merchant-backend-ui/src/hooks/product.ts
new file mode 100644
index 000000000..4fc8bccb7
--- /dev/null
+++ b/packages/merchant-backend-ui/src/hooks/product.ts
@@ -0,0 +1,223 @@
+/*
+ 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,
+ });
+
+ await mutateAll(/@"\/private\/products\/.*"@/);
+ 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/merchant-backend-ui/src/hooks/tips.ts b/packages/merchant-backend-ui/src/hooks/tips.ts
new file mode 100644
index 000000000..345e1faa5
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/hooks/transfer.ts b/packages/merchant-backend-ui/src/hooks/transfer.ts
new file mode 100644
index 000000000..482f00dc5
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/i18n/de.po b/packages/merchant-backend-ui/src/i18n/de.po
new file mode 100644
index 000000000..6b35bd0ce
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/i18n/en.po b/packages/merchant-backend-ui/src/i18n/en.po
new file mode 100644
index 000000000..6b35bd0ce
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/i18n/es.po b/packages/merchant-backend-ui/src/i18n/es.po
new file mode 100644
index 000000000..9075d4656
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/i18n/fr.po b/packages/merchant-backend-ui/src/i18n/fr.po
new file mode 100644
index 000000000..6b35bd0ce
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/i18n/index.tsx b/packages/merchant-backend-ui/src/i18n/index.tsx
new file mode 100644
index 000000000..63c8e1934
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/i18n/it.po b/packages/merchant-backend-ui/src/i18n/it.po
new file mode 100644
index 000000000..6b35bd0ce
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/i18n/poheader b/packages/merchant-backend-ui/src/i18n/poheader
new file mode 100644
index 000000000..ee3fcd7be
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/i18n/strings-prelude b/packages/merchant-backend-ui/src/i18n/strings-prelude
new file mode 100644
index 000000000..cca13afad
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/i18n/strings.ts b/packages/merchant-backend-ui/src/i18n/strings.ts
new file mode 100644
index 000000000..63e96949a
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/i18n/sv.po b/packages/merchant-backend-ui/src/i18n/sv.po
new file mode 100644
index 000000000..6b35bd0ce
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/i18n/taler-merchant-backoffice.pot b/packages/merchant-backend-ui/src/i18n/taler-merchant-backoffice.pot
new file mode 100644
index 000000000..21fd863b0
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/index.tsx b/packages/merchant-backend-ui/src/index.tsx
new file mode 100644
index 000000000..275f63371
--- /dev/null
+++ b/packages/merchant-backend-ui/src/index.tsx
@@ -0,0 +1,61 @@
+/*
+ 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 { Footer } from './components/Footer';
+// 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 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/merchant-backend-ui/src/pages/DepletedTip.stories.tsx b/packages/merchant-backend-ui/src/pages/DepletedTip.stories.tsx
new file mode 100644
index 000000000..c20f6dc18
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/pages/DepletedTip.tsx b/packages/merchant-backend-ui/src/pages/DepletedTip.tsx
new file mode 100644
index 000000000..756b08d6a
--- /dev/null
+++ b/packages/merchant-backend-ui/src/pages/DepletedTip.tsx
@@ -0,0 +1,60 @@
+/*
+ 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, h, render, VNode } from 'preact';
+import { render as renderToString } from 'preact-render-to-string';
+import { Footer } from '../components/Footer';
+import "../css/pure-min.css";
+import "../css/style.css";
+import { Page } from '../styled';
+
+function Head(): VNode {
+ return <title>Status of your tip</title>
+}
+
+export function DepletedTip(): VNode {
+ return <Page>
+ <section>
+ <h1>Tip already collected</h1>
+ <div>
+ You have already collected this tip.
+ </div>
+ </section>
+ <Footer />
+ </Page>
+}
+
+export function mount(): void {
+ try {
+ render(<DepletedTip />, document.body);
+ } catch (e) {
+ console.error("got error", e);
+ if (e instanceof Error) {
+ document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;
+ }
+ }
+}
+
+export function buildTimeRendering(): { head: string, body: string } {
+ return {
+ head: renderToString(<Head />),
+ body: renderToString(<DepletedTip />)
+ }
+}
diff --git a/packages/merchant-backend-ui/src/pages/OfferRefund.stories.tsx b/packages/merchant-backend-ui/src/pages/OfferRefund.stories.tsx
new file mode 100644
index 000000000..92694f867
--- /dev/null
+++ b/packages/merchant-backend-ui/src/pages/OfferRefund.stories.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, VNode, FunctionalComponent } from 'preact';
+import { createSVG } from '../components/QR';
+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
+}
+
+const REFUND_URI_EXAMPLE = 'taler://pay/backend.demo.taler.net/instances/blog/2021.249-022NW2KG88QGA/def537eb-00c2-4a8b-8a17-0be034d118d3?c=2Y4N4PMST7KYAPS83428GTPCD4'
+
+export const Example = createExample(TestedComponent, {
+ refundURI: REFUND_URI_EXAMPLE,
+ qr_code: createSVG(REFUND_URI_EXAMPLE)
+});
diff --git a/packages/merchant-backend-ui/src/pages/OfferRefund.tsx b/packages/merchant-backend-ui/src/pages/OfferRefund.tsx
new file mode 100644
index 000000000..14c9372c2
--- /dev/null
+++ b/packages/merchant-backend-ui/src/pages/OfferRefund.tsx
@@ -0,0 +1,154 @@
+/*
+ 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, h, render, VNode } from 'preact';
+import { render as renderToString } from 'preact-render-to-string';
+import { useEffect } from 'preact/hooks';
+import { Footer } from '../components/Footer';
+import { QR } from '../components/QR';
+import "../css/pure-min.css";
+import "../css/style.css";
+import { Page, QRPlaceholder, WalletLink } from '../styled';
+
+/**
+ * This page creates a refund offer QR code
+ *
+ * It will build into a mustache html template for server side rendering
+ *
+ * server side rendering params:
+ * - order_status_url
+ * - taler_refund_qrcode_svg
+ * - taler_refund_uri
+ *
+ * request params:
+ * - refund_uri
+ * - order_status_url
+ */
+
+interface Props {
+ refundURI?: string;
+ order_status_url?: string;
+ qr_code?: string;
+}
+
+function Head({ order_summary }: { order_summary?: string }): VNode {
+ return <Fragment>
+ <meta charSet="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <noscript>
+ <meta http-equiv="refresh" content="1" />
+ </noscript>
+ <title>Refund available for {order_summary ? order_summary : `{{ order_summary }}`}</title>
+ </Fragment>
+}
+
+export function OfferRefund({ refundURI, qr_code, order_status_url }: Props): VNode {
+ useEffect(() => {
+ let checkUrl: URL;
+ try {
+ checkUrl = new URL(order_status_url ? order_status_url : "{{& order_status_url }}");
+ } catch (e) {
+ return;
+ }
+ 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();
+ }
+ } 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 <Page>
+ <section>
+ <h1>Collect Taler refund</h1>
+ <p>
+ Scan this QR code with your Taler mobile wallet:
+ </p>
+ <QRPlaceholder dangerouslySetInnerHTML={{ __html: qr_code ? qr_code : `{{{ taler_refund_qrcode_svg }}}` }} />
+ <p>
+ <WalletLink href={refundURI ? refundURI : `{{ taler_refund_uri }}`}>
+ Or open your Taller wallet
+ </WalletLink>
+ </p>
+ <p>
+ <a href="https://wallet.taler.net/">Don't have a Taler wallet yet? Install it!</a>
+ </p>
+ </section>
+ <Footer />
+ </Page>
+}
+
+export function mount(): void {
+ try {
+ const fromLocation = new URL(window.location.href).searchParams
+ const os = fromLocation.get('order_summary') || undefined;
+ if (os) {
+ render(<Head order_summary={os} />, document.head);
+ }
+
+ const uri = fromLocation.get('refund_uri') || undefined;
+ const osu = fromLocation.get('order_status_url') || undefined;
+ const qr_code = uri ? renderToString(<QR text={uri} />) : undefined;
+
+ render(<OfferRefund
+ refundURI={uri} order_status_url={osu}
+ qr_code={qr_code}
+ />, document.body);
+ } catch (e) {
+ console.error("got error", e);
+ if (e instanceof Error) {
+ document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;
+ }
+ }
+}
+
+export function buildTimeRendering(): { head: string, body: string } {
+ return {
+ head: renderToString(<Head />),
+ body: renderToString(<OfferRefund />)
+ }
+}
diff --git a/packages/merchant-backend-ui/src/pages/OfferTip.stories.tsx b/packages/merchant-backend-ui/src/pages/OfferTip.stories.tsx
new file mode 100644
index 000000000..dfbf71fff
--- /dev/null
+++ b/packages/merchant-backend-ui/src/pages/OfferTip.stories.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, VNode, FunctionalComponent } from 'preact';
+import { createSVG } from '../components/QR';
+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
+}
+
+const TIP_URI_EXAMPLE = 'taler+http://tip/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0'
+
+export const Example = createExample(TestedComponent, {
+ tipURI: TIP_URI_EXAMPLE,
+ qr_code: createSVG(TIP_URI_EXAMPLE)
+});
diff --git a/packages/merchant-backend-ui/src/pages/OfferTip.tsx b/packages/merchant-backend-ui/src/pages/OfferTip.tsx
new file mode 100644
index 000000000..ace1059ca
--- /dev/null
+++ b/packages/merchant-backend-ui/src/pages/OfferTip.tsx
@@ -0,0 +1,141 @@
+/*
+ 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, h, render, VNode } from 'preact';
+import { render as renderToString } from 'preact-render-to-string';
+import { useEffect } from 'preact/hooks';
+import { Footer } from '../components/Footer';
+import { QR } from '../components/QR';
+import "../css/pure-min.css";
+import "../css/style.css";
+import { Page, QRPlaceholder, WalletLink } from '../styled';
+import { ShowOrderDetails } from './ShowOrderDetails';
+
+
+/**
+ * This page creates a tip offer QR code
+ *
+ * It will build into a mustache html template for server side rendering
+ *
+ * server side rendering params:
+ * - tip_status_url
+ * - taler_tip_qrcode_svg
+ * - taler_tip_uri
+ *
+ * request params:
+ * - tip_uri
+ * - tip_status_url
+ */
+
+interface Props {
+ tipURI?: string,
+ tip_status_url?: string,
+ qr_code?: string,
+}
+
+export function Head(): VNode {
+ return <Fragment>
+ <meta charSet="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <noscript>
+ <meta http-equiv="refresh" content="1" />
+ </noscript>
+ <title>Tip available</title>
+ </Fragment>
+}
+
+export function OfferTip({ tipURI, qr_code, tip_status_url }: Props): VNode {
+ useEffect(() => {
+ let checkUrl: URL;
+ try {
+ checkUrl = new URL(tip_status_url ? tip_status_url : "{{& tip_status_url }}");
+ } catch (e) {
+ return;
+ }
+
+ 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();
+ }
+ setTimeout(retryOnce, delayMs);
+ }
+ };
+ req.onerror = function () {
+ setTimeout(retryOnce, delayMs);
+ }
+ req.open("GET", checkUrl.href);
+ req.send();
+ }
+
+ setTimeout(check, delayMs);
+ })
+ return <Page>
+ <section>
+ <h1 >Collect Taler tip</h1>
+ <p>
+ Scan this QR code with your Taler mobile wallet:
+ </p>
+ <QRPlaceholder dangerouslySetInnerHTML={{ __html: qr_code ? qr_code : `{{{ taler_tip_qrcode_svg }}}` }} />
+ <p>
+ <WalletLink href={tipURI ? tipURI : `{{ taler_tip_uri }}`}>
+ Or open your Taller wallet
+ </WalletLink>
+ </p>
+ <p>
+ <a href="https://wallet.taler.net/">Don't have a Taler wallet yet? Install it!</a>
+ </p>
+ </section>
+ <Footer />
+ </Page>
+}
+
+export function mount(): void {
+ try {
+ const fromLocation = new URL(window.location.href).searchParams
+
+ const uri = fromLocation.get('tip_uri') || undefined
+ const tsu = fromLocation.get('tip_status_url') || undefined
+
+ render(<OfferTip tipURI={uri} tip_status_url={tsu} />, document.body);
+ } catch (e) {
+ console.error("got error", e);
+ if (e instanceof Error) {
+ document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;
+ }
+ }
+}
+
+export function buildTimeRendering(): { head: string, body: string } {
+ return {
+ head: renderToString(<Head />),
+ body: renderToString(<ShowOrderDetails />)
+ }
+}
diff --git a/packages/merchant-backend-ui/src/pages/RequestPayment.stories.tsx b/packages/merchant-backend-ui/src/pages/RequestPayment.stories.tsx
new file mode 100644
index 000000000..5d6d79adf
--- /dev/null
+++ b/packages/merchant-backend-ui/src/pages/RequestPayment.stories.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 { FunctionalComponent, h } from 'preact';
+import { createSVG } from '../components/QR';
+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
+}
+
+const PAYTO_URI_EXAMPLE = 'taler+http://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0'
+
+export const Example = createExample(TestedComponent, {
+ payURI: 'taler+http://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0',
+ qr_code: createSVG(PAYTO_URI_EXAMPLE)
+});
diff --git a/packages/merchant-backend-ui/src/pages/RequestPayment.tsx b/packages/merchant-backend-ui/src/pages/RequestPayment.tsx
new file mode 100644
index 000000000..050755dfb
--- /dev/null
+++ b/packages/merchant-backend-ui/src/pages/RequestPayment.tsx
@@ -0,0 +1,196 @@
+/*
+ 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, h, render, VNode } from "preact";
+import { render as renderToString } from "preact-render-to-string";
+import { useEffect } from "preact/hooks";
+import { Footer } from "../components/Footer";
+import "../css/pure-min.css";
+import "../css/style.css";
+import { QR } from "../components/QR";
+import { Page, QRPlaceholder, WalletLink } from "../styled";
+
+/**
+ * This page creates a payment request QR code
+ *
+ * It will build into a mustache html template for server side rendering
+ *
+ * server side rendering params:
+ * - order_status_url
+ * - taler_pay_qrcode_svg
+ * - taler_pay_uri
+ * - order_summary
+ *
+ * request params:
+ * - pay_uri
+ * - order_summary
+ * - order_status_url
+ */
+
+interface Props {
+ payURI?: string;
+ order_status_url?: string;
+ qr_code?: string;
+}
+
+function Head({ order_summary }: { order_summary?: string }): VNode {
+ return (
+ <Fragment>
+ <meta charSet="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <noscript>
+ <meta http-equiv="refresh" content="1" />
+ </noscript>
+ <title>
+ Payment requested for{" "}
+ {order_summary ? order_summary : `{{ order_summary }}`}
+ </title>
+ </Fragment>
+ );
+}
+
+export function RequestPayment({
+ payURI,
+ qr_code,
+ order_status_url,
+}: Props): VNode {
+ useEffect(() => {
+ const longpollDelayMs = 60 * 1000;
+ let checkUrl: URL;
+ try {
+ checkUrl = new URL(
+ order_status_url ? order_status_url : "{{& order_status_url }}"
+ );
+ } catch (e) {
+ return;
+ }
+ checkUrl.searchParams.set("timeout_s", 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 (
+ <Page>
+ <section>
+ <h1>Pay with Taler</h1>
+ <p>Scan this QR code with your mobile wallet:</p>
+ <QRPlaceholder
+ dangerouslySetInnerHTML={{
+ __html: qr_code ? qr_code : `{{{ taler_pay_qrcode_svg }}}`,
+ }}
+ />
+ <p>
+ <WalletLink href={payURI ? payURI : `{{ taler_pay_uri }}`}>
+ Or open your Taller wallet
+ </WalletLink>
+ </p>
+ <p>
+ <a href="https://wallet.taler.net/">
+ Don't have a Taler wallet yet? Install it!
+ </a>
+ </p>
+ </section>
+ <Footer />
+ </Page>
+ );
+}
+
+export function mount(): void {
+ try {
+ const fromLocation = new URL(window.location.href).searchParams;
+ const os = fromLocation.get("order_summary") || undefined;
+ if (os) {
+ render(<Head order_summary={os} />, document.head);
+ }
+
+ const uri = fromLocation.get("pay_uri") || undefined;
+ const osu = fromLocation.get("order_status_url") || undefined;
+ const qr_code = uri ? renderToString(<QR text={uri} />) : undefined;
+
+ render(
+ <RequestPayment payURI={uri} order_status_url={osu} qr_code={qr_code} />,
+ document.body
+ );
+ } catch (e) {
+ console.error("got error", e);
+ if (e instanceof Error) {
+ document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;
+ }
+ }
+}
+
+export function buildTimeRendering(): { head: string; body: string } {
+ return {
+ head: renderToString(<Head />),
+ body: renderToString(<RequestPayment />),
+ };
+}
diff --git a/packages/merchant-backend-ui/src/pages/ShowOrderDetails.examples.ts b/packages/merchant-backend-ui/src/pages/ShowOrderDetails.examples.ts
new file mode 100644
index 000000000..ba68397ee
--- /dev/null
+++ b/packages/merchant-backend-ui/src/pages/ShowOrderDetails.examples.ts
@@ -0,0 +1,219 @@
+/*
+ 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 { MerchantBackend } from '../declaration';
+import { Props } from './ShowOrderDetails';
+
+
+const defaultContractTerms: MerchantBackend.ContractTerms = {
+ order_id: 'XRS8876388373',
+ amount: 'USD:10',
+ summary: 'this is a short summary',
+ pay_deadline: {
+ t_s: new Date().getTime() + 6 * 24 * 60 * 60 * 1000
+ },
+ merchant: {
+ name: 'the merchant (inc)',
+ address: {
+ country_subdivision: 'Buenos Aires',
+ town: 'CABA',
+ country: 'Argentina'
+ },
+ jurisdiction: {
+ country_subdivision: 'Cordoba',
+ town: 'Capital',
+ country: 'Argentina'
+ },
+ },
+ max_fee: 'USD:0.1',
+ max_wire_fee: 'USD:0.2',
+ wire_fee_amortization: 1,
+ products: [],
+ timestamp: {
+ t_s: new Date().getTime()
+ },
+ auditors: [],
+ exchanges: [],
+ h_wire: '',
+ merchant_base_url: 'http://merchant.base.url/',
+ merchant_pub: 'QWEASDQWEASD',
+ nonce: 'NONCE',
+ refund_deadline: {
+ t_s: new Date().getTime() + 6 * 24 * 60 * 60 * 1000
+ },
+ wire_method: 'x-taler-bank',
+ wire_transfer_deadline: {
+ t_s: new Date().getTime() + 3 * 24 * 60 * 60 * 1000
+ },
+};
+
+const inSixDays = new Date().getTime() + 6 * 24 * 60 * 60 * 1000
+const in10Minutes = new Date().getTime() + 10 * 60 * 1000
+const in15Minutes = new Date().getTime() + 15 * 60 * 1000
+const in20Minutes = new Date().getTime() + 20 * 60 * 1000
+
+export const exampleData: { [name: string]: Props } = {
+ Simplest: {
+ order_summary: 'here goes the order summary',
+ contract_terms: defaultContractTerms,
+ },
+ WithRefundAmount: {
+ order_summary: 'here goes the order summary',
+ refund_amount: 'USD:10',
+ contract_terms: defaultContractTerms,
+ },
+ WithDeliveryDate: {
+ order_summary: 'here goes the order summary',
+ contract_terms: {
+ ...defaultContractTerms,
+ delivery_date: {
+ t_s: inSixDays
+ },
+ },
+ },
+ WithDeliveryLocation: {
+ order_summary: 'here goes the order summary',
+ contract_terms: {
+ ...defaultContractTerms,
+ delivery_location: {
+ address_lines: ['addr line 1', 'addr line 2', 'addr line 3', 'addr line 4', 'addr line 5', 'addr line 6', 'addr line 7'],
+ building_name: 'building-name',
+ building_number: 'building-number',
+ country: 'country',
+ country_subdivision: 'country sub',
+ district: 'district',
+ post_code: 'post-code',
+ street: 'street',
+ town: 'town',
+ town_location: 'town loc',
+ },
+ },
+ },
+ WithDeliveryLocationAndDate: {
+ order_summary: 'here goes the order summary',
+ contract_terms: {
+ ...defaultContractTerms,
+ delivery_location: {
+ address_lines: ['addr1', 'addr2', 'addr3', 'addr4', 'addr5', 'addr6', 'addr7'],
+ building_name: 'building-name',
+ building_number: 'building-number',
+ country: 'country',
+ country_subdivision: 'country sub',
+ district: 'district',
+ post_code: 'post-code',
+ street: 'street',
+ town: 'town',
+ town_location: 'town loc',
+ },
+ delivery_date: {
+ t_s: inSixDays
+ },
+ },
+ },
+ WithThreeProducts: {
+ order_summary: 'here goes the order summary',
+ contract_terms: {
+ ...defaultContractTerms,
+ products: [{
+ description: 'description of the first product',
+ price: '5:USD',
+ quantity: 1,
+ delivery_date: { t_s: in10Minutes },
+ product_id: '12333',
+ }, {
+ description: 'another description',
+ price: '10:USD',
+ quantity: 5,
+ unit: 't-shirt',
+ }, {
+ description: 'one last description',
+ price: '10:USD',
+ quantity: 5
+ }]
+ } as MerchantBackend.ContractTerms
+ },
+ WithProductWithTaxes: {
+ order_summary: 'here goes the order summary',
+ contract_terms: {
+ ...defaultContractTerms,
+ products: [{
+ description: 'description of the first product',
+ price: '5:USD',
+ quantity: 1,
+ unit: 'beer',
+ delivery_date: { t_s: in10Minutes },
+ product_id: '456',
+ taxes: [{
+ name: 'VAT', tax: 'USD:1'
+ }],
+ }, {
+ description: 'one last description',
+ price: '10:USD',
+ quantity: 5,
+ product_id: '123',
+ unit: 'beer',
+ taxes: [{
+ name: 'VAT', tax: 'USD:1'
+ }],
+ }]
+ } as MerchantBackend.ContractTerms
+ },
+ WithExchangeList: {
+ order_summary: 'here goes the order summary',
+ contract_terms: {
+ ...defaultContractTerms,
+ exchanges: [{
+ master_pub: 'ABCDEFGHIJKLMNO',
+ url: 'http://exchange0.taler.net'
+ }, {
+ master_pub: 'AAAAAAAAAAAAAAA',
+ url: 'http://exchange1.taler.net'
+ }, {
+ master_pub: 'BBBBBBBBBBBBBBB',
+ url: 'http://exchange2.taler.net'
+ }]
+ },
+ },
+ WithAuditorList: {
+ order_summary: 'here goes the order summary',
+ contract_terms: {
+ ...defaultContractTerms,
+ auditors: [{
+ auditor_pub: 'ABCDEFGHIJKLMNO',
+ name: 'the USD auditor',
+ url: 'http://auditor-usd.taler.net'
+ }, {
+ auditor_pub: 'OPQRSTUVWXYZABCD',
+ name: 'the EUR auditor',
+ url: 'http://auditor-eur.taler.net'
+ }]
+ },
+ },
+ WithAutoRefund: {
+ order_summary: 'here goes the order summary',
+ contract_terms: {
+ ...defaultContractTerms,
+ auto_refund: {
+ d_us: 1000 * 60 * 60 * 26 + 1000 * 60 * 30
+ }
+ },
+ },
+}
diff --git a/packages/merchant-backend-ui/src/pages/ShowOrderDetails.stories.tsx b/packages/merchant-backend-ui/src/pages/ShowOrderDetails.stories.tsx
new file mode 100644
index 000000000..6a902cc9e
--- /dev/null
+++ b/packages/merchant-backend-ui/src/pages/ShowOrderDetails.stories.tsx
@@ -0,0 +1,49 @@
+/*
+ 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 { FunctionalComponent, h } from 'preact';
+import { ShowOrderDetails as TestedComponent } from './ShowOrderDetails';
+import { exampleData } from './ShowOrderDetails.examples';
+
+export default {
+ title: 'ShowOrderDetails',
+ component: TestedComponent,
+ argTypes: {
+ },
+ excludeStories: /.*Data$/,
+};
+
+function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
+ const r = (args: any) => <Component {...args} />
+ r.args = props
+ return r
+}
+
+export const Simplest = createExample(TestedComponent, exampleData.Simplest);
+export const WithRefundAmount = createExample(TestedComponent, exampleData.WithRefundAmount);
+export const WithDeliveryDate = createExample(TestedComponent, exampleData.WithDeliveryDate);
+export const WithDeliveryLocation = createExample(TestedComponent, exampleData.WithDeliveryLocation);
+export const WithDeliveryLocationAndDate = createExample(TestedComponent, exampleData.WithDeliveryLocationAndDate);
+export const WithThreeProducts = createExample(TestedComponent, exampleData.WithThreeProducts);
+export const WithAuditorList = createExample(TestedComponent, exampleData.WithAuditorList);
+export const WithExchangeList = createExample(TestedComponent, exampleData.WithExchangeList);
+export const WithAutoRefund = createExample(TestedComponent, exampleData.WithAutoRefund);
+export const WithProductWithTaxes = createExample(TestedComponent, exampleData.WithProductWithTaxes);
diff --git a/packages/merchant-backend-ui/src/pages/ShowOrderDetails.tsx b/packages/merchant-backend-ui/src/pages/ShowOrderDetails.tsx
new file mode 100644
index 000000000..aa62c2932
--- /dev/null
+++ b/packages/merchant-backend-ui/src/pages/ShowOrderDetails.tsx
@@ -0,0 +1,551 @@
+/*
+ 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 { format, formatDuration } from "date-fns";
+import { intervalToDuration } from "date-fns/esm";
+import { Fragment, h, render, VNode } from "preact";
+import { render as renderToString } from "preact-render-to-string";
+import { Footer } from "../components/Footer";
+import "../css/pure-min.css";
+import "../css/style.css";
+import { MerchantBackend } from "../declaration";
+import { Page, InfoBox, TableExpanded, TableSimple } from "../styled";
+
+/**
+ * This page creates a payment request QR code
+ *
+ * It will build into a mustache html template for server side rendering
+ *
+ * server side rendering params:
+ * - order_summary
+ * - contract_terms
+ * - refund_amount
+ *
+ * request params:
+ * - refund_amount
+ * - contract_terms
+ * - order_summary
+ */
+
+export interface Props {
+ btr?: boolean; // build time rendering flag
+ order_summary?: string;
+ refund_amount?: string;
+ contract_terms?: MerchantBackend.ContractTerms;
+}
+
+function Head({ order_summary }: { order_summary?: string }): VNode {
+ return (
+ <Fragment>
+ <meta charSet="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <noscript>
+ <meta http-equiv="refresh" content="1" />
+ </noscript>
+ <title>
+ Status of your order for{" "}
+ {order_summary ? order_summary : `{{ order_summary }}`}
+ </title>
+ <script>{`
+ var contractTermsStr = '{{{contract_terms_json}}}';
+ `}</script>
+ </Fragment>
+ );
+}
+
+function Location({
+ templateName,
+ location,
+ btr,
+}: {
+ templateName: string;
+ location: MerchantBackend.Location | undefined;
+ btr?: boolean;
+}) {
+ //FIXME: mustache strings show be constructed in a way that ends in the final output of the html but is not present in the
+ // javascript code, otherwise when mustache render engine run over the html it will also replace string in the javascript code
+ // that is made to run when the browser has javascript enable leading into undefined behavior.
+ // that's why in the next fields we are using concatenations to build the mustache placeholder.
+ return (
+ <Fragment>
+ {btr && `{{` + `#${templateName}.building_name}}`}
+ <dd>
+ {location?.building_name ||
+ (btr && `{{ ${templateName}.building_name }}`)}{" "}
+ {location?.building_number ||
+ (btr && `{{ ${templateName}.building_number }}`)}
+ </dd>
+ {btr && `{{` + `/${templateName}.building_name}}`}
+
+ {btr && `{{` + `#${templateName}.country}}`}
+ <dd>
+ {location?.country || (btr && `{{ ${templateName}.country }}`)}{" "}
+ {location?.country_subdivision ||
+ (btr && `{{ ${templateName}.country_subdivision }}`)}
+ </dd>
+ {btr && `{{` + `/${templateName}.country}}`}
+
+ {btr && `{{` + `#${templateName}.district}}`}
+ <dd>{location?.district || (btr && `{{ ${templateName}.district }}`)}</dd>
+ {btr && `{{` + `/${templateName}.district}}`}
+
+ {btr && `{{` + `#${templateName}.post_code}}`}
+ <dd>
+ {location?.post_code || (btr && `{{ ${templateName}.post_code }}`)}
+ </dd>
+ {btr && `{{` + `/${templateName}.post_code}}`}
+
+ {btr && `{{` + `#${templateName}.street}}`}
+ <dd>{location?.street || (btr && `{{ ${templateName}.street }}`)}</dd>
+ {btr && `{{` + `/${templateName}.street}}`}
+
+ {btr && `{{` + `#${templateName}.town}}`}
+ <dd>{location?.town || (btr && `{{ ${templateName}.town }}`)}</dd>
+ {btr && `{{` + `/${templateName}.town}}`}
+
+ {btr && `{{` + `#${templateName}.town_location}}`}
+ <dd>
+ {location?.town_location ||
+ (btr && `{{ ${templateName}.town_location }}`)}
+ </dd>
+ {btr && `{{` + `/${templateName}.town_location}}`}
+ </Fragment>
+ );
+}
+
+export function ShowOrderDetails({
+ order_summary,
+ refund_amount,
+ contract_terms,
+ btr,
+}: Props): VNode {
+ const productList = btr
+ ? [{} as MerchantBackend.Product]
+ : contract_terms?.products || [];
+ const auditorsList = btr
+ ? [{} as MerchantBackend.Auditor]
+ : contract_terms?.auditors || [];
+ const exchangesList = btr
+ ? [{} as MerchantBackend.Exchange]
+ : contract_terms?.exchanges || [];
+ const hasDeliveryInfo =
+ btr ||
+ !!contract_terms?.delivery_date ||
+ !!contract_terms?.delivery_location;
+
+ return (
+ <Page>
+ <header>
+ <h1>
+ Details of order{" "}
+ {contract_terms?.order_id || `{{ contract_terms.order_id }}`}
+ </h1>
+ </header>
+
+ <section>
+ {btr && `{{#refund_amount}}`}
+ {(btr || refund_amount) && (
+ <section>
+ <InfoBox>
+ <b>Refunded:</b> The merchant refunded you{" "}
+ <b>{refund_amount || `{{ refund_amount }}`}</b>.
+ </InfoBox>
+ </section>
+ )}
+ {btr && `{{/refund_amount}}`}
+
+ <section>
+ <TableExpanded>
+ <dt>Order summary:</dt>
+ <dd>{contract_terms?.summary || `{{ contract_terms.summary }}`}</dd>
+ <dt>Amount paid:</dt>
+ <dd>{contract_terms?.amount || `{{ contract_terms.amount }}`}</dd>
+ <dt>Order date:</dt>
+ <dd>
+ {contract_terms?.timestamp
+ ? contract_terms?.timestamp.t_s != "never"
+ ? format(
+ contract_terms?.timestamp.t_s,
+ "dd MMM yyyy HH:mm:ss"
+ )
+ : "never"
+ : `{{ contract_terms.timestamp_str }}`}{" "}
+ </dd>
+ <dt>Merchant name:</dt>
+ <dd>
+ {contract_terms?.merchant.name ||
+ `{{ contract_terms.merchant.name }}`}
+ </dd>
+ </TableExpanded>
+ </section>
+
+ {btr && `{{#contract_terms.hasProducts}}`}
+ {!productList.length ? null : (
+ <section>
+ <h2>Products purchased</h2>
+ <TableSimple>
+ {btr && "{{" + "#contract_terms.products" + "}}"}
+ {productList.map((p, i) => {
+ const taxList = btr
+ ? [{} as MerchantBackend.Tax]
+ : p.taxes || [];
+
+ return (
+ <Fragment key={i}>
+ <p>{p.description || `{{description}}`}</p>
+ <dl>
+ <dt>Quantity:</dt>
+ <dd>{p.quantity || `{{quantity}}`}</dd>
+
+ <dt>Price:</dt>
+ <dd>{p.price || `{{price}}`}</dd>
+
+ {btr && `{{#hasTaxes}}`}
+ {!taxList.length ? null : (
+ <Fragment>
+ {btr && "{{" + "#taxes" + "}}"}
+ {taxList.map((t, i) => {
+ return (
+ <Fragment key={i}>
+ <dt>{t.name || `{{name}}`}</dt>
+ <dd>{t.tax || `{{tax}}`}</dd>
+ </Fragment>
+ );
+ })}
+ {btr && "{{" + "/taxes" + "}}"}
+ </Fragment>
+ )}
+ {btr && `{{/hasTaxes}}`}
+
+ {btr && `{{#delivery_date}}`}
+ {(btr || p.delivery_date) && (
+ <Fragment>
+ <dt>Delivered on:</dt>
+ <dd>
+ {p.delivery_date
+ ? p.delivery_date.t_s != "never"
+ ? format(
+ p.delivery_date.t_s,
+ "dd MMM yyyy HH:mm:ss"
+ )
+ : "never"
+ : `{{ delivery_date_str }}`}{" "}
+ </dd>
+ </Fragment>
+ )}
+ {btr && `{{/delivery_date}}`}
+
+ {btr && `{{#unit}}`}
+ {(btr || p.unit) && (
+ <Fragment>
+ <dt>Product unit:</dt>
+ <dd>{p.unit || `{{.}}`}</dd>
+ </Fragment>
+ )}
+ {btr && `{{/unit}}`}
+
+ {btr && `{{#product_id}}`}
+ {(btr || p.product_id) && (
+ <Fragment>
+ <dt>Product ID:</dt>
+ <dd>{p.product_id || `{{.}}`}</dd>
+ </Fragment>
+ )}
+ {btr && `{{/product_id}}`}
+ </dl>
+ </Fragment>
+ );
+ })}
+ {btr && "{{" + "/contract_terms.products" + "}}"}
+ </TableSimple>
+ </section>
+ )}
+ {btr && `{{/contract_terms.hasProducts}}`}
+
+ {btr && `{{#contract_terms.has_delivery_info}}`}
+ {!hasDeliveryInfo ? null : (
+ <section>
+ <h2>Delivery information</h2>
+ <TableExpanded>
+ {btr && `{{#contract_terms.delivery_date}}`}
+ {(btr || contract_terms?.delivery_date) && (
+ <Fragment>
+ <dt>Delivery date:</dt>
+ <dd>
+ {contract_terms?.delivery_date
+ ? contract_terms?.delivery_date.t_s != "never"
+ ? format(
+ contract_terms?.delivery_date.t_s,
+ "dd MMM yyyy HH:mm:ss"
+ )
+ : "never"
+ : `{{ contract_terms.delivery_date_str }}`}{" "}
+ </dd>
+ </Fragment>
+ )}
+ {btr && `{{/contract_terms.delivery_date}}`}
+
+ {btr && `{{#contract_terms.delivery_location}}`}
+ {(btr || contract_terms?.delivery_location) && (
+ <Fragment>
+ <dt>Delivery address:</dt>
+ <Location
+ btr={btr}
+ location={contract_terms?.delivery_location}
+ templateName="contract_terms.delivery_location"
+ />
+ </Fragment>
+ )}
+ {btr && `{{/contract_terms.delivery_location}}`}
+ </TableExpanded>
+ </section>
+ )}
+ {btr && `{{/contract_terms.has_delivery_info}}`}
+
+ <section>
+ <h2>Full payment information</h2>
+ <TableExpanded>
+ <dt>Amount paid:</dt>
+ <dd>{contract_terms?.amount || `{{ contract_terms.amount }}`}</dd>
+ <dt>Wire transfer method:</dt>
+ <dd>
+ {contract_terms?.wire_method ||
+ `{{ contract_terms.wire_method }}`}
+ </dd>
+ <dt>Payment deadline:</dt>
+ <dd>
+ {contract_terms?.pay_deadline
+ ? contract_terms?.pay_deadline.t_s != "never"
+ ? format(
+ contract_terms?.pay_deadline.t_s,
+ "dd MMM yyyy HH:mm:ss"
+ )
+ : "never"
+ : `{{ contract_terms.pay_deadline_str }}`}{" "}
+ </dd>
+ <dt>Exchange transfer deadline:</dt>
+ <dd>
+ {contract_terms?.wire_transfer_deadline
+ ? contract_terms?.wire_transfer_deadline.t_s != "never"
+ ? format(
+ contract_terms?.wire_transfer_deadline.t_s,
+ "dd MMM yyyy HH:mm:ss"
+ )
+ : "never"
+ : `{{ contract_terms.wire_transfer_deadline_str }}`}{" "}
+ </dd>
+ <dt>Maximum deposit fee:</dt>
+ <dd>{contract_terms?.max_fee || `{{ contract_terms.max_fee }}`}</dd>
+ <dt>Maximum wire fee:</dt>
+ <dd>
+ {contract_terms?.max_wire_fee ||
+ `{{ contract_terms.max_wire_fee }}`}
+ </dd>
+ <dt>Wire fee amortization:</dt>
+ <dd>
+ {contract_terms?.wire_fee_amortization ||
+ `{{ contract_terms.wire_fee_amortization }}`}{" "}
+ transactions
+ </dd>
+ </TableExpanded>
+ </section>
+
+ <section>
+ <h2>Refund information</h2>
+ <TableExpanded>
+ <dt>Refund deadline:</dt>
+ <dd>
+ {contract_terms?.refund_deadline
+ ? contract_terms?.refund_deadline.t_s != "never"
+ ? format(
+ contract_terms?.refund_deadline.t_s,
+ "dd MMM yyyy HH:mm:ss"
+ )
+ : "never"
+ : `{{ contract_terms.refund_deadline_str }}`}{" "}
+ </dd>
+
+ {btr && `{{#contract_terms.auto_refund}}`}
+ {(btr || contract_terms?.auto_refund) && (
+ <Fragment>
+ <dt>Attempt autorefund for:</dt>
+ <dd>
+ {contract_terms?.auto_refund
+ ? contract_terms?.auto_refund.d_us != "forever"
+ ? formatDuration(
+ intervalToDuration({
+ start: 0,
+ end: contract_terms?.auto_refund.d_us,
+ })
+ )
+ : "forever"
+ : `{{ contract_terms.auto_refund_str }}`}{" "}
+ </dd>
+ </Fragment>
+ )}
+ {btr && `{{/contract_terms.auto_refund}}`}
+ </TableExpanded>
+ </section>
+
+ <section>
+ <h2>Additional order details</h2>
+ <TableExpanded>
+ <dt>Public reorder URL:</dt>
+ <dd> -- not defined yet -- </dd>
+ {btr && `{{#contract_terms.fulfillment_url}}`}
+ {(btr || contract_terms?.fulfillment_url) && (
+ <Fragment>
+ <dt>Fulfillment URL:</dt>
+ <dd>
+ {contract_terms?.fulfillment_url ||
+ (btr && `{{ contract_terms.fulfillment_url }}`)}
+ </dd>
+ </Fragment>
+ )}
+ {btr && `{{/contract_terms.fulfillment_url}}`}
+ {/* <dt>Fulfillment message:</dt>
+ <dd> -- not defined yet -- </dd> */}
+ </TableExpanded>
+ </section>
+
+ <section>
+ <h2>Full merchant information</h2>
+ <TableExpanded>
+ <dt>Merchant name:</dt>
+ <dd>
+ {contract_terms?.merchant.name ||
+ `{{ contract_terms.merchant.name }}`}
+ </dd>
+ <dt>Merchant address:</dt>
+ <Location
+ btr={btr}
+ location={contract_terms?.merchant.address}
+ templateName="contract_terms.merchant.address"
+ />
+ <dt>Merchant's jurisdiction:</dt>
+ <Location
+ btr={btr}
+ location={contract_terms?.merchant.jurisdiction}
+ templateName="contract_terms.merchant.jurisdiction"
+ />
+ <dt>Merchant URI:</dt>
+ <dd>
+ {contract_terms?.merchant_base_url ||
+ `{{ contract_terms.merchant_base_url }}`}
+ </dd>
+ <dt>Merchant's public key:</dt>
+ <dd>
+ {contract_terms?.merchant_pub ||
+ `{{ contract_terms.merchant_pub }}`}
+ </dd>
+ {/* <dt>Merchant's hash:</dt>
+ <dd> -- not defined yet -- </dd> */}
+ </TableExpanded>
+ </section>
+
+ {btr && `{{#contract_terms.hasAuditors}}`}
+ {!auditorsList.length ? null : (
+ <section>
+ <h2>Auditors accepted by the merchant</h2>
+ <TableExpanded>
+ {btr && "{{" + "#contract_terms.auditors" + "}}"}
+ {auditorsList.map((p, i) => {
+ return (
+ <Fragment key={i}>
+ <p>{p.name || `{{name}}`}</p>
+ <dt>Auditor's public key:</dt>
+ <dd>{p.auditor_pub || `{{auditor_pub}}`}</dd>
+ <dt>Auditor's URL:</dt>
+ <dd>{p.url || `{{url}}`}</dd>
+ </Fragment>
+ );
+ })}
+ {btr && "{{" + "/contract_terms.auditors" + "}}"}
+ </TableExpanded>
+ </section>
+ )}
+ {btr && `{{/contract_terms.hasAuditors}}`}
+
+ {btr && `{{#contract_terms.hasExchanges}}`}
+ {!exchangesList.length ? null : (
+ <section>
+ <h2>Exchanges accepted by the merchant</h2>
+ <TableExpanded>
+ {btr && "{{" + "#contract_terms.exchanges" + "}}"}
+ {exchangesList.map((p, i) => {
+ return (
+ <Fragment key={i}>
+ <dt>Exchange's URL:</dt>
+ <dd>{p.url || `{{url}}`}</dd>
+ <dt>Public key:</dt>
+ <dd>{p.master_pub || `{{master_pub}}`}</dd>
+ </Fragment>
+ );
+ })}
+ {btr && "{{" + "/contract_terms.exchanges" + "}}"}
+ </TableExpanded>
+ </section>
+ )}
+ {btr && `{{/contract_terms.hasExchanges}}`}
+ </section>
+
+ <Footer />
+ </Page>
+ );
+}
+
+export function mount(): void {
+ try {
+ const fromLocation = new URL(window.location.href).searchParams;
+ const os = fromLocation.get("order_summary") || undefined;
+ if (os) {
+ render(<Head order_summary={os} />, document.head);
+ }
+
+ const ra = fromLocation.get("refund_amount") || undefined;
+ const ct = fromLocation.get("contract_terms") || undefined;
+
+ let contractTerms: MerchantBackend.ContractTerms | undefined;
+ try {
+ contractTerms = JSON.parse((window as any).contractTermsStr);
+ } catch {}
+
+ render(
+ <ShowOrderDetails
+ contract_terms={contractTerms}
+ order_summary={os}
+ refund_amount={ra}
+ />,
+ document.body
+ );
+ } catch (e) {
+ console.error("got error", e);
+ if (e instanceof Error) {
+ document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;
+ }
+ }
+}
+
+export function buildTimeRendering(): { head: string; body: string } {
+ return {
+ head: renderToString(<Head />),
+ body: renderToString(<ShowOrderDetails btr />),
+ };
+}
diff --git a/packages/merchant-backend-ui/src/styled/index.tsx b/packages/merchant-backend-ui/src/styled/index.tsx
new file mode 100644
index 000000000..55803b9cd
--- /dev/null
+++ b/packages/merchant-backend-ui/src/styled/index.tsx
@@ -0,0 +1,178 @@
+/*
+ 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 { styled } from '@linaria/react'
+
+export const QRPlaceholder = styled.div`
+ margin: auto;
+ text-align: center;
+ width: 340px;
+`
+
+export const FooterBar = styled.footer`
+ text-align: center;
+ background-color: #033;
+ color: white;
+ padding: 1em;
+ overflow: auto;
+
+ & > p > a:link,
+ & > p > a:visited,
+ & > p > a:hover,
+ & > p > a:active {
+ color: white;
+ }
+`
+
+export const Page = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ min-height: 100vh;
+ align-items: center;
+
+ a:link,
+ a:visited,
+ a:hover,
+ a:active {
+ color: black;
+ }
+
+ section {
+ text-align: center;
+ width: 600px;
+ /* margin: auto; */
+ /* margin-top: 0px; */
+ margin-bottom: auto;
+ /* overflow: auto; */
+ }
+ section:not(:first-of-type) {
+ margin-top: 2em;
+ }
+ & > header {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ text-align: center;
+ }
+ & > footer {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ width: 100%;
+ margin-bottom: 0px;
+ }
+`
+export const Center = styled.div`
+ display: flex;
+ justify-content: center;
+`
+
+export const WalletLink = styled.a<{ upperCased?: boolean }>`
+ display: inline-block;
+ zoom: 1;
+ line-height: normal;
+ white-space: nowrap;
+ vertical-align: middle;
+ text-align: center;
+ cursor: pointer;
+ user-select: none;
+ box-sizing: border-box;
+ text-transform: ${({ upperCased }) => upperCased ? 'uppercase' : 'none'};
+
+ font-family: inherit;
+ font-size: 100%;
+ padding: 0.5em 1em;
+ color: #444; /* rgba not supported (IE 8) */
+ color: rgba(0, 0, 0, 0.8); /* rgba supported */
+ border: 1px solid #999; /*IE 6/7/8*/
+ border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/
+ background-color: '#e6e6e6';
+ text-decoration: none;
+ border-radius: 2px;
+
+ :focus {
+ outline: 0;
+ }
+
+ &:disabled {
+ border: none;
+ background-image: none;
+ /* csslint ignore:start */
+ filter: alpha(opacity=40);
+ /* csslint ignore:end */
+ opacity: 0.4;
+ cursor: not-allowed;
+ box-shadow: none;
+ pointer-events: none;
+ }
+
+ :hover {
+ filter: alpha(opacity=90);
+ background-image: linear-gradient(
+ transparent,
+ rgba(0, 0, 0, 0.05) 40%,
+ rgba(0, 0, 0, 0.1)
+ );
+ }
+
+ background-color: #e6e6e6;
+ border-radius: 4px;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
+ 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;
+`;
+
+export const InfoBox = styled.div`
+ border-radius: 0.25em;
+ flex-direction: column;
+ /* margin: 0.5em; */
+ padding: 1em;
+ /* width: 100%; */
+ border:solid 1px #b8daff;
+ background-color:#cce5ff;
+ color:#004085;
+`
+
+export const TableExpanded = styled.dl`
+ text-align: left;
+ dt {
+ font-weight: bold;
+ margin-top: 1em;
+ }
+ dd {
+ margin-inline-start: 0px;
+ }
+`
+
+export const TableSimple = styled.dl`
+ text-align: left;
+ dt {
+ font-weight: bold;
+ display: inline-block;
+ width:30%;
+ }
+ dd {
+ margin-inline-start: 0px;
+ display: inline-block;
+ width:70%;
+ }
+` \ No newline at end of file
diff --git a/packages/merchant-backend-ui/src/utils/amount.ts b/packages/merchant-backend-ui/src/utils/amount.ts
new file mode 100644
index 000000000..85f230427
--- /dev/null
+++ b/packages/merchant-backend-ui/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_s === 'never' || //current doesnt have timestamp
+ (tail = prev[prev.length - 1]).timestamp.t_s === 'never' || // last doesnt have timestamp
+ cur.reason !== tail.reason || //different reason
+ Math.abs(cur.timestamp.t_s - tail.timestamp.t_s) > 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/merchant-backend-ui/src/utils/constants.ts b/packages/merchant-backend-ui/src/utils/constants.ts
new file mode 100644
index 000000000..37c46e4c2
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/utils/table.ts b/packages/merchant-backend-ui/src/utils/table.ts
new file mode 100644
index 000000000..3d713a6f7
--- /dev/null
+++ b/packages/merchant-backend-ui/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/merchant-backend-ui/src/utils/types.ts b/packages/merchant-backend-ui/src/utils/types.ts
new file mode 100644
index 000000000..9e49d39e1
--- /dev/null
+++ b/packages/merchant-backend-ui/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'
+
diff --git a/packages/merchant-backend-ui/tests/__mocks__/browserMocks.ts b/packages/merchant-backend-ui/tests/__mocks__/browserMocks.ts
new file mode 100644
index 000000000..ee6bba505
--- /dev/null
+++ b/packages/merchant-backend-ui/tests/__mocks__/browserMocks.ts
@@ -0,0 +1,42 @@
+/*
+ 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)
+ */
+
+// Mock Browser API's which are not supported by JSDOM, e.g. ServiceWorker, LocalStorage
+/**
+ * An example how to mock localStorage is given below πŸ‘‡
+ */
+
+/*
+// Mocks localStorage
+const localStorageMock = (function() {
+ let store = {};
+
+ return {
+ getItem: (key) => store[key] || null,
+ setItem: (key, value) => store[key] = value.toString(),
+ clear: () => store = {}
+ };
+
+})();
+
+Object.defineProperty(window, 'localStorage', {
+ value: localStorageMock
+}); */
diff --git a/packages/merchant-backend-ui/tests/__mocks__/fileMocks.ts b/packages/merchant-backend-ui/tests/__mocks__/fileMocks.ts
new file mode 100644
index 000000000..0c045e9d1
--- /dev/null
+++ b/packages/merchant-backend-ui/tests/__mocks__/fileMocks.ts
@@ -0,0 +1,24 @@
+/*
+ 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)
+ */
+
+// This fixed an error related to the CSS and loading gif breaking my Jest test
+// See https://facebook.github.io/jest/docs/en/webpack.html#handling-static-assets
+export default 'test-file-stub';
diff --git a/packages/merchant-backend-ui/tests/__mocks__/fileTransformer.js b/packages/merchant-backend-ui/tests/__mocks__/fileTransformer.js
new file mode 100644
index 000000000..51ebbfa62
--- /dev/null
+++ b/packages/merchant-backend-ui/tests/__mocks__/fileTransformer.js
@@ -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/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+// fileTransformer.js
+
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const path = require('path');
+
+module.exports = {
+ process(src, filename, config, options) {
+ return `module.exports = ${ JSON.stringify(path.basename(filename)) };`;
+ },
+};
+
diff --git a/packages/merchant-backend-ui/tests/__mocks__/setupTests.ts b/packages/merchant-backend-ui/tests/__mocks__/setupTests.ts
new file mode 100644
index 000000000..ab0f08b2f
--- /dev/null
+++ b/packages/merchant-backend-ui/tests/__mocks__/setupTests.ts
@@ -0,0 +1,28 @@
+/*
+ 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 'regenerator-runtime/runtime'
+import { configure } from 'enzyme';
+import Adapter from 'enzyme-adapter-preact-pure';
+
+configure({
+ adapter: new Adapter()
+});
diff --git a/packages/merchant-backend-ui/tests/funcitons/regex.test.ts b/packages/merchant-backend-ui/tests/funcitons/regex.test.ts
new file mode 100644
index 000000000..fc8a6a42f
--- /dev/null
+++ b/packages/merchant-backend-ui/tests/funcitons/regex.test.ts
@@ -0,0 +1,87 @@
+/*
+ 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 { AMOUNT_REGEX, PAYTO_REGEX } from "../../src/utils/constants";
+
+describe('payto uri format', () => {
+ const valids = [
+ 'payto://iban/DE75512108001245126199?amount=EUR:200.0&message=hello',
+ 'payto://ach/122000661/1234',
+ 'payto://upi/alice@example.com?receiver-name=Alice&amount=INR:200',
+ 'payto://void/?amount=EUR:10.5',
+ 'payto://ilp/g.acme.bob'
+ ]
+
+ test('should be valid', () => {
+ valids.forEach(v => expect(v).toMatch(PAYTO_REGEX))
+ });
+
+ const invalids = [
+ // has two question marks
+ 'payto://iban/DE75?512108001245126199?amount=EUR:200.0&message=hello',
+ // has a space
+ 'payto://ach /122000661/1234',
+ // has a space
+ 'payto://upi/alice@ example.com?receiver-name=Alice&amount=INR:200',
+ // invalid field name (mount instead of amount)
+ 'payto://void/?mount=EUR:10.5',
+ // payto:// is incomplete
+ 'payto: //ilp/g.acme.bob'
+ ]
+
+ test('should not be valid', () => {
+ invalids.forEach(v => expect(v).not.toMatch(PAYTO_REGEX))
+ });
+})
+
+describe('amount format', () => {
+ const valids = [
+ 'ARS:10',
+ 'COL:10.2',
+ 'UY:1,000.2',
+ 'ARS:10.123,123',
+ 'ARS:1,000,000',
+ 'ARSCOL:10',
+ 'THISISTHEMOTHERCOIN:1,000,000.123,123',
+ ]
+
+ test('should be valid', () => {
+ valids.forEach(v => expect(v).toMatch(AMOUNT_REGEX))
+ });
+
+ const invalids = [
+ //no currency name
+ ':10',
+ //use . instead of ,
+ 'ARS:1.000.000',
+ //currency name with numbers
+ '1ARS:10',
+ //currency name with numbers
+ 'AR5:10',
+ //missing value
+ 'USD:',
+ ]
+
+ test('should not be valid', () => {
+ invalids.forEach(v => expect(v).not.toMatch(AMOUNT_REGEX))
+ });
+
+}) \ No newline at end of file
diff --git a/packages/merchant-backend-ui/tests/util.ts b/packages/merchant-backend-ui/tests/util.ts
new file mode 100644
index 000000000..14b82b51c
--- /dev/null
+++ b/packages/merchant-backend-ui/tests/util.ts
@@ -0,0 +1,62 @@
+/*
+ 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 * as axios from 'axios';
+
+type Query<Req, Res> = (GetQuery | PostQuery | DeleteQuery | PatchQuery) & RequestResponse<Req, Res>
+
+interface RequestResponse<Req, Res> {
+ request?: Req,
+ params?: any,
+ response?: Res,
+}
+interface GetQuery { get: string }
+interface PostQuery { post: string }
+interface DeleteQuery { delete: string }
+interface PatchQuery { patch: string }
+
+export function simulateBackendResponse<R, T>(query: Query<R, T>): void {
+ (axios.default as jest.MockedFunction<axios.AxiosStatic>).mockImplementationOnce(function (opt?: axios.AxiosRequestConfig): axios.AxiosPromise {
+ // console.log(opt, JSON.stringify(query,undefined,2))
+ expect(opt).toBeDefined();
+ if (!opt)
+ return Promise.reject();
+
+ // expect(query.request).toStrictEqual(opt.data);
+ // expect(query.params).toStrictEqual(opt.params);
+ if ('get' in query) {
+ expect(opt.method).toBe('get');
+ expect(opt.url).toBe(query.get);
+ }
+ if ('post' in query) {
+ expect(opt.method).toBe('post');
+ expect(opt.url).toBe(query.post);
+ }
+ if ('delete' in query) {
+ expect(opt.method).toBe('delete');
+ expect(opt.url).toBe(query.delete);
+ }
+ if ('patch' in query) {
+ expect(opt.method).toBe('patch');
+ expect(opt.url).toBe(query.patch);
+ }
+ return ({ data: query.response, config: {} } as any);
+ } as any)
+}
diff --git a/packages/merchant-backend-ui/tsconfig.back.json b/packages/merchant-backend-ui/tsconfig.back.json
new file mode 100644
index 000000000..9ac5a3c25
--- /dev/null
+++ b/packages/merchant-backend-ui/tsconfig.back.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "lib": ["es6", "DOM"],
+ "jsx": "react",
+ "jsxFactory": "h",
+ "jsxFragmentFactory": "Fragment",
+ "moduleResolution": "Node",
+ "module": "ESNext",
+ "target": "ES6",
+ "noImplicitAny": true,
+ "noEmitOnError": true,
+ "strict": true,
+ "incremental": true,
+ "sourceMap": true,
+ "esModuleInterop": true,
+ "importHelpers": true,
+ "rootDir": "./src",
+ "typeRoots": ["./node_modules/@types"]
+ },
+ "include": ["src/**/*"]
+ }
+ \ No newline at end of file
diff --git a/packages/merchant-backend-ui/tsconfig.json b/packages/merchant-backend-ui/tsconfig.json
new file mode 100644
index 000000000..7a4d70a17
--- /dev/null
+++ b/packages/merchant-backend-ui/tsconfig.json
@@ -0,0 +1,61 @@
+{
+ "compilerOptions": {
+ /* Basic Options */
+ "target": "ES6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
+ "module": "ESNext", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
+ // "lib": [], /* Specify library files to be included in the compilation: */
+ "allowJs": true, /* Allow javascript files to be compiled. */
+ // "checkJs": true, /* Report errors in .js files. */
+ "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
+ "jsxFactory": "h", /* Specify the JSX factory function to use when targeting react JSX emit, e.g. React.createElement or h. */
+ "jsxFragmentFactory": "Fragment", // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#custom-jsx-factories
+ // "declaration": true, /* Generates corresponding '.d.ts' file. */
+ // "sourceMap": true, /* Generates corresponding '.map' file. */
+ // "outFile": "./", /* Concatenate and emit output to single file. */
+ // "outDir": "./", /* Redirect output structure to the directory. */
+ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
+ // "removeComments": true, /* Do not emit comments to output. */
+ "noEmit": true, /* Do not emit outputs. */
+ // "importHelpers": true, /* Import emit helpers from 'tslib'. */
+ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
+ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
+
+ /* Strict Type-Checking Options */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* Enable strict null checks. */
+ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
+ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
+
+ /* Additional Checks */
+ // "noUnusedLocals": true, /* Report errors on unused locals. */
+ // "noUnusedParameters": true, /* Report errors on unused parameters. */
+ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
+ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
+
+ /* Module Resolution Options */
+ "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
+ "esModuleInterop": true, /* */
+ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
+ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
+ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
+ // "typeRoots": [], /* List of folders to include type definitions from. */
+ // "types": [], /* Type declaration files to be included in compilation. */
+ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
+ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
+
+ /* Source Map Options */
+ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
+ // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
+ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
+
+ /* Experimental Options */
+ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
+ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
+
+ /* Advanced Options */
+ "skipLibCheck": true /* Skip type checking of declaration files. */
+ },
+ "include": ["src/**/*", "tests/**/*"]
+}