gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: exchange and merchant api


From: gnunet
Subject: [taler-wallet-core] branch master updated: exchange and merchant api
Date: Sun, 05 Nov 2023 04:54:02 +0100

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 31cf3187e exchange and merchant api
31cf3187e is described below

commit 31cf3187e447e2c4ec8a473362c5bacc07a874f1
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Sun Nov 5 00:53:53 2023 -0300

    exchange and merchant api
---
 .../taler-harness/src/http-client/bank-core.ts     |  244 ++-
 packages/taler-harness/src/index.ts                |   79 +-
 .../taler-util/src/http-client/bank-integration.ts |    8 +-
 .../taler-util/src/http-client/bank-revenue.ts     |    9 +-
 packages/taler-util/src/http-client/bank-wire.ts   |    7 +-
 packages/taler-util/src/http-client/exchange.ts    |   37 +
 packages/taler-util/src/http-client/merchant.ts    |   37 +
 packages/taler-util/src/http-client/types.ts       | 1983 +++++++++++++++++++-
 packages/taler-util/src/operation.ts               |    2 +-
 9 files changed, 2304 insertions(+), 102 deletions(-)

diff --git a/packages/taler-harness/src/http-client/bank-core.ts 
b/packages/taler-harness/src/http-client/bank-core.ts
index ccefd2bfe..9919fc0a7 100644
--- a/packages/taler-harness/src/http-client/bank-core.ts
+++ b/packages/taler-harness/src/http-client/bank-core.ts
@@ -1,8 +1,8 @@
-import { AccessToken, Amounts, TalerCoreBankHttpClient, TalerCorebankApi, 
TestForApi, buildPayto, encodeCrock, failOrThrow, getRandomBytes, 
parsePaytoUri, stringifyPaytoUri, succeedOrThrow } from "@gnu-taler/taler-util"
+import { AccessToken, AmountJson, Amounts, TalerCoreBankHttpClient, 
TalerCorebankApi, TalerRevenueHttpClient, TalerWireGatewayApi, 
TalerWireGatewayHttpClient, TestForApi, buildPayto, encodeCrock, failOrThrow, 
getRandomBytes, parsePaytoUri, stringifyPaytoUri, succeedOrThrow } from 
"@gnu-taler/taler-util"
 
 
 
-export function createTestForBankCore(adminToken: AccessToken): 
TestForApi<TalerCoreBankHttpClient> {
+export function createTestForBankCore(api: TalerCoreBankHttpClient, 
adminToken: AccessToken): TestForApi<TalerCoreBankHttpClient> {
   return {
     test_abortCashoutById: {
       success: undefined,
@@ -46,17 +46,17 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
       success: undefined,
     },
     test_abortWithdrawalById: {
-      "invalid-id": async (api) => {
+      "invalid-id": async () => {
         await failOrThrow("invalid-id", () =>
           api.abortWithdrawalById("invalid")
         )
       },
-      "not-found": async (api) => {
+      "not-found": async () => {
         await failOrThrow("not-found", () =>
           api.abortWithdrawalById("11111111-1111-1111-1111-111111111111")
         )
       },
-      "previously-confirmed": async (api) => {
+      "previously-confirmed": async () => {
         const { username: exchangeUser, token: exchangeToken } = await 
createRandomTestUser(api, adminToken, { is_taler_exchange: true })
         const { username, token } = await createRandomTestUser(api, adminToken)
 
@@ -86,7 +86,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           api.abortWithdrawalById(withdrawal_id)
         )
       },
-      success: async (api) => {
+      success: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
 
         const userInfo = await succeedOrThrow(() =>
@@ -105,15 +105,15 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
       },
     },
     test_confirmWithdrawalById: {
-      "insufficient-funds": async (api) => {
+      "insufficient-funds": async () => {
 
       },
-      "invalid-id": async (api) => {
+      "invalid-id": async () => {
         await failOrThrow("invalid-id", () =>
           api.confirmWithdrawalById("invalid")
         )
       },
-      "no-exchange-or-reserve-selected": async (api) => {
+      "no-exchange-or-reserve-selected": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
 
         const userInfo = await succeedOrThrow(() =>
@@ -130,12 +130,12 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           api.confirmWithdrawalById(withdrawal_id)
         )
       },
-      "not-found": async (api) => {
+      "not-found": async () => {
         await failOrThrow("not-found", () =>
           api.confirmWithdrawalById("11111111-1111-1111-1111-111111111111")
         )
       },
-      "previously-aborted": async (api) => {
+      "previously-aborted": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         const userInfo = await succeedOrThrow(() =>
           api.getAccount({ username, token })
@@ -153,7 +153,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           api.confirmWithdrawalById(withdrawal_id)
         )
       },
-      success: async (api) => {
+      success: async () => {
         const { username: exchangeUser, token: exchangeToken } = await 
createRandomTestUser(api, adminToken, { is_taler_exchange: true })
         const { username, token } = await createRandomTestUser(api, adminToken)
 
@@ -185,7 +185,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
     },
     test_createAccount: {
       "insufficient-funds": undefined,
-      "payto-already-exists": async (api) => {
+      "payto-already-exists": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
 
         const userInfo = await succeedOrThrow(() =>
@@ -203,7 +203,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
         );
 
       },
-      "username-reserved": async (api) => {
+      "username-reserved": async () => {
         await failOrThrow("username-reserved", () =>
           api.createAccount(adminToken, {
             name: "admin",
@@ -211,7 +211,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           })
         )
       },
-      "username-already-exists": async (api) => {
+      "username-already-exists": async () => {
         const username = "harness-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
         await succeedOrThrow(() =>
           api.createAccount(adminToken, {
@@ -227,7 +227,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           })
         );
       },
-      "invalid-phone-or-email": async (api) => {
+      "invalid-phone-or-email": async () => {
         const username = "harness-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
         await failOrThrow("invalid-input", () =>
           api.createAccount(adminToken, {
@@ -240,7 +240,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           })
         )
       },
-      success: async (api) => {
+      success: async () => {
         const username = "harness-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
 
         await succeedOrThrow(() =>
@@ -250,7 +250,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           })
         )
       },
-      unauthorized: async (api) => {
+      unauthorized: async () => {
         const username = "harness-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
 
         await succeedOrThrow(() =>
@@ -278,7 +278,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
 
     },
     test_createTransaction: {
-      "creditor-not-found": async (api) => {
+      "creditor-not-found": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
 
         const userInfo = await succeedOrThrow(() =>
@@ -294,7 +294,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           })
         )
       },
-      "creditor-same": async (api) => {
+      "creditor-same": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
 
         const userInfo = await succeedOrThrow(() =>
@@ -311,7 +311,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
         )
 
       },
-      "insufficient-funds": async (api) => {
+      "insufficient-funds": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         const { username: otherUser, token: otherToken } = await 
createRandomTestUser(api, adminToken)
 
@@ -331,7 +331,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           })
         )
       },
-      "not-found": async (api) => {
+      "not-found": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         const { username: otherUser, token: otherToken } = await 
createRandomTestUser(api, adminToken)
 
@@ -351,7 +351,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           })
         )
       },
-      "invalid-input": async (api) => {
+      "invalid-input": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         const { username: otherUser, token: otherToken } = await 
createRandomTestUser(api, adminToken)
 
@@ -380,7 +380,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
         )
 
       },
-      success: async (api) => {
+      success: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         const { username: otherUser, token: otherToken } = await 
createRandomTestUser(api, adminToken)
 
@@ -401,7 +401,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
         )
 
       },
-      unauthorized: async (api) => {
+      unauthorized: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         const { username: otherUser, token: otherToken } = await 
createRandomTestUser(api, adminToken)
 
@@ -423,7 +423,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
       },
     },
     test_createWithdrawal: {
-      "account-not-found": async (api) => {
+      "account-not-found": async () => {
         const userInfo = await succeedOrThrow(() =>
           api.getAccount({ username: "admin", token: adminToken })
         )
@@ -434,7 +434,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
         )
 
       },
-      "insufficient-funds": async (api) => {
+      "insufficient-funds": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         const userInfo = await succeedOrThrow(() =>
           api.getAccount({ username, token })
@@ -448,7 +448,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           })
         )
       },
-      success: async (api) => {
+      success: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         const userInfo = await succeedOrThrow(() =>
           api.getAccount({ username, token })
@@ -460,7 +460,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
         )
 
       },
-      unauthorized: async (api) => {
+      unauthorized: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         const userInfo = await succeedOrThrow(() =>
           api.getAccount({ username, token })
@@ -474,7 +474,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
       },
     },
     test_deleteAccount: {
-      "balance-not-zero": async (api) => {
+      "balance-not-zero": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
 
         await failOrThrow("balance-not-zero", () =>
@@ -482,12 +482,12 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
         )
 
       },
-      "not-found": async (api) => {
+      "not-found": async () => {
         await failOrThrow("not-found", () =>
           api.deleteAccount({ username: "not-found", token: adminToken })
         )
       },
-      "username-reserved": async (api) => {
+      "username-reserved": async () => {
         await failOrThrow("username-reserved", () =>
           api.deleteAccount({ username: "admin", token: adminToken })
         )
@@ -495,7 +495,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           api.deleteAccount({ username: "bank", token: adminToken })
         )
       },
-      success: async (api) => {
+      success: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
 
         const userInfo = await succeedOrThrow(() =>
@@ -518,7 +518,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
         )
 
       },
-      unauthorized: async (api) => {
+      unauthorized: async () => {
         const username = "harness-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
 
         await succeedOrThrow(() =>
@@ -536,19 +536,19 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
       },
     },
     test_getAccount: {
-      "not-found": async (api) => {
+      "not-found": async () => {
         await failOrThrow("not-found", () =>
           api.getAccount({ username: "not-found", token: adminToken })
         )
 
       },
-      success: async (api) => {
+      success: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         await succeedOrThrow(() =>
           api.getAccount({ username, token })
         )
       },
-      unauthorized: async (api) => {
+      unauthorized: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         await failOrThrow("unauthorized", () =>
           api.getAccount({ username, token: "wrongtoken" as AccessToken })
@@ -556,7 +556,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
       },
     },
     test_getAccounts: {
-      success: async (api) => {
+      success: async () => {
         await succeedOrThrow(() =>
           api.getAccounts(adminToken)
         )
@@ -573,14 +573,14 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           })
         )
       },
-      unauthorized: async (api) => {
+      unauthorized: async () => {
         await failOrThrow("unauthorized", () =>
           api.getAccounts("ASDASD" as AccessToken)
         )
       },
     },
     test_getConfig: {
-      success: async (api) => {
+      success: async () => {
         const config = await succeedOrThrow(() => api.getConfig())
 
         if (!api.isCompatible(config.version)) {
@@ -590,13 +590,13 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
       },
     },
     test_getMonitor: {
-      "unauthorized": async (api) => {
+      "unauthorized": async () => {
         await failOrThrow("unauthorized", () => (
           api.getMonitor("wrongtoken" as AccessToken)
         ))
 
       },
-      "invalid-input": async (api) => {
+      "invalid-input": async () => {
 
         await failOrThrow("invalid-input", () => (
           api.getMonitor(adminToken, {
@@ -607,7 +607,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
 
       },
       "monitor-not-supported": undefined,
-      success: async (api) => {
+      success: async () => {
 
         await succeedOrThrow(() => (
           api.getMonitor(adminToken)
@@ -623,7 +623,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
       },
     },
     test_getPublicAccounts: {
-      success: async (api) => {
+      success: async () => {
         await succeedOrThrow(() => (
           api.getPublicAccounts()
         ))
@@ -646,13 +646,13 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
       },
     },
     test_getTransactionById: {
-      "not-found": async (api) => {
+      "not-found": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         await failOrThrow("not-found", () =>
           api.getTransactionById({ username, token }, 123123123)
         )
       },
-      success: async (api) => {
+      success: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         const { username: otherUser, token: otherToken } = await 
createRandomTestUser(api, adminToken)
 
@@ -683,7 +683,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
         )
 
       },
-      unauthorized: async (api) => {
+      unauthorized: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         await failOrThrow("unauthorized", () =>
           api.getTransactionById({ username, token: "wrongtoken" as 
AccessToken }, 123123123)
@@ -691,13 +691,13 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
       },
     },
     test_getTransactions: {
-      "not-found": async (api) => {
+      "not-found": async () => {
         await failOrThrow("not-found", () => api.getTransactions({
           username: "not-found",
           token: adminToken,
         }))
       },
-      success: async (api) => {
+      success: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         // await succeedOrThrow(() => api.getTransactions(creds))
         const txs = await succeedOrThrow(() => api.getTransactions({ username, 
token }, {
@@ -705,7 +705,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           order: "asc"
         }))
       },
-      unauthorized: async (api) => {
+      unauthorized: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
 
         await failOrThrow("unauthorized", () => api.getTransactions({
@@ -716,20 +716,20 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
       },
     },
     test_getWithdrawalById: {
-      "invalid-id": async (api) => {
+      "invalid-id": async () => {
 
         await failOrThrow("invalid-id", () =>
           api.getWithdrawalById("invalid")
         )
 
       },
-      "not-found": async (api) => {
+      "not-found": async () => {
         await failOrThrow("not-found", () =>
           api.getWithdrawalById("11111111-1111-1111-1111-111111111111")
         )
 
       },
-      success: async (api) => {
+      success: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         const userInfo = await succeedOrThrow(() =>
           api.getAccount({ username, token })
@@ -745,7 +745,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
       },
     },
     test_updateAccount: {
-      "cant-change-legal-name-or-admin": async (api) => {
+      "cant-change-legal-name-or-admin": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
 
         await failOrThrow("cant-change-legal-name-or-admin", () =>
@@ -755,7 +755,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
         )
 
       },
-      "not-found": async (api) => {
+      "not-found": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         await failOrThrow("not-found", () =>
           api.updateAccount({ username: "notfound", token }, {
@@ -765,7 +765,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
           })
         )
       },
-      success: async (api) => {
+      success: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
 
         await succeedOrThrow(() =>
@@ -777,7 +777,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
         )
 
       },
-      unauthorized: async (api) => {
+      unauthorized: async () => {
 
         await failOrThrow("unauthorized", () =>
           api.updateAccount({ username: "notfound", token: "wrongtoken" as 
AccessToken }, {
@@ -789,7 +789,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
       },
     },
     test_updatePassword: {
-      "not-found": async (api) => {
+      "not-found": async () => {
 
         await failOrThrow("not-found", () =>
           api.updatePassword({ username: "notfound", token: adminToken }, {
@@ -800,7 +800,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
 
 
       },
-      "old-password-invalid-or-not-allowed": async (api) => {
+      "old-password-invalid-or-not-allowed": async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
 
         await failOrThrow("old-password-invalid-or-not-allowed", () =>
@@ -811,7 +811,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
         )
 
       },
-      success: async (api) => {
+      success: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
 
         await succeedOrThrow(() =>
@@ -823,7 +823,7 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
 
 
       },
-      unauthorized: async (api) => {
+      unauthorized: async () => {
         const { username, token } = await createRandomTestUser(api, adminToken)
         await failOrThrow("unauthorized", () =>
           api.updatePassword({ username: "admin", token }, {
@@ -838,6 +838,128 @@ export function createTestForBankCore(adminToken: 
AccessToken): TestForApi<Taler
   }
 }
 
+export function createTestForBankRevenue(bank: TalerCoreBankHttpClient, 
adminToken: AccessToken): TestForApi<TalerRevenueHttpClient> {
+
+  return {
+    test_getHistory: {
+      "endpoint-wrong-or-username-wrong": async () => {
+        const history = await failOrThrow("endpoint-wrong-or-username-wrong", 
() =>
+          bank.getRevenueAPI("notfound").getHistory("wrongtoken" as 
AccessToken)
+        )
+      },
+      "invalid-input": undefined,
+      success: async () => {
+        const { token: exchangeToken, username: exchangeUsername } = await 
createRandomTestUser(bank, adminToken, {
+          is_taler_exchange: true
+        })
+        const { token: merchantToken, username: merchantUsername } = await 
createRandomTestUser(bank, adminToken)
+        const config = await succeedOrThrow(() => bank.getConfig())
+
+        const merchantinfo = await succeedOrThrow(() =>
+          bank.getAccount({ username: merchantUsername, token: merchantToken })
+        )
+        const account = parsePaytoUri(merchantinfo.payto_uri)!
+        account.params["message"] = "all"
+
+        const amount = Amounts.stringify({
+          currency: config.currency.name,
+          fraction: 0,
+          value: 1
+        })
+
+        await succeedOrThrow(() =>
+          bank.createTransaction({ username: exchangeUsername, token: 
exchangeToken }, {
+            payto_uri: stringifyPaytoUri(account),
+            amount
+          })
+        )
+        const history = await succeedOrThrow(() =>
+          bank.getRevenueAPI(merchantUsername).getHistory(merchantToken)
+        )
+      },
+      unauthorized: async () => {
+        const { token: merchantToken, username: merchantUsername } = await 
createRandomTestUser(bank, adminToken)
+        const history = await failOrThrow("unauthorized", () =>
+          bank.getRevenueAPI(merchantUsername).getHistory("wrongtoken" as 
AccessToken)
+        )
+      },
+    }
+  }
+}
+
+export function createTestForBankWireGateway(bank: TalerCoreBankHttpClient, 
adminToken: AccessToken): TestForApi<TalerWireGatewayHttpClient> {
+  return {
+    //not used in production
+    test_addIncoming: {
+      "invalid-input": undefined,
+      "not-found": undefined,
+      "reserve-id-already-used": undefined,
+      success: undefined,
+      unauthorized: undefined,
+    },
+    test_getHistoryIncoming: {
+      "invalid-input": async () => {
+      },
+      "not-found": async () => {
+      },
+      success: async () => {
+      },
+      unauthorized: async () => {
+      },
+    },
+    test_getHistoryOutgoing: {
+      "invalid-input": async () => {
+      },
+      "not-found": async () => {
+      },
+      success: async () => {
+      },
+      unauthorized: async () => {
+      },
+    },
+    test_transfer: {
+      "invalid-input": async () => {
+      },
+      "not-found": async () => {
+      },
+      "request-uid-already-used": async () => {
+      },
+      success: async () => {
+        const { token: exchangeToken, username: exchangeUsername } = await 
createRandomTestUser(bank, adminToken, {
+          is_taler_exchange: true
+        })
+        const { token: merchantToken, username: merchantUsername } = await 
createRandomTestUser(bank, adminToken)
+        const config = await succeedOrThrow(() => bank.getConfig())
+
+        const merchantInfo = await succeedOrThrow(() =>
+          bank.getAccount({ username: merchantUsername, token: merchantToken })
+        )
+        const account = parsePaytoUri(merchantInfo.payto_uri)!
+        account.params["message"] = "all"
+
+        const amount = Amounts.stringify({
+          currency: config.currency.name,
+          fraction: 0,
+          value: 1
+        })
+        const resp = await succeedOrThrow(() =>
+          bank.getWireGatewayAPI(merchantUsername).transfer(exchangeToken, {
+            amount,
+            credit_account: merchantInfo.payto_uri,
+            exchange_base_url: "",
+            request_uid: "",
+            wtid: ""
+          })
+        )
+
+      },
+      unauthorized: async () => {
+      },
+    }
+  }
+}
+
+
 export async function createRandomTestUser(api: TalerCoreBankHttpClient, 
adminToken: AccessToken, options: 
Partial<TalerCorebankApi.RegisterAccountRequest> = {}) {
   const username = "harness-" + encodeCrock(getRandomBytes(10)).toLowerCase();
   await succeedOrThrow(() =>
diff --git a/packages/taler-harness/src/index.ts 
b/packages/taler-harness/src/index.ts
index af4e5c788..5bf60d54f 100644
--- a/packages/taler-harness/src/index.ts
+++ b/packages/taler-harness/src/index.ts
@@ -30,6 +30,7 @@ import {
   TalerCoreBankHttpClient,
   TalerCorebankApiClient,
   TalerError,
+  TestForApi,
   addPaytoQueryParams,
   decodeCrock,
   generateIban,
@@ -656,12 +657,46 @@ deploymentCli
     process.exit(2);
   });
 
+
+type TestResult = { testName: string, caseName: string, result: "skiped" | 
"ok" | "fail", error?: any }
+
+async function getTestSummary<T extends object>(filter: string | undefined, 
...apis: Array<TestForApi<T>>) {
+  const regex = !filter ? undefined : new RegExp(filter)
+  const apiState = await Promise.all(apis.flatMap(api => 
Object.entries(api).flatMap(([testName, casesMap]) => {
+    return Object.entries(casesMap).map(async ([caseName, caseFunc]): 
Promise<TestResult> => {
+      if (!caseFunc) {
+        return { testName, caseName, result: "skiped" as const }
+      }
+      if (regex && !regex.test(`${testName}:${caseName}`)) {
+        return { testName, caseName, result: "skiped" as const }
+      }
+      return caseFunc()
+        .then(r => ({ testName, caseName, result: "ok" as const }))
+        .catch(error => ({ testName, caseName, result: "fail" as const, error 
}))
+    })
+  })))
+
+  return apiState.reduce((prev, testResult) => {
+    if (testResult.result === "ok") {
+      prev.ok.push(testResult)
+    }
+    if (testResult.result === "skiped") {
+      prev.skiped.push(testResult)
+    }
+    if (testResult.result === "fail") {
+      prev.fail.push(testResult)
+    }
+    return prev
+  }, { "ok": [] as TestResult[], "skiped": [] as TestResult[], "fail": [] as 
TestResult[] })
+}
+
 deploymentCli
   .subcommand("testBankAPI", "test-bank-api", {
     help: "test api compatibility.",
   })
   .requiredArgument("corebankApiBaseUrl", clk.STRING)
   .maybeOption("adminPwd", ["--admin-password"], clk.STRING)
+  .maybeOption("filter", ["--filter"], clk.STRING)
   .flag("showCurl", ["--show-curl"])
   .action(async (args) => {
     const httpLib = createPlatformHttpLib();
@@ -685,55 +720,33 @@ deploymentCli
       scope: "readwrite"
     })
 
+    if (args.testBankAPI.showCurl) {
+      setPrintHttpRequestAsCurl(true)
+    }
+
     if (resp.type === "fail") {
       console.log("wrong admin password")
       return;
     }
-    const tester = createTestForBankCore(resp.body.access_token)
 
-    if (args.testBankAPI.showCurl) {
-      setPrintHttpRequestAsCurl(true)
-    }
+    const bankCore = createTestForBankCore(api, resp.body.access_token)
 
-    const apiState = await 
Promise.all(Object.entries(tester).flatMap(([testName, casesMap]) => {
-      return Object.entries(casesMap).map(([caseName, caseFunc]) => {
-        if (!caseFunc) {
-          return { testName, caseName, result: "skiped" as const }
-        }
-        return caseFunc(api)
-          .then(r => ({ testName, caseName, result: "ok" as const }))
-          .catch(error => ({ testName, caseName, result: "fail" as const, 
error }))
-      })
-    }))
-
-    const total = apiState.reduce((prev, testResult) => {
-      if (testResult.result === "ok") {
-        prev.ok += 1
-      }
-      if (testResult.result === "skiped") {
-        prev.skiped += 1
-      }
-      if (testResult.result === "fail") {
-        prev.fail += 1
-      }
-      return prev
-    }, { "ok": 0, "skiped": 0, "fail": 0 })
+    const summary = await getTestSummary(args.testBankAPI.filter, bankCore)
 
-    console.log("successful tests:", total.ok)
-    console.log("uncompleted tests:", total.skiped)
-    apiState.forEach((testResult) => {
+    console.log("successful tests:", summary.ok.length)
+    console.log("uncompleted tests:", summary.skiped.length)
+    summary.skiped.forEach((testResult) => {
       if (testResult.result === "skiped") {
         console.log("    ", testResult.testName, testResult.caseName)
       }
     })
-    console.log("failed tests:", total.fail)
-    apiState.filter(t => t.result === "fail").forEach((testResult, i) => {
+    console.log("failed tests:", summary.fail.length)
+    summary.fail.forEach((testResult, i) => {
       console.log(i, ")", testResult)
     })
   });
 
 
-
 deploymentCli
   .subcommand("coincfg", "gen-coin-config", {
     help: "Generate a coin/denomination configuration for the exchange.",
diff --git a/packages/taler-util/src/http-client/bank-integration.ts 
b/packages/taler-util/src/http-client/bank-integration.ts
index 7cddcb9a9..b526805df 100644
--- a/packages/taler-util/src/http-client/bank-integration.ts
+++ b/packages/taler-util/src/http-client/bank-integration.ts
@@ -1,7 +1,7 @@
 import { HttpRequestLibrary, readSuccessResponseJsonOrThrow } from 
"../http-common.js";
 import { HttpStatusCode } from "../http-status-codes.js";
 import { createPlatformHttpLib } from "../http.js";
-import { opKnownFailure, opSuccess, opUnknownFailure } from "../operation.js";
+import { FailCasesByMethod, ResultByMethod, opKnownFailure, opSuccess, 
opUnknownFailure } from "../operation.js";
 import { TalerErrorCode } from "../taler-error-codes.js";
 import { codecForTalerErrorDetail } from "../wallet-types.js";
 import {
@@ -11,6 +11,12 @@ import {
   codecForIntegrationBankConfig
 } from "./types.js";
 
+export type TalerBankIntegrationResultByMethod<prop extends keyof 
TalerBankIntegrationHttpClient> = 
ResultByMethod<TalerBankIntegrationHttpClient, prop>
+export type TalerBankIntegrationErrorsByMethod<prop extends keyof 
TalerBankIntegrationHttpClient> = 
FailCasesByMethod<TalerBankIntegrationHttpClient, prop>
+
+/**
+ * The API is used by the wallets.
+ */
 export class TalerBankIntegrationHttpClient {
   httpLib: HttpRequestLibrary;
 
diff --git a/packages/taler-util/src/http-client/bank-revenue.ts 
b/packages/taler-util/src/http-client/bank-revenue.ts
index c6adac557..14d93fbe6 100644
--- a/packages/taler-util/src/http-client/bank-revenue.ts
+++ b/packages/taler-util/src/http-client/bank-revenue.ts
@@ -1,10 +1,17 @@
 import { HttpRequestLibrary, makeBasicAuthHeader, 
readSuccessResponseJsonOrThrow } from "../http-common.js";
 import { HttpStatusCode } from "../http-status-codes.js";
 import { createPlatformHttpLib } from "../http.js";
-import { opKnownFailure, opSuccess, opUnknownFailure } from "../operation.js";
+import { FailCasesByMethod, ResultByMethod, opKnownFailure, opSuccess, 
opUnknownFailure } from "../operation.js";
 import { PaginationParams, TalerRevenueApi, codecForMerchantIncomingHistory } 
from "./types.js";
 import { addPaginationParams } from "./utils.js";
 
+export type TalerBankRevenueResultByMethod<prop extends keyof 
TalerRevenueHttpClient> = ResultByMethod<TalerRevenueHttpClient, prop>
+export type TalerBankRevenueErrorsByMethod<prop extends keyof 
TalerRevenueHttpClient> = FailCasesByMethod<TalerRevenueHttpClient, prop>
+
+/**
+ * The API is used by the merchant (or other parties) to query 
+ * for incoming transactions to their account.
+ */
 export class TalerRevenueHttpClient {
   httpLib: HttpRequestLibrary;
 
diff --git a/packages/taler-util/src/http-client/bank-wire.ts 
b/packages/taler-util/src/http-client/bank-wire.ts
index af0857ac5..0d312704e 100644
--- a/packages/taler-util/src/http-client/bank-wire.ts
+++ b/packages/taler-util/src/http-client/bank-wire.ts
@@ -1,10 +1,13 @@
-import { HttpRequestLibrary, makeBasicAuthHeader, 
readSuccessResponseJsonOrThrow } from "../http-common.js";
+import { HttpRequestLibrary, makeBasicAuthHeader } from "../http-common.js";
 import { HttpStatusCode } from "../http-status-codes.js";
 import { createPlatformHttpLib } from "../http.js";
-import { opEmptySuccess, opFixedSuccess, opKnownFailure, opSuccess, 
opUnknownFailure } from "../operation.js";
+import { FailCasesByMethod, ResultByMethod, opFixedSuccess, opKnownFailure, 
opSuccess, opUnknownFailure } from "../operation.js";
 import { PaginationParams, TalerWireGatewayApi, codecForAddIncomingResponse, 
codecForIncomingHistory, codecForOutgoingHistory, codecForTransferResponse } 
from "./types.js";
 import { addPaginationParams } from "./utils.js";
 
+export type TalerWireGatewayResultByMethod<prop extends keyof 
TalerWireGatewayHttpClient> = ResultByMethod<TalerWireGatewayHttpClient, prop>
+export type TalerWireGatewayErrorsByMethod<prop extends keyof 
TalerWireGatewayHttpClient> = FailCasesByMethod<TalerWireGatewayHttpClient, 
prop>
+
 /**
  * The API is used by the exchange to trigger transactions and query 
  * incoming transactions, as well as by the auditor to query incoming 
diff --git a/packages/taler-util/src/http-client/exchange.ts 
b/packages/taler-util/src/http-client/exchange.ts
new file mode 100644
index 000000000..52f5dc5a6
--- /dev/null
+++ b/packages/taler-util/src/http-client/exchange.ts
@@ -0,0 +1,37 @@
+import { HttpRequestLibrary } from "../http-common.js";
+import { HttpStatusCode } from "../http-status-codes.js";
+import { createPlatformHttpLib } from "../http.js";
+import { FailCasesByMethod, ResultByMethod, opSuccess, opUnknownFailure } from 
"../operation.js";
+import { codecForExchangeConfig } from "./types.js";
+
+export type TalerExchangeResultByMethod<prop extends keyof 
TalerExchangeHttpClient> = ResultByMethod<TalerExchangeHttpClient, prop>
+export type TalerExchangeErrorsByMethod<prop extends keyof 
TalerExchangeHttpClient> = FailCasesByMethod<TalerExchangeHttpClient, prop>
+
+/**
+ */
+export class TalerExchangeHttpClient {
+  httpLib: HttpRequestLibrary;
+
+  constructor(
+    readonly baseUrl: string,
+    httpClient?: HttpRequestLibrary,
+  ) {
+    this.httpLib = httpClient ?? createPlatformHttpLib();
+  }
+
+  /**
+   * 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 opSuccess(resp, codecForExchangeConfig())
+      default: return opUnknownFailure(resp, await resp.text())
+    }
+  }
+
+}
\ No newline at end of file
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..5aace2d78
--- /dev/null
+++ b/packages/taler-util/src/http-client/merchant.ts
@@ -0,0 +1,37 @@
+import { HttpRequestLibrary } from "../http-common.js";
+import { HttpStatusCode } from "../http-status-codes.js";
+import { createPlatformHttpLib } from "../http.js";
+import { FailCasesByMethod, ResultByMethod, opSuccess, opUnknownFailure } from 
"../operation.js";
+import { codecForMerchantConfig } from "./types.js";
+
+export type TalerMerchantResultByMethod<prop extends keyof 
TalerMerchantHttpClient> = ResultByMethod<TalerMerchantHttpClient, prop>
+export type TalerMerchantErrorsByMethod<prop extends keyof 
TalerMerchantHttpClient> = FailCasesByMethod<TalerMerchantHttpClient, prop>
+
+/**
+ */
+export class TalerMerchantHttpClient {
+  httpLib: HttpRequestLibrary;
+
+  constructor(
+    readonly baseUrl: string,
+    httpClient?: HttpRequestLibrary,
+  ) {
+    this.httpLib = httpClient ?? createPlatformHttpLib();
+  }
+
+  /**
+   * 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 opSuccess(resp, codecForMerchantConfig())
+      default: return opUnknownFailure(resp, await resp.text())
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/packages/taler-util/src/http-client/types.ts 
b/packages/taler-util/src/http-client/types.ts
index 38df08f9a..fe69925f6 100644
--- a/packages/taler-util/src/http-client/types.ts
+++ b/packages/taler-util/src/http-client/types.ts
@@ -187,6 +187,7 @@ export namespace TalerAuthentication {
   }
 }
 
+// DD51 https://docs.taler.net/design-documents/051-fractional-digits.html
 export interface CurrencySpecification {
 
   // Name of the currency.
@@ -239,7 +240,8 @@ export const codecForIntegrationBankConfig =
 export const codecForCoreBankConfig =
   (): Codec<TalerCorebankApi.Config> =>
     buildCodecForObject<TalerCorebankApi.Config>()
-      .property("name", codecForConstString("taler-corebank"))
+      .property("name", codecForConstString("libeufin-bank"))
+      // .property("name", codecForConstString("taler-corebank"))
       .property("version", codecForString())
       .property("allow_deletions", codecForBoolean())
       .property("allow_registrations", codecForBoolean())
@@ -249,6 +251,25 @@ export const codecForCoreBankConfig =
       .property("conversion_info", 
codecOptional(codecForConversionRatesResponse()))
       .build("TalerCorebankApi.Config")
 
+export const codecForMerchantConfig =
+  (): Codec<TalerMerchantApi.VersionResponse> =>
+    buildCodecForObject<TalerMerchantApi.VersionResponse>()
+      .property("name", codecForConstString("taler-merchant"))
+      .property("currency", codecForString())
+      .property("version", codecForString())
+      .property("currencies", codecForMap(codecForCurrencySpecificiation()))
+      .build("TalerMerchantApi.VersionResponse")
+
+export const codecForExchangeConfig =
+  (): Codec<TalerExchangeApi.ExchangeVersionResponse> =>
+    buildCodecForObject<TalerExchangeApi.ExchangeVersionResponse>()
+      .property("version", codecForString())
+      .property("name", codecForConstString("taler-exchange"))
+      .property("currency", codecForString())
+      .property("currency_specification", codecForCurrencySpecificiation())
+      .property("supported_kyc_requirements", codecForList(codecForString()))
+      .build("TalerExchangeApi.ExchangeVersionResponse")
+
 const codecForBalance = (): Codec<TalerCorebankApi.Balance> =>
   buildCodecForObject<TalerCorebankApi.Balance>()
     .property("amount", codecForAmountString())
@@ -552,12 +573,41 @@ export const codecForAddIncomingResponse =
 
 type EmailAddress = string;
 type PhoneNumber = string;
+type EddsaSignature = string;
+// base32 encoded RSA blinded signature.
+type BlindedRsaSignature = string;
+type Base32 = string;
+
 type DecimalNumber = number;
+type RsaSignature = string;
+// The type of a coin's blinded envelope depends on the cipher that is used
+// for signing with a denomination key.
+type CoinEnvelope = RSACoinEnvelope | CSCoinEnvelope;
+// For denomination signatures based on RSA, the planchet is just a blinded
+// coin's public EdDSA key.
+interface RSACoinEnvelope {
+  cipher: "RSA" | "RSA+age_restricted";
+  rsa_blinded_planchet: string;          // Crockford Base32 encoded
+}
+// For denomination signatures based on Blind Clause-Schnorr, the planchet
+// consists of the public nonce and two Curve25519 scalars which are two
+// blinded challenges in the Blinded Clause-Schnorr signature scheme.
+// See https://taler.net/papers/cs-thesis.pdf for details.
+interface CSCoinEnvelope {
+  cipher: "CS" | "CS+age_restricted";
+  cs_nonce: string;      // Crockford Base32 encoded
+  cs_blinded_c0: string; // Crockford Base32 encoded
+  cs_blinded_c1: string; // Crockford Base32 encoded
+}
+// Secret for blinding/unblinding.
+// An RSA blinding secret, which is basically
+// a 256-bit nonce, converted to Crockford Base32.
+type DenominationBlindingKeyP = string;
 
 const codecForURL = codecForString
 const codecForLibtoolVersion = codecForString
 const codecForCurrencyName = codecForString
-const codecForTalerWithdrawalURI = codecForString
+const codecForEddsaSignature = codecForString
 const codecForDecimalNumber = codecForNumber
 
 enum TanChannel {
@@ -869,7 +919,9 @@ export namespace TalerCorebankApi {
   }
   export interface Config {
     // Name of this API, always "taler-corebank".
-    name: "taler-corebank";
+    name: "libeufin-bank";
+    // name: "taler-corebank";
+
     // API version in the form $n:$n:$n
     version: string;
 
@@ -1286,3 +1338,1928 @@ export namespace TalerCorebankApi {
 
 
 }
+
+export namespace TalerExchangeApi {
+
+
+  export interface ExchangeVersionResponse {
+    // libtool-style representation of the Exchange protocol version, see
+    // 
https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+    // The format is "current:revision:age".
+    version: string;
+
+    // Name of the protocol.
+    name: "taler-exchange";
+
+    // Currency supported by this exchange.
+    currency: string;
+
+    // How wallets should render this currency.
+    currency_specification: CurrencySpecification;
+
+    // Names of supported KYC requirements.
+    supported_kyc_requirements: string[];
+
+  }
+
+  type AccountRestriction =
+    | RegexAccountRestriction
+    | DenyAllAccountRestriction
+  // Account restriction that disables this type of
+  // account for the indicated operation categorically.
+  interface DenyAllAccountRestriction {
+
+    type: "deny";
+  }
+  // Accounts interacting with this type of account
+  // restriction must have a payto://-URI matching
+  // the given regex.
+  interface RegexAccountRestriction {
+
+    type: "regex";
+
+    // Regular expression that the payto://-URI of the
+    // partner account must follow.  The regular expression
+    // should follow posix-egrep, but without support for character
+    // classes, GNU extensions, back-references or intervals. See
+    // 
https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002degrep-regular-expression-syntax.html
+    // for a description of the posix-egrep syntax. Applications
+    // may support regexes with additional features, but exchanges
+    // must not use such regexes.
+    payto_regex: string;
+
+    // Hint for a human to understand the restriction
+    // (that is hopefully easier to comprehend than the regex itself).
+    human_hint: string;
+
+    // Map from IETF BCP 47 language tags to localized
+    // human hints.
+    human_hint_i18n?: { [lang_tag: string]: string };
+
+  }
+
+  export interface WireAccount {
+    // payto:// URI identifying the account and wire method
+    payto_uri: PaytoString;
+
+    // URI to convert amounts from or to the currency used by
+    // this wire account of the exchange. Missing if no
+    // conversion is applicable.
+    conversion_url?: string;
+
+    // Restrictions that apply to bank accounts that would send
+    // funds to the exchange (crediting this exchange bank account).
+    // Optional, empty array for unrestricted.
+    credit_restrictions: AccountRestriction[];
+
+    // Restrictions that apply to bank accounts that would receive
+    // funds from the exchange (debiting this exchange bank account).
+    // Optional, empty array for unrestricted.
+    debit_restrictions: AccountRestriction[];
+
+    // Signature using the exchange's offline key over
+    // a TALER_MasterWireDetailsPS
+    // with purpose TALER_SIGNATURE_MASTER_WIRE_DETAILS.
+    master_sig: EddsaSignature;
+  }
+
+}
+
+export namespace TalerMerchantApi {
+  export interface VersionResponse {
+    // libtool-style representation of the Merchant protocol version, see
+    // 
https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+    // The format is "current:revision:age".
+    version: string;
+
+    // Name of the protocol.
+    name: "taler-merchant";
+
+    // Default (!) currency supported by this backend.
+    // This is the currency that the backend should
+    // suggest by default to the user when entering
+    // amounts. See currencies for a list of
+    // supported currencies and how to render them.
+    currency: string;
+
+    // How wallets 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.
+    currencies: { [currency: string]: CurrencySpecification };
+
+  }
+
+  export interface ClaimRequest {
+    // Nonce to identify the wallet that claimed the order.
+    nonce: string;
+
+    // Token that authorizes the wallet to claim the order.
+    // *Optional* as the merchant may not have required it
+    // (create_token set to false in PostOrderRequest).
+    token?: ClaimToken;
+  }
+
+  export interface ClaimResponse {
+    // Contract terms of the claimed order
+    contract_terms: ContractTerms;
+
+    // Signature by the merchant over the contract terms.
+    sig: EddsaSignature;
+  }
+
+  export interface PaymentResponse {
+    // Signature on TALER_PaymentResponsePS with the public
+    // key of the merchant instance.
+    sig: EddsaSignature;
+
+    // Text to be shown to the point-of-sale staff as a proof of
+    // payment.
+    pos_confirmation?: string;
+
+  }
+
+  interface PayRequest {
+    // The coins used to make the payment.
+    coins: CoinPaySig[];
+
+    // Custom inputs from the wallet for the contract.
+    wallet_data?: Object;
+
+    // The session for which the payment is made (or replayed).
+    // Only set for session-based payments.
+    session_id?: string;
+
+  }
+  export interface CoinPaySig {
+    // Signature by the coin.
+    coin_sig: EddsaSignature;
+
+    // Public key of the coin being spent.
+    coin_pub: EddsaPublicKey;
+
+    // Signature made by the denomination public key.
+    ub_sig: RsaSignature;
+
+    // The hash of the denomination public key associated with this coin.
+    h_denom: HashCode;
+
+    // The amount that is subtracted from this coin with this payment.
+    contribution: AmountString;
+
+    // URL of the exchange this coin was withdrawn from.
+    exchange_url: string;
+  }
+
+
+  interface StatusPaid {
+    // Was the payment refunded (even partially, via refund or abort)?
+    refunded: boolean;
+
+    // Is any amount of the refund still waiting to be picked up (even 
partially)?
+    refund_pending: boolean;
+
+    // Amount that was refunded in total.
+    refund_amount: AmountString;
+
+    // Amount that already taken by the wallet.
+    refund_taken: AmountString;
+  }
+  interface StatusGotoResponse {
+    // 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 {
+    // URI that the wallet must process to complete the payment.
+    taler_pay_uri: string;
+
+    // Status URL, can be used as a redirect target for the browser
+    // to show the order QR code / trigger the wallet.
+    fulfillment_url?: string;
+
+    // Alternative order ID which was paid for already in the same session.
+    // Only given if the same product was purchased before in the same session.
+    already_paid_order_id?: string;
+  }
+
+  interface PaidRefundStatusResponse {
+
+    // Text to be shown to the point-of-sale staff as a proof of
+    // payment (present only if re-usable OTP algorithm is used).
+    pos_confirmation?: string;
+
+    // True if the order has been subjected to
+    // refunds. False if it was simply paid.
+    refunded: boolean;
+  }
+  interface PaidRequest {
+    // Signature on TALER_PaymentResponsePS with the public
+    // key of the merchant instance.
+    sig: EddsaSignature;
+
+    // Hash of the order's contract terms (this is used to authenticate the
+    // wallet/customer and to enable signature verification without
+    // database access).
+    h_contract: HashCode;
+
+    // Hash over custom inputs from the wallet for the contract.
+    wallet_data_hash?: HashCode;
+
+    // Session id for which the payment is proven.
+    session_id: string;
+  }
+
+  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;
+
+    // List of coins the wallet would like to see refunds for.
+    // (Should be limited to the coins for which the original
+    // payment succeeded, as far as the wallet knows.)
+    coins: AbortingCoin[];
+  }
+  interface AbortingCoin {
+    // Public key of a coin for which the wallet is requesting an 
abort-related refund.
+    coin_pub: EddsaPublicKey;
+
+    // The amount to be refunded (matches the original contribution)
+    contribution: AmountString;
+
+    // URL of the exchange this coin was withdrawn from.
+    exchange_url: string;
+  }
+  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 =
+    | MerchantAbortPayRefundSuccessStatus
+    | MerchantAbortPayRefundFailureStatus;
+  // Details about why a refund failed.
+  interface MerchantAbortPayRefundFailureStatus {
+    // Used as tag for the sum type RefundStatus sum type.
+    type: "failure";
+
+    // HTTP status of the exchange request, must NOT be 200.
+    exchange_status: Integer;
+
+    // Taler error code from the exchange reply, if available.
+    exchange_code?: Integer;
+
+    // If available, HTTP reply from the exchange.
+    exchange_reply?: Object;
+  }
+  // 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 {
+    // Used as tag for the sum type MerchantCoinRefundStatus sum type.
+    type: "success";
+
+    // HTTP status of the exchange request, 200 (integer) required for refund 
confirmations.
+    exchange_status: 200;
+
+    // The EdDSA :ref:signature (binary-only) with purpose
+    // TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND using a current signing key of 
the
+    // exchange affirming the successful refund.
+    exchange_sig: EddsaSignature;
+
+    // Public EdDSA key of the exchange that was used to generate the 
signature.
+    // Should match one of the exchange's signing keys from /keys.  It is given
+    // explicitly as the client might otherwise be confused by clock skew as to
+    // which signing key was used.
+    exchange_pub: EddsaPublicKey;
+  }
+
+  interface WalletRefundRequest {
+    // Hash of the order's contract terms (this is used to authenticate the
+    // wallet/customer).
+    h_contract: HashCode;
+  }
+  interface WalletRefundResponse {
+    // Amount that was refunded in total.
+    refund_amount: AmountString;
+
+    // Successful refunds for this payment, empty array for none.
+    refunds: MerchantCoinRefundStatus[];
+
+    // Public key of the merchant.
+    merchant_pub: EddsaPublicKey;
+
+  }
+  type MerchantCoinRefundStatus =
+    | MerchantCoinRefundSuccessStatus
+    | MerchantCoinRefundFailureStatus;
+  // Details about why a refund failed.
+  interface MerchantCoinRefundFailureStatus {
+    // Used as tag for the sum type RefundStatus sum type.
+    type: "failure";
+
+    // HTTP status of the exchange request, must NOT be 200.
+    exchange_status: Integer;
+
+    // Taler error code from the exchange reply, if available.
+    exchange_code?: Integer;
+
+    // If available, HTTP reply from the exchange.
+    exchange_reply?: Object;
+
+    // Refund transaction ID.
+    rtransaction_id: Integer;
+
+    // Public key of a coin that was refunded.
+    coin_pub: EddsaPublicKey;
+
+    // Amount that was refunded, including refund fee charged by the exchange
+    // to the customer.
+    refund_amount: AmountString;
+
+    // Timestamp when the merchant approved the refund.
+    // Useful for grouping refunds.
+    execution_time: Timestamp;
+  }
+  // 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 {
+    // Used as tag for the sum type MerchantCoinRefundStatus sum type.
+    type: "success";
+
+    // HTTP status of the exchange request, 200 (integer) required for refund 
confirmations.
+    exchange_status: 200;
+
+    // The EdDSA :ref:signature (binary-only) with purpose
+    // TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND using a current signing key of 
the
+    // exchange affirming the successful refund.
+    exchange_sig: EddsaSignature;
+
+    // Public EdDSA key of the exchange that was used to generate the 
signature.
+    // Should match one of the exchange's signing keys from /keys.  It is given
+    // explicitly as the client might otherwise be confused by clock skew as to
+    // which signing key was used.
+    exchange_pub: EddsaPublicKey;
+
+    // Refund transaction ID.
+    rtransaction_id: Integer;
+
+    // Public key of a coin that was refunded.
+    coin_pub: EddsaPublicKey;
+
+    // Amount that was refunded, including refund fee charged by the exchange
+    // to the customer.
+    refund_amount: AmountString;
+
+    // Timestamp when the merchant approved the refund.
+    // Useful for grouping refunds.
+    execution_time: Timestamp;
+  }
+
+  interface RewardInformation {
+
+    // Exchange from which the reward will be withdrawn. Needed by the
+    // wallet to determine denominations, fees, etc.
+    exchange_url: string;
+
+    // URL where to go after obtaining the reward.
+    next_url: string;
+
+    // (Remaining) amount of the reward (including fees).
+    reward_amount: AmountString;
+
+    // Timestamp indicating when the reward is set to expire (may be in the 
past).
+    // Note that rewards that have expired MAY also result in a 404 response.
+    expiration: Timestamp;
+  }
+
+  interface RewardPickupRequest {
+
+    // List of planchets the wallet wants to use for the reward.
+    planchets: PlanchetDetail[];
+  }
+  interface PlanchetDetail {
+    // Hash of the denomination's public key (hashed to reduce
+    // bandwidth consumption).
+    denom_pub_hash: HashCode;
+
+    // Coin's blinded public key.
+    coin_ev: CoinEnvelope;
+  }
+  interface RewardResponse {
+
+    // Blind RSA signatures over the planchets.
+    // The order of the signatures matches the planchets list.
+    blind_sigs: BlindSignature[];
+  }
+  interface BlindSignature {
+
+    // The (blind) RSA signature. Still needs to be unblinded.
+    blind_sig: BlindedRsaSignature;
+  }
+
+  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;
+
+    // Merchant name corresponding to this instance.
+    name: string;
+
+    // Type of the user (business or individual).
+    // Defaults to 'business'. Should become mandatory field
+    // in the future, left as optional for API compatibility for now.
+    user_type?: string;
+
+    // Merchant email for customer contact.
+    email?: string;
+
+    // Merchant public website.
+    website?: string;
+
+    // Merchant logo.
+    logo?: ImageDataUrl;
+
+    // Authentication settings for this instance
+    auth: InstanceAuthConfigurationMessage;
+
+    // The merchant's physical address (to be put into contracts).
+    address: Location;
+
+    // The jurisdiction under which the merchant conducts its business
+    // (to be put into contracts).
+    jurisdiction: Location;
+
+    // Use STEFAN curves to determine default fees?
+    // If false, no fees are allowed by default.
+    // Can always be overridden by the frontend on a per-order basis.
+    use_stefan: boolean;
+
+    // If the frontend does NOT specify an execution date, how long should
+    // we tell the exchange to wait to aggregate transactions before
+    // executing the wire transfer?  This delay is added to the current
+    // time when we generate the advisory execution time for the exchange.
+    default_wire_transfer_delay: RelativeTime;
+
+    // If the frontend does NOT specify a payment deadline, how long should
+    // offers we make be valid by default?
+    default_pay_delay: RelativeTime;
+
+  }
+
+  interface InstanceAuthConfigurationMessage {
+    // Type of authentication.
+    // "external":  The mechant backend does not do
+    //   any authentication checks.  Instead an API
+    //   gateway must do the authentication.
+    // "token": The merchant checks an auth token.
+    //   See "token" for details.
+    method: "external" | "token";
+
+    // For method "token", this field is mandatory.
+    // The token MUST begin with the string "secret-token:".
+    // After the auth token has been set (with method "token"),
+    // the value must be provided in a "Authorization: Bearer $token"
+    // header.
+    token?: string;
+
+  }
+
+  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 {
+
+    // Merchant name corresponding to this instance.
+    name: string;
+
+    // Type of the user (business or individual).
+    // Defaults to 'business'. Should become mandatory field
+    // in the future, left as optional for API compatibility for now.
+    user_type?: string;
+
+    // Merchant email for customer contact.
+    email?: string;
+
+    // Merchant public website.
+    website?: string;
+
+    // Merchant logo.
+    logo?: ImageDataUrl;
+
+    // The merchant's physical address (to be put into contracts).
+    address: Location;
+
+    // The jurisdiction under which the merchant conducts its business
+    // (to be put into contracts).
+    jurisdiction: Location;
+
+    // Use STEFAN curves to determine default fees?
+    // If false, no fees are allowed by default.
+    // Can always be overridden by the frontend on a per-order basis.
+    use_stefan: boolean;
+
+    // If the frontend does NOT specify an execution date, how long should
+    // we tell the exchange to wait to aggregate transactions before
+    // executing the wire transfer?  This delay is added to the current
+    // time when we generate the advisory execution time for the exchange.
+    default_wire_transfer_delay: RelativeTime;
+
+    // If the frontend does NOT specify a payment deadline, how long should
+    // offers we make be valid by default?
+    default_pay_delay: RelativeTime;
+
+  }
+
+  interface InstancesResponse {
+    // List of instances that are present in the backend (see Instance).
+    instances: Instance[];
+  }
+
+  interface Instance {
+    // Merchant name corresponding to this instance.
+    name: string;
+
+    // Type of the user ("business" or "individual").
+    user_type: string;
+
+    // Merchant public website.
+    website?: string;
+
+    // Merchant logo.
+    logo?: ImageDataUrl;
+
+    // Merchant instance this response is about ($INSTANCE).
+    id: string;
+
+    // Public key of the merchant/instance, in Crockford Base32 encoding.
+    merchant_pub: EddsaPublicKey;
+
+    // List of the payment targets supported by this instance. Clients can
+    // specify the desired payment target in /order requests.  Note that
+    // front-ends do not have to support wallets selecting payment targets.
+    payment_targets: string[];
+
+    // Has this instance been deleted (but not purged)?
+    deleted: boolean;
+  }
+
+  interface QueryInstancesResponse {
+
+    // Merchant name corresponding to this instance.
+    name: string;
+
+    // Type of the user ("business" or "individual").
+    user_type: string;
+
+    // Merchant email for customer contact.
+    email?: string;
+
+    // Merchant public website.
+    website?: string;
+
+    // Merchant logo.
+    logo?: ImageDataUrl;
+
+    // Public key of the merchant/instance, in Crockford Base32 encoding.
+    merchant_pub: EddsaPublicKey;
+
+    // The merchant's physical address (to be put into contracts).
+    address: Location;
+
+    // The jurisdiction under which the merchant conducts its business
+    // (to be put into contracts).
+    jurisdiction: Location;
+
+    // Use STEFAN curves to determine default fees?
+    // If false, no fees are allowed by default.
+    // Can always be overridden by the frontend on a per-order basis.
+    use_stefan: boolean;
+
+    // If the frontend does NOT specify an execution date, how long should
+    // we tell the exchange to wait to aggregate transactions before
+    // executing the wire transfer?  This delay is added to the current
+    // time when we generate the advisory execution time for the exchange.
+    default_wire_transfer_delay: RelativeTime;
+
+    // If the frontend does NOT specify a payment deadline, how long should
+    // offers we make be valid by default?
+    default_pay_delay: RelativeTime;
+
+    // Authentication configuration.
+    // Does not contain the token when token auth is configured.
+    auth: {
+      type: "external" | "token";
+    };
+
+  }
+
+  interface AccountKycRedirects {
+
+    // Array of pending KYCs.
+    pending_kycs: MerchantAccountKycRedirect[];
+
+    // Array of exchanges with no reply.
+    timeout_kycs: ExchangeKycTimeout[];
+  }
+
+  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).
+    // Optional, missing if the account is blocked
+    // due to AML and not due to KYC.
+    kyc_url?: string;
+
+    // AML status of the account.
+    aml_status: Integer;
+
+    // Base URL of the exchange this is about.
+    exchange_url: string;
+
+    // Our bank wire account this is about.
+    payto_uri: PaytoString;
+
+  }
+
+  interface ExchangeKycTimeout {
+
+    // Base URL of the exchange this is about.
+    exchange_url: string;
+
+    // Numeric error code indicating errors the exchange
+    // returned, or TALER_EC_INVALID for none.
+    exchange_code: number;
+
+    // HTTP status code returned by the exchange when we asked for
+    // information about the KYC status.
+    // 0 if there was no response at all.
+    exchange_http_status: number;
+
+  }
+
+  interface AccountAddDetails {
+
+    // payto:// URI of the account.
+    payto_uri: PaytoString;
+
+    // URL from where the merchant can download information
+    // about incoming wire transfers to this account.
+    credit_facade_url?: string;
+
+    // Credentials to use when accessing the credit facade.
+    // Never returned on a GET (as this may be somewhat
+    // sensitive data). Can be set in POST
+    // or PATCH requests to update (or delete) credentials.
+    // To really delete credentials, set them to the type: "none".
+    credit_facade_credentials?: FacadeCredentials;
+
+  }
+
+  type FacadeCredentials =
+    | NoFacadeCredentials
+    | BasicAuthFacadeCredentials;
+  interface NoFacadeCredentials {
+    type: "none";
+  };
+  interface BasicAuthFacadeCredentials {
+    type: "basic";
+
+    // Username to use to authenticate
+    username: string;
+
+    // Password to use to authenticate
+    password: string;
+  };
+  interface AccountAddResponse {
+
+    // Hash over the wire details (including over the salt).
+    h_wire: HashCode;
+
+    // Salt used to compute h_wire.
+    salt: HashCode;
+
+  }
+
+  interface AccountPatchDetails {
+
+    // URL from where the merchant can download information
+    // about incoming wire transfers to this account.
+    credit_facade_url?: string;
+
+    // Credentials to use when accessing the credit facade.
+    // Never returned on a GET (as this may be somewhat
+    // sensitive data). Can be set in POST
+    // or PATCH requests to update (or delete) credentials.
+    // To really delete credentials, set them to the type: "none".
+    // If the argument is omitted, the old credentials
+    // are simply preserved.
+    credit_facade_credentials?: FacadeCredentials;
+  }
+
+  interface AccountsSummaryResponse {
+
+    // List of accounts that are known for the instance.
+    accounts: BankAccountEntry[];
+  }
+  interface BankAccountEntry {
+
+    // payto:// URI of the account.
+    payto_uri: PaytoString;
+
+    // Hash over the wire details (including over the salt).
+    h_wire: HashCode;
+
+    // Salt used to compute h_wire.
+    salt: HashCode;
+
+    // URL from where the merchant can download information
+    // about incoming wire transfers to this account.
+    credit_facade_url?: string;
+
+    // true if this account is active,
+    // false if it is historic.
+    active: boolean;
+  }
+
+  interface ProductAddDetail {
+
+    // Product ID to use.
+    product_id: string;
+
+    // Human-readable product description.
+    description: string;
+
+    // Map from IETF BCP 47 language tags to localized descriptions.
+    description_i18n?: { [lang_tag: string]: string };
+
+    // Unit in which the product is measured (liters, kilograms, packages, 
etc.).
+    unit: string;
+
+    // The price for one unit of the product. Zero is used
+    // to imply that this product is not sold separately, or
+    // that the price is not fixed, and must be supplied by the
+    // front-end.  If non-zero, this price MUST include applicable
+    // taxes.
+    price: AmountString;
+
+    // An optional base64-encoded product image.
+    image?: ImageDataUrl;
+
+    // A list of taxes paid by the merchant for one unit of this product.
+    taxes?: Tax[];
+
+    // Number of units of the product in stock in sum in total,
+    // including all existing sales ever. Given in product-specific
+    // units.
+    // A value of -1 indicates "infinite" (i.e. for "electronic" books).
+    total_stock: Integer;
+
+    // Identifies where the product is in stock.
+    address?: Location;
+
+    // Identifies when we expect the next restocking to happen.
+    next_restock?: Timestamp;
+
+    // Minimum age buyer must have (in years). Default is 0.
+    minimum_age?: Integer;
+
+  }
+
+  interface ProductPatchDetail {
+
+    // Human-readable product description.
+    description: string;
+
+    // Map from IETF BCP 47 language tags to localized descriptions.
+    description_i18n?: { [lang_tag: string]: string };
+
+    // Unit in which the product is measured (liters, kilograms, packages, 
etc.).
+    unit: string;
+
+    // The price for one unit of the product. Zero is used
+    // to imply that this product is not sold separately, or
+    // that the price is not fixed, and must be supplied by the
+    // front-end.  If non-zero, this price MUST include applicable
+    // taxes.
+    price: AmountString;
+
+    // An optional base64-encoded product image.
+    image?: ImageDataUrl;
+
+    // A list of taxes paid by the merchant for one unit of this product.
+    taxes?: Tax[];
+
+    // Number of units of the product in stock in sum in total,
+    // including all existing sales ever. Given in product-specific
+    // units.
+    // A value of -1 indicates "infinite" (i.e. for "electronic" books).
+    total_stock: Integer;
+
+    // Number of units of the product that were lost (spoiled, stolen, etc.).
+    total_lost?: Integer;
+
+    // Identifies where the product is in stock.
+    address?: Location;
+
+    // Identifies when we expect the next restocking to happen.
+    next_restock?: Timestamp;
+
+    // Minimum age buyer must have (in years). Default is 0.
+    minimum_age?: Integer;
+
+  }
+
+  interface InventorySummaryResponse {
+    // List of products that are present in the inventory.
+    products: InventoryEntry[];
+  }
+
+
+  interface InventoryEntry {
+    // Product identifier, as found in the product.
+    product_id: string;
+
+  }
+
+  interface ProductDetail {
+
+    // Human-readable product description.
+    description: string;
+
+    // Map from IETF BCP 47 language tags to localized descriptions.
+    description_i18n: { [lang_tag: string]: string };
+
+    // Unit in which the product is measured (liters, kilograms, packages, 
etc.).
+    unit: string;
+
+    // The price for one unit of the product. Zero is used
+    // to imply that this product is not sold separately, or
+    // that the price is not fixed, and must be supplied by the
+    // front-end.  If non-zero, this price MUST include applicable
+    // taxes.
+    price: AmountString;
+
+    // An optional base64-encoded product image.
+    image: ImageDataUrl;
+
+    // A list of taxes paid by the merchant for one unit of this product.
+    taxes: Tax[];
+
+    // Number of units of the product in stock in sum in total,
+    // including all existing sales ever. Given in product-specific
+    // units.
+    // A value of -1 indicates "infinite" (i.e. for "electronic" books).
+    total_stock: Integer;
+
+    // Number of units of the product that have already been sold.
+    total_sold: Integer;
+
+    // Number of units of the product that were lost (spoiled, stolen, etc.).
+    total_lost: Integer;
+
+    // Identifies where the product is in stock.
+    address: Location;
+
+    // Identifies when we expect the next restocking to happen.
+    next_restock?: Timestamp;
+
+    // Minimum age buyer must have (in years).
+    minimum_age?: Integer;
+
+  }
+  interface LockRequest {
+
+    // UUID that identifies the frontend performing the lock
+    // Must be unique for the lifetime of the lock.
+    lock_uuid: string;
+
+    // How long does the frontend intend to hold the lock?
+    duration: RelativeTime;
+
+    // How many units should be locked?
+    quantity: Integer;
+
+  }
+
+  interface PostOrderRequest {
+    // The order must at least contain the minimal
+    // order detail, but can override all.
+    order: Order;
+
+    // If set, the backend will then set the refund deadline to the current
+    // time plus the specified delay.  If it's not set, refunds will not be
+    // possible.
+    refund_delay?: RelativeTime;
+
+    // Specifies the payment target preferred by the client. Can be used
+    // to select among the various (active) wire methods supported by the 
instance.
+    payment_target?: string;
+
+    // Specifies that some products are to be included in the
+    // order from the inventory.  For these inventory management
+    // is performed (so the products must be in stock) and
+    // details are completed from the product data of the backend.
+    inventory_products?: MinimalInventoryProduct[];
+
+    // Specifies a lock identifier that was used to
+    // lock a product in the inventory.  Only useful if
+    // inventory_products is set.  Used in case a frontend
+    // reserved quantities of the individual products while
+    // the shopping cart was being built.  Multiple UUIDs can
+    // be used in case different UUIDs were used for different
+    // products (i.e. in case the user started with multiple
+    // shopping sessions that were combined during checkout).
+    lock_uuids: string[];
+
+    // Should a token for claiming the order be generated?
+    // False can make sense if the ORDER_ID is sufficiently
+    // high entropy to prevent adversarial claims (like it is
+    // if the backend auto-generates one). Default is 'true'.
+    create_token?: boolean;
+
+    // OTP device ID to associate with the order.
+    // This parameter is optional.
+    otp_id?: string;
+  }
+
+  type Order = MinimalOrderDetail | ContractTerms;
+
+  interface MinimalOrderDetail {
+    // Amount to be paid by the customer.
+    amount: AmountString;
+
+    // Short summary of the order.
+    summary: string;
+
+    // See documentation of fulfillment_url in ContractTerms.
+    // Either fulfillment_url or fulfillment_message must be specified.
+    fulfillment_url?: string;
+
+    // See documentation of fulfillment_message in ContractTerms.
+    // Either fulfillment_url or fulfillment_message must be specified.
+    fulfillment_message?: string;
+  }
+
+  interface MinimalInventoryProduct {
+    // Which product is requested (here mandatory!).
+    product_id: string;
+
+    // How many units of the product are requested.
+    quantity: Integer;
+  }
+
+  interface PostOrderResponse {
+    // Order ID of the response that was just created.
+    order_id: string;
+
+    // Token that authorizes the wallet to claim the order.
+    // Provided only if "create_token" was set to 'true'
+    // in the request.
+    token?: ClaimToken;
+  }
+  interface OutOfStockResponse {
+
+    // Product ID of an out-of-stock item.
+    product_id: string;
+
+    // Requested quantity.
+    requested_quantity: Integer;
+
+    // Available quantity (must be below requested_quantity).
+    available_quantity: Integer;
+
+    // When do we expect the product to be again in stock?
+    // Optional, not given if unknown.
+    restock_expected?: Timestamp;
+  }
+
+  interface OrderHistory {
+    // Timestamp-sorted array of all orders matching the query.
+    // The order of the sorting depends on the sign of delta.
+    orders: OrderHistoryEntry[];
+  }
+  interface OrderHistoryEntry {
+
+    // Order ID of the transaction related to this entry.
+    order_id: string;
+
+    // Row ID of the order in the database.
+    row_id: number;
+
+    // When the order was created.
+    timestamp: Timestamp;
+
+    // The amount of money the order is for.
+    amount: AmountString;
+
+    // The summary of the order.
+    summary: string;
+
+    // Whether some part of the order is refundable,
+    // that is the refund deadline has not yet expired
+    // and the total amount refunded so far is below
+    // the value of the original transaction.
+    refundable: boolean;
+
+    // Whether the order has been paid or not.
+    paid: boolean;
+  }
+
+  type MerchantOrderStatusResponse = CheckPaymentPaidResponse |
+    CheckPaymentClaimedResponse |
+    CheckPaymentUnpaidResponse;
+  interface CheckPaymentPaidResponse {
+    // The customer paid for this contract.
+    order_status: "paid";
+
+    // Was the payment refunded (even partially)?
+    refunded: boolean;
+
+    // True if there are any approved refunds that the wallet has
+    // not yet obtained.
+    refund_pending: boolean;
+
+    // Did the exchange wire us the funds?
+    wired: boolean;
+
+    // Total amount the exchange deposited into our bank account
+    // for this contract, excluding fees.
+    deposit_total: AmountString;
+
+    // Numeric error code indicating errors the exchange
+    // encountered tracking the wire transfer for this purchase (before
+    // we even got to specific coin issues).
+    // 0 if there were no issues.
+    exchange_code: number;
+
+    // HTTP status code returned by the exchange when we asked for
+    // information to track the wire transfer for this purchase.
+    // 0 if there were no issues.
+    exchange_http_status: number;
+
+    // Total amount that was refunded, 0 if refunded is false.
+    refund_amount: AmountString;
+
+    // Contract terms.
+    contract_terms: ContractTerms;
+
+    // The wire transfer status from the exchange for this order if
+    // available, otherwise empty array.
+    wire_details: TransactionWireTransfer[];
+
+    // Reports about trouble obtaining wire transfer details,
+    // empty array if no trouble were encountered.
+    wire_reports: TransactionWireReport[];
+
+    // The refund details for this order.  One entry per
+    // refunded coin; empty array if there are no refunds.
+    refund_details: RefundDetails[];
+
+    // Status URL, can be used as a redirect target for the browser
+    // to show the order QR code / trigger the wallet.
+    order_status_url: string;
+  }
+  interface CheckPaymentClaimedResponse {
+    // A wallet claimed the order, but did not yet pay for the contract.
+    order_status: "claimed";
+
+    // Contract terms.
+    contract_terms: ContractTerms;
+
+  }
+  interface CheckPaymentUnpaidResponse {
+    // The order was neither claimed nor paid.
+    order_status: "unpaid";
+
+    // URI that the wallet must process to complete the payment.
+    taler_pay_uri: string;
+
+    // when was the order created
+    creation_time: Timestamp;
+
+    // Order summary text.
+    summary: string;
+
+    // Total amount of the order (to be paid by the customer).
+    total_amount: AmountString;
+
+    // Alternative order ID which was paid for already in the same session.
+    // Only given if the same product was purchased before in the same session.
+    already_paid_order_id?: string;
+
+    // Fulfillment URL of an already paid order. Only given if under this
+    // session an already paid order with a fulfillment URL exists.
+    already_paid_fulfillment_url?: string;
+
+    // Status URL, can be used as a redirect target for the browser
+    // to show the order QR code / trigger the wallet.
+    order_status_url: string;
+
+    // We do we NOT return the contract terms here because they may not
+    // exist in case the wallet did not yet claim them.
+  }
+  interface RefundDetails {
+    // Reason given for the refund.
+    reason: string;
+
+    // Set to true if a refund is still available for the wallet for this 
payment.
+    pending: boolean;
+
+    // When was the refund approved.
+    timestamp: Timestamp;
+
+    // Total amount that was refunded (minus a refund fee).
+    amount: AmountString;
+  }
+  interface TransactionWireTransfer {
+    // Responsible exchange.
+    exchange_url: string;
+
+    // 32-byte wire transfer identifier.
+    wtid: Base32;
+
+    // Execution time of the wire transfer.
+    execution_time: Timestamp;
+
+    // Total amount that has been wire transferred
+    // to the merchant.
+    amount: AmountString;
+
+    // Was this transfer confirmed by the merchant via the
+    // POST /transfers API, or is it merely claimed by the exchange?
+    confirmed: boolean;
+  }
+  interface TransactionWireReport {
+    // Numerical error code.
+    code: number;
+
+    // Human-readable error description.
+    hint: string;
+
+    // Numerical error code from the exchange.
+    exchange_code: number;
+
+    // HTTP status code received from the exchange.
+    exchange_http_status: number;
+
+    // Public key of the coin for which we got the exchange error.
+    coin_pub: CoinPublicKey;
+  }
+
+  interface ForgetRequest {
+
+    // Array of valid JSON paths to forgettable fields in the order's
+    // contract terms.
+    fields: string[];
+  }
+
+  interface RefundRequest {
+    // Amount to be refunded.
+    refund: AmountString;
+
+    // Human-readable refund justification.
+    reason: string;
+  }
+  interface MerchantRefundResponse {
+
+    // URL (handled by the backend) that the wallet should access to
+    // trigger refund processing.
+    // taler://refund/...
+    taler_refund_uri: string;
+
+    // Contract hash that a client may need to authenticate an
+    // HTTP request to obtain the above URI in a wallet-friendly way.
+    h_contract: HashCode;
+  }
+
+  interface TransferInformation {
+    // How much was wired to the merchant (minus fees).
+    credit_amount: AmountString;
+
+    // Raw wire transfer identifier identifying the wire transfer (a 
base32-encoded value).
+    wtid: WireTransferIdentifierRawP;
+
+    // Target account that received the wire transfer.
+    payto_uri: PaytoString;
+
+    // Base URL of the exchange that made the wire transfer.
+    exchange_url: string;
+  }
+
+  interface TransferList {
+    // List of all the transfers that fit the filter that we know.
+    transfers: TransferDetails[];
+  }
+  interface TransferDetails {
+    // How much was wired to the merchant (minus fees).
+    credit_amount: AmountString;
+
+    // Raw wire transfer identifier identifying the wire transfer (a 
base32-encoded value).
+    wtid: WireTransferIdentifierRawP;
+
+    // Target account that received the wire transfer.
+    payto_uri: PaytoString;
+
+    // Base URL of the exchange that made the wire transfer.
+    exchange_url: string;
+
+    // Serial number identifying the transfer in the merchant backend.
+    // Used for filtering via offset.
+    transfer_serial_id: number;
+
+    // Time of the execution of the wire transfer by the exchange, according 
to the exchange
+    // Only provided if we did get an answer from the exchange.
+    execution_time?: Timestamp;
+
+    // True if we checked the exchange's answer and are happy with it.
+    // False if we have an answer and are unhappy, missing if we
+    // do not have an answer from the exchange.
+    verified?: boolean;
+
+    // True if the merchant uses the POST /transfers API to confirm
+    // that this wire transfer took place (and it is thus not
+    // something merely claimed by the exchange).
+    confirmed?: boolean;
+  }
+
+  interface ReserveCreateRequest {
+    // Amount that the merchant promises to put into the reserve.
+    initial_balance: AmountString;
+
+    // Exchange the merchant intends to use for rewards.
+    exchange_url: string;
+
+    // Desired wire method, for example "iban" or "x-taler-bank".
+    wire_method: string;
+  }
+  interface ReserveCreateConfirmation {
+    // Public key identifying the reserve.
+    reserve_pub: EddsaPublicKey;
+
+    // Wire accounts of the exchange where to transfer the funds.
+    accounts: TalerExchangeApi.WireAccount[];
+  }
+
+  interface RewardReserveStatus {
+    // Array of all known reserves (possibly empty!).
+    reserves: ReserveStatusEntry[];
+  }
+  interface ReserveStatusEntry {
+    // Public key of the reserve.
+    reserve_pub: EddsaPublicKey;
+
+    // Timestamp when it was established.
+    creation_time: Timestamp;
+
+    // Timestamp when it expires.
+    expiration_time: Timestamp;
+
+    // Initial amount as per reserve creation call.
+    merchant_initial_amount: AmountString;
+
+    // Initial amount as per exchange, 0 if exchange did
+    // not confirm reserve creation yet.
+    exchange_initial_amount: AmountString;
+
+    // Amount picked up so far.
+    pickup_amount: AmountString;
+
+    // Amount approved for rewards that exceeds the pickup_amount.
+    committed_amount: AmountString;
+
+    // Is this reserve active (false if it was deleted but not purged)?
+    active: boolean;
+  }
+
+  interface ReserveDetail {
+    // Timestamp when it was established.
+    creation_time: Timestamp;
+
+    // Timestamp when it expires.
+    expiration_time: Timestamp;
+
+    // Initial amount as per reserve creation call.
+    merchant_initial_amount: AmountString;
+
+    // Initial amount as per exchange, 0 if exchange did
+    // not confirm reserve creation yet.
+    exchange_initial_amount: AmountString;
+
+    // Amount picked up so far.
+    pickup_amount: AmountString;
+
+    // Amount approved for rewards that exceeds the pickup_amount.
+    committed_amount: AmountString;
+
+    // Array of all rewards created by this reserves (possibly empty!).
+    // Only present if asked for explicitly.
+    rewards?: RewardStatusEntry[];
+
+    // Is this reserve active (false if it was deleted but not purged)?
+    active: boolean;
+
+    // Array of wire accounts of the exchange that could
+    // be used to fill the reserve, can be NULL
+    // if the reserve is inactive or was already filled
+    accounts?: TalerExchangeApi.WireAccount[];
+
+    // URL of the exchange hosting the reserve,
+    // NULL if the reserve is inactive
+    exchange_url: string;
+  }
+  interface RewardStatusEntry {
+
+    // Unique identifier for the reward.
+    reward_id: HashCode;
+
+    // Total amount of the reward that can be withdrawn.
+    total_amount: AmountString;
+
+    // Human-readable reason for why the reward was granted.
+    reason: string;
+  }
+
+  interface RewardCreateRequest {
+    // Amount that the customer should be rewarded.
+    amount: AmountString;
+
+    // Justification for giving the reward.
+    justification: string;
+
+    // URL that the user should be directed to after receiving the reward,
+    // will be included in the reward_token.
+    next_url: string;
+  }
+  interface RewardCreateConfirmation {
+    // Unique reward identifier for the reward that was created.
+    reward_id: HashCode;
+
+    // taler://reward URI for the reward.
+    taler_reward_uri: string;
+
+    // URL that will directly trigger processing
+    // the reward when the browser is redirected to it.
+    reward_status_url: string;
+
+    // When does the reward expire?
+    reward_expiration: Timestamp;
+  }
+
+  interface RewardDetails {
+    // Amount that we authorized for this reward.
+    total_authorized: AmountString;
+
+    // Amount that was picked up by the user already.
+    total_picked_up: AmountString;
+
+    // Human-readable reason given when authorizing the reward.
+    reason: string;
+
+    // Timestamp indicating when the reward is set to expire (may be in the 
past).
+    expiration: Timestamp;
+
+    // Reserve public key from which the reward is funded.
+    reserve_pub: EddsaPublicKey;
+
+    // Array showing the pickup operations of the wallet (possibly empty!).
+    // Only present if asked for explicitly.
+    pickups?: PickupDetail[];
+  }
+  interface PickupDetail {
+    // Unique identifier for the pickup operation.
+    pickup_id: HashCode;
+
+    // Number of planchets involved.
+    num_planchets: Integer;
+
+    // Total amount requested for this pickup_id.
+    requested_amount: AmountString;
+  }
+
+  interface RewardsResponse {
+
+    // List of rewards that are present in the backend.
+    rewards: Reward[];
+  }
+  interface Reward {
+
+    // ID of the reward in the backend database.
+    row_id: number;
+
+    // Unique identifier for the reward.
+    reward_id: HashCode;
+
+    // (Remaining) amount of the reward (including fees).
+    reward_amount: AmountString;
+  }
+
+  interface OtpDeviceAddDetails {
+
+    // Device ID to use.
+    otp_device_id: string;
+
+    // Human-readable description for the device.
+    otp_device_description: string;
+
+    // A base64-encoded key
+    otp_key: string;
+
+    // Algorithm for computing the POS confirmation.
+    otp_algorithm: Integer;
+
+    // Counter for counter-based OTP devices.
+    otp_ctr?: Integer;
+  }
+
+  interface OtpDevicePatchDetails {
+
+    // Human-readable description for the device.
+    otp_device_description: string;
+
+    // A base64-encoded key
+    otp_key: string;
+
+    // Algorithm for computing the POS confirmation.
+    otp_algorithm: Integer;
+
+    // Counter for counter-based OTP devices.
+    otp_ctr?: Integer;
+  }
+
+  interface OtpDeviceSummaryResponse {
+
+    // Array of devices that are present in our backend.
+    otp_devices: OtpDeviceEntry[];
+  }
+  interface OtpDeviceEntry {
+
+    // Device identifier.
+    otp_device_id: string;
+
+    // Human-readable description for the device.
+    device_description: string;
+  }
+
+  interface OtpDeviceDetails {
+
+    // Human-readable description for the device.
+    device_description: string;
+
+    // Algorithm for computing the POS confirmation.
+    otp_algorithm: Integer;
+
+    // Counter for counter-based OTP devices.
+    otp_ctr?: Integer;
+
+  }
+  interface TemplateAddDetails {
+
+    // Template ID to use.
+    template_id: string;
+
+    // Human-readable description for the template.
+    template_description: string;
+
+    // OTP device ID.
+    // This parameter is optional.
+    otp_id?: string;
+
+    // Additional information in a separate template.
+    template_contract: TemplateContractDetails;
+  }
+  interface TemplateContractDetails {
+
+    // Human-readable summary for the template.
+    summary?: string;
+
+    // Required currency for payments to the template.
+    // The user may specify any amount, but it must be
+    // in this currency.
+    // This parameter is optional and should not be present
+    // if "amount" is given.
+    currency?: string;
+
+    // The price is imposed by the merchant and cannot be changed by the 
customer.
+    // This parameter is optional.
+    amount?: AmountString;
+
+    // Minimum age buyer must have (in years). Default is 0.
+    minimum_age: Integer;
+
+    // The time the customer need to pay before his order will be deleted.
+    // It is deleted if the customer did not pay and if the duration is over.
+    pay_duration: RelativeTime;
+
+  }
+  interface TemplatePatchDetails {
+
+    // Human-readable description for the template.
+    template_description: string;
+
+    // OTP device ID.
+    // This parameter is optional.
+    otp_id?: string;
+
+    // Additional information in a separate template.
+    template_contract: TemplateContractDetails;
+
+  }
+
+  interface TemplateSummaryResponse {
+
+    // List of templates that are present in our backend.
+    templates_list: TemplateEntry[];
+  }
+
+
+  interface TemplateEntry {
+
+    // Template identifier, as found in the template.
+    template_id: string;
+
+    // Human-readable description for the template.
+    template_description: string;
+
+  }
+  interface TemplateDetails {
+
+    // Human-readable description for the template.
+    template_description: string;
+
+    // OTP device ID.
+    // This parameter is optional.
+    otp_id?: string;
+
+    // Additional information in a separate template.
+    template_contract: TemplateContractDetails;
+  }
+  interface UsingTemplateDetails {
+
+    // Summary of the template
+    summary?: string;
+
+    // The amount entered by the customer.
+    amount?: AmountString;
+  }
+
+  interface WebhookAddDetails {
+
+    // Webhook ID to use.
+    webhook_id: string;
+
+    // The event of the webhook: why the webhook is used.
+    event_type: string;
+
+    // URL of the webhook where the customer will be redirected.
+    url: string;
+
+    // Method used by the webhook
+    http_method: string;
+
+    // Header template of the webhook
+    header_template?: string;
+
+    // Body template by the webhook
+    body_template?: string;
+
+  }
+
+  interface WebhookPatchDetails {
+
+    // The event of the webhook: why the webhook is used.
+    event_type: string;
+
+    // URL of the webhook where the customer will be redirected.
+    url: string;
+
+    // Method used by the webhook
+    http_method: string;
+
+    // Header template of the webhook
+    header_template?: string;
+
+    // Body template by the webhook
+    body_template?: string;
+
+  }
+
+  interface WebhookSummaryResponse {
+
+    // Return webhooks that are present in our backend.
+    webhooks: WebhookEntry[];
+
+  }
+
+
+  interface WebhookEntry {
+
+    // Webhook identifier, as found in the webhook.
+    webhook_id: string;
+
+    // The event of the webhook: why the webhook is used.
+    event_type: string;
+
+  }
+
+  interface WebhookDetails {
+
+    // The event of the webhook: why the webhook is used.
+    event_type: string;
+
+    // URL of the webhook where the customer will be redirected.
+    url: string;
+
+    // Method used by the webhook
+    http_method: string;
+
+    // Header template of the webhook
+    header_template?: string;
+
+    // Body template by the webhook
+    body_template?: string;
+
+  }
+
+  interface ContractTerms {
+    // Human-readable description of the whole purchase.
+    summary: string;
+
+    // Map from IETF BCP 47 language tags to localized summaries.
+    summary_i18n?: { [lang_tag: string]: string };
+
+    // Unique, free-form identifier for the proposal.
+    // Must be unique within a merchant instance.
+    // For merchants that do not store proposals in their DB
+    // before the customer paid for them, the order_id can be used
+    // by the frontend to restore a proposal from the information
+    // encoded in it (such as a short product identifier and timestamp).
+    order_id: string;
+
+    // Total price for the transaction.
+    // The exchange will subtract deposit fees from that amount
+    // before transferring it to the merchant.
+    amount: AmountString;
+
+    // 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;
+
+    // URL that will show that the order was successful after
+    // it has been paid for.  Optional. When POSTing to the
+    // merchant, the placeholder "${ORDER_ID}" will be
+    // replaced with the actual order ID (useful if the
+    // order ID is generated server-side and needs to be
+    // in the URL).
+    // Note that this placeholder can only be used once.
+    // Either fulfillment_url or fulfillment_message must be specified.
+    fulfillment_url?: string;
+
+    // Message shown to the customer after paying for the order.
+    // Either fulfillment_url or fulfillment_message must be specified.
+    fulfillment_message?: string;
+
+    // Map from IETF BCP 47 language tags to localized fulfillment
+    // messages.
+    fulfillment_message_i18n?: { [lang_tag: string]: string };
+
+    // Maximum total deposit fee accepted by the merchant for this contract.
+    // Overrides defaults of the merchant instance.
+    max_fee: AmountString;
+
+    // List of products that are part of the purchase (see Product).
+    products: Product[];
+
+    // Time when this contract was generated.
+    timestamp: Timestamp;
+
+    // After this deadline has passed, no refunds will be accepted.
+    refund_deadline: Timestamp;
+
+    // After this deadline, the merchant won't accept payments for the 
contract.
+    pay_deadline: Timestamp;
+
+    // Transfer deadline for the exchange.  Must be in the
+    // deposit permissions of coins used to pay for this order.
+    wire_transfer_deadline: Timestamp;
+
+    // Merchant's public key used to sign this proposal; this information
+    // is typically added by the backend. Note that this can be an ephemeral 
key.
+    merchant_pub: EddsaPublicKey;
+
+    // Base URL of the (public!) merchant backend API.
+    // Must be an absolute URL that ends with a slash.
+    merchant_base_url: string;
+
+    // More info about the merchant, see below.
+    merchant: Merchant;
+
+    // The hash of the merchant instance's wire details.
+    h_wire: HashCode;
+
+    // Wire transfer method identifier for the wire method associated with 
h_wire.
+    // The wallet may only select exchanges via a matching auditor if the
+    // exchange also supports this wire method.
+    // The wire transfer fees must be added based on this wire transfer method.
+    wire_method: string;
+
+    // Exchanges that the merchant accepts even if it does not accept any 
auditors that audit them.
+    exchanges: Exchange[];
+
+    // Delivery location for (all!) products.
+    delivery_location?: Location;
+
+    // Time indicating when the order should be delivered.
+    // May be overwritten by individual products.
+    delivery_date?: Timestamp;
+
+    // Nonce generated by the wallet and echoed by the merchant
+    // in this field when the proposal is generated.
+    nonce: string;
+
+    // Specifies for how long the wallet should try to get an
+    // automatic refund for the purchase. If this field is
+    // present, the wallet should wait for a few seconds after
+    // the purchase and then automatically attempt to obtain
+    // a refund.  The wallet should probe until "delay"
+    // after the payment was successful (i.e. via long polling
+    // or via explicit requests with exponential back-off).
+    //
+    // In particular, if the wallet is offline
+    // at that time, it MUST repeat the request until it gets
+    // one response from the merchant after the delay has expired.
+    // If the refund is granted, the wallet MUST automatically
+    // recover the payment.  This is used in case a merchant
+    // knows that it might be unable to satisfy the contract and
+    // desires for the wallet to attempt to get the refund without any
+    // customer interaction.  Note that it is NOT an error if the
+    // merchant does not grant a refund.
+    auto_refund?: RelativeTime;
+
+    // Extra data that is only interpreted by the merchant frontend.
+    // Useful when the merchant needs to store extra information on a
+    // contract without storing it separately in their database.
+    extra?: any;
+  }
+
+  interface Product {
+    // Merchant-internal identifier for the product.
+    product_id?: string;
+
+    // Human-readable product description.
+    description: string;
+
+    // Map from IETF BCP 47 language tags to localized descriptions.
+    description_i18n?: { [lang_tag: string]: string };
+
+    // The number of units of the product to deliver to the customer.
+    quantity?: Integer;
+
+    // Unit in which the product is measured (liters, kilograms, packages, 
etc.).
+    unit?: string;
+
+    // The price of the product; this is the total price for quantity times 
unit of this product.
+    price?: AmountString;
+
+    // An optional base64-encoded product image.
+    image?: ImageDataUrl;
+
+    // A list of taxes paid by the merchant for this product. Can be empty.
+    taxes?: Tax[];
+
+    // Time indicating when this product should be delivered.
+    delivery_date?: Timestamp;
+  }
+
+  interface Tax {
+    // The name of the tax.
+    name: string;
+
+    // Amount paid in tax.
+    tax: AmountString;
+  }
+  interface Merchant {
+    // The merchant's legal name of business.
+    name: 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;
+  }
+  // Delivery location, loosely modeled as a subset of
+  // ISO20022's PostalAddress25.
+  interface Location {
+    // Nation with its own government.
+    country?: string;
+
+    // Identifies a subdivision of a country such as state, region, county.
+    country_subdivision?: string;
+
+    // Identifies a subdivision within a country sub-division.
+    district?: string;
+
+    // Name of a built-up area, with defined boundaries, and a local 
government.
+    town?: string;
+
+    // Specific location name within the town.
+    town_location?: string;
+
+    // Identifier consisting of a group of letters and/or numbers that
+    // is added to a postal address to assist the sorting of mail.
+    post_code?: string;
+
+    // Name of a street or thoroughfare.
+    street?: string;
+
+    // Name of the building or house.
+    building_name?: string;
+
+    // Number that identifies the position of a building on a street.
+    building_number?: string;
+
+    // Free-form address lines, should not exceed 7 elements.
+    address_lines?: string[];
+  }
+  interface Auditor {
+    // Official name.
+    name: string;
+
+    // Auditor's public key.
+    auditor_pub: EddsaPublicKey;
+
+    // Base URL of the auditor.
+    url: string;
+  }
+  interface Exchange {
+    // The exchange's base URL.
+    url: string;
+
+    // How much would the merchant like to use this exchange.
+    // The wallet should use a suitable exchange with high
+    // priority. The following priority values are used, but
+    // it should be noted that they are NOT in any way normative.
+    //
+    // 0: likely it will not work (recently seen with account
+    //    restriction that would be bad for this merchant)
+    // 512: merchant does not know, might be down (merchant
+    //    did not yet get /wire response).
+    // 1024: good choice (recently confirmed working)
+    priority: Integer;
+
+    // Master public key of the exchange.
+    master_pub: EddsaPublicKey;
+  }
+
+}
\ No newline at end of file
diff --git a/packages/taler-util/src/operation.ts 
b/packages/taler-util/src/operation.ts
index 9d7594d14..06bfe26bd 100644
--- a/packages/taler-util/src/operation.ts
+++ b/packages/taler-util/src/operation.ts
@@ -90,6 +90,6 @@ type AllKnownCases<t extends object, d extends keyof t> = 
"success" | FailCasesB
 
 export type TestForApi<ApiType extends object> = {
   [OpType in MethodsOfOperations<ApiType> as `test_${OpType & string}`]: {
-    [c in AllKnownCases<ApiType, OpType>]: undefined | ((api: ApiType) => 
Promise<void>);
+    [c in AllKnownCases<ApiType, OpType>]: undefined | (() => Promise<void>);
   };
 };

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