.. 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 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with TALER; see the file COPYING. If not, see @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 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:`/private/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:`/private/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:`/private/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:`/private/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, the customer's wallet will wait that long after authorization (and before fulfillment) before automatically receiving a refund. 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`. NOTE: explain hack to have the backend include the order ID in the fulfillment URI somewhere (useful if the backend auto-generates an 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:`/private/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 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, "next_restock": "0000-00-00" } // (backend returns 204 No content) Scenarios --------- TODO/FIXME: Add various scenarios (including JSON).