taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit bd5e3a2bb6b8334d422d3eada2242f11595ea8a8
parent 6a1ebc106552be354906630c916a4639d05435a8
Author: Florian Dold <dold@taler.net>
Date:   Thu, 23 Jul 2026 03:22:44 +0200

wallet: use typed clients in dbless and testing helpers

Diffstat:
Mpackages/taler-wallet-core/src/dbless.ts | 80+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mpackages/taler-wallet-core/src/testing.ts | 103++++++++++++++++++++++++++++++++-----------------------------------------------
2 files changed, 83 insertions(+), 100 deletions(-)

diff --git a/packages/taler-wallet-core/src/dbless.ts b/packages/taler-wallet-core/src/dbless.ts @@ -42,13 +42,14 @@ import { Logger, Paytos, Result, + TalerBankIntegrationHttpClient, TalerCorebankApiClient, TalerExchangeHttpClient, TalerProtocolTimestamp, + TalerUriAction, + TalerUris, + TalerWireGatewayHttpClient, UnblindedDenominationSignature, - codecForAny, - codecForBankWithdrawalOperationPostResponse, - codecForBatchDepositSuccess, encodeCrock, getRandomBytes, hashDenomPub, @@ -56,13 +57,10 @@ import { j2s, succeedOrThrow, } from "@gnu-taler/taler-util"; -import { - HttpRequestLibrary, - readSuccessResponseJsonOrThrow, -} from "@gnu-taler/taler-util/http"; +import { HttpRequestLibrary } from "@gnu-taler/taler-util/http"; import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js"; import { ExchangeInfo, downloadExchangeInfo } from "./exchanges.js"; -import { getBankStatusUrl, getBankWithdrawalInfo } from "./withdraw.js"; +import { getBankWithdrawalInfo } from "./withdraw.js"; export { downloadExchangeInfo }; @@ -108,7 +106,6 @@ export async function topupReserveWithBank(args: TopupReserveWithBankArgs) { amount, ); const bankInfo = await getBankWithdrawalInfo(http, wopi.taler_withdraw_uri); - const bankStatusUrl = getBankStatusUrl(wopi.taler_withdraw_uri); if (!bankInfo.exchange) { throw Error("no suggested exchange"); } @@ -117,16 +114,24 @@ export async function topupReserveWithBank(args: TopupReserveWithBankArgs) { if (plainPaytoUris.length <= 0) { throw new Error(); } - const httpResp = await http.fetch(bankStatusUrl.href, { - method: "POST", - body: { - reserve_pub: reservePub, - selected_exchange: plainPaytoUris[0], - }, - }); - await readSuccessResponseJsonOrThrow( - httpResp, - codecForBankWithdrawalOperationPostResponse(), + const uriResult = Result.orUndefined( + TalerUris.parseRestricted(wopi.taler_withdraw_uri, TalerUriAction.Withdraw), + ); + if (!uriResult) { + throw Error(`can't parse withdrawal URI ${wopi.taler_withdraw_uri}`); + } + const bankIntegrationClient = new TalerBankIntegrationHttpClient( + uriResult.bankIntegrationApiBaseUrl, + { httpClient: http }, + ); + succeedOrThrow( + await bankIntegrationClient.completeWithdrawalOperationById( + uriResult.withdrawalOperationId, + { + reserve_pub: reservePub, + selected_exchange: plainPaytoUris[0], + }, + ), ); await bankClient.confirmWithdrawalOperation(bankUser.username, { withdrawalOperationId: wopi.withdrawal_id, @@ -359,12 +364,10 @@ export async function depositCoinBatch(args: { refund_deadline: refundDeadline, merchant_pub: merchantPub, }; - const url = new URL(`batch-deposit`, args.exchangeBaseUrl); - const httpResp = await http.fetch(url.href, { - method: "POST", - body: requestBody, + const exchangeClient = new TalerExchangeHttpClient(args.exchangeBaseUrl, { + httpClient: http, }); - await readSuccessResponseJsonOrThrow(httpResp, codecForBatchDepositSuccess()); + succeedOrThrow(await exchangeClient.batchDeposit({ body: requestBody })); } export async function refreshCoin(req: { @@ -448,21 +451,22 @@ export async function createTestingReserve(args: { } const components = pt.fullPath.split("/"); const creditorAcct = components[components.length - 1]; - const fbReq = await http.fetch( + const wireGatewayClient = new TalerWireGatewayHttpClient( new URL( - `accounts/${creditorAcct}/taler-wire-gateway/admin/add-incoming`, + `accounts/${creditorAcct}/taler-wire-gateway/`, corebankApiBaseUrl, ).href, - { - method: "POST", + { httpClient: http }, + ); + succeedOrThrow( + await wireGatewayClient.addIncoming({ body: { - amount, + amount: amount as AmountString, reserve_pub: reservePub, debit_account: "payto://x-taler-bank/localhost/testdebtor", }, - }, + }), ); - await readSuccessResponseJsonOrThrow(fbReq, codecForAny()); } /** @@ -475,14 +479,14 @@ export async function checkReserve( reservePub: string, longpollTimeoutMs: number = 500, ): Promise<void> { - const reqUrl = new URL(`reserves/${reservePub}`, exchangeBaseUrl); - if (longpollTimeoutMs) { - reqUrl.searchParams.set("timeout_ms", `${longpollTimeoutMs}`); - } - const resp = await http.fetch(reqUrl.href, { - method: "GET", + const exchangeClient = new TalerExchangeHttpClient(exchangeBaseUrl, { + httpClient: http, }); - if (resp.status !== 200) { + const resp = await exchangeClient.getReserveStatus( + reservePub, + longpollTimeoutMs > 0, + ); + if (resp.case !== "ok") { throw new Error("reserve not okay"); } } diff --git a/packages/taler-wallet-core/src/testing.ts b/packages/taler-wallet-core/src/testing.ts @@ -25,13 +25,12 @@ */ import { AbsoluteTime, + AccessToken, addPaytoQueryParams, Amounts, AmountString, checkLogicInvariant, CheckPaymentResponse, - codecForAny, - codecForCheckPaymentResponse, ConfirmPayResultType, Duration, IntegrationTestArgs, @@ -42,7 +41,9 @@ import { Paytos, Result, PreparePayResultType, + succeedOrThrow, TalerCorebankApiClient, + TalerMerchantInstanceHttpClient, TestingWaitBalanceRequest, TestingWaitTransactionRequest, TestPayArgs, @@ -56,10 +57,7 @@ import { URL, WithdrawTestBalanceRequest, } from "@gnu-taler/taler-util"; -import { - HttpRequestLibrary, - readSuccessResponseJsonOrThrow, -} from "@gnu-taler/taler-util/http"; +import { HttpRequestLibrary } from "@gnu-taler/taler-util/http"; import { getBalanceDetail, getBalances } from "./balance.js"; import { genericWaitForState } from "./common.js"; import { createDepositGroup } from "./deposits.js"; @@ -170,21 +168,6 @@ async function withdrawTestBalanceImpl( }; } -/** - * FIXME: User TalerMerchantInstanceHttpClient instead. - */ -function getMerchantAuthHeader(m: MerchantBackendInfo): Record<string, string> { - if (m.authToken) { - return { - Authorization: `Bearer ${m.authToken}`, - }; - } - return {}; -} - -/** - * FIXME: User TalerMerchantInstanceHttpClient instead. - */ async function refund( http: HttpRequestLibrary, merchantBackend: MerchantBackendInfo, @@ -192,21 +175,20 @@ async function refund( reason: string, refundAmount: string, ): Promise<string> { - const reqUrl = new URL( - `private/orders/${orderId}/refund`, + const merchantClient = new TalerMerchantInstanceHttpClient( merchantBackend.baseUrl, + http, + ); + const r = succeedOrThrow( + await merchantClient.addRefund( + merchantBackend.authToken as AccessToken, + orderId, + { + reason, + refund: refundAmount as AmountString, + }, + ), ); - const refundReq = { - order_id: orderId, - reason, - refund: refundAmount, - }; - const resp = await http.fetch(reqUrl.href, { - method: "POST", - body: refundReq, - headers: getMerchantAuthHeader(merchantBackend), - }); - const r = await readSuccessResponseJsonOrThrow(resp, codecForAny()); const refundUri = r.taler_refund_uri; if (!refundUri) { throw Error("no refund URI in response"); @@ -214,9 +196,6 @@ async function refund( return refundUri; } -/** - * FIXME: User TalerMerchantInstanceHttpClient instead. - */ async function createOrder( http: HttpRequestLibrary, merchantBackend: MerchantBackendInfo, @@ -225,22 +204,21 @@ async function createOrder( fulfillmentUrl: string, ): Promise<{ orderId: string }> { const t = Math.floor(new Date().getTime() / 1000) + 15 * 60; - const reqUrl = new URL("private/orders", merchantBackend.baseUrl).href; - const orderReq = { - order: { - amount, - summary, - fulfillment_url: fulfillmentUrl, - refund_deadline: { t_s: t }, - wire_transfer_deadline: { t_s: t }, - }, - }; - const resp = await http.fetch(reqUrl, { - method: "POST", - body: orderReq, - headers: getMerchantAuthHeader(merchantBackend), - }); - const r = await readSuccessResponseJsonOrThrow(resp, codecForAny()); + const merchantClient = new TalerMerchantInstanceHttpClient( + merchantBackend.baseUrl, + http, + ); + const r = succeedOrThrow( + await merchantClient.createOrder(merchantBackend.authToken as AccessToken, { + order: { + amount: amount as AmountString, + summary, + fulfillment_url: fulfillmentUrl, + refund_deadline: { t_s: t }, + wire_transfer_deadline: { t_s: t }, + }, + }), + ); const orderId = r.order_id; if (!orderId) { throw Error("no order id in response"); @@ -248,20 +226,21 @@ async function createOrder( return { orderId }; } -/** - * FIXME: User TalerMerchantInstanceHttpClient instead. - */ async function checkPayment( http: HttpRequestLibrary, merchantBackend: MerchantBackendInfo, orderId: string, ): Promise<CheckPaymentResponse> { - const reqUrl = new URL(`private/orders/${orderId}`, merchantBackend.baseUrl); - reqUrl.searchParams.set("order_id", orderId); - const resp = await http.fetch(reqUrl.href, { - headers: getMerchantAuthHeader(merchantBackend), - }); - return readSuccessResponseJsonOrThrow(resp, codecForCheckPaymentResponse()); + const merchantClient = new TalerMerchantInstanceHttpClient( + merchantBackend.baseUrl, + http, + ); + return succeedOrThrow( + await merchantClient.getOrderDetails( + merchantBackend.authToken as AccessToken, + orderId, + ), + ); } interface MakePaymentResult {