diff options
Diffstat (limited to 'orphaned/taler-mcig.rst')
-rw-r--r-- | orphaned/taler-mcig.rst | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/orphaned/taler-mcig.rst b/orphaned/taler-mcig.rst new file mode 100644 index 00000000..1950f4af --- /dev/null +++ b/orphaned/taler-mcig.rst @@ -0,0 +1,563 @@ +.. + This file is part of GNU TALER. + Copyright (C) 2021 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + + @author Thien-Thi Nguyen + + +Merchant/Customer Interaction Guide +################################### + +The audience for the Mechant/Customer Interaction Guide is the merchant +who wishes to set up a "web shop" that works with the Taler payment system. + + +Introduction +============ + +.. include:: frags/taler-payment-cycle.rst + +This guide focuses on step 4, the interaction between the customer and the +merchant. In particular, we first review two basic interaction flows +(with and without shopping cart), then describe Taler features involved in the +interaction, the decisions you (the merchant) must make, and +how to configure the Taler merchant backend to best support those decisions. +Lastly, we present protocol *traces* for various fictitious interaction flows. + + +Two Basic Flows +=============== + +.. index:: shopping cart experience +.. index:: individual product selection / purchase experience +.. index:: inventory management +.. index:: repurchase detection / prevention + +There are two basic payment flows, the first involving a shopping cart, +and the second, without (individual product selection / purchase). +We distinguish these because for some purchases, a shopping cart is overkill. +In either case, Taler can integrate +with your *inventory management* system. +Additionally, Taler offers *repurchase detection / prevention*, +most suitable for digital goods. + +In the shopping cart experience, you first offer a product on the website. +The customer adds the product to their shopping cart, at which point you may +optionally *lock* the product in the inventory system for a certain period of +time. +The accumulated set of products in the shopping cart is the *order*. +This process repeats until the customer is ready to move to the +*checkout* phase. + +At checkout, you may optionally support different payment methods (and make +this choice available to the customer) for the order. +This guide assumes you and the customer agree to use the Taler payment system. + +At this point, you generate a *contract* and present it to the customer for +authorization. +The contract includes: + +- the total amount due; +- a short summary; +- a *fulfillment URI*; +- the *duration* of the offer (how long the customer has to authorize before timeout); +- (optional) an itemized product list, with: + + - (optional) some kind of identification for the selected product(s); + +- (optional) applicable taxes and fee limits; +- (optional) an order ID (if omitted, the backend will auto-generate one); +- (optional) information which details are *forgettable*; +- (optional) a *claim token* that the customer can use later; +- (optional) information on the *refund deadline*; +- (optional) information on the *auto-refund period* (how long does the wallet check for refunds without user prompting for it). + +If the customer does nothing (timeout / the contract expires), +the merchant backend automatically *unlocks* the product(s), +allowing other consumers to add more items of the limited stock +to their orders. + +On the other hand, if the customer authorizes payment, +the customer's wallet transfers payment coins to you, +previously locked products are removed from inventory, +and (if possible) the wallet redirects the customer +to the *fulfillment URI*. + +The individual product selection / purchase experience is like the shopping +cart experience with the following exceptions: +- there is no shopping cart -- the order is solely the selected product; +- Taler payment method is assumed; +- customer selection moves directly to checkout; +- *repurchase detection / prevention* can be useful (for digital products). + + +Taler Details +============= + +This section describes aspects of Taler involved +in the basic payment flows in more detail. +Each aspect also includes one or more backend API calls that +are demonstrated in the next section. + +**product locking** + Taler can integrate with your inventory system to set aside + a certain quantity of a product for some duration of time. + This is called *product locking*. + This is useful for physical goods, or for goods that have a limited supply, + such as airline tickets. + Even for digital goods, product locking may be useful to effect exclusivity. + + To lock a product, use: + :http:post:`[/instances/$INSTANCE]/private/products/$PRODUCT_ID/lock`, + specifying a ``duration`` and a ``quantity``. + + If the customer removes a product from the shopping cart, you can *unlock* + the product by using the same API call, specifying a ``quantity`` of 0 (zero). + (Products are also unlocked automatically on timeout / contract expiration.) + + Before you can lock products, you need to manage the inventory, creating + an entry for the product (assigning a $PRODUCT_ID) and configure the + available stock. This can be done using the + Taler merchant backoffice Web interface. + + .. note:: + + Once we have documentation for that web interface, we should link to it here. + +**taxes** + The default taxes for each product is part of the product ``price`` + maintained by the backend. + Taxes can be set when the product is added to the inventory, + prior to any customer purchase experience + (see :http:post:`[/instances/$INSTANCE]/private/products`, + :http:get:`[/instances/$INSTANCE]/private/products`, + and :http:get:`[/instances/$INSTANCE]/private/products/$PRODUCT_ID`) + or specified explicitly by the frontend when adding + products to an order that are not managed by the backend inventory + (see :http:post:`[/instances/$INSTANCE]/private/orders`). + +**fees** + The Taler protocol charges a *deposit fee* (see step 5, above), + which you may choose to pay or to pass on to the customer. + This can be configured to a maximum amount, per order. + + You can set ``default_max_deposit_fee`` in :http:post:`/management/instances`, + or override the default by setting ``max_fee`` when creating an order. + + There is also the *wire fee* (see step 6, above), + which you may choose to pay or to pass on to the customer. + + You can set ``default_max_wire_fee`` in :http:post:`/management/instances`, + and ``max_wire_fee`` in the contract. + If unspecified, the default value is zero (meaning you bear the entire fee). + + You can *amortize* the wire fee across a number of customers + by setting ``default_wire_fee_amortization`` in :http:post:`/management/instances`, + and ``wire_fee_amortization`` in the contract. + This is the number of customer transactions over which you expect to + amortize wire fees on average. + If unspecified, the default value is one. + + .. Note:: :http:post:`/management/instances` must be done at + instance-setup time (prior to any purchase). + +**forgettable customer details** + Although Taler allows the customer to remain anonymous, you may need to + collect customer details (e.g. for shipping). + Taler has support for forgetting such details, to comply with GDPR + (for example). + This can occur even in the face of refunds (see below). + + To forget a set of details, first the details that are to be forgotten + must be marked by including the names of the respective fields + in one or more special ``_forgettable`` field(s) in the contract. + + Then, you can use: + :http:patch:`[/instances/$INSTANCE]/private/orders/$ORDER_ID/forget` + to forget those details. + +**claim token** + The claim token is a sort of handle on the order and its payment. + It is useful when the order ID is easily guessable + (e.g. incrementing serial number), + to prevent one customer hijacking the order of another. + On the other hand, even if the order ID is not easily guessable, + if you don't care about order theft (e.g. infinite supply, digital goods) + and you wish to reduce the required processing (e.g. smaller QR code), + you can safely disable the claim token. + + By default, Taler creates a claim token for each order. + To disable this, you can specify ``create_token`` to be ``false`` + in :http:post:`[/instances/$INSTANCE]/private/orders`. + +**refund deadline** + The refund deadline specifies the time after which you will prohibit + refunds. + Refunds may be full or partial. + Refunds do not require customer details. + You can configure the deadline to expire immediately to effect + an "all sales are final" policy. + + To set the deadline, specify ``refund_delay`` + in :http:post:`[/instances/$INSTANCE]/private/orders`. + To disable refunds altogether, omit this field. + +**auto-refund period** + The Taler protocol can automatically offer refunds to the customer's + wallet without their explicit prompting during the auto-refund period. + + This is useful in the case where the purchase cannot be fulfilled + (e.g. jammed vending machine), but there is no way to notify the + customer about a refund. + + If specified, after contract authorization, the customer's wallet will + repeatedly check for either fulfillment or refund, up to the end of + the auto-refund period. + (If neither occur within that period, the customer should complain + to you for breach of contract.) + + To set the auto-refund period, specify ``auto_refund`` + in :http:post:`[/instances/$INSTANCE]/private/orders`. + +**repurchase detection / prevention** + Taler can detect a repurchase attempt and prevent it from going through. + This feature allows customers to purchase a digital good only once, + but to later access the same digital good repeatedly (e.g. reload + in browser, after network trouble, etc.) without having to pay again. + + This feature is automatic in the protocol; + you do not need to do anything to enable it. + + .. note:: + For repurchase detection / prevention to work reliably, + you must use the same fulfillment URI for the same product + and likewise different fulfillment URIs for different products. + +**fulfillment URI** + This may be the actual product (digital goods), + or a tracking URL (physical goods). + If you issue a claim token with the contract, the customer can + access the fulfillment URI from a different device than the + one where the wallet is installed. + + The fulfillment URI is normally included in the contract. + You specify it in :http:post:`[/instances/$INSTANCE]/private/orders`. + + If the fulfillment URI contains the literal string ``${ORDER_ID}`` + (including curly braces), that will be replaced by the order ID when + POSTing to the merchant. (FIXME: What does "POSTing to the merchant" mean?) + This is useful when the backend auto-generates the order ID. + + +Sample Interaction Traces +========================= + +In the following descriptions, ``C`` stands for *customer*, ``W`` stands for +*customer's wallet*, ``M`` stands for *merchant* (you), and ``E`` stands for +*exchange*. +Unless otherwise noted, all API calls are directed toward the Taler backend. + +Also, all the traces share the initial pre-sales configuration step. + + +Pre-Sales Configuration +----------------------- + +In the pre-sales configuration step, you set up the *default instance*, +and add products to the inventory. + +NOTE: not sure we want to ultimately document this HERE. Most merchants +should do _this_ part via the Merchant Web interface that Sebastian is +building right now, and for that we want a separate guide that explains +the API (as you do here), and the Web interface. In this document, +we should focus on how the merchant integrates the (Web)front-end with the +backend, not how the backend itself is configured. +(This also applies to the other instance setup parts you described +above => refer to other guide, but of course specify how we can +override defaults from instance setup per-order.) + + +M: :http:post:`/management/instances` + +.. code-block:: javascript + + // InstanceConfigurationMessage + { + "accounts": [{"payto_uri":"payto://iban/CH9300762011623852957"}], + "id": "default", + "name": "Pretty Pianos", + "auth": + // InstanceAuthConfigurationMessage + { + "method": "external", + "token": "secret-token:eighty-eight-keys" + }, + "default_max_wire_fee": "KUDOS:5.0", + "default_wire_fee_amortization": 1, + "default_max_deposit_fee": "KUDOS:10.0", + "default_wire_transfer_delay": "2 days", + "default_pay_delay": "5 hours" + } + // (backend returns 204 No content) + +The fictitious store, Pretty Pianos, has only two products: +- pianos (physical good); +- *Beethoven Sonatas* (sheet music PDF files, digital good). + +M: POST ``/instances/default/private/products`` + +.. code-block:: javascript + + // ProductAddDetail + { + "product_id": "p001", + "description": "piano", + "unit": "unit", + "image": "data:image/png;base64,AAA=", + "price": "KUDOS:20000.0", + "taxes": [], + "total_stock": 3, + "next_restock": "2021-04-22", + "_forgettable": ["image"] + } + // (backend returns 204 No content) + +Note that the ``image`` field is mentioned by name in the ``_forgettable`` +field's list value. +This means the ``image`` value is *marked as forgettable*. +This will come into play later (see below). + +M: POST ``/instances/default/private/products`` + +.. code-block:: javascript + + // ProductAddDetail + { + "product_id": "f001", + "description": "Beethoven Sonatas", + "unit": "file", + "price": "KUDOS:9.87", + "taxes": [], + "total_stock": -1 + } + // (backend returns 204 No content) + +Note that there is no ``next_restock`` field in this ``ProductAddDetail`` +object. +This is because the ``total_stock`` field has value ``-1`` (meaning "infinite") +since the product is a PDF file. + + +Scenario 1: Simple File Purchase +-------------------------------- + +The first scenario is a simple file purchase, without shopping cart, +similar to the `GNU Essay demo <https://shop.demo.taler.net/en/>`_ experience. + +.. We hardcode "en/" for now because support for other + languages is not yet available (at time of writing). + (FIXME: Drop "en/" when other languages are supported.) + +Because there are infinite supplies of product ``f001``, +there is really no need for inventory management. +However, you choose to anyway generate a separate order ID +in the backend for accounting purposes. +Also, since the product is an easily reproduced digital good, +you decline to offer the customer the ability to select a "quantity" +other than 1 (one), and decide that "all sales are final" +(no refund possible). +On the other hand, you wish to enable repurchase detection / +prevention feature, so that once customers pay for the PDF file, +they need never pay again for it. + +When the customer clicks on the product's "buy" button, +you first POST to ``/private/orders`` to create an order: + +M: POST ``/instances/default/private/orders`` + +.. code-block:: javascript + + // PostOrderRequest + { + "order": + // Order (MinimalOrderDetail) + { + "amount": "KUDOS:9.87", + "summary": "Beethoven Sonatas", + "fulfillment_URI": "https://example.com/f001?${ORDER_ID}" + }, + "create_token": true + } + +Notes: + +- There is no ``refund_delay`` field (no refunds possible). +- We show the ``create_token`` field with value ``true`` even though that is the default (for illustrative purposes). +- The ``order`` value is actually a ``MinimalOrderDetail`` object. +- The ``fulfillment_URI`` value includes the product ID and the literal string ``${ORDER_ID}``, to be replaced by the backend-generated order ID. + +The backend returns ``200 OK`` with the body: + +.. code-block:: javascript + + // PostOrderResponse + { + "order_id": "G93420934823", + "token": "TEUFHEFBQALK" + } + +Notes: +- The backend-generated order ID is ``G93420934823``. +- The claim token is ``TEUFHEFBQALK``. + +(FIXME: Replace w/ more realistic examples?) + +Now that there is an order in the system, the wallet *claims* the order. + +W: POST ``/orders/G93420934823/claim`` + +.. code-block:: javascript + + // ClaimRequest + { + "nonce": "lksjdflaksjfdlaksjf", + "token": "TEUFHEFBQALK" + } + +Notes: + +- The ``nonce`` value is a randomly-generated string. +- The POST endpoint includes the order ID ``G93420934823``. +- The ``token`` value is the claim token ``TEUFHEFBQALK`` received in the ``PostOrderResponse``. + +The backend returns ``200 OK`` with body: + +.. code-block:: javascript + + // ContractTerms + { + "summary": "one copy of Beethoven Sonatas", + "order_id": "G93420934823", + "amount": "KUDOS:9.87000000", + "fulfillment_url": "https://example.com/f001?G93420934823", + "max_fee": "KUDOS:0.01500000", + "max_wire_fee": "KUDOS:0.01500000", + "wire_fee_amortization": 1, + "products": [ + // Product + { + "product_id": "f001", + "description": "Beethoven Sonatas" + } + ], + "timestamp": { "t_ms": 1616537665000 }, + "refund_deadline": { "t_ms": 1616537665000 }, + "pay_deadline": { "t_ms": 1616537725000 }, + "wire_transfer_deadline": { "t_ms": 1616537785000 }, + "merchant_pub": FIXME, + "merchant_base_url": "https://example.com/", + "merchant": + // Merchant + { + }, + "h_wire": FIXME, + "wire_method": FIXME, + "auditors": [ + // Auditor + ], + "exchanges": [ + // Exchange + ], + "nonce": "lksjdflaksjfdlaksjf" + } + +Notes: + +- The backend determined both fees to be 0.015 KUDOS. + Because the amortization is 1 (one), both fees (processing and wire + transfer) are included in full. + Thus, the total due by the customer is 9.87 + 0.015 + 0.015 = 9.900 KUDOS. +- The ``order_id`` value is the one given in the ``PostOrderResponse``. +- The ``timestamp`` value represents 2021-03-23 22:14:25 UTC + in milliseconds after the `epoch <https://en.wikipedia.org/wiki/Unix_epoch>`__. +- The ``refund_deadline`` value is the same as the ``timestamp`` value + (no refunds possible). +- The ``pay_deadline`` value is one minute after the ``timestamp`` value. +- The ``wire_transfer_deadline`` value is two minutes after + the ``timestamp`` value. +- The ``products`` value is a list of one element (one ``Product`` object), + which omits the ``price`` field since that is included in the + ``ContractTerms.amount`` value. Also, its ``quantity`` defaults to 1 (one). +- The ``nonce`` value is the same one specified by the wallet. + +At this point, the wallet displays the contract terms (or a subset of them) +to the customer, who now has the option to accept the contract or reject it +(either explicitly by pressing a "cancel" button, or implicitly by waiting +for the offer to time out). + +The customer accepts the contract: + +W: POST ``/orders/G93420934823/pay`` + +.. code-block:: javascript + + // PayRequest + { + "coins": [ + // CoinPaySig + { + "coin_sig": ..., + "coin_pub": ..., + "ub_sig": ..., + "h_denom": ..., + "contribution": "KUDOS:8.0", + "exchange_url": ... + }, + { + "coin_sig": ..., + "coin_pub": ..., + "ub_sig": ..., + "h_denom": ..., + "contribution": "KUDOS:2.0", + "exchange_url": ... + } + ] + } + +Notes: + +- There is no session ID in the ``PayRequest`` object. +- The total of the contribution is 8.0 + 2.0 = 10.0 KUDOS, + which is enough to cover the purchase price (9.900 KUDOS + from 9.87 + 0.015 + 0.015). + +The backend returns ``200 OK`` with body: + +.. code-block:: javascript + + // PaymentResponse + { + "sig": "..." // EddsaSignature + } + +FIXME: At this point, does the wallet need to query (status)? +Also, does the frontend need to do anything else? + +The wallet then redirects to the fulfillment URI, which displays +(or makes available for download) the PDF file "Beethoven Sonatas". + + + + +TODO/FIXME: Add more scenarios (including JSON). |