commit b6b4e0468e9b51d39c169d2059c2c2605492b06b
parent f9cca6501824f02759fb3f22af03451dc949983a
Author: Marcello Stanisci <marcello.stanisci@inria.fr>
Date: Tue, 15 Nov 2016 23:47:51 +0100
About to test payment; documentation adapted to new code samples
Diffstat:
17 files changed, 539 insertions(+), 446 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,3 @@
+php/doc/tutorial.*
+php/doc/arch.*
+!php/doc/tutorial.texi
diff --git a/Makefile b/Makefile
@@ -0,0 +1,14 @@
+# This Makefile is in the public domain
+
+.PHONY: graphics
+
+SUBDIRS := php
+
+all: $(SUBDIRS)
+
+$(SUBDIRS): graphics
+ cp -t $@/doc graphics/arch.png
+ cd $@/doc/; texi2pdf tutorial.texi
+
+graphics:
+ cd graphics; dot -Tpng arch.dot > arch.png
diff --git a/Makefile.am b/Makefile.am
@@ -1,3 +0,0 @@
-# This Makefile is in the public domain
-
-SUBDIRS = php
diff --git a/graphics/arch.dot b/graphics/arch.dot
@@ -0,0 +1,22 @@
+digraph G {
+
+ user[label="Customer browser"];
+ admin[label="Shop admin"];
+ subgraph cluster_0 {
+ Frontend;
+ Backoffice;
+ Backend;
+ DBMS;
+ label="Shop server";
+ }
+ subgraph cluster_1 {
+ Exchange;
+ label="Exchange";
+ }
+ user->Frontend;
+ admin->Backoffice;
+ Frontend->Backend;
+ Backoffice->Backend;
+ Backend->DBMS;
+ Backend->Exchange;
+}
diff --git a/graphics/arch.jpg b/graphics/arch.jpg
Binary files differ.
diff --git a/graphics/arch.pdf b/graphics/arch.pdf
Binary files differ.
diff --git a/graphics/arch.png b/graphics/arch.png
Binary files differ.
diff --git a/php/copying.php b/php/copying.php
@@ -0,0 +1,18 @@
+<?php
+/*
+ This file is part of GNU TALER.
+ Copyright (C) 2014, 2015, 2016 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser 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, If not, see <http://www.gnu.org/licenses/>
+
+*/
+?>
diff --git a/php/doc/manual.texi b/php/doc/manual.texi
@@ -1,333 +0,0 @@
-\input texinfo @c -*-texinfo-*-
-@c %**start of header
-@setfilename manual.info
-@include version.texi
-@settitle The GNU Taler manual for Web shop operators @value{VERSION}
-
-@c Define a new index for options.
-@defcodeindex op
-@c Combine everything into one index (arbitrarily chosen to be the
-@c concept index).
-@syncodeindex op cp
-@c %**end of header
-
-@copying
-This manual is for the GNU Taler merchant backend (version @value{VERSION}, @value{UPDATED}),
-
-Copyright @copyright{} 2016 INRIA
-
-@quotation
-Permission is granted to copy, distribute and/or modify this document
-under the terms of the GNU Free Documentation License, Version 1.3 or
-any later version published by the Free Software Foundation; with no
-Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
-Texts. A copy of the license is included in the section entitled
-``GNU Free Documentation License''.
-@end quotation
-@end copying
-@c If your manual is published on paper by the FSF, it should include
-@c The standard FSF Front-Cover and Back-Cover Texts, as given in
-@c maintain.texi.
-@c
-@c Titlepage
-@c
-@titlepage
-@title The GNU Taler manual for Web shops
-@subtitle Version @value{VERSION}
-@subtitle @value{UPDATED}
-@author Marcello Stanisci (@email{marcello.stanisci@@inria.fr})
-@author Christian Grothoff (@email{christian.grothoff@@inria.fr})
-@page
-@vskip 0pt plus 1filll
-@insertcopying
-@end titlepage
-
-@summarycontents
-@contents
-
-@ifnottex
-@node Top
-@top The GNU Taler manual for Web shops
-@insertcopying
-@end ifnottex
-
-@menu
-* Introduction:: Whom this manual is addressed to
-* Hello-world:: How to set up a minimalistic shop
-* Back-office-integration:: How to integrate with the back office
-* Advanced topics:: Detailed solutions to specific issues
-@end menu
-
-
-@node Introduction
-@chapter Introduction
-
-@section About GNU Taler
-
-GNU Taler is an open protocol for an electronic payment system with a
-free software reference implementation. GNU Taler offers secure, fast
-and easy payment processing using well understood cryptographic
-techniques. GNU Taler allows customers to remain anonymous, while
-ensuring that merchants can be held accountable by governments.
-Hence, GNU Taler is compatible with anti-money-laundering (AML) and
-know-your-customer (KYC) regulation, as well as data protection
-regulation (such as GDPR).
-
-
-@section About this manual
-
-This manual is for Web developers and addresses how to integrate GNU Taler
-with Web shops. It describes how to create a Web shop that cooperates with
-a GNU Taler merchant @emph{backend}.
-
-You can download all of the code examples given in this tutorial from
-@url{https://git.taler.net/merchant-frontend-examples.git/tree/php/}.
-
-@c Add section giving an overview of what we will do in the tutorial!
-
-
-@section Architecture overview
-
-The Taler software stack for a merchant consists of four main components:
-
-@itemize
-@item A frontend which interacts with the customer's browser. The
- frontend enables the customer to build a shopping cart and place
- an order. Upon payment, it triggers the respective business logic
- to satisfy the order. This component is not included with Taler,
- but rather assumed to exist at the merchant. This manual
- describes how to integrate Taler with Web shop frontends.
-@item A back office application that enables the shop operators to
- view customer orders, match them to financial transfers, and possibly
- approve refunds if an order cannot be satisfied. This component is
- again not included with Taler, but rather assumed to exist at the
- merchant. This manual will describe how to integrate such a component
- to handle payments managed by Taler.
-@item A Taler-specific payment backend which makes it easy for the
- frontend to process financial transactions with Taler. The
- next two chapters will describe how to install and configure
- this backend.
-@item A DBMS which stores the transaction history for the Taler backend.
- For now, the GNU Taler reference implemenation only supports Postgres,
- but the code could be easily extended to support another DBMS.
-@end itemize
-
-The following image illustrates the various interactions of these
-key components:
-
-@center @image{arch, 3in, 4in}
-
-
-Basically, the backend provides the cryptographic protocol support,
-stores Taler-specific financial information in a DBMS and communicates
-with the GNU Taler exchange over the Internet. The frontend accesses
-the backend via a RESTful API. As a result, the frontend never has to
-directly communicate with the exchange, and also does not deal with
-sensitive data. In particular, the merchant's signing keys and bank
-account information is encapsulated within the Taler backend.
-
-
-@c FIXME: this should be a separate manual
-
-
-@node Hello-world
-@chapter Setting up a simple Web shop with GNU Taler
-
-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
-contract offering him the opportunity to make a fixed donation,
-for example to donate 1 KUDOS to the charity operating the shop.
-
-@c NOTE: include explaining wallet installation to Web developer here!
-
-Note that if the customer does not have the Taler wallet installed,
-they should instead be prompted to proceed with a classic dialog for
-credit card payments.
-
-
-@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:
-
-@smallexample
-<form action="/donate-handler">
- <input type="submit" value="Donate!"></input>
-</form>
-@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:
-
-@itemize
-@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} implementation is shown below (in PHP):
-
-@smallexample
-// merchant/doc/examples/donate_handler.php
-@include examples/donate_handler.php
-@end smallexample
-
-
-Given this response, the Taler wallet will fetch the contract from
-@url{/generate-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 via JavaScript.
-
-@c We will consider this case in a later chapter.
-@c FIXME: not yet ;-)
-
-@section Generating the contract
-
-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 POSTed to the
-backend at @code{/contract}. The main result of POSTing the proposal
-to the backend is that it will be cryptographically signed. This is
-necessary as by design the frontend does not perform any cryptographic
-work.
-
-A simple @code{/generate-contract} handler may thus look like this:
-
-@smallexample
-// merchant/doc/examples/generate_contract.php
-@include examples/generate_contract.php
-@end smallexample
-
-Note that in practice the frontend might want to generate a monotonically
-increasing series of transaction IDs to avoid a chance of collisions.
-Transaction IDs must be in the range of @math{[0:2^{51})}.
-
-The function @code{post_to_backend} is shown below; we will use it
-again in other examples:
-
-@smallexample
-// merchant/doc/examples/post_to_backend.php
-@include examples/post_to_backend.php
-@end smallexample
-
-After the browser has fetched the contract, the user will
-be given the opportunity to affirm the payment.
-
-
-@section Receiving payments via Taler
-
-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.
-
-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 the
-session state with the browser to remember that the user paid.
-The following code implements this in PHP:
-
-@smallexample
-// merchant/doc/examples/pay_handler.php
-@include examples/pay_handler.php
-@end smallexample
-
-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.
-
-
-@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 in the URI of the fulfillment page the data
-which is needed to allow the page to determine which contract the user is
-trying to access.
-Thus, the fulfillment URL for our example looks like the following:@*
-
-@smallexample
-https://shop.com/fulfillment? \
-transaction_id=<TRANSACTION_ID>×tamp=<CONTRACTTIMESTAMP>
-@end smallexample
-
-
-@*The @code{/fulfillment} handler will then perform the following actions:
-
-@smallexample
-// merchant/doc/examples/fulfillment_handler.php
-@include examples/fulfillment_handler.php
-@end smallexample
-
-
-@node Back-office-integration
-@chapter Integration of GNU Taler with the back office
-
-
-@node Advanced topics
-@chapter Advanced topics
-
-@section Payments using JavaScript
-
-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
-
-
-
-@section Design considerations for the fulfillment page
-
-The fulfillment page mechanism is designed to provide the following two properties:
-
-@enumerate
-@item Taler payments @emph{can} be implemented in DB-less frontends.
-
-@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
-
-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".
diff --git a/php/doc/tutorial.texi b/php/doc/tutorial.texi
@@ -0,0 +1,330 @@
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename manual.info
+@include version.texi
+@settitle The GNU Taler manual for Web shop operators @value{VERSION}
+
+@c Define a new index for options.
+@defcodeindex op
+@c Combine everything into one index (arbitrarily chosen to be the
+@c concept index).
+@syncodeindex op cp
+@c %**end of header
+
+@copying
+This manual is for the GNU Taler merchant backend (version @value{VERSION}, @value{UPDATED}),
+
+Copyright @copyright{} 2016 INRIA
+
+@quotation
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.3 or
+any later version published by the Free Software Foundation; with no
+Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
+Texts. A copy of the license is included in the section entitled
+``GNU Free Documentation License''.
+@end quotation
+@end copying
+@c If your manual is published on paper by the FSF, it should include
+@c The standard FSF Front-Cover and Back-Cover Texts, as given in
+@c maintain.texi.
+@c
+@c Titlepage
+@c
+@titlepage
+@title The GNU Taler manual for Web shops
+@subtitle Version @value{VERSION}
+@subtitle @value{UPDATED}
+@author Marcello Stanisci (@email{marcello.stanisci@@inria.fr})
+@author Christian Grothoff (@email{christian.grothoff@@inria.fr})
+@page
+@vskip 0pt plus 1filll
+@insertcopying
+@end titlepage
+
+@summarycontents
+@contents
+
+@ifnottex
+@node Top
+@top The GNU Taler manual for Web shops
+@insertcopying
+@end ifnottex
+
+@menu
+* Introduction:: Whom this manual is addressed to
+* Hello-world:: How to set up a minimalistic shop
+* Back-office-integration:: How to integrate with the back office
+* Advanced topics:: Detailed solutions to specific issues
+@end menu
+
+
+@node Introduction
+@chapter Introduction
+
+@section About GNU Taler
+
+GNU Taler is an open protocol for an electronic payment system with a
+free software reference implementation. GNU Taler offers secure, fast
+and easy payment processing using well understood cryptographic
+techniques. GNU Taler allows customers to remain anonymous, while
+ensuring that merchants can be held accountable by governments.
+Hence, GNU Taler is compatible with anti-money-laundering (AML) and
+know-your-customer (KYC) regulation, as well as data protection
+regulation (such as GDPR).
+
+
+@section About this manual
+
+This manual is for Web developers and addresses how to integrate GNU Taler
+with Web shops. It describes how to create a Web shop that cooperates with
+a GNU Taler merchant @emph{backend}.
+
+You can download all of the code examples given in this tutorial from
+@url{https://git.taler.net/merchant-frontend-examples.git/tree/php/}.
+
+@c Add section giving an overview of what we will do in the tutorial!
+
+
+@section Architecture overview
+
+The Taler software stack for a merchant consists of four main components:
+
+@itemize
+@item A frontend which interacts with the customer's browser. The
+ frontend enables the customer to build a shopping cart and place
+ an order. Upon payment, it triggers the respective business logic
+ to satisfy the order. This component is not included with Taler,
+ but rather assumed to exist at the merchant. This manual
+ describes how to integrate Taler with Web shop frontends.
+@item A back office application that enables the shop operators to
+ view customer orders, match them to financial transfers, and possibly
+ approve refunds if an order cannot be satisfied. This component is
+ again not included with Taler, but rather assumed to exist at the
+ merchant. This manual will describe how to integrate such a component
+ to handle payments managed by Taler.
+@item A Taler-specific payment backend which makes it easy for the
+ frontend to process financial transactions with Taler. The
+ next two chapters will describe how to install and configure
+ this backend.
+@item A DBMS which stores the transaction history for the Taler backend.
+ For now, the GNU Taler reference implemenation only supports Postgres,
+ but the code could be easily extended to support another DBMS.
+@end itemize
+
+The following image illustrates the various interactions of these
+key components:
+
+@c FIXME, hangs the compilation: @center @image{arch, 3in, 4in}
+@image{arch, 3in, 4in}
+
+
+Basically, the backend provides the cryptographic protocol support,
+stores Taler-specific financial information in a DBMS and communicates
+with the GNU Taler exchange over the Internet. The frontend accesses
+the backend via a RESTful API. As a result, the frontend never has to
+directly communicate with the exchange, and also does not deal with
+sensitive data. In particular, the merchant's signing keys and bank
+account information is encapsulated within the Taler backend.
+
+@c FIXME: this should be a separate manual
+
+
+@node Hello-world
+@chapter Setting up a simple Web shop with GNU Taler
+
+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
+contract offering him the opportunity to make a fixed donation,
+for example to donate 1 KUDOS to the charity operating the shop.
+
+@c NOTE: include explaining wallet installation to Web developer here!
+
+Note that if the customer does not have the Taler wallet installed,
+they should instead be prompted to proceed with a classic dialog for
+credit card payments.
+
+
+@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:
+
+@smallexample
+<form action="/donate-handler.php">
+ <input type="submit" value="Donate!"></input>
+</form>
+@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:
+
+@itemize
+@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.php} implementation is shown below (in PHP):
+
+@smallexample
+// php/donate-handler.php
+@include ../donate-handler.php
+@end smallexample
+
+Given this response, the Taler wallet will fetch the contract from
+@url{/generate-contract.php} 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 via JavaScript.
+
+@c We will consider this case in a later chapter.
+@c FIXME: not yet ;-)
+
+@section Generating the contract
+
+The server-side handler for @code{/generate-contract.php} now has to
+generate a contract proposal about donating 1 KUDOS to the 'Taler
+charity program'. This proposed contract is then POSTed to the
+backend at @code{/contract}. The main result of POSTing the proposal
+to the backend is that it will be cryptographically signed. This is
+necessary as by design the frontend does not perform any cryptographic
+work.
+
+A simple @code{/generate-contract.php} handler may thus look like this:
+
+@smallexample
+// php/generate-contract.php
+@include ../generate-contract.php
+@end smallexample
+
+Note that in practice the frontend might want to generate a monotonically
+increasing series of transaction IDs to avoid a chance of collisions.
+Transaction IDs must be in the range of @math{[0:2^{51})}.
+
+The function @code{post_to_backend} is shown below; we will use it
+again in other examples:
+
+@smallexample
+FIXME: "extract" post_to_backend() from helpers.php in some way.
+@end smallexample
+
+After the browser has fetched the contract, the user will
+be given the opportunity to affirm the payment.
+
+@section Receiving payments via Taler
+
+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.
+
+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 the
+session state with the browser to remember that the user paid.
+The following code implements this in PHP:
+
+@smallexample
+// php/pay.php
+@include ../pay.php
+@end smallexample
+
+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.
+
+
+@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 in the URI of the fulfillment page the data
+which is needed to allow the page to determine which contract the user is
+trying to access.
+Thus, the fulfillment URL for our example looks like the following:@*
+
+@smallexample
+https://shop.com/fulfillment.php? \
+transaction_id=<TRANSACTION_ID>×tamp=<CONTRACTTIMESTAMP>
+@end smallexample
+
+@* @code{fulfillment.php} will then perform the following actions:
+
+@smallexample
+// php/fulfillment.php
+@include ../fulfillment.php
+@end smallexample
+
+@bye
+
+@node Back-office-integration
+@chapter Integration of GNU Taler with the back office
+
+
+@node Advanced topics
+@chapter Advanced topics
+
+@section Payments using JavaScript
+
+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
+
+
+
+@section Design considerations for the fulfillment page
+
+The fulfillment page mechanism is designed to provide the following two properties:
+
+@enumerate
+@item Taler payments @emph{can} be implemented in DB-less frontends.
+
+@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
+
+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".
diff --git a/php/doc/version.texi b/php/doc/version.texi
@@ -0,0 +1,4 @@
+@set UPDATED 11 November 2016
+@set UPDATED-MONTH November 2016
+@set EDITION 0.1.0
+@set VERSION 0.1.0
diff --git a/php/donate-handler.php b/php/donate-handler.php
@@ -1,5 +1,5 @@
<?php
- http_response_code (402); // 402: Payment required
+ http_response_code(402); // 402: Payment required
header ('X-Taler-Contract-Url: /generate-contract.php');
echo "<html>
<head>
diff --git a/php/fulfillment.php b/php/fulfillment.php
@@ -0,0 +1,36 @@
+<?php
+ include 'copying.php';
+ include 'helpers.php';
+
+ session_start();
+
+ if(pull($_SESSION, 'paid', false)){
+ echo sprintf("<p>Thanks for your donation!</p>
+ <br>
+ <p>The transaction ID was: %s; use it to
+ track your money.</p>",
+ $_SESSION['transaction_id']);
+ return;
+ }
+
+ $_SESSION['transaction_id'] = $_GET['transaction_id'];
+
+ $now = new DateTime();
+ $now->setTimestamp(intval($_GET["timestamp"]));
+
+ $rec_proposal = make_contract(intval($_GET['transaction_id']), $now);
+ $response = post_to_backend("/contract", $rec_proposal);
+ file_put_contents("/tmp/php.out", $response["body"], FILE_APPEND);
+ http_response_code($response["code"]);
+ if (200 != $response["status_code"]) {
+ echo build_error($response, "Failed to reconstruct the contract");
+ return;
+ }
+ // The user needs to pay, instruct the wallet to send the payment.
+ $body = json_decode($response['body']);
+ http_response_code(402);
+ header('X-Taler-Contract-Hash: ' . $body->H_contract);
+ header('X-Taler-Pay-Url: ' . url_rel("/pay.php"));
+ header('X-Taler-Offer-Url: ' . '/fixme');
+ return;
+?>
diff --git a/php/generate-contract.php b/php/generate-contract.php
@@ -1,106 +1,21 @@
<?php
-/*
- This file is part of GNU TALER.
- Copyright (C) 2014, 2015 INRIA
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser 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, If not, see <http://www.gnu.org/licenses/>
-*/
-
- include 'config.php';
+ include 'copying.php';
include 'helpers.php';
- function make_contract($transaction_id, $now){
- $contract = array ('amount' =>
- array ('value' => 1,
- 'fraction' => 0,
- 'currency' => "KUDOS"),
- 'max_fee' =>
- array ('value' => 0,
- 'fraction' => 50000,
- 'currency' => "KUDOS"),
- 'transaction_id' => $transaction_id,
- 'products' => array (
- array ('description' => "Donation to charity program",
- 'quantity' => 1,
- 'price' =>
- array ('value' => 1,
- 'fraction' => 0,
- 'currency' => "KUDOS"),
- 'product_id' => 0,
- 'taxes' => array(),
- 'delivery_date' => "/Date(" . $now->getTimestamp() . ")/",
- 'delivery_location' => 'LNAME1')),
- 'timestamp' => "/Date(" . $now->getTimestamp() . ")/",
- 'summary' => "Personal donation to charity program",
- 'expiry' =>
- "/Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")/",
- 'refund_deadline' =>
- "/Date(" . $now->add(new DateInterval('P3M'))->getTimestamp() . ")/",
- 'repurchase_correlation_id' => '',
- 'fulfillment_url' => url_rel("/fulfillment.php?"
- . "transaction_id=$transaction_id×tamp="
- . $now->getTimestamp()),
- 'merchant' =>
- array ('address' => 'LNAME2',
- 'name' => "Charity donation shop",
- 'jurisdiction' => 'LNAME3'),
- 'locations' =>
- array ('LNAME1' =>
- array ('country' => 'Test Country 1',
- 'city' => 'Test City 1',
- 'state' => 'Test State 1',
- 'region' => 'Test Region 1',
- 'province' => 'Test Province 1',
- 'ZIP code' => 49081,
- 'street' => 'test street 1',
- 'street number' => 201),
- 'LNAME2' =>
- array ('country' => 'Test Country 2',
- 'city' => 'Test City 2',
- 'state' => 'Test State 2',
- 'region' => 'Test Region 2',
- 'province' => 'Test Province 2',
- 'ZIP code' => 49082,
- 'street' => 'test street 2',
- 'street number' => 202),
- 'LNAME3' =>
- array ('country' => 'Test Country 3',
- 'city' => 'Test City 3',
- 'state' => 'Test State 3',
- 'region' => 'Test Region 3',
- 'province' => 'Test Province 3',
- 'ZIP code' => 49083)));
- return array ('contract' => $contract);
- }
-
-
-
- /* 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 */
+ // 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
$transaction_id = rand(1,90000); // simplified, do not do this!
$proposal = make_contract($transaction_id, new DateTime('now'));
- # Here the frontend POSTs the proposal to the backend
+ // Here the frontend POSTs the proposal to the backend
$response = post_to_backend("/contract", $proposal);
file_put_contents("/tmp/php.out", $response["body"], FILE_APPEND);
// We always return verbatim what the backend returned
http_response_code($response["code"]);
if (200 != $response["status_code"]) {
- echo json_encode(array(
- 'error' => "internal error",
- 'hint' => "failed to generate contract",
- 'detail' => $response["body"]),
- JSON_PRETTY_PRINT);
- return;
+ echo build_error($response, "Failed to generate contract");
+ return;
}
echo $response["body"];
?>
diff --git a/php/helpers.php b/php/helpers.php
@@ -1,22 +1,13 @@
<?php
-/*
- This file is part of GNU TALER.
- Copyright (C) 2014, 2015 INRIA
+ include 'copying.php';
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser 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, If not, see <http://www.gnu.org/licenses/>
-*/
-
- include 'config.php';
+ function &pull(&$arr, $idx, $default) {
+ if (!isset($arr[$idx])) {
+ $arr[$idx] = $default;
+ }
+ return $arr[$idx];
+ }
function url_join($base, $path) {
// Please note that this trivial way of joining URLs is
@@ -34,8 +25,81 @@
return $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].$path;
}
+ function make_contract($transaction_id, $now){
+ $contract = array ('amount' =>
+ array ('value' => 1,
+ 'fraction' => 0,
+ 'currency' => "KUDOS"),
+ 'max_fee' =>
+ array ('value' => 0,
+ 'fraction' => 50000,
+ 'currency' => "KUDOS"),
+ 'transaction_id' => $transaction_id,
+ 'products' => array (
+ array ('description' => "Donation to charity program",
+ 'quantity' => 1,
+ 'price' =>
+ array ('value' => 1,
+ 'fraction' => 0,
+ 'currency' => "KUDOS"),
+ 'product_id' => 0,
+ 'taxes' => array(),
+ 'delivery_date' => "/Date(" . $now->getTimestamp() . ")/",
+ 'delivery_location' => 'LNAME1')),
+ 'summary' => "Personal donation to charity program",
+ 'timestamp' => "/Date(" . $now->getTimestamp() . ")/",
+ 'fulfillment_url' => url_rel("/fulfillment.php?"
+ . "transaction_id=$transaction_id×tamp="
+ . $now->getTimestamp()),
+ 'repurchase_correlation_id' => '',
+ 'expiry' =>
+ "/Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")/",
+ 'refund_deadline' =>
+ "/Date(" . $now->add(new DateInterval('P3M'))->getTimestamp() . ")/",
+ 'merchant' =>
+ array ('address' => 'LNAME2',
+ 'name' => "Charity donation shop",
+ 'jurisdiction' => 'LNAME3'),
+ 'locations' =>
+ array ('LNAME1' =>
+ array ('country' => 'Test Country 1',
+ 'city' => 'Test City 1',
+ 'state' => 'Test State 1',
+ 'region' => 'Test Region 1',
+ 'province' => 'Test Province 1',
+ 'ZIP code' => 49081,
+ 'street' => 'test street 1',
+ 'street number' => 201),
+ 'LNAME2' =>
+ array ('country' => 'Test Country 2',
+ 'city' => 'Test City 2',
+ 'state' => 'Test State 2',
+ 'region' => 'Test Region 2',
+ 'province' => 'Test Province 2',
+ 'ZIP code' => 49082,
+ 'street' => 'test street 2',
+ 'street number' => 202),
+ 'LNAME3' =>
+ array ('country' => 'Test Country 3',
+ 'city' => 'Test City 3',
+ 'state' => 'Test State 3',
+ 'region' => 'Test Region 3',
+ 'province' => 'Test Province 3',
+ 'ZIP code' => 49083)));
+ return array ('contract' => $contract);
+ }
+
+ function build_error($response, $hint){
+ return json_encode(array(
+ 'error' => "internal error",
+ 'hint' => $hint,
+ 'detail' => $response["body"]),
+ JSON_PRETTY_PRINT);
+ }
+
+
/**
- * 'body' is an array, representing the JSON to POST. NOTE: we do NOT
+ * 'body' is an object, representing the JSON to POST. NOTE: we do NOT
* rely on a more structured way of doing HTTP, like the one offered by
* pecl_http, as its installation was NOT always straightforward.
*/
diff --git a/php/index.php b/php/index.php
@@ -1,4 +1,7 @@
<?php
+
+ include 'copying.php';
+
echo "<html>
<head>
<title>Taler tutorial</title>
diff --git a/php/pay.php b/php/pay.php
@@ -0,0 +1,20 @@
+<?php
+ include 'copying.php';
+
+ session_start();
+ if(!isset($_SESSION['paid'])){
+ echo "<p>No session active. Aborting.</p>";
+ return;
+ }
+ // Get coins.
+ $body = file_get_contents('php://input');
+ $response = post_to_backend("/pay", json_decode($body));
+ http_response_header($response['status_code']);
+ if (200 != $response['status_code']){
+ echo build_error($response, "Could not send paymnet to backend");
+ return;
+ }
+ // Payment went through!
+ $_SESSION['paid'] = true;
+ return;
+?>