taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 6eee7ef1967d69875684f7d80d79c8cdd5a912f4
parent 7c44ca8644c80770ec049e0c50b65801125e8553
Author: Florian Dold <florian@dold.me>
Date:   Thu,  5 Jun 2025 02:21:45 +0200

unify diverged exchange clients

Issue: https://bugs.taler.net/n/10049

Diffstat:
Mpackages/aml-backoffice-ui/src/App.tsx | 14++++++++------
Mpackages/aml-backoffice-ui/src/hooks/account.ts | 4++--
Mpackages/aml-backoffice-ui/src/hooks/decisions.ts | 12++++--------
Mpackages/aml-backoffice-ui/src/hooks/server-info.ts | 9+++------
Mpackages/aml-backoffice-ui/src/hooks/transfers.ts | 8+++-----
Mpackages/aml-backoffice-ui/src/pages/CaseDetails.tsx | 6+-----
Mpackages/aml-backoffice-ui/src/pages/Search.tsx | 7+------
Mpackages/aml-backoffice-ui/src/pages/ShowCollectedInfo.tsx | 1-
Mpackages/aml-backoffice-ui/src/pages/Transfers.tsx | 27+++++++++++++--------------
Mpackages/aml-backoffice-ui/src/pages/decision/Rules.tsx | 169+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mpackages/bank-ui/src/hooks/account.ts | 7++-----
Mpackages/kyc-ui/src/hooks/kyc.ts | 8++++----
Mpackages/kyc-ui/src/pages/FillForm.tsx | 22++++++----------------
Mpackages/taler-harness/src/harness/environments.ts | 6+++---
Mpackages/taler-harness/src/harness/tops.ts | 9++++-----
Mpackages/taler-harness/src/integrationtests/test-withdrawal-idempotent.ts | 4++--
Mpackages/taler-util/src/aml/reporting.ts | 42++++++++++++++++++++++++++++++------------
Mpackages/taler-util/src/http-client/exchange-client.ts | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Dpackages/taler-util/src/http-client/exchange.ts | 1001-------------------------------------------------------------------------------
Mpackages/taler-util/src/http-client/officer-account.ts | 31+++++++++++++++++--------------
Mpackages/taler-util/src/index.ts | 1-
Mpackages/taler-util/src/types-taler-common.ts | 26++++++++++++++++----------
Mpackages/taler-util/src/types-taler-exchange.ts | 3+++
Mpackages/taler-wallet-core/src/dbless.ts | 6+++---
Mpackages/taler-wallet-core/src/versions.ts | 4++--
Mpackages/taler-wallet-core/src/wallet.ts | 6+++---
Mpackages/web-util/src/context/exchange-api.ts | 2+-
Mpackages/web-util/src/hooks/useAsync.ts | 8++++----
28 files changed, 318 insertions(+), 1262 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/App.tsx b/packages/aml-backoffice-ui/src/App.tsx @@ -24,21 +24,23 @@ import { ExchangeApiProvider, Loading, TranslationProvider, - UiForms, } from "@gnu-taler/web-util/browser"; import { VNode, h } from "preact"; import { useEffect, useState } from "preact/hooks"; import { SWRConfig } from "swr"; import { ExchangeAmlFrame } from "./ExchangeAmlFrame.js"; import { Routing } from "./Routing.js"; -import { UiSettingsProvider } from "./context/ui-settings.js"; -import { strings } from "./i18n/strings.js"; -import "./scss/main.css"; -import { UiSettings, fetchUiSettings } from "./context/ui-settings.js"; import { UiFormsProvider } from "./context/ui-forms.js"; -import { revalidateAccountDecisions } from "./hooks/decisions.js"; +import { + UiSettings, + UiSettingsProvider, + fetchUiSettings, +} from "./context/ui-settings.js"; import { revalidateAccountInformation } from "./hooks/account.js"; +import { revalidateAccountDecisions } from "./hooks/decisions.js"; import { usePreferences } from "./hooks/preferences.js"; +import { strings } from "./i18n/strings.js"; +import "./scss/main.css"; const WITH_LOCAL_STORAGE_CACHE = false; diff --git a/packages/aml-backoffice-ui/src/hooks/account.ts b/packages/aml-backoffice-ui/src/hooks/account.ts @@ -16,7 +16,7 @@ import { OfficerAccount, PaytoString, - TalerExchangeResultByMethod, + TalerExchangeResultByMethod2, TalerHttpError, } from "@gnu-taler/taler-util"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 @@ -50,7 +50,7 @@ export function useAccountInformation(paytoHash?: string) { } const { data, error } = useSWR< - TalerExchangeResultByMethod<"getAmlAttributesForAccount">, + TalerExchangeResultByMethod2<"getAmlAttributesForAccount">, TalerHttpError >(!session ? undefined : [session, paytoHash], fetcher, { refreshInterval: 0, diff --git a/packages/aml-backoffice-ui/src/hooks/decisions.ts b/packages/aml-backoffice-ui/src/hooks/decisions.ts @@ -17,14 +17,10 @@ import { useState } from "preact/hooks"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 import { - AmlDecision, - HttpStatusCode, OfficerAccount, - OperationFail, OperationOk, opFixedSuccess, - TalerError, - TalerExchangeResultByMethod, + TalerExchangeResultByMethod2, TalerHttpError, } from "@gnu-taler/taler-util"; import { useExchangeApiContext } from "@gnu-taler/web-util/browser"; @@ -70,7 +66,7 @@ export function useCurrentDecisions({ } const { data, error } = useSWR< - TalerExchangeResultByMethod<"getAmlDecisions">, + TalerExchangeResultByMethod2<"getAmlDecisions">, TalerHttpError >( !session ? undefined : [session, offset, investigated, "getAmlDecisions"], @@ -121,7 +117,7 @@ export function useAccountDecisions(accountStr: string) { } const { data, error } = useSWR< - TalerExchangeResultByMethod<"getAmlDecisions">, + TalerExchangeResultByMethod2<"getAmlDecisions">, TalerHttpError >( !session ? undefined : [session, accountStr, offset, "getAmlDecisions"], @@ -171,7 +167,7 @@ export function useAccountActiveDecision(accountStr?: string) { } const { data, error } = useSWR< - TalerExchangeResultByMethod<"getAmlDecisions">, + TalerExchangeResultByMethod2<"getAmlDecisions">, TalerHttpError >( !session ? undefined : [session, accountStr, offset, "getAmlDecisions"], diff --git a/packages/aml-backoffice-ui/src/hooks/server-info.ts b/packages/aml-backoffice-ui/src/hooks/server-info.ts @@ -16,9 +16,7 @@ import { AbsoluteTime, CounterResultByEventName, - EventReporting_TOPS_calculation, EventReporting_TOPS_queries, - EventReporting_VQF_calculation, EventReporting_VQF_queries, fetchTopsInfoFromServer, fetchVqfInfoFromServer, @@ -26,16 +24,15 @@ import { OfficerAccount, OperationOk, opFixedSuccess, - TalerExchangeHttpClient, - TalerExchangeResultByMethod, + TalerExchangeResultByMethod2, TalerHttpError, TOPS_AmlEventsName, } from "@gnu-taler/taler-util"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 import { useExchangeApiContext } from "@gnu-taler/web-util/browser"; +import { endOfYear, setYear, startOfYear } from "date-fns"; import _useSWR, { SWRHook } from "swr"; import { useOfficer } from "./officer.js"; -import { endOfYear, setYear, startOfYear } from "date-fns"; const useSWR = _useSWR as unknown as SWRHook; export function useServerMeasures() { @@ -51,7 +48,7 @@ export function useServerMeasures() { } const { data, error } = useSWR< - TalerExchangeResultByMethod<"getAmlMeasures">, + TalerExchangeResultByMethod2<"getAmlMeasures">, TalerHttpError >(!session ? undefined : [session], fetcher, { refreshInterval: 0, diff --git a/packages/aml-backoffice-ui/src/hooks/transfers.ts b/packages/aml-backoffice-ui/src/hooks/transfers.ts @@ -20,8 +20,7 @@ import { AmountJson, OfficerAccount, OperationOk, - opFixedSuccess, - TalerExchangeResultByMethod, + TalerExchangeResultByMethod2, TalerHttpError, } from "@gnu-taler/taler-util"; import { useExchangeApiContext } from "@gnu-taler/web-util/browser"; @@ -68,7 +67,7 @@ export function useTransferDebit() { } const { data, error } = useSWR< - TalerExchangeResultByMethod<"getTransfersDebit">, + TalerExchangeResultByMethod2<"getTransfersDebit">, TalerHttpError >(!session ? undefined : [session, offset, "getTransfersDebit"], fetcher); @@ -121,7 +120,7 @@ export function useTransferList({ } const { data, error } = useSWR< - TalerExchangeResultByMethod<"getTransfersCredit">, + TalerExchangeResultByMethod2<"getTransfersCredit">, TalerHttpError >( !session @@ -139,7 +138,6 @@ export function useTransferList({ ); } - type PaginatedResult<T> = OperationOk<T> & { isLastPage: boolean; isFirstPage: boolean; diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx @@ -54,16 +54,13 @@ import { Fragment, h, Ref, VNode } from "preact"; import { useState } from "preact/hooks"; import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js"; import { useAccountInformation } from "../hooks/account.js"; -import { - DecisionRequest, -} from "../hooks/decision-request.js"; +import { DecisionRequest } from "../hooks/decision-request.js"; import { useAccountDecisions } from "../hooks/decisions.js"; import { useOfficer } from "../hooks/officer.js"; import { useServerMeasures } from "../hooks/server-info.js"; import { CurrentMeasureTable, MeasureInfo, Mesaures } from "./MeasuresTable.js"; import { Officer } from "./Officer.js"; import { RulesInfo } from "./RulesInfo.js"; -import { ShowConsolidated } from "./ShowConsolidated.js"; // export type AmlEvent = // | AmlFormEvent @@ -172,7 +169,6 @@ export function CaseDetails({ // const events = getEventsFromAmlHistory(accountDetails, i18n); - function ShortcutActionButtons(): VNode { return ( <div> diff --git a/packages/aml-backoffice-ui/src/pages/Search.tsx b/packages/aml-backoffice-ui/src/pages/Search.tsx @@ -38,7 +38,6 @@ import { Loading, Time, UIFormElementConfig, - UIHandlerId, useExchangeApiContext, useForm, useTranslationContext, @@ -52,7 +51,6 @@ import { privatePages } from "../Routing.js"; import { Pagination, ToInvestigateIcon } from "./Cases.js"; import { HandleAccountNotReady } from "./HandleAccountNotReady.js"; import { Officer } from "./Officer.js"; -import { DecisionRequest } from "../hooks/decision-request.js"; export function Search({ onNewDecision, @@ -298,10 +296,7 @@ function ShowResult({ // payto: encodeCrockForURI(paytoStr), // })} onClick={async () => { - onNewDecision( - account, - encodeCrockForURI(paytoStr), - ); + onNewDecision(account, encodeCrockForURI(paytoStr)); }} class="text-indigo-600 hover:text-indigo-900" > diff --git a/packages/aml-backoffice-ui/src/pages/ShowCollectedInfo.tsx b/packages/aml-backoffice-ui/src/pages/ShowCollectedInfo.tsx @@ -13,7 +13,6 @@ import { FormUI, Loading, RouteDefinition, - useExchangeApiContext, useForm, useTranslationContext, } from "@gnu-taler/web-util/browser"; diff --git a/packages/aml-backoffice-ui/src/pages/Transfers.tsx b/packages/aml-backoffice-ui/src/pages/Transfers.tsx @@ -1,31 +1,30 @@ import { + AbsoluteTime, + AmountJson, + Amounts, + assertUnreachable, + CurrencySpecification, + encodeCrock, + hashNormalizedPaytoUri, + HttpStatusCode, + TalerError, +} from "@gnu-taler/taler-util"; +import { Attention, FormDesign, FormUI, Loading, RouteDefinition, Time, - UIHandlerId, useExchangeApiContext, useForm, useTranslationContext, } from "@gnu-taler/web-util/browser"; +import { format } from "date-fns"; import { Fragment, h, VNode } from "preact"; -import { useTransferList } from "../hooks/transfers.js"; -import { - AbsoluteTime, - AmountJson, - Amounts, - assertUnreachable, - CurrencySpecification, - encodeCrock, - hashNormalizedPaytoUri, - HttpStatusCode, - TalerError, -} from "@gnu-taler/taler-util"; import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js"; +import { useTransferList } from "../hooks/transfers.js"; import { Officer } from "./Officer.js"; -import { format } from "date-fns"; export function Transfers({ routeToCaseById, diff --git a/packages/aml-backoffice-ui/src/pages/decision/Rules.tsx b/packages/aml-backoffice-ui/src/pages/decision/Rules.tsx @@ -21,7 +21,6 @@ import { FormDesign, FormUI, InternationalizationAPI, - Loading, onComponentUnload, useExchangeApiContext, useForm, @@ -30,7 +29,6 @@ import { import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { useCurrentDecisionRequest } from "../../hooks/decision-request.js"; -import { useAccountActiveDecision } from "../../hooks/decisions.js"; import { useServerMeasures } from "../../hooks/server-info.js"; import { ShowDecisionLimitInfo } from "../CaseDetails.js"; import { RulesInfo } from "../RulesInfo.js"; @@ -119,14 +117,10 @@ export function findRuleInconsistency( * @param param0 * @returns */ -export function Rules({ - newPayto, -}: { - newPayto?: PaytoString; -}): VNode { +export function Rules({ newPayto }: { newPayto?: PaytoString }): VNode { const { i18n } = useTranslationContext(); const { config } = useExchangeApiContext(); - const [request] = useCurrentDecisionRequest() + const [request] = useCurrentDecisionRequest(); let newPaytoParsed: PaytoUri | undefined; const isNewAccountAWallet = @@ -148,14 +142,15 @@ export function Rules({ ? undefined : measures.body.roots; - const defaultRules = - (!measures || measures instanceof TalerError || measures.type === "fail" + const defaultRules = ( + !measures || measures instanceof TalerError || measures.type === "fail" ? [] - : measures.body.default_rules).filter((r) => { - return isWallet - ? WALLET_RULES.includes(r.operation_type) - : BANK_RULES.includes(r.operation_type); - }); + : measures.body.default_rules + ).filter((r) => { + return isWallet + ? WALLET_RULES.includes(r.operation_type) + : BANK_RULES.includes(r.operation_type); + }); return ( <div> @@ -180,7 +175,9 @@ export function Rules({ ) : ( <ShowDecisionLimitInfo fixed - since={AbsoluteTime.fromProtocolTimestamp(request.original.decision_time)} + since={AbsoluteTime.fromProtocolTimestamp( + request.original.decision_time, + )} until={AbsoluteTime.fromProtocolTimestamp( request.original.limits.expiration_time, )} @@ -258,11 +255,10 @@ function UpdateRulesForm({ limits: LegitimizationRuleSet; isWallet: boolean | undefined; rootMeasures: AvailableMeasureSummary["roots"] | undefined; - defaultRules: KycRule[] + defaultRules: KycRule[]; }): VNode { const { i18n } = useTranslationContext(); - const [request, updateRequest] = - useCurrentDecisionRequest(); + const [request, updateRequest] = useCurrentDecisionRequest(); const [showAddRuleForm, setShowAddRuleForm] = useState(false); const measureList = !rootMeasures ? [] : Object.keys(rootMeasures); const customMeasures = Object.keys(request.custom_measures ?? {}); @@ -270,7 +266,9 @@ function UpdateRulesForm({ ...measureList, ...customMeasures, ]); - const [currentRules, setRules] = useState<KycRule[]>(!request.rules ? limits.rules : request.rules); + const [currentRules, setRules] = useState<KycRule[]>( + !request.rules ? limits.rules : request.rules, + ); const expirationForm = useForm<ExpirationFormType>(expirationFormDesign, { expiration: request.deadline ?? @@ -284,7 +282,7 @@ function UpdateRulesForm({ ? undefined : expirationForm.status.result.expiration; const doesntExpire = !deadline || AbsoluteTime.isNever(deadline); - updateRequest("unload rules",{ + updateRequest("unload rules", { rules: currentRules, deadline, onExpire_measure: @@ -427,7 +425,7 @@ function UpdateRulesForm({ </button> <button onClick={() => { - setRules( FREEZE_PLAN(config.currency, isWallet)); + setRules(FREEZE_PLAN(config.currency, isWallet)); }} class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" > @@ -435,7 +433,7 @@ function UpdateRulesForm({ </button> <button onClick={() => { - setRules( BASIC_PLAN(config.currency, isWallet)); + setRules(BASIC_PLAN(config.currency, isWallet)); }} class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" > @@ -443,7 +441,7 @@ function UpdateRulesForm({ </button> <button onClick={() => { - setRules( PREMIUM_PLAN(config.currency, isWallet)); + setRules(PREMIUM_PLAN(config.currency, isWallet)); }} class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" > @@ -451,7 +449,7 @@ function UpdateRulesForm({ </button> <button onClick={() => { - setRules( E_COMMERCE(config.currency, isWallet)); + setRules(E_COMMERCE(config.currency, isWallet)); }} class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" > @@ -459,7 +457,7 @@ function UpdateRulesForm({ </button> <button onClick={() => { - setRules( POINT_OF_SALE(config.currency, isWallet)); + setRules(POINT_OF_SALE(config.currency, isWallet)); }} class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" > @@ -954,62 +952,62 @@ const POINT_OF_SALE: TemplateRulesFunction = (currency, isWallet) => : BANK_RULES.includes(r.operation_type); }); - const E_COMMERCE: TemplateRulesFunction = (currency, isWallet) => - [ - { - display_priority: 1, - measures: ["VERBOTEN"], - operation_type: LimitOperationType.withdraw, - threshold: Amounts.stringify({ - currency, - fraction: 0, - value: 0, - }), - timeframe: Duration.toTalerProtocolDuration( - Duration.fromSpec({ months: 1 }), - ), - }, - { - display_priority: 1, - measures: ["VERBOTEN"], - operation_type: LimitOperationType.merge, - threshold: Amounts.stringify({ - currency, - fraction: 0, - value: 0, - }), - timeframe: Duration.toTalerProtocolDuration( - Duration.fromSpec({ months: 1 }), - ), - }, - { - display_priority: 1, - measures: ["preserve-investigate"], - operation_type: LimitOperationType.deposit, - threshold: Amounts.stringify({ - currency, - fraction: 0, - value: 25 * 1000, - }), - timeframe: Duration.toTalerProtocolDuration( - Duration.fromSpec({ months: 1 }), - ), - }, - { - display_priority: 1, - measures: ["preserve-investigate"], - operation_type: LimitOperationType.aggregate, - threshold: Amounts.stringify({ - currency, - fraction: 0, - value: 25 * 1000, - }), - timeframe: Duration.toTalerProtocolDuration( - Duration.fromSpec({ months: 1 }), - ), - }, - ].filter((r) => { - return isWallet - ? WALLET_RULES.includes(r.operation_type) - : BANK_RULES.includes(r.operation_type); - }); -\ No newline at end of file +const E_COMMERCE: TemplateRulesFunction = (currency, isWallet) => + [ + { + display_priority: 1, + measures: ["VERBOTEN"], + operation_type: LimitOperationType.withdraw, + threshold: Amounts.stringify({ + currency, + fraction: 0, + value: 0, + }), + timeframe: Duration.toTalerProtocolDuration( + Duration.fromSpec({ months: 1 }), + ), + }, + { + display_priority: 1, + measures: ["VERBOTEN"], + operation_type: LimitOperationType.merge, + threshold: Amounts.stringify({ + currency, + fraction: 0, + value: 0, + }), + timeframe: Duration.toTalerProtocolDuration( + Duration.fromSpec({ months: 1 }), + ), + }, + { + display_priority: 1, + measures: ["preserve-investigate"], + operation_type: LimitOperationType.deposit, + threshold: Amounts.stringify({ + currency, + fraction: 0, + value: 25 * 1000, + }), + timeframe: Duration.toTalerProtocolDuration( + Duration.fromSpec({ months: 1 }), + ), + }, + { + display_priority: 1, + measures: ["preserve-investigate"], + operation_type: LimitOperationType.aggregate, + threshold: Amounts.stringify({ + currency, + fraction: 0, + value: 25 * 1000, + }), + timeframe: Duration.toTalerProtocolDuration( + Duration.fromSpec({ months: 1 }), + ), + }, + ].filter((r) => { + return isWallet + ? WALLET_RULES.includes(r.operation_type) + : BANK_RULES.includes(r.operation_type); + }); diff --git a/packages/bank-ui/src/hooks/account.ts b/packages/bank-ui/src/hooks/account.ts @@ -23,18 +23,15 @@ import { TalerCoreBankResultByMethod, TalerError, TalerHttpError, - WithdrawalOperationStatusFlag, } from "@gnu-taler/taler-util"; -import { useState, useMemo } from "preact/hooks"; +import { useState } from "preact/hooks"; import { useSessionState } from "./session.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 import { - delayMs, - preventBurst, useAsync, - useLongPolling, useBankCoreApiContext, + useLongPolling, } from "@gnu-taler/web-util/browser"; import _useSWR, { mutate, SWRHook } from "swr"; import { PAGINATED_LIST_REQUEST } from "../utils.js"; diff --git a/packages/kyc-ui/src/hooks/kyc.ts b/packages/kyc-ui/src/hooks/kyc.ts @@ -13,11 +13,11 @@ 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 } from "@gnu-taler/taler-util"; -import { opFixedSuccess } from "@gnu-taler/taler-util"; import { AccessToken, + HttpStatusCode, KycProcessClientInformationWithEtag, + opFixedSuccess, } from "@gnu-taler/taler-util"; import { useAsync, @@ -44,7 +44,7 @@ export function useKycInfo(token?: AccessToken) { token === undefined ? undefined : () => { - return api.checkKycInfo(token, undefined); + return api.checkKycInfoSpa(token, undefined); }, [token], ); @@ -57,7 +57,7 @@ export function useKycInfo(token?: AccessToken) { return result.body; }, async (latestStatus: KycProcessClientInformationWithEtag) => { - const res = await api.checkKycInfo(token!, latestStatus.etag, { + const res = await api.checkKycInfoSpa(token!, latestStatus.etag, { timeoutMs: 5000, }); if (res.type === "fail" && res.case === HttpStatusCode.NotModified) { diff --git a/packages/kyc-ui/src/pages/FillForm.tsx b/packages/kyc-ui/src/pages/FillForm.tsx @@ -16,10 +16,10 @@ import { AbsoluteTime, AccessToken, - AmountJson, - Amounts, HttpStatusCode, KycRequirementInformation, + KycRequirementInformationId, + TalerFormAttributes, assertUnreachable, } from "@gnu-taler/taler-util"; import { @@ -42,8 +42,6 @@ import { Fragment, VNode, h } from "preact"; import { usePreferences } from "../context/preferences.js"; import { useUiFormsContext } from "../context/ui-forms.js"; import { preloadedForms } from "../forms/index.js"; -import { TalerFormAttributes } from "@gnu-taler/taler-util"; -import { KycRequirementInformationId } from "@gnu-taler/taler-util"; const TALER_SCREEN_ID = 103; @@ -57,7 +55,7 @@ type Props = { type FormType = { form_id: string; [TalerFormAttributes.FORM_ID]: string; - [TalerFormAttributes.FORM_VERSION]: number, + [TalerFormAttributes.FORM_VERSION]: number; // state: TalerExchangeApi.AmlState; }; @@ -149,7 +147,7 @@ function ShowForm({ case HttpStatusCode.Conflict: return i18n.str`Officer disabled or more recent decision was already submitted.`; default: - assertUnreachable(fail.case); + assertUnreachable(fail); } }, ); @@ -234,20 +232,12 @@ export function FillForm({ if (!reqId) { return ( <Attention title={i18n.str`Can't upload information`} type="danger"> - <i18n.Translate> - The KYC requirement doesn't have an ID - </i18n.Translate> + <i18n.Translate>The KYC requirement doesn't have an ID</i18n.Translate> </Attention> ); } - return ( - <ShowForm - onComplete={onComplete} - reqId={reqId} - theForm={theForm} - /> - ); + return <ShowForm onComplete={onComplete} reqId={reqId} theForm={theForm} />; } function searchForm( diff --git a/packages/taler-harness/src/harness/environments.ts b/packages/taler-harness/src/harness/environments.ts @@ -46,7 +46,7 @@ import { succeedOrThrow, TalerCorebankApiClient, TalerCoreBankHttpClient, - TalerExchangeHttpClient2, + TalerExchangeHttpClient, TalerMerchantApi, TalerMerchantInstanceHttpClient, TalerProtocolTimestamp, @@ -1098,7 +1098,7 @@ export interface KycTestEnv { amlKeypair: EddsaKeyPairStrings; merchant: MerchantService; bankApi: TalerCoreBankHttpClient; - exchangeApi: TalerExchangeHttpClient2; + exchangeApi: TalerExchangeHttpClient; wireGatewayApi: TalerWireGatewayHttpClient; merchantApi: TalerMerchantInstanceHttpClient; } @@ -1272,7 +1272,7 @@ export async function createKycTestkudosEnvironment( harnessHttpLib, ); - const exchangeApi = new TalerExchangeHttpClient2(exchange.baseUrl, { + const exchangeApi = new TalerExchangeHttpClient(exchange.baseUrl, { httpClient: harnessHttpLib, }); diff --git a/packages/taler-harness/src/harness/tops.ts b/packages/taler-harness/src/harness/tops.ts @@ -36,7 +36,6 @@ import { TalerCorebankApiClient, TalerCoreBankHttpClient, TalerExchangeHttpClient, - TalerExchangeHttpClient2, TalerMerchantInstanceHttpClient, TalerProtocolDuration, TalerProtocolTimestamp, @@ -948,7 +947,7 @@ export interface MeasuresTestEnvironment { challengerPostal: TestfakeChallengerService; challengerSms: TestfakeChallengerService; officerAcc: OfficerAccount; - exchangeClient: TalerExchangeHttpClient2; + exchangeClient: TalerExchangeHttpClient; } export async function setupMeasuresTestEnvironment( @@ -975,7 +974,7 @@ export async function setupMeasuresTestEnvironment( const merchantClient = new TalerMerchantInstanceHttpClient( merchant.makeInstanceBaseUrl(), ); - const exchangeClient = new TalerExchangeHttpClient2(exchange.baseUrl, { + const exchangeClient = new TalerExchangeHttpClient(exchange.baseUrl, { httpClient: harnessHttpLib, }); @@ -1143,7 +1142,7 @@ async function doTriggerReset( t: GlobalTestState, args: { officerAcc: OfficerAccount; - exchangeClient: TalerExchangeHttpClient2; + exchangeClient: TalerExchangeHttpClient; merchantPaytoHash: string; }, ): Promise<void> { @@ -1205,7 +1204,7 @@ async function doTriggerMeasure( t: GlobalTestState, args: { officerAcc: OfficerAccount; - exchangeClient: TalerExchangeHttpClient2; + exchangeClient: TalerExchangeHttpClient; merchantPaytoHash: string; measure: string; }, diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-idempotent.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-idempotent.ts @@ -25,7 +25,7 @@ import { ExchangeWithdrawRequest, getRandomBytes, succeedOrThrow, - TalerExchangeHttpClient2, + TalerExchangeHttpClient, } from "@gnu-taler/taler-util"; import { HttpRequestLibrary } from "@gnu-taler/taler-util/http"; import { @@ -117,7 +117,7 @@ async function myWithdrawCoin(args: { value: Amounts.parseOrThrow(denom.value), }); - const exchangeClient = new TalerExchangeHttpClient2(exchangeBaseUrl); + const exchangeClient = new TalerExchangeHttpClient(exchangeBaseUrl); const sigResp = await cryptoApi.signWithdrawal({ amount: Amounts.stringify(denom.value), diff --git a/packages/taler-util/src/aml/reporting.ts b/packages/taler-util/src/aml/reporting.ts @@ -1,4 +1,10 @@ -import { AbsoluteTime, Duration, OfficerAccount, TalerExchangeHttpClient, TOPS_AmlEventsName } from "../index.js"; +import { + AbsoluteTime, + Duration, + OfficerAccount, + TalerExchangeHttpClient, + TOPS_AmlEventsName, +} from "../index.js"; /** * Define a set of parameters to make a request to the server @@ -21,7 +27,7 @@ export type QueryModel<Ev> = { * https://docs.taler.net/deployments/tops.html#event-reporting-tops * * Maps a request key to request parameters - * + * */ export const EventReporting_TOPS_queries = { // Number of accounts that are opened @@ -107,12 +113,12 @@ export const EventReporting_TOPS_queries = { /** * All the calculation needed to create the Event Reporting (TOPS) * https://docs.taler.net/deployments/tops.html#event-reporting-tops - * - * Maps a event reporting name with a calculation which uses the + * + * Maps a event reporting name with a calculation which uses the * result of a query to the server. - * + * * @param events The result of event reporting query - * @returns + * @returns */ export const EventReporting_TOPS_calculation = ( events: CounterResultByEventName<typeof EventReporting_TOPS_queries>, @@ -159,9 +165,12 @@ export const EventReporting_TOPS_calculation = ( * * Maps a request key to request parameters. * Requires the times for the query range. - * + * */ -export const EventReporting_VQF_queries = (jan_1st: AbsoluteTime, dec_31st: AbsoluteTime) => { +export const EventReporting_VQF_queries = ( + jan_1st: AbsoluteTime, + dec_31st: AbsoluteTime, +) => { const zero = AbsoluteTime.fromMilliseconds(0); return { @@ -256,7 +265,9 @@ export const EventReporting_VQF_queries = (jan_1st: AbsoluteTime, dec_31st: Abso }; export const EventReporting_VQF_calculation = ( - events: CounterResultByEventName<ReturnType<typeof EventReporting_VQF_queries>>, + events: CounterResultByEventName< + ReturnType<typeof EventReporting_VQF_queries> + >, ) => { return { // Number of open accounts on January 1st (self-declaration 3.1.1) @@ -336,8 +347,10 @@ function safeAdd( return a === undefined || b == undefined ? undefined : a + b; } - -export async function fetchTopsInfoFromServer(api: TalerExchangeHttpClient, officer: OfficerAccount) { +export async function fetchTopsInfoFromServer( + api: TalerExchangeHttpClient, + officer: OfficerAccount, +) { type EventType = typeof EventReporting_TOPS_queries; const eventList = Object.entries(EventReporting_TOPS_queries); @@ -361,7 +374,12 @@ export async function fetchTopsInfoFromServer(api: TalerExchangeHttpClient, offi return EventReporting_TOPS_calculation(resultMap); } -export async function fetchVqfInfoFromServer(api: TalerExchangeHttpClient, officer: OfficerAccount, jan_1st: AbsoluteTime, dec_31st: AbsoluteTime) { +export async function fetchVqfInfoFromServer( + api: TalerExchangeHttpClient, + officer: OfficerAccount, + jan_1st: AbsoluteTime, + dec_31st: AbsoluteTime, +) { const VQF_EVENTS_THIS_YEAR = EventReporting_VQF_queries(jan_1st, dec_31st); type EventType = typeof VQF_EVENTS_THIS_YEAR; const eventList = Object.entries(VQF_EVENTS_THIS_YEAR); diff --git a/packages/taler-util/src/http-client/exchange-client.ts b/packages/taler-util/src/http-client/exchange-client.ts @@ -33,6 +33,7 @@ import { opEmptySuccess, opFixedSuccess, opKnownAlternativeHttpFailure, + opKnownFailure, opKnownHttpFailure, opSuccessFromHttp, opUnknownHttpFailure, @@ -40,12 +41,11 @@ import { import { encodeCrock } from "../taler-crypto.js"; import { AccessToken, - AmountString, EddsaPublicKeyString, EddsaSignatureString, + LongPollParams, OfficerAccount, PaginationParams, - ReserveAccount, codecForTalerCommonConfigResponse, } from "../types-taler-common.js"; import { @@ -72,12 +72,13 @@ import { ExchangeWithdrawResponse, KycAttributes, KycProcessClientInformation, + KycProcessClientInformationWithEtag, KycProcessStartInformation, KycRequirementInformationId, + LegitimizationMeasuresList, LegitimizationNeededResponse, PurseConflict, PurseConflictPartial, - WalletKycCheckResponse, WalletKycRequest, codecForAccountKycStatus, codecForAmlDecisionsResponse, @@ -100,7 +101,12 @@ import { codecForPurseConflict, codecForPurseConflictPartial, } from "../types-taler-exchange.js"; -import { CacheEvictor, addPaginationParams, nullEvictor } from "./utils.js"; +import { + CacheEvictor, + addLongPollingParam, + addPaginationParams, + nullEvictor, +} from "./utils.js"; import { TalerError } from "../errors.js"; import { @@ -110,20 +116,19 @@ import { LongpollQueue, signAmlDecision, signAmlQuery, - signWalletAccountSetup, } from "../index.js"; import { TalerErrorCode } from "../taler-error-codes.js"; import { AbsoluteTime } from "../time.js"; import { EmptyObject, codecForEmptyObject } from "../types-taler-wallet.js"; export type TalerExchangeResultByMethod2< - prop extends keyof TalerExchangeHttpClient2, -> = ResultByMethod<TalerExchangeHttpClient2, prop>; + prop extends keyof TalerExchangeHttpClient, +> = ResultByMethod<TalerExchangeHttpClient, prop>; export type TalerExchangeErrorsByMethod2< - prop extends keyof TalerExchangeHttpClient2, -> = FailCasesByMethod<TalerExchangeHttpClient2, prop>; + prop extends keyof TalerExchangeHttpClient, +> = FailCasesByMethod<TalerExchangeHttpClient, prop>; -export enum TalerExchangeCacheEviction2 { +export enum TalerExchangeCacheEviction { UPLOAD_KYC_FORM, MAKE_AML_DECISION, } @@ -131,10 +136,10 @@ export enum TalerExchangeCacheEviction2 { /** * Client library for the GNU Taler exchange service. */ -export class TalerExchangeHttpClient2 { +export class TalerExchangeHttpClient { public static readonly SUPPORTED_EXCHANGE_PROTOCOL_VERSION = "27:0:2"; private httpLib: HttpRequestLibrary; - private cacheEvictor: CacheEvictor<TalerExchangeCacheEviction2>; + private cacheEvictor: CacheEvictor<TalerExchangeCacheEviction>; private preventCompression: boolean; private cancelationToken: CancellationToken; private longPollQueue: LongpollQueue; @@ -143,7 +148,7 @@ export class TalerExchangeHttpClient2 { readonly baseUrl: string, params: { httpClient?: HttpRequestLibrary; - cacheEvictor?: CacheEvictor<TalerExchangeCacheEviction2>; + cacheEvictor?: CacheEvictor<TalerExchangeCacheEviction>; preventCompression?: boolean; cancelationToken?: CancellationToken; longPollQueue?: LongpollQueue; @@ -159,7 +164,7 @@ export class TalerExchangeHttpClient2 { isCompatible(version: string): boolean { const compare = LibtoolVersion.compare( - TalerExchangeHttpClient2.SUPPORTED_EXCHANGE_PROTOCOL_VERSION, + TalerExchangeHttpClient.SUPPORTED_EXCHANGE_PROTOCOL_VERSION, version, ); return compare?.compatible ?? false; @@ -243,7 +248,7 @@ export class TalerExchangeHttpClient2 { code: TalerErrorCode.GENERIC_CLIENT_UNSUPPORTED_PROTOCOL_VERSION, requestUrl: resp.requestUrl, httpStatusCode: resp.status, - detail: `Unsupported protocol version, client supports ${TalerExchangeHttpClient2.SUPPORTED_EXCHANGE_PROTOCOL_VERSION}, server supports ${minBody.version}`, + detail: `Unsupported protocol version, client supports ${TalerExchangeHttpClient.SUPPORTED_EXCHANGE_PROTOCOL_VERSION}, server supports ${minBody.version}`, }); } // Now that we've checked the basic body, re-parse the full response. @@ -568,26 +573,10 @@ export class TalerExchangeHttpClient2 { * https://docs.taler.net/core/api-exchange.html#post--kyc-wallet * */ - async notifyKycBalanceLimit( - account: ReserveAccount, - balance: AmountString, - ): Promise< - | OperationOk<void> - | OperationFail<HttpStatusCode.Forbidden> - | OperationAlternative< - HttpStatusCode.UnavailableForLegalReasons, - LegitimizationNeededResponse - > - | OperationOk<WalletKycCheckResponse> - > { - const body: WalletKycRequest = { - balance, - reserve_pub: account.id as any, - reserve_sig: encodeCrock( - signWalletAccountSetup(account.signingKey, balance), - ) as any, - }; - const resp = await this.fetch(`kyc-wallet`, { + async notifyKycBalanceLimit(body: WalletKycRequest) { + const url = new URL(`kyc-wallet`, this.baseUrl); + + const resp = await this.httpLib.fetch(url.href, { method: "POST", body, }); @@ -610,6 +599,35 @@ export class TalerExchangeHttpClient2 { } } + async getAmlLegitimizations(args: { + officerAcc: OfficerAccount; + }): Promise<OperationOk<LegitimizationMeasuresList>> { + const url = new URL( + `aml/${args.officerAcc.id}/legitimizations`, + this.baseUrl, + ); + + const resp = await this.httpLib.fetch(url.href, { + headers: { + "Taler-AML-Officer-Signature": encodeCrock( + signAmlQuery(args.officerAcc.signingKey), + ), + }, + }); + + switch (resp.status) { + case HttpStatusCode.Ok: + // FIXME: Parse properly. + return opSuccessFromHttp(resp, codecForAny()); + case HttpStatusCode.NoContent: + return opFixedSuccess({ + measures: [], + }); + default: + return opUnknownHttpFailure(resp); + } + } + /** * https://docs.taler.net/core/api-exchange.html#get--kyc-check-$H_NORMALIZED_PAYTO * @@ -760,6 +778,51 @@ export class TalerExchangeHttpClient2 { } /** + * SPA-Specific version of checkKycInfo + */ + async checkKycInfoSpa( + token: AccessToken, + etag: string | undefined, + params: LongPollParams = {}, + ) { + const url = new URL(`kyc-info/${token}`, this.baseUrl); + + addLongPollingParam(url, params); + + const resp = await this.httpLib.fetch(url.href, { + method: "GET", + headers: { + "If-None-Match": etag, + }, + }); + switch (resp.status) { + case HttpStatusCode.Ok: { + // we need to add the etag to the response because the + // client needs it to repeat the request and + // do the long polling + const etag = resp.headers.get("etag") ?? undefined; + const body = await readSuccessResponseJsonOrThrow( + resp, + codecForKycProcessClientInformation(), + ); + return opFixedSuccess<KycProcessClientInformationWithEtag>({ + ...body, + etag, + }); + } + case HttpStatusCode.Accepted: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NoContent: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotModified: + // do not read details from response + return opKnownFailure(resp.status); + default: + return opUnknownHttpFailure(resp); + } + } + + /** * https://docs.taler.net/core/api-exchange.html#post--kyc-upload-$ID * */ @@ -780,7 +843,7 @@ export class TalerExchangeHttpClient2 { switch (resp.status) { case HttpStatusCode.NoContent: { this.cacheEvictor.notifySuccess( - TalerExchangeCacheEviction2.UPLOAD_KYC_FORM, + TalerExchangeCacheEviction.UPLOAD_KYC_FORM, ); return opEmptySuccess(); } @@ -1071,7 +1134,7 @@ export class TalerExchangeHttpClient2 { switch (resp.status) { case HttpStatusCode.NoContent: { this.cacheEvictor.notifySuccess( - TalerExchangeCacheEviction2.MAKE_AML_DECISION, + TalerExchangeCacheEviction.MAKE_AML_DECISION, ); return opEmptySuccess(); } diff --git a/packages/taler-util/src/http-client/exchange.ts b/packages/taler-util/src/http-client/exchange.ts @@ -1,1001 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2022-2025 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 { codecForAny } from "../codec.js"; -import { - HttpRequestLibrary, - readSuccessResponseJsonOrThrow, -} from "../http-common.js"; -import { HttpStatusCode } from "../http-status-codes.js"; -import { createPlatformHttpLib } from "../http.js"; -import { LibtoolVersion } from "../libtool-version.js"; -import { - FailCasesByMethod, - OperationAlternative, - OperationFail, - OperationOk, - ResultByMethod, - opEmptySuccess, - opFixedSuccess, - opKnownAlternativeHttpFailure, - opKnownFailure, - opKnownHttpFailure, - opSuccessFromHttp, - opUnknownHttpFailure, -} from "../operation.js"; -import { encodeCrock } from "../taler-crypto.js"; -import { - AccessToken, - EddsaPublicKeyString, - EddsaSignatureString, - LongPollParams, - OfficerAccount, - PaginationParams, - codecForTalerCommonConfigResponse, -} from "../types-taler-common.js"; -import { - AccountKycStatus, - AmlDecisionRequest, - ExchangeKycUploadFormRequest, - ExchangePurseDeposits, - ExchangePurseMergeRequest, - ExchangeReservePurseRequest, - ExchangeVersionResponse, - KycProcessClientInformation, - KycRequirementInformationId, - LegitimizationMeasuresList, - PurseCreate, - WalletKycRequest, - codecForAccountKycStatus, - codecForAmlDecisionsResponse, - codecForAmlKycAttributes, - codecForAmlWalletKycCheckResponse, - codecForAvailableMeasureSummary, - codecForEventCounter, - codecForExchangeConfig, - codecForExchangeGetContractResponse, - codecForExchangeKeysResponse, - codecForExchangeMergeConflictResponse, - codecForExchangeMergeSuccessResponse, - codecForExchangePurseStatus, - codecForExchangeTransferList, - codecForKycProcessClientInformation, - codecForKycProcessStartInformation, - codecForLegitimizationNeededResponse, - codecForPurseConflict, - codecForPurseConflictPartial, -} from "../types-taler-exchange.js"; -import { - CacheEvictor, - addLongPollingParam, - addPaginationParams, - nullEvictor, -} from "./utils.js"; - -import { TalerError } from "../errors.js"; -import { - AmountJson, - Amounts, - CancellationToken, - LongpollQueue, - signAmlDecision, - signAmlQuery, -} from "../index.js"; -import { TalerErrorCode } from "../taler-error-codes.js"; -import { AbsoluteTime } from "../time.js"; - -export type TalerExchangeResultByMethod< - prop extends keyof TalerExchangeHttpClient, -> = ResultByMethod<TalerExchangeHttpClient, prop>; -export type TalerExchangeErrorsByMethod< - prop extends keyof TalerExchangeHttpClient, -> = FailCasesByMethod<TalerExchangeHttpClient, prop>; - -export enum TalerExchangeCacheEviction { - UPLOAD_KYC_FORM, - MAKE_AML_DECISION, -} - -declare const __pubId: unique symbol; -export type ReservePub = string & { [__pubId]: true }; - -export type KycProcessClientInformationWithEtag = - KycProcessClientInformation & { etag: string | undefined }; - -/** - * Client library for the GNU Taler exchange service. - * - * FIXME: This client library is currently used by the SPA. - * However, we should merge it with the other exchange client implementation. - */ -export class TalerExchangeHttpClient { - public static readonly PROTOCOL_VERSION = "28:0:2"; - private httpLib: HttpRequestLibrary; - private cacheEvictor: CacheEvictor<TalerExchangeCacheEviction>; - private preventCompression: boolean; - - constructor( - readonly baseUrl: string, - params: { - httpClient?: HttpRequestLibrary; - cacheEvictor?: CacheEvictor<TalerExchangeCacheEviction>; - preventCompression?: boolean; - cancelationToken?: CancellationToken; - longPollQueue?: LongpollQueue; - }, - ) { - this.httpLib = params.httpClient ?? createPlatformHttpLib(); - this.cacheEvictor = params.cacheEvictor ?? nullEvictor; - this.preventCompression = !!params.preventCompression; - } - - isCompatible(version: string): boolean { - const compare = LibtoolVersion.compare( - TalerExchangeHttpClient.PROTOCOL_VERSION, - version, - ); - return compare?.compatible ?? false; - } - - // EXCHANGE INFORMATION - - /** - * https://docs.taler.net/core/api-exchange.html#get--seed - * - */ - async getSeed() { - const url = new URL(`seed`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "GET", - }); - switch (resp.status) { - case HttpStatusCode.Ok: - const buffer = await resp.bytes(); - const uintar = new Uint8Array(buffer); - return opFixedSuccess(uintar); - case HttpStatusCode.NotFound: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - /** - * https://docs.taler.net/core/api-exchange.html#get--config - * - */ - async getConfig(): Promise< - | OperationFail<HttpStatusCode.NotFound> - | OperationOk<ExchangeVersionResponse> - > { - const url = new URL(`config`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "GET", - }); - switch (resp.status) { - case HttpStatusCode.Ok: { - const minBody = await readSuccessResponseJsonOrThrow( - resp, - codecForTalerCommonConfigResponse(), - ); - const expectedName = "taler-exchange"; - if (minBody.name !== expectedName) { - throw TalerError.fromUncheckedDetail({ - code: TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR, - requestUrl: resp.requestUrl, - httpStatusCode: resp.status, - detail: `Unexpected server component name (got ${minBody.name}, expected ${expectedName}})`, - }); - } - if (!this.isCompatible(minBody.version)) { - throw TalerError.fromUncheckedDetail({ - code: TalerErrorCode.GENERIC_CLIENT_UNSUPPORTED_PROTOCOL_VERSION, - requestUrl: resp.requestUrl, - httpStatusCode: resp.status, - detail: `Unsupported protocol version, client supports ${TalerExchangeHttpClient.PROTOCOL_VERSION}, server supports ${minBody.version}`, - }); - } - // Now that we've checked the basic body, re-parse the full response. - const body = await readSuccessResponseJsonOrThrow( - resp, - codecForExchangeConfig(), - ); - return opFixedSuccess(body); - } - case HttpStatusCode.NotFound: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-merchant.html#get--config - * - * PARTIALLY IMPLEMENTED!! - */ - async getKeys() { - const url = new URL(`keys`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "GET", - }); - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForExchangeKeysResponse()); - default: - return opUnknownHttpFailure(resp); - } - } - - // WALLET TO WALLET - - /** - * https://docs.taler.net/core/api-exchange.html#get--purses-$PURSE_PUB-merge - * - */ - async getPurseStatusAtMerge(pursePub: EddsaPublicKeyString) { - const url = new URL(`purses/${pursePub}/merge`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "GET", - }); - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForExchangePurseStatus()); - case HttpStatusCode.Gone: - case HttpStatusCode.NotFound: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#get--purses-$PURSE_PUB-deposit - * - */ - async getPurseStatusAtDeposit(pursePub: EddsaPublicKeyString) { - const url = new URL(`purses/${pursePub}/deposit`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "GET", - }); - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForExchangePurseStatus()); - case HttpStatusCode.Gone: - case HttpStatusCode.NotFound: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#post--purses-$PURSE_PUB-create - * - */ - async createPurseFromDeposit( - pursePub: EddsaPublicKeyString, - body: PurseCreate, - ) { - const url = new URL(`purses/${pursePub}/create`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - body, - }); - switch (resp.status) { - case HttpStatusCode.Ok: - // FIXME: parse PurseCreateSuccessResponse - return opSuccessFromHttp(resp, codecForAny()); - case HttpStatusCode.Conflict: - return opKnownAlternativeHttpFailure( - resp, - resp.status, - codecForPurseConflict(), - ); - case HttpStatusCode.Forbidden: - case HttpStatusCode.NotFound: - case HttpStatusCode.TooEarly: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#delete--purses-$PURSE_PUB - * - */ - async deletePurse(pursePub: string, purseSig: string) { - const url = new URL(`purses/${pursePub}`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "DELETE", - headers: { - "taler-purse-signature": purseSig, - }, - }); - switch (resp.status) { - case HttpStatusCode.NoContent: - return opEmptySuccess(); - case HttpStatusCode.NotFound: - return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Conflict: - return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Forbidden: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * POST /purses/$PURSE_PUB/merge - * - * https://docs.taler.net/core/api-exchange.html#post--purses-$PURSE_PUB-merge - */ - async postPurseMerge(pursePub: string, body: ExchangePurseMergeRequest) { - const url = new URL(`purses/${pursePub}/merge`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - body, - }); - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForExchangeMergeSuccessResponse()); - case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeHttpFailure( - resp, - resp.status, - codecForLegitimizationNeededResponse(), - ); - case HttpStatusCode.Conflict: - return opKnownAlternativeHttpFailure( - resp, - resp.status, - codecForExchangeMergeConflictResponse(), - ); - case HttpStatusCode.Gone: - case HttpStatusCode.Forbidden: - case HttpStatusCode.NotFound: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#post--reserves-$RESERVE_PUB-purse - * - */ - async createPurseFromReserve( - pursePub: EddsaPublicKeyString, - body: ExchangeReservePurseRequest, - ) { - const url = new URL(`reserves/${pursePub}/purse`, this.baseUrl); - - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - body, - }); - switch (resp.status) { - case HttpStatusCode.Ok: - // FIXME: parse PurseCreateSuccessResponse - return opSuccessFromHttp(resp, codecForAny()); - case HttpStatusCode.PaymentRequired: - case HttpStatusCode.Forbidden: - case HttpStatusCode.NotFound: - return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Conflict: - return opKnownAlternativeHttpFailure( - resp, - resp.status, - codecForPurseConflictPartial(), - ); - case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeHttpFailure( - resp, - resp.status, - codecForLegitimizationNeededResponse(), - ); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#get--contracts-$CONTRACT_PUB - * - */ - async getContract(pursePub: EddsaPublicKeyString) { - const url = new URL(`contracts/${pursePub}`, this.baseUrl); - - const resp = await this.httpLib.fetch(url.href, { - method: "GET", - }); - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForExchangeGetContractResponse()); - case HttpStatusCode.NotFound: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#post--purses-$PURSE_PUB-deposit - * - */ - async depositIntoPurse( - pursePub: EddsaPublicKeyString, - body: ExchangePurseDeposits, - ) { - const url = new URL(`purses/${pursePub}/deposit`, this.baseUrl); - - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - body, - }); - switch (resp.status) { - case HttpStatusCode.Ok: - // FIXME: parse PurseDepositSuccessResponse - return opSuccessFromHttp(resp, codecForAny()); - case HttpStatusCode.Conflict: - return opKnownAlternativeHttpFailure( - resp, - resp.status, - codecForPurseConflict(), - ); - case HttpStatusCode.Forbidden: - case HttpStatusCode.NotFound: - case HttpStatusCode.Gone: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - // WADS - - /** - * https://docs.taler.net/core/api-exchange.html#get--wads-$WAD_ID - * - */ - async getWadInfo(): Promise<never> { - throw Error("not yet implemented"); - } - - // - // KYC - // - - /** - * https://docs.taler.net/core/api-exchange.html#post--kyc-wallet - * - */ - async notifyKycBalanceLimit(body: WalletKycRequest) { - const url = new URL(`kyc-wallet`, this.baseUrl); - - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - body, - }); - - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForAmlWalletKycCheckResponse()); - case HttpStatusCode.NoContent: - return opEmptySuccess(); - case HttpStatusCode.Forbidden: - return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeHttpFailure( - resp, - resp.status, - codecForLegitimizationNeededResponse(), - ); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * Endpoint: GET /kyc-check/$H_NORMALIZED_PAYTO - * - * https://docs.taler.net/core/api-exchange.html#get--kyc-check-$H_NORMALIZED_PAYTO - */ - async checkKycStatus(args: { - paytoHash: string; - accountPub: EddsaPublicKeyString; - accountSig: EddsaSignatureString; - longpoll?: boolean; - awaitAuth?: boolean; - }): Promise< - | OperationOk<void> - | OperationAlternative<HttpStatusCode.Ok, AccountKycStatus> - | OperationAlternative<HttpStatusCode.Accepted, AccountKycStatus> - | OperationFail<HttpStatusCode.Forbidden> - | OperationFail<HttpStatusCode.NotFound> - | OperationFail<HttpStatusCode.Conflict> - > { - const { paytoHash, accountPub, accountSig, longpoll, awaitAuth } = args; - const url = new URL(`kyc-check/${paytoHash}`, this.baseUrl); - if (awaitAuth !== undefined) { - url.searchParams.set("await_auth", awaitAuth ? "YES" : "NO"); - } - - const resp = await this.httpLib.fetch(url.href, { - headers: { - "Account-Owner-Signature": accountSig, - "Account-Owner-Pub": accountPub, - }, - }); - - switch (resp.status) { - case HttpStatusCode.Ok: - case HttpStatusCode.Accepted: - return opKnownAlternativeHttpFailure( - resp, - resp.status, - codecForAccountKycStatus(), - ); - case HttpStatusCode.NoContent: - return opEmptySuccess(); - case HttpStatusCode.Forbidden: - case HttpStatusCode.NotFound: - case HttpStatusCode.Conflict: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#get--kyc-info-$ACCESS_TOKEN - * - */ - async checkKycInfo( - token: AccessToken, - etag: string | undefined, - params: LongPollParams = {}, - ) { - const url = new URL(`kyc-info/${token}`, this.baseUrl); - - addLongPollingParam(url, params); - - const resp = await this.httpLib.fetch(url.href, { - method: "GET", - headers: { - "If-None-Match": etag, - }, - }); - switch (resp.status) { - case HttpStatusCode.Ok: { - // we need to add the etag to the response because the - // client needs it to repeat the request and - // do the long polling - const etag = resp.headers.get("etag") ?? undefined; - const body = await readSuccessResponseJsonOrThrow( - resp, - codecForKycProcessClientInformation(), - ); - return opFixedSuccess<KycProcessClientInformationWithEtag>({ - ...body, - etag, - }); - } - case HttpStatusCode.Accepted: - return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NoContent: - return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotModified: - // do not read details from response - return opKnownFailure(resp.status); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#post--kyc-upload-$ID - * - */ - async uploadKycForm<T extends ExchangeKycUploadFormRequest>( - requirement: KycRequirementInformationId, - body: T, - ) { - const url = new URL(`kyc-upload/${requirement}`, this.baseUrl); - - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - body, - compress: this.preventCompression ? undefined : "deflate", - }); - switch (resp.status) { - case HttpStatusCode.NoContent: { - this.cacheEvictor.notifySuccess( - TalerExchangeCacheEviction.UPLOAD_KYC_FORM, - ); - return opEmptySuccess(); - } - case HttpStatusCode.NotFound: - case HttpStatusCode.Conflict: - case HttpStatusCode.PayloadTooLarge: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#post--kyc-start-$ID - * - */ - async startExternalKycProcess( - requirement: KycRequirementInformationId, - body: object = {}, - ) { - const url = new URL(`kyc-start/${requirement}`, this.baseUrl); - - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - body, - }); - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForKycProcessStartInformation()); - case HttpStatusCode.NotFound: - case HttpStatusCode.Conflict: - case HttpStatusCode.PayloadTooLarge: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#get--kyc-proof-$PROVIDER_NAME?state=$H_PAYTO - * - */ - async completeExternalKycProcess( - provider: string, - state: string, - code: string, - ) { - const url = new URL(`kyc-proof/${provider}`, this.baseUrl); - url.searchParams.set("state", state); - url.searchParams.set("code", code); - const resp = await this.httpLib.fetch(url.href, { - method: "GET", - redirect: "manual", - }); - - switch (resp.status) { - case HttpStatusCode.SeeOther: - return opEmptySuccess(); - case HttpStatusCode.NotFound: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - // - // AML operations - // - - /** - * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-measures - * - */ - async getAmlMeasures(auth: OfficerAccount) { - const url = new URL(`aml/${auth.id}/measures`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "GET", - headers: { - "Taler-AML-Officer-Signature": encodeCrock( - signAmlQuery(auth.signingKey), - ), - }, - }); - - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForAvailableMeasureSummary()); - case HttpStatusCode.Conflict: - case HttpStatusCode.NotFound: - case HttpStatusCode.Forbidden: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-measures - * - */ - async getAmlKycStatistics( - auth: OfficerAccount, - name: string, - filter: { - since?: AbsoluteTime; - until?: AbsoluteTime; - } = {}, - ) { - const url = new URL(`aml/${auth.id}/kyc-statistics/${name}`, this.baseUrl); - - if (filter.since !== undefined && filter.since.t_ms !== "never") { - url.searchParams.set("start_date", String(filter.since.t_ms)); - } - if (filter.until !== undefined && filter.until.t_ms !== "never") { - url.searchParams.set("end_date", String(filter.until.t_ms)); - } - - const resp = await this.httpLib.fetch(url.href, { - method: "GET", - headers: { - "Taler-AML-Officer-Signature": encodeCrock( - signAmlQuery(auth.signingKey), - ), - }, - }); - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForEventCounter()); - case HttpStatusCode.Conflict: - case HttpStatusCode.NotFound: - case HttpStatusCode.Forbidden: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-decisions - * - */ - async getAmlDecisions( - auth: OfficerAccount, - params: PaginationParams & { - account?: string; - active?: boolean; - investigation?: boolean; - } = {}, - ) { - const url = new URL(`aml/${auth.id}/decisions`, this.baseUrl); - - addPaginationParams(url, params); - if (params.account !== undefined) { - url.searchParams.set("h_payto", params.account); - } - if (params.active !== undefined) { - url.searchParams.set("active", params.active ? "YES" : "NO"); - } - if (params.investigation !== undefined) { - url.searchParams.set( - "investigation", - params.investigation ? "YES" : "NO", - ); - } - - const resp = await this.httpLib.fetch(url.href, { - headers: { - "Taler-AML-Officer-Signature": encodeCrock( - signAmlQuery(auth.signingKey), - ), - }, - }); - - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForAmlDecisionsResponse()); - case HttpStatusCode.NoContent: - return opFixedSuccess({ records: [] }); - case HttpStatusCode.Forbidden: - case HttpStatusCode.NotFound: - case HttpStatusCode.Conflict: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-decisions - * - */ - async getAmlLegitimizations(args: { - officerAcc: OfficerAccount; - }): Promise<OperationOk<LegitimizationMeasuresList>> { - const url = new URL( - `aml/${args.officerAcc.id}/legitimizations`, - this.baseUrl, - ); - - const resp = await this.httpLib.fetch(url.href, { - headers: { - "Taler-AML-Officer-Signature": encodeCrock( - signAmlQuery(args.officerAcc.signingKey), - ), - }, - }); - - switch (resp.status) { - case HttpStatusCode.Ok: - // FIXME: Parse properly. - return opSuccessFromHttp(resp, codecForAny()); - case HttpStatusCode.NoContent: - return opFixedSuccess({ - measures: [], - }); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-attributes-$H_PAYTO - * - */ - async getAmlAttributesForAccount( - auth: OfficerAccount, - account: string, - params: PaginationParams = {}, - ) { - const url = new URL(`aml/${auth.id}/attributes/${account}`, this.baseUrl); - - addPaginationParams(url, params); - const resp = await this.httpLib.fetch(url.href, { - headers: { - "Taler-AML-Officer-Signature": encodeCrock( - signAmlQuery(auth.signingKey), - ), - }, - }); - - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForAmlKycAttributes()); - case HttpStatusCode.NoContent: - return opFixedSuccess({ details: [] }); - case HttpStatusCode.Forbidden: - case HttpStatusCode.NotFound: - case HttpStatusCode.Conflict: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#post--aml-$OFFICER_PUB-decision - * - */ - async makeAmlDesicion( - auth: OfficerAccount, - decision: Omit<AmlDecisionRequest, "officer_sig">, - ) { - const body: AmlDecisionRequest = { - officer_sig: encodeCrock( - signAmlDecision(auth.signingKey, decision), - ) as EddsaSignatureString, - ...decision, - }; - const url = new URL(`aml/${auth.id}/decision`, this.baseUrl); - - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - headers: { - "Taler-AML-Officer-Signature": encodeCrock( - signAmlQuery(auth.signingKey), - ), - }, - body, - compress: this.preventCompression ? undefined : "deflate", - }); - - switch (resp.status) { - case HttpStatusCode.NoContent: { - this.cacheEvictor.notifySuccess( - TalerExchangeCacheEviction.MAKE_AML_DECISION, - ); - return opEmptySuccess(); - } - case HttpStatusCode.Forbidden: - case HttpStatusCode.NotFound: - case HttpStatusCode.Conflict: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-transfers-credit - * - */ - async getTransfersCredit( - auth: OfficerAccount, - params: PaginationParams & { threshold?: AmountJson } = {}, - ) { - const url = new URL(`aml/${auth.id}/transfers-credit`, this.baseUrl); - - addPaginationParams(url, params); - - if (params.threshold) { - url.searchParams.set("threshold", Amounts.stringify(params.threshold)); - } - - const resp = await this.httpLib.fetch(url.href, { - headers: { - "Taler-AML-Officer-Signature": encodeCrock( - signAmlQuery(auth.signingKey), - ), - }, - }); - - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForExchangeTransferList()); - case HttpStatusCode.NoContent: - return opFixedSuccess({ transfers: [] }); - case HttpStatusCode.Forbidden: - case HttpStatusCode.NotFound: - case HttpStatusCode.Conflict: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } - - /** - * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-transfers-debit - * - */ - async getTransfersDebit( - auth: OfficerAccount, - params: PaginationParams & { threshold?: AmountJson } = {}, - ) { - const url = new URL(`aml/${auth.id}/transfers-debit`, this.baseUrl); - - addPaginationParams(url, params); - - if (params.threshold) { - url.searchParams.set("threshold", Amounts.stringify(params.threshold)); - } - - const resp = await this.httpLib.fetch(url.href, { - headers: { - "Taler-AML-Officer-Signature": encodeCrock( - signAmlQuery(auth.signingKey), - ), - }, - }); - - switch (resp.status) { - case HttpStatusCode.Ok: - return opSuccessFromHttp(resp, codecForExchangeTransferList()); - case HttpStatusCode.NoContent: - return opFixedSuccess({ transfers: [] }); - case HttpStatusCode.Forbidden: - case HttpStatusCode.NotFound: - case HttpStatusCode.Conflict: - return opKnownHttpFailure(resp.status, resp); - default: - return opUnknownHttpFailure(resp); - } - } -} diff --git a/packages/taler-util/src/http-client/officer-account.ts b/packages/taler-util/src/http-client/officer-account.ts @@ -21,7 +21,6 @@ import { OfficerAccount, OfficerId, ReserveAccount, - ReservePub, createEddsaKeyPair, decodeCrock, decryptWithDerivedKey, @@ -30,7 +29,7 @@ import { encryptWithDerivedKey, getRandomBytes, kdf, - stringToBytes + stringToBytes, } from "@gnu-taler/taler-util"; /** @@ -112,21 +111,25 @@ export async function createNewWalletKycAccount( ): Promise<ReserveAccount & { safe?: LockedAccount }> { const { eddsaPriv, eddsaPub } = createEddsaKeyPair(); - const mergedRnd: EncryptionNonceP = extraNonce && password - ? kdf(24, stringToBytes("aml-officer"), extraNonce, getRandomBytes(24)) - : getRandomBytes(24); + const mergedRnd: EncryptionNonceP = + extraNonce && password + ? kdf(24, stringToBytes("aml-officer"), extraNonce, getRandomBytes(24)) + : getRandomBytes(24); - - const protectedPrivKey = password ? await encryptWithDerivedKey( - mergedRnd, - stringToBytes(password), - eddsaPriv, - password, - ) : undefined; + const protectedPrivKey = password + ? await encryptWithDerivedKey( + mergedRnd, + stringToBytes(password), + eddsaPriv, + password, + ) + : undefined; const signingKey = eddsaPriv as EddsaPrivP; - const accountId = encodeCrock(eddsaPub) as ReservePub; - const safe = protectedPrivKey ? encodeCrock(protectedPrivKey) as LockedAccount : undefined; + const accountId = encodeCrock(eddsaPub); + const safe = protectedPrivKey + ? (encodeCrock(protectedPrivKey) as LockedAccount) + : undefined; return { id: accountId, signingKey, safe }; } diff --git a/packages/taler-util/src/index.ts b/packages/taler-util/src/index.ts @@ -16,7 +16,6 @@ export * from "./http-client/bank-integration.js"; export * from "./http-client/bank-revenue.js"; export * from "./http-client/bank-wire.js"; export * from "./http-client/challenger.js"; -export * from "./http-client/exchange.js"; export * from "./http-client/exchange-client.js"; export * from "./http-client/merchant.js"; export * from "./http-client/officer-account.js"; diff --git a/packages/taler-util/src/types-taler-common.ts b/packages/taler-util/src/types-taler-common.ts @@ -39,11 +39,10 @@ import { codecForString, } from "./codec.js"; import { + EddsaPrivP, codecForEither, codecForList, codecOptional, - EddsaPrivP, - ReservePub, } from "./index.js"; import { TalerProtocolDuration, @@ -194,11 +193,11 @@ export type AmountString = // export type AmountString = string; export type Base32String = string; declare const __eddsasign_str: unique symbol; -export type EddsaSignatureString = string;// & { [__eddsasign_str]: true }; +export type EddsaSignatureString = string; // & { [__eddsasign_str]: true }; declare const __eddsapub_str: unique symbol; -export type EddsaPublicKeyString = string;// & { [__eddsapub_str]: true }; +export type EddsaPublicKeyString = string; // & { [__eddsapub_str]: true }; declare const __eddsapriv_str: unique symbol; -export type EddsaPrivateKeyString = string;// & { [__eddsapriv_str]: true }; +export type EddsaPrivateKeyString = string; // & { [__eddsapriv_str]: true }; export type CoinPublicKeyString = string; // FIXME: implement this codec @@ -210,11 +209,14 @@ export const codecForCurrencyName = codecForString; // FIXME: implement this codec export const codecForDecimalNumber = codecForString; // FIXME: implement this codec -export const codecForEddsaPublicKey = codecForString as () => Codec<EddsaPublicKeyString>; +export const codecForEddsaPublicKey = + codecForString as () => Codec<EddsaPublicKeyString>; // FIXME: implement this codec -export const codecForEddsaPrivateKey = codecForString as () => Codec<EddsaPrivateKeyString>; +export const codecForEddsaPrivateKey = + codecForString as () => Codec<EddsaPrivateKeyString>; // FIXME: implement this codec -export const codecForEddsaSignature = codecForString as () => Codec<EddsaSignatureString>; +export const codecForEddsaSignature = + codecForString as () => Codec<EddsaSignatureString>; export const codecForInternationalizedString = (): Codec<InternationalizedString> => codecForMap(codecForString()); @@ -359,7 +361,11 @@ export const codecForCoinHistoryResponse = () => .build("CoinHistoryResponse"); export type TokenScope = BankTokenScope | MerchantTokenScope; -export type BankTokenScope = "readonly" | "readwrite" | "revenue" | "wiregateway"; +export type BankTokenScope = + | "readonly" + | "readwrite" + | "revenue" + | "wiregateway"; export type MerchantTokenScope = "write"; export interface TokenRequest { // Service-defined scope for the token. @@ -567,7 +573,7 @@ export interface OfficerAccount { } export interface ReserveAccount { - id: ReservePub; + id: EddsaPublicKeyString; signingKey: EddsaPrivP; } diff --git a/packages/taler-util/src/types-taler-exchange.ts b/packages/taler-util/src/types-taler-exchange.ts @@ -2014,6 +2014,9 @@ export interface KycProcessClientInformation { voluntary_measures?: KycRequirementInformation[]; } +export type KycProcessClientInformationWithEtag = + KycProcessClientInformation & { etag: string | undefined }; + declare const opaque_brand: unique symbol; declare const opaque_kycReq: unique symbol; diff --git a/packages/taler-wallet-core/src/dbless.ts b/packages/taler-wallet-core/src/dbless.ts @@ -39,7 +39,7 @@ import { ExchangeRefreshRevealRequestV2, Logger, TalerCorebankApiClient, - TalerExchangeHttpClient2, + TalerExchangeHttpClient, UnblindedDenominationSignature, codecForAny, codecForBankWithdrawalOperationPostResponse, @@ -147,7 +147,7 @@ export async function withdrawCoin(args: { secretSeed: encodeCrock(getRandomBytes(32)), value: Amounts.parseOrThrow(denom.value), }); - const exchangeClient = new TalerExchangeHttpClient2(exchangeBaseUrl); + const exchangeClient = new TalerExchangeHttpClient(exchangeBaseUrl); const sigResp = await cryptoApi.signWithdrawal({ amount: Amounts.stringify(denom.value), @@ -323,7 +323,7 @@ export async function refreshCoin(req: { logger.info(`requesting melt: ${j2s(meltReqBody)}`); - const exchangeClient = new TalerExchangeHttpClient2(oldCoin.exchangeBaseUrl); + const exchangeClient = new TalerExchangeHttpClient(oldCoin.exchangeBaseUrl); const meltResponse = succeedOrThrow( await exchangeClient.postMelt({ body: meltReqBody }), diff --git a/packages/taler-wallet-core/src/versions.ts b/packages/taler-wallet-core/src/versions.ts @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { TalerExchangeHttpClient2 } from "@gnu-taler/taler-util"; +import { TalerExchangeHttpClient } from "@gnu-taler/taler-util"; /** * Protocol version spoken with the exchange. @@ -22,7 +22,7 @@ import { TalerExchangeHttpClient2 } from "@gnu-taler/taler-util"; * Uses libtool's current:revision:age versioning. */ export const WALLET_EXCHANGE_PROTOCOL_VERSION = - TalerExchangeHttpClient2.SUPPORTED_EXCHANGE_PROTOCOL_VERSION; + TalerExchangeHttpClient.SUPPORTED_EXCHANGE_PROTOCOL_VERSION; /** * Protocol version spoken with the merchant. diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts @@ -113,7 +113,7 @@ import { TalerBankIntegrationHttpClient, TalerError, TalerErrorCode, - TalerExchangeHttpClient2, + TalerExchangeHttpClient, TalerMerchantInstanceHttpClient, TalerProtocolTimestamp, TalerUriAction, @@ -427,8 +427,8 @@ export interface WalletExecutionContext { export function walletExchangeClient( baseUrl: string, wex: WalletExecutionContext, -): TalerExchangeHttpClient2 { - return new TalerExchangeHttpClient2(baseUrl, { +): TalerExchangeHttpClient { + return new TalerExchangeHttpClient(baseUrl, { httpClient: wex.http, cancelationToken: wex.cancellationToken, longPollQueue: wex.ws.longpollQueue, diff --git a/packages/web-util/src/context/exchange-api.ts b/packages/web-util/src/context/exchange-api.ts @@ -246,7 +246,7 @@ function buildExchangeApiClient( return { getRemoteConfig, - VERSION: TalerExchangeHttpClient.PROTOCOL_VERSION, + VERSION: TalerExchangeHttpClient.SUPPORTED_EXCHANGE_PROTOCOL_VERSION, lib: { exchange: ex, }, diff --git a/packages/web-util/src/hooks/useAsync.ts b/packages/web-util/src/hooks/useAsync.ts @@ -13,8 +13,8 @@ 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 { opUnknownFailure } from "@gnu-taler/taler-util"; import { useEffect, useState } from "preact/hooks"; -import { opFixedSuccess, opUnknownFailure } from "@gnu-taler/taler-util"; /** * convert the async function into preact hook @@ -57,10 +57,10 @@ export function useAsync<Res>( * First start with `first` value * Check if the it should do long-polling with `shouldRetryFn` * Call `retryFn` if is required, long poll is expected for this function - * - * If `retryFn` returns faster than `minTime` (by error or because server + * + * If `retryFn` returns faster than `minTime` (by error or because server * returned a response faster) then wait until `minTime` - * + * * @param fetcher fetcher should be a memoized function * @param retry * @param deps