From a04e822af063951d39f4ddc597cd163037cb5010 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 19 Jan 2024 17:09:09 -0300 Subject: fixes #8146 --- .../taler-util/src/http-client/authentication.ts | 8 +- .../taler-util/src/http-client/bank-conversion.ts | 36 ++-- packages/taler-util/src/http-client/bank-core.ts | 204 ++++++++------------- .../taler-util/src/http-client/bank-integration.ts | 47 +++-- .../taler-util/src/http-client/bank-revenue.ts | 38 +++- packages/taler-util/src/http-client/bank-wire.ts | 80 +++++--- packages/taler-util/src/http-client/exchange.ts | 31 ++-- packages/taler-util/src/http-client/merchant.ts | 2 +- packages/taler-util/src/http-client/types.ts | 124 +++---------- packages/taler-util/src/http-client/utils.ts | 12 +- 10 files changed, 276 insertions(+), 306 deletions(-) (limited to 'packages/taler-util/src/http-client') diff --git a/packages/taler-util/src/http-client/authentication.ts b/packages/taler-util/src/http-client/authentication.ts index b27a266e9..66e00ded5 100644 --- a/packages/taler-util/src/http-client/authentication.ts +++ b/packages/taler-util/src/http-client/authentication.ts @@ -17,7 +17,7 @@ import { HttpStatusCode } from "../http-status-codes.js"; import { HttpRequestLibrary, createPlatformHttpLib, makeBasicAuthHeader } from "../http.js"; import { LibtoolVersion } from "../libtool-version.js"; -import { opEmptySuccess, opKnownFailure, opSuccess, opUnknownFailure } from "../operation.js"; +import { opEmptySuccess, opKnownHttpFailure, opSuccess, opUnknownFailure } from "../operation.js"; import { AccessToken, TalerAuthentication, codecForTokenSuccessResponse } from "./types.js"; import { makeBearerTokenAuthHeader } from "./utils.js"; @@ -59,8 +59,8 @@ export class TalerAuthenticationHttpClient { switch (resp.status) { case HttpStatusCode.Ok: return opSuccess(resp, codecForTokenSuccessResponse()) //FIXME: missing in docs - case HttpStatusCode.Unauthorized: return opKnownFailure("wrong-credentials", resp) - case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp) + case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp) + case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp) default: return opUnknownFailure(resp, await resp.text()) } } @@ -76,7 +76,7 @@ export class TalerAuthenticationHttpClient { switch (resp.status) { case HttpStatusCode.Ok: return opEmptySuccess() //FIXME: missing in docs - case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp) + case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp) default: return opUnknownFailure(resp, await resp.text()) } } diff --git a/packages/taler-util/src/http-client/bank-conversion.ts b/packages/taler-util/src/http-client/bank-conversion.ts index 2bc9fdb79..3ecb036ee 100644 --- a/packages/taler-util/src/http-client/bank-conversion.ts +++ b/packages/taler-util/src/http-client/bank-conversion.ts @@ -2,13 +2,13 @@ import { AmountJson, Amounts } from "../amounts.js"; import { HttpRequestLibrary } from "../http-common.js"; import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; -import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opKnownFailure, opSuccess, opUnknownFailure } from "../operation.js"; +import { LibtoolVersion } from "../libtool-version.js"; +import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opKnownHttpFailure, opSuccess, opUnknownFailure } from "../operation.js"; import { TalerErrorCode } from "../taler-error-codes.js"; import { codecForTalerErrorDetail } from "../wallet-types.js"; import { AccessToken, TalerBankConversionApi, - UserAndToken, codecForCashinConversionResponse, codecForCashoutConversionResponse, codecForConversionBankConfig @@ -22,6 +22,8 @@ export type TalerBankConversionErrorsByMethod = ResultByMethod export type TalerCoreBankErrorsByMethod = FailCasesByMethod /** - * Protocol version spoken with the bank. + * 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 { @@ -134,8 +139,8 @@ export class TalerCoreBankHttpClient { switch (resp.status) { case HttpStatusCode.Accepted: return opKnownAlternativeFailure(resp, resp.status, codecForChallenge()) case HttpStatusCode.NoContent: return opEmptySuccess() - case HttpStatusCode.NotFound: 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 body = await resp.json() const details = codecForTalerErrorDetail().decode(body) @@ -166,17 +171,17 @@ export class TalerCoreBankHttpClient { switch (resp.status) { case HttpStatusCode.Accepted: return opKnownAlternativeFailure(resp, resp.status, codecForChallenge()) case HttpStatusCode.NoContent: return opEmptySuccess() - case HttpStatusCode.NotFound: 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 body = await resp.json() const details = codecForTalerErrorDetail().decode(body) switch (details.code) { case TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: return opKnownTalerFailure(details.code, resp); case TalerErrorCode.BANK_NON_ADMIN_PATCH_CASHOUT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_NON_ADMIN_PATCH_CONTACT: return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: return opKnownTalerFailure(details.code, resp); case TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED: return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_MISSING_TAN_INFO: return opKnownTalerFailure(details.code, resp); default: return opUnknownFailure(resp, body) } } @@ -289,9 +294,10 @@ export class TalerCoreBankHttpClient { * https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME-transactions * */ - async getTransactions(auth: UserAndToken, pagination?: PaginationParams) { + async getTransactions(auth: UserAndToken, params?: PaginationParams & LongPollParams) { const url = new URL(`accounts/${auth.username}/transactions`, this.baseUrl); - addPaginationParams(url, pagination) + addPaginationParams(url, params) + addLongPollingParam(url, params) const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { @@ -342,8 +348,8 @@ export class TalerCoreBankHttpClient { body, }); switch (resp.status) { - case HttpStatusCode.Accepted: return opKnownAlternativeFailure(resp, resp.status, codecForChallenge()) case HttpStatusCode.Ok: return opSuccess(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); @@ -382,62 +388,63 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.Ok: return opSuccess(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 resp.text()) } } /** - * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-withdrawals-$WITHDRAWAL_ID-abort + * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-withdrawals-$WITHDRAWAL_ID-confirm * */ - async abortWithdrawalById(auth: UserAndToken, wid: string) { - const url = new URL(`accounts/${auth.username}/withdrawals/${wid}/abort`, this.baseUrl); + 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) + 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() //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); + case HttpStatusCode.Conflict: { + const body = await resp.json() + const details = codecForTalerErrorDetail().decode(body) + switch (details.code) { + case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownTalerFailure(details.code, resp); + default: return opUnknownFailure(resp, body) + } + } default: return opUnknownFailure(resp, await resp.text()) } } /** - * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-withdrawals-$WITHDRAWAL_ID-confirm + * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-withdrawals-$WITHDRAWAL_ID-abort * */ - async confirmWithdrawalById(auth: UserAndToken, wid: string, cid?: string) { - const url = new URL(`accounts/${auth.username}/withdrawals/${wid}/confirm`, this.baseUrl); + 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), - "X-Challenge-Id": cid, + Authorization: makeBearerTokenAuthHeader(auth.token) }, }); switch (resp.status) { - case HttpStatusCode.Accepted: return opKnownAlternativeFailure(resp, resp.status, codecForChallenge()) case HttpStatusCode.NoContent: return opEmptySuccess() //FIXME: missing in docs case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp) case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp) - case HttpStatusCode.Conflict: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) - switch (details.code) { - case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownTalerFailure(details.code, resp); - default: return opUnknownFailure(resp, body) - } - } + case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await resp.text()) } } @@ -446,14 +453,13 @@ export class TalerCoreBankHttpClient { * https://docs.taler.net/core/api-corebank.html#get--withdrawals-$WITHDRAWAL_ID * */ - async getWithdrawalById(wid: string, wait?: { + async getWithdrawalById(wid: string, params?: { old_state?: WithdrawalOperationStatus, - timeoutMs: number - }) { + } & LongPollParams) { const url = new URL(`withdrawals/${wid}`, this.baseUrl); - if (wait) { - url.searchParams.set("long_poll_ms", String(wait.timeoutMs)) - url.searchParams.set("old_state", !wait.old_state ? "pending" : wait.old_state) + 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", @@ -486,8 +492,8 @@ export class TalerCoreBankHttpClient { body, }); switch (resp.status) { - case HttpStatusCode.Accepted: return opKnownAlternativeFailure(resp, resp.status, codecForChallenge()) case HttpStatusCode.Ok: return opSuccess(resp, codecForCashoutPending()) + case HttpStatusCode.Accepted: return opKnownAlternativeFailure(resp, resp.status, codecForChallenge()) case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp) case HttpStatusCode.Conflict: { const body = await resp.json() @@ -513,61 +519,6 @@ export class TalerCoreBankHttpClient { } } - /** - * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-cashouts-$CASHOUT_ID-abort - * @deprecated since 4 - */ - async abortCashoutById(auth: UserAndToken, cid: number) { - const url = new URL(`accounts/${auth.username}/cashouts/${cid}/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() - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) - } - } - - /** - * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-cashouts-$CASHOUT_ID-confirm - * @deprecated since 4 - */ - async confirmCashoutById(auth: UserAndToken, cid: number, body: TalerCorebankApi.CashoutConfirmRequest) { - const url = new URL(`accounts/${auth.username}/cashouts/${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() - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Conflict: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) - switch (details.code) { - case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_BAD_CONVERSION: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_TAN_CHALLENGE_FAILED: return opKnownTalerFailure(details.code, resp); - default: return opUnknownFailure(resp, body) - } - } - case HttpStatusCode.TooManyRequests: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) - } - } - /** * https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME-cashouts-$CASHOUT_ID * @@ -604,7 +555,6 @@ export class TalerCoreBankHttpClient { switch (resp.status) { case HttpStatusCode.Ok: return opSuccess(resp, codecForCashouts()) case HttpStatusCode.NoContent: return opFixedSuccess({ cashouts: [] }); - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp);; case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await resp.text()) } @@ -632,41 +582,13 @@ export class TalerCoreBankHttpClient { } // - // MONITOR + // 2FA // /** - * https://docs.taler.net/core/api-corebank.html#get--monitor + * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-challenge-$CHALLENGE_ID * */ - async getMonitor(auth: AccessToken, params: { timeframe?: TalerCorebankApi.MonitorTimeframeParam, which?: number } = {}) { - const url = new URL(`monitor`, this.baseUrl); - if (params.timeframe) { - url.searchParams.set("timeframe", TalerCorebankApi.MonitorTimeframeParam[params.timeframe]) - } - if (params.which) { - url.searchParams.set("which", String(params.which)) - } - const resp = await this.httpLib.fetch(url.href, { - method: "GET", - headers: { - Authorization: makeBearerTokenAuthHeader(auth) - }, - }); - switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForMonitorResponse()) - case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - //FIXME remove when server is updated - //FIXME: should be 404 ? - case HttpStatusCode.ServiceUnavailable: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) - } - } - - // - // 2FA - // 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, { @@ -691,6 +613,10 @@ export class TalerCoreBankHttpClient { } } + /** + * 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, { @@ -718,6 +644,36 @@ export class TalerCoreBankHttpClient { } } + // + // MONITOR + // + + /** + * https://docs.taler.net/core/api-corebank.html#get--monitor + * + */ + async getMonitor(auth: AccessToken, params: { timeframe?: TalerCorebankApi.MonitorTimeframeParam, which?: number } = {}) { + const url = new URL(`monitor`, this.baseUrl); + if (params.timeframe) { + url.searchParams.set("timeframe", TalerCorebankApi.MonitorTimeframeParam[params.timeframe]) + } + if (params.which) { + url.searchParams.set("which", String(params.which)) + } + const resp = await this.httpLib.fetch(url.href, { + method: "GET", + headers: { + Authorization: makeBearerTokenAuthHeader(auth) + }, + }); + switch (resp.status) { + case HttpStatusCode.Ok: return opSuccess(resp, codecForMonitorResponse()) + case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + default: return opUnknownFailure(resp, await resp.text()) + } + } + // // Others API diff --git a/packages/taler-util/src/http-client/bank-integration.ts b/packages/taler-util/src/http-client/bank-integration.ts index 757f1f897..2264b65bf 100644 --- a/packages/taler-util/src/http-client/bank-integration.ts +++ b/packages/taler-util/src/http-client/bank-integration.ts @@ -2,16 +2,18 @@ import { HttpRequestLibrary, readSuccessResponseJsonOrThrow } from "../http-comm import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; import { LibtoolVersion } from "../libtool-version.js"; -import { FailCasesByMethod, ResultByMethod, opKnownFailure, opSuccess, opUnknownFailure } from "../operation.js"; +import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opKnownHttpFailure, opKnownTalerFailure, opSuccess, opUnknownFailure } from "../operation.js"; import { TalerErrorCode } from "../taler-error-codes.js"; import { codecForTalerErrorDetail } from "../wallet-types.js"; import { + LongPollParams, TalerBankIntegrationApi, WithdrawalOperationStatus, codecForBankWithdrawalOperationPostResponse, codecForBankWithdrawalOperationStatus, codecForIntegrationBankConfig } from "./types.js"; +import { addLongPollingParam } from "./utils.js"; export type TalerBankIntegrationResultByMethod = ResultByMethod export type TalerBankIntegrationErrorsByMethod = FailCasesByMethod @@ -20,7 +22,7 @@ export type TalerBankIntegrationErrorsByMethod = ResultByMethod export type TalerBankRevenueErrorsByMethod = FailCasesByMethod @@ -22,15 +22,37 @@ export class TalerRevenueHttpClient { ) { this.httpLib = httpClient ?? createPlatformHttpLib(); } + // public readonly PROTOCOL_VERSION = "4:0:0"; + // 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 opSuccess(resp, codecForCoreBankConfig()) + // default: return opUnknownFailure(resp, await resp.text()) + // } + // } + /** * https://docs.taler.net/core/api-bank-revenue.html#get--history * * @returns */ - async getHistory(auth: string, pagination?: PaginationParams) { + async getHistory(auth: string, params?: PaginationParams & LongPollParams) { const url = new URL(`history`, this.baseUrl); - addPaginationParams(url, pagination) + addPaginationParams(url, params) + addLongPollingParam(url, params) const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { @@ -39,9 +61,9 @@ export class TalerRevenueHttpClient { }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccess(resp, codecForMerchantIncomingHistory()) - case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", resp); - case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", resp); - case HttpStatusCode.NotFound: return opKnownFailure("endpoint-wrong-or-username-wrong", resp); + case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await resp.text()) } } diff --git a/packages/taler-util/src/http-client/bank-wire.ts b/packages/taler-util/src/http-client/bank-wire.ts index 7e3c00637..90c3aeae5 100644 --- a/packages/taler-util/src/http-client/bank-wire.ts +++ b/packages/taler-util/src/http-client/bank-wire.ts @@ -1,9 +1,9 @@ import { HttpRequestLibrary, makeBasicAuthHeader } from "../http-common.js"; import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; -import { FailCasesByMethod, ResultByMethod, opFixedSuccess, opKnownFailure, opSuccess, opUnknownFailure } from "../operation.js"; -import { PaginationParams, TalerWireGatewayApi, codecForAddIncomingResponse, codecForIncomingHistory, codecForOutgoingHistory, codecForTransferResponse } from "./types.js"; -import { addPaginationParams } from "./utils.js"; +import { FailCasesByMethod, ResultByMethod, opFixedSuccess, opKnownHttpFailure, opSuccess, opUnknownFailure } from "../operation.js"; +import { LongPollParams, PaginationParams, TalerWireGatewayApi, codecForAddIncomingResponse, codecForIncomingHistory, codecForOutgoingHistory, codecForTransferResponse } from "./types.js"; +import { addLongPollingParam, addPaginationParams } from "./utils.js"; export type TalerWireGatewayResultByMethod = ResultByMethod export type TalerWireGatewayErrorsByMethod = FailCasesByMethod @@ -25,6 +25,27 @@ export class TalerWireGatewayHttpClient { ) { this.httpLib = httpClient ?? createPlatformHttpLib(); } + // public readonly PROTOCOL_VERSION = "4:0:0"; + // 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 opSuccess(resp, codecForCoreBankConfig()) + // default: return opUnknownFailure(resp, await resp.text()) + // } + // } + /** * https://docs.taler.net/core/api-bank-wire.html#post--transfer @@ -42,10 +63,12 @@ export class TalerWireGatewayHttpClient { }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccess(resp, codecForTransferResponse()) - case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", resp); - case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", resp); - case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp); - case HttpStatusCode.Conflict: return opKnownFailure("request-uid-already-used", resp); + //FIXME: show more details in docs + case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + //FIXME: show more details in docs + case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await resp.text()) } } @@ -54,9 +77,10 @@ export class TalerWireGatewayHttpClient { * https://docs.taler.net/core/api-bank-wire.html#get--history-incoming * */ - async getHistoryIncoming(auth: string, pagination?: PaginationParams) { + async getHistoryIncoming(auth: string, params?: PaginationParams & LongPollParams) { const url = new URL(`history/incoming`, this.baseUrl); - addPaginationParams(url, pagination) + addPaginationParams(url, params) + addLongPollingParam(url, params) const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { @@ -65,22 +89,25 @@ export class TalerWireGatewayHttpClient { }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccess(resp, codecForIncomingHistory()) - case HttpStatusCode.NoContent: return opFixedSuccess({ incoming_transactions: [] }) - case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", resp); - case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", resp); - case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp); + //FIXME: account should not be returned or make it optional + case HttpStatusCode.NoContent: return opFixedSuccess({ incoming_transactions: [], credit_account: undefined }) + //FIXME: show more details in docs + case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + //FIXME: show more details in docs + case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await resp.text()) } - // return readSuccessResponseJsonOrThrow(resp, codecForIncomingHistory()); } /** * https://docs.taler.net/core/api-bank-wire.html#get--history-outgoing * */ - async getHistoryOutgoing(auth: string, pagination?: PaginationParams) { + async getHistoryOutgoing(auth: string, params?: PaginationParams & LongPollParams) { const url = new URL(`history/outgoing`, this.baseUrl); - addPaginationParams(url, pagination) + addPaginationParams(url, params) + addLongPollingParam(url, params) const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { @@ -89,10 +116,13 @@ export class TalerWireGatewayHttpClient { }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccess(resp, codecForOutgoingHistory()) - case HttpStatusCode.NoContent: return opFixedSuccess({ outgoing_transactions: [] }) - case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", resp); - case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", resp); - case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp); + //FIXME: account should not be returned or make it optional + case HttpStatusCode.NoContent: return opFixedSuccess({ outgoing_transactions: [], debit_account: undefined }) + //FIXME: show more details in docs + case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + //FIXME: show more details in docs + case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await resp.text()) } } @@ -112,10 +142,12 @@ export class TalerWireGatewayHttpClient { }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccess(resp, codecForAddIncomingResponse()) - case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", resp); - case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", resp); - case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp); - case HttpStatusCode.Conflict: return opKnownFailure("reserve-id-already-used", resp); + //FIXME: show more details in docs + case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + //FIXME: show more details in docs + case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: return opUnknownFailure(resp, await resp.text()) } } diff --git a/packages/taler-util/src/http-client/exchange.ts b/packages/taler-util/src/http-client/exchange.ts index f55be0043..36adb5a1a 100644 --- a/packages/taler-util/src/http-client/exchange.ts +++ b/packages/taler-util/src/http-client/exchange.ts @@ -3,7 +3,7 @@ import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; import { LibtoolVersion } from "../libtool-version.js"; import { hash } from "../nacl-fast.js"; -import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opFixedSuccess, opKnownFailure, opSuccess, opUnknownFailure } from "../operation.js"; +import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opFixedSuccess, opKnownHttpFailure, opSuccess, opUnknownFailure } from "../operation.js"; import { TalerSignaturePurpose, amountToBuffer, bufferForUint32, buildSigPS, decodeCrock, eddsaSign, encodeCrock, stringToBytes, timestampRoundedToBuffer } from "../taler-crypto.js"; import { OfficerAccount, PaginationParams, SigningKey, TalerExchangeApi, codecForAmlDecisionDetails, codecForAmlRecords, codecForExchangeConfig } from "./types.js"; import { addPaginationParams } from "./utils.js"; @@ -15,7 +15,7 @@ export type TalerExchangeErrorsByMethod => .property("name", codecForConstString("libeufin-bank")) .property("version", codecForString()) .property("allow_conversion", codecForBoolean()) - .property("allow_deletions", codecForBoolean()) .property("allow_registrations", codecForBoolean()) - .property("allow_edit_cashout_payto_uri", codecForBoolean()) + .property("allow_deletions", codecForBoolean()) .property("allow_edit_name", codecForBoolean()) + .property("allow_edit_cashout_payto_uri", codecForBoolean()) .property("default_debit_threshold", codecForAmountString()) - .property("currency_specification", codecForCurrencySpecificiation()) .property("currency", codecForString()) + .property("currency_specification", codecForCurrencySpecificiation()) .property("supported_tan_channels", codecForList(codecForEither( codecForConstString(TalerCorebankApi.TanChannel.SMS), codecForConstString(TalerCorebankApi.TanChannel.EMAIL), @@ -334,10 +336,11 @@ export const codecForPublicAccountsResponse = export const codecForAccountMinimalData = (): Codec => buildCodecForObject() + .property("username", codecForString()) + .property("name", codecForString()) + .property("payto_uri", codecForPaytoString()) .property("balance", codecForBalance()) .property("debit_threshold", codecForAmountString()) - .property("name", codecForString()) - .property("username", codecForString()) .property("is_public", codecForBoolean()) .property("is_taler_exchange", codecForBoolean()) .build("TalerCorebankApi.AccountMinimalData"); @@ -377,13 +380,6 @@ export const codecForChallengeContactData = export const codecForWithdrawalPublicInfo = (): Codec => buildCodecForObject() - .property("username", codecForString()) - .property("amount", codecForAmountString()) - .property( - "selected_exchange_account", - codecOptional(codecForPaytoString()), - ) - .property("selected_reserve_pub", codecOptional(codecForString())) .property( "status", codecForEither( @@ -393,6 +389,13 @@ export const codecForWithdrawalPublicInfo = codecForConstString("confirmed"), ), ) + .property("amount", codecForAmountString()) + .property("username", codecForString()) + .property("selected_reserve_pub", codecOptional(codecForString())) + .property( + "selected_exchange_account", + codecOptional(codecForPaytoString()), + ) .build("TalerCorebankApi.WithdrawalPublicInfo"); export const codecForBankAccountTransactionsResponse = @@ -469,15 +472,6 @@ export const codecForCashouts = (): Codec => export const codecForCashoutInfo = (): Codec => buildCodecForObject() .property("cashout_id", codecForNumber()) - .property( - "status", - codecOptional( - codecForEither( - codecForConstString("pending"), - codecForConstString("aborted"), - codecForConstString("confirmed"), - )), - ) .build("TalerCorebankApi.CashoutInfo"); export const codecForGlobalCashouts = @@ -491,41 +485,15 @@ export const codecForGlobalCashoutInfo = buildCodecForObject() .property("cashout_id", codecForNumber()) .property("username", codecForString()) - .property( - "status", - codecOptional( - codecForEither( - codecForConstString("pending"), - codecForConstString("aborted"), - codecForConstString("confirmed"), - )), - ) .build("TalerCorebankApi.GlobalCashoutInfo"); export const codecForCashoutStatusResponse = (): Codec => buildCodecForObject() - .property("amount_credit", codecForAmountString()) .property("amount_debit", codecForAmountString()) - .property("creation_time", codecForTimestamp) - .property( - "tan_channel", - codecOptional(codecForEither( - codecForConstString(TalerCorebankApi.TanChannel.SMS), - codecForConstString(TalerCorebankApi.TanChannel.EMAIL), - )), - ) + .property("amount_credit", codecForAmountString()) .property("subject", codecForString()) - .property("confirmation_time", codecOptional(codecForTimestamp)) - .property( - "status", - codecOptional(codecForEither( - codecForConstString("pending"), - codecForConstString("aborted"), - codecForConstString("confirmed"), - )), - ) - .property("tan_info", codecOptional(codecForString())) + .property("creation_time", codecForTimestamp) .build("TalerCorebankApi.CashoutStatusResponse"); export const codecForConversionRatesResponse = @@ -607,7 +575,6 @@ export const codecForBankWithdrawalOperationPostResponse = .property( "status", codecForEither( - codecForConstString("pending"), codecForConstString("selected"), codecForConstString("aborted"), codecForConstString("confirmed"), @@ -872,7 +839,7 @@ export const codecForConversionBankConfig = .property("fiat_currency", codecForString()) .property("fiat_currency_specification", codecForCurrencySpecificiation()) - .property("conversion_rate", codecOptional(codecForConversionInfo())) + .property("conversion_rate", (codecForConversionInfo())) .build("ConversionBankConfig.IntegrationConfig"); // export const codecFor = @@ -1177,7 +1144,7 @@ export namespace TalerBankConversionApi { // Extra conversion rate information. // Only present if server opts in to report the static conversion rate. - conversion_rate?: ConversionInfo; + conversion_rate: ConversionInfo; } export interface CashinConversionResponse { @@ -1564,6 +1531,9 @@ export namespace TalerCorebankApi { // Legal name of the account owner. name: string; + // Internal payto URI of this bank account. + payto_uri: PaytoString; + // current balance of the account balance: Balance; @@ -1638,14 +1608,6 @@ export namespace TalerCorebankApi { // otherwise the request will fail. amount_credit: AmountString; - // Which channel the TAN should be sent to. If - // this field is missing, it defaults to SMS. - // The default choice prefers to change the communication - // channel respect to the one used to issue this request. - /** - * @deprecated since 4, use 2fa - */ - tan_channel?: TanChannel; } export interface CashoutResponse { @@ -1680,10 +1642,6 @@ export namespace TalerCorebankApi { export interface GlobalCashoutInfo { cashout_id: number; username: string; - /** - * @deprecated since 4, use new 2fa - */ - status?: "pending" | "aborted" | "confirmed"; } export interface CashoutStatusResponse { @@ -1698,38 +1656,8 @@ export namespace TalerCorebankApi { // Transaction subject. subject: string; - // Fiat bank account that will receive the cashed out amount. - // Specified as a payto URI. - // credit_payto_uri: PaytoString; - // Time when the cashout was created. creation_time: Timestamp; - - /** - * @deprecated since 4, use new 2fa - */ - status?: "pending" | "aborted" | "confirmed"; - - // Time when the cashout was confirmed via its TAN. - // Missing when the operation wasn't confirmed yet. - /** - * @deprecated since 4, use new 2fa - */ - confirmation_time?: Timestamp; - - // Channel of the last successful transmission of the TAN challenge. - // Missing when all transmissions failed. - /** - * @deprecated since 4, use new 2fa - */ - tan_channel?: TanChannel; - - // Info of the last successful transmission of the TAN challenge. - // Missing when all transmissions failed. - /** - * @deprecated since 4, use new 2fa - */ - tan_info?: string; } export interface ConversionRatesResponse { diff --git a/packages/taler-util/src/http-client/utils.ts b/packages/taler-util/src/http-client/utils.ts index ab6f809ef..e4b874c2f 100644 --- a/packages/taler-util/src/http-client/utils.ts +++ b/packages/taler-util/src/http-client/utils.ts @@ -1,6 +1,6 @@ import { base64FromArrayBuffer } from "../base64.js"; import { stringToBytes } from "../taler-crypto.js"; -import { AccessToken, PaginationParams } from "./types.js"; +import { AccessToken, LongPollParams, PaginationParams } from "./types.js"; /** * Helper function to generate the "Authorization" HTTP header. @@ -25,9 +25,6 @@ export function makeBearerTokenAuthHeader(token: AccessToken): string { */ export function addPaginationParams(url: URL, pagination?: PaginationParams) { if (!pagination) return; - if (pagination.timoutMs) { - url.searchParams.set("long_poll_ms", String(pagination.timoutMs)) - } if (pagination.offset) { url.searchParams.set("start", pagination.offset) } @@ -36,3 +33,10 @@ export function addPaginationParams(url: URL, pagination?: PaginationParams) { //always send delta url.searchParams.set("delta", String(order * limit)) } + +export function addLongPollingParam(url: URL, param?: LongPollParams) { + if (!param) return; + if (param.timeoutMs) { + url.searchParams.set("long_poll_ms", String(param.timeoutMs)) + } +} -- cgit v1.2.3