summaryrefslogtreecommitdiff
path: root/orphaned/taler-mcig.rst
diff options
context:
space:
mode:
Diffstat (limited to 'orphaned/taler-mcig.rst')
-rw-r--r--orphaned/taler-mcig.rst563
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).