gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (18f283995 -> 62d5a5ef7)


From: gnunet
Subject: [taler-wallet-core] branch master updated (18f283995 -> 62d5a5ef7)
Date: Wed, 20 Mar 2024 14:30:12 +0100

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

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

    from 18f283995 fix #8607
     new b5312973a wip
     new a9b004e61 wip
     new 3c03ac5de remove contract terms fields that has been deprecated
     new 7c7086e11 wip
     new 630f53f8a wip
     new c09caa5fd wip, found #8653
     new 62d5a5ef7 wip, MerchantRefundResponse is deprecated

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


Summary of changes:
 packages/bank-ui/package.json                      |    8 +-
 .../merchant-backoffice-ui/src/hooks/backend.ts    |    2 +-
 .../src/hooks/{bank.ts => merchant.ts}             |    0
 .../src/integrationtests/test-wallet-wirefees.ts   |    1 -
 .../.eslintrc.cjs                                  |    0
 packages/taler-util/package.json                   |    4 +-
 packages/taler-util/src/MerchantApiClient.ts       |    7 +-
 packages/taler-util/src/http-client/README.md      |   24 +
 packages/taler-util/src/http-client/merchant.ts    | 1655 ++++++++++++++++++++
 packages/taler-util/src/http-client/types.ts       | 1137 ++++++++++++--
 packages/taler-util/src/http-client/utils.ts       |   15 +
 packages/taler-util/src/merchant-api-types.ts      |   26 +-
 packages/taler-util/src/taler-types.ts             |  344 ++--
 packages/taler-util/src/taleruri.ts                |   10 +-
 packages/taler-util/src/wallet-types.ts            |   14 +-
 packages/taler-wallet-core/src/deposits.ts         |   15 +-
 packages/taler-wallet-core/src/pay-merchant.ts     |   26 +-
 .../src/components/PaymentButtons.tsx              |   26 +-
 .../ShowFullContractTermPopup.stories.tsx          |    1 -
 .../src/components/ShowFullContractTermPopup.tsx   |   16 +-
 pnpm-lock.yaml                                     |   12 +-
 21 files changed, 2893 insertions(+), 450 deletions(-)
 copy packages/merchant-backoffice-ui/src/hooks/{bank.ts => merchant.ts} (100%)
 copy packages/{aml-backoffice-ui => taler-util}/.eslintrc.cjs (100%)
 create mode 100644 packages/taler-util/src/http-client/README.md
 create mode 100644 packages/taler-util/src/http-client/merchant.ts

diff --git a/packages/bank-ui/package.json b/packages/bank-ui/package.json
index dd5589fe2..c25decf8d 100644
--- a/packages/bank-ui/package.json
+++ b/packages/bank-ui/package.json
@@ -27,6 +27,10 @@
   },
   "devDependencies": {
     "eslint": "^8.56.0",
+    "@typescript-eslint/eslint-plugin": "^6.19.0",
+    "@typescript-eslint/parser": "^6.19.0",
+    "eslint-config-prettier": "^9.1.0",
+    "eslint-plugin-react": "^7.33.2",
     "@gnu-taler/pogen": "^0.0.5",
     "@tailwindcss/forms": "^0.5.3",
     "@tailwindcss/typography": "^0.5.9",
@@ -34,13 +38,9 @@
     "@types/history": "^4.7.8",
     "@types/mocha": "^10.0.1",
     "@types/node": "^18.11.17",
-    "@typescript-eslint/eslint-plugin": "^6.19.0",
-    "@typescript-eslint/parser": "^6.19.0",
     "autoprefixer": "^10.4.14",
     "chai": "^4.3.6",
     "esbuild": "^0.19.9",
-    "eslint-config-prettier": "^9.1.0",
-    "eslint-plugin-react": "^7.33.2",
     "mocha": "9.2.0",
     "po2json": "^0.4.5",
     "tailwindcss": "^3.3.2",
diff --git a/packages/merchant-backoffice-ui/src/hooks/backend.ts 
b/packages/merchant-backoffice-ui/src/hooks/backend.ts
index a3bb43545..292261bc8 100644
--- a/packages/merchant-backoffice-ui/src/hooks/backend.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/backend.ts
@@ -278,7 +278,7 @@ export function useBackendBaseRequest(): 
useBackendBaseRequestType {
 
 export function useBackendInstanceRequest(): useBackendInstanceRequestType {
   const { url: rootBackendUrl, token: rootToken } = useBackendContext();
-  const { token: instanceToken, id, admin } = useInstanceContext();
+  const { token: instanceToken, admin } = useInstanceContext();
   const { request: requestHandler } = useApiContext();
 
   const { baseUrl, token: loginToken } = !admin
diff --git a/packages/merchant-backoffice-ui/src/hooks/bank.ts 
b/packages/merchant-backoffice-ui/src/hooks/merchant.ts
similarity index 100%
copy from packages/merchant-backoffice-ui/src/hooks/bank.ts
copy to packages/merchant-backoffice-ui/src/hooks/merchant.ts
diff --git 
a/packages/taler-harness/src/integrationtests/test-wallet-wirefees.ts 
b/packages/taler-harness/src/integrationtests/test-wallet-wirefees.ts
index 618b8a144..1bf9bd659 100644
--- a/packages/taler-harness/src/integrationtests/test-wallet-wirefees.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallet-wirefees.ts
@@ -134,7 +134,6 @@ export async function runWalletWirefeesTest(t: 
GlobalTestState) {
     fulfillment_url: "taler://fulfillment-success/thx",
     //max_wire_fee: "TESTKUDOS:0.1",
     max_fee: "TESTKUDOS:0.1",
-    wire_fee_amortization: 1,
   } satisfies Partial<MerchantContractTerms>;
 
   const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
diff --git a/packages/aml-backoffice-ui/.eslintrc.cjs 
b/packages/taler-util/.eslintrc.cjs
similarity index 100%
copy from packages/aml-backoffice-ui/.eslintrc.cjs
copy to packages/taler-util/.eslintrc.cjs
diff --git a/packages/taler-util/package.json b/packages/taler-util/package.json
index 1caf1af9e..c1ff9873f 100644
--- a/packages/taler-util/package.json
+++ b/packages/taler-util/package.json
@@ -64,11 +64,13 @@
     "pretty": "prettier --write src"
   },
   "devDependencies": {
+    "eslint": "^8.56.0",
+    "@typescript-eslint/eslint-plugin": "^6.19.0",
+    "@typescript-eslint/parser": "^6.19.0",
     "@types/follow-redirects": "^1.14.4",
     "@types/node": "^18.11.17",
     "ava": "^6.0.1",
     "esbuild": "^0.19.9",
-    "prettier": "^3.1.1",
     "typescript": "^5.3.3"
   },
   "dependencies": {
diff --git a/packages/taler-util/src/MerchantApiClient.ts 
b/packages/taler-util/src/MerchantApiClient.ts
index a71887940..65827849d 100644
--- a/packages/taler-util/src/MerchantApiClient.ts
+++ b/packages/taler-util/src/MerchantApiClient.ts
@@ -18,6 +18,7 @@ import { codecForAny } from "./codec.js";
 import {
   TalerMerchantApi,
   codecForMerchantConfig,
+  codecForMerchantOrderPrivateStatusResponse,
 } from "./http-client/types.js";
 import { HttpStatusCode } from "./http-status-codes.js";
 import {
@@ -30,12 +31,10 @@ import { LibtoolVersion } from "./libtool-version.js";
 import { Logger } from "./logging.js";
 import {
   MerchantInstancesResponse,
-  MerchantOrderPrivateStatusResponse,
   MerchantPostOrderRequest,
   MerchantPostOrderResponse,
   MerchantTemplateAddDetails,
-  codecForMerchantOrderPrivateStatusResponse,
-  codecForMerchantPostOrderResponse,
+  codecForMerchantPostOrderResponse
 } from "./merchant-api-types.js";
 import {
   FailCasesByMethod,
@@ -258,7 +257,7 @@ export class MerchantApiClient {
 
   async queryPrivateOrderStatus(
     query: PrivateOrderStatusQuery,
-  ): Promise<MerchantOrderPrivateStatusResponse> {
+  ): Promise<TalerMerchantApi.MerchantOrderStatusResponse> {
     const reqUrl = new URL(`private/orders/${query.orderId}`, this.baseUrl);
     if (query.sessionId) {
       reqUrl.searchParams.set("session_id", query.sessionId);
diff --git a/packages/taler-util/src/http-client/README.md 
b/packages/taler-util/src/http-client/README.md
new file mode 100644
index 000000000..5af5d48a7
--- /dev/null
+++ b/packages/taler-util/src/http-client/README.md
@@ -0,0 +1,24 @@
+HTTP Cclients
+-------------
+
+This folder contain class or function specifically designed to facilitate HTTP 
client
+interactions with a the core systems. 
+
+These API defines:
+
+1.  **API Communication**: Handle communication with the component API, 
+abstracting away the details of HTTP requests and responses. 
+This includes making GET, POST, PUT, and DELETE requests to the servers.
+    
+2.  **Data Formatting**: Responsible for formatting requests to the API in a 
+way that's expected by the servers (JSON) and parsing the responses back 
+into formats usable by the client.
+    
+3.  **Authentication and Security**: Handling authentication with the server 
API, 
+which could involve sending API keys, client credentials, or managing tokens. 
+It might also implement security features to ensure data integrity and 
confidentiality during transit.
+    
+4.  **Error Handling**: Providing robust error handling and retry mechanisms 
+for failed HTTP requests, including logging and potentially user notifications 
for critical failures.
+    
+5.  **Data Validation**: Before sending requests, it could validate the data 
to ensure it meets the API's expected format, types, and value ranges, reducing 
the likelihood of errors and improving system reliability.
diff --git a/packages/taler-util/src/http-client/merchant.ts 
b/packages/taler-util/src/http-client/merchant.ts
new file mode 100644
index 000000000..1cc410191
--- /dev/null
+++ b/packages/taler-util/src/http-client/merchant.ts
@@ -0,0 +1,1655 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022-2024 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import {
+  HttpStatusCode,
+  LibtoolVersion,
+  PaginationParams,
+  TalerMerchantApi,
+  codecForAbortResponse,
+  codecForAccountAddResponse,
+  codecForAccountKycRedirects,
+  codecForAccountsSummaryResponse,
+  codecForBankAccountEntry,
+  codecForClaimResponse,
+  codecForInstancesResponse,
+  codecForInventorySummaryResponse,
+  codecForMerchantConfig,
+  codecForMerchantOrderPrivateStatusResponse,
+  codecForMerchantRefundResponse,
+  codecForOrderHistory,
+  codecForOtpDeviceDetails,
+  codecForOtpDeviceSummaryResponse,
+  codecForOutOfStockResponse,
+  codecForPaidRefundStatusResponse,
+  codecForPaymentResponse,
+  codecForPostOrderResponse,
+  codecForProductDetail,
+  codecForQueryInstancesResponse,
+  codecForStatusGoto,
+  codecForStatusPaid,
+  codecForStatusStatusUnpaid,
+  codecForTansferList,
+  codecForTemplateDetails,
+  codecForTemplateSummaryResponse,
+  codecForTokenFamiliesList,
+  codecForTokenFamilyDetails,
+  codecForWalletRefundResponse,
+  codecForWalletTemplateDetails,
+  codecForWebhookDetails,
+  codecForWebhookSummaryResponse,
+  opEmptySuccess,
+  opKnownAlternativeFailure,
+  opKnownHttpFailure
+} from "@gnu-taler/taler-util";
+import {
+  HttpRequestLibrary,
+  HttpResponse,
+  createPlatformHttpLib,
+} from "@gnu-taler/taler-util/http";
+import { opSuccessFromHttp, opUnknownFailure } from "../operation.js";
+import { CacheEvictor, addMerchantPaginationParams, nullEvictor } from 
"./utils.js";
+
+export enum TalerMerchantCacheEviction {
+  CREATE_ORDER,
+}
+/**
+ * Protocol version spoken with the core bank.
+ *
+ * Endpoint must be ordered in the same way that in the docs
+ * Response code (http and taler) must have the same order that in the docs
+ * That way is easier to see changes
+ *
+ * Uses libtool's current:revision:age versioning.
+ */
+export class TalerMerchantInstanceHttpClient {
+  public readonly PROTOCOL_VERSION = "10:0:6";
+
+  readonly httpLib: HttpRequestLibrary;
+  readonly cacheEvictor: CacheEvictor<TalerMerchantCacheEviction>;
+
+  constructor(
+    readonly baseUrl: string,
+    httpClient?: HttpRequestLibrary,
+    cacheEvictor?: CacheEvictor<TalerMerchantCacheEviction>,
+  ) {
+    this.httpLib = httpClient ?? createPlatformHttpLib();
+    this.cacheEvictor = cacheEvictor ?? nullEvictor;
+  }
+
+  isCompatible(version: string): boolean {
+    const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version);
+    return compare?.compatible ?? false;
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#get--config
+   *
+   */
+  async getConfig() {
+    const url = new URL(`config`, this.baseUrl);
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForMerchantConfig());
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  //
+  // Wallet API
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-orders-$ORDER_ID-claim
+   */
+  async claimOrder(orderId: string, body: TalerMerchantApi.ClaimRequest) {
+    const url = new URL(`orders/${orderId}/claim`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForClaimResponse());
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-orders-$ORDER_ID-pay
+   */
+  async makePayment(orderId: string, body: TalerMerchantApi.PayRequest) {
+    const url = new URL(`orders/${orderId}/pay`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForPaymentResponse());
+      case HttpStatusCode.BadRequest:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.PaymentRequired:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Forbidden:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.RequestTimeout:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Gone:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.PreconditionFailed:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.BadGateway:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.GatewayTimeout:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-orders-$ORDER_ID
+   */
+
+  async getPaymentStatus(
+    orderId: string,
+    params: TalerMerchantApi.PaymentStatusRequestParams = {},
+  ) {
+    const url = new URL(`orders/${orderId}`, this.baseUrl);
+
+    if (params.allowRefundedForRepurchase !== undefined) {
+      url.searchParams.set(
+        "allow_refunded_for_repurchase",
+        params.allowRefundedForRepurchase ? "YES" : "NO",
+      );
+    }
+    if (params.awaitRefundObtained !== undefined) {
+      url.searchParams.set(
+        "await_refund_obtained",
+        params.allowRefundedForRepurchase ? "YES" : "NO",
+      );
+    }
+    if (params.claimToken !== undefined) {
+      url.searchParams.set("token", params.claimToken);
+    }
+    if (params.contractTermHash !== undefined) {
+      url.searchParams.set("h_contract", params.contractTermHash);
+    }
+    if (params.refund !== undefined) {
+      url.searchParams.set("refund", params.refund);
+    }
+    if (params.sessionId !== undefined) {
+      url.searchParams.set("session_id", params.sessionId);
+    }
+    if (params.timeout !== undefined) {
+      url.searchParams.set("timeout_ms", String(params.timeout));
+    }
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+      // body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForStatusPaid());
+      case HttpStatusCode.Accepted:
+        return opSuccessFromHttp(resp, codecForStatusGoto());
+      // case HttpStatusCode.Found: not possible since content is not HTML
+      case HttpStatusCode.PaymentRequired:
+        return opSuccessFromHttp(resp, codecForStatusStatusUnpaid());
+      case HttpStatusCode.Forbidden:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotAcceptable:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#demonstrating-payment
+   */
+  async demostratePayment(orderId: string, body: TalerMerchantApi.PaidRequest) 
{
+    const url = new URL(`orders/${orderId}/paid`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForPaidRefundStatusResponse());
+      case HttpStatusCode.BadRequest:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Forbidden:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#aborting-incomplete-payments
+   */
+  async abortIncompletePayment(
+    orderId: string,
+    body: TalerMerchantApi.AbortRequest,
+  ) {
+    const url = new URL(`orders/${orderId}/abort`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForAbortResponse());
+      case HttpStatusCode.BadRequest:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Forbidden:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#obtaining-refunds
+   */
+  async obtainRefund(
+    orderId: string,
+    body: TalerMerchantApi.WalletRefundRequest,
+  ) {
+    const url = new URL(`orders/${orderId}/refund`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForWalletRefundResponse());
+      case HttpStatusCode.BadRequest:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Forbidden:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  //
+  // Management
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-auth
+   */
+  async updateCurrentInstanceAuthentication(
+    body: TalerMerchantApi.InstanceAuthConfigurationMessage,
+  ) {
+    const url = new URL(`private/auth`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    //
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * Get the auth api agaisnt the current instance
+   *
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-token
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-token
+   */
+  getAuthenticationAPI(): URL {
+    return new URL(`/`, this.baseUrl);
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private
+   */
+  async updateCurrentInstance(
+    body: TalerMerchantApi.InstanceReconfigurationMessage,
+  ) {
+    const url = new URL(`private`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "PATCH",
+      body,
+    });
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private
+   *
+   */
+  async getCurrentInstance() {
+    const url = new URL(`private`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForQueryInstancesResponse());
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private
+   */
+  async deleteCurrentInstance(params: { purge?: boolean }) {
+    const url = new URL(`private`, this.baseUrl);
+
+    if (params.purge) {
+      url.searchParams.set("purge", "YES");
+    }
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "DELETE",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.Unauthorized:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get--instances-$INSTANCE-private-kyc
+   */
+  async getCurrentIntanceKycStatus(
+    params: TalerMerchantApi.GetKycStatusRequestParams,
+  ) {
+    const url = new URL(`private/kyc`, this.baseUrl);
+
+    if (params.wireHash) {
+      url.searchParams.set("h_wire", params.wireHash);
+    }
+    if (params.exchangeURL) {
+      url.searchParams.set("exchange_url", params.exchangeURL);
+    }
+    if (params.timeout) {
+      url.searchParams.set("timeout_ms", String(params.timeout));
+    }
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Accepted:
+        return opSuccessFromHttp(resp, codecForAccountKycRedirects());
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.BadGateway:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.ServiceUnavailable:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  //
+  // Bank Accounts
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-accounts
+   */
+  async addAccount(body: TalerMerchantApi.AccountAddDetails) {
+    const url = new URL(`private/accounts`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForAccountAddResponse());
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private-accounts-$H_WIRE
+   */
+  async updateAccount(
+    wireAccount: string,
+    body: TalerMerchantApi.AccountPatchDetails,
+  ) {
+    const url = new URL(`private/accounts/${wireAccount}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "PATCH",
+      body,
+    });
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-accounts
+   */
+  async listAccounts() {
+    const url = new URL(`private/accounts`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForAccountsSummaryResponse());
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-accounts-$H_WIRE
+   */
+  async getAccount(wireAccount: string) {
+    const url = new URL(`private/accounts/${wireAccount}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForBankAccountEntry());
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-accounts-$H_WIRE
+   */
+  async deleteAccount(wireAccount: string) {
+    const url = new URL(`private/accounts/${wireAccount}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "DELETE",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  //
+  // Inventory Management
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-products
+   */
+  async addProduct(body: TalerMerchantApi.ProductAddDetail) {
+    const url = new URL(`private/products`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private-products-$PRODUCT_ID
+   */
+  async updateProduct(
+    productId: string,
+    body: TalerMerchantApi.ProductAddDetail,
+  ) {
+    const url = new URL(`private/products/${productId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "PATCH",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-products
+   */
+  async listProducts(params?: PaginationParams) {
+    const url = new URL(`private/products`, this.baseUrl);
+
+    addMerchantPaginationParams(url, params);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForInventorySummaryResponse());
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-products-$PRODUCT_ID
+   */
+  async getProduct(productId: string) {
+    const url = new URL(`private/products/${productId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForProductDetail());
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#reserving-inventory
+   */
+  async lockProduct(productId: string) {
+    const url = new URL(`private/products/${productId}/lock`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp)
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Gone:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#removing-products-from-inventory
+   */
+  async removeProduct(productId: string) {
+    const url = new URL(`private/products/${productId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "DELETE",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp)
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  //
+  // Payment processing
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-orders
+   */
+  async createOrder(body: TalerMerchantApi.PostOrderRequest) {
+    const url = new URL(`private/orders`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+    return TalerMerchantInstanceHttpClient.procesOrderCreationResponse(resp)
+  }
+
+  private static async procesOrderCreationResponse(resp: HttpResponse) {
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForPostOrderResponse())
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Gone:
+        return opKnownAlternativeFailure(resp, resp.status, 
codecForOutOfStockResponse());
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#inspecting-orders
+   */
+  async listOrders(params: TalerMerchantApi.ListOrdersRequestParams = {}) {
+    const url = new URL(`private/orders`, this.baseUrl);
+
+    if (params.date) {
+      url.searchParams.set("date_s", String(params.date));
+    }
+    if (params.fulfillmentUrl) {
+      url.searchParams.set("fulfillment_url", params.fulfillmentUrl);
+    }
+    if (params.paid) {
+      url.searchParams.set("paid", "YES");
+    }
+    if (params.refunded) {
+      url.searchParams.set("refunded", "YES");
+    }
+    if (params.sessionId) {
+      url.searchParams.set("session_id", params.sessionId);
+    }
+    if (params.timeout) {
+      url.searchParams.set("timeout", String(params.timeout));
+    }
+    if (params.wired) {
+      url.searchParams.set("wired", "YES");
+    }
+    addMerchantPaginationParams(url, params)
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForOrderHistory())
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-orders-$ORDER_ID
+   */
+  async getOrder(
+    orderId: string,
+    params: TalerMerchantApi.GetOrderRequestParams = {},
+  ) {
+    const url = new URL(`private/orders/${orderId}`, this.baseUrl);
+
+    if (params.allowRefundedForRepurchase) {
+      url.searchParams.set("allow_refunded_for_repurchase", "YES");
+    }
+    if (params.sessionId) {
+      url.searchParams.set("session_id", params.sessionId);
+    }
+    if (params.timeout) {
+      url.searchParams.set("timeout_ms", String(params.timeout));
+    }
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, 
codecForMerchantOrderPrivateStatusResponse())
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.BadGateway:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.GatewayTimeout:
+        return opKnownAlternativeFailure(resp, resp.status, 
codecForOutOfStockResponse());
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#private-order-data-cleanup
+   */
+  async forgetOrder(orderId: string, body: TalerMerchantApi.ForgetRequest) {
+    const url = new URL(`private/orders/${orderId}/forget`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "PATCH",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opEmptySuccess(resp)
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp)
+      case HttpStatusCode.BadRequest:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-orders-$ORDER_ID
+   */
+  async deleteOrder(orderId: string) {
+    const url = new URL(`private/orders/${orderId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "DELETE",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp)
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  //
+  // Refunds
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-orders-$ORDER_ID-refund
+   */
+  async addRefund(orderId: string, body: TalerMerchantApi.RefundRequest) {
+    const url = new URL(`private/orders/${orderId}/refund`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForMerchantRefundResponse())
+      case HttpStatusCode.Forbidden:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Gone:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  //
+  // Wire Transfer
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-transfers
+   */
+  async informWireTransfer(body: TalerMerchantApi.TransferInformation) {
+    const url = new URL(`private/transfers`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp)
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-transfers
+   */
+  async listWireTransfers(
+    params: TalerMerchantApi.ListWireTransferRequestParams = {},
+  ) {
+    const url = new URL(`private/transfers`, this.baseUrl);
+
+    if (params.after) {
+      url.searchParams.set("after", String(params.after));
+    }
+    if (params.before) {
+      url.searchParams.set("before", String(params.before));
+    }
+    if (params.paytoURI) {
+      url.searchParams.set("payto_uri", params.paytoURI);
+    }
+    if (params.verified) {
+      url.searchParams.set("verified", "YES");
+    }
+    addMerchantPaginationParams(url, params)
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForTansferList())
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-transfers-$TID
+   */
+  async deleteWireTransfer(transferId: string) {
+    const url = new URL(`private/transfers/${transferId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "DELETE",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp)
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  //
+  // OTP Devices
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-otp-devices
+   */
+  async addOtpDevice(body: TalerMerchantApi.OtpDeviceAddDetails) {
+    const url = new URL(`private/otp-devices`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp)
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private-otp-devices-$DEVICE_ID
+   */
+  async updateOtpDevice(
+    deviceId: string,
+    body: TalerMerchantApi.OtpDevicePatchDetails,
+  ) {
+    const url = new URL(`private/otp-devices/${deviceId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "PATCH",
+      body,
+    });
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp)
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-otp-devices
+   */
+  async listOtpDevices() {
+    const url = new URL(`private/otp-devices`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForOtpDeviceSummaryResponse())
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-otp-devices-$DEVICE_ID
+   */
+  async getOtpDevice(
+    deviceId: string,
+    params: TalerMerchantApi.GetOtpDeviceRequestParams = {},
+  ) {
+    const url = new URL(`private/otp-devices/${deviceId}`, this.baseUrl);
+
+    if (params.faketime) {
+      url.searchParams.set("faketime", String(params.faketime));
+    }
+    if (params.price) {
+      url.searchParams.set("price", params.price);
+    }
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForOtpDeviceDetails())
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-otp-devices-$DEVICE_ID
+   */
+  async deleteOtpDevice(deviceId: string) {
+    const url = new URL(`private/otp-devices/${deviceId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "DELETE",
+    });
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  //
+  // Templates
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-templates
+   */
+  async addTemplate(body: TalerMerchantApi.TemplateAddDetails) {
+    const url = new URL(`private/templates`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private-templates-$TEMPLATE_ID
+   */
+  async updateTemplate(
+    templateId: string,
+    body: TalerMerchantApi.TemplatePatchDetails,
+  ) {
+    const url = new URL(`private/templates/${templateId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "PATCH",
+      body,
+    });
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#inspecting-template
+   */
+  async listTemplates() {
+    const url = new URL(`private/templates`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForTemplateSummaryResponse());
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-templates-$TEMPLATE_ID
+   */
+  async getTemplate(templateId: string) {
+    const url = new URL(`private/templates/${templateId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForTemplateDetails());
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-templates-$TEMPLATE_ID
+   */
+  async deleteTemplate(templateId: string) {
+    const url = new URL(`private/templates/${templateId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "DELETE",
+    });
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-templates-$TEMPLATE_ID
+   */
+  async useTemplateGetInfo(templateId: string) {
+    const url = new URL(`templates/${templateId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForWalletTemplateDetails());
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCES]-templates-$TEMPLATE_ID
+   */
+  async useTemplateCreateOrder(
+    templateId: string,
+    body: TalerMerchantApi.UsingTemplateDetails,
+  ) {
+    const url = new URL(`templates/${templateId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    return TalerMerchantInstanceHttpClient.procesOrderCreationResponse(resp)
+  }
+
+  //
+  // Webhooks
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCES]-private-webhooks
+   */
+  async addWebhook(body: TalerMerchantApi.WebhookAddDetails) {
+    const url = new URL(`private/webhooks`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp)
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCES]-private-webhooks-$WEBHOOK_ID
+   */
+  async updateWebhook(
+    webhookId: string,
+    body: TalerMerchantApi.WebhookPatchDetails,
+  ) {
+    const url = new URL(`private/webhooks/${webhookId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "PATCH",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp)
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-webhooks
+   */
+  async listWebhooks() {
+    const url = new URL(`private/webhooks`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opSuccessFromHttp(resp, codecForWebhookSummaryResponse())
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-webhooks-$WEBHOOK_ID
+   */
+  async getWebhook(webhookId: string) {
+    const url = new URL(`private/webhooks/${webhookId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opSuccessFromHttp(resp, codecForWebhookDetails())
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCES]-private-webhooks-$WEBHOOK_ID
+   */
+  async removeWebhook(webhookId: string) {
+    const url = new URL(`private/webhooks/${webhookId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "DELETE",
+    });
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+
+  }
+
+  //
+  // token families
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCES]-private-tokenfamilies
+   */
+  async createTokenFamily(body: TalerMerchantApi.TokenFamilyCreateRequest) {
+    const url = new URL(`private/tokenfamilies`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCES]-private-tokenfamilies-$TOKEN_FAMILY_SLUG
+   */
+  async updateTokenFamily(
+    tokenSlug: string,
+    body: TalerMerchantApi.TokenFamilyUpdateRequest,
+  ) {
+    const url = new URL(`private/tokenfamilies/${tokenSlug}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForTokenFamilyDetails());
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-tokenfamilies
+   */
+  async listTokenFamilies() {
+    const url = new URL(`private/tokenfamilies`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForTokenFamiliesList());
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-tokenfamilies-$TOKEN_FAMILY_SLUG
+   */
+  async getTokenFamily(tokenSlug: string) {
+    const url = new URL(`private/tokenfamilies/${tokenSlug}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForTokenFamilyDetails());
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCES]-private-tokenfamilies-$TOKEN_FAMILY_SLUG
+   */
+  async deleteTokenFamily(tokenSlug: string) {
+    const url = new URL(`private/tokenfamilies/${tokenSlug}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "DELETE",
+    });
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+}
+
+export class TalerMerchantManagementHttpClient extends 
TalerMerchantInstanceHttpClient {
+  constructor(
+    readonly baseUrl: string,
+    httpClient?: HttpRequestLibrary,
+    cacheEvictor?: CacheEvictor<TalerMerchantCacheEviction>,
+  ) {
+    super(baseUrl, httpClient, cacheEvictor);
+  }
+
+  getSubInstanceApi(instanceId: string) {
+    return new URL(`instances/${instanceId}`, this.baseUrl);
+  }
+
+  //
+  // Instance Management
+  //
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#post--management-instances
+   */
+  async createInstance(body: TalerMerchantApi.InstanceConfigurationMessage) {
+    const url = new URL(`management/instances`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post--management-instances-$INSTANCE-auth
+   */
+  async updateInstanceAuthentication(
+    body: TalerMerchantApi.InstanceAuthConfigurationMessage,
+  ) {
+    const url = new URL(`management/instances`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch--management-instances-$INSTANCE
+   */
+  async updateInstance(
+    instanceId: string,
+    body: TalerMerchantApi.InstanceReconfigurationMessage,
+  ) {
+    const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "PATCH",
+      body,
+    });
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#get--management-instances
+   */
+  async listInstances() {
+    const url = new URL(`management/instances`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForInstancesResponse())
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get--management-instances-$INSTANCE
+   *
+   */
+  async getInstance(instanceId: string) {
+    const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForQueryInstancesResponse())
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete--management-instances-$INSTANCE
+   */
+  async deleteInstance(instanceId: string, params: { purge?: boolean } = {}) {
+    const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
+
+    if (params.purge) {
+      url.searchParams.set("purge", "YES");
+    }
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "DELETE",
+    });
+    switch (resp.status) {
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.Unauthorized:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get--management-instances-$INSTANCE-kyc
+   */
+  async getIntanceKycStatus(
+    instanceId: string,
+    params: TalerMerchantApi.GetKycStatusRequestParams,
+  ) {
+    const url = new URL(`management/instances/${instanceId}/kyc`, 
this.baseUrl);
+
+    if (params.wireHash) {
+      url.searchParams.set("h_wire", params.wireHash);
+    }
+    if (params.exchangeURL) {
+      url.searchParams.set("exchange_url", params.exchangeURL);
+    }
+    if (params.timeout) {
+      url.searchParams.set("timeout_ms", String(params.timeout));
+    }
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+    switch (resp.status) {
+      case HttpStatusCode.Accepted:
+        return opSuccessFromHttp(resp, codecForAccountKycRedirects());
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess(resp);
+      case HttpStatusCode.BadGateway:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.ServiceUnavailable:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+}
diff --git a/packages/taler-util/src/http-client/types.ts 
b/packages/taler-util/src/http-client/types.ts
index 132ca867d..5fa3d0e7a 100644
--- a/packages/taler-util/src/http-client/types.ts
+++ b/packages/taler-util/src/http-client/types.ts
@@ -6,6 +6,7 @@ import {
   buildCodecForUnion,
   codecForAny,
   codecForBoolean,
+  codecForConstNumber,
   codecForConstString,
   codecForEither,
   codecForList,
@@ -15,11 +16,18 @@ import {
   codecOptional,
 } from "../codec.js";
 import { PaytoString, codecForPaytoString } from "../payto.js";
-import { AmountString } from "../taler-types.js";
-import { TalerActionString, codecForTalerActionString } from "../taleruri.js";
 import {
+  AmountString,
+  codecForInternationalizedString,
+  codecForLocation,
+} from "../taler-types.js";
+import { TalerUriString, codecForTalerUriString } from "../taleruri.js";
+import {
+  AbsoluteTime,
   TalerProtocolDuration,
   TalerProtocolTimestamp,
+  codecForAbsoluteTime,
+  codecForDuration,
   codecForTimestamp,
 } from "../time.js";
 
@@ -60,7 +68,7 @@ export type PaginationParams = {
   /**
    * order
    */
-  order: "asc" | "dec";
+  order?: "asc" | "dec";
 };
 
 export type LongPollParams = {
@@ -293,6 +301,14 @@ export const codecForCoreBankConfig = (): 
Codec<TalerCorebankApi.Config> =>
 //FIXME: implement this codec
 export const codecForURN = codecForString;
 
+export const codecForExchangeConfigInfo =
+  (): Codec<TalerMerchantApi.ExchangeConfigInfo> =>
+    buildCodecForObject<TalerMerchantApi.ExchangeConfigInfo>()
+      .property("base_url", codecForString())
+      .property("currency", codecForString())
+      .property("master_pub", codecForString())
+      .build("TalerMerchantApi.ExchangeConfigInfo");
+
 export const codecForMerchantConfig =
   (): Codec<TalerMerchantApi.VersionResponse> =>
     buildCodecForObject<TalerMerchantApi.VersionResponse>()
@@ -300,8 +316,594 @@ export const codecForMerchantConfig =
       .property("currency", codecForString())
       .property("version", codecForString())
       .property("currencies", codecForMap(codecForCurrencySpecificiation()))
+      .property("exchanges", codecForList(codecForExchangeConfigInfo()))
       .build("TalerMerchantApi.VersionResponse");
 
+export const codecForClaimResponse =
+  (): Codec<TalerMerchantApi.ClaimResponse> =>
+    buildCodecForObject<TalerMerchantApi.ClaimResponse>()
+      .property("contract_terms", codecForContractTerms())
+      .property("sig", codecForString())
+      .build("TalerMerchantApi.ClaimResponse");
+
+export const codecForPaymentResponse =
+  (): Codec<TalerMerchantApi.PaymentResponse> =>
+    buildCodecForObject<TalerMerchantApi.PaymentResponse>()
+      .property("pos_confirmation", codecOptional(codecForString()))
+      .property("sig", codecForString())
+      .build("TalerMerchantApi.PaymentResponse");
+
+export const codecForStatusPaid = (): Codec<TalerMerchantApi.StatusPaid> =>
+  buildCodecForObject<TalerMerchantApi.StatusPaid>()
+    .property("refund_amount", codecForAmountString())
+    .property("refund_pending", codecForBoolean())
+    .property("refund_taken", codecForAmountString())
+    .property("refunded", codecForBoolean())
+    .property("type", codecForConstString("paid"))
+    .build("TalerMerchantApi.StatusPaid");
+
+export const codecForStatusGoto =
+  (): Codec<TalerMerchantApi.StatusGotoResponse> =>
+    buildCodecForObject<TalerMerchantApi.StatusGotoResponse>()
+      .property("public_reorder_url", codecForURL())
+      .property("type", codecForConstString("goto"))
+      .build("TalerMerchantApi.StatusGotoResponse");
+
+export const codecForStatusStatusUnpaid =
+  (): Codec<TalerMerchantApi.StatusUnpaidResponse> =>
+    buildCodecForObject<TalerMerchantApi.StatusUnpaidResponse>()
+      .property("type", codecForConstString("unpaid"))
+      .property("already_paid_order_id", codecOptional(codecForString()))
+      .property("fulfillment_url", codecOptional(codecForString()))
+      .property("taler_pay_uri", codecForTalerUriString())
+      .build("TalerMerchantApi.PaymentResponse");
+
+export const codecForPaidRefundStatusResponse =
+  (): Codec<TalerMerchantApi.PaidRefundStatusResponse> =>
+    buildCodecForObject<TalerMerchantApi.PaidRefundStatusResponse>()
+      .property("pos_confirmation", codecOptional(codecForString()))
+      .property("refunded", codecForBoolean())
+      .build("TalerMerchantApi.PaidRefundStatusResponse");
+
+export const codecForMerchantAbortPayRefundSuccessStatus =
+  (): Codec<TalerMerchantApi.MerchantAbortPayRefundSuccessStatus> =>
+    buildCodecForObject<TalerMerchantApi.MerchantAbortPayRefundSuccessStatus>()
+      .property("exchange_pub", codecForString())
+      .property("exchange_sig", codecForString())
+      .property("exchange_status", codecForConstNumber(200))
+      .property("type", codecForConstString("success"))
+      .build("TalerMerchantApi.MerchantAbortPayRefundSuccessStatus");
+
+export const codecForMerchantAbortPayRefundFailureStatus =
+  (): Codec<TalerMerchantApi.MerchantAbortPayRefundFailureStatus> =>
+    buildCodecForObject<TalerMerchantApi.MerchantAbortPayRefundFailureStatus>()
+      .property("exchange_code", codecForNumber())
+      .property("exchange_reply", codecForAny())
+      .property("exchange_status", codecForNumber())
+      .property("type", codecForConstString("failure"))
+      .build("TalerMerchantApi.MerchantAbortPayRefundFailureStatus");
+
+export const codecForMerchantAbortPayRefundStatus =
+  (): Codec<TalerMerchantApi.MerchantAbortPayRefundStatus> =>
+    buildCodecForUnion<TalerMerchantApi.MerchantAbortPayRefundStatus>()
+      .discriminateOn("type")
+      .alternative("success", codecForMerchantAbortPayRefundSuccessStatus())
+      .alternative("failure", codecForMerchantAbortPayRefundFailureStatus())
+      .build("TalerMerchantApi.MerchantAbortPayRefundStatus");
+
+export const codecForAbortResponse =
+  (): Codec<TalerMerchantApi.AbortResponse> =>
+    buildCodecForObject<TalerMerchantApi.AbortResponse>()
+      .property("refunds", 
codecForList(codecForMerchantAbortPayRefundStatus()))
+      .build("TalerMerchantApi.AbortResponse");
+
+export const codecForWalletRefundResponse =
+  (): Codec<TalerMerchantApi.WalletRefundResponse> =>
+    buildCodecForObject<TalerMerchantApi.WalletRefundResponse>()
+      .property("merchant_pub", codecForString())
+      .property("refund_amount", codecForAmountString())
+      .property("refunds", codecForList(codecForMerchantCoinRefundStatus()))
+      .build("TalerMerchantApi.AbortResponse");
+
+export const codecForMerchantCoinRefundSuccessStatus =
+  (): Codec<TalerMerchantApi.MerchantCoinRefundSuccessStatus> =>
+    buildCodecForObject<TalerMerchantApi.MerchantCoinRefundSuccessStatus>()
+      .property("type", codecForConstString("success"))
+      .property("coin_pub", codecForString())
+      .property("exchange_status", codecForConstNumber(200))
+      .property("exchange_sig", codecForString())
+      .property("rtransaction_id", codecForNumber())
+      .property("refund_amount", codecForAmountString())
+      .property("exchange_pub", codecForString())
+      .property("execution_time", codecForTimestamp)
+      .build("TalerMerchantApi.MerchantCoinRefundSuccessStatus");
+
+export const codecForMerchantCoinRefundFailureStatus =
+  (): Codec<TalerMerchantApi.MerchantCoinRefundFailureStatus> =>
+    buildCodecForObject<TalerMerchantApi.MerchantCoinRefundFailureStatus>()
+      .property("type", codecForConstString("failure"))
+      .property("coin_pub", codecForString())
+      .property("exchange_status", codecForNumber())
+      .property("rtransaction_id", codecForNumber())
+      .property("refund_amount", codecForAmountString())
+      .property("exchange_code", codecOptional(codecForNumber()))
+      .property("exchange_reply", codecOptional(codecForAny()))
+      .property("execution_time", codecForTimestamp)
+      .build("TalerMerchantApi.MerchantCoinRefundFailureStatus");
+
+export const codecForMerchantCoinRefundStatus =
+  (): Codec<TalerMerchantApi.MerchantCoinRefundStatus> =>
+    buildCodecForUnion<TalerMerchantApi.MerchantCoinRefundStatus>()
+      .discriminateOn("type")
+      .alternative("success", codecForMerchantCoinRefundSuccessStatus())
+      .alternative("failure", codecForMerchantCoinRefundFailureStatus())
+      .build("TalerMerchantApi.MerchantCoinRefundStatus");
+
+export const codecForQueryInstancesResponse =
+  (): Codec<TalerMerchantApi.QueryInstancesResponse> =>
+    buildCodecForObject<TalerMerchantApi.QueryInstancesResponse>()
+      .property("name", codecForString())
+      .property("user_type", codecForString())
+      .property("email", codecOptional(codecForString()))
+      .property("website", codecOptional(codecForString()))
+      .property("logo", codecOptional(codecForString()))
+      .property("merchant_pub", codecForString())
+      .property("address", codecForLocation())
+      .property("jurisdiction", codecForLocation())
+      .property("use_stefan", codecForBoolean())
+      .property("default_wire_transfer_delay", codecForDuration)
+      .property("default_pay_delay", codecForDuration)
+      .property(
+        "auth",
+        buildCodecForObject<{
+          type: "external" | "token";
+        }>()
+          .property(
+            "type",
+            codecForEither(
+              codecForConstString("token"),
+              codecForConstString("external"),
+            ),
+          )
+          .build("TalerMerchantApi.QueryInstancesResponse.auth"),
+      )
+      .build("TalerMerchantApi.QueryInstancesResponse");
+
+export const codecForAccountKycRedirects =
+  (): Codec<TalerMerchantApi.AccountKycRedirects> =>
+    buildCodecForObject<TalerMerchantApi.AccountKycRedirects>()
+      .property(
+        "pending_kycs",
+        codecForList(codecForMerchantAccountKycRedirect()),
+      )
+      .property("timeout_kycs", codecForList(codecForExchangeKycTimeout()))
+
+      .build("TalerMerchantApi.AccountKycRedirects");
+
+export const codecForMerchantAccountKycRedirect =
+  (): Codec<TalerMerchantApi.MerchantAccountKycRedirect> =>
+    buildCodecForObject<TalerMerchantApi.MerchantAccountKycRedirect>()
+      .property("kyc_url", codecForURL())
+      .property("aml_status", codecForNumber())
+      .property("exchange_url", codecForURL())
+      .property("payto_uri", codecForPaytoString())
+      .build("TalerMerchantApi.MerchantAccountKycRedirect");
+
+export const codecForExchangeKycTimeout =
+  (): Codec<TalerMerchantApi.ExchangeKycTimeout> =>
+    buildCodecForObject<TalerMerchantApi.ExchangeKycTimeout>()
+      .property("exchange_url", codecForURL())
+      .property("exchange_code", codecForNumber())
+      .property("exchange_http_status", codecForNumber())
+      .build("TalerMerchantApi.ExchangeKycTimeout");
+
+export const codecForAccountAddResponse =
+  (): Codec<TalerMerchantApi.AccountAddResponse> =>
+    buildCodecForObject<TalerMerchantApi.AccountAddResponse>()
+      .property("h_wire", codecForString())
+      .property("salt", codecForString())
+      .build("TalerMerchantApi.AccountAddResponse");
+
+export const codecForAccountsSummaryResponse =
+  (): Codec<TalerMerchantApi.AccountsSummaryResponse> =>
+    buildCodecForObject<TalerMerchantApi.AccountsSummaryResponse>()
+      .property("accounts", codecForList(codecForBankAccountEntry()))
+      .build("TalerMerchantApi.AccountsSummaryResponse");
+
+export const codecForBankAccountEntry =
+  (): Codec<TalerMerchantApi.BankAccountEntry> =>
+    buildCodecForObject<TalerMerchantApi.BankAccountEntry>()
+      .property("payto_uri", codecForPaytoString())
+      .property("h_wire", codecForString())
+      .property("salt", codecForString())
+      .property("credit_facade_url", codecForURL())
+      .property("active", codecForBoolean())
+      .build("TalerMerchantApi.BankAccountEntry");
+
+export const codecForInventorySummaryResponse =
+  (): Codec<TalerMerchantApi.InventorySummaryResponse> =>
+    buildCodecForObject<TalerMerchantApi.InventorySummaryResponse>()
+      .property("products", codecForList(codecForInventoryEntry()))
+      .build("TalerMerchantApi.InventorySummaryResponse");
+
+export const codecForInventoryEntry =
+  (): Codec<TalerMerchantApi.InventoryEntry> =>
+    buildCodecForObject<TalerMerchantApi.InventoryEntry>()
+      .property("product_id", codecForString())
+      .property("product_serial", codecForNumber())
+      .build("TalerMerchantApi.InventoryEntry");
+
+export const codecForProductDetail =
+  (): Codec<TalerMerchantApi.ProductDetail> =>
+    buildCodecForObject<TalerMerchantApi.ProductDetail>()
+      .property("description", codecForString())
+      .property("description_i18n", codecForInternationalizedString())
+      .property("unit", codecForString())
+      .property("price", codecForAmountString())
+      .property("image", codecForString())
+      .property("taxes", codecForList(codecForTax()))
+      .property("address", codecForLocation())
+      .property("next_restock", codecForTimestamp)
+      .property("total_stock", codecForNumber())
+      .property("total_sold", codecForNumber())
+      .property("total_lost", codecForNumber())
+      .property("minimum_age", codecOptional(codecForNumber()))
+      .build("TalerMerchantApi.ProductDetail");
+
+export const codecForTax = (): Codec<TalerMerchantApi.Tax> =>
+  buildCodecForObject<TalerMerchantApi.Tax>()
+    .property("name", codecForString())
+    .property("tax", codecForAmountString())
+    .build("TalerMerchantApi.Tax");
+
+export const codecForPostOrderResponse =
+  (): Codec<TalerMerchantApi.PostOrderResponse> =>
+    buildCodecForObject<TalerMerchantApi.PostOrderResponse>()
+      .property("order_id", codecForString())
+      .property("token", codecOptional(codecForString()))
+      .build("TalerMerchantApi.PostOrderResponse");
+
+export const codecForOutOfStockResponse =
+  (): Codec<TalerMerchantApi.OutOfStockResponse> =>
+    buildCodecForObject<TalerMerchantApi.OutOfStockResponse>()
+      .property("product_id", codecForString())
+      .property("available_quantity", codecForNumber())
+      .property("requested_quantity", codecForNumber())
+      .property("restock_expected", codecForTimestamp)
+      .build("TalerMerchantApi.OutOfStockResponse");
+
+export const codecForOrderHistory =
+  (): Codec<TalerMerchantApi.OrderHistory> =>
+    buildCodecForObject<TalerMerchantApi.OrderHistory>()
+      .property("orders", codecForList(codecForOrderHistoryEntry()))
+      .build("TalerMerchantApi.OrderHistory");
+
+export const codecForOrderHistoryEntry =
+  (): Codec<TalerMerchantApi.OrderHistoryEntry> =>
+    buildCodecForObject<TalerMerchantApi.OrderHistoryEntry>()
+      .property("order_id", codecForString())
+      .property("row_id", codecForNumber())
+      .property("timestamp", codecForTimestamp)
+      .property("amount", codecForAmountString())
+      .property("summary", codecForString())
+      .property("refundable", codecForBoolean())
+      .property("paid", codecForBoolean())
+      .build("TalerMerchantApi.OrderHistoryEntry");
+
+
+export const codecForMerchant = (): Codec<TalerMerchantApi.Merchant> =>
+  buildCodecForObject<TalerMerchantApi.Merchant>()
+    .property("name", codecForString())
+    .property("email", codecOptional(codecForString()))
+    .property("logo", codecOptional(codecForString()))
+    .property("website", codecOptional(codecForString()))
+    .property("address", codecOptional(codecForLocation()))
+    .property("jurisdiction", codecOptional(codecForLocation()))
+    .build("TalerMerchantApi.MerchantInfo");
+
+export const codecForExchange = (): Codec<TalerMerchantApi.Exchange> =>
+  buildCodecForObject<TalerMerchantApi.Exchange>()
+    .property("master_pub", codecForString())
+    .property("priority", codecForNumber())
+    .property("url", codecForString())
+    .build("TalerMerchantApi.Exchange");
+
+export const codecForContractTerms = (): Codec<TalerMerchantApi.ContractTerms> 
=>
+  buildCodecForObject<TalerMerchantApi.ContractTerms>()
+    .property("order_id", codecForString())
+    .property("fulfillment_url", codecOptional(codecForString()))
+    .property("fulfillment_message", codecOptional(codecForString()))
+    .property(
+      "fulfillment_message_i18n",
+      codecOptional(codecForInternationalizedString()),
+    )
+    .property("merchant_base_url", codecForString())
+    .property("h_wire", codecForString())
+    .property("auto_refund", codecOptional(codecForDuration))
+    .property("wire_method", codecForString())
+    .property("summary", codecForString())
+    .property("summary_i18n", codecOptional(codecForInternationalizedString()))
+    .property("nonce", codecForString())
+    .property("amount", codecForAmountString())
+    .property("pay_deadline", codecForTimestamp)
+    .property("refund_deadline", codecForTimestamp)
+    .property("wire_transfer_deadline", codecForTimestamp)
+    .property("timestamp", codecForTimestamp)
+    .property("delivery_location", codecOptional(codecForLocation()))
+    .property("delivery_date", codecOptional(codecForTimestamp))
+    .property("max_fee", codecForAmountString())
+    .property("merchant", codecForMerchant())
+    .property("merchant_pub", codecForString())
+    .property("exchanges", codecForList(codecForExchange()))
+    .property("products", codecForList(codecForProduct()))
+    .property("extra", codecForAny())
+    .build("TalerMerchantApi.ContractTerms");
+
+export const codecForProduct = (): Codec<TalerMerchantApi.Product> =>
+  buildCodecForObject<TalerMerchantApi.Product>()
+    .property("product_id", codecOptional(codecForString()))
+    .property("description", codecForString())
+    .property(
+      "description_i18n",
+      codecOptional(codecForInternationalizedString()),
+    )
+    .property("quantity", codecOptional(codecForNumber()))
+    .property("unit", codecOptional(codecForString()))
+    .property("price", codecOptional(codecForAmountString()))
+    .property("image", codecOptional(codecForString()))
+    .property("taxes", codecOptional(codecForList(codecForTax())))
+    .property("delivery_date", codecOptional(codecForTimestamp))
+    .build("TalerMerchantApi.Product");
+
+
+
+export const codecForCheckPaymentPaidResponse =
+  (): Codec<TalerMerchantApi.CheckPaymentPaidResponse> =>
+    buildCodecForObject<TalerMerchantApi.CheckPaymentPaidResponse>()
+      .property("order_status", codecForConstString("paid"))
+      .property("refunded", codecForBoolean())
+      .property("refund_pending", codecForBoolean())
+      .property("wired", codecForBoolean())
+      .property("deposit_total", codecForAmountString())
+      .property("exchange_code", codecForNumber())
+      .property("exchange_http_status", codecForNumber())
+      .property("refund_amount", codecForAmountString())
+      .property("contract_terms", codecForContractTerms())
+      .property("wire_reports", codecForList(codecForTransactionWireReport()))
+      .property("wire_details", 
codecForList(codecForTransactionWireTransfer()))
+      .property("refund_details", codecForList(codecForRefundDetails()))
+      .property("order_status_url", codecForURL())
+      .build("TalerMerchantApi.CheckPaymentPaidResponse");
+
+export const codecForCheckPaymentUnpaidResponse =
+  (): Codec<TalerMerchantApi.CheckPaymentUnpaidResponse> =>
+    buildCodecForObject<TalerMerchantApi.CheckPaymentUnpaidResponse>()
+      .property("order_status", codecForConstString("unpaid"))
+      .property("taler_pay_uri", codecForTalerUriString())
+      .property("creation_time", codecForTimestamp)
+      .property("summary", codecForString())
+      .property("total_amount", codecForAmountString())
+      .property("already_paid_order_id", codecOptional(codecForString()))
+      .property("already_paid_fulfillment_url", 
codecOptional(codecForString()))
+      .property("order_status_url", codecForString())
+      .build("TalerMerchantApi.CheckPaymentPaidResponse");
+
+export const codecForCheckPaymentClaimedResponse =
+  (): Codec<TalerMerchantApi.CheckPaymentClaimedResponse> =>
+    buildCodecForObject<TalerMerchantApi.CheckPaymentClaimedResponse>()
+      .property("order_status", codecForConstString("claimed"))
+      .property("contract_terms", codecForContractTerms())
+      .build("TalerMerchantApi.CheckPaymentClaimedResponse");
+
+export const codecForMerchantOrderPrivateStatusResponse =
+  (): Codec<TalerMerchantApi.MerchantOrderStatusResponse> =>
+    buildCodecForUnion<TalerMerchantApi.MerchantOrderStatusResponse>()
+      .discriminateOn("order_status")
+      .alternative("paid", codecForCheckPaymentPaidResponse())
+      .alternative("unpaid", codecForCheckPaymentUnpaidResponse())
+      .alternative("claimed", codecForCheckPaymentClaimedResponse())
+      .build("TalerMerchantApi.MerchantOrderStatusResponse");
+
+
+
+export const codecForRefundDetails =
+  (): Codec<TalerMerchantApi.RefundDetails> =>
+    buildCodecForObject<TalerMerchantApi.RefundDetails>()
+      .property("reason", codecForString())
+      .property("pending", codecForBoolean())
+      .property("timestamp", codecForTimestamp)
+      .property("amount", codecForAmountString())
+      .build("TalerMerchantApi.RefundDetails");
+
+export const codecForTransactionWireTransfer =
+  (): Codec<TalerMerchantApi.TransactionWireTransfer> =>
+    buildCodecForObject<TalerMerchantApi.TransactionWireTransfer>()
+      .property("exchange_url", codecForURL())
+      .property("wtid", codecForString())
+      .property("execution_time", codecForTimestamp)
+      .property("amount", codecForAmountString())
+      .property("confirmed", codecForBoolean())
+      .build("TalerMerchantApi.TransactionWireTransfer");
+
+export const codecForTransactionWireReport =
+  (): Codec<TalerMerchantApi.TransactionWireReport> =>
+    buildCodecForObject<TalerMerchantApi.TransactionWireReport>()
+      .property("code", codecForNumber())
+      .property("hint", codecForString())
+      .property("exchange_code", codecForNumber())
+      .property("exchange_http_status", codecForNumber())
+      .property("coin_pub", codecForString())
+      .build("TalerMerchantApi.TransactionWireReport");
+
+export const codecForMerchantRefundResponse =
+  (): Codec<TalerMerchantApi.MerchantRefundResponse> =>
+    buildCodecForObject<TalerMerchantApi.MerchantRefundResponse>()
+      .property("taler_refund_uri", codecForTalerUriString())
+      .property("h_contract", codecForString())
+      .build("TalerMerchantApi.MerchantRefundResponse");
+
+export const codecForTansferList =
+  (): Codec<TalerMerchantApi.TransferList> =>
+    buildCodecForObject<TalerMerchantApi.TransferList>()
+      .property("transfers", codecForList(codecForTransferDetails()))
+      .build("TalerMerchantApi.TransferList");
+
+export const codecForTransferDetails =
+  (): Codec<TalerMerchantApi.TransferDetails> =>
+    buildCodecForObject<TalerMerchantApi.TransferDetails>()
+      .property("credit_amount", codecForAmountString())
+      .property("wtid", codecForString())
+      .property("payto_uri", codecForPaytoString())
+      .property("exchange_url", codecForURL())
+      .property("transfer_serial_id", codecForNumber())
+      .property("execution_time", codecForTimestamp)
+      .property("verified", codecOptional(codecForBoolean()))
+      .property("confirmed", codecOptional(codecForBoolean()))
+      .build("TalerMerchantApi.TransferDetails");
+
+export const codecForOtpDeviceSummaryResponse =
+  (): Codec<TalerMerchantApi.OtpDeviceSummaryResponse> =>
+    buildCodecForObject<TalerMerchantApi.OtpDeviceSummaryResponse>()
+      .property("otp_devices", codecForList(codecForOtpDeviceEntry()))
+      .build("TalerMerchantApi.OtpDeviceSummaryResponse");
+
+export const codecForOtpDeviceEntry =
+  (): Codec<TalerMerchantApi.OtpDeviceEntry> =>
+    buildCodecForObject<TalerMerchantApi.OtpDeviceEntry>()
+      .property("otp_device_id", codecForString())
+      .property("device_description", codecForString())
+      .build("TalerMerchantApi.OtpDeviceEntry");
+
+export const codecForOtpDeviceDetails =
+  (): Codec<TalerMerchantApi.OtpDeviceDetails> =>
+    buildCodecForObject<TalerMerchantApi.OtpDeviceDetails>()
+      .property("device_description", codecForString())
+      .property("otp_algorithm", codecForNumber())
+      .property("otp_ctr", codecOptional(codecForNumber()))
+      .property("otp_timestamp", codecForNumber())
+      .property("otp_code", codecOptional(codecForString()))
+      .build("TalerMerchantApi.OtpDeviceDetails");
+
+
+export const codecForTemplateSummaryResponse =
+  (): Codec<TalerMerchantApi.TemplateSummaryResponse> =>
+    buildCodecForObject<TalerMerchantApi.TemplateSummaryResponse>()
+      .property("templates_list", codecForList(codecForTemplateEntry()))
+      .build("TalerMerchantApi.TemplateSummaryResponse");
+
+export const codecForTemplateEntry =
+  (): Codec<TalerMerchantApi.TemplateEntry> =>
+    buildCodecForObject<TalerMerchantApi.TemplateEntry>()
+      .property("template_id", codecForString())
+      .property("template_description", codecForString())
+      .build("TalerMerchantApi.TemplateEntry");
+
+export const codecForTemplateDetails =
+  (): Codec<TalerMerchantApi.TemplateDetails> =>
+    buildCodecForObject<TalerMerchantApi.TemplateDetails>()
+      .property("template_description", codecForString())
+      .property("otp_id", codecOptional(codecForString()))
+      .property("template_contract", codecForTemplateContractDetails())
+      .build("TalerMerchantApi.TemplateDetails");
+
+export const codecForTemplateContractDetails =
+  (): Codec<TalerMerchantApi.TemplateContractDetails> =>
+    buildCodecForObject<TalerMerchantApi.TemplateContractDetails>()
+      .property("summary", codecOptional(codecForString()))
+      .property("currency", codecOptional(codecForString()))
+      .property("amount", codecOptional(codecForAmountString()))
+      .property("minimum_age", codecForNumber())
+      .property("pay_duration", codecForDuration)
+      .build("TalerMerchantApi.TemplateContractDetails");
+
+export const codecForWalletTemplateDetails =
+  (): Codec<TalerMerchantApi.WalletTemplateDetails> =>
+    buildCodecForObject<TalerMerchantApi.WalletTemplateDetails>()
+      .property("template_contract", codecForTemplateContractDetails())
+      .build("TalerMerchantApi.WalletTemplateDetails");
+
+
+export const codecForWebhookSummaryResponse =
+  (): Codec<TalerMerchantApi.WebhookSummaryResponse> =>
+    buildCodecForObject<TalerMerchantApi.WebhookSummaryResponse>()
+      .property("webhooks", codecForList(codecForWebhookEntry()))
+      .build("TalerMerchantApi.WebhookSummaryResponse");
+
+export const codecForWebhookEntry =
+  (): Codec<TalerMerchantApi.WebhookEntry> =>
+    buildCodecForObject<TalerMerchantApi.WebhookEntry>()
+      .property("webhook_id", codecForString())
+      .property("event_type", codecForString())
+      .build("TalerMerchantApi.WebhookEntry");
+
+export const codecForWebhookDetails =
+  (): Codec<TalerMerchantApi.WebhookDetails> =>
+    buildCodecForObject<TalerMerchantApi.WebhookDetails>()
+      .property("event_type", codecForString())
+      .property("url", codecForString())
+      .property("http_method", codecForString())
+      .property("header_template", codecOptional(codecForString()))
+      .property("body_template", codecOptional(codecForString()))
+      .build("TalerMerchantApi.WebhookDetails");
+
+export const codecForTokenFamilyKind =
+  (): Codec<TalerMerchantApi.TokenFamilyKind> =>
+    codecForEither(
+      codecForConstString("discount"),
+      codecForConstString("subscription"),
+    ) as any //FIXME: create a codecForEnum
+  ;
+export const codecForTokenFamilyDetails =
+  (): Codec<TalerMerchantApi.TokenFamilyDetails> =>
+    buildCodecForObject<TalerMerchantApi.TokenFamilyDetails>()
+      .property("slug", codecForString())
+      .property("name", codecForString())
+      .property("description", codecForString())
+      .property("description_i18n", (codecForInternationalizedString()))
+      .property("valid_after", (codecForTimestamp))
+      .property("valid_before", (codecForTimestamp))
+      .property("duration", (codecForDuration))
+      .property("kind", codecForTokenFamilyKind())
+      .property("issued", (codecForNumber()))
+      .property("redeemed", (codecForNumber()))
+      .build("TalerMerchantApi.TokenFamilyDetails");
+
+export const codecForTokenFamiliesList =
+  (): Codec<TalerMerchantApi.TokenFamiliesList> =>
+    buildCodecForObject<TalerMerchantApi.TokenFamiliesList>()
+      .property("token_families", codecForList(codecForTokenFamilySummary()))
+      .build("TalerMerchantApi.TokenFamiliesList");
+
+export const codecForTokenFamilySummary =
+  (): Codec<TalerMerchantApi.TokenFamilySummary> =>
+    buildCodecForObject<TalerMerchantApi.TokenFamilySummary>()
+      .property("slug", codecForString())
+      .property("name", codecForString())
+      .property("valid_after", codecForTimestamp)
+      .property("valid_before", codecForTimestamp)
+      .property("kind", codecForTokenFamilyKind())
+      .build("TalerMerchantApi.TokenFamilySummary");
+
+
+export const codecForInstancesResponse =
+  (): Codec<TalerMerchantApi.InstancesResponse> =>
+    buildCodecForObject<TalerMerchantApi.InstancesResponse>()
+      .property("instances", codecForList(codecForInstance()))
+      .build("TalerMerchantApi.InstancesResponse");
+
+export const codecForInstance =
+  (): Codec<TalerMerchantApi.Instance> =>
+    buildCodecForObject<TalerMerchantApi.Instance>()
+      .property("name", codecForString())
+      .property("user_type", codecForString())
+      .property("website", codecOptional(codecForString()))
+      .property("logo", codecOptional(codecForString()))
+      .property("id", codecForString())
+      .property("merchant_pub", codecForString())
+      .property("payment_targets", codecForList(codecForString()))
+      .property("deleted", codecForBoolean())
+      .build("TalerMerchantApi.Instance");
+
 export const codecForExchangeConfig =
   (): Codec<TalerExchangeApi.ExchangeVersionResponse> =>
     buildCodecForObject<TalerExchangeApi.ExchangeVersionResponse>()
@@ -458,7 +1060,7 @@ export const codecForRegisterAccountResponse =
 export const codecForBankAccountCreateWithdrawalResponse =
   (): Codec<TalerCorebankApi.BankAccountCreateWithdrawalResponse> =>
     buildCodecForObject<TalerCorebankApi.BankAccountCreateWithdrawalResponse>()
-      .property("taler_withdraw_uri", codecForTalerActionString())
+      .property("taler_withdraw_uri", codecForTalerUriString())
       .property("withdrawal_id", codecForString())
       .build("TalerCorebankApi.BankAccountCreateWithdrawalResponse");
 
@@ -1377,7 +1979,7 @@ export namespace TalerCorebankApi {
     withdrawal_id: string;
 
     // URI that can be passed to the wallet to initiate the withdrawal.
-    taler_withdraw_uri: TalerActionString;
+    taler_withdraw_uri: TalerUriString;
   }
   export interface WithdrawalPublicInfo {
     // Current status of the operation
@@ -2403,6 +3005,10 @@ export namespace TalerMerchantApi {
     // Name of the protocol.
     name: "taler-merchant";
 
+    // URN of the implementation (needed to interpret 'revision' in version).
+    // @since **v8**, may become mandatory in the future.
+    implementation?: string;
+
     // Default (!) currency supported by this backend.
     // This is the currency that the backend should
     // suggest by default to the user when entering
@@ -2410,15 +3016,39 @@ export namespace TalerMerchantApi {
     // supported currencies and how to render them.
     currency: string;
 
-    // How wallets should render currencies supported
+    // How services should render currencies supported
     // by this backend.  Maps
     // currency codes (e.g. "EUR" or "KUDOS") to
     // the respective currency specification.
     // All currencies in this map are supported by
-    // the backend.
+    // the backend.  Note that the actual currency
+    // specifications are a *hint* for applications
+    // that would like *advice* on how to render amounts.
+    // Applications *may* ignore the currency specification
+    // if they know how to render currencies that they are
+    // used with.
     currencies: { [currency: string]: CurrencySpecification };
+
+    // Array of exchanges trusted by the merchant.
+    // Since protocol **v6**.
+    exchanges: ExchangeConfigInfo[];
   }
 
+  export interface ExchangeConfigInfo {
+    // Base URL of the exchange REST API.
+    base_url: string;
+
+    // Currency for which the merchant is configured
+    // to trust the exchange.
+    // May not be the one the exchange actually uses,
+    // but is the only one we would trust this exchange for.
+    currency: string;
+
+    // Offline master public key of the exchange. The
+    // /keys data must be signed with this public
+    // key for us to trust it.
+    master_pub: EddsaPublicKey;
+  }
   export interface ClaimRequest {
     // Nonce to identify the wallet that claimed the order.
     nonce: string;
@@ -2447,7 +3077,131 @@ export namespace TalerMerchantApi {
     pos_confirmation?: string;
   }
 
-  interface PayRequest {
+  export interface PaymentStatusRequestParams {
+    // Hash of the order’s contract terms (this is used to
+    // authenticate the wallet/customer in case
+    // $ORDER_ID is guessable).
+    // Required once an order was claimed.
+    contractTermHash?: string;
+    // Authorizes the request via the claim token that
+    // was returned in the PostOrderResponse. Used with
+    // unclaimed orders only. Whether token authorization is
+    // required is determined by the merchant when the
+    // frontend creates the order.
+    claimToken?: string;
+    // Session ID that the payment must be bound to.
+    // If not specified, the payment is not session-bound.
+    sessionId?: string;
+    // If specified, the merchant backend will wait up to
+    // timeout_ms milliseconds for completion of the payment
+    // before sending the HTTP response. A client must never
+    // rely on this behavior, as the merchant backend may return
+    // a response immediately.
+    timeout?: number;
+    // If set to “yes”, poll for the order’s pending refunds
+    // to be picked up. timeout_ms specifies how long we
+    // will wait for the refund.
+    awaitRefundObtained?: boolean;
+    // Indicates that we are polling for a refund above the
+    // given AMOUNT. timeout_ms will specify how long we
+    // will wait for the refund.
+    refund?: AmountString;
+    // Since protocol v9 refunded orders are only returned
+    // under “already_paid_order_id” if this flag is set
+    // explicitly to “YES”.
+    allowRefundedForRepurchase?: boolean;
+  }
+  export interface GetKycStatusRequestParams {
+    // If specified, the KYC check should return
+    // the KYC status only for this wire account.
+    // Otherwise, for all wire accounts.
+    wireHash?: string;
+    //  If specified, the KYC check should return
+    // the KYC status only for the given exchange.
+    // Otherwise, for all exchanges we interacted with.
+    exchangeURL?: string;
+    //  If specified, the merchant will wait up to
+    // timeout_ms milliseconds for the exchanges to
+    // confirm completion of the KYC process(es).
+    timeout?: number;
+  }
+  export interface GetOtpDeviceRequestParams {
+    // Timestamp in seconds to use when calculating
+    // the current OTP code of the device. Since protocol v10.
+    faketime?: number;
+    // Price to use when calculating the current OTP
+    // code of the device. Since protocol v10.
+    price?: AmountString;
+  }
+  export interface GetOrderRequestParams {
+    // Session ID that the payment must be bound to.
+    // If not specified, the payment is not session-bound.
+    sessionId?: string;
+    // Timeout in milliseconds to wait for a payment if
+    // the answer would otherwise be negative (long polling).
+    timeout?: number;
+    // Since protocol v9 refunded orders are only returned
+    // under “already_paid_order_id” if this flag is set
+    // explicitly to “YES”.
+    allowRefundedForRepurchase?: boolean;
+  }
+  export interface ListWireTransferRequestParams {
+    // Filter for transfers to the given bank account
+    // (subject and amount MUST NOT be given in the payto URI).
+    paytoURI?: string;
+    // Filter for transfers executed before the given timestamp.
+    before?: number;
+    // Filter for transfers executed after the given timestamp.
+    after?: number;
+    // At most return the given number of results. Negative for
+    // descending in execution time, positive for ascending in
+    // execution time. Default is -20.
+    limit?: number;
+    // Starting transfer_serial_id for an iteration.
+    offset?: string;
+    // Filter transfers by verification status.
+    verified?: boolean;
+    order?: "asc" | "dec";
+  }
+  export interface ListOrdersRequestParams {
+    // If set to yes, only return paid orders, if no only
+    // unpaid orders. Do not give (or use “all”) to see all
+    // orders regardless of payment status.
+    paid?: boolean;
+    // If set to yes, only return refunded orders, if no only
+    // unrefunded orders. Do not give (or use “all”) to see
+    // all orders regardless of refund status.
+    refunded?: boolean;
+    // If set to yes, only return wired orders, if no only
+    // orders with missing wire transfers. Do not give (or
+    // use “all”) to see all orders regardless of wire transfer
+    // status.
+    wired?: boolean;
+    // At most return the given number of results. Negative 
+    // for descending by row ID, positive for ascending by 
+    // row ID. Default is 20. Since protocol v12.
+    limit?: number;
+    // Non-negative date in seconds after the UNIX Epoc, see delta
+    // for its interpretation. If not specified, we default to the
+    // oldest or most recent entry, depending on delta.
+    date?: AbsoluteTime;
+    // Starting product_serial_id for an iteration. 
+    // Since protocol v12.
+    offset?: string;
+    // Timeout in milliseconds to wait for additional orders if the
+    // answer would otherwise be negative (long polling). Only useful
+    // if delta is positive. Note that the merchant MAY still return
+    // a response that contains fewer than delta orders.
+    timeout?: number;
+    // Since protocol v6. Filters by session ID.
+    sessionId?: string;
+    // Since protocol v6. Filters by fulfillment URL.
+    fulfillmentUrl?: string;
+
+    order?: "asc" | "dec";
+  }
+
+  export interface PayRequest {
     // The coins used to make the payment.
     coins: CoinPaySig[];
 
@@ -2478,7 +3232,9 @@ export namespace TalerMerchantApi {
     exchange_url: string;
   }
 
-  interface StatusPaid {
+  export interface StatusPaid {
+    type: "paid";
+
     // Was the payment refunded (even partially, via refund or abort)?
     refunded: boolean;
 
@@ -2491,14 +3247,16 @@ export namespace TalerMerchantApi {
     // Amount that already taken by the wallet.
     refund_taken: AmountString;
   }
-  interface StatusGotoResponse {
+  export interface StatusGotoResponse {
+    type: "goto";
     // The client should go to the reorder URL, there a fresh
     // order might be created as this one is taken by another
     // customer or wallet (or repurchase detection logic may
     // apply).
     public_reorder_url: string;
   }
-  interface StatusUnpaidResponse {
+  export interface StatusUnpaidResponse {
+    type: "unpaid";
     // URI that the wallet must process to complete the payment.
     taler_pay_uri: string;
 
@@ -2511,7 +3269,7 @@ export namespace TalerMerchantApi {
     already_paid_order_id?: string;
   }
 
-  interface PaidRefundStatusResponse {
+  export interface PaidRefundStatusResponse {
     // Text to be shown to the point-of-sale staff as a proof of
     // payment (present only if reusable OTP algorithm is used).
     pos_confirmation?: string;
@@ -2520,7 +3278,7 @@ export namespace TalerMerchantApi {
     // refunds. False if it was simply paid.
     refunded: boolean;
   }
-  interface PaidRequest {
+  export interface PaidRequest {
     // Signature on TALER_PaymentResponsePS with the public
     // key of the merchant instance.
     sig: EddsaSignature;
@@ -2537,7 +3295,7 @@ export namespace TalerMerchantApi {
     session_id: string;
   }
 
-  interface AbortRequest {
+  export interface AbortRequest {
     // Hash of the order's contract terms (this is used to authenticate the
     // wallet/customer in case $ORDER_ID is guessable).
     h_contract: HashCode;
@@ -2557,18 +3315,18 @@ export namespace TalerMerchantApi {
     // URL of the exchange this coin was withdrawn from.
     exchange_url: string;
   }
-  interface AbortResponse {
+  export interface AbortResponse {
     // List of refund responses about the coins that the wallet
     // requested an abort for.  In the same order as the coins
     // from the original request.
     // The rtransaction_id is implied to be 0.
     refunds: MerchantAbortPayRefundStatus[];
   }
-  type MerchantAbortPayRefundStatus =
+  export type MerchantAbortPayRefundStatus =
     | MerchantAbortPayRefundSuccessStatus
     | MerchantAbortPayRefundFailureStatus;
   // Details about why a refund failed.
-  interface MerchantAbortPayRefundFailureStatus {
+  export interface MerchantAbortPayRefundFailureStatus {
     // Used as tag for the sum type RefundStatus sum type.
     type: "failure";
 
@@ -2584,7 +3342,7 @@ export namespace TalerMerchantApi {
   // Additional details needed to verify the refund confirmation signature
   // (h_contract_terms and merchant_pub) are already known
   // to the wallet and thus not included.
-  interface MerchantAbortPayRefundSuccessStatus {
+  export interface MerchantAbortPayRefundSuccessStatus {
     // Used as tag for the sum type MerchantCoinRefundStatus sum type.
     type: "success";
 
@@ -2603,12 +3361,12 @@ export namespace TalerMerchantApi {
     exchange_pub: EddsaPublicKey;
   }
 
-  interface WalletRefundRequest {
+  export interface WalletRefundRequest {
     // Hash of the order's contract terms (this is used to authenticate the
     // wallet/customer).
     h_contract: HashCode;
   }
-  interface WalletRefundResponse {
+  export interface WalletRefundResponse {
     // Amount that was refunded in total.
     refund_amount: AmountString;
 
@@ -2618,11 +3376,11 @@ export namespace TalerMerchantApi {
     // Public key of the merchant.
     merchant_pub: EddsaPublicKey;
   }
-  type MerchantCoinRefundStatus =
+  export type MerchantCoinRefundStatus =
     | MerchantCoinRefundSuccessStatus
     | MerchantCoinRefundFailureStatus;
   // Details about why a refund failed.
-  interface MerchantCoinRefundFailureStatus {
+  export interface MerchantCoinRefundFailureStatus {
     // Used as tag for the sum type RefundStatus sum type.
     type: "failure";
 
@@ -2652,7 +3410,7 @@ export namespace TalerMerchantApi {
   // Additional details needed to verify the refund confirmation signature
   // (h_contract_terms and merchant_pub) are already known
   // to the wallet and thus not included.
-  interface MerchantCoinRefundSuccessStatus {
+  export interface MerchantCoinRefundSuccessStatus {
     // Used as tag for the sum type MerchantCoinRefundStatus sum type.
     type: "success";
 
@@ -2723,7 +3481,7 @@ export namespace TalerMerchantApi {
     blind_sig: BlindedRsaSignature;
   }
 
-  interface InstanceConfigurationMessage {
+  export interface InstanceConfigurationMessage {
     // Name of the merchant instance to create (will become $INSTANCE).
     // Must match the regex ^[A-Za-z0-9][A-Za-z0-9_.@-]+$.
     id: string;
@@ -2771,7 +3529,7 @@ export namespace TalerMerchantApi {
     default_pay_delay: RelativeTime;
   }
 
-  interface InstanceAuthConfigurationMessage {
+  export interface InstanceAuthConfigurationMessage {
     // Type of authentication.
     // "external":  The mechant backend does not do
     //   any authentication checks.  Instead an API
@@ -2788,37 +3546,7 @@ export namespace TalerMerchantApi {
     token?: string;
   }
 
-  interface LoginTokenRequest {
-    // Scope of the token (which kinds of operations it will allow)
-    scope: "readonly" | "write";
-
-    // Server may impose its own upper bound
-    // on the token validity duration
-    duration?: RelativeTime;
-
-    // Can this token be refreshed?
-    // Defaults to false.
-    refreshable?: boolean;
-  }
-  interface LoginTokenSuccessResponse {
-    // The login token that can be used to access resources
-    // that are in scope for some time. Must be prefixed
-    // with "Bearer " when used in the "Authorization" HTTP header.
-    // Will already begin with the RFC 8959 prefix.
-    token: string;
-
-    // Scope of the token (which kinds of operations it will allow)
-    scope: "readonly" | "write";
-
-    // Server may impose its own upper bound
-    // on the token validity duration
-    expiration: Timestamp;
-
-    // Can this token be refreshed?
-    refreshable: boolean;
-  }
-
-  interface InstanceReconfigurationMessage {
+  export interface InstanceReconfigurationMessage {
     // Merchant name corresponding to this instance.
     name: string;
 
@@ -2859,12 +3587,12 @@ export namespace TalerMerchantApi {
     default_pay_delay: RelativeTime;
   }
 
-  interface InstancesResponse {
+  export interface InstancesResponse {
     // List of instances that are present in the backend (see Instance).
     instances: Instance[];
   }
 
-  interface Instance {
+  export interface Instance {
     // Merchant name corresponding to this instance.
     name: string;
 
@@ -2892,7 +3620,7 @@ export namespace TalerMerchantApi {
     deleted: boolean;
   }
 
-  interface QueryInstancesResponse {
+  export interface QueryInstancesResponse {
     // Merchant name corresponding to this instance.
     name: string;
 
@@ -2940,7 +3668,7 @@ export namespace TalerMerchantApi {
     };
   }
 
-  interface AccountKycRedirects {
+  export interface AccountKycRedirects {
     // Array of pending KYCs.
     pending_kycs: MerchantAccountKycRedirect[];
 
@@ -2948,7 +3676,7 @@ export namespace TalerMerchantApi {
     timeout_kycs: ExchangeKycTimeout[];
   }
 
-  interface MerchantAccountKycRedirect {
+  export interface MerchantAccountKycRedirect {
     // URL that the user should open in a browser to
     // proceed with the KYC process (as returned
     // by the exchange's /kyc-check/ endpoint).
@@ -2966,7 +3694,7 @@ export namespace TalerMerchantApi {
     payto_uri: PaytoString;
   }
 
-  interface ExchangeKycTimeout {
+  export interface ExchangeKycTimeout {
     // Base URL of the exchange this is about.
     exchange_url: string;
 
@@ -2980,7 +3708,7 @@ export namespace TalerMerchantApi {
     exchange_http_status: number;
   }
 
-  interface AccountAddDetails {
+  export interface AccountAddDetails {
     // payto:// URI of the account.
     payto_uri: PaytoString;
 
@@ -2996,11 +3724,13 @@ export namespace TalerMerchantApi {
     credit_facade_credentials?: FacadeCredentials;
   }
 
-  type FacadeCredentials = NoFacadeCredentials | BasicAuthFacadeCredentials;
-  interface NoFacadeCredentials {
+  export type FacadeCredentials =
+    | NoFacadeCredentials
+    | BasicAuthFacadeCredentials;
+  export interface NoFacadeCredentials {
     type: "none";
   }
-  interface BasicAuthFacadeCredentials {
+  export interface BasicAuthFacadeCredentials {
     type: "basic";
 
     // Username to use to authenticate
@@ -3009,7 +3739,7 @@ export namespace TalerMerchantApi {
     // Password to use to authenticate
     password: string;
   }
-  interface AccountAddResponse {
+  export interface AccountAddResponse {
     // Hash over the wire details (including over the salt).
     h_wire: HashCode;
 
@@ -3017,7 +3747,7 @@ export namespace TalerMerchantApi {
     salt: HashCode;
   }
 
-  interface AccountPatchDetails {
+  export interface AccountPatchDetails {
     // URL from where the merchant can download information
     // about incoming wire transfers to this account.
     credit_facade_url?: string;
@@ -3032,11 +3762,11 @@ export namespace TalerMerchantApi {
     credit_facade_credentials?: FacadeCredentials;
   }
 
-  interface AccountsSummaryResponse {
+  export interface AccountsSummaryResponse {
     // List of accounts that are known for the instance.
     accounts: BankAccountEntry[];
   }
-  interface BankAccountEntry {
+  export interface BankAccountEntry {
     // payto:// URI of the account.
     payto_uri: PaytoString;
 
@@ -3055,7 +3785,7 @@ export namespace TalerMerchantApi {
     active: boolean;
   }
 
-  interface ProductAddDetail {
+  export interface ProductAddDetail {
     // Product ID to use.
     product_id: string;
 
@@ -3097,7 +3827,7 @@ export namespace TalerMerchantApi {
     minimum_age?: Integer;
   }
 
-  interface ProductPatchDetail {
+  export interface ProductPatchDetail {
     // Human-readable product description.
     description: string;
 
@@ -3139,17 +3869,19 @@ export namespace TalerMerchantApi {
     minimum_age?: Integer;
   }
 
-  interface InventorySummaryResponse {
+  export interface InventorySummaryResponse {
     // List of products that are present in the inventory.
     products: InventoryEntry[];
   }
 
-  interface InventoryEntry {
+  export interface InventoryEntry {
     // Product identifier, as found in the product.
     product_id: string;
+    // product_serial_id of the product in the database.
+    product_serial: Integer;
   }
 
-  interface ProductDetail {
+  export interface ProductDetail {
     // Human-readable product description.
     description: string;
 
@@ -3205,7 +3937,7 @@ export namespace TalerMerchantApi {
     quantity: Integer;
   }
 
-  interface PostOrderRequest {
+  export interface PostOrderRequest {
     // The order must at least contain the minimal
     // order detail, but can override all.
     order: Order;
@@ -3257,6 +3989,9 @@ export namespace TalerMerchantApi {
 
     // See documentation of fulfillment_url in ContractTerms.
     // Either fulfillment_url or fulfillment_message must be specified.
+    // When creating an order, the fulfillment URL can
+    // contain ${ORDER_ID} which will be substituted with the
+    // order ID of the newly created order.
     fulfillment_url?: string;
 
     // See documentation of fulfillment_message in ContractTerms.
@@ -3272,7 +4007,7 @@ export namespace TalerMerchantApi {
     quantity: Integer;
   }
 
-  interface PostOrderResponse {
+  export interface PostOrderResponse {
     // Order ID of the response that was just created.
     order_id: string;
 
@@ -3281,7 +4016,7 @@ export namespace TalerMerchantApi {
     // in the request.
     token?: ClaimToken;
   }
-  interface OutOfStockResponse {
+  export interface OutOfStockResponse {
     // Product ID of an out-of-stock item.
     product_id: string;
 
@@ -3296,12 +4031,12 @@ export namespace TalerMerchantApi {
     restock_expected?: Timestamp;
   }
 
-  interface OrderHistory {
+  export 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 {
+  export interface OrderHistoryEntry {
     // Order ID of the transaction related to this entry.
     order_id: string;
 
@@ -3327,11 +4062,11 @@ export namespace TalerMerchantApi {
     paid: boolean;
   }
 
-  type MerchantOrderStatusResponse =
+  export type MerchantOrderStatusResponse =
     | CheckPaymentPaidResponse
     | CheckPaymentClaimedResponse
     | CheckPaymentUnpaidResponse;
-  interface CheckPaymentPaidResponse {
+  export interface CheckPaymentPaidResponse {
     // The customer paid for this contract.
     order_status: "paid";
 
@@ -3382,14 +4117,14 @@ export namespace TalerMerchantApi {
     // to show the order QR code / trigger the wallet.
     order_status_url: string;
   }
-  interface CheckPaymentClaimedResponse {
+  export 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 {
+  export interface CheckPaymentUnpaidResponse {
     // The order was neither claimed nor paid.
     order_status: "unpaid";
 
@@ -3420,7 +4155,7 @@ export namespace TalerMerchantApi {
     // 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 {
+  export interface RefundDetails {
     // Reason given for the refund.
     reason: string;
 
@@ -3433,7 +4168,7 @@ export namespace TalerMerchantApi {
     // Total amount that was refunded (minus a refund fee).
     amount: AmountString;
   }
-  interface TransactionWireTransfer {
+  export interface TransactionWireTransfer {
     // Responsible exchange.
     exchange_url: string;
 
@@ -3451,7 +4186,7 @@ export namespace TalerMerchantApi {
     // POST /transfers API, or is it merely claimed by the exchange?
     confirmed: boolean;
   }
-  interface TransactionWireReport {
+  export interface TransactionWireReport {
     // Numerical error code.
     code: number;
 
@@ -3468,20 +4203,20 @@ export namespace TalerMerchantApi {
     coin_pub: CoinPublicKey;
   }
 
-  interface ForgetRequest {
+  export interface ForgetRequest {
     // Array of valid JSON paths to forgettable fields in the order's
     // contract terms.
     fields: string[];
   }
 
-  interface RefundRequest {
+  export interface RefundRequest {
     // Amount to be refunded.
     refund: AmountString;
 
     // Human-readable refund justification.
     reason: string;
   }
-  interface MerchantRefundResponse {
+  export interface MerchantRefundResponse {
     // URL (handled by the backend) that the wallet should access to
     // trigger refund processing.
     // taler://refund/...
@@ -3492,7 +4227,7 @@ export namespace TalerMerchantApi {
     h_contract: HashCode;
   }
 
-  interface TransferInformation {
+  export interface TransferInformation {
     // How much was wired to the merchant (minus fees).
     credit_amount: AmountString;
 
@@ -3506,11 +4241,11 @@ export namespace TalerMerchantApi {
     exchange_url: string;
   }
 
-  interface TransferList {
+  export interface TransferList {
     // List of all the transfers that fit the filter that we know.
     transfers: TransferDetails[];
   }
-  interface TransferDetails {
+  export interface TransferDetails {
     // How much was wired to the merchant (minus fees).
     credit_amount: AmountString;
 
@@ -3710,28 +4445,40 @@ export namespace TalerMerchantApi {
     reward_amount: AmountString;
   }
 
-  interface OtpDeviceAddDetails {
+  export interface OtpDeviceAddDetails {
     // Device ID to use.
     otp_device_id: string;
 
     // Human-readable description for the device.
     otp_device_description: string;
 
-    // A base64-encoded key
+    // A key encoded with RFC 3548 Base32.
+    // IMPORTANT: This is not using the typical
+    // Taler base32-crockford encoding.
+    // Instead it uses the RFC 3548 encoding to
+    // be compatible with the TOTP standard.
     otp_key: string;
 
     // Algorithm for computing the POS confirmation.
-    otp_algorithm: Integer;
+    // "NONE" or 0: No algorithm (no pos confirmation will be generated)
+    // "TOTP_WITHOUT_PRICE" or 1: Without amounts (typical OTP device)
+    // "TOTP_WITH_PRICE" or 2: With amounts (special-purpose OTP device)
+    // The "String" variants are supported @since protocol **v7**.
+    otp_algorithm: Integer | string;
 
     // Counter for counter-based OTP devices.
     otp_ctr?: Integer;
   }
 
-  interface OtpDevicePatchDetails {
+  export interface OtpDevicePatchDetails {
     // Human-readable description for the device.
     otp_device_description: string;
 
-    // A base64-encoded key
+    // A key encoded with RFC 3548 Base32.
+    // IMPORTANT: This is not using the typical
+    // Taler base32-crockford encoding.
+    // Instead it uses the RFC 3548 encoding to
+    // be compatible with the TOTP standard.
     otp_key: string;
 
     // Algorithm for computing the POS confirmation.
@@ -3741,11 +4488,11 @@ export namespace TalerMerchantApi {
     otp_ctr?: Integer;
   }
 
-  interface OtpDeviceSummaryResponse {
+  export interface OtpDeviceSummaryResponse {
     // Array of devices that are present in our backend.
     otp_devices: OtpDeviceEntry[];
   }
-  interface OtpDeviceEntry {
+  export interface OtpDeviceEntry {
     // Device identifier.
     otp_device_id: string;
 
@@ -3753,17 +4500,55 @@ export namespace TalerMerchantApi {
     device_description: string;
   }
 
-  interface OtpDeviceDetails {
+  export interface OtpDeviceDetails {
     // Human-readable description for the device.
     device_description: string;
 
     // Algorithm for computing the POS confirmation.
+    //
+    // Currently, the following numbers are defined:
+    // 0: None
+    // 1: TOTP without price
+    // 2: TOTP with price
     otp_algorithm: Integer;
 
     // Counter for counter-based OTP devices.
     otp_ctr?: Integer;
+
+    // Current time for time-based OTP devices.
+    // Will match the faketime argument of the
+    // query if one was present, otherwise the current
+    // time at the backend.
+    //
+    // Available since protocol **v10**.
+    otp_timestamp: Integer;
+
+    // Current OTP confirmation string of the device.
+    // Matches exactly the string that would be returned
+    // as part of a payment confirmation for the given
+    // amount and time (so may contain multiple OTP codes).
+    //
+    // If the otp_algorithm is time-based, the code is
+    // returned for the current time, or for the faketime
+    // if a TIMESTAMP query argument was provided by the client.
+    //
+    // When using OTP with counters, the counter is **NOT**
+    // increased merely because this endpoint created
+    // an OTP code (this is a GET request, after all!).
+    //
+    // If the otp_algorithm requires an amount, the
+    // amount argument must be specified in the
+    // query, otherwise the otp_code is not
+    // generated.
+    //
+    // This field is *optional* in the response, as it is
+    // only provided if we could compute it based on the
+    // otp_algorithm and matching client query arguments.
+    //
+    // Available since protocol **v10**.
+    otp_code?: string;
   }
-  interface TemplateAddDetails {
+  export interface TemplateAddDetails {
     // Template ID to use.
     template_id: string;
 
@@ -3777,7 +4562,7 @@ export namespace TalerMerchantApi {
     // Additional information in a separate template.
     template_contract: TemplateContractDetails;
   }
-  interface TemplateContractDetails {
+  export interface TemplateContractDetails {
     // Human-readable summary for the template.
     summary?: string;
 
@@ -3799,7 +4584,7 @@ export namespace TalerMerchantApi {
     // It is deleted if the customer did not pay and if the duration is over.
     pay_duration: RelativeTime;
   }
-  interface TemplatePatchDetails {
+  export interface TemplatePatchDetails {
     // Human-readable description for the template.
     template_description: string;
 
@@ -3811,19 +4596,27 @@ export namespace TalerMerchantApi {
     template_contract: TemplateContractDetails;
   }
 
-  interface TemplateSummaryResponse {
+  export interface TemplateSummaryResponse {
     // List of templates that are present in our backend.
     templates_list: TemplateEntry[];
   }
 
-  interface TemplateEntry {
+  export interface TemplateEntry {
     // Template identifier, as found in the template.
     template_id: string;
 
     // Human-readable description for the template.
     template_description: string;
   }
-  interface TemplateDetails {
+
+  export interface WalletTemplateDetails {
+
+    // Hard-coded information about the contrac terms
+    // for this template.
+    template_contract: TemplateContractDetails;
+  }
+
+  export interface TemplateDetails {
     // Human-readable description for the template.
     template_description: string;
 
@@ -3834,7 +4627,7 @@ export namespace TalerMerchantApi {
     // Additional information in a separate template.
     template_contract: TemplateContractDetails;
   }
-  interface UsingTemplateDetails {
+  export interface UsingTemplateDetails {
     // Summary of the template
     summary?: string;
 
@@ -3842,7 +4635,7 @@ export namespace TalerMerchantApi {
     amount?: AmountString;
   }
 
-  interface WebhookAddDetails {
+  export interface WebhookAddDetails {
     // Webhook ID to use.
     webhook_id: string;
 
@@ -3862,7 +4655,7 @@ export namespace TalerMerchantApi {
     body_template?: string;
   }
 
-  interface WebhookPatchDetails {
+  export interface WebhookPatchDetails {
     // The event of the webhook: why the webhook is used.
     event_type: string;
 
@@ -3879,12 +4672,12 @@ export namespace TalerMerchantApi {
     body_template?: string;
   }
 
-  interface WebhookSummaryResponse {
+  export interface WebhookSummaryResponse {
     // Return webhooks that are present in our backend.
     webhooks: WebhookEntry[];
   }
 
-  interface WebhookEntry {
+  export interface WebhookEntry {
     // Webhook identifier, as found in the webhook.
     webhook_id: string;
 
@@ -3892,7 +4685,7 @@ export namespace TalerMerchantApi {
     event_type: string;
   }
 
-  interface WebhookDetails {
+  export interface WebhookDetails {
     // The event of the webhook: why the webhook is used.
     event_type: string;
 
@@ -3909,7 +4702,115 @@ export namespace TalerMerchantApi {
     body_template?: string;
   }
 
-  interface ContractTerms {
+  export interface TokenFamilyCreateRequest {
+    // Identifier for the token family consisting of unreserved characters
+    // according to RFC 3986.
+    slug: string;
+
+    // Human-readable name for the token family.
+    name: string;
+
+    // Human-readable description for the token family.
+    description: string;
+
+    // Optional map from IETF BCP 47 language tags to localized descriptions.
+    description_i18n?: { [lang_tag: string]: string };
+
+    // Start time of the token family's validity period.
+    // If not specified, merchant backend will use the current time.
+    valid_after?: Timestamp;
+
+    // End time of the token family's validity period.
+    valid_before: Timestamp;
+
+    // Validity duration of an issued token.
+    duration: RelativeTime;
+
+    // Kind of the token family.
+    kind: TokenFamilyKind;
+  }
+
+  export enum TokenFamilyKind {
+    Discount = "discount",
+    Subscription = "subscription",
+  }
+
+  export interface TokenFamilyUpdateRequest {
+    // Human-readable name for the token family.
+    name: string;
+
+    // Human-readable description for the token family.
+    description: string;
+
+    // Optional map from IETF BCP 47 language tags to localized descriptions.
+    description_i18n: { [lang_tag: string]: string };
+
+    // Start time of the token family's validity period.
+    valid_after: Timestamp;
+
+    // End time of the token family's validity period.
+    valid_before: Timestamp;
+
+    // Validity duration of an issued token.
+    duration: RelativeTime;
+  }
+
+  export interface TokenFamiliesList {
+    // All configured token families of this instance.
+    token_families: TokenFamilySummary[];
+  }
+
+  export interface TokenFamilySummary {
+    // Identifier for the token family consisting of unreserved characters
+    // according to RFC 3986.
+    slug: string;
+
+    // Human-readable name for the token family.
+    name: string;
+
+    // Start time of the token family's validity period.
+    valid_after: Timestamp;
+
+    // End time of the token family's validity period.
+    valid_before: Timestamp;
+
+    // Kind of the token family.
+    kind: TokenFamilyKind;
+  }
+
+  export interface TokenFamilyDetails {
+    // Identifier for the token family consisting of unreserved characters
+    // according to RFC 3986.
+    slug: string;
+
+    // Human-readable name for the token family.
+    name: string;
+
+    // Human-readable description for the token family.
+    description: string;
+
+    // Optional map from IETF BCP 47 language tags to localized descriptions.
+    description_i18n?: { [lang_tag: string]: string };
+
+    // Start time of the token family's validity period.
+    valid_after: Timestamp;
+
+    // End time of the token family's validity period.
+    valid_before: Timestamp;
+
+    // Validity duration of an issued token.
+    duration: RelativeTime;
+
+    // Kind of the token family.
+    kind: TokenFamilyKind;
+
+    // How many tokens have been issued for this family.
+    issued: Integer;
+
+    // How many tokens have been redeemed for this family.
+    redeemed: Integer;
+  }
+  export interface ContractTerms {
     // Human-readable description of the whole purchase.
     summary: string;
 
@@ -4032,7 +4933,7 @@ export namespace TalerMerchantApi {
     extra?: any;
   }
 
-  interface Product {
+  export interface Product {
     // Merchant-internal identifier for the product.
     product_id?: string;
 
@@ -4061,14 +4962,14 @@ export namespace TalerMerchantApi {
     delivery_date?: Timestamp;
   }
 
-  interface Tax {
+  export interface Tax {
     // The name of the tax.
     name: string;
 
     // Amount paid in tax.
     tax: AmountString;
   }
-  interface Merchant {
+  export interface Merchant {
     // The merchant's legal name of business.
     name: string;
 
@@ -4132,7 +5033,7 @@ export namespace TalerMerchantApi {
     // Base URL of the auditor.
     url: string;
   }
-  interface Exchange {
+  export interface Exchange {
     // The exchange's base URL.
     url: string;
 
diff --git a/packages/taler-util/src/http-client/utils.ts 
b/packages/taler-util/src/http-client/utils.ts
index f925a5610..dbfe64796 100644
--- a/packages/taler-util/src/http-client/utils.ts
+++ b/packages/taler-util/src/http-client/utils.ts
@@ -59,6 +59,21 @@ export function addPaginationParams(url: URL, pagination?: 
PaginationParams) {
   url.searchParams.set("delta", String(order * limit));
 }
 
+export function addMerchantPaginationParams(url: URL, pagination?: 
PaginationParams) {
+  if (!pagination) return;
+  if (pagination.offset) {
+    url.searchParams.set("offset", pagination.offset);
+  }
+  const order = !pagination || pagination.order === "asc" ? 1 : -1;
+  const limit =
+    !pagination || !pagination.limit || pagination.limit === 0
+      ? 5
+      : Math.abs(pagination.limit);
+  //always send delta
+  url.searchParams.set("limit", String(order * limit));
+}
+
+
 export function addLongPollingParam(url: URL, param?: LongPollParams) {
   if (!param) return;
   if (param.timeoutMs) {
diff --git a/packages/taler-util/src/merchant-api-types.ts 
b/packages/taler-util/src/merchant-api-types.ts
index 838bfa99d..6d5570b61 100644
--- a/packages/taler-util/src/merchant-api-types.ts
+++ b/packages/taler-util/src/merchant-api-types.ts
@@ -40,6 +40,8 @@ import {
   codecForAmountString,
   codecForAny,
   codecForBoolean,
+  codecForCheckPaymentClaimedResponse,
+  codecForCheckPaymentUnpaidResponse,
   codecForConstString,
   codecForExchangeWireAccount,
   codecForList,
@@ -112,30 +114,6 @@ export const codecForMerchantCheckPaymentPaidResponse =
       .property("refund_details", codecForAny())
       .build("CheckPaymentPaidResponse");
 
-export const codecForCheckPaymentUnpaidResponse =
-  (): Codec<CheckPaymentUnpaidResponse> =>
-    buildCodecForObject<CheckPaymentUnpaidResponse>()
-      .property("order_status", codecForConstString("unpaid"))
-      .property("taler_pay_uri", codecForString())
-      .property("order_status_url", codecForString())
-      .property("already_paid_order_id", codecOptional(codecForString()))
-      .build("CheckPaymentPaidResponse");
-
-export const codecForCheckPaymentClaimedResponse =
-  (): Codec<CheckPaymentClaimedResponse> =>
-    buildCodecForObject<CheckPaymentClaimedResponse>()
-      .property("order_status", codecForConstString("claimed"))
-      .property("contract_terms", codecForMerchantContractTerms())
-      .build("CheckPaymentClaimedResponse");
-
-export const codecForMerchantOrderPrivateStatusResponse =
-  (): Codec<MerchantOrderPrivateStatusResponse> =>
-    buildCodecForUnion<MerchantOrderPrivateStatusResponse>()
-      .discriminateOn("order_status")
-      .alternative("paid", codecForMerchantCheckPaymentPaidResponse())
-      .alternative("unpaid", codecForCheckPaymentUnpaidResponse())
-      .alternative("claimed", codecForCheckPaymentClaimedResponse())
-      .build("MerchantOrderPrivateStatusResponse");
 
 export type MerchantOrderPrivateStatusResponse =
   | MerchantCheckPaymentPaidResponse
diff --git a/packages/taler-util/src/taler-types.ts 
b/packages/taler-util/src/taler-types.ts
index fe8f47e30..bad9186dc 100644
--- a/packages/taler-util/src/taler-types.ts
+++ b/packages/taler-util/src/taler-types.ts
@@ -46,6 +46,7 @@ import {
   CurrencySpecification,
   codecForCurrencySpecificiation,
   codecForEither,
+  codecForProduct,
 } from "./index.js";
 import { Edx25519PublicKeyEnc } from "./taler-crypto.js";
 import {
@@ -303,15 +304,11 @@ export interface CoinDepositPermission {
  * merchant's contract terms.
  */
 export interface ExchangeHandle {
-  /**
-   * Master public signing key of the exchange.
-   */
-  master_pub: string;
-
-  /**
-   * Base URL of the exchange.
-   */
+  // The exchange's base URL.
   url: string;
+
+  // Master public key of the exchange.
+  master_pub: EddsaPublicKeyString;
 }
 
 export interface AuditorHandle {
@@ -323,7 +320,7 @@ export interface AuditorHandle {
   /**
    * Master public signing key of the auditor.
    */
-  auditor_pub: string;
+  auditor_pub: EddsaPublicKeyString;
 
   /**
    * Base URL of the auditor.
@@ -367,12 +364,24 @@ export interface Location {
 }
 
 export interface MerchantInfo {
+  // The merchant's legal name of business.
   name: string;
-  jurisdiction?: Location;
-  address?: Location;
-  logo?: string;
-  website?: string;
+
+  // Label for a location with the business address of the merchant.
   email?: string;
+
+  // Label for a location with the business address of the merchant.
+  website?: string;
+
+  // An optional base64-encoded product image.
+  logo?: ImageDataUrl;
+
+  // Label for a location with the business address of the merchant.
+  address?: Location;
+
+  // 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;
 }
 
 export interface Tax {
@@ -391,10 +400,10 @@ export interface Product {
   description: string;
 
   // Map from IETF BCP 47 language tags to localized descriptions
-  description_i18n?: { [lang_tag: string]: string };
+  description_i18n?: InternationalizedString;
 
   // The number of units of the product to deliver to the customer.
-  quantity?: number;
+  quantity?: Integer;
 
   // The unit in which the product is measured (liters, kilograms, packages, 
etc.)
   unit?: string;
@@ -403,7 +412,7 @@ export interface Product {
   price?: AmountString;
 
   // An optional base64-encoded product image
-  image?: string;
+  image?: ImageDataUrl;
 
   // a list of taxes paid by the merchant for this product. Can be empty.
   taxes?: Tax[];
@@ -421,141 +430,139 @@ export interface InternationalizedString {
  * FIXME: Add type field!
  */
 export interface MerchantContractTerms {
-  /**
-   * Hash of the merchant's wire details.
-   */
+  // The hash of the merchant instance's wire details.
   h_wire: string;
 
-  /**
-   * Hash of the merchant's wire details.
-   */
+  // 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?: TalerProtocolDuration;
 
-  /**
-   * Wire method the merchant wants to use.
-   */
+  // 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;
 
-  /**
-   * Human-readable short summary of the contract.
-   */
+  // Human-readable description of the whole purchase.
   summary: string;
 
+  // Map from IETF BCP 47 language tags to localized summaries.
   summary_i18n?: InternationalizedString;
 
-  /**
-   * Nonce used to ensure freshness.
-   */
-  nonce: 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 amount payable.
-   */
+  // Total price for the transaction.
+  // The exchange will subtract deposit fees from that amount
+  // before transferring it to the merchant.
   amount: string;
 
-  /**
-   * Deadline to pay for the contract.
-   */
-  pay_deadline: TalerProtocolTimestamp;
+  // Nonce generated by the wallet and echoed by the merchant
+  // in this field when the proposal is generated.
+  nonce: string;
 
-  /**
-   * Maximum deposit fee covered by the merchant.
-   */
-  max_fee: string;
+  // After this deadline, the merchant won't accept payments for the contract.
+  pay_deadline: TalerProtocolTimestamp;
 
-  /**
-   * Information about the merchant.
-   */
+  // More info about the merchant, see below.
   merchant: MerchantInfo;
 
-  /**
-   * Public key of the merchant.
-   */
+  // 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: string;
 
-  /**
-   * Time indicating when the order should be delivered.
-   * May be overwritten by individual products.
-   */
+  // Time indicating when the order should be delivered.
+  // May be overwritten by individual products.
   delivery_date?: TalerProtocolTimestamp;
 
-  /**
-   * Delivery location for (all!) products.
-   */
+  // Delivery location for (all!) products.
   delivery_location?: Location;
 
-  /**
-   * List of accepted exchanges.
-   */
+  // Exchanges that the merchant accepts even if it does not accept any 
auditors that audit them.
   exchanges: ExchangeHandle[];
 
-  /**
-   * Products that are sold in this contract.
-   */
+  // List of products that are part of the purchase (see Product).
   products?: Product[];
 
-  /**
-   * Deadline for refunds.
-   */
+  // After this deadline has passed, no refunds will be accepted.
   refund_deadline: TalerProtocolTimestamp;
 
-  /**
-   * Deadline for the wire transfer.
-   */
+  // Transfer deadline for the exchange.  Must be in the
+  // deposit permissions of coins used to pay for this order.
   wire_transfer_deadline: TalerProtocolTimestamp;
 
-  /**
-   * Time when the contract was generated by the merchant.
-   */
+  // Time when this contract was generated.
   timestamp: TalerProtocolTimestamp;
 
-  /**
-   * Order id to uniquely identify the purchase within
-   * one merchant instance.
-   */
-  order_id: string;
-
-  /**
-   * Base URL of the merchant's backend.
-   */
+  // Base URL of the (public!) merchant backend API.
+  // Must be an absolute URL that ends with a slash.
   merchant_base_url: string;
 
-  /**
-   * Fulfillment URL to view the product or
-   * delivery status.
-   */
+  // URL that will show that the order was successful after
+  // it has been paid for.  Optional, but either fulfillment_url
+  // or fulfillment_message must be specified in every
+  // contract terms.
+  //
+  // If a non-unique fulfillment URL is used, a customer can only
+  // buy the order once and will be redirected to a previous purchase
+  // when trying to buy an order with the same fulfillment URL a second
+  // time. This is useful for digital goods that a customer only needs
+  // to buy once but should be able to repeatedly download.
+  //
+  // For orders where the customer is expected to be able to make
+  // repeated purchases (for equivalent goods), the fulfillment URL
+  // should be made unique for every order. The easiest way to do
+  // this is to include a unique order ID in the fulfillment URL.
+  //
+  // When POSTing to the merchant, the placeholder text "${ORDER_ID}"
+  // is be replaced with the actual order ID (useful if the
+  // order ID is generated server-side and needs to be
+  // in the URL). Note that this placeholder can only be used once.
+  // Front-ends may use other means to generate a unique fulfillment URL.
   fulfillment_url?: string;
 
-  /**
-   * URL meant to share the shopping cart.
-   */
+  // URL where the same contract could be ordered again (if
+  // available). Returned also at the public order endpoint
+  // for people other than the actual buyer (hence public,
+  // in case order IDs are guessable).
   public_reorder_url?: string;
 
-  /**
-   * Plain text fulfillment message in the merchant's default language.
-   */
+  // Message shown to the customer after paying for the order.
+  // Either fulfillment_url or fulfillment_message must be specified.
   fulfillment_message?: string;
 
-  /**
-   * Internationalized fulfillment messages.
-   */
+  // Map from IETF BCP 47 language tags to localized fulfillment
+  // messages.
   fulfillment_message_i18n?: InternationalizedString;
 
-  /**
-   * Share of the wire fee that must be settled with one payment.
-   */
-  wire_fee_amortization?: number;
-
-  /**
-   * Maximum wire fee that the merchant agrees to pay for.
-   */
-  max_wire_fee?: string;
-
-  minimum_age?: number;
+  // Maximum total deposit fee accepted by the merchant for this contract.
+  // Overrides defaults of the merchant instance.
+  max_fee: string;
 
-  /**
-   * Extra data, interpreted by the mechant only.
-   */
+  // 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.
+  // Must really be an Object (not a string, integer, float or array).
   extra?: any;
 }
 
@@ -610,26 +617,6 @@ export interface MerchantAbortPayRefundDetails {
   exchange_http_status: number;
 }
 
-/**
- * Response for a refund pickup or a /pay in abort mode.
- */
-export interface MerchantRefundResponse {
-  /**
-   * Public key of the merchant
-   */
-  merchant_pub: string;
-
-  /**
-   * Contract terms hash of the contract that
-   * is being refunded.
-   */
-  h_contract_terms: string;
-
-  /**
-   * The signed refund permissions, to be sent to the exchange.
-   */
-  refunds: MerchantAbortPayRefundDetails[];
-}
 
 /**
  * Planchet detail sent to the merchant.
@@ -831,6 +818,7 @@ export interface DenomCommon {
 
 export type RsaPublicKeySring = string;
 export type AgeMask = number;
+export type ImageDataUrl = string;
 
 /**
  * 32-byte value representing a point on Curve25519.
@@ -1406,27 +1394,10 @@ export const codecForMerchantInfo = (): 
Codec<MerchantInfo> =>
     .property("jurisdiction", codecOptional(codecForLocation()))
     .build("MerchantInfo");
 
-export const codecForTax = (): Codec<Tax> =>
-  buildCodecForObject<Tax>()
-    .property("name", codecForString())
-    .property("tax", codecForAmountString())
-    .build("Tax");
 
 export const codecForInternationalizedString =
   (): Codec<InternationalizedString> => codecForMap(codecForString());
 
-export const codecForProduct = (): Codec<Product> =>
-  buildCodecForObject<Product>()
-    .property("product_id", codecOptional(codecForString()))
-    .property("description", codecForString())
-    .property(
-      "description_i18n",
-      codecOptional(codecForInternationalizedString()),
-    )
-    .property("quantity", codecOptional(codecForNumber()))
-    .property("unit", codecOptional(codecForString()))
-    .property("price", codecOptional(codecForAmountString()))
-    .build("Tax");
 
 export const codecForMerchantContractTerms = (): Codec<MerchantContractTerms> 
=>
   buildCodecForObject<MerchantContractTerms>()
@@ -1444,21 +1415,19 @@ export const codecForMerchantContractTerms = (): 
Codec<MerchantContractTerms> =>
     .property("summary", codecForString())
     .property("summary_i18n", codecOptional(codecForInternationalizedString()))
     .property("nonce", codecForString())
-    .property("amount", codecForString())
+    .property("amount", codecForAmountString())
     .property("pay_deadline", codecForTimestamp)
     .property("refund_deadline", codecForTimestamp)
     .property("wire_transfer_deadline", codecForTimestamp)
     .property("timestamp", codecForTimestamp)
     .property("delivery_location", codecOptional(codecForLocation()))
     .property("delivery_date", codecOptional(codecForTimestamp))
-    .property("max_fee", codecForString())
-    .property("max_wire_fee", codecOptional(codecForString()))
+    .property("max_fee", codecForAmountString())
     .property("merchant", codecForMerchantInfo())
     .property("merchant_pub", codecForString())
     .property("exchanges", codecForList(codecForExchangeHandle()))
     .property("products", codecOptional(codecForList(codecForProduct())))
     .property("extra", codecForAny())
-    .property("minimum_age", codecOptional(codecForNumber()))
     .build("MerchantContractTerms");
 
 export const codecForPeerContractTerms = (): Codec<PeerContractTerms> =>
@@ -1482,14 +1451,6 @@ export const codecForMerchantRefundPermission =
       .property("exchange_pub", codecOptional(codecForString()))
       .build("MerchantRefundPermission");
 
-export const codecForMerchantRefundResponse =
-  (): Codec<MerchantRefundResponse> =>
-    buildCodecForObject<MerchantRefundResponse>()
-      .property("merchant_pub", codecForString())
-      .property("h_contract_terms", codecForString())
-      .property("refunds", codecForList(codecForMerchantRefundPermission()))
-      .build("MerchantRefundResponse");
-
 export const codecForBlindSigWrapperV2 = (): Codec<MerchantBlindSigWrapperV2> 
=>
   buildCodecForObject<MerchantBlindSigWrapperV2>()
     .property("blind_sig", codecForBlindedDenominationSignature())
@@ -1645,40 +1606,6 @@ export const codecForExchangeRevealResponse =
       .property("ev_sigs", codecForList(codecForExchangeRevealItem()))
       .build("ExchangeRevealResponse");
 
-export const codecForMerchantCoinRefundSuccessStatus =
-  (): Codec<MerchantCoinRefundSuccessStatus> =>
-    buildCodecForObject<MerchantCoinRefundSuccessStatus>()
-      .property("type", codecForConstString("success"))
-      .property("coin_pub", codecForString())
-      .property("exchange_status", codecForConstNumber(200))
-      .property("exchange_sig", codecForString())
-      .property("rtransaction_id", codecForNumber())
-      .property("refund_amount", codecForAmountString())
-      .property("exchange_pub", codecForString())
-      .property("execution_time", codecForTimestamp)
-      .build("MerchantCoinRefundSuccessStatus");
-
-export const codecForMerchantCoinRefundFailureStatus =
-  (): Codec<MerchantCoinRefundFailureStatus> =>
-    buildCodecForObject<MerchantCoinRefundFailureStatus>()
-      .property("type", codecForConstString("failure"))
-      .property("coin_pub", codecForString())
-      .property("exchange_status", codecForNumber())
-      .property("rtransaction_id", codecForNumber())
-      .property("refund_amount", codecForAmountString())
-      .property("exchange_code", codecOptional(codecForNumber()))
-      .property("exchange_reply", codecOptional(codecForAny()))
-      .property("execution_time", codecForTimestamp)
-      .build("MerchantCoinRefundFailureStatus");
-
-export const codecForMerchantCoinRefundStatus =
-  (): Codec<MerchantCoinRefundStatus> =>
-    buildCodecForUnion<MerchantCoinRefundStatus>()
-      .discriminateOn("type")
-      .alternative("success", codecForMerchantCoinRefundSuccessStatus())
-      .alternative("failure", codecForMerchantCoinRefundFailureStatus())
-      .build("MerchantCoinRefundStatus");
-
 export const codecForMerchantOrderStatusPaid =
   (): Codec<MerchantOrderStatusPaid> =>
     buildCodecForObject<MerchantOrderStatusPaid>()
@@ -1688,13 +1615,6 @@ export const codecForMerchantOrderStatusPaid =
       .property("refunded", codecForBoolean())
       .build("MerchantOrderStatusPaid");
 
-export const codecForMerchantOrderRefundPickupResponse =
-  (): Codec<MerchantOrderRefundResponse> =>
-    buildCodecForObject<MerchantOrderRefundResponse>()
-      .property("merchant_pub", codecForString())
-      .property("refund_amount", codecForAmountString())
-      .property("refunds", codecForList(codecForMerchantCoinRefundStatus()))
-      .build("MerchantOrderRefundPickupResponse");
 
 export const codecForMerchantOrderStatusUnpaid =
   (): Codec<MerchantOrderStatusUnpaid> =>
@@ -1733,36 +1653,6 @@ export interface AbortResponse {
   refunds: MerchantAbortPayRefundStatus[];
 }
 
-export const codecForMerchantAbortPayRefundSuccessStatus =
-  (): Codec<MerchantAbortPayRefundSuccessStatus> =>
-    buildCodecForObject<MerchantAbortPayRefundSuccessStatus>()
-      .property("exchange_pub", codecForString())
-      .property("exchange_sig", codecForString())
-      .property("exchange_status", codecForConstNumber(200))
-      .property("type", codecForConstString("success"))
-      .build("MerchantAbortPayRefundSuccessStatus");
-
-export const codecForMerchantAbortPayRefundFailureStatus =
-  (): Codec<MerchantAbortPayRefundFailureStatus> =>
-    buildCodecForObject<MerchantAbortPayRefundFailureStatus>()
-      .property("exchange_code", codecForNumber())
-      .property("exchange_reply", codecForAny())
-      .property("exchange_status", codecForNumber())
-      .property("type", codecForConstString("failure"))
-      .build("MerchantAbortPayRefundFailureStatus");
-
-export const codecForMerchantAbortPayRefundStatus =
-  (): Codec<MerchantAbortPayRefundStatus> =>
-    buildCodecForUnion<MerchantAbortPayRefundStatus>()
-      .discriminateOn("type")
-      .alternative("success", codecForMerchantAbortPayRefundSuccessStatus())
-      .alternative("failure", codecForMerchantAbortPayRefundFailureStatus())
-      .build("MerchantAbortPayRefundStatus");
-
-export const codecForAbortResponse = (): Codec<AbortResponse> =>
-  buildCodecForObject<AbortResponse>()
-    .property("refunds", codecForList(codecForMerchantAbortPayRefundStatus()))
-    .build("AbortResponse");
 
 export type MerchantAbortPayRefundStatus =
   | MerchantAbortPayRefundSuccessStatus
diff --git a/packages/taler-util/src/taleruri.ts 
b/packages/taler-util/src/taleruri.ts
index 4a8647d21..97b82c061 100644
--- a/packages/taler-util/src/taleruri.ts
+++ b/packages/taler-util/src/taleruri.ts
@@ -43,11 +43,11 @@ export type TalerUri =
   | WithdrawExchangeUri;
 
 declare const __action_str: unique symbol;
-export type TalerActionString = string & { [__action_str]: true };
+export type TalerUriString = string & { [__action_str]: true };
 
-export function codecForTalerActionString(): Codec<TalerActionString> {
+export function codecForTalerUriString(): Codec<TalerUriString> {
   return {
-    decode(x: any, c?: Context): TalerActionString {
+    decode(x: any, c?: Context): TalerUriString {
       if (typeof x !== "string") {
         throw new DecodingError(
           `expected string at ${renderContext(c)} but got ${typeof x}`,
@@ -55,10 +55,10 @@ export function codecForTalerActionString(): 
Codec<TalerActionString> {
       }
       if (parseTalerUri(x) === undefined) {
         throw new DecodingError(
-          `invalid taler action at ${renderContext(c)} but got "${x}"`,
+          `invalid taler URI at ${renderContext(c)} but got "${x}"`,
         );
       }
-      return x as TalerActionString;
+      return x as TalerUriString;
     },
   };
 }
diff --git a/packages/taler-util/src/wallet-types.ts 
b/packages/taler-util/src/wallet-types.ts
index 4ebaa3668..61b9e1b59 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -628,11 +628,11 @@ export interface CoinDumpJson {
     withdrawal_reserve_pub: string | undefined;
     coin_status: CoinStatus;
     spend_allocation:
-      | {
-          id: string;
-          amount: AmountString;
-        }
-      | undefined;
+    | {
+      id: string;
+      amount: AmountString;
+    }
+    | undefined;
     /**
      * Information about the age restriction
      */
@@ -831,7 +831,7 @@ export const codecForPreparePayResultPaymentPossible =
       )
       .build("PreparePayResultPaymentPossible");
 
-export interface BalanceDetails {}
+export interface BalanceDetails { }
 
 /**
  * Detailed reason for why the wallet's balance is insufficient.
@@ -2947,7 +2947,6 @@ export interface WalletContractData {
   summary: string;
   summaryI18n: { [lang_tag: string]: string } | undefined;
   autoRefund: TalerProtocolDuration | undefined;
-  wireFeeAmortization: number;
   payDeadline: TalerProtocolTimestamp;
   refundDeadline: TalerProtocolTimestamp;
   allowedExchanges: AllowedExchangeInfo[];
@@ -2955,7 +2954,6 @@ export interface WalletContractData {
   wireMethod: string;
   wireInfoHash: string;
   maxDepositFee: AmountString;
-  minimumAge?: number;
 }
 
 export interface TestingWaitTransactionRequest {
diff --git a/packages/taler-wallet-core/src/deposits.ts 
b/packages/taler-wallet-core/src/deposits.ts
index 93f70c2ce..6c04b20de 100644
--- a/packages/taler-wallet-core/src/deposits.ts
+++ b/packages/taler-wallet-core/src/deposits.ts
@@ -33,6 +33,7 @@ import {
   DepositGroupFees,
   Duration,
   ExchangeBatchDepositRequest,
+  ExchangeHandle,
   ExchangeRefundRequest,
   HttpStatusCode,
   Logger,
@@ -754,9 +755,9 @@ async function processDepositGroupPendingTrack(
     let updatedTxStatus: DepositElementStatus | undefined = undefined;
     let newWiredCoin:
       | {
-          id: string;
-          value: DepositTrackingInfo;
-        }
+        id: string;
+        value: DepositTrackingInfo;
+      }
       | undefined;
 
     if (depositGroup.statusPerCoin[i] !== DepositElementStatus.Wired) {
@@ -1167,7 +1168,7 @@ export async function prepareDepositGroup(
   }
   const amount = Amounts.parseOrThrow(req.amount);
 
-  const exchangeInfos: { url: string; master_pub: string }[] = [];
+  const exchangeInfos: ExchangeHandle[] = [];
 
   await wex.db.runReadOnlyTx(["exchangeDetails", "exchanges"], async (tx) => {
     const allExchanges = await tx.exchanges.iter().toArray();
@@ -1189,7 +1190,6 @@ export async function prepareDepositGroup(
     exchanges: exchangeInfos,
     amount: req.amount,
     max_fee: Amounts.stringify(amount),
-    max_wire_fee: Amounts.stringify(amount),
     wire_method: p.targetType,
     timestamp: nowRounded,
     merchant_base_url: "",
@@ -1226,7 +1226,7 @@ export async function prepareDepositGroup(
     restrictWireMethod: contractData.wireMethod,
     contractTermsAmount: Amounts.parseOrThrow(contractData.amount),
     depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee),
-    wireFeeAmortization: contractData.wireFeeAmortization ?? 1,
+    wireFeeAmortization: 1, // FIXME #8653
     prevPayCoins: [],
   });
 
@@ -1309,7 +1309,6 @@ export async function createDepositGroup(
     exchanges: exchangeInfos,
     amount: req.amount,
     max_fee: Amounts.stringify(amount),
-    max_wire_fee: Amounts.stringify(amount),
     wire_method: p.targetType,
     timestamp: nowRounded,
     merchant_base_url: "",
@@ -1346,7 +1345,7 @@ export async function createDepositGroup(
     restrictWireMethod: contractData.wireMethod,
     contractTermsAmount: Amounts.parseOrThrow(contractData.amount),
     depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee),
-    wireFeeAmortization: contractData.wireFeeAmortization ?? 1,
+    wireFeeAmortization: 1, // FIXME #8653
     prevPayCoins: [],
   });
 
diff --git a/packages/taler-wallet-core/src/pay-merchant.ts 
b/packages/taler-wallet-core/src/pay-merchant.ts
index 872e554c9..e473566b0 100644
--- a/packages/taler-wallet-core/src/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/pay-merchant.ts
@@ -36,11 +36,11 @@ import {
   checkDbInvariant,
   codecForAbortResponse,
   codecForMerchantContractTerms,
-  codecForMerchantOrderRefundPickupResponse,
   codecForMerchantOrderStatusPaid,
   codecForMerchantPayResponse,
   codecForMerchantPostOrderResponse,
   codecForProposal,
+  codecForWalletRefundResponse,
   CoinDepositPermission,
   CoinRefreshRequest,
   ConfirmPayResult,
@@ -580,7 +580,6 @@ export function extractContractData(
     autoRefund: parsedContractTerms.auto_refund,
     payDeadline: parsedContractTerms.pay_deadline,
     refundDeadline: parsedContractTerms.refund_deadline,
-    wireFeeAmortization: parsedContractTerms.wire_fee_amortization || 1,
     allowedExchanges: parsedContractTerms.exchanges.map((x) => ({
       exchangeBaseUrl: x.url,
       exchangePub: x.master_pub,
@@ -591,7 +590,6 @@ export function extractContractData(
     maxDepositFee: Amounts.stringify(parsedContractTerms.max_fee),
     merchant: parsedContractTerms.merchant,
     summaryI18n: parsedContractTerms.summary_i18n,
-    minimumAge: parsedContractTerms.minimum_age,
   };
 }
 
@@ -611,8 +609,7 @@ async function processDownloadProposal(
 
   if (proposal.purchaseStatus != PurchaseStatus.PendingDownloadingProposal) {
     logger.error(
-      `unexpected state ${proposal.purchaseStatus}/${
-        PurchaseStatus[proposal.purchaseStatus]
+      `unexpected state 
${proposal.purchaseStatus}/${PurchaseStatus[proposal.purchaseStatus]
       } for ${ctx.transactionId} in processDownloadProposal`,
     );
     return TaskRunResult.finished();
@@ -868,8 +865,7 @@ async function createOrReusePurchase(
     oldProposal.claimToken === claimToken
   ) {
     logger.info(
-      `Found old proposal (status=${
-        PurchaseStatus[oldProposal.purchaseStatus]
+      `Found old proposal (status=${PurchaseStatus[oldProposal.purchaseStatus]
       }) for order ${orderId} at ${merchantBaseUrl}`,
     );
     if (oldProposal.purchaseStatus === PurchaseStatus.DialogShared) {
@@ -1149,9 +1145,9 @@ async function handleInsufficientFunds(
     restrictWireMethod: contractData.wireMethod,
     contractTermsAmount: Amounts.parseOrThrow(contractData.amount),
     depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee),
-    wireFeeAmortization: contractData.wireFeeAmortization ?? 1,
+    wireFeeAmortization: 1, // FIXME #8653
     prevPayCoins,
-    requiredMinimumAge: contractData.minimumAge,
+    requiredMinimumAge: undefined, // FIXME #8653
   });
 
   if (res.type !== "success") {
@@ -1278,9 +1274,9 @@ async function checkPaymentByProposalId(
       },
       contractTermsAmount: instructedAmount,
       depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee),
-      wireFeeAmortization: contractData.wireFeeAmortization ?? 1,
+      wireFeeAmortization: 1, // FIXME #8653
       prevPayCoins: [],
-      requiredMinimumAge: contractData.minimumAge,
+      requiredMinimumAge: undefined, // FIXME #8653
       restrictWireMethod: contractData.wireMethod,
     });
 
@@ -1634,7 +1630,7 @@ export async function generateDepositPermissions(
       timestamp: contractData.timestamp,
       wireInfoHash,
       ageCommitmentProof: coin.ageCommitmentProof,
-      requiredMinimumAge: contractData.minimumAge,
+      requiredMinimumAge: undefined,// FIXME #8653
     });
     depositPermissions.push(dp);
   }
@@ -1817,9 +1813,9 @@ export async function confirmPay(
     restrictWireMethod: contractData.wireMethod,
     contractTermsAmount: Amounts.parseOrThrow(contractData.amount),
     depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee),
-    wireFeeAmortization: contractData.wireFeeAmortization ?? 1,
+    wireFeeAmortization: 1, // FIXME #8653
     prevPayCoins: [],
-    requiredMinimumAge: contractData.minimumAge,
+    requiredMinimumAge: undefined, // FIXME #8653
     forcedSelection: forcedCoinSel,
   });
 
@@ -2887,7 +2883,7 @@ async function processPurchaseAcceptRefund(
 
   const refundResponse = await readSuccessResponseJsonOrThrow(
     request,
-    codecForMerchantOrderRefundPickupResponse(),
+    codecForWalletRefundResponse(),
   );
   return await storeRefunds(
     wex,
diff --git 
a/packages/taler-wallet-webextension/src/components/PaymentButtons.tsx 
b/packages/taler-wallet-webextension/src/components/PaymentButtons.tsx
index 8eb3c6e7a..7fa0376c9 100644
--- a/packages/taler-wallet-webextension/src/components/PaymentButtons.tsx
+++ b/packages/taler-wallet-webextension/src/components/PaymentButtons.tsx
@@ -80,39 +80,34 @@ export function PaymentButtons({
       case "age-acceptable": {
         BalanceMessage = i18n.str`Balance is not enough because you have 
${Amounts.stringifyValue(
           payStatus.balanceDetails.balanceAgeAcceptable,
-        )} ${amount.currency}  to pay for contracts restricted for age above ${
-          payStatus.contractTerms.minimum_age
-        } years old`;
+        )} ${amount.currency} to pay for this contract which is restricted.`;
         break;
       }
       case "available": {
         BalanceMessage = i18n.str`Balance is not enough because you have 
${Amounts.stringifyValue(
           payStatus.balanceDetails.balanceAvailable,
-        )} ${amount.currency}  available.`;
+        )} ${amount.currency} available.`;
         break;
       }
       case "merchant-acceptable": {
         BalanceMessage = i18n.str`Balance is not enough because merchant will 
just accept ${Amounts.stringifyValue(
           payStatus.balanceDetails.balanceReceiverAcceptable,
-        )} ${
-          amount.currency
-        } . To know more you can check which exchange and auditors the 
merchant trust.`;
+        )} ${amount.currency
+          } . To know more you can check which exchange and auditors the 
merchant trust.`;
         break;
       }
       case "merchant-depositable": {
         BalanceMessage = i18n.str`Balance is not enough because merchant will 
just accept ${Amounts.stringifyValue(
           payStatus.balanceDetails.balanceReceiverDepositable,
-        )} ${
-          amount.currency
-        } . To know more you can check which wire methods the merchant 
accepts.`;
+        )} ${amount.currency
+          } . To know more you can check which wire methods the merchant 
accepts.`;
         break;
       }
       case "material": {
         BalanceMessage = i18n.str`Balance is not enough because you have 
${Amounts.stringifyValue(
           payStatus.balanceDetails.balanceMaterial,
-        )} ${
-          amount.currency
-        } to spend right know. There are some coins that need to be 
refreshed.`;
+        )} ${amount.currency
+          } to spend right know. There are some coins that need to be 
refreshed.`;
         break;
       }
       case "fee-gap": {
@@ -123,9 +118,8 @@ export function PaymentButtons({
               payStatus.balanceDetails.maxEffectiveSpendAmount,
             ).amount,
           ),
-        )} ${
-          amount.currency
-        } more balance in your wallet or ask your merchant to cover more of 
the fees.`;
+        )} ${amount.currency
+          } more balance in your wallet or ask your merchant to cover more of 
the fees.`;
         break;
       }
       default:
diff --git 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
index 7142089cb..99e2d0a76 100644
--- 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
@@ -48,7 +48,6 @@ const cd: WalletContractData = {
   refundDeadline: {
     t_s: 1660002673,
   },
-  wireFeeAmortization: 1,
   allowedExchanges: [
     {
       exchangeBaseUrl: "https://exchange.taler.ar/";,
diff --git 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
index 6346b475a..b0f43d0d9 100644
--- 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
+++ 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
@@ -337,8 +337,8 @@ export function ShowView({ contractTerms, hideHandler }: 
States.Show): VNode {
                     !contractTerms.autoRefund
                       ? Duration.getZero()
                       : Duration.fromTalerProtocolDuration(
-                          contractTerms.autoRefund,
-                        ),
+                        contractTerms.autoRefund,
+                      ),
                   )}
                   format="dd MMMM yyyy, HH:mm"
                 />
@@ -384,24 +384,12 @@ export function ShowView({ contractTerms, hideHandler }: 
States.Show): VNode {
               <Amount value={contractTerms.maxDepositFee} />
             </td>
           </tr>
-          <tr>
-            <td>
-              <i18n.Translate>Minimum age</i18n.Translate>
-            </td>
-            <td>{contractTerms.minimumAge}</td>
-          </tr>
           {/* <tr>
           <td>Extra</td>
           <td>
             <pre>{contractTerms.}</pre>
           </td>
         </tr> */}
-          <tr>
-            <td>
-              <i18n.Translate>Wire fee amortization</i18n.Translate>
-            </td>
-            <td>{contractTerms.wireFeeAmortization}</td>
-          </tr>
           <tr>
             <td>
               <i18n.Translate>Exchanges</i18n.Translate>
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b896375eb..6f53c7d90 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -774,15 +774,21 @@ importers:
       '@types/node':
         specifier: ^18.11.17
         version: 18.11.17
+      '@typescript-eslint/eslint-plugin':
+        specifier: ^6.19.0
+        version: 
6.19.0(@typescript-eslint/parser@6.19.0)(eslint@8.56.0)(typescript@5.3.3)
+      '@typescript-eslint/parser':
+        specifier: ^6.19.0
+        version: 6.19.0(eslint@8.56.0)(typescript@5.3.3)
       ava:
         specifier: ^6.0.1
         version: 6.0.1(@ava/typescript@4.1.0)
       esbuild:
         specifier: ^0.19.9
         version: 0.19.9
-      prettier:
-        specifier: ^3.1.1
-        version: 3.1.1
+      eslint:
+        specifier: ^8.56.0
+        version: 8.56.0
       typescript:
         specifier: ^5.3.3
         version: 5.3.3

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]