From f7f95782434a2268bc3583fe46fd0c386a1c1439 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Fri, 24 Nov 2017 20:01:43 +0100 Subject: actual donations logic --- talerdonations/donations/donations.py | 212 ++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 talerdonations/donations/donations.py 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 +# +# @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 -- cgit v1.2.3