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).