# 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 from urllib.parse import urljoin, parse_qsl import logging import os import uuid import base64 import random import requests import flask import traceback from ..talerconfig import TalerConfig 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) CURRENCY = TC["taler"]["currency"].value_string(required=True) app.config.from_object(__name__) @app.context_processor def utility_processor(): def env(name, default=None): return os.environ.get(name, default) 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.request.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("/") 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 = expect_parameter("donation_amount") donation_receiver = expect_parameter("donation_receiver") donation_donor = expect_parameter("donation_donor") return flask.render_template( "templates/checkout.html", donation_amount=amount, donation_receiver=donation_receiver, donation_donor=donation_donor, merchant_currency=CURRENCY) @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(flask.url_for("provider_not_supported")) fulfillment_url = flask.url_for("fulfillment", receiver=donation_receiver, _external=True) order = dict( amount=donation_amount, extra=dict(donor=donation_donor, receiver=donation_receiver, amount=donation_amount), 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", receiver=donation_receiver, order_id=order_id)) @app.route("/donation/") def fulfillment(receiver): order_id = expect_parameter("order_id") pay_params = dict( instance=receiver, order_id=order_id, ) pay_status = backend_get("check-payment", pay_params) if pay_status.get("payment_redirect_url"): return flask.redirect(pay_status["payment_redirect_url"]) if pay_status.get("paid"): extra = pay_status["contract_terms"]["extra"] return flask.render_template( "templates/fulfillment.html", donation_receiver=extra["receiver"], donation_amount=extra["amount"], donation_donor=extra["donor"], order_id=order_id, currency=CURRENCY) # no pay_redirect but article not paid, this should never happen! err_abort(500, message="Internal error, invariant failed", json=pay_status)