taler-typescript-core

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

commit 1efe89c4a42c3614de4611a60efe65b16b742c85
parent cd9f1a96af8a62aad632637e8dd386fa7566e1b0
Author: Florian Dold <florian@dold.me>
Date:   Fri, 13 Mar 2026 11:45:21 +0100

harness: test CSV report gen

Diffstat:
Mpackages/taler-harness/src/integrationtests/test-merchant-reports.ts | 31+++++++++++++++++++++++--------
Mpackages/taler-util/src/http-client/merchant.ts | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 100 insertions(+), 10 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-merchant-reports.ts b/packages/taler-harness/src/integrationtests/test-merchant-reports.ts @@ -22,6 +22,7 @@ import { succeedOrThrow, TalerCorebankApiClient, TalerMerchantApi, + TalerMerchantInstanceHttpClient, TalerMerchantManagementHttpClient, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; @@ -58,13 +59,16 @@ export async function runMerchantReportsTest(t: GlobalTestState) { amount: "TESTKUDOS:30", }); - const instanceApi = new TalerMerchantManagementHttpClient( + const instanceMgmtApi = new TalerMerchantManagementHttpClient( + merchant.makeInstanceBaseUrl(), + ); + const instanceApi = new TalerMerchantInstanceHttpClient( merchant.makeInstanceBaseUrl(), ); { const reportBytes = succeedOrThrow( - await instanceApi.getStatisticsReportPdf( + await instanceMgmtApi.getStatisticsReportPdf( merchantAdminAccessToken, "transactions", ), @@ -76,7 +80,7 @@ export async function runMerchantReportsTest(t: GlobalTestState) { { const order: TalerMerchantApi.Order = { - summary: "Buy me!", + summary: "Test payment 1", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", }; @@ -95,7 +99,7 @@ export async function runMerchantReportsTest(t: GlobalTestState) { { const reportBytes = succeedOrThrow( - await instanceApi.getStatisticsReportPdf( + await instanceMgmtApi.getStatisticsReportPdf( merchantAdminAccessToken, "transactions", ), @@ -107,7 +111,7 @@ export async function runMerchantReportsTest(t: GlobalTestState) { { const order: TalerMerchantApi.Order = { - summary: "Buy me again!", + summary: "Test payment 2", amount: "TESTKUDOS:1", fulfillment_url: "taler://fulfillment-success/thx", }; @@ -126,7 +130,7 @@ export async function runMerchantReportsTest(t: GlobalTestState) { { const reportBytes = succeedOrThrow( - await instanceApi.getStatisticsReportPdf( + await instanceMgmtApi.getStatisticsReportPdf( merchantAdminAccessToken, "transactions", ), @@ -151,7 +155,7 @@ export async function runMerchantReportsTest(t: GlobalTestState) { { const order: TalerMerchantApi.Order = { - summary: "Buy me again!", + summary: "Test payment 3", amount: "TESTKUDOS:0.5", fulfillment_url: "taler://fulfillment-success/thx", }; @@ -170,7 +174,7 @@ export async function runMerchantReportsTest(t: GlobalTestState) { { const reportBytes = succeedOrThrow( - await instanceApi.getStatisticsReportPdf( + await instanceMgmtApi.getStatisticsReportPdf( merchantAdminAccessToken, "transactions", ), @@ -179,6 +183,17 @@ export async function runMerchantReportsTest(t: GlobalTestState) { fs.writeFileSync(f, Buffer.from(reportBytes)); console.log(`written to ${f}`); } + + { + const reportBytes = succeedOrThrow( + await instanceMgmtApi.listOrdersRaw(merchantAdminAccessToken, { + mime: "text/csv", + }), + ); + const f = t.testDir + `/report-4.csv`; + fs.writeFileSync(f, Buffer.from(reportBytes)); + console.log(`written to ${f}`); + } } runMerchantReportsTest.suites = ["wallet"]; diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts @@ -1594,7 +1594,11 @@ export class TalerMerchantInstanceHttpClient { async listOrders( token: AccessToken, params: TalerMerchantApi.ListOrdersRequestParams = {}, - ) { + ): Promise< + | OperationFail<HttpStatusCode.Unauthorized> + | OperationFail<HttpStatusCode.NotFound> + | OperationOk<TalerMerchantApi.OrderHistory> + > { const url = new URL(`private/orders`, this.baseUrl); if (params.paid !== undefined) { @@ -1650,13 +1654,84 @@ export class TalerMerchantInstanceHttpClient { } /** + * https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-orders + */ + async listOrdersRaw( + token: AccessToken, + params: TalerMerchantApi.ListOrdersRequestParams & { mime: string }, + ): Promise< + | OperationOk<Uint8Array> + | OperationFail<HttpStatusCode.Unauthorized> + | OperationFail<HttpStatusCode.NotFound> + > { + const url = new URL(`private/orders`, this.baseUrl); + + if (params.paid !== undefined) { + url.searchParams.set("paid", params.paid ? "YES" : "NO"); + } + if (params.refunded !== undefined) { + url.searchParams.set("refunded", params.refunded ? "YES" : "NO"); + } + if (params.wired !== undefined) { + url.searchParams.set("wired", params.wired ? "YES" : "NO"); + } + if (params.date && !AbsoluteTime.isNever(params.date)) { + const time = AbsoluteTime.toProtocolTimestamp(params.date); + url.searchParams.set("date_s", String(time.t_s)); + } + if (params.maxAge && !Duration.isForever(params.maxAge)) { + const time = Duration.toTalerProtocolDuration(params.maxAge); + url.searchParams.set("max_age", String(time.d_us)); + } + if (params.timeout) { + url.searchParams.set("timeout_ms", String(params.timeout)); + } + if (params.sessionId) { + url.searchParams.set("session_id", params.sessionId); + } + if (params.fulfillmentUrl) { + url.searchParams.set("fulfillment_url", params.fulfillmentUrl); + } + if (params.summary) { + url.searchParams.set("summary_filter", params.summary); + } + addPaginationParams(url, params); + + const headers: Record<string, string> = {}; + if (token) { + headers.Authorization = makeBearerTokenAuthHeader(token); + } + headers["Accept"] = params.mime; + const resp = await this.httpLib.fetch(url.href, { + method: "GET", + headers, + }); + + switch (resp.status) { + case HttpStatusCode.Ok: + return opFixedSuccess(new Uint8Array(await resp.bytes())); + case HttpStatusCode.NotFound: // FIXME: missing in docs + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: // FIXME: missing in docs + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownHttpFailure(resp); + } + } + + /** * https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-orders-$ORDER_ID */ async getOrderDetails( token: AccessToken, orderId: string, params: TalerMerchantApi.GetOrderRequestParams = {}, - ) { + ): Promise< + | OperationOk<TalerMerchantApi.MerchantOrderStatusResponse> + | OperationFail<TalerErrorCode.MERCHANT_GENERIC_ORDER_UNKNOWN> + | OperationFail<TalerErrorCode.MERCHANT_GENERIC_INSTANCE_UNKNOWN> + | OperationFail<HttpStatusCode.Unauthorized> + > { const url = new URL(`private/orders/${orderId}`, this.baseUrl); if (params.allowRefundedForRepurchase !== undefined) {