commit eda86e5f4883513a3586cc35ee355968d18c335a
parent dc5af7eebfd05cca4acca313c4c544f4cace59f6
Author: Christian Grothoff <christian@grothoff.org>
Date: Tue, 4 Aug 2026 19:18:19 +0200
also allow '~' in slugs, for consistency with libeufin.
Also spec for multiple editable-amounts in paivana templates.
Diffstat:
5 files changed, 100 insertions(+), 9 deletions(-)
diff --git a/core/api-common.rst b/core/api-common.rst
@@ -292,8 +292,8 @@ without any escaping. We call such an identifier a *slug*.
// Non-empty string that can be used as-is as a single path
// component of a URL. A slug must only use ASCII alphanumeric
- // characters ("A-Z", "a-z", "0-9") and the five punctuation
- // characters "-", ".", "_", ":" and "=". Consequently a slug
+ // characters ("A-Z", "a-z", "0-9") and the six punctuation
+ // characters "-", ".", "_", ":", "=" and "~". Consequently a slug
// never contains a "/" and never requires percent-encoding.
// The strings "." and ".." are not valid slugs, as they have a
// special meaning in URL paths.
@@ -302,8 +302,9 @@ without any escaping. We call such an identifier a *slug*.
// documented with the respective endpoint or field.
type Slug = string;
-This character set is deliberately narrower than what RFC 3986 permits in a
-path segment: it excludes ``@``, ``~`` and all sub-delimiters except ``=``,
+A slug is thus the set of RFC 3986 *unreserved* characters, plus ``:`` and
+``=``. The character set is deliberately narrower than what RFC 3986 permits
+in a path segment: it excludes ``@`` and all sub-delimiters except ``=``,
which are legal in a URL path but cause trouble once a slug is embedded in
other syntax, such as a shell command, a query string or a filename. ``=`` is
permitted because base64-encoded identifiers may end in padding.
diff --git a/core/api-merchant.rst b/core/api-merchant.rst
@@ -29,7 +29,7 @@ Merchant Backend RESTful API
Version History
---------------
-The currently implemented protocol version is **v33**.
+The currently implemented protocol version is **v34**.
* The Android PoS app is currently targeting **v20**.
* The SPA is currently targeting **vXX**.
@@ -62,6 +62,10 @@ The currently implemented protocol version is **v33**.
* ``v33``: adds ``build_version`` to :http:get:`/config </config>` and
``max_age_s`` to ``[/instances/$INSTANCE]/private/orders``, deprecating
the ``max_age`` query parameter of that endpoint
+* ``v34``: adds ``editable_amount`` to the choices of a Paivana template
+ together with the ``choice_amounts`` request field to instantiate them,
+ and ``min_amount``/``max_amount`` to bound client-chosen amounts for
+ templates of any type
**Upcoming versions:**
@@ -1106,6 +1110,24 @@ open or deferred.
// if "amount" is given.
currency?: string;
+ // Lower bound (inclusive) on amounts the client gets to choose
+ // when instantiating this template. Excludes any tip. Only
+ // amounts the client had an influence on are checked against
+ // this bound: the amount given in the request for a "fixed-order"
+ // template, the total of the selected products for an
+ // "inventory-cart" template, and the amount of an editable choice
+ // for a "paivana" template. Requires "currency" to be given and
+ // to match, and must not exceed "max_amount".
+ // Since protocol **v34**.
+ min_amount?: Amount;
+
+ // Upper bound (inclusive) on amounts the client gets to choose
+ // when instantiating this template. Excludes any tip. Checked
+ // against the same amounts as "min_amount". Requires "currency"
+ // to be given and to match.
+ // Since protocol **v34**.
+ max_amount?: Amount;
+
// 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;
@@ -1180,9 +1202,17 @@ open or deferred.
website_regex?: string;
// Methods to pay for the contract.
- choices: OrderChoice[];
+ choices: TemplateOrderChoice[];
}
+ If exactly one of the ``choices`` of a Paivana template has
+ ``editable_amount`` set, the template may also carry an ``amount`` in its
+ ``editable_defaults`` to tell the user interface to offer a single amount
+ field with that default; the client then submits it as the ``amount`` of
+ the `UsingTemplateCommonRequest`. With more than one editable choice, the
+ defaults are the ``amount`` values of the choices themselves and the client
+ must use ``choice_amounts``.
+
.. _template-choice:
Template Choices
@@ -1225,6 +1255,23 @@ Template Choices
max_fee?: Amount;
}
+ A choice of a *template* contract may additionally allow the client to
+ replace the amount when the template is instantiated.
+
+ .. ts:def:: TemplateOrderChoice
+
+ interface TemplateOrderChoice extends OrderChoice {
+ // If true, the client may replace "amount" when instantiating
+ // the template; "amount" then merely provides the default. The
+ // client may only change the value, never the currency. The
+ // resulting amount is subject to the "min_amount" and
+ // "max_amount" bounds of the template (if any).
+ // Defaults to false, in which case the amount is fixed by the
+ // merchant.
+ // Since protocol **v34**.
+ editable_amount?: boolean;
+ }
+
.. ts:def:: OrderInput
// For now, only token inputs are supported.
diff --git a/core/corebank/post-accounts.rst b/core/corebank/post-accounts.rst
@@ -9,8 +9,11 @@
.. ts:def:: RegisterAccountRequest
interface RegisterAccountRequest {
- // Username of the account
- // Must match [a-zA-Z0-9-._~]{1, 126}
+ // Username of the account.
+ // Must be at most 126 characters long. Implementations
+ // may further restrict the character set; libeufin-bank
+ // accepts only the RFC 3986 unreserved characters (that
+ // is, a slug without ":" and "=").
username: Slug;
// Password of the account used for authentication
diff --git a/core/merchant/get-templates-TEMPLATE_ID.rst b/core/merchant/get-templates-TEMPLATE_ID.rst
@@ -47,7 +47,9 @@
// may reject requests if the ``template_contract``
// or ``editable_defaults`` do
// specify an amount in a different currency.
- // This parameter is optional.
+ // This parameter is optional; since protocol **v34** it is
+ // returned whenever the ``template_contract`` specifies a
+ // ``currency``.
// Since protocol **v13**.
required_currency?: string;
}
diff --git a/core/merchant/post-templates-TEMPLATE_ID.rst b/core/merchant/post-templates-TEMPLATE_ID.rst
@@ -24,6 +24,17 @@
The template, instance, or product is unknown.
Returned with ``TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN`` or
``TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN``.
+ :http:statuscode:`409 Conflict`:
+ The request contradicts the template. In particular, this is
+ returned with ``TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT``
+ if the template fixes the amount but the request provides one, if
+ ``amount`` and ``choice_amounts`` are both given, if ``amount`` is
+ used as a shorthand but the template has no or more than one choice
+ with ``editable_amount``, if a ``choice_amounts`` entry selects a
+ choice without ``editable_amount``, or if a client-chosen amount
+ violates the ``min_amount`` or ``max_amount`` of the template.
+ Returned with ``TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH`` if a
+ client-chosen amount is in the wrong currency.
:http:statuscode:`413 Request entity too large`:
The uploaded body is to long, it exceeds the size limit.
Returned with an error code of
@@ -60,6 +71,12 @@
summary?: string;
// The amount to be paid, including tip.
+ // For a "paivana" template, this is a shorthand that is only
+ // allowed if exactly one of the choices of the template has
+ // "editable_amount" set: the amount then replaces the amount of
+ // that choice, the tip is added on top of it (as for a choice
+ // with a fixed amount), and the field is mutually exclusive with
+ // "choice_amounts".
amount?: Amount;
// Optional tip amount. Must match the currency of ``amount`` or the
@@ -121,4 +138,25 @@
// access control.
paivana_id: string;
+ // Amounts chosen by the client for the choices of the template
+ // that have "editable_amount" set. Choices that are not
+ // mentioned keep the amount given in the template. Mutually
+ // exclusive with the "amount" field.
+ // Since protocol **v34**.
+ choice_amounts?: TemplateChoiceAmount[];
+
+ }
+
+ .. ts:def:: TemplateChoiceAmount
+
+ interface TemplateChoiceAmount {
+ // Index of the choice in the "choices" array of the template
+ // contract. The choice must have "editable_amount" set.
+ choice_index: Integer;
+
+ // Amount to use for that choice, excluding any tip. Must be in
+ // the currency of the choice, and within the "min_amount" and
+ // "max_amount" bounds of the template (if any). Any "tip" given
+ // in the request is added on top of this amount.
+ amount: Amount;
}