commit d9d2a345b497f3b92fa4ff4a56aa7bdef8e882a9
parent b47d3b94e43658fb387f6b57fa80873732f6b27a
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date: Sun, 11 Jan 2026 01:17:51 +0100
[merchant-api] adding info about the templates update vTEMPLATE (described in DD73)
Diffstat:
| M | core/api-merchant.rst | | | 279 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
1 file changed, 271 insertions(+), 8 deletions(-)
diff --git a/core/api-merchant.rst b/core/api-merchant.rst
@@ -45,6 +45,8 @@ Android PoS app is currently targeting **v20**.
**Upcoming versions:**
+* ``vTEMPLATE``: adds template types, inventory-backed templates, and tip support
+ for template instantiation
* ``vTAXES``: adds features to manage taxes
* ``vPAIVANA``: adds features for templates to support session-based payments
@@ -2797,6 +2799,19 @@ contracts (such that the frontends have to do less work). You can use the
Taler merchant backend to process payments *without* using its inventory
management.
+.. _decimal-quantity:
+
+Decimal quantities
+^^^^^^^^^^^^^^^^^^
+
+.. ts:def:: DecimalQuantity
+
+ // Fixed-point decimal string in the form "<integer>[.<fraction>]".
+ // Fractional part has up to six digits.
+ // "-1" is only valid for fields that explicitly allow "infinity".
+ // Since protocol **vUNIT**; used in template selection since **vTEMPLATE**.
+ type DecimalQuantity = string;
+
Managing measurement units
^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -3205,7 +3220,7 @@ Adding products to the inventory
// Preferred way to express inventory counts using "<integer>[.<fraction>]" syntax.
// Use "-1" for unlimited stock. Required unless ``total_stock`` is provided.
- unit_total_stock?: string;
+ unit_total_stock?: DecimalQuantity;
// Legacy stock counter. Deprecated, use ``unit_total_stock`` instead.
// Still required when ``unit_total_stock`` is omitted.
@@ -3218,7 +3233,9 @@ Adding products to the inventory
// is true.
// Zero implies that the product is not sold separately or that the price must be supplied
// by the frontend.
+ // Each entry must use a distinct currency.
// Since API version **vUNIT**.
+ // Currency uniqueness enforced since protocol **vTEMPLATE**.
unit_price?: Amount[];
// True if the price(s) given are a net prices, false if they are
@@ -3267,7 +3284,8 @@ Adding products to the inventory
``unit_total_stock`` uses a fixed-point decimal string in the form ``INTEGER[.FRACTION]`` with
at most six fractional digits. Scientific notation or special values such as ``NaN`` are not
- accepted. Supply ``"-1"`` to represent unlimited stock.
+ accepted. Supply ``"-1"`` to represent unlimited stock. See
+ :ref:`DecimalQuantity <decimal-quantity>`.
Fractional behaviour defaults to the value of ``unit``. The backend disables fractions for
``Piece``, ``Set``, ``Custom``, ``WeightUnitMg`` and ``SizeUnitMm``. Other predefined units
@@ -3360,7 +3378,9 @@ Adding products to the inventory
// 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.
+ // Each entry must use a distinct currency.
// Since API version **vUNIT**.
+ // Currency uniqueness enforced since protocol **vTEMPLATE**.
unit_price?: Amount[];
// Legacy price field.
@@ -3381,7 +3401,7 @@ Adding products to the inventory
// Preferred way to express inventory counts using "<integer>[.<fraction>]" syntax.
// Use "-1" for unlimited stock.
- unit_total_stock?: string;
+ unit_total_stock?: DecimalQuantity;
// Legacy stock counter. Deprecated, use ``unit_total_stock`` instead.
total_stock?: Integer;
@@ -3533,7 +3553,7 @@ Inspecting inventory
// Stock expressed using "<integer>[.<fraction>]" syntax with up to six fractional digits.
// Use ``"-1"`` for unlimited stock.
- unit_total_stock: string;
+ unit_total_stock: DecimalQuantity;
// Number of units of the product that have already been sold.
total_sold: Integer;
@@ -5330,6 +5350,11 @@ connectivity or where a Web site wants to enable payments on a purely static
Web page (for example to collect donations). In these cases, the GNU Taler
wallet can be used to setup an order (and then of course pay for it).
+Templates can describe a fixed contract (``fixed-order``), an inventory-backed
+cart where the wallet picks products and quantities (``inventory-cart``), or a
+session-based template (``paivana``). The template type controls which fields
+appear in the contract and which inputs are required during instantiation.
+
The template itself primarily provides order details that cannot be be changed
by the customer when the wallet creates the order. The idea is that the user
*may* be prompted to enter certain information, such as the amount to be paid,
@@ -5421,11 +5446,27 @@ Adding templates
.. ts:def:: TemplateContractDetails
- interface TemplateContractDetails {
+ type TemplateContractDetails = (TemplateContractFixedOrder | TemplateContractInventoryCart | TemplateContractPaivana) & TemplateContractCommon;
+
+ .. ts:def:: TemplateContractCommon
+
+ interface TemplateContractCommon {
+ // Template type to apply. Defaults to "fixed-order" if omitted.
+ // Prescribes which interface has to be followed
+ // Since protocol **vTEMPLATE**.
+ template_type?: TemplateType;
// Human-readable summary for the template.
summary?: string;
+ // The time the customer need to pay before his order will be deleted.
+ // It is deleted if the customer did not pay and if the duration is over.
+ pay_duration: RelativeTime;
+ }
+
+ .. ts:def:: TemplateContractFixedOrder
+
+ interface TemplateContractFixedOrder {
// Required currency for payments to the template.
// The user may specify any amount, but it must be
// in this currency.
@@ -5439,13 +5480,69 @@ Adding templates
// Minimum age buyer must have (in years). Default is 0.
minimum_age: Integer;
+ }
- // The time the customer need to pay before his order will be deleted.
- // It is deleted if the customer did not pay and if the duration is over.
- pay_duration: RelativeTime;
+ .. ts:def:: TemplateContractInventoryCart
+
+ interface TemplateContractInventoryCart {
+
+ // Inventory-cart: request a tip during instantiation.
+ // Since protocol **vTEMPLATE**.
+ request_tip?: boolean;
+
+ // Inventory-cart: allow any inventory item to be selected.
+ // Since protocol **vTEMPLATE**.
+ selected_all?: boolean;
+ // Inventory-cart: only products in these categories are selectable.
+ // Since protocol **vTEMPLATE**.
+ selected_categories?: Integer[];
+
+ // Inventory-cart: only these products are selectable.
+ // Since protocol **vTEMPLATE**.
+ selected_products?: string[];
+
+ // Inventory-cart: require exactly one selection entry.
+ // Since protocol **vTEMPLATE**.
+ choose_one?: boolean;
+
+ // Inventory-cart: backend-provided payload with selectable data.
+ // Only present in ``GET /templates/$TEMPLATE_ID`` responses.
+ // Since protocol **vTEMPLATE**.
+ inventory_payload?: InventoryPayload;
}
+ .. ts:def:: TemplateContractPaivana
+
+ interface TemplateContractPaivana {
+
+ // Required currency for payments to the template.
+ // The user may specify any amount, but it must be
+ // in this currency.
+ // This parameter is optional and should not be present
+ // if "amount" is given.
+ currency?: string;
+
+ // The price is imposed by the merchant and cannot be changed by the customer.
+ // This parameter is optional.
+ amount?: Amount;
+
+ // Minimum age buyer must have (in years). Default is 0.
+ minimum_age: Integer;
+
+ // Paivana: identifier of the session or campaign.
+ // Since protocol **vPAIVANA**.
+ paivana_id?: string;
+ }
+
+ .. ts:def:: TemplateType
+
+ enum TemplateType {
+ FIXED_ORDER = "fixed-order",
+ INVENTORY_CART = "inventory-cart",
+ PAIVANA = "paivana"
+ }
+
Editing templates
@@ -5628,6 +5725,11 @@ Using template
**Details:**
+ For ``inventory-cart`` templates the backend augments the returned
+ ``template_contract`` with ``inventory_payload`` containing products,
+ categories, and units. The payload is filtered by the template's
+ ``selected_all``, ``selected_categories``, and ``selected_products`` settings.
+
.. ts:def:: WalletTemplateDetails
interface WalletTemplateDetails {
@@ -5654,6 +5756,118 @@ Using template
required_currency?: string;
}
+ .. ts:def:: InventoryPayload
+
+ interface InventoryPayload {
+ // Inventory products available for selection.
+ // Since protocol **vTEMPLATE**.
+ products: InventoryPayloadProduct[];
+
+ // Categories referenced by the payload products.
+ // Since protocol **vTEMPLATE**.
+ categories: InventoryPayloadCategory[];
+
+ // Custom units referenced by the payload products.
+ // Since protocol **vTEMPLATE**.
+ units: InventoryPayloadUnit[];
+ }
+
+ .. ts:def:: InventoryPayloadProduct
+
+ interface InventoryPayloadProduct {
+ // Product identifier.
+ // Since protocol **vTEMPLATE**.
+ product_id: string;
+
+ // Human-readable product name.
+ // Since protocol **vTEMPLATE**.
+ product_name: string;
+
+ // Human-readable product description.
+ // Since protocol **vTEMPLATE**.
+ description: string;
+
+ // Localized product descriptions.
+ // Since protocol **vTEMPLATE**.
+ description_i18n?: { [lang_tag: string]: string };
+
+ // Unit identifier for the product.
+ // Since protocol **vTEMPLATE**.
+ unit: string;
+
+ // Price tiers for the product.
+ // Since protocol **vTEMPLATE**.
+ unit_prices: Amount[];
+
+ // Whether fractional quantities are allowed for this unit.
+ // Since protocol **vTEMPLATE**.
+ unit_allow_fraction: boolean;
+
+ // Maximum fractional precision (0-6) enforced for this unit.
+ // Since protocol **vTEMPLATE**.
+ unit_precision_level: Integer;
+
+ // Category identifiers associated with this product.
+ // Since protocol **vTEMPLATE**.
+ categories: Integer[];
+
+ // Taxes applied to the product.
+ // Since protocol **vTEMPLATE**.
+ taxes?: Tax[];
+
+ // Hash of the product image (if any).
+ // Since protocol **vTEMPLATE**.
+ image_hash?: string;
+ }
+
+ .. ts:def:: InventoryPayloadCategory
+
+ interface InventoryPayloadCategory {
+ // Category identifier.
+ // Since protocol **vTEMPLATE**.
+ category_id: Integer;
+
+ // Human-readable category name.
+ // Since protocol **vTEMPLATE**.
+ category_name: string;
+
+ // Localized category names.
+ // Since protocol **vTEMPLATE**.
+ category_name_i18n?: { [lang_tag: string]: string };
+ }
+
+ .. ts:def:: InventoryPayloadUnit
+
+ interface InventoryPayloadUnit {
+ // Unit identifier.
+ // Since protocol **vTEMPLATE**.
+ unit: string;
+
+ // Human-readable long label.
+ // Since protocol **vTEMPLATE**.
+ unit_name_long: string;
+
+ // Localized long labels.
+ // Since protocol **vTEMPLATE**.
+ unit_name_long_i18n?: { [lang_tag: string]: string };
+
+ // Human-readable short label.
+ // Since protocol **vTEMPLATE**.
+ unit_name_short: string;
+
+ // Localized short labels.
+ // Since protocol **vTEMPLATE**.
+ unit_name_short_i18n?: { [lang_tag: string]: string };
+
+ // Whether fractional quantities are allowed for this unit.
+ // Since protocol **vTEMPLATE**.
+ unit_allow_fraction: boolean;
+
+ // Maximum fractional precision (0-6) enforced for this unit.
+ // Since protocol **vTEMPLATE**.
+ unit_precision_level: Integer;
+ }
+
.. http:post:: [/instances/$INSTANCES]/templates/$TEMPLATE_ID
@@ -5672,6 +5886,10 @@ Using template
**Details:**
+ Based on the template type, provide one of the following request bodies:
+
+ 1. For ``fixed-order``, use `UsingTemplateDetails`.
+
.. ts:def:: UsingTemplateDetails
interface UsingTemplateDetails {
@@ -5683,6 +5901,51 @@ Using template
amount?: Amount;
}
+ 2. For ``inventory-cart``, use `UsingTemplateDetailsInventory`.
+
+ .. ts:def:: UsingTemplateDetailsInventory
+
+ interface UsingTemplateDetailsInventory {
+
+ // Summary of the template
+ summary?: string;
+
+ // The amount entered by the customer.
+ amount?: Amount;
+
+ // Optional tip amount. Must match the currency of ``amount`` or the
+ // fixed template currency.
+ // Since protocol **vTEMPLATE**.
+ tip?: Amount;
+
+ // Inventory-cart: selected products and quantities.
+ // Since protocol **vTEMPLATE**.
+ inventory_selection?: InventorySelectionEntry[];
+ }
+
+ .. ts:def:: InventorySelectionEntry
+
+ interface InventorySelectionEntry {
+ // Inventory product to add.
+ product_id: string;
+
+ // Quantity in "<integer>[.<fraction>]" form using the product unit rules.
+ quantity: DecimalQuantity;
+ }
+
+ 3. For ``paivana``, use `UsingTemplateDetailsPaivana`.
+
+ .. ts:def:: UsingTemplateDetailsPaivana
+
+ interface UsingTemplateDetailsPaivana {
+
+ // Summary of the template
+ summary?: string;
+
+ // The amount entered by the customer.
+ amount?: Amount;
+ }
+
--------
Webhooks