commit 482c6cfddf2f8c2a074efa653dba06c266d64ad0
parent 86a836eebeb95f230925185b794b2e0ad988af74
Author: Florian Dold <dold@taler.net>
Date: Wed, 19 Aug 2026 18:16:21 +0200
taler-util: preserve merchant token metadata
Diffstat:
2 files changed, 38 insertions(+), 0 deletions(-)
diff --git a/packages/taler-util/src/types-taler-merchant.test.ts b/packages/taler-util/src/types-taler-merchant.test.ts
@@ -19,6 +19,7 @@ import { test } from "node:test";
import {
codecForLoginTokenSuccessResponse,
codecForQueryInstancesResponse,
+ codecForTokenFamilyDetails,
} from "./types-taler-merchant.js";
function instancesResponse(method: string): any {
@@ -91,3 +92,28 @@ test("every login token scope the SPA can request decodes", (t) => {
);
}
});
+
+test("token family details retain arbitrary extra_data", () => {
+ const extra_data = {
+ discount: {
+ type: "percentage",
+ percentage: "20",
+ rounding: { mode: "nearest", precision: "0.01" },
+ product_categories: [1, 2],
+ },
+ future_field: { keep: true },
+ };
+ const decoded = codecForTokenFamilyDetails().decode({
+ slug: "coffee20",
+ name: "Coffee club",
+ description: "Twenty percent off coffee",
+ extra_data,
+ valid_after: { t_s: 1 },
+ valid_before: { t_s: 2 },
+ duration: { d_us: 1_000_000 },
+ kind: "discount",
+ issued: 3,
+ used: 1,
+ });
+ assert.deepStrictEqual(decoded.extra_data, extra_data);
+});
diff --git a/packages/taler-util/src/types-taler-merchant.ts b/packages/taler-util/src/types-taler-merchant.ts
@@ -3854,6 +3854,11 @@ export interface TokenFamilyCreateRequest {
// Optional map from IETF BCP 47 language tags to localized descriptions.
description_i18n?: { [lang_tag: string]: string };
+ // Additional kind-specific metadata. Parts of this object can become
+ // wallet-visible when the family is referenced by an order, so callers
+ // must not use it for secrets.
+ extra_data?: Record<string, unknown>;
+
// Start time of the token family's validity period.
// If not specified, merchant backend will use the current time.
valid_after?: Timestamp;
@@ -3899,6 +3904,9 @@ export interface TokenFamilyUpdateRequest {
// Optional map from IETF BCP 47 language tags to localized descriptions.
description_i18n: { [lang_tag: string]: string };
+ // Additional kind-specific metadata.
+ extra_data?: Record<string, unknown>;
+
// Start time of the token family's validity period.
valid_after: Timestamp;
@@ -3956,6 +3964,9 @@ export interface TokenFamilyDetails {
// Optional map from IETF BCP 47 language tags to localized descriptions.
description_i18n?: { [lang_tag: string]: string };
+ // Additional kind-specific metadata.
+ extra_data?: Record<string, unknown>;
+
// Start time of the token family's validity period.
valid_after: Timestamp;
@@ -5550,6 +5561,7 @@ export const codecForTokenFamilyDetails = (): Codec<TokenFamilyDetails> =>
"description_i18n",
codecOptional(codecForInternationalizedString()),
)
+ .property("extra_data", codecOptional(codecForAny()))
.property("valid_after", codecForTimestamp)
.property("valid_before", codecForTimestamp)
.property("duration", codecForDuration)