From 44b4a0b20e798756826d7e3175e453b439e758dc Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Wed, 20 Jan 2016 18:15:46 +0100 Subject: Adding most of blog-type website. --- src/frontend/checkout.php | 2 +- src/frontend_blog/README | 20 +++ src/frontend_blog/articles/essay-x.html | 19 +++ src/frontend_blog/blog_lib.php | 26 ++++ src/frontend_blog/essay_checkout.php | 244 ++++++++++++++++++++++++++++++++ src/frontend_blog/essay_contract.php | 78 ++++++++++ src/frontend_blog/essay_fulfillment.php | 20 +++ src/frontend_blog/essay_pay.php | 75 ++++++++++ src/frontend_blog/execute.js | 33 +++++ src/frontend_blog/execute.php | 56 ++++++++ src/frontend_blog/execute.tsx | 41 ++++++ src/frontend_blog/index.html | 79 +++++++++++ src/frontend_blog/style.css | 123 ++++++++++++++++ src/frontend_blog/teaser.php | 46 ++++++ src/frontend_lib/merchants.php | 91 ++++++++++++ 15 files changed, 952 insertions(+), 1 deletion(-) create mode 100644 src/frontend_blog/README create mode 100644 src/frontend_blog/articles/essay-x.html create mode 100644 src/frontend_blog/blog_lib.php create mode 100644 src/frontend_blog/essay_checkout.php create mode 100644 src/frontend_blog/essay_contract.php create mode 100644 src/frontend_blog/essay_fulfillment.php create mode 100644 src/frontend_blog/essay_pay.php create mode 100644 src/frontend_blog/execute.js create mode 100644 src/frontend_blog/execute.php create mode 100644 src/frontend_blog/execute.tsx create mode 100644 src/frontend_blog/index.html create mode 100644 src/frontend_blog/style.css create mode 100644 src/frontend_blog/teaser.php create mode 100644 src/frontend_lib/merchants.php diff --git a/src/frontend/checkout.php b/src/frontend/checkout.php index 21d4d8c4..cf789b11 100644 --- a/src/frontend/checkout.php +++ b/src/frontend/checkout.php @@ -237,7 +237,7 @@ function taler_wallet_unload_cb(aEvent) (as the extension may be loaded/enabled after the page was loaded) */ function signal_taler_wallet_onload() { - var eve = new Event('taler-checkout-probe'); + var eve = new Event('taler-probe'); document.dispatchEvent(eve); }; diff --git a/src/frontend_blog/README b/src/frontend_blog/README new file mode 100644 index 00000000..8cfb490f --- /dev/null +++ b/src/frontend_blog/README @@ -0,0 +1,20 @@ +This directory contains the files implementing the frontend of the new merchant architecture. + +Only tested on nginx. To run the website, it suffices to have all the .php and .html files +in the same directory, and having PHP (with the extension 'pecl_http' enabled) enabled. + +File |What implements +-------------------------------- +o index.html | The homepage. Here it is possible to decide how much donate + to whom you would like. + +o fake_wire_transfer.php | PHP script that takes the wire transfer request and passes it on + | to the /admin/add/incoming API of the demo-mint. + +o checkout.php | The "payment selection" that is the form + that allows the user to choose the payment method he wishes to use. + It also implements the request of certificate and trigger the certificate + viewer in the extension when it arrives in the customer's machine. + +o pay.php | Actual receiving of money, plus it gives back a "fullfillment" page + that informs the user of his well ended deal. diff --git a/src/frontend_blog/articles/essay-x.html b/src/frontend_blog/articles/essay-x.html new file mode 100644 index 00000000..48db9115 --- /dev/null +++ b/src/frontend_blog/articles/essay-x.html @@ -0,0 +1,19 @@ + + + + Blog site demonstration + + + +
+

+ The x essay is +

+
+
+

+ about something +

+
+ + diff --git a/src/frontend_blog/blog_lib.php b/src/frontend_blog/blog_lib.php new file mode 100644 index 00000000..4238e993 --- /dev/null +++ b/src/frontend_blog/blog_lib.php @@ -0,0 +1,26 @@ +loadHTML($content); + $teaser = $doc->getElementById("teaser"); + return $teaser; +} + +/** + * Take a (article's) filename and return its + * DOM. It has the articles folder hardcoded + */ +function get_article($name){ + $content = file_get_contents("articles/$name.html"); + $doc = new DOMDocument(); + $doc->loadHTML($content); + return $doc; +} + +?> diff --git a/src/frontend_blog/essay_checkout.php b/src/frontend_blog/essay_checkout.php new file mode 100644 index 00000000..c013d7c5 --- /dev/null +++ b/src/frontend_blog/essay_checkout.php @@ -0,0 +1,244 @@ + + + + Blog - Payment method - Taler Demo + + + + + +
+ + +

Blog - Select payment method

+
+ + + +
+
+ +

Select your payment method

+ +

+ 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. +

+

+ 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. +

+

+ 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. +

+ +
+

+ Lisa +
+ You Card +
+ Card Me +
+ Taler +
+ +
+
+ +
+
+ + + + diff --git a/src/frontend_blog/essay_contract.php b/src/frontend_blog/essay_contract.php new file mode 100644 index 00000000..4aeb8c99 --- /dev/null +++ b/src/frontend_blog/essay_contract.php @@ -0,0 +1,78 @@ + +*/ + +/** + * This file should: + * + * 1. check if some article is going to be bought + * 2. check if the wallet is installed TODO + * 3. get the contract (having the teaser as product detail) to the wallet + * + */ +include("../frontend_lib/merchants.php"); +include("./blog_lib.php"); +session_start(); +if (!isset($_SESSION['article'])){ + echo "Please land here just to buy articles"; + die(); + } +$article = $_SESSION['article']; +// 2 to-do + +// send contract +$transaction_id = rand(0, 1001); +$p_id = hexdec(substr(sha1($article), -5)); +$teatax = array ('value' => 1, + 'fraction' => 0, + 'currency' => "KUDOS"); +$now = new DateTime('now'); +$teaser = get_teaser($article); +$pay_url = "essay_pay.php"; +$exec_url = "execute.php"; +$contract_json = generate_contract(1, + 0, + "KUDOS", + $transaction_id, + trim($teaser->nodeValue), + $p_id, + $teatax, + $now, + $pay_url, + $exec_url); +$resp = give_to_backend($_SERVER["HTTP_HOST"], + "backend/contract", + $contract_json); + +// Our response code is the same we got from the backend: +$status_code = $resp->getResponseCode(); +http_response_code ($status_code); + +// Now generate our body +if ($status_code != 200) +{ + echo "Error while generating the contract"; + echo $resp->body->toString (); +} +else +{ $got_json = json_decode ($resp->body->toString ()); + $_SESSION['H_contract'] = $got_json->H_contract; + $_SESSION['article_value'] = 1; + $_SESSION['article_fraction'] = 0; + $_SESSION['article_currency'] = "KUDOS"; + echo $resp->body->toString (); +} +?> diff --git a/src/frontend_blog/essay_fulfillment.php b/src/frontend_blog/essay_fulfillment.php new file mode 100644 index 00000000..df2f8cfa --- /dev/null +++ b/src/frontend_blog/essay_fulfillment.php @@ -0,0 +1,20 @@ +saveHTML(); +?> diff --git a/src/frontend_blog/essay_pay.php b/src/frontend_blog/essay_pay.php new file mode 100644 index 00000000..6d906b74 --- /dev/null +++ b/src/frontend_blog/essay_pay.php @@ -0,0 +1,75 @@ + +*/ + +/** + * This file should: + * 1. Check if the session is valid + * 2. augment the deposit permission with missin values + * 3. forward payment to backend + */ + +if (!isset($_SESSION['H_contract'])) +{ + echo "No session active."; + http_response_code (301); + return; +} + +$article = $_SESSION['article']; +$post_body = file_get_contents('php://input'); +$deposit_permission = json_decode ($post_body, true); +$to_add = array('max_fee' => array('value' => 3, + 'fraction' => 8, + 'currency' => $_SESSION['currency']), + 'amount' => array('value' => $_SESSION['article_value'], + 'fraction' => $_SESSION['article_fraction'], + 'currency' => $_SESSION['article_currency'])); +$complete_deposit_permission = array_merge($deposit_permission, $to_add); +$resp = give_to_backend($_SERVER['HTTP_HOST'], + "backend/pay", + $complete_deposit_permission); +$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) +{ + /* error: just forwarding to the wallet what + gotten from the backend (which is forwarding 'as is' + the error gotten from the mint) */ + echo json_encode ($new_deposit_permission); + echo "Error came from the backend, payment undone. Status $status_code\n"; + echo "\n"; + echo $resp->body->toString (); +} +else +{ + $_SESSION['payment_ok'] = true; + if (!isset($_SESSION['allowed_articles'])){ + $_SESSION['allowed_articles'] = array ($_SESSION['article'] => true); + else $_SESSION['allowed_articles'] = + array_push($_SESSION['allowed_articles'], array ($article => true)); + } + + http_response_code (301); + $url = (new http\URL($_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'])) + ->mod(array ("path" => "essay_fulfillment.php?article=$article"), http\Url::JOIN_PATH); + header("Location: $url"); + die(); +} + diff --git a/src/frontend_blog/execute.js b/src/frontend_blog/execute.js new file mode 100644 index 00000000..a9045cb5 --- /dev/null +++ b/src/frontend_blog/execute.js @@ -0,0 +1,33 @@ +"use strict"; +// JSX literals are compiled to calls to React.createElement calls. +let React = { + createElement: function (tag, props, ...children) { + let e = document.createElement(tag); + for (let k in props) { + e.setAttribute(k, props[k]); + } + for (let child of children) { + if ("string" === typeof child || "number" == typeof child) { + child = document.createTextNode(child); + } + e.appendChild(child); + } + return e; + } +}; +document.addEventListener("DOMContentLoaded", function (e) { + var eve = new CustomEvent('taler-execute-payment', { detail: { H_contract: h_contract } }); + document.dispatchEvent(eve); +}); +function replace(el, r) { + el.parentNode.replaceChild(r, el); +} +document.addEventListener("taler-payment-result", function (e) { + if (!e.detail.success) { + alert("Payment failed\n" + JSON.stringify(e.detail)); + return; + } + console.log("finished payment"); + let msg = React.createElement("div", null, "Payment successful. View your ", React.createElement("a", {"href": e.detail.fulfillmentUrl}, "product"), "."); + replace(document.getElementById("loading"), msg); +}); diff --git a/src/frontend_blog/execute.php b/src/frontend_blog/execute.php new file mode 100644 index 00000000..33021fd3 --- /dev/null +++ b/src/frontend_blog/execute.php @@ -0,0 +1,56 @@ + + + + Toy Store - Taler Demo + + + + + + + +
+ +

Toy Store - Taler Demo

+
+ + + +
+

Executing Payment ...

+
Loading...
+ + diff --git a/src/frontend_blog/execute.tsx b/src/frontend_blog/execute.tsx new file mode 100644 index 00000000..67cf8e06 --- /dev/null +++ b/src/frontend_blog/execute.tsx @@ -0,0 +1,41 @@ +"use strict"; + + +// JSX literals are compiled to calls to React.createElement calls. +let React = { + createElement: function(tag, props, ...children) { + let e = document.createElement(tag); + for (let k in props) { + e.setAttribute(k, props[k]); + } + for (let child of children) { + if ("string" === typeof child || "number" == typeof child) { + child = document.createTextNode(child); + } + e.appendChild(child); + } + return e; + } +}; + +declare var h_contract: string; + +document.addEventListener("DOMContentLoaded", function (e) { + var eve = new CustomEvent('taler-execute-payment', {detail: {H_contract: h_contract}}); + document.dispatchEvent(eve); +}); + +function replace(el, r) { + el.parentNode.replaceChild(r, el); +} + +document.addEventListener("taler-payment-result", function (e: CustomEvent) { + if (!e.detail.success) { + alert("Payment failed\n" + JSON.stringify(e.detail)); + return; + } + console.log("finished payment"); + let msg = +
Payment successful. View your product.
; + replace(document.getElementById("loading"), msg); +}); diff --git a/src/frontend_blog/index.html b/src/frontend_blog/index.html new file mode 100644 index 00000000..a1ad1db7 --- /dev/null +++ b/src/frontend_blog/index.html @@ -0,0 +1,79 @@ + + + + Blog site demonstration + + + + + +
+ + +

Blog site demonstration

+
+ + + +
+
+

Welcome to the Taler blog

+

This blog simulates how a website selling articles which integrates + Taler should work. If you don't have a Taler wallet installed, + please visit demo.taler.net. + By clicking on some article below, you get its teaser shown, but the + actual purchase happens once you will click on the 'read more' link + associated with that teaser. +

+
+
+ +
+

Articles list

+
+ + +
+
+ + diff --git a/src/frontend_blog/style.css b/src/frontend_blog/style.css new file mode 100644 index 00000000..c2cc51ee --- /dev/null +++ b/src/frontend_blog/style.css @@ -0,0 +1,123 @@ +body { + background-color: white; + margin: 0; + padding: 0; + font-family: Verdana, sans; +} + +header { + width: 100%; + height: 100px; + margin: 0; + padding: 0; + border-bottom: 1px solid black; +} + +header h1 { + font-size: 200%; + margin: 0; + padding: 0 0 0 120px; + position: relative; + top: 50%; + transform: translateY(-50%); +} + +header #logo { + float: left; + width: 100px; + padding: 0; + margin: 0; + text-align: center; + border-right: 1px solid black; +} + +aside { + width: 100px; + float: left; +} + +section#main { + margin: 0 0 0 100px; + padding: 20px; + border-left: 1px solid black; + height: 100%; + max-width: 40em; +} + +section#main h1:first-child { + margin-top: 0; +} + +h1 { + font-size: 160%; +} + +h2 { + font-size: 140%; +} + +h3 { + font-size: 120%; +} + +h4, h5, h6 { + font-size: 100%; +} + +.loader { + font-size: 10px; + margin: 50px auto; + text-indent: -9999em; + width: 11em; + height: 11em; + border-radius: 50%; + background: #ffffff; + background: -moz-linear-gradient(left, #000 10%, rgba(255, 255, 255, 0) 42%); + background: -webkit-linear-gradient(left, #000 10%, rgba(255, 255, 255, 0) 42%); + background: -o-linear-gradient(left, #000 10%, rgba(255, 255, 255, 0) 42%); + background: -ms-linear-gradient(left, #000 10%, rgba(255, 255, 255, 0) 42%); + position: relative; + -webkit-animation: load3 1.4s infinite linear; + animation: load3 1.4s infinite linear; + -webkit-transform: translateZ(0); + -ms-transform: translateZ(0); + transform: translateZ(0); +} + +.loader:after { + background: #fff; + width: 75%; + height: 75%; + border-radius: 50%; + content: ''; + margin: auto; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; +} + +@-webkit-keyframes load3 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@keyframes load3 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + + diff --git a/src/frontend_blog/teaser.php b/src/frontend_blog/teaser.php new file mode 100644 index 00000000..e5f2e97b --- /dev/null +++ b/src/frontend_blog/teaser.php @@ -0,0 +1,46 @@ + + +*/ + +/** + * This file should: + * + * 1. fetch the article teaser and attach a 'read more' link to it + * + */ +$article = (isset($_GET['article']) ? $_GET['article'] : "No article"); +$content = file_get_contents("articles/$article.html"); +$doc = new DOMDocument(); +$doc->loadHTML($content); +$teaser = $doc->getElementById("teaser"); +?> + + + <?php echo $article ?> + + + +nodeValue + . "
read more";}; ?> + + + diff --git a/src/frontend_lib/merchants.php b/src/frontend_lib/merchants.php new file mode 100644 index 00000000..bbb0d90e --- /dev/null +++ b/src/frontend_lib/merchants.php @@ -0,0 +1,91 @@ + + * triple). Moreover, `teatax` should be a *list* of taxes + */ +function generate_contract($amount_value, + $amount_fraction, + $currency, + $transaction_id, + $desc, + $p_id, + $teatax, + $now, + $pay_url, + $exec_url){ + $contract = array ('amount' => array ('value' => $amount_value, + 'fraction' => $amount_fraction, + 'currency' => $currency), + 'max_fee' => array ('value' => 3, + 'fraction' => 01010, + 'currency' => $currency), + 'transaction_id' => $transaction_id, + 'products' => array ( + array ('description' => $desc, + 'quantity' => 1, + 'price' => array ('value' => $amount_value, + 'fraction' => $amount_fraction, + 'currency' => $currency), + 'product_id' => $p_id, + 'taxes' => array (array ('teatax' => $teatax)), + 'delivery_date' => "Some Date Format", + 'delivery_location' => 'LNAME1')), + 'timestamp' => "/Date(" . $now->getTimestamp() . ")/", + 'pay_url' => $pay_url, + 'exec_url' => $exec_url, + 'expiry' => "/Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")/", + 'refund_deadline' => "/Date(" . $now->add(new DateInterval('P3M'))->getTimestamp() . ")/", + 'merchant' => array ('address' => 'LNAME2', + 'name' => 'test merchant', + '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, 'exec_url' => $exec_url, 'pay_url' => $pay_url), 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_host, $backend_relative_url, $json){ + $url = (new http\URL("http://$backend_host")) + ->mod(array ("path" => $backend_relative_url), http\Url::JOIN_PATH); + + $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(); +} +?> -- cgit v1.2.3