From 3c3459ebd0ff66f0cff4c5703a04dd1782f18b2b Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Thu, 15 Dec 2016 17:48:44 +0100 Subject: Reworking #4169 --- example-essay-store.rst | 162 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 138 insertions(+), 24 deletions(-) diff --git a/example-essay-store.rst b/example-essay-store.rst index ec22ff42..a7e104ac 100644 --- a/example-essay-store.rst +++ b/example-essay-store.rst @@ -16,46 +16,160 @@ @author Marcello Stanisci -================================== +==================== Example: Essay Store -================================== +==================== This section shows how to set up a merchant :ref:`frontend `, and is inspired by our demonstration shop running at `https://blog.demo.taler.net/`. +It is recommended that the reader is already familiar with the +:ref:`payment protocol and terminology `. The code we are going to describe is available at https://git.taler.net/merchant-frontends.git/tree/talerfrontends/blog and is implemented in Python+Flask. -The desired effect is that the homepage has a list of buyable article, and once the -user clicks on one of them, they will either get the Taler :ref:`contract ` +The desired effect is the homepage showing a list of buyable articles, and once the +user clicks one of them, they will either get the Taler :ref:`contract ` or a credit card paywall if they have no Taler wallet installed. +This logic is implemented in the offer URL, which shows the article name: -The offer URLs trigger the expected interaction with the wallet. In practical terms, the -offer URL returns a HTML page that can either show a pay-form in case Taler is not installed -in the user's browser or download the contract from the merchant. -If the user has Taler installed and wants to pay, the wallet will POST the coins to a URL -of the form: + `https://shop.demo.taler.net/essay/Appendix_A:_A_Note_on_Software` - `https://blog.demo.taler.net/pay?uuid=${contract_hashcode}` +Once the server side logic receives a request for a offer URL, it needs to +instruct the wallet to retrieve a Taler contract. This action can be taken +either with or with*out* the use of JavaScript, see next two sections. -The URL comes with the contract's hashcode because each contract is an entry in -the merchant's state, so it can mark it as ``payed`` whenever it receives coins. +.. note:: + The code samples shown below are intentionally incomplete, as often + one function contains logic for multiple actions. Thus in order to not + mix concepts form different actions under one section, parts of code not + related to the section being documented have been left out. -For the essay store, the fulfillment URL matches the initial part of -an offer URL, but contains the additional parameters needed to -reconstruct the contract, in this case the `tid` (transaction id) and -a `timestamp`. Hence, a fulfillment URL for the essay store looks like: ++++++++++++++++ +With JavaScript ++++++++++++++++ + +We return a HTML page, whose template is in +``talerfrontends/blog/templates/purchase.html``, that imports ``taler-wallet-lib.js``, +so that the function ``taler.offerContractFrom()`` can be invoked into the user's +browser. - `https://blog.demo.taler.net/essay/article_title?tid=3489×tamp=8374738` +The server side handler for a offer URL needs to render ``purchase.html`` by passing +the right parameters to ``taler.offerContractFrom()``. -This is sufficient for the simple essay store, as we do not need any further -details to reconstruct the contract. In particular, the essay store -assumes for simplicity that all the articles have the same price forever. +The rendering is done by the ``article`` function at ``talerfrontends/blog/blog.py``, +and looks like the following sample. -We note that 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 -differnt domain name. +.. sourcecode:: python + + return render_template('templates/purchase.html', + article_name=name, + no_contract=1, + contract_url=quote(contract_url), + data_attribute="data-taler-contractoffer=%s" % contract_url) + +After the rendering, (part of) ``purchase.html`` will look like shown below. + +.. sourcecode:: html + + ... + + + ... + + + ... + ... + +
+

+ Oops, it looks like you don't have a Taler wallet installed. Why don't you enter + all your credit card details before reading the article? You can also + use GNU Taler to complete the purchase at any time. +

+ +
+ First name

+ Family name

+ Age

+ Nationality

+ Gender
Male + CC number

+ Female
+
+
+ +
+
+ +
+ Processing payment with GNU Taler, please wait +
+ ... + +The script ``purchase.js`` is now in charge of implementing the behaviour we seek. +It needs to register two handlers: one called whenever the wallet is detected in the +browser, the other if the user has no wallet installed. + +That is done with: + +.. sourcecode:: javascript + + taler.onPresent(handleWalletPresent); + taler.onAbsent(handleWalletAbsent); + +.. note:: + + The ``taler`` object is exported by ``taler-wallet-lib.js``, and contains all is + needed to communicate with the wallet. + + +``handleWalletAbsent`` doesn't need to do much: it has to only hide the "please wait" +message and uncover the credit card pay form. See below. + +.. sourcecode:: javascript + + function handleWalletAbsent() { + document.getElementById("talerwait").style.display = "none"; + document.body.style.display = ""; + } + +On the other hand, ``handleWalletPresent`` needs to firstly hide the credit card +pay form and show the "please wait" message. After that, it needs to fetch the +contract URL from the responsible ``meta`` tag, and finally invoke ``taler.offerContractFrom()`` using it. See below both parts. + +.. sourcecode:: javascript + + function handleWalletPresent() { + document.getElementById("ccfakeform").style.display = "none"; + document.getElementById("talerwait").style.display = ""; + ... + ... + // Fetch contract URL from 'meta' tag. + let contract_url = document.querySelectorAll("[name=contract_url]")[0]; + taler.offerContractFrom(decodeURIComponent(contract_url.getAttribute("value"))); + ... + } + + + +++++++++++++++++++ +Without JavaScript +++++++++++++++++++ + + +.. + Fundamental steps: + + - How 402 HTTP headers are set in each step. + - How OTOH JavaScript accomplishes the same. + - How the handler detects offer vs fulfillment. + + To mention: + + - difference between fulfillment and offer URL, although + that pattern is not mandatory at all. + - how few details we need to reconstruct the contract. -- cgit v1.2.3