From ca076e5eab10d2d471dd68732a7d8a7ac9f5f6bc Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 13 Nov 2023 15:33:13 -0300 Subject: fix new libeufin api --- packages/taler-util/src/http-client/bank-core.ts | 89 ++++++++--- packages/taler-util/src/http-client/types.ts | 185 ++++++++++++++--------- 2 files changed, 183 insertions(+), 91 deletions(-) (limited to 'packages/taler-util/src/http-client') diff --git a/packages/taler-util/src/http-client/bank-core.ts b/packages/taler-util/src/http-client/bank-core.ts index 1107a3c93..0b99943a3 100644 --- a/packages/taler-util/src/http-client/bank-core.ts +++ b/packages/taler-util/src/http-client/bank-core.ts @@ -31,7 +31,7 @@ import { TalerAuthenticationHttpClient } from "./authentication.js"; import { TalerBankIntegrationHttpClient } from "./bank-integration.js"; import { TalerRevenueHttpClient } from "./bank-revenue.js"; import { TalerWireGatewayHttpClient } from "./bank-wire.js"; -import { AccessToken, PaginationParams, TalerCorebankApi, UserAndToken, codecForAccountData, codecForBankAccountCreateWithdrawalResponse, codecForBankAccountGetWithdrawalResponse, codecForBankAccountTransactionInfo, codecForBankAccountTransactionsResponse, codecForCashoutConversionResponse, codecForCashoutPending, codecForCashoutStatusResponse, codecForCashouts, codecForCoreBankConfig, codecForGlobalCashouts, codecForListBankAccountsResponse, codecForMonitorResponse, codecForPublicAccountsResponse } from "./types.js"; +import { AccessToken, PaginationParams, TalerCorebankApi, UserAndToken, codecForAccountData, codecForBankAccountCreateWithdrawalResponse, codecForBankAccountGetWithdrawalResponse, codecForBankAccountTransactionInfo, codecForBankAccountTransactionsResponse, codecForCashinConversionResponse, codecForCashoutConversionResponse, codecForCashoutPending, codecForCashoutStatusResponse, codecForCashouts, codecForCoreBankConfig, codecForGlobalCashouts, codecForListBankAccountsResponse, codecForMonitorResponse, codecForPublicAccountsResponse } from "./types.js"; import { addPaginationParams, makeBearerTokenAuthHeader } from "./utils.js"; @@ -181,6 +181,9 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.NoContent: return opEmptySuccess() case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp); case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", resp); + //FIXME: add code to split cases + // * An admin account tried to make its account an exchange + // * A non-admin user tried to change properties reserved for the admin case HttpStatusCode.Forbidden: return opKnownFailure("old-password-invalid-or-not-allowed", resp); default: return opUnknownFailure(resp, await resp.text()) } @@ -444,13 +447,15 @@ export class TalerCoreBankHttpClient { const body = await resp.json() const details = codecForTalerErrorDetail().decode(body) switch (details.code) { + case TalerErrorCode.BANK_TRANSFER_REQUEST_UID_REUSED: return opKnownFailure("request-already-used", resp); case TalerErrorCode.BANK_BAD_CONVERSION: return opKnownFailure("incorrect-exchange-rate", resp); case TalerErrorCode.BANK_MISSING_TAN_INFO: return opKnownFailure("no-contact-info", resp); case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownFailure("no-enough-balance", resp); default: return opUnknownFailure(resp, body) } } - case HttpStatusCode.ServiceUnavailable: return opKnownFailure("cashout-not-supported", resp); + case HttpStatusCode.NotImplemented: return opKnownFailure("cashout-not-supported", resp); + case HttpStatusCode.BadGateway: return opKnownFailure("tan-failed", resp); default: return opUnknownFailure(resp, await resp.text()) } } @@ -472,7 +477,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp); case HttpStatusCode.Conflict: return opKnownFailure("already-confirmed", resp); //FIXME: should be 404 ? - case HttpStatusCode.ServiceUnavailable: return opKnownFailure("cashout-not-supported", resp); + case HttpStatusCode.NotImplemented: return opKnownFailure("cashout-not-supported", resp); default: return opUnknownFailure(resp, await resp.text()) } } @@ -492,11 +497,22 @@ export class TalerCoreBankHttpClient { }); switch (resp.status) { case HttpStatusCode.NoContent: return opEmptySuccess() - case HttpStatusCode.Forbidden: return opKnownFailure("wrong-tan-or-credential", resp); case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp); - case HttpStatusCode.Conflict: return opKnownFailure("cashout-address-changed", resp); - //FIXME: should be 404 ? - case HttpStatusCode.ServiceUnavailable: return opKnownFailure("cashout-not-supported", resp); + // case HttpStatusCode.Forbidden: return opKnownFailure("wrong-tan-or-credential", resp); + case HttpStatusCode.Conflict: { + const body = await resp.json() + const details = codecForTalerErrorDetail().decode(body) + switch (details.code) { + case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: return opKnownFailure("already-aborted", resp); + case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return opKnownFailure("no-cashout-payto", resp); + case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownFailure("no-enough-balance", resp); + case TalerErrorCode.BANK_BAD_CONVERSION: return opKnownFailure("incorrect-exchange-rate", resp); + // case TalerErrorCode.BANK_TAN_CHALLENGE_FAILED: return opKnownFailure("no-enough-balance", resp); + default: return opUnknownFailure(resp, body) + } + } + case HttpStatusCode.TooManyRequests: return opKnownFailure("too-many-attempts", resp); + case HttpStatusCode.NotImplemented: return opKnownFailure("cashout-not-supported", resp); default: return opUnknownFailure(resp, await resp.text()) } } @@ -518,7 +534,7 @@ export class TalerCoreBankHttpClient { case HttpStatusCode.Ok: return opSuccess(resp, codecForCashouts()) case HttpStatusCode.NoContent: return opFixedSuccess({ cashouts: [] }); case HttpStatusCode.NotFound: return opKnownFailure("account-not-found", resp);; - case HttpStatusCode.ServiceUnavailable: return opKnownFailure("cashout-not-supported", resp); + case HttpStatusCode.NotImplemented: return opKnownFailure("cashout-not-supported", resp); default: return opUnknownFailure(resp, await resp.text()) } } @@ -538,7 +554,7 @@ export class TalerCoreBankHttpClient { switch (resp.status) { case HttpStatusCode.Ok: return opSuccess(resp, codecForCashoutStatusResponse()) case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp); - case HttpStatusCode.ServiceUnavailable: return opKnownFailure("cashout-not-supported", resp); + case HttpStatusCode.NotImplemented: return opKnownFailure("cashout-not-supported", resp); default: return opUnknownFailure(resp, await resp.text()) } } @@ -559,7 +575,7 @@ export class TalerCoreBankHttpClient { switch (resp.status) { case HttpStatusCode.Ok: return opSuccess(resp, codecForGlobalCashouts()) case HttpStatusCode.NoContent: return opFixedSuccess({ cashouts: [] }); - case HttpStatusCode.ServiceUnavailable: return opKnownFailure("cashout-not-supported", resp); + case HttpStatusCode.NotImplemented: return opKnownFailure("cashout-not-supported", resp); default: return opUnknownFailure(resp, await resp.text()) } } @@ -574,24 +590,61 @@ export class TalerCoreBankHttpClient { url.searchParams.set("amount_debit", Amounts.stringify(conversion.debit)) } if (conversion.credit) { - url.searchParams.set("amount_debit", Amounts.stringify(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 opSuccess(resp, codecForCashoutConversionResponse()) - // FIXME: error code for - // * the requested currency was not supported - // * the calculation is not correct - case HttpStatusCode.BadRequest: return opKnownFailure("wrong-calculation", resp); - case HttpStatusCode.NotFound: return opKnownFailure("not-supported", resp); - //FIXME: should be 404 ? - case HttpStatusCode.ServiceUnavailable: return opKnownFailure("cashout-not-supported", resp); + case HttpStatusCode.BadRequest: { + const body = await resp.json() + const details = codecForTalerErrorDetail().decode(body) + switch (details.code) { + case TalerErrorCode.GENERIC_PARAMETER_MISSING: return opKnownFailure("missing-params", resp); + case TalerErrorCode.GENERIC_PARAMETER_MALFORMED : return opKnownFailure("wrong-calculation", resp); + case TalerErrorCode.GENERIC_CURRENCY_MISMATCH: return opKnownFailure("wrong-currency", resp); + default: return opUnknownFailure(resp, body) + } + } + case HttpStatusCode.Conflict: return opKnownFailure("amount-too-small", resp); + case HttpStatusCode.NotImplemented: return opKnownFailure("cashout-not-supported", resp); default: return opUnknownFailure(resp, await resp.text()) } } + /** + * https://docs.taler.net/core/api-corebank.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 opSuccess(resp, codecForCashinConversionResponse()) + case HttpStatusCode.BadRequest: { + const body = await resp.json() + const details = codecForTalerErrorDetail().decode(body) + switch (details.code) { + case TalerErrorCode.GENERIC_PARAMETER_MISSING: return opKnownFailure("missing-params", resp); + case TalerErrorCode.GENERIC_PARAMETER_MALFORMED : return opKnownFailure("wrong-calculation", resp); + case TalerErrorCode.GENERIC_CURRENCY_MISMATCH: return opKnownFailure("wrong-currency", resp); + default: return opUnknownFailure(resp, body) + } + } + case HttpStatusCode.Conflict: return opKnownFailure("amount-too-small", resp); + case HttpStatusCode.NotImplemented: return opKnownFailure("cashout-not-supported", resp); + default: return opUnknownFailure(resp, await resp.text()) + } + } // // MONITOR // diff --git a/packages/taler-util/src/http-client/types.ts b/packages/taler-util/src/http-client/types.ts index 77004cf5b..f6542abcd 100644 --- a/packages/taler-util/src/http-client/types.ts +++ b/packages/taler-util/src/http-client/types.ts @@ -357,13 +357,13 @@ export const codecForBankAccountTransactionsResponse = export const codecForBankAccountTransactionInfo = (): Codec => buildCodecForObject() - .property("amount", codecForAmountString()) .property("creditor_payto_uri", codecForPaytoString()) - .property("date", codecForTimestamp) .property("debtor_payto_uri", codecForPaytoString()) + .property("amount", codecForAmountString()) .property("direction", codecForEither(codecForConstString("debit"), codecForConstString("credit"))) - .property("row_id", codecForNumber()) .property("subject", codecForString()) + .property("row_id", codecForNumber()) + .property("date", codecForTimestamp) .build("TalerCorebankApi.BankAccountTransactionInfo"); export const codecForBankAccountCreateWithdrawalResponse = @@ -376,8 +376,8 @@ export const codecForBankAccountCreateWithdrawalResponse = export const codecForBankAccountGetWithdrawalResponse = (): Codec => buildCodecForObject() - .property("aborted", codecForBoolean()) .property("amount", codecForAmountString()) + .property("aborted", codecForBoolean()) .property("confirmation_done", codecForBoolean()) .property("selected_exchange_account", codecOptional(codecForPaytoString())) .property("selected_reserve_pub", codecOptional(codecForString())) @@ -397,6 +397,13 @@ export const codecForCashoutConversionResponse = .property("amount_debit", codecForAmountString()) .build("TalerCorebankApi.CashoutConversionResponse"); +export const codecForCashinConversionResponse = + (): Codec => + buildCodecForObject() + .property("amount_credit", codecForAmountString()) + .property("amount_debit", codecForAmountString()) + .build("TalerCorebankApi.CashinConversionResponse"); + export const codecForCashouts = (): Codec => buildCodecForObject() @@ -407,7 +414,7 @@ export const codecForCashoutInfo = (): Codec => buildCodecForObject() .property("cashout_id", codecForString()) - .property("status", codecForEither(codecForConstString("pending"), codecForConstString("confirmed"),)) + .property("status", codecForEither(codecForConstString("pending"), codecForConstString("aborted"), codecForConstString("confirmed"),)) .build("TalerCorebankApi.CashoutInfo"); export const codecForGlobalCashouts = @@ -421,7 +428,7 @@ export const codecForGlobalCashoutInfo = buildCodecForObject() .property("cashout_id", codecForString()) .property("username", codecForString()) - .property("status", codecForEither(codecForConstString("pending"), codecForConstString("confirmed"),)) + .property("status", codecForEither(codecForConstString("pending"), codecForConstString("aborted"), codecForConstString("confirmed"),)) .build("TalerCorebankApi.GlobalCashoutInfo"); export const codecForCashoutStatusResponse = @@ -431,8 +438,8 @@ export const codecForCashoutStatusResponse = .property("amount_debit", codecForAmountString()) .property("confirmation_time", codecForTimestamp) .property("creation_time", codecForTimestamp) - .property("credit_payto_uri", codecForPaytoString()) - .property("status", codecForEither(codecForConstString("pending"), codecForConstString("confirmed"))) + // .property("credit_payto_uri", codecForPaytoString()) + .property("status", codecForEither(codecForConstString("pending"), codecForConstString("aborted"), codecForConstString("confirmed"))) .property("subject", codecForString()) .build("TalerCorebankApi.CashoutStatusResponse"); @@ -448,28 +455,34 @@ export const codecForConversionRatesResponse = export const codecForMonitorResponse = (): Codec => buildCodecForUnion() .discriminateOn("type") - .alternative("just-payouts", codecForMonitorResponseJustPayout()) - .alternative("with-cashout", codecForMonitorResponseWithCashout()) + .alternative("no-conversions", codecForMonitorNoConversion()) + .alternative("with-conversions", codecForMonitorWithCashout()) .build("TalerWireGatewayApi.IncomingBankTransaction"); -export const codecForMonitorResponseJustPayout = - (): Codec => - buildCodecForObject() - .property("type", codecForConstString("just-payouts")) - .property("talerPayoutCount", codecForNumber()) - .property("talerPayoutInternalVolume", codecForAmountString()) +export const codecForMonitorNoConversion = + (): Codec => + buildCodecForObject() + .property("type", codecForConstString("no-conversions")) + .property("talerInCount", codecForNumber()) + .property("talerInVolume", codecForAmountString()) + .property("talerOutCount", codecForNumber()) + .property("talerOutVolume", codecForAmountString()) .build("TalerCorebankApi.MonitorJustPayouts"); -export const codecForMonitorResponseWithCashout = - (): Codec => - buildCodecForObject() - .property("type", codecForConstString("with-cashout")) - .property("cashinCount", (codecForNumber())) - .property("cashinExternalVolume", (codecForAmountString())) - .property("cashoutCount", (codecForNumber())) - .property("cashoutExternalVolume", (codecForAmountString())) - .property("talerPayoutCount", codecForNumber()) - .property("talerPayoutInternalVolume", codecForAmountString()) +export const codecForMonitorWithCashout = + (): Codec => + buildCodecForObject() + .property("type", codecForConstString("with-conversions")) + .property("cashinCount", codecForNumber()) + .property("cashinFiatVolume", codecForAmountString()) + .property("cashinRegionalVolume", codecForAmountString()) + .property("cashoutCount", codecForNumber()) + .property("cashoutFiatVolume", codecForAmountString()) + .property("cashoutRegionalVolume", codecForAmountString()) + .property("talerInCount", codecForNumber()) + .property("talerInVolume", codecForAmountString()) + .property("talerOutCount", codecForNumber()) + .property("talerOutVolume", codecForAmountString()) .build("TalerCorebankApi.MonitorWithCashout"); export const codecForBankVersion = @@ -697,7 +710,6 @@ const codecForDecimalNumber = codecForNumber enum TanChannel { SMS = "sms", EMAIL = "email", - FILE = "file" } export namespace TalerWireGatewayApi { @@ -1174,7 +1186,7 @@ export namespace TalerCorebankApi { // If present, change the is_exchange configuration. // See RegisterAccountRequest - is_exchange?: boolean; + is_taler_exchange?: boolean; // If present, change the max debit allowed for this user // Only admin can change this property. @@ -1252,10 +1264,15 @@ export namespace TalerCorebankApi { export interface CashoutRequest { + // Nonce to make the request idempotent. Requests with the same + // request_uid that differ in any of the other fields + // are rejected. + request_uid: ShortHashCode; + // Optional subject to associate to the // cashout operation. This data will appear // as the incoming wire transfer subject in - // the user's external bank account. + // the user's fiat bank account. subject?: string; // That is the plain amount that the user specified @@ -1301,6 +1318,15 @@ export namespace TalerCorebankApi { amount_credit: AmountString; } + export interface CashinConversionResponse { + // Amount that the user will get deducted from their fiat + // bank account, according to the 'amount_credit' value. + amount_debit: AmountString; + // Amount that the user will receive in their regional + // bank account, according to 'amount_debit'. + amount_credit: AmountString; + } + export interface Cashouts { // Every string represents a cash-out operation ID. cashouts: CashoutInfo[]; @@ -1308,7 +1334,7 @@ export namespace TalerCorebankApi { export interface CashoutInfo { cashout_id: string; - status: "pending" | "confirmed"; + status: "pending" | "aborted" | "confirmed"; } export interface GlobalCashouts { // Every string represents a cash-out operation ID. @@ -1317,11 +1343,11 @@ export namespace TalerCorebankApi { export interface GlobalCashoutInfo { cashout_id: string; username: string; - status: "pending" | "confirmed"; + status: "pending" | "aborted" | "confirmed"; } export interface CashoutStatusResponse { - status: "pending" | "confirmed"; + status: "pending" | "aborted" | "confirmed"; // Amount debited to the internal // regional currency bank account. @@ -1335,7 +1361,7 @@ export namespace TalerCorebankApi { // Fiat bank account that will receive the cashed out amount. // Specified as a payto URI. - credit_payto_uri: PaytoString; + // credit_payto_uri: PaytoString; // Time when the cashout was created. creation_time: Timestamp; @@ -1365,59 +1391,72 @@ export namespace TalerCorebankApi { } export type MonitorResponse = - | MonitorJustPayouts - | MonitorWithCashout; + | MonitorNoConversion + | MonitorWithConversion; // Monitoring stats when conversion is not supported - export interface MonitorJustPayouts { - type: "just-payouts"; + export interface MonitorNoConversion { + type: "no-conversions"; + + // How many payments were made to a Taler exchange by another + // bank account. + talerInCount: number; - // This number identifies how many payments were made by a - // Taler exchange to a merchant bank account in the internal - // currency, in the timeframe specified in the request. - talerPayoutCount: number; + // Overall volume that has been paid to a Taler + // exchange by another bank account. + talerInVolume: AmountString; - // This amount accounts the overall *internal* currency that - // has been paid by a Taler exchange to a merchant internal - // bank account, in the timeframe specified in the request. - talerPayoutInternalVolume: AmountString; + // How many payments were made by a Taler exchange to another + // bank account. + talerOutCount: number; + + // Overall volume that has been paid by a Taler + // exchange to another bank account. + talerOutVolume: AmountString; } // Monitoring stats when conversion is supported - export interface MonitorWithCashout { - type: "with-cashout"; + export interface MonitorWithConversion { + type: "with-conversions"; - // This number identifies how many cashin operations - // took place in the timeframe specified in the request. - // This number corresponds to how many withdrawals have - // been initiated by a wallet owner. Note: wallet owners + // How many cashin operations were confirmed by a + // wallet owner. Note: wallet owners // are NOT required to be customers of the libeufin-bank. cashinCount: number; - // This amount accounts how much external currency has been - // spent to withdraw Taler coins in the internal currency. - // The exact amount of internal currency being created can be - // calculated using the advertised conversion rates. - cashinExternalVolume: AmountString; + // Overall regional currency that has been paid by the regional admin account + // to regional bank accounts to fulfill all the confirmed cashin operations. + cashinRegionalVolume: AmountString; + + // Overall fiat currency that has been paid to the fiat admin account + // by fiat bank accounts to fulfill all the confirmed cashin operations. + cashinFiatVolume: AmountString; - // This number identifies how many cashout operations were - // confirmed in the timeframe speficied in the request. + // How many cashout operations were confirmed. cashoutCount: number; - // This amount corresponds to how much *external* currency was - // paid by the libeufin-bank administrator to fulfill all the - // confirmed cashouts related to the timeframe specified in the - // request. - cashoutExternalVolume: AmountString; - - // This number identifies how many payments were made by a - // Taler exchange to a merchant bank account in the internal - // currency, in the timeframe specified in the request. - talerPayoutCount: number; - - // This amount accounts the overall *internal* currency that - // has been paid by a Taler exchange to a merchant internal - // bank account, in the timeframe specified in the request. - talerPayoutInternalVolume: AmountString; + // Overall regional currency that has been paid to the regional admin account + // by fiat bank accounts to fulfill all the confirmed cashout operations. + cashoutRegionalVolume: AmountString; + + // Overall fiat currency that has been paid by the fiat admin account + // to fiat bank accounts to fulfill all the confirmed cashout operations. + cashoutFiatVolume: AmountString; + + // How many payments were made to a Taler exchange by another + // bank account. + talerInCount: number; + + // Overall volume that has been paid to a Taler + // exchange by another bank account. + talerInVolume: AmountString; + + // How many payments were made by a Taler exchange to another + // bank account. + talerOutCount: number; + + // Overall volume that has been paid by a Taler + // exchange to another bank account. + talerOutVolume: AmountString; } -- cgit v1.2.3