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:
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) {