/*
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
*/
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 { Logger } from "../logging.js";
import {
FailCasesByMethod,
ResultByMethod,
opEmptySuccess,
opKnownHttpFailure,
opKnownTalerFailure,
opSuccessFromHttp,
opUnknownFailure,
} from "../operation.js";
import { TalerErrorCode } from "../taler-error-codes.js";
import {
BankWithdrawalOperationPostRequest,
WithdrawalOperationStatus,
codecForBankWithdrawalOperationPostResponse,
codecForBankWithdrawalOperationStatus,
} from "../types-taler-bank-integration.js";
import { LongPollParams } from "../types-taler-common.js";
import { codecForIntegrationBankConfig } from "../types-taler-corebank.js";
import { codecForTalerErrorDetail } from "../types-taler-wallet.js";
import { addLongPollingParam } from "./utils.js";
export type TalerBankIntegrationResultByMethod<
prop extends keyof TalerBankIntegrationHttpClient,
> = ResultByMethod;
export type TalerBankIntegrationErrorsByMethod<
prop extends keyof TalerBankIntegrationHttpClient,
> = FailCasesByMethod;
const logger = new Logger("bank-integration.ts");
/**
* The API is used by the wallets.
*/
export class TalerBankIntegrationHttpClient {
public static readonly PROTOCOL_VERSION = "2:0:1";
public readonly PROTOCOL_VERSION =
TalerBankIntegrationHttpClient.PROTOCOL_VERSION;
httpLib: HttpRequestLibrary;
constructor(
readonly baseUrl: string,
httpClient?: HttpRequestLibrary,
) {
this.httpLib = httpClient ?? createPlatformHttpLib();
}
isCompatible(version: string): boolean {
const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version);
return compare?.compatible ?? false;
}
/**
* https://docs.taler.net/core/api-bank-integration.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, codecForIntegrationBankConfig());
default:
logger.warn(`config request failed, status ${resp.status}`);
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-bank-integration.html#get--withdrawal-operation-$WITHDRAWAL_ID
*
*/
async getWithdrawalOperationById(
woid: string,
params?: {
old_state?: WithdrawalOperationStatus;
} & LongPollParams,
) {
const url = new URL(`withdrawal-operation/${woid}`, this.baseUrl);
addLongPollingParam(url, params);
if (params) {
url.searchParams.set(
"old_state",
!params.old_state ? "pending" : params.old_state,
);
}
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(resp, codecForBankWithdrawalOperationStatus());
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-bank-integration.html#post-$BANK_API_BASE_URL-withdrawal-operation-$wopid
*
*/
async completeWithdrawalOperationById(
woid: string,
body: BankWithdrawalOperationPostRequest,
) {
const url = new URL(`withdrawal-operation/${woid}`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body,
});
switch (resp.status) {
case HttpStatusCode.Ok:
return opSuccessFromHttp(
resp,
codecForBankWithdrawalOperationPostResponse(),
);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict: {
const body = await readTalerErrorResponse(resp);
const details = codecForTalerErrorDetail().decode(body);
switch (details.code) {
case TalerErrorCode.BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT:
return opKnownTalerFailure(details.code, details);
case TalerErrorCode.BANK_DUPLICATE_RESERVE_PUB_SUBJECT:
return opKnownTalerFailure(details.code, details);
case TalerErrorCode.BANK_UNKNOWN_ACCOUNT:
return opKnownTalerFailure(details.code, details);
case TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE:
return opKnownTalerFailure(details.code, details);
case TalerErrorCode.BANK_AMOUNT_DIFFERS:
return opKnownTalerFailure(details.code, details);
case TalerErrorCode.BANK_AMOUNT_REQUIRED:
return opKnownTalerFailure(details.code, details);
default:
return opUnknownFailure(resp, details);
}
}
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
/**
* https://docs.taler.net/core/api-bank-integration.html#post-$BANK_API_BASE_URL-withdrawal-operation-$wopid
*
*/
async abortWithdrawalOperationById(woid: string) {
const url = new URL(`withdrawal-operation/${woid}/abort`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
});
switch (resp.status) {
case HttpStatusCode.NoContent:
return opEmptySuccess(resp);
case HttpStatusCode.NotFound:
return opKnownHttpFailure(resp.status, resp);
case HttpStatusCode.Conflict:
return opKnownHttpFailure(resp.status, resp);
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
}
}