summaryrefslogtreecommitdiff
path: root/talerdonations/donations/donations.py
diff options
context:
space:
mode:
authorMarcello Stanisci <stanisci.m@gmail.com>2017-11-24 20:01:43 +0100
committerMarcello Stanisci <stanisci.m@gmail.com>2017-11-24 20:01:43 +0100
commitf7f95782434a2268bc3583fe46fd0c386a1c1439 (patch)
tree6d18be909f0184371e4a641795f96410499ba84e /talerdonations/donations/donations.py
parent23d289f6a0e163c948bee1684d2ca17398a24b8c (diff)
downloaddonations-f7f95782434a2268bc3583fe46fd0c386a1c1439.tar.gz
donations-f7f95782434a2268bc3583fe46fd0c386a1c1439.tar.bz2
donations-f7f95782434a2268bc3583fe46fd0c386a1c1439.zip
actual donations logic
Diffstat (limited to 'talerdonations/donations/donations.py')
-rw-r--r--talerdonations/donations/donations.py212
1 files changed, 212 insertions, 0 deletions
diff --git a/talerdonations/donations/donations.py b/talerdonations/donations/donations.py
new file mode 100644
index 0000000..a86479d
--- /dev/null
+++ b/talerdonations/donations/donations.py
@@ -0,0 +1,212 @@
+# 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
+# GNU TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+#
+# @author Florian Dold
+# @author Marcello Stanisci
+
+import flask
+from urllib.parse import urljoin, urlencode, quote, parse_qsl
+import requests
+import logging
+import os
+import base64
+import random
+import time
+from datetime import datetime
+import jinja2
+from talerfrontends.talerconfig import TalerConfig
+from talerfrontends.helpers import (make_url,
+expect_parameter, amount_from_float, amount_to_float,
+join_urlparts, get_query_string, MissingParameterException,
+backend_error)
+
+logger = logging.getLogger(__name__)
+
+base_dir = os.path.dirname(os.path.abspath(__file__))
+
+app = flask.Flask(__name__, template_folder=base_dir)
+app.debug = True
+app.secret_key = base64.b64encode(os.urandom(64)).decode('utf-8')
+
+tc = TalerConfig.from_env()
+BACKEND_URL = tc["frontends"]["backend"].value_string(required=True)
+FRACTION = tc["frontends"]["fraction"].value_int(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)
+
+
+@app.route("/")
+def index():
+ return flask.render_template("templates/index.html", merchant_currency=CURRENCY)
+
+@app.route("/javascript")
+def javascript_licensing():
+ return flask.render_template("templates/javascript.html")
+
+
+@app.route("/checkout", methods=["GET"])
+def checkout():
+ amount_str = expect_parameter("donation_amount")
+ donation_receiver = expect_parameter("donation_receiver")
+ try:
+ amount = float(amount_str)
+ except ValueError:
+ logger.warn("Invalid amount ('%s')", amount_str)
+ e = flask.jsonify(error="invalid amount")
+ return flask.make_response(e, 400)
+ display_alert = flask.request.args.get("display_alert", None)
+ return flask.render_template("templates/checkout.html",
+ donation_amount=amount_str,
+ donation_receiver=donation_receiver,
+ merchant_currency=CURRENCY,
+ display_alert=display_alert)
+
+
+@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 e:
+ return flask.jsonify(dict(error="Missing parameter '%s'" % e)), 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",
+ ),
+ )
+ r = requests.post(urljoin(BACKEND_URL, 'proposal'), json=dict(order=order))
+ if 200 != r.status_code:
+ # 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(r)
+ return flask.jsonify(r.json()), r.status_code
+
+@app.route("/donate")
+def donate():
+ donation_receiver = expect_parameter("donation_receiver")
+ donation_amount = expect_parameter("donation_amount")
+ payment_system = expect_parameter("payment_system")
+ if "taler" != payment_system:
+ 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
+
+
+@app.route("/pay", methods=["POST"])
+def pay():
+ deposit_permission = flask.request.get_json()
+ if deposit_permission is None:
+ e = flask.jsonify(error="no json in body")
+ return e, 400
+ r = requests.post(urljoin(BACKEND_URL, "pay"), json=deposit_permission)
+ if 200 != r.status_code:
+ return backend_error(r)
+ proposal_data = r.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"])
+ )
+ print("received payment for", order_id)
+ return flask.jsonify(r.json()), r.status_code
+
+@app.route("/backoffice")
+def track():
+ response = flask.make_response(flask.render_template("templates/backoffice.html"))
+ return response
+
+
+@app.route("/history")
+def history():
+ qs = get_query_string().decode("utf-8")
+ url = urljoin(BACKEND_URL, "history")
+ r = requests.get(url, params=dict(parse_qsl(qs)))
+ if 200 != r.status_code:
+ return backend_error(r)
+ return flask.jsonify(r.json()), r.status_code
+
+
+@app.route("/track/order")
+def track_order():
+ instance = expect_parameter("instance")
+ order_id = expect_parameter("order_id")
+ url = urljoin(BACKEND_URL, "track/transaction")
+ r = requests.get(url, params=dict(order_id=order_id, instance=instance))
+ if 200 != r.status_code:
+ return backend_error(r)
+ return flask.jsonify(r.json()), r.status_code