commit fad6e72d1e778cfa0c6772cae1e16d8f544267cb
parent 89cf63785ee4cd8c7838cebc2046374e7f1b2f51
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date: Wed, 26 Nov 2025 14:30:42 +0100
adding actual changes from the update-inventory-logic branch + info on get /images
Diffstat:
| M | core/api-merchant.rst | | | 263 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- |
1 file changed, 236 insertions(+), 27 deletions(-)
diff --git a/core/api-merchant.rst b/core/api-merchant.rst
@@ -2616,6 +2616,184 @@ Taler merchant backend to process payments *without* using its inventory
management.
+Managing measurement units
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. http:get:: [/instances/$INSTANCE]/private/units
+
+ Returns the list of measurement units available to an instance.
+
+ **Required permission:** ``units-read``
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend returned the unit catalog. The body is a `UnitListResponse`.
+
+ **Details:**
+
+ .. ts:def:: UnitListResponse
+
+ interface UnitListResponse {
+ units: MerchantUnit[];
+ }
+
+ .. ts:def:: MerchantUnit
+
+ interface MerchantUnit {
+ // Unique serial for the unit.
+ unit_serial: Integer;
+
+ // Short identifier used in product definitions.
+ unit: string;
+
+ // Human-readable long label.
+ unit_name_long: string;
+
+ // Human-readable short label.
+ unit_name_short: string;
+
+ // Optional translations for the long label.
+ unit_name_long_i18n?: { [lang_tag: string]: string };
+
+ // Optional translations for the short label.
+ unit_name_short_i18n?: { [lang_tag: string]: string };
+
+ // Whether fractional quantities are allowed for this unit.
+ unit_allow_fraction: boolean;
+
+ // Maximum fractional precision (0-6) enforced for this unit.
+ unit_precision_level: Integer;
+
+ // Whether the unit is currently active.
+ unit_active: boolean;
+
+ // True if the unit is provided by the backend and cannot be removed.
+ unit_builtin: boolean;
+ }
+
+.. http:get:: [/instances/$INSTANCE]/private/units/$UNIT
+
+ Fetch details for a single measurement unit.
+
+ **Required permission:** ``units-read``
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend returned the unit definition. The body is a `MerchantUnit`.
+ :http:statuscode:`404 Not found`:
+ The unit is unknown to the backend.
+
+.. http:post:: [/instances/$INSTANCE]/private/units
+
+ Create a custom measurement unit or reactivate a previously disabled one.
+
+ **Required permission:** ``units-write``
+
+ **Request:**
+
+ The request must be a `UnitAddRequest`.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The backend added the unit.
+ :http:statuscode:`409 Conflict`:
+ A built-in unit with the same short name already exists.
+
+ .. ts:def:: UnitAddRequest
+
+ interface UnitAddRequest {
+ // Short identifier to reference the unit from products and orders.
+ unit: string;
+
+ // Human-readable long label (e.g. "kilogram").
+ unit_name_long: string;
+
+ // Human-readable short label (e.g. "kg").
+ unit_name_short: string;
+
+ // Optional translations for the long label keyed by BCP 47 language tags.
+ unit_name_long_i18n?: { [lang_tag: string]: string };
+
+ // Optional translations for the short label keyed by BCP 47 language tags.
+ unit_name_short_i18n?: { [lang_tag: string]: string };
+
+ // Defaults to false; set to true to enable fractional quantities.
+ unit_allow_fraction?: boolean;
+
+ // Fractional precision (0-6). Ignored when ``unit_allow_fraction`` is false.
+ unit_precision_level?: Integer;
+
+ // Defaults to true.
+ unit_active?: boolean;
+ }
+
+.. http:patch:: [/instances/$INSTANCE]/private/units/$UNIT
+
+ Update attributes of a measurement unit.
+
+ **Required permission:** ``units-write``
+
+ **Request:**
+
+ The request must be a `UnitPatchRequest`.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The backend updated the unit.
+ :http:statuscode:`404 Not found`:
+ The unit is unknown to the backend.
+
+ Built-in units allow changing ``unit_allow_fraction``, ``unit_precision_level``, and
+ ``unit_active``; other fields are immutable for built-ins.
+
+ .. ts:def:: UnitPatchRequest
+
+ interface UnitPatchRequest {
+ // Updated human-readable long label.
+ unit_name_long?: string;
+
+ // Updated human-readable short label.
+ unit_name_short?: string;
+
+ // Updated translations for the long label keyed by BCP 47 language tags.
+ unit_name_long_i18n?: { [lang_tag: string]: string };
+
+ // Updated translations for the short label keyed by BCP 47 language tags.
+ unit_name_short_i18n?: { [lang_tag: string]: string };
+
+ // Override to allow or forbid fractional quantities.
+ unit_allow_fraction?: boolean;
+
+ // Override for the fractional precision (0-6). Ignored if fractions are disabled.
+ unit_precision_level?: Integer;
+
+ // Toggle whether the unit is active.
+ unit_active?: boolean;
+ }
+
+ Fractional precision is capped at six decimal places. Disabling
+ ``unit_allow_fraction`` forces ``unit_precision_level`` to zero.
+
+.. http:delete:: [/instances/$INSTANCE]/private/units/$UNIT
+
+ Remove a custom unit from an instance.
+
+ **Required permission:** ``units-write``
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The unit was removed.
+ :http:statuscode:`404 Not found`:
+ The unit is unknown to the backend.
+ :http:statuscode:`409 Conflict`:
+ Built-in units cannot be deleted.
+
+
Managing product categories
^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -2841,12 +3019,13 @@ Adding products to the inventory
// Still required when ``unit_total_stock`` is omitted.
total_stock?: Integer;
- // Preferred way to express the per-unit price of the product. Zero implies that
- // the product is not sold separately or that the price must be supplied by the frontend.
- // If non-zero, this price MUST include applicable taxes.
- unit_price?: Amount;
+ // Preferred way to express the per-unit price of the product. Supply at least one entry;
+ // the first entry represents the base price and MUST include applicable taxes.
+ // Zero implies that the product is not sold separately or that the price must be supplied
+ // by the frontend.
+ unit_price?: Amount[];
- // Legacy price field. Deprecated; when present it must match ``unit_price``.
+ // Legacy price field. Deprecated; when present it must match the first element of ``unit_price``.
price?: Amount;
// An optional base64-encoded product image.
@@ -2869,10 +3048,11 @@ Adding products to the inventory
Clients SHOULD migrate to the new ``unit_*`` fields and treat ``total_stock`` and ``price``
as deprecated compatibility shims. If both the legacy and the new representation are supplied,
their values must match. Omitting both ``unit_total_stock`` and ``total_stock`` (or both
- ``unit_price`` and ``price``) results in a ``400 Bad Request``.
+ ``unit_price`` and ``price``) results in a ``400 Bad Request``. When only ``price`` is given,
+ the backend treats it as a one-element ``unit_price`` array.
``unit_total_stock`` uses a fixed-point decimal string in the form ``INTEGER[.FRACTION]`` with
- at most eight fractional digits. Scientific notation or special values such as ``NaN`` are not
+ at most six fractional digits. Scientific notation or special values such as ``NaN`` are not
accepted. Supply ``"-1"`` to represent unlimited stock.
Fractional behaviour defaults to the value of ``unit``. The backend disables fractions for
@@ -2954,12 +3134,13 @@ Adding products to the inventory
// Since API version **v16**.
categories?: Integer[];
- // Preferred way to express the per-unit price. Zero implies that
- // the product is not sold separately or that the price must be supplied by the frontend.
- // If non-zero, this price MUST include applicable taxes.
- unit_price?: Amount;
+ // Preferred way to express the per-unit price. Supply at least one entry;
+ // the first entry represents the base price and MUST include applicable taxes.
+ // Zero implies that the product is not sold separately or that the price must be supplied
+ // by the frontend.
+ unit_price?: Amount[];
- // Legacy price field. Deprecated; when present it must match ``unit_price``.
+ // Legacy price field. Deprecated; when present it must match the first element of ``unit_price``.
price?: Amount;
// An optional base64-encoded product image.
@@ -3083,12 +3264,12 @@ Inspecting inventory
// Since API version **v16**.
categories: Integer[];
- // Price for one ``unit`` of the product. Zero is used to imply that this product
- // is not sold separately, or that the price is not fixed, and must be supplied by the
- // front-end. If non-zero, this price MUST include applicable taxes.
- unit_price: Amount;
+ // Price tiers for this product. The first entry represents the base price and MUST include
+ // applicable taxes. Zero implies the product is not sold separately or that the price is not
+ // fixed, and must be supplied by the front-end.
+ unit_price: Amount[];
- // Legacy price field kept for compatibility. Deprecated; equal to ``unit_price``.
+ // Legacy price field kept for compatibility. Deprecated; equal to the first element of ``unit_price``.
price: Amount;
// An optional base64-encoded product image.
@@ -3101,7 +3282,7 @@ Inspecting inventory
// Legacy integer stock counter kept for compatibility. ``-1`` indicates unlimited stock.
total_stock: Integer;
- // Stock expressed using "<integer>[.<fraction>]" syntax with up to eight fractional digits.
+ // Stock expressed using "<integer>[.<fraction>]" syntax with up to six fractional digits.
// Use ``"-1"`` for unlimited stock.
unit_total_stock: string;
@@ -3186,12 +3367,12 @@ Inspecting inventory
// Maximum fractional precision (0-6) enforced for inventory operations.
unit_precision_level: Integer;
- // Price for one ``unit`` of the product. Zero is used to imply that this product
- // is not sold separately, or that the price is not fixed, and must be supplied by the
- // front-end. If non-zero, this price MUST include applicable taxes.
- unit_price: Amount;
+ // Price tiers for this product. The first entry represents the base price and MUST include
+ // applicable taxes. Zero implies the product is not sold separately or that the price is not
+ // fixed, and must be supplied by the front-end.
+ unit_price: Amount[];
- // Legacy price field kept for compatibility. Deprecated; equal to ``unit_price``.
+ // Legacy price field kept for compatibility. Deprecated; equal to the first element of ``unit_price``.
price: Amount;
// An optional base64-encoded product image.
@@ -3203,7 +3384,7 @@ Inspecting inventory
// Legacy integer stock counter kept for compatibility. ``-1`` indicates unlimited stock.
total_stock?: Integer;
- // Stock expressed using "<integer>[.<fraction>]" syntax with up to eight fractional digits.
+ // Stock expressed using "<integer>[.<fraction>]" syntax with up to six fractional digits.
// Use ``"-1"`` for unlimited stock.
unit_total_stock: string;
@@ -3226,6 +3407,34 @@ Inspecting inventory
}
+Fetching product images
+^^^^^^^^^^^^^^^^^^^^^^^
+
+.. http:get:: /products/$IMAGE_HASH/image
+
+ Returns a stored product image by its content hash. The hash is the lowercase
+ hexadecimal SHA-256 digest of the base64-encoded image data supplied in
+ ``image`` during product creation or update.
+
+ **Required permission:** none (public endpoint)
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The body is a `ProductImageResponse`.
+ :http:statuscode:`400 Bad Request`:
+ The hash is not a valid hex-encoded SHA-256 digest.
+ :http:statuscode:`404 Not found`:
+ No image is known for the given hash.
+
+ .. ts:def:: ProductImageResponse
+
+ interface ProductImageResponse {
+ // Data URL containing the product image as stored in the backend.
+ image: ImageDataUrl;
+ }
+
+
Reserving inventory
^^^^^^^^^^^^^^^^^^^
@@ -3734,13 +3943,13 @@ Creating orders
// Legacy integer quantity requested. Deprecated; see ``unit_requested_quantity``.
requested_quantity: Integer;
- // Requested quantity using "<integer>[.<fraction>]" syntax with up to eight fractional digits.
+ // Requested quantity using "<integer>[.<fraction>]" syntax with up to six fractional digits.
unit_requested_quantity: string;
// Legacy integer availability (must be below ``requested_quantity``).
available_quantity: Integer;
- // Available quantity using "<integer>[.<fraction>]" syntax with up to eight fractional digits.
+ // Available quantity using "<integer>[.<fraction>]" syntax with up to six fractional digits.
unit_available_quantity: string;
// When do we expect the product to be again in stock?
@@ -6092,7 +6301,7 @@ It has the following structure:
// Legacy integer portion of the quantity to deliver defaults to 1 if not specified.
quantity?: Integer;
- // Preferred quantity string using "<integer>[.<fraction>]" syntax with up to eight fractional digits.
+ // Preferred quantity string using "<integer>[.<fraction>]" syntax with up to six fractional digits.
unit_quantity?: string;
// Unit in which the product is measured (liters, kilograms, packages, etc.).