taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

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:
Mcore/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