/* 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 { AbsoluteTime, HttpStatusCode, LibtoolVersion, LongPollParams, OperationAlternative, OperationFail, OperationOk, TalerErrorCode, codecForChallenge, codecForTanTransmission, opKnownAlternativeFailure, opKnownHttpFailure, opKnownTalerFailure, } from "@gnu-taler/taler-util"; import { HttpRequestLibrary, createPlatformHttpLib, readTalerErrorResponse, } from "@gnu-taler/taler-util/http"; import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opFixedSuccess, opSuccessFromHttp, opUnknownFailure, } from "../operation.js"; import { AccessToken, PaginationParams, TalerCorebankApi, UserAndToken, WithdrawalOperationStatus, codecForAccountData, codecForBankAccountCreateWithdrawalResponse, codecForBankAccountTransactionInfo, codecForBankAccountTransactionsResponse, codecForCashoutPending, codecForCashoutStatusResponse, codecForCashouts, codecForCoreBankConfig, codecForCreateTransactionResponse, codecForGlobalCashouts, codecForListBankAccountsResponse, codecForMonitorResponse, codecForPublicAccountsResponse, codecForRegisterAccountResponse, codecForWithdrawalPublicInfo, } from "./types.js"; import { CacheEvictor, IdempotencyRetry, addLongPollingParam, addPaginationParams, makeBearerTokenAuthHeader, nullEvictor, } from "./utils.js"; export type TalerCoreBankResultByMethod< prop extends keyof TalerCoreBankHttpClient, > = ResultByMethod; export type TalerCoreBankErrorsByMethod< prop extends keyof TalerCoreBankHttpClient, > = FailCasesByMethod; export enum TalerCoreBankCacheEviction { DELETE_ACCOUNT, CREATE_ACCOUNT, UPDATE_ACCOUNT, UPDATE_PASSWORD, CREATE_TRANSACTION, CONFIRM_WITHDRAWAL, ABORT_WITHDRAWAL, CREATE_WITHDRAWAL, CREATE_CASHOUT, } /** * 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. */ export class TalerCoreBankHttpClient { public readonly PROTOCOL_VERSION = "4:0:0"; httpLib: HttpRequestLibrary; cacheEvictor: CacheEvictor; constructor( readonly baseUrl: string, httpClient?: HttpRequestLibrary, cacheEvictor?: CacheEvictor, ) { 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-corebank.html#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, codecForCoreBankConfig()); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } // // ACCOUNTS // /** * https://docs.taler.net/core/api-corebank.html#post--accounts * */ async createAccount( auth: AccessToken | undefined, body: TalerCorebankApi.RegisterAccountRequest, ) { const url = new URL(`accounts`, this.baseUrl); const headers: Record = {}; if (auth) { headers.Authorization = makeBearerTokenAuthHeader(auth); } const resp = await this.httpLib.fetch(url.href, { method: "POST", body, headers: headers, }); switch (resp.status) { case HttpStatusCode.Ok: { await this.cacheEvictor.notifySuccess( TalerCoreBankCacheEviction.CREATE_ACCOUNT, ); return opSuccessFromHttp(resp, codecForRegisterAccountResponse()); } case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { const details = await readTalerErrorResponse(resp); switch (details.code) { case TalerErrorCode.BANK_REGISTER_USERNAME_REUSE: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_REGISTER_PAYTO_URI_REUSE: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_NON_ADMIN_SET_MIN_CASHOUT: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_NON_ADMIN_SET_TAN_CHANNEL: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_MISSING_TAN_INFO: return opKnownTalerFailure(details.code, details); default: return opUnknownFailure(resp, details); } } default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#delete--accounts-$USERNAME * */ async deleteAccount(auth: UserAndToken, cid?: string) { const url = new URL(`accounts/${auth.username}`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "DELETE", headers: { Authorization: makeBearerTokenAuthHeader(auth.token), "X-Challenge-Id": cid, }, }); switch (resp.status) { case HttpStatusCode.Accepted: return opKnownAlternativeFailure( resp, resp.status, codecForChallenge(), ); case HttpStatusCode.NoContent: return opEmptySuccess(resp); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { const details = await readTalerErrorResponse(resp); switch (details.code) { case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_ACCOUNT_BALANCE_NOT_ZERO: return opKnownTalerFailure(details.code, details); default: return opUnknownFailure(resp, details); } } default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#patch--accounts-$USERNAME * */ async updateAccount( auth: UserAndToken, body: TalerCorebankApi.AccountReconfiguration, cid?: string, ) { const url = new URL(`accounts/${auth.username}`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "PATCH", body, headers: { Authorization: makeBearerTokenAuthHeader(auth.token), "X-Challenge-Id": cid, }, }); switch (resp.status) { case HttpStatusCode.Accepted: return opKnownAlternativeFailure( resp, resp.status, codecForChallenge(), ); case HttpStatusCode.NoContent: return opEmptySuccess(resp); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { const details = await readTalerErrorResponse(resp); switch (details.code) { case TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_NON_ADMIN_PATCH_CASHOUT: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_NON_ADMIN_SET_MIN_CASHOUT: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_MISSING_TAN_INFO: return opKnownTalerFailure(details.code, details); default: return opUnknownFailure(resp, details); } } default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#patch--accounts-$USERNAME-auth * */ async updatePassword( auth: UserAndToken, body: TalerCorebankApi.AccountPasswordChange, cid?: string, ) { const url = new URL(`accounts/${auth.username}/auth`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "PATCH", body, headers: { Authorization: makeBearerTokenAuthHeader(auth.token), "X-Challenge-Id": cid, }, }); switch (resp.status) { case HttpStatusCode.Accepted: return opKnownAlternativeFailure( resp, resp.status, codecForChallenge(), ); case HttpStatusCode.NoContent: return opEmptySuccess(resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { const details = await readTalerErrorResponse(resp); switch (details.code) { case TalerErrorCode.BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_PATCH_BAD_OLD_PASSWORD: return opKnownTalerFailure(details.code, details); default: return opUnknownFailure(resp, details); } } default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#get--public-accounts * */ async getPublicAccounts( filter: { account?: string } = {}, pagination?: PaginationParams, ) { const url = new URL(`public-accounts`, this.baseUrl); addPaginationParams(url, pagination); if (filter.account !== undefined) { url.searchParams.set("filter_name", filter.account); } const resp = await this.httpLib.fetch(url.href, { method: "GET", }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForPublicAccountsResponse()); case HttpStatusCode.NoContent: return opFixedSuccess({ public_accounts: [] }); case HttpStatusCode.NotFound: return opFixedSuccess({ public_accounts: [] }); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#get--accounts * */ async getAccounts( auth: AccessToken, filter: { account?: string } = {}, pagination?: PaginationParams, ) { const url = new URL(`accounts`, this.baseUrl); addPaginationParams(url, pagination); if (filter.account !== undefined) { url.searchParams.set("filter_name", filter.account); } const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { Authorization: makeBearerTokenAuthHeader(auth), }, }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForListBankAccountsResponse()); case HttpStatusCode.NoContent: return opFixedSuccess({ accounts: [] }); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME * */ async getAccount(auth: UserAndToken) { const url = new URL(`accounts/${auth.username}`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForAccountData()); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } // // TRANSACTIONS // /** * https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME-transactions * */ async getTransactions( auth: UserAndToken, params?: PaginationParams & LongPollParams, ) { const url = new URL(`accounts/${auth.username}/transactions`, this.baseUrl); addPaginationParams(url, params); addLongPollingParam(url, params); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp( resp, codecForBankAccountTransactionsResponse(), ); case HttpStatusCode.NoContent: return opFixedSuccess({ transactions: [] }); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME-transactions-$TRANSACTION_ID * */ async getTransactionById(auth: UserAndToken, txid: number) { const url = new URL( `accounts/${auth.username}/transactions/${String(txid)}`, this.baseUrl, ); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForBankAccountTransactionInfo()); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-transactions * */ async createTransaction( auth: UserAndToken, body: TalerCorebankApi.CreateTransactionRequest, idempotencyCheck: IdempotencyRetry | undefined, cid?: string, ): Promise< //manually definition all return types because of recursion | OperationOk | OperationAlternative | OperationFail | OperationFail | OperationFail | OperationFail | OperationFail | OperationFail | OperationFail | OperationFail > { const url = new URL(`accounts/${auth.username}/transactions`, this.baseUrl); if (idempotencyCheck) { body.request_uid = idempotencyCheck.uid; } const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBearerTokenAuthHeader(auth.token), "X-Challenge-Id": cid, }, body, }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForCreateTransactionResponse()); case HttpStatusCode.Accepted: return opKnownAlternativeFailure( resp, resp.status, codecForChallenge(), ); case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { const details = await readTalerErrorResponse(resp); switch (details.code) { case TalerErrorCode.BANK_ADMIN_CREDITOR: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_SAME_ACCOUNT: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_UNKNOWN_CREDITOR: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_TRANSFER_REQUEST_UID_REUSED: if (!idempotencyCheck) { return opKnownTalerFailure(details.code, details); } const nextRetry = idempotencyCheck.next(); return this.createTransaction(auth, body, nextRetry, cid); default: return opUnknownFailure(resp, details); } } default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } // // WITHDRAWALS // /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-withdrawals * */ async createWithdrawal( auth: UserAndToken, body: TalerCorebankApi.BankAccountCreateWithdrawalRequest, ) { const url = new URL(`accounts/${auth.username}/withdrawals`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBearerTokenAuthHeader(auth.token), }, body, }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp( resp, codecForBankAccountCreateWithdrawalResponse(), ); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); //FIXME: missing in docs case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-withdrawals-$WITHDRAWAL_ID-confirm * */ async confirmWithdrawalById(auth: UserAndToken, wid: string, cid?: string) { const url = new URL( `accounts/${auth.username}/withdrawals/${wid}/confirm`, this.baseUrl, ); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBearerTokenAuthHeader(auth.token), "X-Challenge-Id": cid, }, }); switch (resp.status) { case HttpStatusCode.Accepted: return opKnownAlternativeFailure( resp, resp.status, codecForChallenge(), ); case HttpStatusCode.NoContent: return opEmptySuccess(resp); //FIXME: missing in docs case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { const details = await readTalerErrorResponse(resp); switch (details.code) { case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownTalerFailure(details.code, details); default: return opUnknownFailure(resp, details); } } default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-withdrawals-$WITHDRAWAL_ID-abort * */ async abortWithdrawalById(auth: UserAndToken, wid: string) { const url = new URL( `accounts/${auth.username}/withdrawals/${wid}/abort`, this.baseUrl, ); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { case HttpStatusCode.NoContent: return opEmptySuccess(resp); //FIXME: missing in docs case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#get--withdrawals-$WITHDRAWAL_ID * */ async getWithdrawalById( wid: string, params?: { old_state?: WithdrawalOperationStatus; } & LongPollParams, ) { const url = new URL(`withdrawals/${wid}`, 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, codecForWithdrawalPublicInfo()); //FIXME: missing in docs case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } // // CASHOUTS // /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-cashouts * */ async createCashout( auth: UserAndToken, body: TalerCorebankApi.CashoutRequest, cid?: string, ) { const url = new URL(`accounts/${auth.username}/cashouts`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBearerTokenAuthHeader(auth.token), "X-Challenge-Id": cid, }, body, }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForCashoutPending()); case HttpStatusCode.Accepted: return opKnownAlternativeFailure( resp, resp.status, codecForChallenge(), ); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { const details = await readTalerErrorResponse(resp); switch (details.code) { case TalerErrorCode.BANK_TRANSFER_REQUEST_UID_REUSED: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_CONVERSION_AMOUNT_TO_SMALL: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_BAD_CONVERSION: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return opKnownTalerFailure(details.code, details); default: return opUnknownFailure(resp, details); } } case HttpStatusCode.BadGateway: { const details = await readTalerErrorResponse(resp); switch (details.code) { case TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED: return opKnownTalerFailure(details.code, details); default: return opUnknownFailure(resp, details); } } case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME-cashouts-$CASHOUT_ID * */ async getCashoutById(auth: UserAndToken, cid: number) { const url = new URL( `accounts/${auth.username}/cashouts/${cid}`, this.baseUrl, ); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForCashoutStatusResponse()); case HttpStatusCode.NotFound: 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-corebank.html#get--accounts-$USERNAME-cashouts * */ async getAccountCashouts(auth: UserAndToken, pagination?: PaginationParams) { const url = new URL(`accounts/${auth.username}/cashouts`, this.baseUrl); addPaginationParams(url, pagination); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForCashouts()); case HttpStatusCode.NoContent: return opFixedSuccess({ cashouts: [] }); case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#get--cashouts * */ async getGlobalCashouts(auth: AccessToken, pagination?: PaginationParams) { const url = new URL(`cashouts`, this.baseUrl); addPaginationParams(url, pagination); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { Authorization: makeBearerTokenAuthHeader(auth), }, }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForGlobalCashouts()); case HttpStatusCode.NoContent: return opFixedSuccess({ cashouts: [] }); case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } // // 2FA // /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-challenge-$CHALLENGE_ID * */ async sendChallenge(auth: UserAndToken, cid: string) { const url = new URL( `accounts/${auth.username}/challenge/${cid}`, this.baseUrl, ); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForTanTransmission()); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.BadGateway: { const details = await readTalerErrorResponse(resp); switch (details.code) { case TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED: return opKnownTalerFailure(details.code, details); default: return opUnknownFailure(resp, details); } } default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-challenge-$CHALLENGE_ID-confirm * */ async confirmChallenge( auth: UserAndToken, cid: string, body: TalerCorebankApi.ChallengeSolve, ) { const url = new URL( `accounts/${auth.username}/challenge/${cid}/confirm`, this.baseUrl, ); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBearerTokenAuthHeader(auth.token), }, body, }); switch (resp.status) { case HttpStatusCode.NoContent: return opEmptySuccess(resp); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { const details = await readTalerErrorResponse(resp); switch (details.code) { case TalerErrorCode.BANK_TAN_CHALLENGE_EXPIRED: return opKnownTalerFailure(details.code, details); case TalerErrorCode.BANK_TAN_CHALLENGE_FAILED: return opKnownTalerFailure(details.code, details); default: return opUnknownFailure(resp, details); } } case HttpStatusCode.TooManyRequests: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } // // MONITOR // /** * https://docs.taler.net/core/api-corebank.html#get--monitor * */ async getMonitor( auth: AccessToken, params: { timeframe?: TalerCorebankApi.MonitorTimeframeParam; date?: AbsoluteTime; } = {}, ) { const url = new URL(`monitor`, this.baseUrl); if (params.timeframe) { url.searchParams.set( "timeframe", TalerCorebankApi.MonitorTimeframeParam[params.timeframe], ); } if (params.date) { const { t_s: seconds } = AbsoluteTime.toProtocolTimestamp(params.date); if (seconds !== "never") { url.searchParams.set("date_s", String(seconds)); } } const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { Authorization: makeBearerTokenAuthHeader(auth), }, }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForMonitorResponse()); case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } // // Others API // /** * https://docs.taler.net/core/api-corebank.html#taler-bank-integration-api * */ getIntegrationAPI(): URL { return new URL(`taler-integration/`, this.baseUrl); } /** * https://docs.taler.net/core/api-corebank.html#taler-bank-integration-api * */ getWireGatewayAPI(username: string): URL { return new URL(`accounts/${username}/taler-wire-gateway/`, this.baseUrl); } /** * https://docs.taler.net/core/api-corebank.html#taler-bank-integration-api * */ getRevenueAPI(username: string): URL { return new URL(`accounts/${username}/taler-revenue/`, this.baseUrl); } /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token * */ getAuthenticationAPI(username: string): URL { return new URL(`accounts/${username}/`, this.baseUrl); } /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token * */ getConversionInfoAPI(): URL { return new URL(`conversion-info/`, this.baseUrl); } }