commit e51e36a052628cebd0cbafa9ad63bf0e9e3b8ad3
parent c0fd375b2a875c7fd50aaa13ef51aaab4ea2a08c
Author: Marcello Stanisci <marcello.stanisci@inria.fr>
Date: Mon, 14 Nov 2016 23:29:07 +0100
PHP files
Diffstat:
22 files changed, 1863 insertions(+), 0 deletions(-)
diff --git a/php/blog/articles/article_images.php b/php/blog/articles/article_images.php
@@ -0,0 +1,82 @@
+<?php
+ /**
+ * Parse $html_filename and add an entry of the type
+ * "$html_filename" => ("img1.png", "img2.png") for each
+ * encountered 'img' tag having the 'src' attribute formatted
+ * as "/essay/<article_filename>/data/img1.png", to the JSON which
+ * associates any article with its images.
+ * Note that <article_filename> has the final '.html' removed
+ */
+ function add_article($html_filename){
+ $doc = new DOMDocument();
+ $doc->loadHTMLFile($html_filename);
+ $xpath = new DOMXPath($doc);
+ $xpath->registerNamespace('php', 'http://php.net/xpath');
+ $xpath->registerPhpFunctions('preg_match');
+ $elements = $xpath->query('//img[php:functionString("preg_match", "@^/data/[^/]+/[^/]+@", @src) > 0]');
+ $db_filename = "articles_images.json";
+ $json_str;
+ if (file_exists($db_filename))
+ $json_str = file_get_contents($db_filename);
+ else
+ $json_str = "";
+ $json_db = json_decode($json_str);
+ $json_db->$html_filename = array();
+ foreach($elements as $img){
+ $value = $img->getAttributeNode("src")->value;
+ array_push($json_db->$html_filename, basename($value));
+ }
+ file_put_contents($db_filename, json_encode($json_db));
+ }
+
+ /* suppress warnings due to parsing HTML5 */
+ libxml_use_internal_errors(true);
+
+ add_article("scrap1_10.html");
+ add_article("scrap1_11.html");
+ add_article("scrap1_12.html");
+ add_article("scrap1_13.html");
+ add_article("scrap1_14.html");
+ add_article("scrap1_15.html");
+ add_article("scrap1_16.html");
+ add_article("scrap1_17.html");
+ add_article("scrap1_18.html");
+ add_article("scrap1_19.html");
+ add_article("scrap1_1.html");
+ add_article("scrap1_20.html");
+ add_article("scrap1_21.html");
+ add_article("scrap1_22.html");
+ add_article("scrap1_23.html");
+ add_article("scrap1_24.html");
+ add_article("scrap1_25.html");
+ add_article("scrap1_26.html");
+ add_article("scrap1_27.html");
+ add_article("scrap1_28.html");
+ add_article("scrap1_29.html");
+ add_article("scrap1_2.html");
+ add_article("scrap1_30.html");
+ add_article("scrap1_31.html");
+ add_article("scrap1_32.html");
+ add_article("scrap1_33.html");
+ add_article("scrap1_34.html");
+ add_article("scrap1_35.html");
+ add_article("scrap1_36.html");
+ add_article("scrap1_37.html");
+ add_article("scrap1_38.html");
+ add_article("scrap1_39.html");
+ add_article("scrap1_3.html");
+ add_article("scrap1_40.html");
+ add_article("scrap1_41.html");
+ add_article("scrap1_42.html");
+ add_article("scrap1_43.html");
+ add_article("scrap1_46.html");
+ add_article("scrap1_47.html");
+ add_article("scrap1_4.html");
+ add_article("scrap1_5.html");
+ add_article("scrap1_6.html");
+ add_article("scrap1_7.html");
+ add_article("scrap1_8.html");
+ add_article("scrap1_9.html");
+ add_article("scrap1_U.0.html");
+ add_article("scrap1_U.1.html");
+?>
diff --git a/php/blog/blog_lib.php b/php/blog/blog_lib.php
@@ -0,0 +1,53 @@
+<?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/>
+*/
+
+ /**
+ * Take a (article's) filename and return its
+ * teaser. It has the articles folder hardcoded
+ */
+ function get_title($name){
+ $content = file_get_contents("articles/$name.html");
+ $doc = new DOMDocument();
+ $doc->loadHTML($content);
+ $finder = new DOMXPath($doc);
+ $query_set = $finder->query("//h1[@class='chapter' or @class='unnumbered']");
+ if (1 != $query_set->length)
+ return "No title for this item";
+ // assuming all the articles are well-formed..
+ return $query_set->item(0)->nodeValue;
+ }
+
+ /**
+ * Take a (article's) filename and return its
+ * DOM. It has the articles folder hardcoded
+ */
+ function get_article($name){
+ $raw_content = file_get_contents("articles/$name.html");
+ return $raw_content;
+ }
+
+ /**
+ * Fetch the page $page and return its
+ * DOM.
+ */
+ function get_page($page){
+ $content = file_get_contents($page);
+ $doc = new DOMDocument();
+ $doc->loadHTML($content);
+ return $doc;
+ }
+?>
diff --git a/php/blog/essay_cc-fulfillment.php b/php/blog/essay_cc-fulfillment.php
@@ -0,0 +1,36 @@
+<?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/>
+
+ @author Marcello Stanisci
+*/
+
+ include '../../copylib/util.php';
+ include './blog_lib.php';
+
+ $article = get($_GET['article']);
+ if (null == $article){
+ http_response_code(400);
+ echo "Bad request (no article specified)";
+ return;
+ }
+
+ session_start();
+ $payments = &pull($_SESSION, "payments", array());
+ $payments[$article] = array("ispayed" => true);
+ $fulfillment_url = url_rel("essay_fulfillment.php");
+ header("Location: $fulfillment_url");
+ die();
+?>
diff --git a/php/blog/essay_contract.php b/php/blog/essay_contract.php
@@ -0,0 +1,72 @@
+<?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/>
+*/
+
+ include("../../copylib/merchants.php");
+ include("../../copylib/util.php");
+ include("../../copylib/config.php");
+ include("./blog_lib.php");
+ $article = get($_GET['article']);
+ if (null == $article){
+ echo message_from_missing_param("article", "/");
+ die();
+ }
+ // send contract
+ $transaction_id = rand(0, 1001);
+ $now = new DateTime('now');
+ $teaser = get_title($article);
+ $amount_value = 0;
+ $amount_fraction = 50000;
+ $teatax = array ();
+ $transaction_id = rand(0, 1001);
+ $fulfillment_url = url_rel("essay_fulfillment.php")
+ . '×tamp=' . $now->getTimestamp()
+ . '&tid=' . $transaction_id;
+
+ $contract_json = generate_contract(array(
+ "amount_value" => $amount_value,
+ "amount_fraction" => $amount_fraction,
+ "currency" => $MERCHANT_CURRENCY,
+ "refund_delta" => $REFUND_DELTA,
+ "transaction_id" => $transaction_id,
+ "description" => trim($teaser),
+ "merchant_name" => "Free Software Foundation (demo)",
+ "product_id" => $article,
+ "correlation_id" => $article,
+ "taxes" => $teatax,
+ "now" => $now,
+ "fulfillment_url" => $fulfillment_url)
+ );
+ $resp = give_to_backend("backend/contract",
+ $contract_json);
+ $status_code = $resp->getResponseCode();
+ http_response_code ($status_code);
+ if ($status_code != 200){
+ echo json_encode(array(
+ 'error' => "internal error",
+ 'hint' => "backend indicated error",
+ 'detail' => $resp->body->toString()
+ ), JSON_PRETTY_PRINT);
+ }
+ else {
+ $got_json = json_decode($resp->body->toString(), true);
+ $hc = $got_json["H_contract"];
+ session_start();
+ $payments = &pull($_SESSION, "payments", array());
+ $payments[$article] = array("ispayed" => false);
+ echo $resp->body->toString();
+ }
+?>
diff --git a/php/blog/essay_fulfillment.php b/php/blog/essay_fulfillment.php
@@ -0,0 +1,89 @@
+<?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/>
+*/
+ include '../../copylib/util.php';
+ include '../../copylib/merchants.php';
+ include '../../copylib/config.php';
+ include './blog_lib.php';
+
+ $article = get($_GET['article']);
+ if (null == $article){
+ http_response_code(400);
+ echo message_from_missing_param("article", "/");
+ return;
+ }
+ session_start();
+ //syslog($LOG_ERR, "merchant: official log system");
+ $payments = &pull($_SESSION, 'payments', array());
+ $my_payment = &pull($payments, $article, false);
+
+ $pay_url = url_rel("essay_pay.php");
+ $offering_url = url_rel("essay_fulfillment.php", true);
+ $offering_url .= "?article=$article";
+
+ // In PHP false == null
+ if (null == get($payments[$article]['ispayed']) || false == $my_payment){
+ $tid = get($_GET['tid']);
+ $timestamp = get($_GET['timestamp']);
+ // 1st time
+ if (null == $tid || null == $timestamp){
+ $js_code = "get_contract(\"$article\");";
+ $cc_page = template("./essay_cc-form.html", array('article' => $article, 'jscode' => $js_code));
+ echo $cc_page;
+ return;
+ }
+ // using deeplink (whether 1st time or not)
+ // restore contract
+ $now = new DateTime();
+ $now->setTimestamp(intval($timestamp));
+
+ $contract_rec = generate_contract(array(
+ "amount_value" => 0,
+ "amount_fraction" => 50000,
+ "currency" => $MERCHANT_CURRENCY,
+ "refund_delta" => $REFUND_DELTA,
+ "merchant_name" => "Free Software Foundation (demo)",
+ "transaction_id" => intval($tid),
+ "description" => trim(get_title($article)),
+ "product_id" => $article,
+ "correlation_id" => $article,
+ "taxes" => array(),
+ "now" => $now,
+ "fulfillment_url" => get_full_uri())
+ );
+ $resp = give_to_backend("backend/contract",
+ $contract_rec);
+ if ($resp->getResponseCode() != 200){
+ echo json_encode(array(
+ 'error' => "internal error",
+ 'hint' => "non hashable contract",
+ 'detail' => $resp->body->toString()
+ ), JSON_PRETTY_PRINT);
+ die();
+ }
+ $hc = json_decode($resp->body->toString(), true)['H_contract'];
+ $my_payment['hc'] = $hc;
+ //syslog($LOG_INFO, "sending payment event");
+ $js_code = "taler.executePayment(\"$hc\", \"$pay_url\", \"$offering_url\");";
+ $cc_page = template("./essay_cc-form.html", array('article' => $article, 'jscode' => $js_code));
+ echo $cc_page;
+ return;
+ }
+ // control here == article payed
+ //syslog($LOG_INFO, "showing article");
+ $article = get_article($article);
+ echo $article;
+?>
diff --git a/php/blog/essay_pay.php b/php/blog/essay_pay.php
@@ -0,0 +1,77 @@
+<?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/>
+*/
+
+ include("../../copylib/merchants.php");
+ include("../../copylib/util.php");
+ include("./blog_lib.php");
+
+ /*FIXME: the following log gets annoyingly sent to _any_ open terminal */
+ /*syslog($LOG_INFO, "paying");*/
+
+ $article = get($_GET["article"]);
+ if (empty($article)){
+ http_response_code(400);
+ echo json_encode(array(
+ "error" => "missing parameter",
+ "parameter" => "article"
+ ));
+ return;
+ }
+ $deposit_permission = file_get_contents('php://input');
+ session_start();
+ if (!isset($_SESSION["payments"])) {
+ $json = json_encode(
+ array(
+ "error" => "no payments ongoing",
+ "status" => 500,
+ "detail" => "the shop has no state for any article"
+ )
+ );
+ echo $json;
+ die();
+ }
+ $payments = &pull($_SESSION, "payments", array());
+ $dec_dep_perm = json_decode($deposit_permission, true);
+ if ($dec_dep_perm['H_contract'] != $payments[$article]['hc']){
+ $json = json_encode(
+ array(
+ "error" => "ill behaved wallet",
+ "status" => 400,
+ "detail" => "article payed differs from article to be shown"
+ )
+ );
+ echo $json;
+ die();
+ }
+
+ // with the article that's going to be payed
+ $resp = give_to_backend("backend/pay",
+ $deposit_permission);
+ $status_code = $resp->getResponseCode();
+ http_response_code ($status_code);
+ if ($status_code != 200)
+ {
+ $json = json_encode(
+ array(
+ "error" => "backend error",
+ "status" => $status_code,
+ "detail" => $resp->body->toString()));
+ echo $json;
+ die();
+ }
+ $payments[$article]['ispayed'] = true;
+?>
diff --git a/php/blog/images.php b/php/blog/images.php
@@ -0,0 +1,58 @@
+<?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/>
+*/
+
+ include '../../copylib/util.php';
+
+ $article = get($_GET['article']);
+ $image = get($_GET['image']);
+ session_start();
+ $payments = &pull($_SESSION, 'payments', false);
+ if (!$payments) {
+ echo "No session active";
+ return 400;
+ }
+ if (null == get($payments[$article]['ispayed'])) {
+ echo "Article not payed";
+ return 400;
+ }
+
+ $db_filename = "articles/articles_images.json";
+ $json_str;
+ if (file_exists($db_filename))
+ $json_str = file_get_contents($db_filename);
+ else {
+ echo "Internal server error: data registry not found";
+ return 500;
+ }
+ $db = json_decode($json_str, true);
+ $article_images = get($db[$article . ".html"]);
+ if (null == $article_images) {
+ echo "This article has no images to sell";
+ return 400;
+ }
+ if (false === array_search($image, $article_images)) {
+ echo "Requested image does not belong to article '$article'";
+ return 400;
+ }
+
+ $image_path = "data/" . $image;
+ $fp = fopen($image_path, 'rb');
+ header("Content-Type: image/png"); // fix image extension
+ header("Content-Length: " . filesize($image_path));
+ fpassthru($fp);
+ exit;
+?>
diff --git a/php/config.php b/php/config.php
@@ -0,0 +1,3 @@
+<?php
+ $CONFIG = array("BACKEND_BASEURL" => "https://backend.demo.taler.net/")
+?>
diff --git a/php/copylib/config.php b/php/copylib/config.php
@@ -0,0 +1,40 @@
+<?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/>
+*/
+
+$REFUND_DELTA = 'P3M';
+// set to false when done with local tests
+$explicit_currency = "PUDOS";
+$MERCHANT_CURRENCY = $explicit_currency;
+
+$host = $_SERVER["HTTP_HOST"];
+switch ($host) {
+case "blog.demo.taler.net":
+case "shop.demo.taler.net":
+ $MERCHANT_CURRENCY = "KUDOS";
+ break;
+case "blog.test.taler.net":
+case "shop.test.taler.net":
+ $MERCHANT_CURRENCY = "PUDOS";
+ break;
+default:
+ if(false == $explicit_currency){
+ http_response_code (500);
+ echo "<p>Bank configuration error: No currency for domain $host</p>\n";
+ die();
+ }
+}
+?>
diff --git a/php/copylib/merchants.php b/php/copylib/merchants.php
@@ -0,0 +1,105 @@
+<?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/>
+*/
+
+/**
+ * Return a contract proposition to forward to the backend
+ * Note that `teatax` is an associative array representing a
+ * Taler-style amount (so it has the usual <amount,fration,currency>
+ * triple). Moreover, `teatax` should be a *list* of taxes
+ */
+function generate_contract($args){
+ $contract = array ('amount' =>
+ array ('value' => $args['amount_value'],
+ 'fraction' => $args['amount_fraction'],
+ 'currency' => $args['currency']),
+ 'max_fee' =>
+ array ('value' => 3,
+ 'fraction' => 01010,
+ 'currency' => $args['currency']),
+ 'transaction_id' => $args['transaction_id'],
+ 'products' => array (
+ array ('description' => $args['description'],
+ 'quantity' => 1,
+ 'price' =>
+ array ('value' => $args['amount_value'],
+ 'fraction' => $args['amount_fraction'],
+ 'currency' => $args['currency']),
+ 'product_id' => $args['product_id'],
+ 'taxes' => $args['taxes'],
+ 'delivery_date' => "Some Date Format",
+ 'delivery_location' => 'LNAME1')),
+ 'timestamp' => "/Date(" . $args['now']->getTimestamp() . ")/",
+ 'expiry' =>
+ "/Date(" . $args['now']->add(new DateInterval('P2W'))->getTimestamp() . ")/",
+ 'refund_deadline' =>
+ "/Date(" . $args['now']->add(new DateInterval($args['refund_delta']))->getTimestamp() . ")/",
+ 'repurchase_correlation_id' => $args['correlation_id'],
+ 'fulfillment_url' => $args['fulfillment_url'],
+ 'merchant' =>
+ array ('address' => 'LNAME2',
+ 'name' => $args['merchant_name'],
+ 'jurisdiction' => 'LNAME3'),
+ 'locations' =>
+ array ('LNAME1' =>
+ array ('country' => 'Test Country',
+ 'city' => 'Test City',
+ 'state' => 'Test State',
+ 'region' => 'Test Region',
+ 'province' => 'Test Province',
+ 'ZIP code' => 4908,
+ 'street' => 'test street',
+ 'street number' => 20),
+ 'LNAME2' =>
+ array ('country' => 'Test Country',
+ 'city' => 'Test City',
+ 'state' => 'Test State',
+ 'region' => 'Test Region',
+ 'province' => 'Test Province',
+ 'ZIP code' => 4908,
+ 'street' => 'test street',
+ 'street number' => 20),
+ 'LNAME3' =>
+ array ('country' => 'Test Country',
+ 'city' => 'Test City',
+ 'state' => 'Test State',
+ 'region' => 'Test Region',
+ 'province' => 'Test Province',
+ 'ZIP code' => 4908)));
+ $json = json_encode (array ('contract' => $contract), JSON_PRETTY_PRINT);
+ return $json;
+}
+
+
+/**
+ * Feed `$json` to the backend and return the "(pecl) http response object"
+ * corresponding to the `$backend_relative_url` call
+ */
+function give_to_backend($backend_relative_url, $json){
+ $url = url_join("http://".$_SERVER["HTTP_HOST"], $backend_relative_url);
+
+ $req = new http\Client\Request("POST",
+ $url,
+ array ("Content-Type" => "application/json"));
+
+ $req->getBody()->append($json);
+
+ // Execute the HTTP request
+ $client = new http\Client;
+ $client->enqueue($req)->send();
+ return $client->getResponse();
+}
+?>
diff --git a/php/copylib/util.php b/php/copylib/util.php
@@ -0,0 +1,88 @@
+<?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/>
+*/
+
+function get(&$var, $default=null) {
+ return isset($var) ? $var : $default;
+}
+
+function &pull(&$arr, $idx, $default) {
+ if (!isset($arr[$idx])) {
+ $arr[$idx] = $default;
+ }
+ return $arr[$idx];
+}
+
+function message_from_missing_param($missing, $link, $link_name="home page"){
+ return "<p>Bad request, no $missing given. Return to <a href=\"$link\">$link_name</a></p>";
+}
+
+function article_state_to_str($article_state){
+ if(null == $article_state || !isset($article_state))
+ return "undefined state";
+ $str = "Is payed? ";
+ $str .= $article_state['ispayed'] ? "true," : "false,";
+ if(!isset($article_state['hc']))
+ $str .= " no hashcode for this article";
+ else $str .= " " . $article_state['hc'];
+ return $str;
+}
+
+function log_string($str){
+ file_put_contents("/tmp/frontend.dbg", $str . "\n", FILE_APPEND);
+}
+
+function get_full_uri(){
+
+ return $_SERVER['REQUEST_SCHEME'] . '://'
+ . $_SERVER['HTTP_HOST']
+ . $_SERVER['REQUEST_URI'];
+}
+
+function url_join($base, $path, $strip=false) {
+ $flags = $strip ? (http\Url::STRIP_PATH|http\URL::STRIP_QUERY) : 0;
+ return (new http\URL($base, null, $flags))
+ ->mod(array ("path" => $path), http\Url::JOIN_PATH|http\URL::SANITIZE_PATH)
+ ->toString();
+}
+
+// Get a url with a path relative to the
+// current script's path.
+function url_rel($path, $strip=false) {
+ return url_join(
+ $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'],
+ $path,
+ $strip);
+}
+
+function template($file, $array) {
+ if (file_exists($file)) {
+ $output = file_get_contents($file);
+ foreach ($array as $key => $val) {
+ $replace = '{'.$key.'}';
+ $output = str_replace($replace, $val, $output);
+ }
+ return $output;
+ }
+}
+
+function str_to_dom($str){
+ $doc = new DOMDocument();
+ $doc->loadHTML($str);
+ return $doc;
+
+}
+?>
diff --git a/php/doc/Makefile.am b/php/doc/Makefile.am
@@ -0,0 +1,13 @@
+all: manual.pdf manual.html
+
+manual.pdf: arch.pdf manual.texi
+ texi2pdf manual.texi
+manual.html: arch.jpg manual.texi
+ texi2html manual.texi
+arch.pdf: arch.dot
+ dot -Tpdf arch.dot > arch.pdf
+arch.jpg: arch.dot
+ dot -Tjpg arch.dot > arch.jpg
+
+info_TEXINFOS = manual.texi
+manual_TEXINFOS = version.texi
diff --git a/php/doc/manual.texi b/php/doc/manual.texi
@@ -0,0 +1,333 @@
+\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/donate-handler.php b/php/donate-handler.php
@@ -0,0 +1,12 @@
+<?php
+ http_response_code (402); // 402: Payment required
+ header ('X-Taler-Contract-Url: /php/generate-contract.php');
+ echo "<html>
+ <head>
+ <title>Select payment method</title>
+ </head>
+ <body>
+ Here you should put the HTML for the non-Taler (credit card) payment.
+ </body>
+ </html>";
+?>
diff --git a/php/generate-contract.php b/php/generate-contract.php
@@ -0,0 +1,121 @@
+<?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';
+
+ 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() . ")/",
+ 'expiry' =>
+ "/Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")/",
+ 'refund_deadline' =>
+ "/Date(" . $now->add(new DateInterval('P3M'))->getTimestamp() . ")/",
+ 'repurchase_correlation_id' => $args['correlation_id'],
+ 'fulfillment_url' => $_SERVER['REQUEST_SCHEME']
+ .'://'
+ .$_SERVER['HTTP_HOST']
+ ."/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' => 4908 1,
+ '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' => 4908 2,
+ '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)));
+ $json = json_encode (array ('contract' => $contract), JSON_PRETTY_PRINT);
+ return $json;
+ }
+
+
+ /**
+ * Feed `$json` to the backend and return the "(pecl) http response object"
+ * corresponding to the `$backend_relative_url` call
+ */
+ function post_to_backend($backend_relative_url, $json){
+ $url = url_join("http://".$CONFIG["BACKEND_BASEURL"], $backend_relative_url);
+ $req = new http\Client\Request("POST",
+ $url,
+ array ("Content-Type" => "application/json"));
+ $req->getBody()->append($json);
+ // Execute the HTTP request
+ $client = new http\Client;
+ $client->enqueue($req)->send();
+ return $client->getResponse();
+ }
+
+
+ /* 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
+ $response = post_to_backend("/contract", $proposal);
+ if (200 != $response->getResponseCode()) {
+ echo json_encode(array(
+ ’error’ => "internal error",
+ ’hint’ => "failed to generate contract",
+ ’detail’ => $resp->body->toString()
+ ), JSON_PRETTY_PRINT);
+ return;
+ }
+ echo $response->body;
+?>
diff --git a/php/index.php b/php/index.php
@@ -0,0 +1,12 @@
+<?php
+ echo "<html>
+ <head>
+ <title>Taler tutorial</title>
+ </head>
+ <body>
+ <form action='/php/donate-handler.php'>
+ <input type='submit' value='Donate!'></input>
+ </form>
+ </body>
+ </html>";
+?>
diff --git a/php/shop/checkout.php b/php/shop/checkout.php
@@ -0,0 +1,183 @@
+<!DOCTYPE html>
+<!--
+ This file is aprt 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 (GNU LGPL) as published by the
+ Free Software
+ Foundation, either version 3 of the License, or (at your option)
+ any later version. The code is distributed WITHOUT ANY WARRANTY;
+ without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU LGPL for more details.
+
+ As additional permission under GNU LGPL version 3 section 7, you
+ may distribute non-source (e.g., minimized or compacted) forms of
+ that code without the copy of the GNU LGPL normally required by
+ section 4, provided you include this license notice and a URL
+ through which recipients can access the Corresponding Source.
+
+-->
+<html>
+<head>
+ <title>Toy Store - Payment method - Taler Demo</title>
+ <link rel="stylesheet" type="text/css" href="web-common/style.css">
+ <script type="application/javascript" src="web-common/taler-presence.js"></script>
+</head>
+<body>
+
+<?php
+ // get the donation information from form
+ $donation_receiver = $_POST['donation_receiver'];
+ $donation_amount = $_POST['donation_amount'];
+ $donation_currency = $_POST['donation_currency'];
+
+ // get frational part
+ list ($donation_value, $donation_fraction) = explode (".", $donation_amount, 2);
+ // create PHP session and store donation information in session
+ $donation_fraction = (float) ("0." . $donation_fraction);
+ session_start();
+ $_SESSION['receiver'] = $donation_receiver;
+ $_SESSION['amount_value'] = (int) $donation_amount;
+ $_SESSION['amount_fraction'] = (int) ($donation_fraction * 1000000);
+ $_SESSION['currency'] = $donation_currency;
+?>
+
+ <header>
+ <div id="logo">
+ <svg height="100" width="100">
+ <circle cx="50" cy="50" r="40" stroke="darkcyan" stroke-width="6" fill="white" />
+ <text x="19" y="82" font-family="Verdana" font-size="90" fill="darkcyan">S</text>
+ </svg>
+ </div>
+ <!--#include virtual="web-common/dropdown-navbar.html" -->
+ <h1 class="nav">Toy Store - Select payment method</h1>
+ </header>
+
+ <aside class="sidebar" id="left">
+ </aside>
+
+ <section id="main">
+ <article>
+ <h1>Select your payment method</h1>
+
+ <p>
+ This is an example for a "checkout" page of a Web shop.
+ On the previous page, you have created the shopping cart
+ and decided which product to buy (i.e. which project to
+ donate KUDOS to). Now in this page, you are asked to
+ select a payment option. As Taler is not yet universally
+ used, we expect merchants will offer various payment options.
+ </p>
+ <p>
+ The page also demonstrates how to only enable (or show) the Taler
+ option if Taler is actually supported by the browser. For example,
+ if you disable the Taler extension now, the Taler payment option
+ will be disabled in the page. Naturally, you could also trivially
+ hide the Taler option entirely by changing the visibility instead.
+ </p>
+ <p>
+ Note that you MUST select Taler here for the demo to continue,
+ as the other payment options are just placeholders and not
+ really working in the demonstration. Also, it is of course
+ possible to ask the user to make this choice already on the
+ previous page (with the shopping cart), we just separated the
+ two steps to keep each step as simple as possible.
+ </p>
+
+ <form name="tform" action="" method="POST">
+ <div id="opt-form" align="left"><br>
+ <input type="radio" name="payment_system" value="lisa"
+ id="lisa-radio-button-id">Lisa</input>
+ <br/>
+ <input type="radio" name="payment_system" value="ycard">You Card</input>
+ <br/>
+ <input type="radio" name="payment_system" value="cardme">Card Me</input>
+ <br/>
+ <input type="radio" name="payment_system" value="taler"
+ checked
+ id="taler-radio-button-id" class="taler-installed-enable">Taler</input>
+ <br/>
+ <input type="button" onclick="pay(this.form)" value="Ok"></input>
+ </div>
+ </form>
+
+ </article>
+ </section>
+
+<script type="text/javascript">
+
+/* This function is called from "taler_pay" after
+ we downloaded the JSON contract from the merchant.
+ We now need to pass it to the extension. */
+function handle_contract(contract_wrapper) {
+ var cEvent = new CustomEvent('taler-confirm-contract', {
+ detail: {
+ contract_wrapper: contract_wrapper
+ }
+ });
+ document.dispatchEvent(cEvent);
+};
+
+
+/* Trigger Taler contract generation on the server, and pass the
+ contract to the extension once we got it. */
+function taler_pay(form) {
+ var contract_request = new XMLHttpRequest();
+
+ /* Note that the URL we give here is specific to the Demo-shop
+ and not dictated by the protocol: each web shop can
+ have its own way of generating and transmitting the
+ contract, there just must be a way to get the contract
+ and to pass it to the wallet when the user selects 'Pay'. */
+ contract_request.open("GET", "generate_taler_contract.php", true);
+ contract_request.onload = function (e) {
+ if (contract_request.readyState == 4) {
+ if (contract_request.status == 200) {
+ /* display contract_requestificate (i.e. it sends the JSON string
+ to the extension) alert (contract_request.responseText); */
+ console.log("response text:", contract_request.responseText);
+ var contract_wrapper = JSON.parse(contract_request.responseText);
+ if (!contract_wrapper) {
+ console.error("response text is invalid JSON");
+ return;
+ }
+ handle_contract(contract_wrapper);
+ } else {
+ /* There was an error obtaining the contract from the merchant,
+ obviously this should not happen. To keep it simple, we just
+ alert the user to the error. */
+ alert("Failure to download contract from merchant " +
+ "(" + contract_request.status + "):\n" +
+ contract_request.responseText);
+ }
+ }
+ };
+ contract_request.onerror = function (e) {
+ /* There was an error obtaining the contract from the merchant,
+ obviously this should not happen. To keep it simple, we just
+ alert the user to the error. */
+ alert("Failure requesting the contract:\n" + contract_request.statusText);
+ };
+ contract_request.send();
+}
+
+
+/* This function is called when the user presses the
+ 'Ok' button. We are now supposed to trigger the
+ "correct" payment system logic. For this demo, we
+ only handle "taler". */
+function pay(form) {
+ var choice = form.elements["payment_system"].value;
+ if (choice == "taler") {
+ taler_pay(form);
+ }
+ else {
+ alert("You selected '" + choice + "', but we do not support this payment system in the demo.");
+ }
+};
+
+</script>
+</body>
+</html>
diff --git a/php/shop/config.php b/php/shop/config.php
@@ -0,0 +1,20 @@
+<?php
+/* This file is in the public domain */
+
+$host = $_SERVER["HTTP_HOST"];
+
+switch ($host) {
+case "shop.demo.taler.net":
+ $SHOP_CURRENCY = "KUDOS";
+ break;
+case "shop.test.taler.net":
+ $SHOP_CURRENCY = "PUDOS";
+ break;
+default:
+ http_response_code ($status_code);
+ echo "<p>Configuration error: No currency for domain $host</p>\n";
+ die();
+ break;
+}
+
+?>
diff --git a/php/shop/fulfillment.php b/php/shop/fulfillment.php
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<!--
+ 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, see <http://www.gnu.org/licenses/>
+-->
+<html lang="en">
+<head>
+ <title>Taler's "Demo" Shop</title>
+ <link rel="stylesheet" type="text/css" href="web-common/style.css">
+ <script type="application/javascript" src="web-common/taler-wallet-lib.js"></script>
+ <script type="application/javascript">
+ function makeVisible() {
+ function cb() {
+ document.body.style.display = "";
+ }
+ document.addEventListener("DOMContentLoaded", cb, false);
+ }
+ </script>
+</head>
+<body style="display:none;">
+ <header>
+ <div id="logo">
+ <svg height="100" width="100">
+ <circle cx="50" cy="50" r="40" stroke="darkcyan" stroke-width="6" fill="white" />
+ <text x="19" y="82" font-family="Verdana" font-size="90" fill="darkcyan">S</text>
+ </svg>
+ </div>
+ <!--#include virtual="web-common/dropdown-navbar.html"-->
+ <h1 class="nav">Toy Store - Product Page</h1>
+ </header>
+
+ <aside class="sidebar" id="left">
+ </aside>
+
+ <section id="main">
+ <article>
+<?php
+
+include '../../copylib/util.php';
+include "../../copylib/merchants.php";
+
+$receiver = get($_GET["receiver"]);
+$now = new DateTime();
+$now->setTimestamp(intval(get($_GET["timestamp"])));
+
+if (empty($receiver)) {
+ http_response_code(400);
+ echo "<p>Bad request (UUID missing)</p>";
+ return;
+}
+
+session_start();
+
+$payments = &pull($_SESSION, 'payments', array());
+$my_payment = &pull($payments, $receiver, array());
+
+// This will keep the query parameters.
+$pay_url = url_rel("pay.php");
+$offering_url = url_rel("index.php", true);
+
+if (array() === $my_payment || true !== get($my_payment["is_payed"], false)) {
+ // restore contract
+ $contract = generate_contract(array(
+ "amount_value" => intval($_GET['aval']),
+ "amount_fraction" => intval($_GET['afrac']),
+ "currency" => $_GET['acurr'],
+ "refund_delta" => 'P3M',
+ "transaction_id" => intval($_GET['tid']),
+ "description" => "Donation to " . $receiver,
+ "product_id" => "unused",
+ "correlation_id" => "",
+ "merchant_name" => "Kudos Inc.",
+ "taxes" => array(),
+ "now" => $now,
+ "fulfillment_url" => get_full_uri())
+ );
+
+ $resp = give_to_backend("backend/contract", $contract);
+ if ($resp->getResponseCode() != 200){
+ echo json_encode(array(
+ 'error' => "internal error",
+ 'hint' => "failed to regenerate contract",
+ 'detail' => $resp->body->toString()
+ ), JSON_PRETTY_PRINT);
+ return;
+ }
+
+ $hc = json_decode($resp->body->toString(), true)['H_contract'];
+ $my_payment['is_payed'] = false;
+ $my_payment['hc'] = $hc;
+ echo "<p>you have not payed for this contract: " . $hc . "</p>";
+ echo "<p>Asking the wallet to re-execute it ... </p>";
+ echo "<script>taler.executePayment('$hc', '$pay_url', '$offering_url');</script>";
+ return;
+}
+
+$news = false;
+switch ($receiver) {
+ case "Taler":
+ $news = "https://taler.net/news";
+ break;
+ case "GNUnet":
+ $news = "https://gnunet.org/";
+ break;
+ case "Tor":
+ $news = "https://www.torproject.org/press/press.html.en";
+ break;
+}
+
+$msg = "<p>Thanks for donating to " . $receiver . ".</p>";
+if ($news) {
+ $msg .= "<p>Check our latest <a href=\"" . $news . "\">news!</a></p>";
+}
+
+echo $msg;
+
+echo "<script>makeVisible();</script>";
+
+?>
+ </article>
+ </section>
+</body>
+</html>
diff --git a/php/shop/generate_taler_contract.php b/php/shop/generate_taler_contract.php
@@ -0,0 +1,92 @@
+<?php
+/*
+ This file is part of GNU TALER.
+ Copyright (C) 2014-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/>
+ */
+
+include '../../copylib/util.php';
+include "../../copylib/config.php";
+include "../../copylib/merchants.php";
+
+session_start();
+
+if (!isset($_SESSION['receiver']))
+{
+ http_response_code(400);
+ die();
+}
+
+$receiver = $_SESSION['receiver'];
+$amount_value = intval($_SESSION['amount_value']);
+$amount_fraction = intval($_SESSION['amount_fraction']);
+$currency = $_SESSION['currency'];
+
+// generate a front-end transaction id.
+// In production context, we might want to
+// record this value somewhere together
+// with the rest of the contract data.
+$transaction_id = rand(0, 2<<40);
+
+// Human-readable description of this deal
+$desc = "Donation to " . $receiver;
+
+// Take a timestamp
+$now = new DateTime('now');
+
+// Include all information so we can
+// restore the contract without storing it
+$fulfillment_url = url_rel("fulfillment.php")
+ . '?timestamp=' . $now->getTimestamp()
+ . '&receiver=' . urlencode($receiver)
+ . '&aval=' . urlencode($amount_value)
+ . '&afrac=' . urlencode($amount_fraction)
+ . '&acurr=' . urlencode($currency)
+ . '&tid=' . $transaction_id;
+
+$contract = generate_contract(array(
+ "amount_value" => $amount_value,
+ "amount_fraction" => $amount_fraction,
+ "currency" => $currency,
+ "refund_delta" => 'P3M',
+ "transaction_id" => $transaction_id,
+ "description" => $desc,
+ "product_id" => "unused",
+ "correlation_id" => "",
+ "merchant_name" => "Kudos Inc.",
+ "taxes" => array(),
+ "now" => $now,
+ "fulfillment_url" => $fulfillment_url)
+);
+
+$resp = give_to_backend("backend/contract", $contract);
+
+// Our response code is the same we got from the backend:
+http_response_code($resp->getResponseCode());
+
+// Now generate our body
+if ($resp->getResponseCode() != 200)
+{
+ echo json_encode(array(
+ 'error' => "internal error",
+ 'hint' => "backend indicated error",
+ 'detail' => $resp->body->toString()
+ ), JSON_PRETTY_PRINT);
+}
+else
+{
+ # no state here
+ $got_json = json_decode($resp->body->toString(), true);
+ echo json_encode ($got_json, JSON_PRETTY_PRINT);
+}
+?>
diff --git a/php/shop/index.php b/php/shop/index.php
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<!--
+ 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/>
+-->
+<?php
+require_once "../../copylib/config.php";
+session_destroy();
+?>
+<html lang="en">
+<head>
+ <title>Taler Donation Demo</title>
+ <link rel="stylesheet" type="text/css" href="web-common/style.css">
+ <script src="web-common/taler-presence.js" type="text/javascript"></script>
+<script type="text/javascript">
+<?php
+echo "\tvar shop_currency = '$MERCHANT_CURRENCY';\n";
+?>
+
+ function addOption(value, label) {
+ var s = document.getElementById("taler-donation");
+ var e = document.createElement("option");
+ e.textContent = label ? label : ("".concat(value, " ", shop_currency));
+ e.value = value;
+ s.appendChild(e);
+ }
+
+ function init() {
+ var e = document.getElementById("currency-input");
+ e.value = shop_currency;
+ addOption("0.1");
+ addOption("1.0");
+ addOption("6.0", "".concat(5, " ", shop_currency));
+ addOption("10");
+ }
+
+ document.addEventListener("DOMContentLoaded", init);
+
+</script>
+</head>
+
+<body>
+ <header>
+ <div id="logo">
+ <svg height="100" width="100">
+ <circle cx="50" cy="50" r="40" stroke="darkcyan" stroke-width="6" fill="white" />
+ <text x="19" y="82" font-family="Verdana" font-size="90" fill="darkcyan">S</text>
+ </svg>
+ </div>
+ <!--#include virtual="web-common/dropdown-navbar.html" -->
+ <h1 class="nav">Toy "Store" - Taler Demo</h1>
+ </header>
+
+ <aside class="sidebar" id="left">
+ </aside>
+
+ <section id="main">
+ <article>
+ <h1>Welcome to the Taler Donation "Shop" Demo</h1>
+
+ <p>This "toy" website provides you with the ability to
+ experience using the
+ <a href="https://www.taler.net/">GNU Taler</a>
+ payment system without using
+ valuable currency. Instead, for the demonstrator we
+ will be using a "toy" currency, KUDOS. However, please remember that
+ Taler is designed to work with ordinary currencies, such
+ as Dollars or Euros, not just toy currencies.
+ <br>
+ This page, <tt>shop.demo.taler.net</tt> models the behavior of a
+ typical Web shop supporting Taler. The other pages of the demo,
+ <tt>exchange.demo.taler.net</tt> and
+ <tt>bank.demo.taler.net</tt>, correspond to a Taler exchange
+ and bank with tight Taler integration respectively. You
+ may also enjoy visiting the <tt>blog.demo.taler.net</tt>.
+ </p>
+ </article>
+
+ <div class="taler-installed-hide">
+ <h2>Installing the Taler wallet</h2>
+ First, you need to install the Taler wallet browser extension.
+ Install the wallet
+ <span id="install-done" style="visibility: hidden">(done)</span>
+ <ul>
+ <li>from the app store for <a href="https://chrome.google.com/webstore/detail/gnu-taler-wallet/millncjiddlpgdmkklmhfadpacifaonc">Google
+ Chrome and Chromium</a>
+ </li>
+ <li>By visiting our <a href="/landing">landing page</a>.
+ </li>
+ </ul>
+ Wallets for other browsers will be provided in the near future.
+ </div>
+
+ <div class="taler-installed-show">
+ <p>Please choose a project and the amount of KUDOS you
+ wish to donate:</p>
+
+ <form name="tform" action="checkout.php" method="POST">
+ <div class="participation" id="fake-shop">
+ <br>
+ <input type="radio" name="donation_receiver" value="Taler" checked="true">GNU Taler</input>
+ <br>
+ <input type="radio" name="donation_receiver" value="Tor">Tor</input>
+ <br>
+ <input type="radio" name="donation_receiver" value="GNUnet">GNUnet</input>
+ <br>
+ <select id="taler-donation" name="donation_amount">
+ <!-- options will be added dynamically -->
+ </select>
+ <input id="currency-input" type="hidden" name="donation_currency"/>
+ <input type="submit" name="keyName" value="Donate!"/>
+ <br>
+ <br>
+ </div>
+ </form>
+ <p>
+ (*) To make it a bit more fun, the 5 KUDOS option
+ is deliberately implemented with a fault: the merchant will try to
+ make you donate 6 KUDOS instead of the 5 KUDOS you got to see. But do
+ not worry, you will be given the opportunity to review the
+ final offer from the merchant in a window secured
+ by the Taler extension. That way, you can spot the
+ error before committing to an incorrect contract.
+ </p>
+ </div>
+ </section>
+</body>
+</html>
diff --git a/php/shop/pay.php b/php/shop/pay.php
@@ -0,0 +1,101 @@
+<?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/>
+
+*/
+
+include '../../copylib/util.php';
+log_string("getting money");
+// so we won't generate a response for the wrong receiver.
+$receiver = get($_GET["receiver"]);
+if (empty($receiver))
+{
+ http_response_code(400);
+ echo json_encode(array(
+ "error" => "missing parameter",
+ "parameter" => "receiver"
+ ));
+ return;
+}
+
+session_start();
+$payments = &pull($_SESSION, "payments", array());
+
+if (!isset($payments[$receiver]))
+{
+ http_response_code(400);
+ echo json_encode(array(
+ "error" => "no payment session active"
+ ));
+ return;
+}
+
+$post_body = file_get_contents('php://input');
+$deposit_permission = json_decode ($post_body, true);
+
+// Check if the receiver is actually *mentioned* in the contract
+if ($payments[$receiver]['hc'] != $deposit_permission['H_contract']) {
+
+ $json = json_encode(
+ array(
+ "error" => "ill behaved wallet",
+ "status" => 400,
+ "detail" => "deposit permission mismatches with reconstructed contract"
+ )
+ );
+ echo $json;
+ die();
+}
+
+
+/* Craft the HTTP request, note that the backend
+ could be on an entirely different machine if
+ desired. */
+
+// Backend is relative to the shop site.
+$url = url_rel("backend/pay");
+
+$req = new http\Client\Request("POST",
+ $url,
+ array("Content-Type" => "application/json"));
+$req->getBody()->append (json_encode ($deposit_permission));
+
+// Execute the HTTP request to the backend
+$client = new http\Client;
+$client->enqueue($req)->send();
+
+// Fetch the response
+$resp = $client->getResponse();
+$status_code = $resp->getResponseCode();
+
+// Our response code is the same we got from the backend:
+http_response_code($status_code);
+
+// Now generate our body
+if ($status_code != 200)
+{
+ $json = json_encode(
+ array(
+ "error" => "backend error",
+ "status" => $status_code,
+ "detail" => $resp->body->toString()));
+ echo $json;
+ die();
+}
+
+$payments = &pull($_SESSION, "payments", array());
+$payments[$receiver]['is_payed'] = true;
+
+?>