diff options
Diffstat (limited to 'taler-mcig.rst')
-rw-r--r-- | taler-mcig.rst | 563 |
1 files changed, 0 insertions, 563 deletions
diff --git a/taler-mcig.rst b/taler-mcig.rst deleted file mode 100644 index c69d838c..00000000 --- a/taler-mcig.rst +++ /dev/null @@ -1,563 +0,0 @@ -.. - 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 - { - "payto_uris": ["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: :http: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: :http: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: :http: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: :http: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: :http: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). |