taler-docs

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

taler-mcig.rst (20176B)


      1 ..
      2   This file is part of GNU TALER.
      3   Copyright (C) 2021 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 2.1, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 
     16   @author Thien-Thi Nguyen
     17 
     18 
     19 Merchant/Customer Interaction Guide
     20 ###################################
     21 
     22 The audience for the Mechant/Customer Interaction Guide is the merchant
     23 who wishes to set up a "web shop" that works with the Taler payment system.
     24 
     25 
     26 Introduction
     27 ============
     28 
     29 .. include:: frags/taler-payment-cycle.rst
     30 
     31 This guide focuses on step 4, the interaction between the customer and the
     32 merchant.  In particular, we first review two basic interaction flows
     33 (with and without shopping cart), then describe Taler features involved in the
     34 interaction, the decisions you (the merchant) must make, and
     35 how to configure the Taler merchant backend to best support those decisions.
     36 Lastly, we present protocol *traces* for various fictitious interaction flows.
     37 
     38 
     39 Two Basic Flows
     40 ===============
     41 
     42 .. index:: shopping cart experience
     43 .. index:: individual product selection / purchase experience
     44 .. index:: inventory management
     45 .. index:: repurchase detection / prevention
     46 
     47 There are two basic payment flows, the first involving a shopping cart,
     48 and the second, without (individual product selection / purchase).
     49 We distinguish these because for some purchases, a shopping cart is overkill.
     50 In either case, Taler can integrate
     51 with your *inventory management* system.
     52 Additionally, Taler offers *repurchase detection / prevention*,
     53 most suitable for digital goods.
     54 
     55 In the shopping cart experience, you first offer a product on the website.
     56 The customer adds the product to their shopping cart, at which point you may
     57 optionally *lock* the product in the inventory system for a certain period of
     58 time.
     59 The accumulated set of products in the shopping cart is the *order*.
     60 This process repeats until the customer is ready to move to the
     61 *checkout* phase.
     62 
     63 At checkout, you may optionally support different payment methods (and make
     64 this choice available to the customer) for the order.
     65 This guide assumes you and the customer agree to use the Taler payment system.
     66 
     67 At this point, you generate a *contract* and present it to the customer for
     68 authorization.
     69 The contract includes:
     70 
     71 - the total amount due;
     72 - a short summary;
     73 - a *fulfillment URI*;
     74 - the *duration* of the offer (how long the customer has to authorize before timeout);
     75 - (optional) an itemized product list, with:
     76 
     77   - (optional) some kind of identification for the selected product(s);
     78 
     79 - (optional) applicable taxes and fee limits;
     80 - (optional) an order ID (if omitted, the backend will auto-generate one);
     81 - (optional) information which details are *forgettable*;
     82 - (optional) a *claim token* that the customer can use later;
     83 - (optional) information on the *refund deadline*;
     84 - (optional) information on the *auto-refund period* (how long does the wallet check for refunds without user prompting for it).
     85 
     86 If the customer does nothing (timeout / the contract expires),
     87 the merchant backend automatically *unlocks* the product(s),
     88 allowing other consumers to add more items of the limited stock
     89 to their orders.
     90 
     91 On the other hand, if the customer authorizes payment,
     92 the customer's wallet transfers payment coins to you,
     93 previously locked products are removed from inventory,
     94 and (if possible) the wallet redirects the customer
     95 to the *fulfillment URI*.
     96 
     97 The individual product selection / purchase experience is like the shopping
     98 cart experience with the following exceptions:
     99 - there is no shopping cart -- the order is solely the selected product;
    100 - Taler payment method is assumed;
    101 - customer selection moves directly to checkout;
    102 - *repurchase detection / prevention* can be useful (for digital products).
    103 
    104 
    105 Taler Details
    106 =============
    107 
    108 This section describes aspects of Taler involved
    109 in the basic payment flows in more detail.
    110 Each aspect also includes one or more backend API calls that
    111 are demonstrated in the next section.
    112 
    113 **product locking**
    114   Taler can integrate with your inventory system to set aside
    115   a certain quantity of a product for some duration of time.
    116   This is called *product locking*.
    117   This is useful for physical goods, or for goods that have a limited supply,
    118   such as airline tickets.
    119   Even for digital goods, product locking may be useful to effect exclusivity.
    120 
    121   To lock a product, use:
    122   :http:post:`[/instances/$INSTANCE]/private/products/$PRODUCT_ID/lock`,
    123   specifying a ``duration`` and a ``quantity``.
    124 
    125   If the customer removes a product from the shopping cart, you can *unlock*
    126   the product by using the same API call, specifying a ``quantity`` of 0 (zero).
    127   (Products are also unlocked automatically on timeout / contract expiration.)
    128 
    129   Before you can lock products, you need to manage the inventory, creating
    130   an entry for the product (assigning a $PRODUCT_ID) and configure the
    131   available stock. This can be done using the
    132   Taler merchant backoffice Web interface.
    133 
    134   .. note::
    135 
    136      Once we have documentation for that web interface, we should link to it here.
    137 
    138 **taxes**
    139   The default taxes for each product is part of the product ``price``
    140   maintained by the backend.
    141   Taxes can be set when the product is added to the inventory,
    142   prior to any customer purchase experience
    143   (see :http:post:`[/instances/$INSTANCE]/private/products`,
    144   :http:get:`[/instances/$INSTANCE]/private/products`,
    145   and :http:get:`[/instances/$INSTANCE]/private/products/$PRODUCT_ID`)
    146   or specified explicitly by the frontend when adding
    147   products to an order that are not managed by the backend inventory
    148   (see :http:post:`[/instances/$INSTANCE]/private/orders`).
    149 
    150 **fees**
    151   The Taler protocol charges a *deposit fee* (see step 5, above),
    152   which you may choose to pay or to pass on to the customer.
    153   This can be configured to a maximum amount, per order.
    154 
    155   You can set ``default_max_deposit_fee`` in :http:post:`/management/instances`,
    156   or override the default by setting ``max_fee`` when creating an order.
    157 
    158   There is also the *wire fee* (see step 6, above),
    159   which you may choose to pay or to pass on to the customer.
    160 
    161   You can set ``default_max_wire_fee`` in :http:post:`/management/instances`,
    162   and ``max_wire_fee`` in the contract.
    163   If unspecified, the default value is zero (meaning you bear the entire fee).
    164 
    165   You can *amortize* the wire fee across a number of customers
    166   by setting ``default_wire_fee_amortization`` in :http:post:`/management/instances`,
    167   and ``wire_fee_amortization`` in the contract.
    168   This is the number of customer transactions over which you expect to
    169   amortize wire fees on average.
    170   If unspecified, the default value is one.
    171 
    172   .. Note:: :http:post:`/management/instances` must be done at
    173      instance-setup time (prior to any purchase).
    174 
    175 **forgettable customer details**
    176   Although Taler allows the customer to remain anonymous, you may need to
    177   collect customer details (e.g. for shipping).
    178   Taler has support for forgetting such details, to comply with GDPR
    179   (for example).
    180   This can occur even in the face of refunds (see below).
    181 
    182   To forget a set of details, first the details that are to be forgotten
    183   must be marked by including the names of the respective fields
    184   in one or more special ``_forgettable`` field(s) in the contract.
    185 
    186   Then, you can use:
    187   :http:patch:`[/instances/$INSTANCE]/private/orders/$ORDER_ID/forget`
    188   to forget those details.
    189 
    190 **claim token**
    191   The claim token is a sort of handle on the order and its payment.
    192   It is useful when the order ID is easily guessable
    193   (e.g. incrementing serial number),
    194   to prevent one customer hijacking the order of another.
    195   On the other hand, even if the order ID is not easily guessable,
    196   if you don't care about order theft (e.g. infinite supply, digital goods)
    197   and you wish to reduce the required processing (e.g. smaller QR code),
    198   you can safely disable the claim token.
    199 
    200   By default, Taler creates a claim token for each order.
    201   To disable this, you can specify ``create_token`` to be ``false``
    202   in :http:post:`[/instances/$INSTANCE]/private/orders`.
    203 
    204 **refund deadline**
    205   The refund deadline specifies the time after which you will prohibit
    206   refunds.
    207   Refunds may be full or partial.
    208   Refunds do not require customer details.
    209   You can configure the deadline to expire immediately to effect
    210   an "all sales are final" policy.
    211 
    212   To set the deadline, specify ``refund_delay``
    213   in :http:post:`[/instances/$INSTANCE]/private/orders`.
    214   To disable refunds altogether, omit this field.
    215 
    216 **auto-refund period**
    217   The Taler protocol can automatically offer refunds to the customer's
    218   wallet without their explicit prompting during the auto-refund period.
    219 
    220   This is useful in the case where the purchase cannot be fulfilled
    221   (e.g. jammed vending machine), but there is no way to notify the
    222   customer about a refund.
    223 
    224   If specified, after contract authorization, the customer's wallet will
    225   repeatedly check for either fulfillment or refund, up to the end of
    226   the auto-refund period.
    227   (If neither occur within that period, the customer should complain
    228   to you for breach of contract.)
    229 
    230   To set the auto-refund period, specify ``auto_refund``
    231   in :http:post:`[/instances/$INSTANCE]/private/orders`.
    232 
    233 **repurchase detection / prevention**
    234   Taler can detect a repurchase attempt and prevent it from going through.
    235   This feature allows customers to purchase a digital good only once,
    236   but to later access the same digital good repeatedly (e.g. reload
    237   in browser, after network trouble, etc.) without having to pay again.
    238 
    239   This feature is automatic in the protocol;
    240   you do not need to do anything to enable it.
    241 
    242   .. note::
    243      For repurchase detection / prevention to work reliably,
    244      you must use the same fulfillment URI for the same product
    245      and likewise different fulfillment URIs for different products.
    246 
    247 **fulfillment URI**
    248   This may be the actual product (digital goods),
    249   or a tracking URL (physical goods).
    250   If you issue a claim token with the contract, the customer can
    251   access the fulfillment URI from a different device than the
    252   one where the wallet is installed.
    253 
    254   The fulfillment URI is normally included in the contract.
    255   You specify it in :http:post:`[/instances/$INSTANCE]/private/orders`.
    256 
    257   If the fulfillment URI contains the literal string ``${ORDER_ID}``
    258   (including curly braces), that will be replaced by the order ID when
    259   POSTing to the merchant.  (FIXME: What does "POSTing to the merchant" mean?)
    260   This is useful when the backend auto-generates the order ID.
    261 
    262 
    263 Sample Interaction Traces
    264 =========================
    265 
    266 In the following descriptions, ``C`` stands for *customer*, ``W`` stands for
    267 *customer's wallet*, ``M`` stands for *merchant* (you), and ``E`` stands for
    268 *exchange*.
    269 Unless otherwise noted, all API calls are directed toward the Taler backend.
    270 
    271 Also, all the traces share the initial pre-sales configuration step.
    272 
    273 
    274 Pre-Sales Configuration
    275 -----------------------
    276 
    277 In the pre-sales configuration step, you set up the *default instance*,
    278 and add products to the inventory.
    279 
    280 NOTE: not sure we want to ultimately document this HERE. Most merchants
    281 should do _this_ part via the Merchant Web interface that Sebastian is
    282 building right now, and for that we want a separate guide that explains
    283 the API (as you do here), and the Web interface.  In this document,
    284 we should focus on how the merchant integrates the (Web)front-end with the
    285 backend, not how the backend itself is configured.
    286 (This also applies to the other instance setup parts you described
    287 above => refer to other guide, but of course specify how we can
    288 override defaults from instance setup per-order.)
    289 
    290 
    291 M: :http:post:`/management/instances`
    292 
    293 .. code-block:: javascript
    294 
    295    // InstanceConfigurationMessage
    296    {
    297      "accounts": [{"payto_uri":"payto://iban/CH9300762011623852957"}],
    298      "id": "default",
    299      "name": "Pretty Pianos",
    300      "auth":
    301      // InstanceAuthConfigurationMessage
    302      {
    303        "method": "external",
    304        "token": "secret-token:eighty-eight-keys"
    305      },
    306      "default_max_wire_fee": "KUDOS:5.0",
    307      "default_wire_fee_amortization": 1,
    308      "default_max_deposit_fee": "KUDOS:10.0",
    309      "default_wire_transfer_delay": "2 days",
    310      "default_pay_delay": "5 hours"
    311    }
    312    // (backend returns 204 No content)
    313 
    314 The fictitious store, Pretty Pianos, has only two products:
    315 - pianos (physical good);
    316 - *Beethoven Sonatas* (sheet music PDF files, digital good).
    317 
    318 M: POST ``/instances/default/private/products``
    319 
    320 .. code-block:: javascript
    321 
    322    // ProductAddDetail
    323    {
    324      "product_id": "p001",
    325      "description": "piano",
    326      "unit": "unit",
    327      "image": "data:image/png;base64,AAA=",
    328      "price": "KUDOS:20000.0",
    329      "taxes": [],
    330      "total_stock": 3,
    331      "next_restock": "2021-04-22",
    332      "_forgettable": ["image"]
    333    }
    334    // (backend returns 204 No content)
    335 
    336 Note that the ``image`` field is mentioned by name in the ``_forgettable``
    337 field's list value.
    338 This means the ``image`` value is *marked as forgettable*.
    339 This will come into play later (see below).
    340 
    341 M: POST ``/instances/default/private/products``
    342 
    343 .. code-block:: javascript
    344 
    345    // ProductAddDetail
    346    {
    347      "product_id": "f001",
    348      "description": "Beethoven Sonatas",
    349      "unit": "file",
    350      "price": "KUDOS:9.87",
    351      "taxes": [],
    352      "total_stock": -1
    353    }
    354    // (backend returns 204 No content)
    355 
    356 Note that there is no ``next_restock`` field in this ``ProductAddDetail``
    357 object.
    358 This is because the ``total_stock`` field has value ``-1`` (meaning "infinite")
    359 since the product is a PDF file.
    360 
    361 
    362 Scenario 1: Simple File Purchase
    363 --------------------------------
    364 
    365 The first scenario is a simple file purchase, without shopping cart,
    366 similar to the `GNU Essay demo <https://shop.demo.taler.net/en/>`_ experience.
    367 
    368 .. We hardcode "en/" for now because support for other
    369    languages is not yet available (at time of writing).
    370    (FIXME: Drop "en/" when other languages are supported.)
    371 
    372 Because there are infinite supplies of product ``f001``,
    373 there is really no need for inventory management.
    374 However, you choose to anyway generate a separate order ID
    375 in the backend for accounting purposes.
    376 Also, since the product is an easily reproduced digital good,
    377 you decline to offer the customer the ability to select a "quantity"
    378 other than 1 (one), and decide that "all sales are final"
    379 (no refund possible).
    380 On the other hand, you wish to enable repurchase detection /
    381 prevention feature, so that once customers pay for the PDF file,
    382 they need never pay again for it.
    383 
    384 When the customer clicks on the product's "buy" button,
    385 you first POST to ``/private/orders`` to create an order:
    386 
    387 M: POST ``/instances/default/private/orders``
    388 
    389 .. code-block:: javascript
    390 
    391    // PostOrderRequest
    392    {
    393      "order":
    394      // Order (MinimalOrderDetail)
    395      {
    396        "amount": "KUDOS:9.87",
    397        "summary": "Beethoven Sonatas",
    398        "fulfillment_URI": "https://example.com/f001?${ORDER_ID}"
    399      },
    400      "create_token": true
    401    }
    402 
    403 Notes:
    404 
    405 - There is no ``refund_delay`` field (no refunds possible).
    406 - We show the ``create_token`` field with value ``true`` even though that is the default (for illustrative purposes).
    407 - The ``order`` value is actually a ``MinimalOrderDetail`` object.
    408 - The ``fulfillment_URI`` value includes the product ID and the literal string ``${ORDER_ID}``, to be replaced by the backend-generated order ID.
    409 
    410 The backend returns ``200 OK`` with the body:
    411 
    412 .. code-block:: javascript
    413 
    414    // PostOrderResponse
    415    {
    416      "order_id": "G93420934823",
    417      "token": "TEUFHEFBQALK"
    418    }
    419 
    420 Notes:
    421 - The backend-generated order ID is ``G93420934823``.
    422 - The claim token is ``TEUFHEFBQALK``.
    423 
    424 (FIXME: Replace w/ more realistic examples?)
    425 
    426 Now that there is an order in the system, the wallet *claims* the order.
    427 
    428 W: POST ``/orders/G93420934823/claim``
    429 
    430 .. code-block:: javascript
    431 
    432    // ClaimRequest
    433    {
    434      "nonce": "lksjdflaksjfdlaksjf",
    435      "token": "TEUFHEFBQALK"
    436    }
    437 
    438 Notes:
    439 
    440 - The ``nonce`` value is a randomly-generated string.
    441 - The POST endpoint includes the order ID ``G93420934823``.
    442 - The ``token`` value is the claim token ``TEUFHEFBQALK`` received in the ``PostOrderResponse``.
    443 
    444 The backend returns ``200 OK`` with body:
    445 
    446 .. code-block:: javascript
    447 
    448    // ContractTerms
    449    {
    450      "summary": "one copy of Beethoven Sonatas",
    451      "order_id": "G93420934823",
    452      "amount": "KUDOS:9.87000000",
    453      "fulfillment_url": "https://example.com/f001?G93420934823",
    454      "max_fee": "KUDOS:0.01500000",
    455      "max_wire_fee": "KUDOS:0.01500000",
    456      "wire_fee_amortization": 1,
    457      "products": [
    458        // Product
    459        {
    460          "product_id": "f001",
    461          "description": "Beethoven Sonatas"
    462        }
    463      ],
    464      "timestamp": { "t_ms": 1616537665000 },
    465      "refund_deadline": { "t_ms": 1616537665000 },
    466      "pay_deadline": { "t_ms": 1616537725000 },
    467      "wire_transfer_deadline": { "t_ms": 1616537785000 },
    468      "merchant_pub": FIXME,
    469      "merchant_base_url": "https://example.com/",
    470      "merchant":
    471        // Merchant
    472        {
    473        },
    474      "h_wire": FIXME,
    475      "wire_method": FIXME,
    476      "auditors": [
    477        // Auditor
    478      ],
    479      "exchanges": [
    480        // Exchange
    481      ],
    482      "nonce": "lksjdflaksjfdlaksjf"
    483    }
    484 
    485 Notes:
    486 
    487 - The backend determined both fees to be 0.015 KUDOS.
    488   Because the amortization is 1 (one), both fees (processing and wire
    489   transfer) are included in full.
    490   Thus, the total due by the customer is 9.87 + 0.015 + 0.015 = 9.900 KUDOS.
    491 - The ``order_id`` value is the one given in the ``PostOrderResponse``.
    492 - The ``timestamp`` value represents 2021-03-23 22:14:25 UTC
    493   in milliseconds after the `epoch <https://en.wikipedia.org/wiki/Unix_epoch>`__.
    494 - The ``refund_deadline`` value is the same as the ``timestamp`` value
    495   (no refunds possible).
    496 - The ``pay_deadline`` value is one minute after the ``timestamp`` value.
    497 - The ``wire_transfer_deadline`` value is two minutes after
    498   the ``timestamp`` value.
    499 - The ``products`` value is a list of one element (one ``Product`` object),
    500   which omits the ``price`` field since that is included in the
    501   ``ContractTerms.amount`` value.  Also, its ``quantity`` defaults to 1 (one).
    502 - The ``nonce`` value is the same one specified by the wallet.
    503 
    504 At this point, the wallet displays the contract terms (or a subset of them)
    505 to the customer, who now has the option to accept the contract or reject it
    506 (either explicitly by pressing a "cancel" button, or implicitly by waiting
    507 for the offer to time out).
    508 
    509 The customer accepts the contract:
    510 
    511 W: POST ``/orders/G93420934823/pay``
    512 
    513 .. code-block:: javascript
    514 
    515    // PayRequest
    516    {
    517      "coins": [
    518        // CoinPaySig
    519        {
    520          "coin_sig": ...,
    521          "coin_pub": ...,
    522          "ub_sig": ...,
    523          "h_denom": ...,
    524          "contribution": "KUDOS:8.0",
    525          "exchange_url": ...
    526        },
    527        {
    528          "coin_sig": ...,
    529          "coin_pub": ...,
    530          "ub_sig": ...,
    531          "h_denom": ...,
    532          "contribution": "KUDOS:2.0",
    533          "exchange_url": ...
    534        }
    535      ]
    536    }
    537 
    538 Notes:
    539 
    540 - There is no session ID in the ``PayRequest`` object.
    541 - The total of the contribution is 8.0 + 2.0 = 10.0 KUDOS,
    542   which is enough to cover the purchase price (9.900 KUDOS
    543   from 9.87 + 0.015 + 0.015).
    544 
    545 The backend returns ``200 OK`` with body:
    546 
    547 .. code-block:: javascript
    548 
    549    // PaymentResponse
    550    {
    551      "sig": "..." // EddsaSignature
    552    }
    553 
    554 FIXME: At this point, does the wallet need to query (status)?
    555 Also, does the frontend need to do anything else?
    556 
    557 The wallet then redirects to the fulfillment URI, which displays
    558 (or makes available for download) the PDF file "Beethoven Sonatas".
    559 
    560 
    561 
    562 
    563 TODO/FIXME: Add more scenarios (including JSON).