merchant-frontend-examples

ZZZ: Inactive/Deprecated
Log | Files | Refs

commit 3fa57954d3b50115924a7a905d59f8e7ff8e1654
parent e48ba154b78ee2c0d0ca286cb02dba2d29488b26
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 25 Feb 2017 16:58:51 +0100

updates to Python tutorial

Diffstat:
Mpython/doc/tutorial.texi | 134++++++++++++++++++++++++++++++++++++++++++-------------------------------------
1 file changed, 72 insertions(+), 62 deletions(-)

diff --git a/python/doc/tutorial.texi b/python/doc/tutorial.texi @@ -15,7 +15,7 @@ This tutorial is about implementing a merchant frontend to run against a GNU Taler merchant backend (version @value{VERSION}, @value{UPDATED}), -Copyright @copyright{} 2016 INRIA +Copyright @copyright{} 2016, 2017 INRIA @quotation Permission is granted to copy, distribute and/or modify this document @@ -53,9 +53,8 @@ Texts. A copy of the license is included in the section entitled @end ifnottex @menu -* Introduction:: Whom this tutorial is addressed to -* Hello-world:: How to set up a donation page -* Run How to run the code samples +* Introduction:: What this tutorial is about +* Setting up a simple donation page:: How to set up a donation page Appendices @@ -88,7 +87,7 @@ regulation (such as GDPR). @section About this tutorial -This tutorial is for Web developers and addresses how to integrate GNU +This tutorial is for Python Web developers and addresses how to integrate GNU Taler with Web shops. It describes how to create a Web shop that processes payments with the help of a GNU Taler merchant @emph{backend}. In the second chapter, you will learn how to trigger @@ -138,24 +137,25 @@ directly communicate with the exchange, and also does not deal with sensitive data. In particular, the merchant's signing keys and bank account information are encapsulated within the Taler backend. -@node Hello-world + +@node Setting up a simple donation page @chapter Setting up a simple donation page This section describes how to setup a simple shop, which exposes a button to get donations via Taler. The expected behaviour is that once -the ``donate'' button is clicked, the customer will receive a Taler -proposal offering him the opportunity to make a fixed donation, +the ``donate'' button is clicked, the customer will receive a +proposal to make a fixed donation, for example to donate 1.0 KUDOS to the charity operating the shop. All the code samples shown below in the tutorial can be found at @url{https://git.taler.net/merchant-frontend-examples.git/tree/python/example/}. -Each sample is part of a 100% functional frontend that the reader -can run and modify to suit their needs. +Each sample is part of a functional frontend. The language is Python, and the Web is served by Flask. +@c FIXME: add reference to Flask. @c NOTE: include explaining wallet installation to Web developer here! -A error message will be prompted to the user if no Taler wallet is +A error message will be shown to the user if no Taler wallet is installed in the browser. @section Specifying the backend @@ -164,8 +164,9 @@ installed in the browser. @cindex configuration @cindex currency For many critical operations, the frontend needs to communicate -with a Taler backend. Assuming that you do not yet have a backend -configured, you can use the public backend provided by the Taler +with a Taler backend. Assuming that you do not yet have a backend +configured@footnote{FIXME: reference to manual for backend installation}, +you can use the public backend provided by the Taler project for testing. This public backend has been set-up at @code{http://backend.test.taler.net/} specifically for testing frontends. It uses the currency ``PUDOS'' and all payments will @@ -186,9 +187,8 @@ BACKEND_URL = "http://backend.test.taler.net/" @section Talking to the backend @cindex backend -The frontend only needs to issue HTTP POST requests to the backend; -this is easily made by the `requests` library. See below. - +The frontend needs to issue HTTP POST requests to the backend; +this can be done using the `requests` library: @smallexample import flask @@ -206,7 +206,7 @@ if r.status_code != 200: @end smallexample -@node Prompting for payment + @section Prompting for payment @cindex button @@ -216,19 +216,19 @@ to the frontend @code{/donate.php} URL. For this, the HTML would be as follows: @smallexample -// ../example/templates/index.html +<!-- ../example/templates/index.html --> @verbatiminclude ../example/templates/index.html @end smallexample When the server-side handler for @code{/donate} receives the form submission, -it will return a HTML page that will take care of: +it will return a HTML page and HTTP header that will take care of: @itemize @item showing a error message if no wallet is installed @item instruct the wallet to download the proposal related to this donation @end itemize -The @code{/donate} endpoint would then look like follows. +The @code{/donate} endpoint looks like this: @cindex pay handler @cindex 402 @@ -240,28 +240,29 @@ def donate(): response.headers["X-Taler-Contract-Url"] = "/generate-proposal" return response @end smallexample -Whenever the wallet detects the 402 status, it reacts by downloading -the proposal from @code{/generate-proposal}. +The wallet detects the 402 status and reacts by downloading +the proposal from @code{/generate-proposal}. The proposal +is then presented to the user. -If the wallet is not present, the error message will be shown and -the Taler headers and the 402 status code ought to be ignored by -the browser. +If the wallet is not present, the error message ``No wallet +installed!'' will be shown and the Taler ``X-Taler-Contract-Url'' +header and the 402 status code ought to be ignored by the browser. @section A helper function to generate the proposed contract -The next step is then to generate the proposal whenever the -wallet tries to GET @code{/generate-proposal}. In our example, -this logic is implemented by the function @code{generate_proposal()}, -as shown below. +The next step is to generate a proposal whenever the +wallet makes a GET @code{/generate-proposal} request. In our example, +this logic is implemented by the function @code{generate_proposal()}: +@c FIXME: rename package to just 'taler' or 'gnu.taler'? (Check with Florian) @cindex contract @smallexample .. -CURRENCY = "PUDOS" +from pytaler import amount .. @@app.route("/generate-proposal") def generate_proposal(): - DONATION = amounts.string_to_amount("0.1:%s" % CURRENCY) - MAX_FEE = amounts.string_to_amount("0.05:%s" % CURRENCY) + DONATION = amounts.string_to_amount("0.1:%s" % CURRENCY) + MAX_FEE = amounts.string_to_amount("0.05:%s" % CURRENCY) order = dict( nonce=flask.request.args.get("nonce"), amount=DONATION, @@ -278,7 +279,7 @@ def generate_proposal(): merchant=dict( address="nowhere", name="Donation tutorial", - jurisdiction="none", + jurisdiction="Ursa Minor", ), ) @@ -291,8 +292,10 @@ def generate_proposal(): proposal_resp = r.json() return flask.jsonify(**proposal_resp) @end smallexample +@c CHECK ** with Florian, consider inlining... + The function @code{amounts.string_to_amount()} is defined by the -`pytaler` library, and it's used to convert amount given as strings +`pytaler` library, and its used to convert amount given as strings (in the form @code{"1.2:EUR"}) to amount as `dict` (in the form @code{@{value:1, fraction:20000000, currency:"EUR"@}}). @cindex signature @@ -309,7 +312,6 @@ base URL. For example, if the shop is run at @code{https://shop.com}, then @code{make_url("/path")} would result in @code{https://shop.com/path}. -@node Initiating the payment process @section Initiating the payment process @cindex fulfillment URL @@ -324,9 +326,9 @@ fulfillment page must thus use the HTTP session state to detect if the payment has been performed already, and if not request payment from the wallet. -The fulfillment handler at @code{/fulfillment} will firstly figure out -if the user has already paid, and if so confirm the donation. -If the user has not yet paid, it will instead return another ``402 Payment +The fulfillment handler at @code{/fulfillment} must thus first figure out +if the user has already paid, and if so merely confirm the donation. +If the user has not yet paid, it must instead return another ``402 Payment Required'' header, requesting the wallet to pay: @cindex 402 payment required @@ -337,34 +339,39 @@ def fulfillment(): paid = flask.session.get("paid", False) if paid: return "Thank you!" - + # At this point, the user did not pay yet, so we set some # appropriate HTTP headers that will instruct the wallet to - # accomplish the payment + # make the payment, assuming the user already accepted the + # proposal. response = flask.Response(status=402) response.headers["X-Taler-Contract-Url"] = make_url("/generate-proposal") response.headers["X-Taler-Contract-Query"] = "fulfillment_url" - # At this URL the wallet can sand the coins. + # To this URL the wallet can send the coins. response.headers["X-Taler-Pay-Url"] = make_url("/pay") - # This URL will be visited in case the user has clicked + # This URL will be visited in case the user has opened # on someone else's fulfillment URL. As a result, the - # user will be prompted by a new proposal. + # user will be offered a fresh proposal. response.headers["X-Taler-Offer-Url"] = make_url("/donate") return response @end smallexample +@c FIXME: check with Florian: isn't 'x-taler-contract-query' dead by now? @section Receiving payments via Taler -The final next step for the frontend is to accept the payment from the +The final next step for the frontend is to receive the payment from the wallet. For this, the frontend must implement a payment handler at -the URI specified in the @code{X-Taler-Pay-Url} header, as explained -above. +the URI specified in the @code{X-Taler-Pay-Url} header, so @code{/pay} +in our example. The role of the @code{/pay} handler is to receive the payment -from the wallet and forward it to the backend. If the backend -reports that the payment was successful, the handler needs to update +from the wallet and forward it to the backend. The backend +executes the payment. If it reports that the payment was successful +by returning a 200 status code, the handler needs to update the session state with the browser to remember that the user paid. +If the backend reports a failure, the error response is passed on to +the wallet. In our example, that is done by the @code{pay} function; see below. @@ -380,26 +387,28 @@ def pay(): return e, 400 # Forwarding the payment to the backend that will cryptographically - # verify it. + # verify it and persist the proof of payment. r = requests.post(urljoin(BACKEND_URL, 'pay'), json=deposit_permission) + # Pass errors back to the wallet. if 200 != r.status_code: return r.text, r.status_code - + # The payment went through, so we can set the state as "paid". # Note that once this page will return "200 OK", the wallet will # automatically re-visit the fulfillment page (and get the "Thank - # you" message now). + # you" message this time). flask.session["paid"] = True return flask.Response(status=200) @end smallexample -@node Run -@chapter Run + +@section Running the Example + The file @code{python/example/example.py} contains all the code samples -seen so far, plus some additional helper function, and it is fully operational. -It is run as a normal Flask application, by the following commands: +seen so far, incuding the @code{make_url()} helper function. +It is run as a typical Flask application, using the following commands: @smallexample $ cd python/example/ @@ -407,15 +416,16 @@ $ export FLASK_APP=example.py $ flask run @end smallexample -At this point you should have the site running at localhost on port 5000. +At this point you should have the site running at @code{localhost} on port 5000. + +To do a test payment, you first need to visit +@code{https://taler.net/wallet} from where you can install the Taler +wallet. Then, you need to withdraw a few coins from our demonstration +bank running at @code{https://bank.test.taler.net/}. After that, you +should be able to point your browser at @code{http://localhost:5000/} +and make a donation. -Before proceeding with a paying experience, you need to withdraw a few coins -from our demonstration bank running at @code{https://bank.test.taler.net/}. -After that, you should be able to point your browser at @code{http://localhost:5000/} -and start making donations! -Please visit @code{https://test.taler.net/} to know how to install the Taler -wallet. @c **********************************************************