summaryrefslogtreecommitdiff
path: root/packages/taler-util/src/http-client/bank-conversion.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-util/src/http-client/bank-conversion.ts')
-rw-r--r--packages/taler-util/src/http-client/bank-conversion.ts223
1 files changed, 223 insertions, 0 deletions
diff --git a/packages/taler-util/src/http-client/bank-conversion.ts b/packages/taler-util/src/http-client/bank-conversion.ts
new file mode 100644
index 000000000..cb14d8b34
--- /dev/null
+++ b/packages/taler-util/src/http-client/bank-conversion.ts
@@ -0,0 +1,223 @@
+/*
+ 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/>
+ */
+
+/**
+ * Imports.
+ */
+import { AmountJson, Amounts } from "../amounts.js";
+import { HttpRequestLibrary, readTalerErrorResponse } from "../http-common.js";
+import { HttpStatusCode } from "../http-status-codes.js";
+import { createPlatformHttpLib } from "../http.js";
+import { LibtoolVersion } from "../libtool-version.js";
+import {
+ FailCasesByMethod,
+ ResultByMethod,
+ opEmptySuccess,
+ opKnownHttpFailure,
+ opSuccessFromHttp,
+ opUnknownFailure,
+} from "../operation.js";
+import { TalerErrorCode } from "../taler-error-codes.js";
+import { codecForTalerErrorDetail } from "../wallet-types.js";
+import {
+ AccessToken,
+ TalerBankConversionApi,
+ codecForCashinConversionResponse,
+ codecForCashoutConversionResponse,
+ codecForConversionBankConfig,
+} from "./types.js";
+import {
+ CacheEvictor,
+ makeBearerTokenAuthHeader,
+ nullEvictor,
+} from "./utils.js";
+
+export type TalerBankConversionResultByMethod<
+ prop extends keyof TalerBankConversionHttpClient,
+> = ResultByMethod<TalerBankConversionHttpClient, prop>;
+export type TalerBankConversionErrorsByMethod<
+ prop extends keyof TalerBankConversionHttpClient,
+> = FailCasesByMethod<TalerBankConversionHttpClient, prop>;
+
+export enum TalerBankConversionCacheEviction {
+ UPDATE_RATE,
+}
+
+/**
+ * The API is used by the wallets.
+ */
+export class TalerBankConversionHttpClient {
+ public readonly PROTOCOL_VERSION = "0:0:0";
+
+ httpLib: HttpRequestLibrary;
+ cacheEvictor: CacheEvictor<TalerBankConversionCacheEviction>;
+
+ constructor(
+ readonly baseUrl: string,
+ httpClient?: HttpRequestLibrary,
+ cacheEvictor?: CacheEvictor<TalerBankConversionCacheEviction>,
+ ) {
+ this.httpLib = httpClient ?? createPlatformHttpLib();
+ this.cacheEvictor = cacheEvictor ?? nullEvictor;
+ }
+
+ isCompatible(version: string): boolean {
+ const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version);
+ return compare?.compatible ?? false;
+ }
+
+ /**
+ * https://docs.taler.net/core/api-bank-conversion-info.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, codecForConversionBankConfig());
+ case HttpStatusCode.NotImplemented:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+
+ /**
+ * https://docs.taler.net/core/api-bank-conversion-info.html#get--cashin-rate
+ *
+ */
+ async getCashinRate(conversion: { debit?: AmountJson; credit?: AmountJson }) {
+ const url = new URL(`cashin-rate`, this.baseUrl);
+ if (conversion.debit) {
+ url.searchParams.set("amount_debit", Amounts.stringify(conversion.debit));
+ }
+ if (conversion.credit) {
+ url.searchParams.set(
+ "amount_credit",
+ Amounts.stringify(conversion.credit),
+ );
+ }
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "GET",
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(resp, codecForCashinConversionResponse());
+ case HttpStatusCode.BadRequest: {
+ const body = await resp.json();
+ const details = codecForTalerErrorDetail().decode(body);
+ switch (details.code) {
+ case TalerErrorCode.GENERIC_PARAMETER_MISSING:
+ return opKnownHttpFailure(resp.status, resp);
+ case TalerErrorCode.GENERIC_PARAMETER_MALFORMED:
+ return opKnownHttpFailure(resp.status, resp);
+ case TalerErrorCode.GENERIC_CURRENCY_MISMATCH:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, body);
+ }
+ }
+ case HttpStatusCode.Conflict:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotImplemented:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+
+ /**
+ * https://docs.taler.net/core/api-bank-conversion-info.html#get--cashout-rate
+ *
+ */
+ async getCashoutRate(conversion: {
+ debit?: AmountJson;
+ credit?: AmountJson;
+ }) {
+ const url = new URL(`cashout-rate`, this.baseUrl);
+ if (conversion.debit) {
+ url.searchParams.set("amount_debit", Amounts.stringify(conversion.debit));
+ }
+ if (conversion.credit) {
+ url.searchParams.set(
+ "amount_credit",
+ Amounts.stringify(conversion.credit),
+ );
+ }
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "GET",
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok:
+ return opSuccessFromHttp(resp, codecForCashoutConversionResponse());
+ case HttpStatusCode.BadRequest: {
+ const body = await resp.json();
+ const details = codecForTalerErrorDetail().decode(body);
+ switch (details.code) {
+ case TalerErrorCode.GENERIC_PARAMETER_MISSING:
+ return opKnownHttpFailure(resp.status, resp);
+ case TalerErrorCode.GENERIC_PARAMETER_MALFORMED:
+ return opKnownHttpFailure(resp.status, resp);
+ case TalerErrorCode.GENERIC_CURRENCY_MISMATCH:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, body);
+ }
+ }
+ case HttpStatusCode.Conflict:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotImplemented:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+
+ /**
+ * https://docs.taler.net/core/api-bank-conversion-info.html#post--conversion-rate
+ *
+ */
+ async updateConversionRate(
+ auth: AccessToken,
+ body: TalerBankConversionApi.ConversionRate,
+ ) {
+ const url = new URL(`conversion-rate`, this.baseUrl);
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "POST",
+ headers: {
+ Authorization: makeBearerTokenAuthHeader(auth),
+ },
+ body,
+ });
+ switch (resp.status) {
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(
+ TalerBankConversionCacheEviction.UPDATE_RATE,
+ );
+ return opEmptySuccess(resp);
+ }
+ case HttpStatusCode.Unauthorized:
+ return opKnownHttpFailure(resp.status, resp);
+ case HttpStatusCode.NotImplemented:
+ return opKnownHttpFailure(resp.status, resp);
+ default:
+ return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ }
+ }
+}