gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 01/07: wip


From: gnunet
Subject: [taler-wallet-core] 01/07: wip
Date: Wed, 20 Mar 2024 14:30:13 +0100

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

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

commit b5312973a6f25ac73c8e29b659bf7ea23d77ac5e
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Mar 18 12:12:57 2024 -0300

    wip
---
 packages/bank-ui/package.json                      |   8 +-
 .../merchant-backoffice-ui/src/hooks/backend.ts    |   2 +-
 .../merchant-backoffice-ui/src/hooks/merchant.ts   | 211 +++++++
 packages/taler-util/.eslintrc.cjs                  |  28 +
 packages/taler-util/package.json                   |   4 +-
 packages/taler-util/src/http-client/README.md      |  24 +
 packages/taler-util/src/http-client/merchant.ts    | 679 +++++++++++++++++++++
 packages/taler-util/src/http-client/types.ts       | 106 ++--
 pnpm-lock.yaml                                     |  12 +-
 9 files changed, 1027 insertions(+), 47 deletions(-)

diff --git a/packages/bank-ui/package.json b/packages/bank-ui/package.json
index dd5589fe2..c25decf8d 100644
--- a/packages/bank-ui/package.json
+++ b/packages/bank-ui/package.json
@@ -27,6 +27,10 @@
   },
   "devDependencies": {
     "eslint": "^8.56.0",
+    "@typescript-eslint/eslint-plugin": "^6.19.0",
+    "@typescript-eslint/parser": "^6.19.0",
+    "eslint-config-prettier": "^9.1.0",
+    "eslint-plugin-react": "^7.33.2",
     "@gnu-taler/pogen": "^0.0.5",
     "@tailwindcss/forms": "^0.5.3",
     "@tailwindcss/typography": "^0.5.9",
@@ -34,13 +38,9 @@
     "@types/history": "^4.7.8",
     "@types/mocha": "^10.0.1",
     "@types/node": "^18.11.17",
-    "@typescript-eslint/eslint-plugin": "^6.19.0",
-    "@typescript-eslint/parser": "^6.19.0",
     "autoprefixer": "^10.4.14",
     "chai": "^4.3.6",
     "esbuild": "^0.19.9",
-    "eslint-config-prettier": "^9.1.0",
-    "eslint-plugin-react": "^7.33.2",
     "mocha": "9.2.0",
     "po2json": "^0.4.5",
     "tailwindcss": "^3.3.2",
diff --git a/packages/merchant-backoffice-ui/src/hooks/backend.ts 
b/packages/merchant-backoffice-ui/src/hooks/backend.ts
index a3bb43545..292261bc8 100644
--- a/packages/merchant-backoffice-ui/src/hooks/backend.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/backend.ts
@@ -278,7 +278,7 @@ export function useBackendBaseRequest(): 
useBackendBaseRequestType {
 
 export function useBackendInstanceRequest(): useBackendInstanceRequestType {
   const { url: rootBackendUrl, token: rootToken } = useBackendContext();
-  const { token: instanceToken, id, admin } = useInstanceContext();
+  const { token: instanceToken, admin } = useInstanceContext();
   const { request: requestHandler } = useApiContext();
 
   const { baseUrl, token: loginToken } = !admin
diff --git a/packages/merchant-backoffice-ui/src/hooks/merchant.ts 
b/packages/merchant-backoffice-ui/src/hooks/merchant.ts
new file mode 100644
index 000000000..5d5785442
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/hooks/merchant.ts
@@ -0,0 +1,211 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+import {
+  HttpResponse,
+  HttpResponseOk,
+  HttpResponsePaginated,
+  RequestError,
+} from "@gnu-taler/web-util/browser";
+import { useEffect, useState } from "preact/hooks";
+import { MerchantBackend } from "../declaration.js";
+import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
+import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+
+// FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import _useSWR, { SWRHook, mutate } from "swr";
+const useSWR = _useSWR as unknown as SWRHook;
+
+// const MOCKED_ACCOUNTS: Record<string, 
MerchantBackend.BankAccounts.AccountAddDetails> = {
+//   "hwire1": {
+//     h_wire: "hwire1",
+//     payto_uri: "payto://fake/iban/123",
+//     salt: "qwe",
+//   },
+//   "hwire2": {
+//     h_wire: "hwire2",
+//     payto_uri: "payto://fake/iban/123",
+//     salt: "qwe2",
+//   },
+// }
+
+export function useBankAccountAPI(): BankAccountAPI {
+  const mutateAll = useMatchMutate();
+  const { request } = useBackendInstanceRequest();
+
+  const createBankAccount = async (
+    data: MerchantBackend.BankAccounts.AccountAddDetails,
+  ): Promise<HttpResponseOk<void>> => {
+    // MOCKED_ACCOUNTS[data.h_wire] = data
+    // return Promise.resolve({ ok: true, data: undefined });
+    const res = await request<void>(`/private/accounts`, {
+      method: "POST",
+      data,
+    });
+    await mutateAll(/.*private\/accounts.*/);
+    return res;
+  };
+
+  const updateBankAccount = async (
+    h_wire: string,
+    data: MerchantBackend.BankAccounts.AccountPatchDetails,
+  ): Promise<HttpResponseOk<void>> => {
+    // MOCKED_ACCOUNTS[h_wire].credit_facade_credentials = 
data.credit_facade_credentials
+    // MOCKED_ACCOUNTS[h_wire].credit_facade_url = data.credit_facade_url
+    // return Promise.resolve({ ok: true, data: undefined });
+    const res = await request<void>(`/private/accounts/${h_wire}`, {
+      method: "PATCH",
+      data,
+    });
+    await mutateAll(/.*private\/accounts.*/);
+    return res;
+  };
+
+  const deleteBankAccount = async (
+    h_wire: string,
+  ): Promise<HttpResponseOk<void>> => {
+    // delete MOCKED_ACCOUNTS[h_wire]
+    // return Promise.resolve({ ok: true, data: undefined });
+    const res = await request<void>(`/private/accounts/${h_wire}`, {
+      method: "DELETE",
+    });
+    await mutateAll(/.*private\/accounts.*/);
+    return res;
+  };
+
+  return {
+    createBankAccount,
+    updateBankAccount,
+    deleteBankAccount,
+  };
+}
+
+export interface BankAccountAPI {
+  createBankAccount: (
+    data: MerchantBackend.BankAccounts.AccountAddDetails,
+  ) => Promise<HttpResponseOk<void>>;
+  updateBankAccount: (
+    id: string,
+    data: MerchantBackend.BankAccounts.AccountPatchDetails,
+  ) => Promise<HttpResponseOk<void>>;
+  deleteBankAccount: (id: string) => Promise<HttpResponseOk<void>>;
+}
+
+export interface InstanceBankAccountFilter {
+}
+
+export function revalidateInstanceBankAccounts() {
+  // mutate(key => key instanceof)
+  return mutate((key) => Array.isArray(key) && key[key.length - 1] === 
"/private/accounts", undefined, { revalidate: true });
+}
+export function useInstanceBankAccounts(
+  args?: InstanceBankAccountFilter,
+  updatePosition?: (id: string) => void,
+): HttpResponsePaginated<
+  MerchantBackend.BankAccounts.AccountsSummaryResponse,
+  MerchantBackend.ErrorDetail
+> {
+
+  const { fetcher } = useBackendInstanceRequest();
+
+  const [pageAfter, setPageAfter] = useState(1);
+
+  const totalAfter = pageAfter * PAGE_SIZE;
+  const {
+    data: afterData,
+    error: afterError,
+    isValidating: loadingAfter,
+  } = useSWR<
+    HttpResponseOk<MerchantBackend.BankAccounts.AccountsSummaryResponse>,
+    RequestError<MerchantBackend.ErrorDetail>
+  >([`/private/accounts`], fetcher);
+
+  const [lastAfter, setLastAfter] = useState<
+    HttpResponse<
+      MerchantBackend.BankAccounts.AccountsSummaryResponse,
+      MerchantBackend.ErrorDetail
+    >
+  >({ loading: true });
+  useEffect(() => {
+    if (afterData) setLastAfter(afterData);
+  }, [afterData /*, beforeData*/]);
+
+  if (afterError) return afterError.cause;
+
+  // if the query returns less that we ask, then we have reach the end or 
beginning
+  const isReachingEnd =
+    afterData && afterData.data.accounts.length < totalAfter;
+  const isReachingStart = false;
+
+  const pagination = {
+    isReachingEnd,
+    isReachingStart,
+    loadMore: () => {
+      if (!afterData || isReachingEnd) return;
+      if (afterData.data.accounts.length < MAX_RESULT_SIZE) {
+        setPageAfter(pageAfter + 1);
+      } else {
+        const from = `${afterData.data.accounts[afterData.data.accounts.length 
- 1]
+          .h_wire
+          }`;
+        if (from && updatePosition) updatePosition(from);
+      }
+    },
+    loadMorePrev: () => {
+    },
+  };
+
+  const accounts = !afterData ? [] : (afterData || lastAfter).data.accounts;
+  if (loadingAfter /* || loadingBefore */)
+    return { loading: true, data: { accounts } };
+  if (/*beforeData &&*/ afterData) {
+    return { ok: true, data: { accounts }, ...pagination };
+  }
+  return { loading: true };
+}
+
+export function useBankAccountDetails(
+  h_wire: string,
+): HttpResponse<
+  MerchantBackend.BankAccounts.BankAccountEntry,
+  MerchantBackend.ErrorDetail
+> {
+  // return {
+  //   ok: true,
+  //   data: {
+  //     ...MOCKED_ACCOUNTS[h_wire],
+  //     active: true,
+  //   }
+  // }
+  const { fetcher } = useBackendInstanceRequest();
+
+  const { data, error, isValidating } = useSWR<
+    HttpResponseOk<MerchantBackend.BankAccounts.BankAccountEntry>,
+    RequestError<MerchantBackend.ErrorDetail>
+  >([`/private/accounts/${h_wire}`], fetcher, {
+    refreshInterval: 0,
+    refreshWhenHidden: false,
+    revalidateOnFocus: false,
+    revalidateOnReconnect: false,
+    refreshWhenOffline: false,
+  });
+
+  if (isValidating) return { loading: true, data: data?.data };
+  if (data) {
+    return data;
+  }
+  if (error) return error.cause;
+  return { loading: true };
+}
diff --git a/packages/taler-util/.eslintrc.cjs 
b/packages/taler-util/.eslintrc.cjs
new file mode 100644
index 000000000..05618b499
--- /dev/null
+++ b/packages/taler-util/.eslintrc.cjs
@@ -0,0 +1,28 @@
+module.exports = {
+  extends: [
+    'eslint:recommended',
+    'plugin:@typescript-eslint/recommended',
+    'plugin:react/recommended',
+  ],
+  parser: '@typescript-eslint/parser',
+  plugins: ['@typescript-eslint', 'header'],
+  root: true,
+  rules: {
+    "react/no-unknown-property": 0,
+    "react/no-unescaped-entities": 0,
+    "@typescript-eslint/no-namespace": 0,
+    "@typescript-eslint/no-unused-vars": [2,{argsIgnorePattern:"^_"}],
+    "header/header": [2,"copyleft-header.js"]
+  },
+  parserOptions: {
+    ecmaVersion: 6,
+    sourceType: 'module',
+    jsx: true,
+  },
+  settings: {
+    react: {
+      version: "18",
+      pragma: "h",
+    }
+  },
+};
diff --git a/packages/taler-util/package.json b/packages/taler-util/package.json
index 1caf1af9e..c1ff9873f 100644
--- a/packages/taler-util/package.json
+++ b/packages/taler-util/package.json
@@ -64,11 +64,13 @@
     "pretty": "prettier --write src"
   },
   "devDependencies": {
+    "eslint": "^8.56.0",
+    "@typescript-eslint/eslint-plugin": "^6.19.0",
+    "@typescript-eslint/parser": "^6.19.0",
     "@types/follow-redirects": "^1.14.4",
     "@types/node": "^18.11.17",
     "ava": "^6.0.1",
     "esbuild": "^0.19.9",
-    "prettier": "^3.1.1",
     "typescript": "^5.3.3"
   },
   "dependencies": {
diff --git a/packages/taler-util/src/http-client/README.md 
b/packages/taler-util/src/http-client/README.md
new file mode 100644
index 000000000..5af5d48a7
--- /dev/null
+++ b/packages/taler-util/src/http-client/README.md
@@ -0,0 +1,24 @@
+HTTP Cclients
+-------------
+
+This folder contain class or function specifically designed to facilitate HTTP 
client
+interactions with a the core systems. 
+
+These API defines:
+
+1.  **API Communication**: Handle communication with the component API, 
+abstracting away the details of HTTP requests and responses. 
+This includes making GET, POST, PUT, and DELETE requests to the servers.
+    
+2.  **Data Formatting**: Responsible for formatting requests to the API in a 
+way that's expected by the servers (JSON) and parsing the responses back 
+into formats usable by the client.
+    
+3.  **Authentication and Security**: Handling authentication with the server 
API, 
+which could involve sending API keys, client credentials, or managing tokens. 
+It might also implement security features to ensure data integrity and 
confidentiality during transit.
+    
+4.  **Error Handling**: Providing robust error handling and retry mechanisms 
+for failed HTTP requests, including logging and potentially user notifications 
for critical failures.
+    
+5.  **Data Validation**: Before sending requests, it could validate the data 
to ensure it meets the API's expected format, types, and value ranges, reducing 
the likelihood of errors and improving system reliability.
diff --git a/packages/taler-util/src/http-client/merchant.ts 
b/packages/taler-util/src/http-client/merchant.ts
new file mode 100644
index 000000000..32384f8b4
--- /dev/null
+++ b/packages/taler-util/src/http-client/merchant.ts
@@ -0,0 +1,679 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022-2024 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import {
+  HttpStatusCode,
+  LibtoolVersion,
+  MerchantApiClient,
+  TalerMerchantApi,
+  codecForMerchantConfig
+} from "@gnu-taler/taler-util";
+import {
+  HttpRequestLibrary,
+  createPlatformHttpLib,
+} from "@gnu-taler/taler-util/http";
+import {
+  FailCasesByMethod,
+  ResultByMethod,
+  opSuccessFromHttp,
+  opUnknownFailure,
+} from "../operation.js";
+import { CacheEvictor, nullEvictor } from "./utils.js";
+
+export enum TalerMerchantCacheEviction {
+  CREATE_ORDER,
+}
+/**
+ * Protocol version spoken with the core bank.
+ *
+ * Endpoint must be ordered in the same way that in the docs
+ * Response code (http and taler) must have the same order that in the docs
+ * That way is easier to see changes
+ *
+ * Uses libtool's current:revision:age versioning.
+ */
+class TalerMerchantInstanceHttpClient {
+
+  readonly httpLib: HttpRequestLibrary;
+  readonly cacheEvictor: CacheEvictor<TalerMerchantCacheEviction>;
+
+  constructor(
+    readonly baseUrl: string,
+    httpClient?: HttpRequestLibrary,
+    cacheEvictor?: CacheEvictor<TalerMerchantCacheEviction>,
+  ) {
+    this.httpLib = httpClient ?? createPlatformHttpLib();
+    this.cacheEvictor = cacheEvictor ?? nullEvictor;
+  }
+
+  //
+  // Wallet API
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-orders-$ORDER_ID-claim
+   */
+  async claimOrder(orderId: string, body: TalerMerchantApi.ClaimRequest) {
+    const url = new URL(`orders/${orderId}/claim`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+    // switch (resp.status) {
+    //   case HttpStatusCode.Ok:
+    //     return opSuccessFromHttp(resp, codecForClaimResponse());
+    //   case HttpStatusCode.Conflict:
+    //     return opKnownHttpFailure(resp.status, resp)
+    //   case HttpStatusCode.NotFound:
+    //     return opKnownHttpFailure(resp.status, resp)
+    //   default:
+    //     return opUnknownFailure(resp, await resp.text());
+    // }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-orders-$ORDER_ID-pay
+   */
+  async makePayment(orderId: string, body: TalerMerchantApi.PayRequest) {
+    const url = new URL(`orders/${orderId}/pay`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    ///
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-orders-$ORDER_ID
+   */
+
+  async getPaymentStatus(orderId: string, params: 
TalerMerchantApi.PaymentStatusRequestParams = {}) {
+    const url = new URL(`orders/${orderId}`, this.baseUrl);
+
+    if (params.allowRefundedForRepurchase !== undefined) {
+      url.searchParams.set("allow_refunded_for_repurchase", 
params.allowRefundedForRepurchase ? "YES" : "NO")
+    }
+    if (params.awaitRefundObtained !== undefined) {
+      url.searchParams.set("await_refund_obtained", 
params.allowRefundedForRepurchase ? "YES" : "NO")
+    }
+    if (params.claimToken !== undefined) {
+      url.searchParams.set("token", params.claimToken)
+    }
+    if (params.contractTermHash !== undefined) {
+      url.searchParams.set("h_contract", params.contractTermHash)
+    }
+    if (params.refund !== undefined) {
+      url.searchParams.set("refund", params.refund)
+    }
+    if (params.sessionId !== undefined) {
+      url.searchParams.set("session_id", params.sessionId)
+    }
+    if (params.timeout !== undefined) {
+      url.searchParams.set("timeout_ms", String(params.timeout))
+    }
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+      // body,
+    });
+
+    ///
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#demonstrating-payment
+   */
+  async demostratePayment(orderId: string, body: TalerMerchantApi.PaidRequest) 
{
+    const url = new URL(`orders/${orderId}/paid`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#aborting-incomplete-payments
+   */
+  async abortIncompletePayment(orderId: string, body: 
TalerMerchantApi.AbortRequest) {
+    const url = new URL(`orders/${orderId}/abort`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#obtaining-refunds
+   */
+  async obtainRefund(orderId: string, body: 
TalerMerchantApi.WalletRefundRequest) {
+    const url = new URL(`orders/${orderId}/refund`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+  }
+
+  // 
+  // Management
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-auth
+   */
+  async updateCurrentInstanceAuthentication(body: 
TalerMerchantApi.InstanceAuthConfigurationMessage) {
+    const url = new URL(`private/auth`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    //
+  }
+
+  /**
+   * Get the auth api agaisnt the current instance
+   * 
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-token
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-token
+   */
+  getAuthenticationAPI(): URL {
+    return new URL(`/`, this.baseUrl);
+  }
+
+  /**
+ * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private
+ */
+  async updateCurrentInstance(body: 
TalerMerchantApi.InstanceReconfigurationMessage) {
+    const url = new URL(`private`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "PATCH",
+      body,
+    });
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private
+   * 
+   */
+  async getCurrentInstance() {
+    const url = new URL(`private`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+  }
+
+  /**
+ * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private
+ */
+  async deleteCurrentInstance(params: { purge?: boolean }) {
+    const url = new URL(`private`, this.baseUrl);
+
+    if (params.purge) {
+      url.searchParams.set("purge", "YES")
+    }
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "DELETE",
+    });
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get--instances-$INSTANCE-private-kyc
+   */
+  async getCurrentIntanceKycStatus(params: { wireHash?: string, exchangeURL?: 
string, timeout: number }) {
+    const url = new URL(`private/kyc`, this.baseUrl);
+
+    if (params.wireHash) {
+      url.searchParams.set("h_wire", params.wireHash)
+    }
+    if (params.exchangeURL) {
+      url.searchParams.set("exchange_url", params.exchangeURL)
+    }
+    if (params.timeout) {
+      url.searchParams.set("timeout_ms", String(params.timeout))
+    }
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+
+  }
+
+  //
+  // Bank Accounts
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-accounts
+   */
+  async addAccount(body: TalerMerchantApi.AccountAddDetails) {
+    const url = new URL(`private/accounts`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private-accounts-$H_WIRE
+   */
+  async updateAccount(wireAccount: string, body: 
TalerMerchantApi.AccountPatchDetails) {
+    const url = new URL(`private/accounts/${wireAccount}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "PATCH",
+      body,
+    });
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-accounts
+   */
+  async listAccounts() {
+    const url = new URL(`private/accounts`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-accounts-$H_WIRE
+   */
+  async getAccount(wireAccount: string) {
+    const url = new URL(`private/accounts/${wireAccount}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-accounts-$H_WIRE
+   */
+  async deleteAccount(wireAccount: string) {
+    const url = new URL(`private/accounts/${wireAccount}`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "DELETE",
+    });
+  }
+
+  //
+  // Inventory Management
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-products
+   */
+  async addProduct() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private-products-$PRODUCT_ID
+   */
+  async updateProduct() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-products
+   */
+  async listProducts() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-products-$PRODUCT_ID
+   */
+  async getProduct() {
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#reserving-inventory
+   */
+  async lockProduct() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#removing-products-from-inventory
+   */
+  async removeProduct() {
+  }
+
+  //
+  // Payment processing
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-orders
+   */
+  async createOrder() {
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#inspecting-orders
+   */
+  async listOrders() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-orders-$ORDER_ID
+   */
+  async getOrder() {
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#private-order-data-cleanup
+   */
+  async forgetOrder() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-orders-$ORDER_ID
+   */
+  async deleteOrder() {
+  }
+
+  // 
+  // Refunds
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-orders-$ORDER_ID-refund
+   */
+  async addRefund() {
+  }
+
+  //
+  // Wire Transfer
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-transfers
+   */
+  async informWireTransfer() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-transfers
+   */
+  async listWireTransfers() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-transfers-$TID
+   */
+  async deleteWireTransfer() {
+  }
+
+  //
+  // OTP Devices
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-otp-devices
+   */
+  async addOtpDevice() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private-otp-devices-$DEVICE_ID
+   */
+  async updateOtpDevice() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-otp-devices
+   */
+  async listOtpDevices() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-otp-devices-$DEVICE_ID
+   */
+  async getOtpDevice() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-otp-devices-$DEVICE_ID
+   */
+  async deleteOtpDevice() {
+  }
+
+  //
+  // Templates
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-templates
+   */
+  async addTemplate() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private-templates-$TEMPLATE_ID
+   */
+  async updateTemplate() {
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#inspecting-template
+   */
+  async listTemplates() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-templates-$TEMPLATE_ID
+   */
+  async getTemplate() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-templates-$TEMPLATE_ID
+   */
+  async deleteTemplate() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCES]-templates-$TEMPLATE_ID
+   */
+  async useTemplate() {
+  }
+
+  //
+  // Webhooks
+  //
+
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCES]-private-webhooks
+   */
+  async addWebhook() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCES]-private-webhooks-$WEBHOOK_ID
+   */
+  async updateWebhook() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-webhooks
+   */
+  async listWebhooks() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-webhooks-$WEBHOOK_ID
+   */
+  async getWebhook() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCES]-private-webhooks-$WEBHOOK_ID
+   */
+  async removeWebhook() {
+  }
+
+  //
+  // token families
+  //
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCES]-private-tokenfamilies
+   */
+  async createTokenFamily() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCES]-private-tokenfamilies-$TOKEN_FAMILY_SLUG
+   */
+  async updateTokenFamily() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-tokenfamilies
+   */
+  async listTokenFamilies() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-tokenfamilies-$TOKEN_FAMILY_SLUG
+   */
+  async getTokenFamily() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCES]-private-tokenfamilies-$TOKEN_FAMILY_SLUG
+   */
+  async deleteTokenFamily() {
+  }
+
+}
+
+export class TalerMerchantManagementHttpClient extends 
TalerMerchantInstanceHttpClient {
+  public readonly PROTOCOL_VERSION = "10:0:6";
+
+  httpLib: HttpRequestLibrary;
+  cacheEvictor: CacheEvictor<TalerMerchantCacheEviction>;
+  constructor(
+    readonly baseUrl: string,
+    httpClient?: HttpRequestLibrary,
+    cacheEvictor?: CacheEvictor<TalerMerchantCacheEviction>,
+  ) {
+    super(baseUrl, httpClient, cacheEvictor)
+  }
+
+  isCompatible(version: string): boolean {
+    const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version);
+    return compare?.compatible ?? false;
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#get--config
+   *
+   */
+  async getConfig() {
+    const url = new URL(`config`, this.baseUrl);
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+    });
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForMerchantConfig());
+      default:
+        return opUnknownFailure(resp, await resp.text());
+    }
+  }
+
+
+  //
+  // Instance Management
+  //
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#post--management-instances
+   */
+  async createInstance(body: TalerMerchantApi.InstanceConfigurationMessage) {
+    const url = new URL(`management/instances`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    //
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#post--management-instances-$INSTANCE-auth
+   */
+  async updateInstanceAuthentication(body: 
TalerMerchantApi.InstanceAuthConfigurationMessage) {
+    const url = new URL(`management/instances`, this.baseUrl);
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      body,
+    });
+
+    //
+  }
+
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#patch--management-instances-$INSTANCE
+   */
+  async updateInstance() {
+  }
+
+  /**
+   * https://docs.taler.net/core/api-merchant.html#get--management-instances
+   */
+  async listInstances() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get--management-instances-$INSTANCE
+   * 
+   */
+  async getInstance() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#delete--management-instances-$INSTANCE
+   */
+  async deleteInstance() {
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-merchant.html#get--management-instances-$INSTANCE-kyc
+   */
+  async getIntanceKycStatus() {
+  }
+
+
+
+
+}
diff --git a/packages/taler-util/src/http-client/types.ts 
b/packages/taler-util/src/http-client/types.ts
index 132ca867d..08b29106e 100644
--- a/packages/taler-util/src/http-client/types.ts
+++ b/packages/taler-util/src/http-client/types.ts
@@ -2403,6 +2403,10 @@ export namespace TalerMerchantApi {
     // Name of the protocol.
     name: "taler-merchant";
 
+    // URN of the implementation (needed to interpret 'revision' in version).
+    // @since **v8**, may become mandatory in the future.
+    implementation?: string;
+
     // Default (!) currency supported by this backend.
     // This is the currency that the backend should
     // suggest by default to the user when entering
@@ -2417,8 +2421,27 @@ export namespace TalerMerchantApi {
     // All currencies in this map are supported by
     // the backend.
     currencies: { [currency: string]: CurrencySpecification };
+
+    // Array of exchanges trusted by the merchant.
+    // Since protocol **v6**.
+    exchanges: ExchangeConfigInfo[];
   }
 
+  export interface ExchangeConfigInfo {
+    // Base URL of the exchange REST API.
+    base_url: string;
+
+    // Currency for which the merchant is configured
+    // to trust the exchange.
+    // May not be the one the exchange actually uses,
+    // but is the only one we would trust this exchange for.
+    currency: string;
+
+    // Offline master public key of the exchange. The
+    // /keys data must be signed with this public
+    // key for us to trust it.
+    master_pub: EddsaPublicKey;
+  }
   export interface ClaimRequest {
     // Nonce to identify the wallet that claimed the order.
     nonce: string;
@@ -2447,7 +2470,43 @@ export namespace TalerMerchantApi {
     pos_confirmation?: string;
   }
 
-  interface PayRequest {
+  export interface PaymentStatusRequestParams {
+    // Hash of the order’s contract terms (this is used to 
+    // authenticate the wallet/customer in case 
+    // $ORDER_ID is guessable). 
+    // Required once an order was claimed.
+    contractTermHash?: string;
+    // Authorizes the request via the claim token that
+    // was returned in the PostOrderResponse. Used with 
+    // unclaimed orders only. Whether token authorization is
+    // required is determined by the merchant when the
+    // frontend creates the order.
+    claimToken?: string;
+    // Session ID that the payment must be bound to. 
+    // If not specified, the payment is not session-bound.
+    sessionId?: string;
+    // If specified, the merchant backend will wait up to 
+    // timeout_ms milliseconds for completion of the payment 
+    // before sending the HTTP response. A client must never 
+    // rely on this behavior, as the merchant backend may return 
+    // a response immediately.
+    timeout?: number;
+    // If set to “yes”, poll for the order’s pending refunds
+    // to be picked up. timeout_ms specifies how long we 
+    // will wait for the refund.
+    awaitRefundObtained?: boolean;
+    // Indicates that we are polling for a refund above the 
+    // given AMOUNT. timeout_ms will specify how long we 
+    // will wait for the refund.
+    refund?: AmountString;
+    // Since protocol v9 refunded orders are only returned 
+    // under “already_paid_order_id” if this flag is set 
+    // explicitly to “YES”.
+    allowRefundedForRepurchase?: boolean;
+
+  }
+
+  export interface PayRequest {
     // The coins used to make the payment.
     coins: CoinPaySig[];
 
@@ -2520,7 +2579,7 @@ export namespace TalerMerchantApi {
     // refunds. False if it was simply paid.
     refunded: boolean;
   }
-  interface PaidRequest {
+  export interface PaidRequest {
     // Signature on TALER_PaymentResponsePS with the public
     // key of the merchant instance.
     sig: EddsaSignature;
@@ -2537,7 +2596,7 @@ export namespace TalerMerchantApi {
     session_id: string;
   }
 
-  interface AbortRequest {
+  export interface AbortRequest {
     // Hash of the order's contract terms (this is used to authenticate the
     // wallet/customer in case $ORDER_ID is guessable).
     h_contract: HashCode;
@@ -2603,7 +2662,7 @@ export namespace TalerMerchantApi {
     exchange_pub: EddsaPublicKey;
   }
 
-  interface WalletRefundRequest {
+  export interface WalletRefundRequest {
     // Hash of the order's contract terms (this is used to authenticate the
     // wallet/customer).
     h_contract: HashCode;
@@ -2723,7 +2782,7 @@ export namespace TalerMerchantApi {
     blind_sig: BlindedRsaSignature;
   }
 
-  interface InstanceConfigurationMessage {
+  export interface InstanceConfigurationMessage {
     // Name of the merchant instance to create (will become $INSTANCE).
     // Must match the regex ^[A-Za-z0-9][A-Za-z0-9_.@-]+$.
     id: string;
@@ -2771,7 +2830,7 @@ export namespace TalerMerchantApi {
     default_pay_delay: RelativeTime;
   }
 
-  interface InstanceAuthConfigurationMessage {
+  export interface InstanceAuthConfigurationMessage {
     // Type of authentication.
     // "external":  The mechant backend does not do
     //   any authentication checks.  Instead an API
@@ -2788,37 +2847,8 @@ export namespace TalerMerchantApi {
     token?: string;
   }
 
-  interface LoginTokenRequest {
-    // Scope of the token (which kinds of operations it will allow)
-    scope: "readonly" | "write";
-
-    // Server may impose its own upper bound
-    // on the token validity duration
-    duration?: RelativeTime;
-
-    // Can this token be refreshed?
-    // Defaults to false.
-    refreshable?: boolean;
-  }
-  interface LoginTokenSuccessResponse {
-    // The login token that can be used to access resources
-    // that are in scope for some time. Must be prefixed
-    // with "Bearer " when used in the "Authorization" HTTP header.
-    // Will already begin with the RFC 8959 prefix.
-    token: string;
-
-    // Scope of the token (which kinds of operations it will allow)
-    scope: "readonly" | "write";
-
-    // Server may impose its own upper bound
-    // on the token validity duration
-    expiration: Timestamp;
-
-    // Can this token be refreshed?
-    refreshable: boolean;
-  }
 
-  interface InstanceReconfigurationMessage {
+  export interface InstanceReconfigurationMessage {
     // Merchant name corresponding to this instance.
     name: string;
 
@@ -2980,7 +3010,7 @@ export namespace TalerMerchantApi {
     exchange_http_status: number;
   }
 
-  interface AccountAddDetails {
+  export interface AccountAddDetails {
     // payto:// URI of the account.
     payto_uri: PaytoString;
 
@@ -3017,7 +3047,7 @@ export namespace TalerMerchantApi {
     salt: HashCode;
   }
 
-  interface AccountPatchDetails {
+  export interface AccountPatchDetails {
     // URL from where the merchant can download information
     // about incoming wire transfers to this account.
     credit_facade_url?: string;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b896375eb..6f53c7d90 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -774,15 +774,21 @@ importers:
       '@types/node':
         specifier: ^18.11.17
         version: 18.11.17
+      '@typescript-eslint/eslint-plugin':
+        specifier: ^6.19.0
+        version: 
6.19.0(@typescript-eslint/parser@6.19.0)(eslint@8.56.0)(typescript@5.3.3)
+      '@typescript-eslint/parser':
+        specifier: ^6.19.0
+        version: 6.19.0(eslint@8.56.0)(typescript@5.3.3)
       ava:
         specifier: ^6.0.1
         version: 6.0.1(@ava/typescript@4.1.0)
       esbuild:
         specifier: ^0.19.9
         version: 0.19.9
-      prettier:
-        specifier: ^3.1.1
-        version: 3.1.1
+      eslint:
+        specifier: ^8.56.0
+        version: 8.56.0
       typescript:
         specifier: ^5.3.3
         version: 5.3.3

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



reply via email to

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