summaryrefslogtreecommitdiff
path: root/talerdonations
diff options
context:
space:
mode:
Diffstat (limited to 'talerdonations')
-rw-r--r--talerdonations/donations/donations.py225
-rw-r--r--talerdonations/donations/templates/backoffice.html46
-rw-r--r--talerdonations/donations/templates/base.html10
-rw-r--r--talerdonations/donations/templates/checkout.html1
-rw-r--r--talerdonations/donations/templates/execute-payment.html12
-rw-r--r--talerdonations/donations/templates/fallback.html28
-rw-r--r--talerdonations/donations/templates/index.html31
-rw-r--r--talerdonations/donations/templates/provider-not-supported.html5
-rw-r--r--talerdonations/helpers.py101
9 files changed, 116 insertions, 343 deletions
diff --git a/talerdonations/donations/donations.py b/talerdonations/donations/donations.py
index 9b3208a..598cca6 100644
--- a/talerdonations/donations/donations.py
+++ b/talerdonations/donations/donations.py
@@ -20,9 +20,9 @@ import logging
import os
import base64
import random
-from datetime import datetime
import requests
import flask
+import traceback
from ..talerconfig import TalerConfig
from ..helpers import (make_url, \
expect_parameter, amount_from_float, \
@@ -40,17 +40,64 @@ app.secret_key = base64.b64encode(os.urandom(64)).decode('utf-8')
TC = TalerConfig.from_env()
BACKEND_URL = TC["frontends"]["backend"].value_string(required=True)
CURRENCY = TC["taler"]["currency"].value_string(required=True)
-MAX_FEE = dict(value=3, fraction=0, currency=CURRENCY)
app.config.from_object(__name__)
@app.context_processor
def utility_processor():
- def url(my_url):
- return join_urlparts(flask.request.script_root, my_url)
def env(name, default=None):
return os.environ.get(name, default)
- return dict(url=url, env=env)
+ return dict(env=env)
+
+
+def err_abort(abort_status_code, **params):
+ t = flask.render_template("templates/error.html", **params)
+ flask.abort(flask.make_response(t, abort_status_code))
+
+
+def backend_get(endpoint, params):
+ try:
+ resp = requests.get(urljoin(BACKEND_URL, endpoint), params=params)
+ except requests.ConnectionError:
+ err_abort(500, message="Could not establish connection to backend")
+ try:
+ response_json = resp.json()
+ except ValueError:
+ err_abort(500, message="Could not parse response from backend")
+ if resp.status_code != 200:
+ err_abort(500, message="Backend returned error status",
+ json=response_json, status_code=resp.status_code)
+ return response_json
+
+
+def backend_post(endpoint, json):
+ try:
+ resp = requests.post(urljoin(BACKEND_URL, endpoint), json=json)
+ except requests.ConnectionError:
+ err_abort(500, message="Could not establish connection to backend")
+ try:
+ response_json = resp.json()
+ except ValueError:
+ err_abort(500, message="Could not parse response from backend",
+ status_code=resp.status_code)
+ if resp.status_code != 200:
+ err_abort(500, message="Backend returned error status",
+ json=response_json, status_code=resp.status_code)
+ return response_json
+
+
+def expect_parameter(name):
+ val = flask.args.get(name)
+ if not val:
+ return err_abort(400, "parameter '{}' required".format(name))
+ return val
+
+
+@app.errorhandler(Exception)
+def internal_error(e):
+ return flask.render_template("templates/error.html",
+ message="Internal error",
+ stack=traceback.format_exc())
@app.route("/")
@@ -64,151 +111,65 @@ def javascript_licensing():
@app.route("/checkout", methods=["GET"])
def checkout():
- amount_str = expect_parameter("donation_amount")
+ amount = expect_parameter("donation_amount")
donation_receiver = expect_parameter("donation_receiver")
- try:
- float(amount_str)
- except ValueError:
- LOGGER.warning("Invalid amount ('%s')", amount_str)
- return flask.make_response(
- flask.jsonify(error="invalid amount"),
- 400)
- display_alert = flask.request.args.get("display_alert", None)
+ donation_receiver = expect_parameter("donation_donor")
return flask.render_template(
"templates/checkout.html",
donation_amount=amount_str,
donation_receiver=donation_receiver,
- merchant_currency=CURRENCY,
- display_alert=display_alert)
+ donation_donor=donation_donor,
+ merchant_currency=CURRENCY)
-@app.route("/generate-contract", methods=["GET"])
-def generate_contract():
- try:
- donation_receiver = expect_parameter("donation_receiver")
- donation_amount = expect_parameter("donation_amount")
- except MissingParameterException as exc:
- return flask.jsonify(
- dict(error="Missing parameter '%s'" % exc)), 400
- amount = amount_from_float(float(donation_amount))
- order_id = "donation-%s-%X-%s" % \
- (donation_receiver,
- random.randint(0, 0xFFFFFFFF),
- datetime.today().strftime('%H_%M_%S'))
- order = dict(
- summary="Donation!",
- nonce=flask.request.args.get("nonce"),
- amount=amount,
- max_fee=dict(value=1, fraction=0, currency=CURRENCY),
- order_id=order_id,
- products=[
- dict(
- description="Donation to %s" % (donation_receiver,),
- quantity=1,
- product_id=0,
- price=amount,
- ),
- ],
- fulfillment_url=make_url("/fulfillment", ("order_id", order_id)),
- pay_url=make_url("/pay"),
- merchant=dict(
- instance=donation_receiver,
- address="nowhere",
- name="Kudos Inc.",
- jurisdiction="none",
- ),
- )
- resp = requests.post(urljoin(BACKEND_URL, 'proposal'),
- json=dict(order=order))
- if resp.status_code != 200:
- # It is important to use 'backend_error()', as it handles
- # the case where the backend gives NO JSON as response.
- # For example, if it dies, or nginx hijacks somehow the
- # response.
- return backend_error(resp)
- return flask.jsonify(resp.json()), resp.status_code
+@app.route("/provider-not-supported")
+def provider_not_supported():
+ return flask.render_template( "templates/provider-not-supported.html")
+
@app.route("/donate")
def donate():
donation_receiver = expect_parameter("donation_receiver")
donation_amount = expect_parameter("donation_amount")
+ donation_donor = expect_parameter("donation_donor")
payment_system = expect_parameter("payment_system")
if payment_system != "taler":
- return flask.redirect(make_url("checkout",
- ("donation_receiver", donation_receiver),
- ("donation_amount", donation_amount),
- ("display_alert", True)))
- response = flask.make_response(flask.render_template('templates/fallback.html'), 402)
- response.headers["X-Taler-Contract-Url"] = \
- make_url("/generate-contract",
- ("donation_receiver", donation_receiver),
- ("donation_amount", donation_amount))
- return response
-
-
-@app.route("/fulfillment")
-def fulfillment():
- order_id = expect_parameter("order_id")
- payed_order_ids = flask.session.get("payed_order_ids", [])
- print("order_id:", repr(order_id))
- print("session:", repr(flask.session))
- if order_id in payed_order_ids:
- data = payed_order_ids[order_id]
- return flask.render_template(
- "templates/fulfillment.html",
- donation_receiver=data["donation_receiver"],
- donation_amount=data["donation_amount"],
- order_id=order_id,
- currency=CURRENCY)
-
- response = flask.make_response(flask.render_template("templates/fallback.html"), 402)
- response.headers["X-Taler-Contract-Query"] = "fulfillment_url"
- response.headers["X-Taler-Offer-Url"] = make_url("/")
- return response
+ return flask.redirect(flask.url_for(provider_not_supported))
+ fulfillment_url = flask.url_for(fulfillment, order_id=order_id, receiver=donation_receiver, _external=True)
+ order = dict(
+ amount=donation_amount,
+ extra=dict(donor=donation_donor, receiver=donation_receiver),
+ fulfillment_url=fulfillment_url,
+ instance=donation_receiver,
+ summary="Donation to {}".format(donation_receiver),
+ )
+ proposal_resp = backend_post("proposal", dict(order=order))
+ order_id = proposal_resp["order_id"]
+ return flask.redirect(flask.url_for(fulfillment, order_id=order_id))
-@app.route("/pay", methods=["POST"])
-def pay():
- deposit_permission = flask.request.get_json()
- if deposit_permission is None:
- return flask.jsonify(error="no json in body"), 400
- resp = requests.post(urljoin(BACKEND_URL, "pay"),
- json=deposit_permission)
- if resp.status_code != 200:
- return backend_error(resp)
- proposal_data = resp.json()["contract_terms"]
- order_id = proposal_data["order_id"]
- payed_order_ids = flask.session["payed_order_ids"] \
- = flask.session.get("payed_order_ids", {})
- payed_order_ids[order_id] = dict(
- donation_receiver=proposal_data["merchant"]["instance"],
- donation_amount=amount_to_float(proposal_data["amount"])
+@app.route("/fulfillment/<receiver>/<int:order_id>")
+def fulfillment(order_id):
+ pay_params = dict(
+ instance=INSTANCE,
+ order_id=order_id,
+ resource_url=flask.request.base_url,
+ session_id=session_id,
+ session_sig=session_sig,
)
- print("received payment for", order_id)
- return flask.jsonify(resp.json()), resp.status_code
-
-@app.route("/backoffice")
-def track():
- response = flask.make_response(flask.render_template("templates/backoffice.html"))
- return response
+ pay_status = backend_get("check-payment", pay_params)
-@app.route("/history")
-def history():
- qs = get_query_string().decode("utf-8")
- url = urljoin(BACKEND_URL, "history")
- resp = requests.get(url, params=dict(parse_qsl(qs)))
- if resp.status_code != 200:
- return backend_error(resp)
- return flask.jsonify(resp.json()), resp.status_code
+ if pay_status.get("payment_redirect_url"):
+ return flask.redirect(pay_status["payment_redirect_url"])
+ if pay_status.get("paid"):
+ return flask.render_template(
+ "templates/fulfillment.html",
+ donation_receiver=data["donation_receiver"],
+ donation_amount=data["donation_amount"],
+ order_id=order_id,
+ currency=CURRENCY)
-@app.route("/track/order")
-def track_order():
- instance = expect_parameter("instance")
- order_id = expect_parameter("order_id")
- url = urljoin(BACKEND_URL, "track/transaction")
- resp = requests.get(url, params=dict(order_id=order_id, instance=instance))
- if resp.status_code != 200:
- return backend_error(resp)
- return flask.jsonify(resp.json()), resp.status_code
+ # no pay_redirect but article not paid, this should never happen!
+ err_abort(500, message="Internal error, invariant failed", json=pay_status)
diff --git a/talerdonations/donations/templates/backoffice.html b/talerdonations/donations/templates/backoffice.html
deleted file mode 100644
index 719d4b6..0000000
--- a/talerdonations/donations/templates/backoffice.html
+++ /dev/null
@@ -1,46 +0,0 @@
-{% extends "templates/base.html" %}
-{% block main %}
- <h1>Backoffice</h1>
- <p>This page simulates a backoffice facility. Through it,
- the user can see the money flow from Taler transactions to
- wire transfers and viceversa.</p>
- <table id="history" width="60%" style="visibility: hidden;">
- <tbody>
- <tr>
- <th>Order ID</th>
- <th>Summary</th>
- <th>Amount</th>
- <th>Date</th>
- </tr>
- </tbody>
- </table>
- <div class="loader"></div>
- <div id="popup1" class="overlay">
- <div class="popup">
- <h2>
- <a class="close" href="/backoffice">&times;</a>
- </h2>
- <div class="track-content">
- <table>
- <tbody>
- <tr>
- <th>WTID</th>
- <th>Amount</th>
- <th>Coin</th>
- <th>Date</th>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- </div>
- <a href="#" onclick="get_history(true);" style="margin-left: 90%;">Fake scroll</a>
-{% endblock main %}
-
-{% block styles %}
- <link rel="stylesheet" type="text/css" href="{{ url("/static/popup.css") }}">
-{% endblock styles %}
-
-{% block scripts %}
- <script src="{{ url('/static/backoffice.js') }}" type="application/javascript"></script>
-{% endblock scripts %}
diff --git a/talerdonations/donations/templates/base.html b/talerdonations/donations/templates/base.html
index b464752..374e426 100644
--- a/talerdonations/donations/templates/base.html
+++ b/talerdonations/donations/templates/base.html
@@ -18,11 +18,9 @@
<html data-taler-nojs="true">
<head>
<title>Taler Donation Demo</title>
- <link rel="stylesheet" type="text/css" href="{{ url('/static/web-common/pure.css') }}" />
- <link rel="stylesheet" type="text/css" href="{{ url('/static/web-common/demo.css') }}" />
- <link rel="stylesheet" type="text/css" href="{{ url('/static/web-common/taler-fallback.css') }}" id="taler-presence-stylesheet" />
- <script src="{{ url("/static/web-common/taler-wallet-lib.js") }}" type="application/javascript"></script>
- <script src="{{ url("/static/web-common/lang.js") }}" type="application/javascript"></script>
+ <link rel="stylesheet" type="text/css" href="{{ url('static', filename='web-common/pure.css') }}" />
+ <link rel="stylesheet" type="text/css" href="{{ url('static', filename='web-common/demo.css') }}" />
+ <link rel="stylesheet" type="text/css" href="{{ url('static', filename='web-common/taler-fallback.css') }}" id="taler-presence-stylesheet" />
{% block styles %}{% endblock %}
{% block scripts %}{% endblock %}
</head>
@@ -43,7 +41,7 @@
</div>
<section id="main" class="content">
- <a href="{{ url("/") }}">
+ <a href="{{ url_for('index') }}">
<div id="logo">
<svg height="100" width="100">
<circle cx="50" cy="50" r="40" stroke="darkcyan" stroke-width="6" fill="white" />
diff --git a/talerdonations/donations/templates/checkout.html b/talerdonations/donations/templates/checkout.html
index a7d009c..2785af8 100644
--- a/talerdonations/donations/templates/checkout.html
+++ b/talerdonations/donations/templates/checkout.html
@@ -36,6 +36,7 @@
<div id="opt-form" align="left"><br>
<input type="hidden" name="donation_receiver" value="{{ donation_receiver }}"></input>
<input type="hidden" name="donation_amount" value="{{ donation_amount }}"></input>
+ <input type="hidden" name="donation_donor" value="{{ donation_donor }}"></input>
<input type="radio" name="payment_system" value="lisa"
id="lisa-radio-button-id">Lisa</input>
<br/>
diff --git a/talerdonations/donations/templates/execute-payment.html b/talerdonations/donations/templates/execute-payment.html
deleted file mode 100644
index e1283ce..0000000
--- a/talerdonations/donations/templates/execute-payment.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{% extends "templates/base.html" %}
-
-{% block scripts %}
-<meta name="contract-hash" value="{{ hc }}">
-<meta name="pay-url" value="{{ pay_url|safe }}">
-<meta name="offering-url" value="{{ offering_url|safe }}">
-<script src="static/execute-payment.js" type="application/javascript"></script>
-{% endblock scripts %}
-
-{% block main %}
-<div id="msg">Executing payment <span id="action-indicator"></span></div>
-{% endblock main %}
diff --git a/talerdonations/donations/templates/fallback.html b/talerdonations/donations/templates/fallback.html
deleted file mode 100644
index 8dc426b..0000000
--- a/talerdonations/donations/templates/fallback.html
+++ /dev/null
@@ -1,28 +0,0 @@
-{% extends "templates/base.html" %}
-{% block main %}
-
-<p class="taler-installed-show">
-Payment is being processed. If nothing happens, please <a href="{{ request.path }}">click here</a>.
-</p>
-
-<div id="ccfakeform" class="taler-installed-hide">
- <p>
- Oops, it looks like you don't have a Taler wallet installed. Why don't you enter
- all your credit card details before reading the article? <em>You can also
- use GNU Taler to complete the purchase at any time.</em>
- </p>
-
- <form>
- First name<br> <input type="text"><br>
- Family name<br> <input type="text"><br>
- Age<br> <input type="text"><br>
- Nationality<br> <input type="text"><br>
- Gender<br> <input type="radio" name"gender">Male</input>
- CC number<br> <input type="text"><br>
- <input type="radio" name="gender">Female<br>
- </form>
- <form method="get" action="/cc-payment/{{ article_name }}">
- <input type="submit"></input>
- </form>
-</div>
-{% endblock main %}
diff --git a/talerdonations/donations/templates/index.html b/talerdonations/donations/templates/index.html
index 097c781..3ba9638 100644
--- a/talerdonations/donations/templates/index.html
+++ b/talerdonations/donations/templates/index.html
@@ -32,31 +32,26 @@ You are paying with an imaginary currency ({{ merchant_currency }}).
<option value="GNUnet">GNUnet</option>
<option value="Taler">GNU Taler</option>
<option value="Tor">Tor</option>
- <!-- options are added dynamically -->
</select>
<select id="taler-donation" name="donation_amount">
- <option value="0.1">0.1 {{ merchant_currency }}</option>
- <option value="1">1 {{ merchant_currency }}</option>
- <option value="6">5 {{ merchant_currency }}</option>
- <option value="10">10 {{ merchant_currency }}</option>
+ <option value="{{ merchant_currency }}:0.1">0.1 {{ merchant_currency }}</option>
+ <option value="{{ merchant_currency }}:1">1 {{ merchant_currency }}</option>
+ <option value="{{ merchant_currency }}:6">5 {{ merchant_currency }}</option>
+ <option value="{{ merchant_currency }}:10">10 {{ merchant_currency }}</option>
</select>
- <input type="submit" class="pure-button pure-button-primary" value="Donate!"/>
+ <input type="text" name="donation_donor" value="Anonymous" />
+ <input type="submit" class="pure-button pure-button-primary" value="Donate!" />
</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.
+ (*) To make it a bit more fun, the 5 {{ merchant_currency }} option is
+ deliberately implemented with a fault: the merchant will try to make you
+ donate 6 {{ merchant_currency} instead of the 5 {{ merchant_currency }} 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>
-<!-- <h2>Back-office interface</h2> -->
-<!--p>Try the <a href="backoffice">back-office</a>, in order to track
-orders and corresponding deposits.
-</p Depends on #4992 -->
-
{% endblock %}
diff --git a/talerdonations/donations/templates/provider-not-supported.html b/talerdonations/donations/templates/provider-not-supported.html
new file mode 100644
index 0000000..e10d66a
--- /dev/null
+++ b/talerdonations/donations/templates/provider-not-supported.html
@@ -0,0 +1,5 @@
+{% extends "templates/base.html" %}
+
+{% block main %}
+Unfortunately the selected payment provider is not supported in this demo.
+{% endblock main %}
diff --git a/talerdonations/helpers.py b/talerdonations/helpers.py
deleted file mode 100644
index 614e463..0000000
--- a/talerdonations/helpers.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# This file is part of TALER
-# (C) 2016 INRIA
-#
-# TALER is free software; you can redistribute it and/or modify it under the
-# terms of the GNU Affero General Public License as published by the Free Software
-# Foundation; either version 3, 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-#
-# @author Florian Dold
-# @author Marcello Stanisci
-
-from urllib.parse import urljoin, urlencode
-import logging
-import json
-import flask
-from .talerconfig import TalerConfig
-
-LOGGER = logging.getLogger(__name__)
-
-TC = TalerConfig.from_env()
-BACKEND_URL = TC["frontends"]["backend"].value_string(required=True)
-NDIGITS = TC["frontends"]["NDIGITS"].value_int()
-CURRENCY = TC["taler"]["CURRENCY"].value_string()
-
-FRACTION_BASE = 1e8
-
-if not NDIGITS:
- NDIGITS = 2
-
-class MissingParameterException(Exception):
- def __init__(self, param):
- self.param = param
- super().__init__()
-
-def amount_to_float(amount):
- return amount['value'] + (float(amount['fraction']) / float(FRACTION_BASE))
-
-
-def amount_from_float(floatx):
- value = int(floatx)
- fraction = int((floatx - value) * FRACTION_BASE)
- return dict(currency=CURRENCY, value=value, fraction=fraction)
-
-
-def join_urlparts(*parts):
- ret = ""
- part = 0
- while part < len(parts):
- buf = parts[part]
- part += 1
- if ret.endswith("/"):
- buf = buf.lstrip("/")
- elif ret and not buf.startswith("/"):
- buf = "/" + buf
- ret += buf
- return ret
-
-
-def make_url(page, *query_params):
- """
- Return a URL to a page in the current Flask application with the given
- query parameters (sequence of key/value pairs).
- """
- query = urlencode(query_params)
- if page.startswith("/"):
- root = flask.request.url_root
- page = page.lstrip("/")
- else:
- root = flask.request.base_url
- url = urljoin(root, "%s?%s" % (page, query))
- # urlencode is overly eager with quoting, the wallet right now
- # needs some characters unquoted.
- return url.replace("%24", "$").replace("%7B", "{").replace("%7D", "}")
-
-
-def expect_parameter(name, alt=None):
- value = flask.request.args.get(name, None)
- if value is None and alt is None:
- LOGGER.error("Missing parameter '%s' from '%s'." % (name, flask.request.args))
- raise MissingParameterException(name)
- return value if value else alt
-
-
-def get_query_string():
- return flask.request.query_string
-
-def backend_error(requests_response):
- LOGGER.error("Backend error: status code: "
- + str(requests_response.status_code))
- try:
- return flask.jsonify(requests_response.json()), requests_response.status_code
- except json.decoder.JSONDecodeError:
- LOGGER.error("Backend error (NO JSON returned): status code: "
- + str(requests_response.status_code))
- return flask.jsonify(dict(error="Backend died, no JSON got from it")), 502