summaryrefslogtreecommitdiff
path: root/api/integration-merchant.rst
diff options
context:
space:
mode:
authorMarcello Stanisci <marcello.stanisci@inria.fr>2016-12-26 18:42:18 +0100
committerMarcello Stanisci <marcello.stanisci@inria.fr>2017-01-27 10:42:46 +0100
commit735fb8a89f51875196783b167e8ae622368808fc (patch)
tree39db1fdaa9fc253abcbdddc641ce6aee598f545b /api/integration-merchant.rst
parentf248c701bbb16af1a540d56895a772c6748b80a9 (diff)
downloaddocs-735fb8a89f51875196783b167e8ae622368808fc.tar.gz
docs-735fb8a89f51875196783b167e8ae622368808fc.tar.bz2
docs-735fb8a89f51875196783b167e8ae622368808fc.zip
Still on splitting
Diffstat (limited to 'api/integration-merchant.rst')
-rw-r--r--api/integration-merchant.rst315
1 files changed, 315 insertions, 0 deletions
diff --git a/api/integration-merchant.rst b/api/integration-merchant.rst
new file mode 100644
index 00000000..f3221617
--- /dev/null
+++ b/api/integration-merchant.rst
@@ -0,0 +1,315 @@
+..
+ This file is part of GNU TALER.
+
+..
+ Note that this page is more a protocol-explaination than a guide that teaches
+ merchants how to work with Taler wallets
+
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ 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 <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+ @author Christian Grothoff
+
+==================================
+Interaction with merchant websites
+==================================
+
+.. _payprot:
+
++++++++++++++++++++
+The payment process
++++++++++++++++++++
+
+By design, the Taler payment process ensures the following properties:
+
+1. The user must see and accept a contract in a secure context before the payment happens.
+ That contract accounts for all the items which are supposed to be bought.
+
+2. The payment process must be idempotent, that is at any later time the customer must
+ be able to replay the payment and retrieve the resource he paid for.
+ In case where a physical item was bought, this online resource is the merchant's
+ order status page, which may contain tracking information for the customer.
+ Note that by `replaying the payment` we mean reusing the `same coins` used to pay for
+ the product the first time to get the `same product` the user got the first time.
+ So the replay does NOT subtract further credit from the user's total budget.
+
+3. Purchases are shareable: any purchase is given a URL that allows other users to
+ buy the same item(s).
+
+We call an *offer URL* any URL at the merchant's Web site that notifies the
+wallet that the user needs to pay for something. The offer URL must take into
+account that the user has no wallet installed, and manage the situation accordingly
+(for example, by showing a credit card paywall). The notification can happen either
+via JavaScript or via HTTP headers.
+
+The merchant needs to have a *contract URL* which generates the JSON
+contract for Taler. Alternatively, the contract may be embedded
+within the page returned by the offer URL and given to the wallet
+via JavaScript or via an HTTP header.
+
+The merchant must also provide a *pay URL* to which the wallet can
+transmit the payment. Again, how this URL is made known from the merchant
+to the wallet, it is managed by the HTTP headers- or JavaScript-based protocol.
+
+The merchant must also have a *fulfillment URL*, that addresses points 2 and 3 above.
+In particular, fulfillment URL is responsible for:
+
+* Deliver the final product to the user after the payment
+* Instruct the wallet to send the payment to the pay URL
+* Redirect the user to the offer URL in case they hit a shared fulfillment URL.
+
+Again, Taler provides two ways of doing that: JavaScript- and HTTP headers-based.
+
+Taler helps merchants on the JavaScript-based interaction by providing the
+``taler-wallet-lib``. See https://git.taler.net/web-common.git/tree/taler-wallet-lib.ts
+
+-------
+Example
+-------
+
+For example, suppose Alice wants to pay for a movie. She will first
+select the movie from the catalog, which directs her to the offer URL
+*https://merchant/offer?x=8ru42*. This URL generates a "402 Payment
+Required" response, and will instruct the wallet about the contract's
+URL. Then the wallet downloads the contract that states that Alice is
+about to buy a movie. The contract includes a fresh transaction ID, say 62.
+Alice's browser detects the response code and displays the contract
+for Alice.
+
+Alice then confirms that she wants to buy the movie. Her wallet
+associates her confirmation with the details and a hash of the contract.
+After Alice confirms, the wallet redirects her to the fulfillment URL, say
+*https://merchant/fulfillment?x=8ru42&tid=62* that is specified in the
+contract.
+
+The first time Alice visits this URL, the merchant will again
+generate a "402 Payment Required" response, this time not including
+the full contract but merely the hash of the contract (which includes
+Alice's transaction ID 62), as well as the offer URL (which Alice
+will ignore) and the pay URL. Alice's wallet will detect that
+Alice already confirmed that she wants to execute this particular
+contract. The wallet will then transmit the payment to the pay URL,
+obtain a response from the merchant confirming that the payment was
+successful, and then reload the fulfillment URL.
+
+This time (and every time in the future where Alice visits the
+fulfillment URL), she receives the movie. If the browser has lost the
+session state, the merchant will again ask her to pay (as it happened the
+very first time she visited the fulfillment URL), and she will authenticate
+by replaying the payment.
+
+If Alice decides to share the fulfillment URL with Bob and he visits
+it, his browser will not have the right session state and furthermore
+his wallet will not be able to replay the payment. Instead, his wallet
+will automatically redirect Bob to the offer URL and allow him to
+purchase the movie himself.
+
+.. _offer:
+
+---------------
+Making an offer
+---------------
+
+When a user visits a offer URL, the merchant returns a page that can interact
+with the wallet either via JavaScript or by returning a "402 Payment Required".
+This page's main objective is to inform the wallet on where it should get the
+contract. In case of JavaScript interaction, the merchant should just return
+a page whose javascript contains an invocation to ``offerContractFrom(<CONTRACT-URL>)``
+from ``taler-wallet-lib``. This function will download the contract from
+`<CONTRACT-URL>` and hand it to the wallet.
+
+In case of HTTP headers-based protocol, the merchant needs to set the header
+`X-Taler-contract-url` to the contract URL. Once this information reaches the
+browser, the wallet will takes action by reading that header and downloading
+the contract.
+
+Either way, the contract gets to the wallet which then renders it to the user.
+
+.. _fulfillment:
+
+-------------------------------
+Fulfillment interaction details
+-------------------------------
+
+A payment process is triggered whenever the user visits a fulfillment
+URL and he has no rights in the session state to get the items
+accounted in the fulfillment URL. Note that after the user accepts a
+contract, the wallet will automatically point the browser to the
+fulfillment URL.
+
+Becasue fulfillment URLs implements replayable and shareable payments
+(see points 2,3 above), fulfillment URL parameter must encompass all the
+details necessary to reconstruct a contract.
+
+That saves the merchant from writing contracts to disk upon every contract
+generation, and defer this operation until customers actually pay.
+
+..................
+HTTP headers based
+..................
+
+Once the fulfillment URL gets visited, deliver the final product if the user has
+paid, otherwise: the merchant will reconstruct the contract and re-hash it, sending
+back to the client a "402 Payment required" status code and some HTTP headers which
+will help the wallet to manage the payment. Namely:
+
+* `X-taler-contract-hash`
+* `X-taler-pay-URL`
+* `X-taler-offer-URL`
+
+The wallet then looks at `X-taler-contract-hash`, and can face two situations:
+
+1. This hashcode is already present in the wallet's database (meaning that the user did accept the related contract), so the wallet can send the payment to `X-taler-pay-URL`. During this operation, the wallet associates the coins it sent to `X-taler-pay-URL` with this hashcode, so that it can replay payments whenever it gets this hashcode again.
+
+2. This hashcode is unknown to the wallet (meaning that the user visited a shared fulfillment URL). The wallet then points the browser to `X-taler-offer-URL`, which is in charge of generating a contract referring to the same items accounted in the fulfillment URL. Of course, the user is then able to accept or not the contract.
+
+................
+JavaScript based
+................
+
+Once the fulfillment URL gets visited, deliver the final product if the user has paid, otherwise:
+the merchant will reconstruct the contract and re-hash it. Then it will return a page whose JavaScript
+needs to include a call to ``taler.executeContract(..)``. See the following example:
+
+.. sourcecode:: html
+
+ <html>
+ <head>
+ <script src="path/to/taler-wallet-lib.js"></script>
+ <script type="application/javascript">
+ // Imported from taler-wallet-lib.js
+ taler.executePayment(<CONTRACT-HASHCODE>, <PAY-URL>, <OFFERING-URL>);
+ </script>
+ </head>
+ ..
+
+ </html>
+
+The logic which will take place is the same as in the HTTP header based protocol.
+Once ``executePayment(..)`` gets executed in the browser, it will hand its three
+parameters to the wallet, which will:
+
+1. Send the payment to `<PAY-URL>` if `<CONTRACT-HASH>` is found in its database (meaning that the user accepted it).
+2. Redirect the browser to `<OFFER-URL>`, if `<CONTRACT-HASH>` is NOT found in its database, meaning that the user visited a shared fulfillment URL.
+
+--------------------
+Example: Essay Store
+--------------------
+
+This section is a high-level description of a merchant :ref:`frontend <merchant-arch>`,
+and is inspired by our demonstration essay store running at `https://blog.demo.taler.net/`.
+Basically, it tells how the frontend reacts to clients visiting `offer` and `fulfillment`
+URLs.
+
+The website is implemented in Python+Flask, and is available at
+https://git.taler.net/merchant-frontends.git/tree/talerfrontends/blog.
+
+The desired effect is that the homepage has a list of buyable articles, and once the
+user clicks on one of them, they will either get the Taler :ref:`contract <contract>`
+or a credit card paywall if they have no Taler wallet installed.
+
+In particular, any buyable article on the homepage links to an `offer URL`:
+
+.. sourcecode:: html
+
+ <html>
+ ...
+ <h3><a href="/essay/How_to_write_a_frontend">How to write a frontend</a></h3>
+ ...
+ </html>
+
+whence the offer URL design is as follows::
+
+ https://<BASEURL>/essay/<ARTICLE-NAME>
+
+`<ARTICLE-NAME>` is just a token that uniquely identifies the article within the shop.
+
+The server-side handler for the offer URL will return a special page to the client that
+will either HTTP GET the contract from the frontend, or show the credit card paywall.
+See `above <offer>`_ how this special page works.
+
+It is interesting to note that the fulfillment URL is just the offer URL plus
+two additional parameters. It looks as follows::
+
+ https://<BASEURL>/essay/<ARTICLE-NAME>?tid=<TRANSACTION-ID>&timestamp=<TIMESTAMP>
+
+.. note::
+
+ Taler does not require that offer and fulfillment URL have this kind of relationship.
+ In fact, it is perfectly acceptable for the fulfillment URL to be hosted on a different
+ server under a different domain name.
+
+The fulfillment URL server-side handler implements the following logic: it checks the state
+to see if `<ARTICLE-NAME>` has been payed, and if so, returns the article to the user.
+If the user didn't pay, then it `executes` the contract by returning a special page to the
+browser. The contract execution is the order to pay that the frontend gives to the wallet.
+
+Basically, the frontend points the wallet to the hashcode of the contract which is to be paid
+and the wallet responds by giving coins to the frontend. Because the frontend doesn't perform
+any cryptographic work by design, it forwards `<ARTICLE-NAME>`, `<TRANSACTION-ID>` and
+`<TIMESTAMP>` to the frontend in order to get the contract's hashcode.
+
+See `above <fulfillment>`_ for a detailed description of how the frontend triggers the
+payment in the wallet.
+
+..................
+State and security
+..................
+
+The server-side state gets updated in two situations, (1) when an article is
+"about" to be bought, which means when the user visits the fulfillment URL,
+and (2) when the user actually pays. For (1), we use the contract hascode to
+access the state, whereas in (2) we just define a list of payed articles.
+For example:
+
+.. sourcecode:: python
+
+ session[<HASHCODE>] = {'article_name': 'How_to_write_a_frontend'} # (1)
+ session['payed_articles'] = ['How_to_write_a_frontend', 'How_to_install_a_backend'] # (2)
+
+The list of payed articles is used by the frontend to deliver the article to the user:
+if the article name is among ``session['payed_articles']``, then the user gets what they
+paid for.
+
+The reason for using `<HASHCODE>` as the key is to prevent the wallet to send bogus
+parameters along the fulfillment URL. `<HASHCODE>` is the contract hashcode that
+the fulfillment handler gets from the backend using the fulfillment URL parameters.
+
+In fact, when the wallet sends the payment to the frontend pay handler, it has to provide
+both coins and contract hashcode. That hascode is (1) verified by the backend when it
+receives the coins, (2) used by the frontend to update the list of payed articles.
+
+See below an example of pay handler:
+
+.. sourcecode:: python
+
+ ...
+
+ # 'deposit_permission' is the JSON object sent by the wallet
+ # which contains coins and the contract hashcode.
+ response = send_payment_to_backend(deposit_permission)
+
+ # The backend accepted the payment
+ if 200 == response.status_code:
+ # Here we pick the article name from the state defined at
+ # fulfillment time.
+ # deposit_permission['H_contract'] is the contract hashcode
+ payed_article = session[deposit_permission['H_contract']]['article_name']
+ session['payed_articles'].append(payed_article)
+
+
+So the wallet is forced to send a valid contract hashcode along the payment,
+and since that hashcode is then used to update the list of payed articles,
+the wallet is forced to send fulfillment URL parameters that match that hashcode,
+therefore being valid parameters.