summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcello Stanisci <marcello.stanisci@inria.fr>2016-12-15 17:48:44 +0100
committerMarcello Stanisci <marcello.stanisci@inria.fr>2016-12-15 17:48:44 +0100
commit3c3459ebd0ff66f0cff4c5703a04dd1782f18b2b (patch)
treed5b97de693a54fd0db0a668c6201a82fedfc4975
parentd69e0edad1d987278cbcae0f390d8c0cdce6109d (diff)
downloaddocs-3c3459ebd0ff66f0cff4c5703a04dd1782f18b2b.tar.gz
docs-3c3459ebd0ff66f0cff4c5703a04dd1782f18b2b.tar.bz2
docs-3c3459ebd0ff66f0cff4c5703a04dd1782f18b2b.zip
Reworking #4169
-rw-r--r--example-essay-store.rst162
1 files 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 <merchant-arch>`, 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 <payprot>`.
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 <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 <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&timestamp=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
+
+ ...
+ <script src="/static/web-common/taler-wallet-lib.js" type="application/javascript"></script>
+ <script src="/static/purchase.js" type="application/javascript"></script>
+ ...
+ <meta name="contract_url" value="https://shop.demo.taler.net/generate-contract?article_name=Appendix_A:_A_Note_on_Software">
+
+ ...
+ ...
+
+ <div id="ccfakeform" class="fade">
+ <p>
+ 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? <em>You can also
+ use GNU Taler to complete the purchase at any time.</em>
+ </p>
+
+ <form>
+ First name<br> <input type="text"></input><br>
+ Family name<br> <input type="text"></input><br>
+ Age<br> <input type="text"></input><br>
+ Nationality<br> <input type="text"></input><br>
+ Gender<br> <input type="radio" name"gender">Male</input>
+ CC number<br> <input type="text"></input><br>
+ <input type="radio" name="gender">Female</input><br>
+ </form>
+ <form method="get" action="/cc-payment/{{ article_name }}">
+ <input type="submit"></input>
+ </form>
+ </div>
+
+ <div id="talerwait">
+ <em>Processing payment with GNU Taler, please wait <span id="action-indicator"></span></em>
+ </div>
+ ...
+
+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.