diff options
-rw-r--r-- | packages/taler-harness/src/harness/harness.ts | 39 | ||||
-rw-r--r-- | packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts | 34 | ||||
-rw-r--r-- | packages/taler-util/src/http-client/types.ts | 534 | ||||
-rw-r--r-- | packages/taler-util/src/taler-types.ts | 41 | ||||
-rw-r--r-- | packages/taler-util/src/transactions-types.ts | 5 | ||||
-rw-r--r-- | packages/taler-util/src/wallet-types.ts | 24 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/crypto/cryptoImplementation.ts | 2 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/db.ts | 7 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/operations/transactions.ts | 1 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/operations/withdraw.ts | 129 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/wallet.ts | 1 |
11 files changed, 474 insertions, 343 deletions
diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts index 37e0b02a7..242bf2207 100644 --- a/packages/taler-harness/src/harness/harness.ts +++ b/packages/taler-harness/src/harness/harness.ts @@ -928,6 +928,8 @@ export class ExchangeService implements ExchangeServiceInterface { private currentTimetravelOffsetMs: number | undefined; + private exchangeBankAccounts: HarnessExchangeBankAccount[] = []; + setTimetravel(t: number | undefined): void { if (this.isRunning()) { throw Error("can't set time travel while the exchange is running"); @@ -1132,6 +1134,7 @@ export class ExchangeService implements ExchangeServiceInterface { localName: string, exchangeBankAccount: HarnessExchangeBankAccount, ): Promise<void> { + this.exchangeBankAccounts.push(exchangeBankAccount); const config = Configuration.load(this.configFilename); config.setString( `exchange-account-${localName}`, @@ -1277,32 +1280,32 @@ export class ExchangeService implements ExchangeServiceInterface { ["-c", this.configFilename, "download", "sign", "upload"], ); - const accounts: string[] = []; const accountTargetTypes: Set<string> = new Set(); - const config = Configuration.load(this.configFilename); - for (const sectionName of config.getSectionNames()) { - if (sectionName.startsWith("EXCHANGE-ACCOUNT-")) { - const paytoUri = config.getString(sectionName, "payto_uri").required(); - const p = parsePaytoUri(paytoUri); - if (!p) { - throw Error(`invalid payto uri in exchange config: ${paytoUri}`); - } - accountTargetTypes.add(p?.targetType); - accounts.push(paytoUri); + for (const acct of this.exchangeBankAccounts) { + const paytoUri = acct.accountPaytoUri; + const p = parsePaytoUri(paytoUri); + if (!p) { + throw Error(`invalid payto uri in exchange config: ${paytoUri}`); + } + accountTargetTypes.add(p?.targetType); + const optArgs: string[] = []; + if (acct.conversionUrl != null) { + optArgs.push("conversion-url", acct.conversionUrl); } - } - - const accountsDescription = accounts.map((acc) => ` * ${acc}`).join("\n"); - logger.info("configuring bank accounts:"); - logger.info(accountsDescription); - for (const acc of accounts) { await runCommand( this.globalState, "exchange-offline", "taler-exchange-offline", - ["-c", this.configFilename, "enable-account", acc, "upload"], + [ + "-c", + this.configFilename, + "enable-account", + paytoUri, + ...optArgs, + "upload", + ], ); } diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts index 2a9dd5800..6fc403be4 100644 --- a/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts +++ b/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts @@ -23,7 +23,9 @@ import { Duration, Logger, TalerCorebankApiClient, + TransactionType, WireGatewayApiClient, + WithdrawalType, j2s, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; @@ -184,16 +186,21 @@ export async function runWithdrawalConversionTest(t: GlobalTestState) { exchangeBaseUrl: exchange.baseUrl, }); - const infoRes = walletClient.call( + const infoRes = await walletClient.call( WalletApiOperation.GetWithdrawalDetailsForAmount, { exchangeBaseUrl: exchange.baseUrl, - amount: "EXTCOIN:20" as AmountString, + amount: "TESTKUDOS:20" as AmountString, }, ); console.log(`withdrawal details: ${j2s(infoRes)}`); + t.assertAmountEquals( + infoRes.withdrawalAccountList[0].transferAmount, + "FOO:123", + ); + const tStart = AbsoluteTime.now(); logger.info("starting AcceptManualWithdrawal request"); @@ -210,6 +217,29 @@ export async function runWithdrawalConversionTest(t: GlobalTestState) { logger.info("AcceptManualWithdrawal finished"); logger.info(`result: ${j2s(wres)}`); + t.assertAmountEquals( + wres.withdrawalAccountsList[0].transferAmount, + "FOO:123", + ); + + const txInfo = await walletClient.call( + WalletApiOperation.GetTransactionById, + { + transactionId: wres.transactionId, + }, + ); + + t.assertDeepEqual(txInfo.type, TransactionType.Withdrawal); + t.assertDeepEqual( + txInfo.withdrawalDetails.type, + WithdrawalType.ManualTransfer, + ); + t.assertTrue(!!txInfo.withdrawalDetails.exchangeCreditAccounts); + t.assertDeepEqual( + txInfo.withdrawalDetails.exchangeCreditAccounts[0].transferAmount, + "FOO:123", + ); + // Check that the request did not go into long-polling. const duration = AbsoluteTime.difference(tStart, AbsoluteTime.now()); if (typeof duration.d_ms !== "number" || duration.d_ms > 5 * 1000) { diff --git a/packages/taler-util/src/http-client/types.ts b/packages/taler-util/src/http-client/types.ts index d50a0ea90..3ef0ff76c 100644 --- a/packages/taler-util/src/http-client/types.ts +++ b/packages/taler-util/src/http-client/types.ts @@ -1,20 +1,32 @@ import { codecForAmountString } from "../amounts.js"; -import { Codec, buildCodecForObject, buildCodecForUnion, codecForAny, codecForBoolean, codecForConstString, codecForEither, codecForList, codecForMap, codecForNumber, codecForString, codecOptional } from "../codec.js"; +import { + Codec, + buildCodecForObject, + buildCodecForUnion, + codecForAny, + codecForBoolean, + codecForConstString, + codecForEither, + codecForList, + codecForMap, + codecForNumber, + codecForString, + codecOptional, +} from "../codec.js"; import { PaytoString, codecForPaytoString } from "../payto.js"; import { AmountString } from "../taler-types.js"; import { TalerActionString, codecForTalerActionString } from "../taleruri.js"; import { codecForTimestamp } from "../time.js"; - export type UserAndPassword = { - username: string, - password: string, -} + username: string; + password: string; +}; export type UserAndToken = { - username: string, - token: AccessToken, -} + username: string; + token: AccessToken; +}; declare const opaque_OfficerAccount: unique symbol; export type LockedAccount = string & { [opaque_OfficerAccount]: true }; @@ -25,32 +37,30 @@ export type OfficerId = string & { [opaque_OfficerId]: true }; declare const opaque_OfficerSigningKey: unique symbol; export type SigningKey = Uint8Array & { [opaque_OfficerSigningKey]: true }; - export interface OfficerAccount { id: OfficerId; signingKey: SigningKey; } - export type PaginationParams = { /** * row identifier as the starting point of the query */ - offset?: string, + offset?: string; /** * max number of element in the result response * always greater than 0 */ - limit?: number, + limit?: number; /** * milliseconds the server should wait for at least one result to be shown */ - timoutMs?: number, + timoutMs?: number; /** * order */ - order: "asc" | "dec" -} + order: "asc" | "dec"; +}; /// /// HASH @@ -148,7 +158,6 @@ type SafeUint64 = number; // Invalid strings will be rejected by the wallet. type ImageDataUrl = string; - type WadId = string; interface Timestamp { @@ -166,8 +175,8 @@ interface RelativeTime { } export interface LoginToken { - token: AccessToken, - expiration: Timestamp, + token: AccessToken; + expiration: Timestamp; } declare const __ac_token: unique symbol; @@ -175,14 +184,12 @@ export type AccessToken = string & { [__ac_token]: true; }; - declare const __officer_signature: unique symbol; export type OfficerSignature = string & { [__officer_signature]: true; }; export namespace TalerAuthentication { - export interface TokenRequest { // Service-defined scope for the token. // Typical scopes would be "readonly" or "readwrite". @@ -213,7 +220,6 @@ export namespace TalerAuthentication { // DD51 https://docs.taler.net/design-documents/051-fractional-digits.html export interface CurrencySpecification { - // Name of the currency. name: string; @@ -240,7 +246,7 @@ export const codecForTokenSuccessResponse = buildCodecForObject<TalerAuthentication.TokenSuccessResponse>() .property("access_token", codecForAccessToken()) .property("expiration", codecForTimestamp) - .build("TalerAuthentication.TokenSuccessResponse") + .build("TalerAuthentication.TokenSuccessResponse"); export const codecForCurrencySpecificiation = (): Codec<CurrencySpecification> => @@ -250,27 +256,26 @@ export const codecForCurrencySpecificiation = .property("num_fractional_normal_digits", codecForNumber()) .property("num_fractional_trailing_zero_digits", codecForNumber()) .property("alt_unit_names", codecForMap(codecForString())) - .build("CurrencySpecification") + .build("CurrencySpecification"); export const codecForIntegrationBankConfig = (): Codec<TalerCorebankApi.IntegrationConfig> => buildCodecForObject<TalerCorebankApi.IntegrationConfig>() .property("name", codecForConstString("taler-bank-integration")) .property("version", codecForString()) - .property("currency", codecForCurrencySpecificiation()) - .build("TalerCorebankApi.IntegrationConfig") - -export const codecForCoreBankConfig = - (): Codec<TalerCorebankApi.Config> => - buildCodecForObject<TalerCorebankApi.Config>() - .property("name", codecForConstString("libeufin-bank")) - .property("version", codecForString()) - .property("allow_conversion", codecForBoolean()) - .property("allow_deletions", codecForBoolean()) - .property("allow_registrations", codecForBoolean()) - .property("currency_specification", codecForCurrencySpecificiation()) .property("currency", codecForString()) - .build("TalerCorebankApi.Config") + .build("TalerCorebankApi.IntegrationConfig"); + +export const codecForCoreBankConfig = (): Codec<TalerCorebankApi.Config> => + buildCodecForObject<TalerCorebankApi.Config>() + .property("name", codecForConstString("libeufin-bank")) + .property("version", codecForString()) + .property("allow_conversion", codecForBoolean()) + .property("allow_deletions", codecForBoolean()) + .property("allow_registrations", codecForBoolean()) + .property("currency_specification", codecForCurrencySpecificiation()) + .property("currency", codecForString()) + .build("TalerCorebankApi.Config"); export const codecForMerchantConfig = (): Codec<TalerMerchantApi.VersionResponse> => @@ -279,7 +284,7 @@ export const codecForMerchantConfig = .property("currency", codecForString()) .property("version", codecForString()) .property("currencies", codecForMap(codecForCurrencySpecificiation())) - .build("TalerMerchantApi.VersionResponse") + .build("TalerMerchantApi.VersionResponse"); export const codecForExchangeConfig = (): Codec<TalerExchangeApi.ExchangeVersionResponse> => @@ -289,27 +294,32 @@ export const codecForExchangeConfig = .property("currency", codecForString()) .property("currency_specification", codecForCurrencySpecificiation()) .property("supported_kyc_requirements", codecForList(codecForString())) - .build("TalerExchangeApi.ExchangeVersionResponse") + .build("TalerExchangeApi.ExchangeVersionResponse"); const codecForBalance = (): Codec<TalerCorebankApi.Balance> => buildCodecForObject<TalerCorebankApi.Balance>() .property("amount", codecForAmountString()) - .property("credit_debit_indicator", codecForEither(codecForConstString("credit"), codecForConstString("debit"))) - .build("TalerCorebankApi.Balance") + .property( + "credit_debit_indicator", + codecForEither( + codecForConstString("credit"), + codecForConstString("debit"), + ), + ) + .build("TalerCorebankApi.Balance"); const codecForPublicAccount = (): Codec<TalerCorebankApi.PublicAccount> => buildCodecForObject<TalerCorebankApi.PublicAccount>() .property("account_name", codecForString()) .property("balance", codecForBalance()) .property("payto_uri", codecForPaytoString()) - .build("TalerCorebankApi.PublicAccount") + .build("TalerCorebankApi.PublicAccount"); export const codecForPublicAccountsResponse = (): Codec<TalerCorebankApi.PublicAccountsResponse> => buildCodecForObject<TalerCorebankApi.PublicAccountsResponse>() .property("public_accounts", codecForList(codecForPublicAccount())) - .build("TalerCorebankApi.PublicAccountsResponse") - + .build("TalerCorebankApi.PublicAccountsResponse"); export const codecForAccountMinimalData = (): Codec<TalerCorebankApi.AccountMinimalData> => @@ -318,37 +328,38 @@ export const codecForAccountMinimalData = .property("debit_threshold", codecForAmountString()) .property("name", codecForString()) .property("username", codecForString()) - .build("TalerCorebankApi.AccountMinimalData") + .build("TalerCorebankApi.AccountMinimalData"); export const codecForListBankAccountsResponse = (): Codec<TalerCorebankApi.ListBankAccountsResponse> => buildCodecForObject<TalerCorebankApi.ListBankAccountsResponse>() .property("accounts", codecForList(codecForAccountMinimalData())) - .build("TalerCorebankApi.ListBankAccountsResponse") - -export const codecForAccountData = - (): Codec<TalerCorebankApi.AccountData> => - buildCodecForObject<TalerCorebankApi.AccountData>() - .property("name", codecForString()) - .property("balance", codecForBalance()) - .property("payto_uri", codecForPaytoString()) - .property("debit_threshold", codecForAmountString()) - .property("contact_data", codecOptional(codecForChallengeContactData())) - .property("cashout_payto_uri", codecOptional(codecForPaytoString())) - .build("TalerCorebankApi.AccountData") + .build("TalerCorebankApi.ListBankAccountsResponse"); +export const codecForAccountData = (): Codec<TalerCorebankApi.AccountData> => + buildCodecForObject<TalerCorebankApi.AccountData>() + .property("name", codecForString()) + .property("balance", codecForBalance()) + .property("payto_uri", codecForPaytoString()) + .property("debit_threshold", codecForAmountString()) + .property("contact_data", codecOptional(codecForChallengeContactData())) + .property("cashout_payto_uri", codecOptional(codecForPaytoString())) + .build("TalerCorebankApi.AccountData"); export const codecForChallengeContactData = (): Codec<TalerCorebankApi.ChallengeContactData> => buildCodecForObject<TalerCorebankApi.ChallengeContactData>() .property("email", codecOptional(codecForString())) .property("phone", codecOptional(codecForString())) - .build("TalerCorebankApi.ChallengeContactData") + .build("TalerCorebankApi.ChallengeContactData"); export const codecForBankAccountTransactionsResponse = (): Codec<TalerCorebankApi.BankAccountTransactionsResponse> => buildCodecForObject<TalerCorebankApi.BankAccountTransactionsResponse>() - .property("transactions", codecForList(codecForBankAccountTransactionInfo())) + .property( + "transactions", + codecForList(codecForBankAccountTransactionInfo()), + ) .build("TalerCorebankApi.BankAccountTransactionsResponse"); export const codecForBankAccountTransactionInfo = @@ -357,7 +368,13 @@ export const codecForBankAccountTransactionInfo = .property("creditor_payto_uri", codecForPaytoString()) .property("debtor_payto_uri", codecForPaytoString()) .property("amount", codecForAmountString()) - .property("direction", codecForEither(codecForConstString("debit"), codecForConstString("credit"))) + .property( + "direction", + codecForEither( + codecForConstString("debit"), + codecForConstString("credit"), + ), + ) .property("subject", codecForString()) .property("row_id", codecForNumber()) .property("date", codecForTimestamp) @@ -376,9 +393,12 @@ export const codecForBankAccountGetWithdrawalResponse = .property("amount", codecForAmountString()) .property("aborted", codecForBoolean()) .property("confirmation_done", codecForBoolean()) - .property("selected_exchange_account", codecOptional(codecForPaytoString())) + .property( + "selected_exchange_account", + codecOptional(codecForPaytoString()), + ) .property("selected_reserve_pub", codecOptional(codecForString())) - .property("selection_done", (codecForBoolean())) + .property("selection_done", codecForBoolean()) .build("TalerCorebankApi.BankAccountGetWithdrawalResponse"); export const codecForCashoutPending = @@ -401,18 +421,23 @@ export const codecForCashinConversionResponse = .property("amount_debit", codecForAmountString()) .build("TalerCorebankApi.CashinConversionResponse"); -export const codecForCashouts = - (): Codec<TalerCorebankApi.Cashouts> => - buildCodecForObject<TalerCorebankApi.Cashouts>() - .property("cashouts", codecForList(codecForCashoutInfo())) - .build("TalerCorebankApi.Cashouts"); - -export const codecForCashoutInfo = - (): Codec<TalerCorebankApi.CashoutInfo> => - buildCodecForObject<TalerCorebankApi.CashoutInfo>() - .property("cashout_id", codecForString()) - .property("status", codecForEither(codecForConstString("pending"), codecForConstString("aborted"), codecForConstString("confirmed"),)) - .build("TalerCorebankApi.CashoutInfo"); +export const codecForCashouts = (): Codec<TalerCorebankApi.Cashouts> => + buildCodecForObject<TalerCorebankApi.Cashouts>() + .property("cashouts", codecForList(codecForCashoutInfo())) + .build("TalerCorebankApi.Cashouts"); + +export const codecForCashoutInfo = (): Codec<TalerCorebankApi.CashoutInfo> => + buildCodecForObject<TalerCorebankApi.CashoutInfo>() + .property("cashout_id", codecForString()) + .property( + "status", + codecForEither( + codecForConstString("pending"), + codecForConstString("aborted"), + codecForConstString("confirmed"), + ), + ) + .build("TalerCorebankApi.CashoutInfo"); export const codecForGlobalCashouts = (): Codec<TalerCorebankApi.GlobalCashouts> => @@ -425,7 +450,14 @@ export const codecForGlobalCashoutInfo = buildCodecForObject<TalerCorebankApi.GlobalCashoutInfo>() .property("cashout_id", codecForString()) .property("username", codecForString()) - .property("status", codecForEither(codecForConstString("pending"), codecForConstString("aborted"), codecForConstString("confirmed"),)) + .property( + "status", + codecForEither( + codecForConstString("pending"), + codecForConstString("aborted"), + codecForConstString("confirmed"), + ), + ) .build("TalerCorebankApi.GlobalCashoutInfo"); export const codecForCashoutStatusResponse = @@ -436,7 +468,14 @@ export const codecForCashoutStatusResponse = .property("confirmation_time", codecForTimestamp) .property("creation_time", codecForTimestamp) // .property("credit_payto_uri", codecForPaytoString()) - .property("status", codecForEither(codecForConstString("pending"), codecForConstString("aborted"), codecForConstString("confirmed"))) + .property( + "status", + codecForEither( + codecForConstString("pending"), + codecForConstString("aborted"), + codecForConstString("confirmed"), + ), + ) .property("subject", codecForString()) .build("TalerCorebankApi.CashoutStatusResponse"); @@ -449,12 +488,13 @@ export const codecForConversionRatesResponse = .property("sell_out_fee", codecForDecimalNumber()) .build("TalerCorebankApi.ConversionRatesResponse"); - -export const codecForMonitorResponse = (): Codec<TalerCorebankApi.MonitorResponse> => buildCodecForUnion<TalerCorebankApi.MonitorResponse>() - .discriminateOn("type") - .alternative("no-conversions", codecForMonitorNoConversion()) - .alternative("with-conversions", codecForMonitorWithCashout()) - .build("TalerWireGatewayApi.IncomingBankTransaction"); +export const codecForMonitorResponse = + (): Codec<TalerCorebankApi.MonitorResponse> => + buildCodecForUnion<TalerCorebankApi.MonitorResponse>() + .discriminateOn("type") + .alternative("no-conversions", codecForMonitorNoConversion()) + .alternative("with-conversions", codecForMonitorWithCashout()) + .build("TalerWireGatewayApi.IncomingBankTransaction"); export const codecForMonitorNoConversion = (): Codec<TalerCorebankApi.MonitorNoConversion> => @@ -515,7 +555,10 @@ export const codecForMerchantIncomingHistory = (): Codec<TalerRevenueApi.MerchantIncomingHistory> => buildCodecForObject<TalerRevenueApi.MerchantIncomingHistory>() .property("credit_account", codecForPaytoString()) - .property("incoming_transactions", codecForList(codecForMerchantIncomingBankTransaction())) + .property( + "incoming_transactions", + codecForList(codecForMerchantIncomingBankTransaction()), + ) .build("TalerRevenueApi.MerchantIncomingHistory"); export const codecForMerchantIncomingBankTransaction = @@ -540,14 +583,19 @@ export const codecForIncomingHistory = (): Codec<TalerWireGatewayApi.IncomingHistory> => buildCodecForObject<TalerWireGatewayApi.IncomingHistory>() .property("credit_account", codecForPaytoString()) - .property("incoming_transactions", codecForList(codecForIncomingBankTransaction())) + .property( + "incoming_transactions", + codecForList(codecForIncomingBankTransaction()), + ) .build("TalerWireGatewayApi.IncomingHistory"); -export const codecForIncomingBankTransaction = (): Codec<TalerWireGatewayApi.IncomingBankTransaction> => buildCodecForUnion<TalerWireGatewayApi.IncomingBankTransaction>() - .discriminateOn("type") - .alternative("RESERVE", codecForIncomingReserveTransaction()) - .alternative("WAD", codecForIncomingWadTransaction()) - .build("TalerWireGatewayApi.IncomingBankTransaction"); +export const codecForIncomingBankTransaction = + (): Codec<TalerWireGatewayApi.IncomingBankTransaction> => + buildCodecForUnion<TalerWireGatewayApi.IncomingBankTransaction>() + .discriminateOn("type") + .alternative("RESERVE", codecForIncomingReserveTransaction()) + .alternative("WAD", codecForIncomingWadTransaction()) + .build("TalerWireGatewayApi.IncomingBankTransaction"); export const codecForIncomingReserveTransaction = (): Codec<TalerWireGatewayApi.IncomingReserveTransaction> => @@ -577,7 +625,10 @@ export const codecForOutgoingHistory = (): Codec<TalerWireGatewayApi.OutgoingHistory> => buildCodecForObject<TalerWireGatewayApi.OutgoingHistory>() .property("debit_account", codecForPaytoString()) - .property("outgoing_transactions", codecForList(codecForOutgoingBankTransaction())) + .property( + "outgoing_transactions", + codecForList(codecForOutgoingBankTransaction()), + ) .build("TalerWireGatewayApi.OutgoingHistory"); export const codecForOutgoingBankTransaction = @@ -598,20 +649,18 @@ export const codecForAddIncomingResponse = .property("timestamp", codecForTimestamp) .build("TalerWireGatewayApi.AddIncomingResponse"); -export const codecForAmlRecords = - (): Codec<TalerExchangeApi.AmlRecords> => - buildCodecForObject<TalerExchangeApi.AmlRecords>() - .property("records", codecForList(codecForAmlRecord())) - .build("TalerExchangeApi.PublicAccountsResponse"); - -export const codecForAmlRecord = - (): Codec<TalerExchangeApi.AmlRecord> => - buildCodecForObject<TalerExchangeApi.AmlRecord>() - .property("current_state", codecForNumber()) - .property("h_payto", codecForString()) - .property("rowid", codecForNumber()) - .property("threshold", codecForAmountString()) - .build("TalerExchangeApi.AmlRecord"); +export const codecForAmlRecords = (): Codec<TalerExchangeApi.AmlRecords> => + buildCodecForObject<TalerExchangeApi.AmlRecords>() + .property("records", codecForList(codecForAmlRecord())) + .build("TalerExchangeApi.PublicAccountsResponse"); + +export const codecForAmlRecord = (): Codec<TalerExchangeApi.AmlRecord> => + buildCodecForObject<TalerExchangeApi.AmlRecord>() + .property("current_state", codecForNumber()) + .property("h_payto", codecForString()) + .property("rowid", codecForNumber()) + .property("threshold", codecForAmountString()) + .build("TalerExchangeApi.AmlRecord"); export const codecForAmlDecisionDetails = (): Codec<TalerExchangeApi.AmlDecisionDetails> => @@ -636,27 +685,24 @@ interface KycDetail { collection_time: Timestamp; expiration_time: Timestamp; } -export const codecForKycDetail = - (): Codec<TalerExchangeApi.KycDetail> => - buildCodecForObject<TalerExchangeApi.KycDetail>() - .property("provider_section", codecForString()) - .property("attributes", codecOptional(codecForAny())) - .property("collection_time", codecForTimestamp) - .property("expiration_time", codecForTimestamp) - .build("TalerExchangeApi.KycDetail"); - -export const codecForAmlDecision = - (): Codec<TalerExchangeApi.AmlDecision> => - buildCodecForObject<TalerExchangeApi.AmlDecision>() - .property("justification", codecForString()) - .property("new_threshold", codecForAmountString()) - .property("h_payto", codecForString()) - .property("new_state", codecForNumber()) - .property("officer_sig", codecForString()) - .property("decision_time", codecForTimestamp) - .property("kyc_requirements", codecOptional(codecForList(codecForString()))) - .build("TalerExchangeApi.AmlDecision"); - +export const codecForKycDetail = (): Codec<TalerExchangeApi.KycDetail> => + buildCodecForObject<TalerExchangeApi.KycDetail>() + .property("provider_section", codecForString()) + .property("attributes", codecOptional(codecForAny())) + .property("collection_time", codecForTimestamp) + .property("expiration_time", codecForTimestamp) + .build("TalerExchangeApi.KycDetail"); + +export const codecForAmlDecision = (): Codec<TalerExchangeApi.AmlDecision> => + buildCodecForObject<TalerExchangeApi.AmlDecision>() + .property("justification", codecForString()) + .property("new_threshold", codecForAmountString()) + .property("h_payto", codecForString()) + .property("new_state", codecForNumber()) + .property("officer_sig", codecForString()) + .property("decision_time", codecForTimestamp) + .property("kyc_requirements", codecOptional(codecForList(codecForString()))) + .build("TalerExchangeApi.AmlDecision"); // version: string; @@ -710,23 +756,29 @@ export const codecForConversionInfo = .property("cashin_min_amount", codecForAmountString()) .property("cashin_ratio", codecForString()) // .property("cashin_ratio", codecForDecimalNumber()) - .property("cashin_rounding_mode", codecForEither( - codecForConstString("zero"), - codecForConstString("up"), - codecForConstString("nearest") - )) + .property( + "cashin_rounding_mode", + codecForEither( + codecForConstString("zero"), + codecForConstString("up"), + codecForConstString("nearest"), + ), + ) .property("cashin_tiny_amount", codecForAmountString()) .property("cashout_fee", codecForAmountString()) .property("cashout_min_amount", codecForAmountString()) .property("cashout_ratio", codecForString()) // .property("cashout_ratio", codecForDecimalNumber()) - .property("cashout_rounding_mode", codecForEither( - codecForConstString("zero"), - codecForConstString("up"), - codecForConstString("nearest") - )) + .property( + "cashout_rounding_mode", + codecForEither( + codecForConstString("zero"), + codecForConstString("up"), + codecForConstString("nearest"), + ), + ) .property("cashout_tiny_amount", codecForAmountString()) - .build("ConversionBankConfig.ConversionInfo") + .build("ConversionBankConfig.ConversionInfo"); export const codecForConversionBankConfig = (): Codec<TalerBankConversionApi.IntegrationConfig> => @@ -734,7 +786,10 @@ export const codecForConversionBankConfig = .property("name", codecForConstString("taler-conversion-info")) .property("version", codecForString()) .property("regional_currency", codecForString()) - .property("regional_currency_specification", codecForCurrencySpecificiation()) + .property( + "regional_currency_specification", + codecForCurrencySpecificiation(), + ) .property("fiat_currency", codecForString()) .property("fiat_currency_specification", codecForCurrencySpecificiation()) // .property("conversion_info", codecOptional(codecForConversionInfo())) @@ -742,30 +797,35 @@ export const codecForConversionBankConfig = .property("cashin_fee", codecForAmountString()) .property("cashin_min_amount", codecForAmountString()) .property("cashin_ratio", codecForString()) - .property("cashin_rounding_mode", codecForEither( - codecForConstString("zero"), - codecForConstString("up"), - codecForConstString("nearest") - )) + .property( + "cashin_rounding_mode", + codecForEither( + codecForConstString("zero"), + codecForConstString("up"), + codecForConstString("nearest"), + ), + ) .property("cashin_tiny_amount", codecForAmountString()) .property("cashout_fee", codecForAmountString()) .property("cashout_min_amount", codecForAmountString()) .property("cashout_ratio", codecForString()) - .property("cashout_rounding_mode", codecForEither( - codecForConstString("zero"), - codecForConstString("up"), - codecForConstString("nearest") - )) + .property( + "cashout_rounding_mode", + codecForEither( + codecForConstString("zero"), + codecForConstString("up"), + codecForConstString("nearest"), + ), + ) .property("cashout_tiny_amount", codecForAmountString()) ////////////////////////// - .build("ConversionBankConfig.IntegrationConfig") + .build("ConversionBankConfig.IntegrationConfig"); // export const codecFor = // (): Codec<TalerWireGatewayApi.PublicAccountsResponse> => // buildCodecForObject<TalerWireGatewayApi.PublicAccountsResponse>() // .property("", codecForString()) // .build("TalerWireGatewayApi.PublicAccountsResponse"); - type EmailAddress = string; type PhoneNumber = string; type EddsaSignature = string; @@ -782,7 +842,7 @@ type CoinEnvelope = RSACoinEnvelope | CSCoinEnvelope; // coin's public EdDSA key. interface RSACoinEnvelope { cipher: "RSA" | "RSA+age_restricted"; - rsa_blinded_planchet: string; // Crockford Base32 encoded + rsa_blinded_planchet: string; // Crockford Base32 encoded } // For denomination signatures based on Blind Clause-Schnorr, the planchet // consists of the public nonce and two Curve25519 scalars which are two @@ -790,7 +850,7 @@ interface RSACoinEnvelope { // See https://taler.net/papers/cs-thesis.pdf for details. interface CSCoinEnvelope { cipher: "CS" | "CS+age_restricted"; - cs_nonce: string; // Crockford Base32 encoded + cs_nonce: string; // Crockford Base32 encoded cs_blinded_c0: string; // Crockford Base32 encoded cs_blinded_c1: string; // Crockford Base32 encoded } @@ -799,11 +859,11 @@ interface CSCoinEnvelope { // a 256-bit nonce, converted to Crockford Base32. type DenominationBlindingKeyP = string; -const codecForURL = codecForString -const codecForLibtoolVersion = codecForString -const codecForCurrencyName = codecForString -const codecForEddsaSignature = codecForString -const codecForDecimalNumber = codecForNumber +const codecForURL = codecForString; +const codecForLibtoolVersion = codecForString; +const codecForCurrencyName = codecForString; +const codecForEddsaSignature = codecForString; +const codecForDecimalNumber = codecForNumber; enum TanChannel { SMS = "sms", @@ -811,9 +871,7 @@ enum TanChannel { } export namespace TalerWireGatewayApi { - export interface TransferResponse { - // Timestamp that indicates when the wire transfer will be executed. // In cases where the wire transfer gateway is unable to know when // the wire transfer will be executed, the time at which the request @@ -850,7 +908,6 @@ export namespace TalerWireGatewayApi { } export interface IncomingHistory { - // Array of incoming transactions. incoming_transactions: IncomingBankTransaction[]; @@ -859,7 +916,6 @@ export namespace TalerWireGatewayApi { // Credit account is shared by all incoming transactions // as per the nature of the request. credit_account: PaytoString; - } // Union discriminated by the "type" field. @@ -884,7 +940,6 @@ export namespace TalerWireGatewayApi { // The reserve public key extracted from the transaction details. reserve_pub: EddsaPublicKey; - } export interface IncomingWadTransaction { @@ -913,9 +968,7 @@ export namespace TalerWireGatewayApi { wad_id: WadId; } - export interface OutgoingHistory { - // Array of outgoing transactions. outgoing_transactions: OutgoingBankTransaction[]; @@ -924,11 +977,9 @@ export namespace TalerWireGatewayApi { // Credit account is shared by all incoming transactions // as per the nature of the request. debit_account: PaytoString; - } export interface OutgoingBankTransaction { - // Opaque identifier of the returned record. row_id: SafeUint64; @@ -964,7 +1015,6 @@ export namespace TalerWireGatewayApi { } export interface AddIncomingResponse { - // Timestamp that indicates when the wire transfer will be executed. // In cases where the wire transfer gateway is unable to know when // the wire transfer will be executed, the time at which the request @@ -977,14 +1027,10 @@ export namespace TalerWireGatewayApi { // Opaque ID of the transaction that the bank has made. row_id: SafeUint64; } - - - } export namespace TalerRevenueApi { export interface MerchantIncomingHistory { - // Array of incoming transactions. incoming_transactions: MerchantIncomingBankTransaction[]; @@ -993,11 +1039,9 @@ export namespace TalerRevenueApi { // Credit account is shared by all incoming transactions // as per the nature of the request. credit_account: PaytoString; - } export interface MerchantIncomingBankTransaction { - // Opaque identifier of the returned record. row_id: SafeUint64; @@ -1019,7 +1063,6 @@ export namespace TalerRevenueApi { } export namespace TalerBankConversionApi { - export interface ConversionInfo { // Exchange rate to buy regional currency from fiat // cashin_ratio: DecimalNumber; @@ -1097,7 +1140,6 @@ export namespace TalerBankConversionApi { // bank account, according to 'amount_debit'. amount_credit: AmountString; } - } export namespace TalerBankIntegrationApi { export interface BankVersion { @@ -1110,7 +1152,7 @@ export namespace TalerBankIntegrationApi { currency: string; // How the bank SPA should render this currency. - currency_specification: CurrencySpecification; + currency_specification?: CurrencySpecification; // Name of the API. name: "taler-bank-integration"; @@ -1150,7 +1192,6 @@ export namespace TalerBankIntegrationApi { } export interface BankWithdrawalOperationPostRequest { - // Reserve public key. reserve_pub: string; @@ -1159,7 +1200,6 @@ export namespace TalerBankIntegrationApi { } export interface BankWithdrawalOperationPostResponse { - // The transfer has been confirmed and registered by the bank. // Does not guarantee that the funds have arrived at the exchange already. transfer_done: boolean; @@ -1171,23 +1211,18 @@ export namespace TalerBankIntegrationApi { // It may contain withdrawal operation id confirm_transfer_url?: string; } - - } export namespace TalerCorebankApi { - export interface IntegrationConfig { // libtool-style representation of the Bank protocol version, see // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning // The format is "current:revision:age". version: string; - // How the bank SPA should render this currency. - currency: CurrencySpecification; + currency: String; // Name of the API. name: "taler-bank-integration"; - } export interface Config { // Name of this API, always "taler-corebank". @@ -1323,7 +1358,6 @@ export namespace TalerCorebankApi { internal_payto_uri?: PaytoString; } export interface ChallengeContactData { - // E-Mail address email?: EmailAddress; @@ -1332,7 +1366,6 @@ export namespace TalerCorebankApi { } export interface AccountReconfiguration { - // Addresses where to send the TAN for transactions. // Currently only used for cashouts. // If missing, cashouts will fail. @@ -1357,11 +1390,9 @@ export namespace TalerCorebankApi { // If present, change the max debit allowed for this user // Only admin can change this property. - debit_threshold?: AmountString - + debit_threshold?: AmountString; } - export interface AccountPasswordChange { // New password. new_password: string; @@ -1428,9 +1459,7 @@ export namespace TalerCorebankApi { cashout_payto_uri?: PaytoString; } - 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. @@ -1521,7 +1550,6 @@ export namespace TalerCorebankApi { } export interface ConversionRatesResponse { - // Exchange rate to buy the local currency from the external one buy_at_ratio: DecimalNumber; @@ -1536,12 +1564,14 @@ export namespace TalerCorebankApi { } export enum MonitorTimeframeParam { - hour, day, month, year, decade, + hour, + day, + month, + year, + decade, } - export type MonitorResponse = - | MonitorNoConversion - | MonitorWithConversion; + export type MonitorResponse = MonitorNoConversion | MonitorWithConversion; // Monitoring stats when conversion is not supported export interface MonitorNoConversion { @@ -1607,12 +1637,9 @@ export namespace TalerCorebankApi { // exchange to another bank account. talerOutVolume: AmountString; } - - } export namespace TalerExchangeApi { - export enum AmlState { normal = 0, pending = 1, @@ -1620,12 +1647,10 @@ export namespace TalerExchangeApi { } export interface AmlRecords { - // Array of AML records matching the query. records: AmlRecord[]; } export interface AmlRecord { - // Which payto-address is this record about. // Identifies a GNU Taler wallet or an affected bank account. h_payto: PaytoHash; @@ -1638,11 +1663,9 @@ export namespace TalerExchangeApi { // RowID of the record. rowid: Integer; - } export interface AmlDecisionDetails { - // Array of AML decisions made for this account. Possibly // contains only the most recent decision if "history" was // not set to 'true'. @@ -1652,7 +1675,6 @@ export namespace TalerExchangeApi { kyc_attributes: KycDetail[]; } export interface AmlDecisionDetail { - // What was the justification given? justification: string; @@ -1667,10 +1689,8 @@ export namespace TalerExchangeApi { // Who made the decision? decider_pub: AmlOfficerPublicKeyP; - } export interface KycDetail { - // Name of the configuration section that specifies the provider // which was used to collect the KYC details provider_section: string; @@ -1685,12 +1705,9 @@ export namespace TalerExchangeApi { // Time when the validity of the KYC data will expire expiration_time: Timestamp; - } - export interface AmlDecision { - // Human-readable justification for the decision. justification: string; @@ -1719,7 +1736,6 @@ export namespace TalerExchangeApi { kyc_requirements?: string[]; } - export interface ExchangeVersionResponse { // libtool-style representation of the Exchange protocol version, see // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning @@ -1737,23 +1753,20 @@ export namespace TalerExchangeApi { // Names of supported KYC requirements. supported_kyc_requirements: string[]; - } export type AccountRestriction = | RegexAccountRestriction - | DenyAllAccountRestriction + | DenyAllAccountRestriction; // Account restriction that disables this type of // account for the indicated operation categorically. export interface DenyAllAccountRestriction { - type: "deny"; } // Accounts interacting with this type of account // restriction must have a payto://-URI matching // the given regex. export interface RegexAccountRestriction { - type: "regex"; // Regular expression that the payto://-URI of the @@ -1773,7 +1786,6 @@ export namespace TalerExchangeApi { // Map from IETF BCP 47 language tags to localized // human hints. human_hint_i18n?: { [lang_tag: string]: string }; - } export interface WireAccount { @@ -1800,7 +1812,6 @@ export namespace TalerExchangeApi { // with purpose TALER_SIGNATURE_MASTER_WIRE_DETAILS. master_sig: EddsaSignature; } - } export namespace TalerMerchantApi { @@ -1827,7 +1838,6 @@ export namespace TalerMerchantApi { // All currencies in this map are supported by // the backend. currencies: { [currency: string]: CurrencySpecification }; - } export interface ClaimRequest { @@ -1856,7 +1866,6 @@ export namespace TalerMerchantApi { // Text to be shown to the point-of-sale staff as a proof of // payment. pos_confirmation?: string; - } interface PayRequest { @@ -1869,7 +1878,6 @@ export namespace TalerMerchantApi { // The session for which the payment is made (or replayed). // Only set for session-based payments. session_id?: string; - } export interface CoinPaySig { // Signature by the coin. @@ -1891,7 +1899,6 @@ export namespace TalerMerchantApi { exchange_url: string; } - interface StatusPaid { // Was the payment refunded (even partially, via refund or abort)? refunded: boolean; @@ -1926,7 +1933,6 @@ export namespace TalerMerchantApi { } interface PaidRefundStatusResponse { - // Text to be shown to the point-of-sale staff as a proof of // payment (present only if re-usable OTP algorithm is used). pos_confirmation?: string; @@ -1953,7 +1959,6 @@ export namespace TalerMerchantApi { } interface AbortRequest { - // Hash of the order's contract terms (this is used to authenticate the // wallet/customer in case $ORDER_ID is guessable). h_contract: HashCode; @@ -1974,7 +1979,6 @@ export namespace TalerMerchantApi { exchange_url: string; } interface AbortResponse { - // List of refund responses about the coins that the wallet // requested an abort for. In the same order as the coins // from the original request. @@ -2034,7 +2038,6 @@ export namespace TalerMerchantApi { // Public key of the merchant. merchant_pub: EddsaPublicKey; - } type MerchantCoinRefundStatus = | MerchantCoinRefundSuccessStatus @@ -2104,7 +2107,6 @@ export namespace TalerMerchantApi { } interface RewardInformation { - // Exchange from which the reward will be withdrawn. Needed by the // wallet to determine denominations, fees, etc. exchange_url: string; @@ -2121,7 +2123,6 @@ export namespace TalerMerchantApi { } interface RewardPickupRequest { - // List of planchets the wallet wants to use for the reward. planchets: PlanchetDetail[]; } @@ -2134,19 +2135,16 @@ export namespace TalerMerchantApi { coin_ev: CoinEnvelope; } interface RewardResponse { - // Blind RSA signatures over the planchets. // The order of the signatures matches the planchets list. blind_sigs: BlindSignature[]; } interface BlindSignature { - // The (blind) RSA signature. Still needs to be unblinded. blind_sig: BlindedRsaSignature; } interface InstanceConfigurationMessage { - // Name of the merchant instance to create (will become $INSTANCE). // Must match the regex ^[A-Za-z0-9][A-Za-z0-9_.@-]+$. id: string; @@ -2192,7 +2190,6 @@ export namespace TalerMerchantApi { // If the frontend does NOT specify a payment deadline, how long should // offers we make be valid by default? default_pay_delay: RelativeTime; - } interface InstanceAuthConfigurationMessage { @@ -2210,7 +2207,6 @@ export namespace TalerMerchantApi { // the value must be provided in a "Authorization: Bearer $token" // header. token?: string; - } interface LoginTokenRequest { @@ -2244,7 +2240,6 @@ export namespace TalerMerchantApi { } interface InstanceReconfigurationMessage { - // Merchant name corresponding to this instance. name: string; @@ -2283,7 +2278,6 @@ export namespace TalerMerchantApi { // If the frontend does NOT specify a payment deadline, how long should // offers we make be valid by default? default_pay_delay: RelativeTime; - } interface InstancesResponse { @@ -2320,7 +2314,6 @@ export namespace TalerMerchantApi { } interface QueryInstancesResponse { - // Merchant name corresponding to this instance. name: string; @@ -2366,11 +2359,9 @@ export namespace TalerMerchantApi { auth: { type: "external" | "token"; }; - } interface AccountKycRedirects { - // Array of pending KYCs. pending_kycs: MerchantAccountKycRedirect[]; @@ -2379,7 +2370,6 @@ export namespace TalerMerchantApi { } interface MerchantAccountKycRedirect { - // URL that the user should open in a browser to // proceed with the KYC process (as returned // by the exchange's /kyc-check/ endpoint). @@ -2395,11 +2385,9 @@ export namespace TalerMerchantApi { // Our bank wire account this is about. payto_uri: PaytoString; - } interface ExchangeKycTimeout { - // Base URL of the exchange this is about. exchange_url: string; @@ -2411,11 +2399,9 @@ export namespace TalerMerchantApi { // information about the KYC status. // 0 if there was no response at all. exchange_http_status: number; - } interface AccountAddDetails { - // payto:// URI of the account. payto_uri: PaytoString; @@ -2429,15 +2415,12 @@ export namespace TalerMerchantApi { // or PATCH requests to update (or delete) credentials. // To really delete credentials, set them to the type: "none". credit_facade_credentials?: FacadeCredentials; - } - type FacadeCredentials = - | NoFacadeCredentials - | BasicAuthFacadeCredentials; + type FacadeCredentials = NoFacadeCredentials | BasicAuthFacadeCredentials; interface NoFacadeCredentials { type: "none"; - }; + } interface BasicAuthFacadeCredentials { type: "basic"; @@ -2446,19 +2429,16 @@ export namespace TalerMerchantApi { // Password to use to authenticate password: string; - }; + } interface AccountAddResponse { - // Hash over the wire details (including over the salt). h_wire: HashCode; // Salt used to compute h_wire. salt: HashCode; - } interface AccountPatchDetails { - // URL from where the merchant can download information // about incoming wire transfers to this account. credit_facade_url?: string; @@ -2474,12 +2454,10 @@ export namespace TalerMerchantApi { } interface AccountsSummaryResponse { - // List of accounts that are known for the instance. accounts: BankAccountEntry[]; } interface BankAccountEntry { - // payto:// URI of the account. payto_uri: PaytoString; @@ -2499,7 +2477,6 @@ export namespace TalerMerchantApi { } interface ProductAddDetail { - // Product ID to use. product_id: string; @@ -2539,11 +2516,9 @@ export namespace TalerMerchantApi { // Minimum age buyer must have (in years). Default is 0. minimum_age?: Integer; - } interface ProductPatchDetail { - // Human-readable product description. description: string; @@ -2583,7 +2558,6 @@ export namespace TalerMerchantApi { // Minimum age buyer must have (in years). Default is 0. minimum_age?: Integer; - } interface InventorySummaryResponse { @@ -2591,15 +2565,12 @@ export namespace TalerMerchantApi { products: InventoryEntry[]; } - interface InventoryEntry { // Product identifier, as found in the product. product_id: string; - } interface ProductDetail { - // Human-readable product description. description: string; @@ -2642,10 +2613,8 @@ export namespace TalerMerchantApi { // Minimum age buyer must have (in years). minimum_age?: Integer; - } interface LockRequest { - // UUID that identifies the frontend performing the lock // Must be unique for the lifetime of the lock. lock_uuid: string; @@ -2655,7 +2624,6 @@ export namespace TalerMerchantApi { // How many units should be locked? quantity: Integer; - } interface PostOrderRequest { @@ -2735,7 +2703,6 @@ export namespace TalerMerchantApi { token?: ClaimToken; } interface OutOfStockResponse { - // Product ID of an out-of-stock item. product_id: string; @@ -2756,7 +2723,6 @@ export namespace TalerMerchantApi { orders: OrderHistoryEntry[]; } interface OrderHistoryEntry { - // Order ID of the transaction related to this entry. order_id: string; @@ -2782,9 +2748,10 @@ export namespace TalerMerchantApi { paid: boolean; } - type MerchantOrderStatusResponse = CheckPaymentPaidResponse | - CheckPaymentClaimedResponse | - CheckPaymentUnpaidResponse; + type MerchantOrderStatusResponse = + | CheckPaymentPaidResponse + | CheckPaymentClaimedResponse + | CheckPaymentUnpaidResponse; interface CheckPaymentPaidResponse { // The customer paid for this contract. order_status: "paid"; @@ -2842,7 +2809,6 @@ export namespace TalerMerchantApi { // Contract terms. contract_terms: ContractTerms; - } interface CheckPaymentUnpaidResponse { // The order was neither claimed nor paid. @@ -2924,7 +2890,6 @@ export namespace TalerMerchantApi { } interface ForgetRequest { - // Array of valid JSON paths to forgettable fields in the order's // contract terms. fields: string[]; @@ -2938,7 +2903,6 @@ export namespace TalerMerchantApi { reason: string; } interface MerchantRefundResponse { - // URL (handled by the backend) that the wallet should access to // trigger refund processing. // taler://refund/... @@ -3085,7 +3049,6 @@ export namespace TalerMerchantApi { exchange_url: string; } interface RewardStatusEntry { - // Unique identifier for the reward. reward_id: HashCode; @@ -3154,12 +3117,10 @@ export namespace TalerMerchantApi { } interface RewardsResponse { - // List of rewards that are present in the backend. rewards: Reward[]; } interface Reward { - // ID of the reward in the backend database. row_id: number; @@ -3171,7 +3132,6 @@ export namespace TalerMerchantApi { } interface OtpDeviceAddDetails { - // Device ID to use. otp_device_id: string; @@ -3189,7 +3149,6 @@ export namespace TalerMerchantApi { } interface OtpDevicePatchDetails { - // Human-readable description for the device. otp_device_description: string; @@ -3204,12 +3163,10 @@ export namespace TalerMerchantApi { } interface OtpDeviceSummaryResponse { - // Array of devices that are present in our backend. otp_devices: OtpDeviceEntry[]; } interface OtpDeviceEntry { - // Device identifier. otp_device_id: string; @@ -3218,7 +3175,6 @@ export namespace TalerMerchantApi { } interface OtpDeviceDetails { - // Human-readable description for the device. device_description: string; @@ -3227,10 +3183,8 @@ export namespace TalerMerchantApi { // Counter for counter-based OTP devices. otp_ctr?: Integer; - } interface TemplateAddDetails { - // Template ID to use. template_id: string; @@ -3245,7 +3199,6 @@ export namespace TalerMerchantApi { template_contract: TemplateContractDetails; } interface TemplateContractDetails { - // Human-readable summary for the template. summary?: string; @@ -3266,10 +3219,8 @@ export namespace TalerMerchantApi { // The time the customer need to pay before his order will be deleted. // It is deleted if the customer did not pay and if the duration is over. pay_duration: RelativeTime; - } interface TemplatePatchDetails { - // Human-readable description for the template. template_description: string; @@ -3279,27 +3230,21 @@ export namespace TalerMerchantApi { // Additional information in a separate template. template_contract: TemplateContractDetails; - } interface TemplateSummaryResponse { - // List of templates that are present in our backend. templates_list: TemplateEntry[]; } - interface TemplateEntry { - // Template identifier, as found in the template. template_id: string; // Human-readable description for the template. template_description: string; - } interface TemplateDetails { - // Human-readable description for the template. template_description: string; @@ -3311,7 +3256,6 @@ export namespace TalerMerchantApi { template_contract: TemplateContractDetails; } interface UsingTemplateDetails { - // Summary of the template summary?: string; @@ -3320,7 +3264,6 @@ export namespace TalerMerchantApi { } interface WebhookAddDetails { - // Webhook ID to use. webhook_id: string; @@ -3338,11 +3281,9 @@ export namespace TalerMerchantApi { // Body template by the webhook body_template?: string; - } interface WebhookPatchDetails { - // The event of the webhook: why the webhook is used. event_type: string; @@ -3357,29 +3298,22 @@ export namespace TalerMerchantApi { // Body template by the webhook body_template?: string; - } interface WebhookSummaryResponse { - // Return webhooks that are present in our backend. webhooks: WebhookEntry[]; - } - interface WebhookEntry { - // Webhook identifier, as found in the webhook. webhook_id: string; // The event of the webhook: why the webhook is used. event_type: string; - } interface WebhookDetails { - // The event of the webhook: why the webhook is used. event_type: string; @@ -3394,7 +3328,6 @@ export namespace TalerMerchantApi { // Body template by the webhook body_template?: string; - } interface ContractTerms { @@ -3639,5 +3572,4 @@ export namespace TalerMerchantApi { // Master public key of the exchange. master_pub: EddsaPublicKey; } - -}
\ No newline at end of file +} diff --git a/packages/taler-util/src/taler-types.ts b/packages/taler-util/src/taler-types.ts index f21efc516..e32c5a99d 100644 --- a/packages/taler-util/src/taler-types.ts +++ b/packages/taler-util/src/taler-types.ts @@ -41,6 +41,10 @@ import { codecOptional, } from "./codec.js"; import { strcmp } from "./helpers.js"; +import { + CurrencySpecification, + codecForCurrencySpecificiation, +} from "./index.js"; import { AgeCommitmentProof, Edx25519PublicKeyEnc } from "./taler-crypto.js"; import { codecForAbsoluteTime, @@ -1756,7 +1760,6 @@ export interface MerchantAbortPayRefundSuccessStatus { exchange_pub: string; } - export interface FutureKeysResponse { future_denoms: any[]; @@ -2375,3 +2378,39 @@ export const codecForExchangeWireAccount = (): Codec<ExchangeWireAccount> => .property("master_sig", codecForString()) .property("payto_uri", codecForString()) .build("WireAccount"); + +export type Integer = number; + +export interface BankConversionInfoConfig { + // libtool-style representation of the Bank protocol version, see + // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning + // The format is "current:revision:age". + version: string; + + // Name of the API. + name: "taler-conversion-info"; + + regional_currency: string; + + fiat_currency: string; + + // Currency used by this bank. + regional_currency_specification: CurrencySpecification; + + // External currency used during conversion. + fiat_currency_specification: CurrencySpecification; +} + +export const codecForBankConversionInfoConfig = + (): Codec<BankConversionInfoConfig> => + buildCodecForObject<BankConversionInfoConfig>() + .property("name", codecForConstString("taler-conversion-info")) + .property("version", codecForString()) + .property("fiat_currency", codecForString()) + .property("regional_currency", codecForString()) + .property("fiat_currency_specification", codecForCurrencySpecificiation()) + .property( + "regional_currency_specification", + codecForCurrencySpecificiation(), + ) + .build("BankConversionInfoConfig"); diff --git a/packages/taler-util/src/transactions-types.ts b/packages/taler-util/src/transactions-types.ts index 3a7f4d541..f2bf49b00 100644 --- a/packages/taler-util/src/transactions-types.ts +++ b/packages/taler-util/src/transactions-types.ts @@ -51,6 +51,7 @@ import { TalerErrorDetail, TransactionIdStr, TransactionStateFilter, + WithdrawalAccountInfo, } from "./wallet-types.js"; export interface TransactionsRequest { @@ -237,9 +238,13 @@ interface WithdrawalDetailsForManualTransfer { * Payto URIs that the exchange supports. * * Already contains the amount and message. + * + * @deprecated in favor of exchangeCreditAccounts */ exchangePaytoUris: string[]; + exchangeCreditAccounts?: WithdrawalAccountInfo[]; + // Public key of the reserve reservePub: string; diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts index 148117673..346528029 100644 --- a/packages/taler-util/src/wallet-types.ts +++ b/packages/taler-util/src/wallet-types.ts @@ -52,6 +52,7 @@ import { PaytoUri } from "./payto.js"; import { AgeCommitmentProof } from "./taler-crypto.js"; import { TalerErrorCode } from "./taler-error-codes.js"; import { + AccountRestriction, AmountString, AuditorDenomSig, CoinEnvelope, @@ -78,8 +79,6 @@ import { } from "./time.js"; import { OrderShortInfo, - TransactionMajorState, - TransactionMinorState, TransactionState, TransactionType, } from "./transactions-types.js"; @@ -1387,6 +1386,8 @@ export interface AcceptManualWithdrawalResult { */ reservePub: string; + withdrawalAccountsList: WithdrawalAccountInfo[]; + transactionId: TransactionIdStr; } @@ -1416,10 +1417,17 @@ export interface ManualWithdrawalDetails { /** * Ways to pay the exchange. + * + * @deprecated in favor of withdrawalAccountList */ paytoUris: string[]; /** + * Ways to pay the exchange, including + */ + withdrawalAccountList: WithdrawalAccountInfo[]; + + /** * If the exchange supports age-restricted coins it will return * the array of ages. */ @@ -1438,6 +1446,11 @@ export interface DenomSelectionState { }[]; } +export interface WireAccountDetails { + paytoUri: string; + creditRestrictions?: AccountRestriction[]; +} + /** * Information about what will happen doing a withdrawal. * @@ -1451,6 +1464,8 @@ export interface ExchangeWithdrawalDetails { */ exchangeWireAccounts: string[]; + withdrawalAccountList: WithdrawalAccountInfo[]; + /** * Selected denominations for withdraw. */ @@ -2736,3 +2751,8 @@ export interface TestingWaitTransactionRequest { transactionId: string; txState: TransactionState; } + +export interface WithdrawalAccountInfo { + paytoUri: string; + transferAmount: AmountString; +} diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts index 56392f090..36ca128ae 100644 --- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts +++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts @@ -164,7 +164,7 @@ export interface TalerCryptoInterface { req: ContractTermsValidationRequest, ): Promise<ValidationResult>; - createEddsaKeypair(req: unknown): Promise<EddsaKeypair>; + createEddsaKeypair(req: {}): Promise<EddsaKeypair>; eddsaGetPublic(req: EddsaGetPublicRequest): Promise<EddsaGetPublicResponse>; diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 3e8452bcd..0cafae2a1 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -52,10 +52,10 @@ import { TalerPreciseTimestamp, TalerProtocolDuration, TalerProtocolTimestamp, - //TalerProtocolTimestamp, TransactionIdStr, UnblindedSignature, WireInfo, + WithdrawalAccountInfo, codecForAny, } from "@gnu-taler/taler-util"; import { DbRetryInfo, TaskIdentifiers } from "./operations/common.js"; @@ -1373,6 +1373,11 @@ export interface WgInfoBankIntegrated { export interface WgInfoBankManual { withdrawalType: WithdrawalRecordType.BankManual; + + /** + * Info about withdrawal accounts, possibly including currency conversion. + */ + exchangeCreditAccounts?: WithdrawalAccountInfo[]; } export interface WgInfoBankPeerPull { diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 7ee6275b0..b294c163e 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -720,6 +720,7 @@ function buildTransactionForManualWithdraw( type: WithdrawalType.ManualTransfer, reservePub: withdrawalGroup.reservePub, exchangePaytoUris, + exchangeCreditAccounts: withdrawalGroup.wgInfo.exchangeCreditAccounts, reserveIsReady: withdrawalGroup.status === WithdrawalGroupStatus.Done || withdrawalGroup.status === WithdrawalGroupStatus.PendingReady, diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 275d0aaf0..a5a6bded8 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -24,6 +24,7 @@ import { AgeRestriction, AmountJson, AmountLike, + AmountString, Amounts, BankWithdrawDetails, CancellationToken, @@ -42,6 +43,7 @@ import { LibtoolVersion, Logger, NotificationType, + PaytoUri, TalerError, TalerErrorCode, TalerErrorDetail, @@ -54,21 +56,25 @@ import { TransactionType, URL, UnblindedSignature, + WireAccountDetails, WithdrawUriInfoResponse, + WithdrawalAccountInfo, addPaytoQueryParams, canonicalizeBaseUrl, codecForBankWithdrawalOperationPostResponse, + codecForCashinConversionResponse, codecForExchangeWithdrawBatchResponse, codecForIntegrationBankConfig, codecForReserveStatus, codecForWalletKycUuid, codecForWithdrawOperationStatusResponse, + createEddsaKeyPair, encodeCrock, getErrorDetailFromException, getRandomBytes, j2s, makeErrorDetail, - parseWithdrawUri + parseWithdrawUri, } from "@gnu-taler/taler-util"; import { HttpRequestLibrary, @@ -95,9 +101,8 @@ import { import { ExchangeDetailsRecord, ExchangeEntryDbRecordStatus, - PendingTaskType, isWithdrawableDenom, - timestampPreciseToDb + timestampPreciseToDb, } from "../index.js"; import { InternalWalletState } from "../internal-wallet-state.js"; import { @@ -110,6 +115,7 @@ import { makeExchangeListItem, runLongpollAsync, } from "../operations/common.js"; +import { PendingTaskType } from "../pending-types.js"; import { assertUnreachable } from "../util/assertUnreachable.js"; import { selectForcedWithdrawalDenominations, @@ -1751,8 +1757,23 @@ export async function getExchangeWithdrawalInfo( logger.trace("updating exchange"); const { exchange, exchangeDetails } = await ws.exchangeOps.updateExchangeFromUrl(ws, exchangeBaseUrl); + + if (exchangeDetails.currency != instructedAmount.currency) { + // Specifiying the amount in the conversion input currency is not yet supported. + // We might add support for it later. + throw new Error( + `withdrawal only supported when specifying target currency ${exchangeDetails.currency}`, + ); + } + + const withdrawalAccountList = await fetchWithdrawalAccountInfo(ws, { + exchangeDetails, + instructedAmount, + }); + logger.trace("updating withdrawal denoms"); await updateWithdrawalDenoms(ws, exchangeBaseUrl); + logger.trace("getting candidate denoms"); const denoms = await getCandidateWithdrawalDenoms(ws, exchangeBaseUrl); logger.trace("selecting withdrawal denoms"); @@ -1773,8 +1794,15 @@ export async function getExchangeWithdrawalInfo( } const exchangeWireAccounts: string[] = []; + for (const account of exchangeDetails.wireInfo.accounts) { - exchangeWireAccounts.push(account.payto_uri); + const details: WireAccountDetails = { + paytoUri: account.payto_uri, + }; + if (account.credit_restrictions) { + details.creditRestrictions = account.credit_restrictions; + } + exchangeWireAccounts.push(details.paytoUri); } let hasDenomWithAgeRestriction = false; @@ -1854,6 +1882,7 @@ export async function getExchangeWithdrawalInfo( earliestDepositExpiration, exchangePaytoUris: paytoUris, exchangeWireAccounts, + withdrawalAccountList, exchangeVersion: exchangeDetails.protocolVersionRange || "unknown", numOfferedDenoms: possibleDenoms.length, selectedDenoms, @@ -1946,15 +1975,6 @@ export async function getWithdrawalDetailsForUri( }; } -export async function getFundingPaytoUrisTx( - ws: InternalWalletState, - withdrawalGroupId: string, -): Promise<string[]> { - return await ws.db - .mktx((x) => [x.exchanges, x.exchangeDetails, x.withdrawalGroups]) - .runReadWrite((tx) => getFundingPaytoUris(tx, withdrawalGroupId)); -} - export function augmentPaytoUrisForWithdrawal( plainPaytoUris: string[], reservePub: string, @@ -2361,10 +2381,6 @@ export async function internalPrepareCreateWithdrawalGroup( const exchangeInfo = await updateExchangeFromUrl(ws, canonExchange); const exchangeDetails = exchangeInfo.exchangeDetails; - if (!exchangeDetails) { - logger.trace(exchangeDetails); - throw Error("exchange not updated"); - } const transactionId = constructTransactionIdentifier({ tag: TransactionType.Withdrawal, withdrawalGroupId: withdrawalGroup.withdrawalGroupId, @@ -2566,6 +2582,60 @@ export async function acceptWithdrawalFromUri( } /** + * Gather information about bank accounts that can be used for + * withdrawals. This includes accounts that are in a different + * currency and require conversion. + */ +async function fetchWithdrawalAccountInfo( + ws: InternalWalletState, + req: { + exchangeDetails: ExchangeDetailsRecord; + instructedAmount: AmountJson; + reservePub?: string; + }, +): Promise<WithdrawalAccountInfo[]> { + const { exchangeDetails, instructedAmount } = req; + const withdrawalAccounts: WithdrawalAccountInfo[] = []; + for (let acct of exchangeDetails.wireInfo.accounts) { + let paytoUri: string; + let transferAmount: AmountString; + if (acct.conversion_url != null) { + const reqUrl = new URL("cashin-rate", acct.conversion_url); + reqUrl.searchParams.set( + "amount_credit", + Amounts.stringify(instructedAmount), + ); + const httpResp = await ws.http.fetch(reqUrl.href); + const resp = await readSuccessResponseJsonOrThrow( + httpResp, + codecForCashinConversionResponse(), + ); + paytoUri = acct.payto_uri; + transferAmount = resp.amount_debit; + if (req.reservePub) { + } + } else { + paytoUri = acct.payto_uri; + transferAmount = Amounts.stringify(instructedAmount); + } + paytoUri = addPaytoQueryParams(paytoUri, { + amount: Amounts.stringify(transferAmount), + }); + if (req.reservePub != null) { + paytoUri = addPaytoQueryParams(paytoUri, { + message: `Taler Withdrawal ${req.reservePub}`, + }); + } + const acctInfo: WithdrawalAccountInfo = { + paytoUri, + transferAmount, + }; + withdrawalAccounts.push(acctInfo); + } + return withdrawalAccounts; +} + +/** * Create a manual withdrawal operation. * * Adds the corresponding exchange as a trusted exchange if it is neither @@ -2582,15 +2652,39 @@ export async function createManualWithdrawal( forcedDenomSel?: ForcedDenomSel; }, ): Promise<AcceptManualWithdrawalResult> { + const { exchangeBaseUrl } = req; + const amount = Amounts.parseOrThrow(req.amount); + const { exchangeDetails } = await ws.exchangeOps.updateExchangeFromUrl( + ws, + exchangeBaseUrl, + ); + + if (exchangeDetails.currency != amount.currency) { + throw Error( + "manual withdrawal with conversion from foreign currency is not yet supported", + ); + } + const reserveKeyPair: EddsaKeypair = await ws.cryptoApi.createEddsaKeypair( + {}, + ); + + const withdrawalAccountList = await fetchWithdrawalAccountInfo(ws, { + exchangeDetails, + instructedAmount: amount, + reservePub: reserveKeyPair.pub, + }); + const withdrawalGroup = await internalCreateWithdrawalGroup(ws, { amount: Amounts.jsonifyAmount(req.amount), wgInfo: { withdrawalType: WithdrawalRecordType.BankManual, + exchangeCreditAccounts: withdrawalAccountList, }, exchangeBaseUrl: req.exchangeBaseUrl, forcedDenomSel: req.forcedDenomSel, restrictAge: req.restrictAge, reserveStatus: WithdrawalGroupStatus.PendingQueryingStatus, + reserveKeyPair, }); const withdrawalGroupId = withdrawalGroup.withdrawalGroupId; @@ -2610,6 +2704,7 @@ export async function createManualWithdrawal( return { reservePub: withdrawalGroup.reservePub, exchangePaytoUris: exchangePaytoUris, + withdrawalAccountsList: withdrawalAccountList, transactionId, }; } diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 0694aef8a..4472bdbad 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -1215,6 +1215,7 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>( paytoUris: wi.exchangePaytoUris, tosAccepted: wi.termsOfServiceAccepted, ageRestrictionOptions: wi.ageRestrictionOptions, + withdrawalAccountList: wi.withdrawalAccountList, numCoins, }; return resp; |