merchant-frontend-examples

ZZZ: Inactive/Deprecated
Log | Files | Refs

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:
A.gitignore | 3+++
AMakefile | 14++++++++++++++
DMakefile.am | 3---
Agraphics/arch.dot | 22++++++++++++++++++++++
Agraphics/arch.jpg | 0
Agraphics/arch.pdf | 0
Agraphics/arch.png | 0
Aphp/copying.php | 18++++++++++++++++++
Dphp/doc/manual.texi | 333-------------------------------------------------------------------------------
Aphp/doc/tutorial.texi | 330+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aphp/doc/version.texi | 4++++
Mphp/donate-handler.php | 2+-
Aphp/fulfillment.php | 36++++++++++++++++++++++++++++++++++++
Mphp/generate-contract.php | 99++++++-------------------------------------------------------------------------
Mphp/helpers.php | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mphp/index.php | 3+++
Aphp/pay.php | 20++++++++++++++++++++
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>&timestamp=<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>&timestamp=<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&timestamp=" - . $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&timestamp=" + . $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; +?>