diff options
author | Marcello Stanisci <marcello.stanisci@inria.fr> | 2016-12-26 23:27:28 +0100 |
---|---|---|
committer | Marcello Stanisci <marcello.stanisci@inria.fr> | 2017-01-27 10:42:50 +0100 |
commit | 3ee973fe5892dba1384dc4ca97e46148e479febd (patch) | |
tree | 4158d8d40c3adaaa6363d6402a81bae8b7883ad4 /docs | |
parent | 4feb2deebe4118468ef75ac0c7ccf52000e660fe (diff) | |
download | docs-3ee973fe5892dba1384dc4ca97e46148e479febd.tar.gz docs-3ee973fe5892dba1384dc4ca97e46148e479febd.tar.bz2 docs-3ee973fe5892dba1384dc4ca97e46148e479febd.zip |
Moving payment protocol on 'docs'
Diffstat (limited to 'docs')
-rw-r--r-- | docs/integration-bank.rst | 81 | ||||
-rw-r--r-- | docs/integration-general.rst | 82 | ||||
-rw-r--r-- | docs/integration-merchant.rst | 256 |
3 files changed, 419 insertions, 0 deletions
diff --git a/docs/integration-bank.rst b/docs/integration-bank.rst new file mode 100644 index 00000000..1dc2ec8d --- /dev/null +++ b/docs/integration-bank.rst @@ -0,0 +1,81 @@ +============================== +Interaction with bank websites +============================== + +This section describes how bank websites can interact with the +Taler wallet. + +Currently the following functionality is supported: + * Querying for the presence of a Taler wallet. + * Receiving change notifications from the Taler wallet. + * Creating a reserve. + + +For JavaScript code examples, see :ref:`communication`. + +------------------------- +Reserve Creation Request +------------------------- + +The bank website can request the creation of a :term:`reserve`. This operation +will require the user to specify the exchange where he wants to create the reserve +and the resolution of a CAPTCHA, before any action will be taken. + +As a result of the reserve creation request, the following steps will happen in sequence: + 1. The user chooses the desired amount from the bank's form + 2. Upon confirmation, the wallet fetches the desired amount from the user-filled form and + prompts the user for the *exchange base URL*. Then ask the user to confirm creating the + reserve. + 3. The wallet will create a key pair for the reserve. + 4. The wallet will request the CAPTCHA page to the bank. In that request's parameters it + communicates the desired amount, the reserve's public key and the exchange base URL to the + bank + 5. Upon successful resolution of the CAPTCHA by the user, the bank initiates the reserve + creation according to the gotten parameters. Together with `200 OK` status code sent back + to the wallet, it gets also a `ReserveCreated`_ object. + +Note that the reserve creation can be done by a SEPA wire transfer or some other means, +depending on the user's bank and chosen exchange. + +In response to the reserve creation request, the Taler wallet MAY cause the +current document location to be changed, in order to navigate to a +wallet-internal confirmation page. + +The bank requests reserve creation with the ``taler-create-reserve`` event. +The event data must be a `CreateReserveDetail`_: + + +.. _CreateReserveDetail: +.. code-block:: tsref + + interface CreateReserveDetail { + + // JSON 'amount' object. The amount the caller wants to transfer + // to the recipient's count + amount: Amount; + + // CAPTCHA's page URL which needs the following parameters + // query parameters: + // amount_value + // amount_fraction + // amount_currency + // reserve_pub + // exchange + // wire_details (URL encoding of /wire output from the exchange) + callback_url: string; + + // list of wire transfer types supported by the bank + // e.g. "SEPA", "TEST" + wt_types: Array<string> + } + +.. _ReserveCreated: +.. code-block:: tsref + + interface ReserveCreated { + + // A URL informing the user about the succesfull outcome + // of his operation + redirect_url: string; + + } diff --git a/docs/integration-general.rst b/docs/integration-general.rst new file mode 100644 index 00000000..308ecf5a --- /dev/null +++ b/docs/integration-general.rst @@ -0,0 +1,82 @@ +.. _integration-general: + +================================ +Taler Wallet Website Integration +================================ + +.. note:: + The wallet-Websites communication is switching to a new policy which + is NOT based on DOM events, therefore obsoleting this page. To be soon + documented. + + +Websites (such as banks and online shops) can communicate with +the Taler wallet by a standardized protocol. + +From a technical perspective, the Taller wallet communicates with +the website by sending and receiving `DOM events <http://www.w3.org/TR/DOM-Level-3-Events/>`_ +on the bank website's ``HTMLDocument``. + +DOM events used by Taler have the prefix ``taler-``. + +------------------------- +Wallet Presence Awareness +------------------------- + +The bank website queries the wallet's presence by sending a ``taler-probe`` event. The +event data should be `null`. + +If the wallet is present and active, it will respond with a ``taler-wallet-present`` event. + +While the user agent is displaying a website, the user might deactivate or +re-activate the wallet. A Taler-aware *should* react to those events, and +indicate to the user that they should (re-)enable the wallet if necessary. + +When the wallet is activated, the ``taler-wallet-load`` event is sent +by the wallet. When the wallet is deactivated, the ``taler-wallet-unload`` event +is sent by the wallet. + +.. _communication: + +---------------------- +Communication Example +---------------------- + +The bank website can send the event ``taler-XYZ`` with the event data ``eventData`` +to the wallet with the following JavaScript code: + +.. sourcecode:: javascript + + const myEvent = new CustomEvent("taler-XYZ", eventData); + document.dispatchEvent(myEvent); + +Events can be received by installing a listener: + + +.. sourcecode:: javascript + + function myListener(talerEvent) { + // handle event here! + } + document.addEventListener("taler-XYZ", myListener); + + +-------------------- +Normalized Base URLs +-------------------- + +Exchanges and merchants have a base URL for their service. This URL *must* be in a +canonical form when it is stored (e.g. in the wallet's database) or transmitted +(e.g. to a bank page). + +1. The URL must be absolute. This implies that the URL has a schema. +2. The path component of the URL must end with a slash. +3. The URL must not contain a fragment or query. + +When a user enters a URL that is, technically, relative (such as "alice.example.com/exchange"), wallets +*may* transform it into a canonical base URL ("http://alice.example.com/exchange/"). Other components *should not* accept +URLs that are not canonical. + +Rationale: Joining non-canonical URLs with relative URLs (e.g. "exchange.example.com" with "reserve/status") +results in different and slightly unexpected behavior in some URL handling libraries. +Canonical URLs give more predictable results with standard URL joining. diff --git a/docs/integration-merchant.rst b/docs/integration-merchant.rst new file mode 100644 index 00000000..7cf93044 --- /dev/null +++ b/docs/integration-merchant.rst @@ -0,0 +1,256 @@ +.. + 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. + +.. + .................. + 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. |