taler-docs

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

commit 9fc64995dd8fe9106d600b92a87d23bea80ee806
parent fa579727df1f58159f8a4cfc86768bdf01861ccc
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date:   Mon, 22 Jun 2026 18:59:19 +0200

adding dd96 about partial payment

Diffstat:
MREADME | 1+
Mdesign-documents/095-captcha-100.rst | 5++++-
Adesign-documents/096-partial-payments.rst | 296+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdesign-documents/index.rst | 2++
Mflake.nix | 1+
5 files changed, 304 insertions(+), 1 deletion(-)

diff --git a/README b/README @@ -5,6 +5,7 @@ To build things on a Debian (-based) system, install these packages: - python3-sphinx - python3-myst-parser - python3-sphinx-book-theme + - python3-sphinx-design - graphviz - texlive-latex-extra - dvipng diff --git a/design-documents/095-captcha-100.rst b/design-documents/095-captcha-100.rst @@ -68,21 +68,25 @@ Proposed Solution ================= Business: + - Open bank accounts in various currencies - Consider getting no-action letters from regulators Wallets: + - Implement wallet dialect with non-payment captcha-oriented language - Ensure wallets respect 'no deposit' and 'no-p2p' settings nicely - Communicate special fee structure clearly on withdraw Other development: + - Implement, test and document Pepsi - update merchant backend to support wire method 'void' (auto-configured bank account payto://void/) for some particular settings. Administration: + - Deploy a single exchange in CAPTCHAS and hook it up to all of these accounts with some reasonable currency conversion ratio @@ -129,4 +133,3 @@ Drawbacks Discussion / Q&A ================ - diff --git a/design-documents/096-partial-payments.rst b/design-documents/096-partial-payments.rst @@ -0,0 +1,296 @@ +DD 96: Partial Payments +####################### + +Summary +======= + +This document proposes support for orders where only part of the total amount +is paid with Taler and the remaining amount is paid with other payment +methods, such as cash, card, vouchers or others. + +The main protocol change is to allow the amount of an order or order choice to +be either the existing ``Amount`` string or an ``AmountObject`` that splits the +total by payment method. The split must always contain a ``taler`` entry, even +when the Taler amount is 0. + +Motivation +========== + +In person purchases might involve mixed payments. A customer may pay part of +an order in cash and the rest with Taler, or a cashier may need to combine +Taler with a card terminal, voucher system or other local payment method. +Today, the merchant backend and wallet assume that the amount in the contract +is the amount the wallet pays with Taler. This model cannot represent a +single receipt and order that is settled by multiple methods. + +The goal is not to make the merchant backend process card or cash payments. +The goal is to let the merchant backend, wallet core and POS applications agree +on the order total, the Taler portion and the non-Taler portions that must have +already been completed outside of Taler. + +Requirements +============ + +* Orders and choices must be able to express mixed payment amounts. +* The existing plain ``Amount`` form must remain valid for backwards + compatibility. +* The split amount form must always include the ``taler`` method. +* The total order amount is the sum of all payment-method amounts. +* The wallet must only pay the amount assigned to ``taler``. +* The POS or other accommodating application must execute all non-Taler + payments before the Taler payment. +* The Taler payment is always the last payment step. +* If the Taler payment fails after other payments succeeded, the POS must + either modify the order and retry the Taler step or refund the already + completed non-Taler payments. +* The merchant backend must preserve enough information for receipts, + reporting and order inspection to show how the total was split. +* Per-method payment information must be stored in ``extra.payments`` in a + flat structure that the merchant portal can render as a generic table. +* The design must not require the wallet to validate that cash, card or other + non-Taler payments actually happened. + +Proposed Solution +================= + +Amount Variant +-------------- + +Introduce a new amount type for order creation and contract terms: + +:: + + type AmountSpec = Amount | AmountObject; + + interface AmountObject { + // Amount paid with GNU Taler. This key is mandatory. + taler: Amount; + + // Additional payment methods, such as "cash", "card" or + // integration-specific identifiers. + [method: string]: Amount; + } + +The new ``AmountSpec`` type replaces ``Amount`` in: + +* ``OrderV0.amount`` +* ``OrderChoice.amount`` +* ``ContractTermsV0.amount`` +* ``ContractChoice.amount`` + +The existing ``Amount`` string remains the short form for a pure Taler +payment. It is equivalent to: + +:: + + { + "taler": Amount; + } + +All amounts in an ``AmountObject`` must use the same currency. Zero amounts +are allowed only where the corresponding payment method is still meaningful to +the application. In particular, ``taler`` may be zero to represent an order +that is tracked by a Taler-aware POS and receipt flow, but paid entirely by +other methods. + +Payment Method Names +-------------------- + +The initial reserved method names are: + +* ``taler`` for GNU Taler +* ``cash`` for cash accepted by the merchant or cashier + +Other names are allowed for integrations, but they should be stable ASCII +identifiers. + +Payment Details in Contract Extra +--------------------------------- + +The split in ``AmountObject`` only defines how much is assigned to each payment +method. Additional per-method information must be stored in the contract's +``extra`` object under ``extra.payments``: + +:: + + interface PaymentInfo { + // Payment method, for example "cash", "card"... + method: string; + + // Identifier of the payment action within the order. + // Examples: "cash1", "sumup1", "sumup2". + id: string; + + // Amount covered by this payment action. + amount: Amount; + + // Additional method-specific fields. These fields must be + // stored only at this level. + [field: string]: string | Amount | Integer | boolean | null; + } + + interface PartialPaymentExtra { + payments: PaymentInfo[]; + } + +For cash payments, additional fields may include the cashier name, cashier +number, register identifier or similar local information. For card payments, +additional fields may include the terminal identifier, acquirer reference, +transaction ID or authorization code. Other systems may add the fields they +need for reconciliation or audit. + +The additional fields must be stored only one level below the payment entry. +Nested method-specific objects should not be used. This allows the merchant +portal to render ``extra.payments`` as a simple table without knowing a custom +rendering format for each payment method. + +Payment Flow +------------ + +The POS or integrating application is responsible for orchestrating mixed +payments: + +1. Create or update the order with an ``AmountSpec`` that reflects the intended split. +2. Run all non-Taler payment steps, such as cash handling or card terminal authorization. +3. Start the Taler payment as the final step. +4. Complete the sale only after the merchant backend confirms the Taler payment, unless the ``taler`` amount is zero. + +The wallet receives the contract terms and computes the payable Taler amount +from the selected amount variant. It ignores non-Taler methods for coin +selection and payment signing, but it may render the full split so that the +customer understands why the Taler amount is lower than the order total. + +Failure Handling +---------------- + +Mixed payments introduce a failure mode where a non-Taler payment has already +succeeded but the final Taler payment fails. The merchant backend cannot +automatically repair this state because it does not control the external +payment method. + +The POS or integrating application must therefore choose one of these recovery +paths: + +* modify the order amount split and retry the Taler payment; +* cancel the order and refund or void the completed non-Taler payments; +* proceed with different payment method, and make Taler part lower or 0. + +Receipt Handling +---------------- + +For normal wallet flows, the customer can access the Taler receipt after the +wallet payment. In POS deployments this may not be enough. Some jurisdictions +require a printed or otherwise directly provided receipt, and in a mixed +payment flow the customer may not receive a Taler receipt if the POS +application performs self-pickup or the Taler amount is zero. + +POS applications and other accommodating applications must therefore support a +mode where they retrieve the receipt themselves from the merchant backend and +provide it to the customer through the locally required channel, such as a +printer, terminal display, e-mail or another regulated receipt mechanism. + +Reporting +--------- + +The merchant backend should store the selected amount split as part of the +contract terms and expose it through order status and history APIs. Existing +reporting that expects a single amount should continue to show the total order +amount. Detailed views should show the split by method. + +The merchant portal should render ``extra.payments`` as a table. Common columns +are ``method``, ``id`` and ``amount``. Additional columns can be derived from +the union of the flat method-specific fields present in the payment entries. +The merchant portal should not need method-specific rendering logic to show +this information. + +Refunds +------- + +Taler refunds can only refund the amount actually paid with Taler. Refunds for +cash, card or other methods remain the responsibility of the POS or external +payment integration. When an order has a split amount, APIs and UIs should +avoid wording that implies the merchant backend can refund the whole order +through Taler. + +Test Plan +========= + +* Merchant backend tests for accepting both plain ``Amount`` and + ``AmountObject`` in v0 orders and v1 choices. +* Merchant backend tests rejecting split amounts with missing ``taler`` method in ``AmountObject``, + mixed currencies or invalid method names. +* Merchant backend tests preserving ``extra.payments`` payment entries with + flat method-specific fields. +* Wallet core tests for computing the payable Taler amount from both variants. +* Wallet core tests for rendering or exposing the full split without attempting + to pay non-Taler amounts. +* POS integration tests for a successful cash/card-first and Taler-last flow. +* POS integration tests for Taler failure after a non-Taler payment succeeded. + +Definition of Done +================== + +* Merchant backend supports the new amount variant for order creation, + contract terms, order status and history. +* Merchant backend validates that every split amount contains ``taler`` and + that all split entries use one currency. +* Merchant backend preserves per-method payment details in ``extra.payments``. +* Wallet core supports the new amount variant and pays only the ``taler`` + portion. +* Wallet UIs can display the total and the selected Taler amount clearly. +* POS and other accommodating applications support the required orchestration: + non-Taler payments first, Taler payment last. +* Merchant portal renders ``extra.payments`` as a generic table without + method-specific renderers. +* Documentation explains that non-Taler refunds and failure recovery are owned + by the integrating application. + +Alternatives +============ + +Create Separate Orders +---------------------- + +The POS could create one Taler order only for the Taler amount and track cash +or card payments in its own system. This avoids changing the contract amount +type, but it loses the single-order receipt and reporting model. It also makes +customer-facing order totals harder to verify. As well it looses the backup +and synchronisation between device possibilities. + +Let Taler Run Before Other Methods +---------------------------------- + +Running Taler before cash or card would make the Taler part successful while +the external payment can still fail. That leaves the merchant with a paid +Taler contract for an order that may not be otherwise settled. Requiring Taler +to be last gives the POS a clearer recovery path because external payments can +still be voided, refunded or used to recompute the remaining Taler amount. As +well it can create problems when refund deadline for Taler option was set as 0 +and other method of payment failed. + +Drawbacks +========= + +* The amount field becomes more complex for all components that parse contract + terms. +* POS implementations must handle partial failure and external refunds + carefully. +* Some old integrations may break when new object is found. +* Reporting and refund UIs must distinguish total order amount from Taler-paid + amount. + +Open Questions +============== + +* Should money pots store full totals, per-method totals, or both? Should + merchant backend auto create new pots per each new payment method found in + order? +* Should payment method names be centrally registered, or is validation of + stable ASCII identifiers sufficient? +* Should there be a dedicated status for orders where non-Taler payments + succeeded but the final Taler payment failed? or we can just delete them? +* Should the merchant backend expose explicit receipt self-pickup endpoints for + POS devices, or are existing private order status APIs sufficient? + +Discussion / Q&A +================ diff --git a/design-documents/index.rst b/design-documents/index.rst @@ -106,4 +106,6 @@ Design documents that start with "XX" are considered deprecated. 092-incremental-backup-sync 093-checkout-page 094-discounts-passes-wallet + 095-captcha-100 + 096-partial-payments 999-template diff --git a/flake.nix b/flake.nix @@ -60,6 +60,7 @@ python-pkgs.sphinx-book-theme python-pkgs.myst-parser python-pkgs.sphinxcontrib-httpdomain + python-pkgs.sphinx-design ])) ];