commit 139be76451b61ad031750e7904c1ae20b54e66c9
parent b8fc6ceb48f6d1e259cfda6c4e0da34b1765e708
Author: Christian Grothoff <christian@grothoff.org>
Date: Wed, 26 Oct 2016 01:03:20 +0200
more editing
Diffstat:
| M | doc/manual.texi | | | 424 | ++++++++++++++++++++++++++++++++++++++++++------------------------------------- |
1 file changed, 228 insertions(+), 196 deletions(-)
diff --git a/doc/manual.texi b/doc/manual.texi
@@ -59,7 +59,7 @@ Texts. A copy of the license is included in the section entitled
@end menu
@node Introduction
-@chapter Whom this manual is addressed to
+@chapter Introduction
This manual addresses how to integrate GNU Taler with Web shops. It
describes how to install a GNU Taler merchant @emph{backend} and make it
@@ -252,7 +252,7 @@ This section has not yet been written.
@node Configuration
-@chapter How to configure the Merchant backend
+@chapter How to configure the merchant's backend
The installation already provides reasonable defaults for most of the
configuration options. However, some of them must be tuned to the
@@ -425,6 +425,92 @@ in the section name instead of @code{default}.
@end table
+@section Sample backend configuration
+
+The following is an example for a complete backend configuration:
+
+
+@c FIXME: change wire format to 'test'
+@c FIXME: provide actual master key of exchange.demo!
+@example
+[merchant]
+wireformat = SEPA
+serve = TCP
+port = 9898
+currency = EUR
+database = postgres
+
+[merchant-instance-default]
+KEYFILE = $DATADIR/key.priv
+
+[merchant-instance-wireformat-default]
+SEPA_RESPONSE_FILE = $DATADIR/sepa.json
+
+[merchantdb-postgres]
+config = postgres:///donations
+
+[merchant-demoexchange]
+uri = https://exchange.demo.taler.net/
+master_key = EXAMPLEMASTERKEY
+
+@end example
+
+The configuration file is typically located at
+@code{$HOME/.config/taler.conf}. An alternative location can nbe
+specified to @code{taler-merchant-httpd} using the @code{-c} option.
+
+The contents of the XXX.json might look like this:
+
+@example
+FIXME: provide sample test wire transfer JSON file
+@end example
+
+
+The resulting backend will be listening on port 9898.
+
+The backend's private key will be located at @code{$DATADIR/key.priv}.
+You can determine the expaned path using
+@example
+$ taler-config -f -s merchant-instance-wireformat-default -o TEST_RESPONSE_FILE
+@end example
+
+The backend will use a database named @code{donations} within
+Postgresql.
+
+The backend will deposit the coins it receives to the exchange at
+@url{https://exchange.demo.taler.net/}, which has the master key
+"EXAMPLEMASTERKEY".
+@c FIXME: include the actual key from demo!
+
+
+@section Launching the backend
+
+As mentioned previously, the system adminstrator should make sure that
+a database named @code{donations} is defined and accessible in the
+system. Once this configuration is ready, the
+merchant backend can be launched using:
+
+@example
+$ taler-merchant-httpd
+@end example
+
+If everything worked as expected, the command
+
+@example
+$ curl http://localhost:9898/
+@end example
+
+should return the message
+
+@example
+Hello, I'm a merchant's Taler backend. This HTTP server is not for humans.
+@end example
+
+Please note that your backend is right now likely globally
+reachable. Production systems should be configured to bind
+to a UNIX domain socket or properly restrict access to the
+port.
+
@node Hello-world
@chapter Setting up a simple Web shop with GNU Taler
@@ -440,15 +526,12 @@ they should instead be prompted to proceed with a classic dialog for
credit card payments.
-@section How to trigger Taler payment
-The goal is to trigger a Taler payment once the customer has clicked
-on the donation button. The triggering happens when the frontend requests
-a @emph{contract} to the Merchant backend. According to the API
-@footnote{Full specification available at @url{https://api.taler.net/}}, the backend generates contracts by
-serving HTTP requests addressed to @code{/contract}. So our button's goal
-is to trigger some server-side logic that will, in the end, issue a HTTP POST
-to the backend's @code{/contract}.
-Then, its HTML would be as follows:
+@section Prompting for payment
+
+Our goal is to trigger a Taler payment once the customer has clicked
+on a donation button. We will use a button that issues an HTTP POST
+to the frontend @code{/donate} URL. For this, the HTML would be as
+follows:
@example
<form action="/donate">
@@ -456,154 +539,157 @@ Then, its HTML would be as follows:
<form>
@end example
-Namely, its work is merely invoking server-side logic.
-
-Note that we @emph{could} have bypassed the server-side logic to request
-the contract to the backend, and have requested it directly from the button's
-JavaScript. However, this is highly unpractical because the backend needs
-lots of information in the request's body, and we would end up hardcoding
-all that information in the JavaScript.
-
When the server-side handler for @code{/donate} receives the form submission,
it will return a HTML page that will take care of:
@itemize
-@item showing a credit card paywall to the user, if no wallet is found.
-@item triggering contract generation (in JavaScript) to server-side logic.
+@item showing a credit card paywall to the user if no wallet is found, and
+@item fetching a Taler contract and passing it to the wallet if one is found
@end itemize
A minimalistic @code{/donate} handler is shown below (in PHP):
-
-@display
-echo("fallback.html");
-@end display
-
-In practical terms, all the logic to detect the wallet in the browser and
-request the contract lies in @code{fallback.html}. Moreover, the frontend
-developer can get help by the @code{taler-wallet-lib.js} library, which
-abstracts the signaling between the HTML page and the wallet. Let's see
-below how @code{fallback.html} looks like:
-
@display
+<!php
+ http_response_code (402); // 402: Payment required
+ header ('X-Taler-Pay-Url: /pay');
+ header ('X-Taler-Offer-Url: /generate-contract');
+?>
<html>
<head>
- <script src="taler-wallet-lib.js" type="application/javascript"></script>
- <script type="application/javascript">
-
- /* This function will be called whenever the wallet signals its presence.
- It then will use the library's function taler.offerContractFrom to
- request the contract to the server-side logic. Finally, taler.offerContractFrom
- will hand the contrat to the wallet once it gets it. */
- function onWalletPresent()@{
- contract_url = "/generate-contract";
- taler.offerContractFrom(contract_url);
- @}
- /* The 'taler' object is exported by the library above */
- taler.onPresent(onWalletPresent);
- </script>
+ <title>Select payment method</title>
</head>
<body>
<!-- Put the credit card paywall here -->
</body>
+</html>
@end display
-The result is that if a wallet is found, then the contract is requested so
-the user doesn't see the credit card paywall@footnote{In this simple
-implementation, the user will se the paywall as long as no contract is received.
-Nevertheless, it is easy to temporarily hide the paywall by using the
-@code{taler.onAbsent} handler}, otherwise the page rests untouched and the
-paywall remains shown.
+Given this response, the Taler wallet will fetch the contract from
+@url{/contract} and display it to the user. If the wallet is not
+present, the HTML body will be shown and the Taler headers will be
+ignored by the browser. Instead of specifying the contract via an
+URL, it is also possible to inline short contracts directly.
+
+Note that we @emph{could} have bypassed the POST request to trigger
+the payment, and performed the interaction with the wallet directly
+from the button's via JavaScript. We will consider this case in a
+later chapter.
+
+
+@section Generating the contract
-Now, it's the server-side handler for @code{/generate-contract} that continues.
-It has to generate a contract proposal about donating 1 KUDOS to the 'Taler charity program'.
-This proposal will then be POSTed to the backend at @code{/contract}@footnote{We assume that
-frontend and backend are reachable at the same host}. The main goal of POSTing the proposal
-to the backend is to get it signed, as by design the frontend does not perform any
-cryptographic work.
+The server-side handler for @code{/generate-contract} now has to
+generate a contract proposal about donating 1 KUDOS to the 'Taler
+charity program'. This proposed contract is then be POSTed to the
+backend at @code{/contract}. The main result of POSTing the proposal
+to the backend is that it will be cryptographically signed, as by
+design the frontend does not perform any cryptographic work.
-Let's see how @code{/generate-contract} handler should look like:
+A simple @code{/generate-contract} handler could look like this:
+@c FIXME: actually include the full PHP logic to create a sample contract!
+@c FIXME: where does 'post_to_backend' come from? Give a working example!
+@c FIXME: include reasonable implementation of 'manage error'!
+@c FIXME: include fulfillment URI in contract example!
@display
...
# this variable is the JSON of a contract proposal,
# see https://api.taler.net/api-merchant.html#post--contract
# the second parameter is the transaction id
-$proposal = make_contract("1 KUDOS", rand(1,10000));
+$proposal = make_contract("1 KUDOS", rand(1, 10000));
# Here the frontend POSTs the proposal to the backend
$response = post_to_backend("/contract", $proposal);
-if(200 != $response->status_code)@{
+if (200 != $response->status_code) @{
manage_error($response);
return;
@}
echo $response->body;
@end display
-As said, @code{taler.offerContractFrom} will hand the contract to the wallet,
-which then will prompt the user a formatted version of it.
+After the browser has fetched the contract, the user will
+be given the opportunity to affirm the payment.
-@section How to pay via Taler
-The next step for the frontend is to accept the payment from the wallet,
-whenever the user accepts the contract. In Taler terminology, this action
-is managed by two pages: the @emph{fulfillment} and @emph{pay} page.
+@section Receiving payments via Taler
-The fulfillment page is in charge of implementing the following two properties:
+The next step for the frontend is to accept the payment from the wallet,
+assuming the user accepts the contract. For this, the frontend must
+implement a payment handler at the URI specified for as the
+@code{X-Taler-Pay-Url} in the example above.
-@enumerate
-@item Taler payments @emph{can} be implemented in DB-less frontends.
+The role of the @code{/pay} handler is to receive the deposit
+permission from the wallet and forward it to the backend. If the
+backend reports that the payment was successful, the handler needs to
+update the session state with the browser to remember that the user
+paid. The following code implements this in PHP:
-@item Taler payments are replayable, meaning that each purchase is associated with
-a URL (the fulfillment URL) that shows the product each time it gets visited (and
-of course, only the first time takes the user's money).
-@end enumerate
+@c FIXME: do expand on ``manage_error''
+@example
+# Check if a session exists already
+session_start();
+if (! isset($_SESSION['paid'])) @{
+ echo "<p>There is no session for this purchase. Something is wrong.</p>";
+ return;
+@}
+# Get the HTTP POST body
+$deposit_permission = file_get_contents('php://input');
+$response = post_to_backend("/pay", $deposit_permission);
+if (200 != $response->status_code)@{
+ manage_error($response);
+ return;
+@}
+$_SESSION['paid'] = true;
+return $response->body;
+@end example
-In order to implement property (1), the frontend needs a way to recall what a contract
-is about (e.g. which product, which price, the timestamp, etc.) before proceeding with
-the actual payment and eventually deliver the final product.
-That is achieved by @emph{reconstructing} the contract using the fulfillment page's
-URL parameters@footnote{the fulfillment URL equipped with all the parameters is included
-in the contract}.
+Do not be confused by the @code{isset} test for the session state. In
+our simple example, it will be set to ``false'' by the fulfillment URL
+which the browser actually always visits first.@footnote{This is for
+technical reasons; the natural logical progression would of course be
+to pay before accessing the fulfillment URL.} We describe how the
+fulfillment URL works in the next section.
-In order to implement property (2), the frontend will firstly check the state to see if
-the product claimed among the fulfillment URL parameter has been paid; if so, the product
-is given to the customer. Otherwise, the frontend sets the payment as "pending" in the state
-and @emph{executes} it in the wallet. The payment execution is performed by returning JavaScript
-code from @code{taler-wallet-lib.js} that triggers the wallet to send the payment to the pay page.
-Once the pay page receives the payment, it sets the state for the payment as "payed".
-In this scenario, the wallet's behaviour upon payment execution is to send the payment to the pay
-page and, if the payment is successful, visit again the fulfillment URL which then (thanks to the
-new state set by the pay page) will return the final product.
+@section The fulfillment page
+The fulfillment page can be called by users that have already paid for
+the item, as well as by users that have not yet paid at all. The
+fulfillment page must use the HTTP session state to detect if the
+payment has been performed already, and if not request payment from
+the wallet.
+For our example, we include the full contract details, including a
+transaction ID, in the URI of the fulfillment page to allow the page
+to determine which contract the user is trying to access.
Thus, a fulfillment URL for our example looks like the following:
@example
-https://example.com/fulfillment?transaction_id=9
+https://shop/fulfillment?transaction_id=9
@end example
The @code{/fulfillment} handler will then perform the following actions:
+@c FIXME: convert to JS-less method!
+@c FIXME: provide complete example code!
@example
...
-
# At first, check if the user has already paid for the product.
# If so, deliver the product.
session_start();
-if(!isset($_SESSION['payed']))@{
+if (! isset($_SESSION['paid']))@{
# set as pending
- $_SESSION['payed'] = false;
+ $_SESSION['paid'] = false;
@}
else@{
- if($_SESSION['payed'])@{
- echo "<p>Thanks!</p>";
+ if($_SESSION['paid'])@{
+ echo "<p>Thanks for your donation!</p>";
return;
@}
else@{
- echo "<p>Payment still pending</p>";
+ echo "<!-- Put the credit card paywall here -->";
return;
@}
@}
@@ -626,54 +712,16 @@ echo "<html>
</html>";
@end example
-The function @code{executePayment} exported by @code{taler-wallet-lib.js} will basically
-hand its three parameters to the wallet which implements the following semantics:@*
-check in the internal DB if @code{$response['H_contract']} has an entry, and:
-@itemize
-@item if that is the case, then the user accepted the contract previously and the wallet
-sends a deposit permission @footnote{Roughly speaking, a deposit permission is a JSON
-containing the coins to pay for a contract. Its full specification is available at:
-@url{https://api.taler.net/api-merchant.html#depositpermission}} to @code{/frontend-pay}.
-If this operation succeeds, then visit again the fulfillment URL, and finally enjoy
-the product.
-@item if not, redirect the browser to @code{/donate} (which will then reinitiate the
-whole contract negotiation). This happens when the user visits a shared fulfillment URL.
-The idea is to let that user buy the same products as the user who shared the fulfillment
-URL. Nonetheless, the shop is not forced to follow this semantics when provides the third
-parameter to @code{executePayment}.
-@end itemize
-By design, the wallet can send infinite times the @emph{same} deposit permission
-for exactly the same contract, and get again the related product. No new coins
-will be consumed starting from the second time.
-The last step is the @code{/frontend-pay} handler. As said, its duty is
-to receive the deposit permission from the wallet, forward it to the backend, and set
-the state as "paid", if the payment is valid. See the figure below:
-@example
+@chapter Advanced topics
-# Check if a session exists already
-session_start();
-if(!isset($_SESSION['payed']))@{
- echo "<p>There is no session for this purchase. Pass through the fulfillment URL first</p>";
- return;
-@}
-# Get the HTTP POST body
-$deposit_permission = file_get_contents('php://input');
-$response = post_to_backend("/pay", $deposit_permission);
-if(200 != $response->status_code)@{
- manage_error($response);
- return;
-@}
-$_SESSION['payed'] = true;
-return $response->body;
+This chapter includes draft texts for advanced topics which have
+not yet been properly integrated with the main text.
-@end example
-@section Backend configuration
-
-@subsection Wireformat
+@section Using the SEPA wire transfer method
Let's say that all the donations go to the following recipient,
expressed in @code{SEPA} format@footnote{As said, supporting SEPA is still
@@ -692,75 +740,59 @@ work in progress}.
Assume this information is stored in file @code{$DATADIR/sepa.json}.
-@subsection Serving
-
-The backend will be listening on port 9898 on the host @code{example.taler.net}
-
-@subsection Key
-
-The backend's key is located at @code{$DATADIR/key.priv}
-
-@subsection Database
-The backend will use a database named @code{donations} within Postgresql.
-@subsection Exchange
+@section Payments using JavaScript
-The backend will deposit the coins it receives to the exchange at
-@url{https://exchange.demo.taler.net/}, having the master key
-"EXAMPLEMASTERKEY".
-
-@subsection Final configuration
-
-The following configuration will drive the Merchant backend to
-realize what we planned in the subsections above. We assume the file
-@code{$HOME/.config/taler.conf} is being edited. It is the default
-file looked by the Merchant backend, but we can pass it config
-files from other locations using the @code{-c} option.
-
-
-@example
-
-[merchant]
-wireformat = SEPA
-serve = TCP
-port = 9898
-currency = EUR
-database = postgres
-
-[merchant-instance-default]
-KEYFILE = $DATADIR/key.priv
-
-[merchant-instance-wireformat-default]
-SEPA_RESPONSE_FILE = $DATADIR/sepa.json
+The function @code{executePayment} exported by @code{taler-wallet-lib.js} will basically
+hand its three parameters to the wallet which implements the following semantics:@*
+check in the internal DB if @code{$response['H_contract']} has an entry, and:
+@itemize
+@item if that is the case, then the user accepted the contract previously and the wallet
+sends a deposit permission @footnote{Roughly speaking, a deposit permission is a JSON
+containing the coins to pay for a contract. Its full specification is available at:
+@url{https://api.taler.net/api-merchant.html#depositpermission}} to @code{/frontend-pay}.
+If this operation succeeds, then visit again the fulfillment URL, and finally enjoy
+the product.
+@item if not, redirect the browser to @code{/donate} (which will then reinitiate the
+whole contract negotiation). This happens when the user visits a shared fulfillment URL.
+The idea is to let that user buy the same products as the user who shared the fulfillment
+URL. Nonetheless, the shop is not forced to follow this semantics when provides the third
+parameter to @code{executePayment}.
+@end itemize
-[merchantdb-postgres]
-config = postgres:///donations
-[merchant-demoexchange]
-uri = https://exchange.demo.taler.net/
-master_key = EXAMPLEMASTERKEY
-@end example
+@section Design considerations for the fulfillment page
-The sysadmin should make sure that a database named @code{donations} is
-defined and accessible in the system. Once this configuration is ready,
-it suffices to run the Merchant backend with:
+The fulfillment page mechanism is designed to provide the following two properties:
-@example
-taler-merchant-httpd
-@end example
+@enumerate
+@item Taler payments @emph{can} be implemented in DB-less frontends.
-If everything worked as expected, the following command:
-@example
-$ curl http://example.taler.net/
-@end example
+@item Taler payments are replayable, meaning that each purchase is associated with
+a URL (the fulfillment URL) that shows the product each time it gets visited (and
+of course, only the first time takes the user's money).
+@end enumerate
-should return the following message:
+In order to implement property (1), the frontend needs a way to recall
+what a contract is about (e.g. which product, which price, the
+timestamp, etc.) before proceeding with the actual payment and
+eventually deliver the final product. That is achieved by
+@emph{reconstructing} the contract using the fulfillment page's URL
+parameters@footnote{the fulfillment URL equipped with all the
+parameters is included in the contract}.
+
+In order to implement property (2), the frontend will firstly check
+the state to see if the product claimed among the fulfillment URL
+parameter has been paid; if so, the product is given to the
+customer. Otherwise, the frontend sets the payment as "pending" in the
+state and @emph{executes} it in the wallet. The payment execution is
+performed by returning JavaScript code from @code{taler-wallet-lib.js}
+that triggers the wallet to send the payment to the pay page. Once
+the pay page receives the payment, it sets the state for the payment
+as "payed".
-@example
-Hello, I'm a merchant's Taler backend. This HTTP server is not for humans.
-@end example