summaryrefslogtreecommitdiff
path: root/talerdonations/donations/donations.py
diff options
context:
space:
mode:
Diffstat (limited to 'talerdonations/donations/donations.py')
-rw-r--r--talerdonations/donations/donations.py113
1 files changed, 88 insertions, 25 deletions
diff --git a/talerdonations/donations/donations.py b/talerdonations/donations/donations.py
index a7b41c2..9fd28c1 100644
--- a/talerdonations/donations/donations.py
+++ b/talerdonations/donations/donations.py
@@ -26,6 +26,10 @@ import random
import requests
import flask
import traceback
+import urllib
+import qrcode
+import qrcode.image.svg
+import lxml.etree
from taler.util.talerconfig import TalerConfig
LOGGER = logging.getLogger(__name__)
@@ -53,6 +57,7 @@ app.config.from_object(__name__)
def utility_processor():
def env(name, default=None):
return os.environ.get(name, default)
+
return dict(env=env)
@@ -76,7 +81,9 @@ def err_abort(abort_status_code, **params):
def backend_get(endpoint, params):
headers = {"Authorization": "ApiKey " + APIKEY}
try:
- resp = requests.get(urljoin(BACKEND_URL, endpoint), params=params, headers=headers)
+ resp = requests.get(
+ urljoin(BACKEND_URL, endpoint), params=params, headers=headers
+ )
except requests.ConnectionError:
err_abort(500, message="Could not establish connection to backend")
try:
@@ -84,8 +91,12 @@ def backend_get(endpoint, params):
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)
+ err_abort(
+ 500,
+ message="Backend returned error status",
+ json=response_json,
+ status_code=resp.status_code
+ )
return response_json
@@ -100,21 +111,29 @@ def backend_get(endpoint, params):
def backend_post(endpoint, json):
headers = {"Authorization": "ApiKey " + APIKEY}
try:
- resp = requests.post(urljoin(BACKEND_URL, endpoint), json=json, headers=headers)
+ resp = requests.post(
+ urljoin(BACKEND_URL, endpoint), json=json, headers=headers
+ )
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)
+ 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)
+ err_abort(
+ 500,
+ message="Backend returned error status",
+ json=response_json,
+ status_code=resp.status_code
+ )
return response_json
-
##
# Inspect GET arguments in the look for a parameter.
#
@@ -126,6 +145,7 @@ def expect_parameter(name):
return err_abort(400, message="parameter '{}' required".format(name))
return val
+
##
# "Fallback" exception handler to capture all the unmanaged errors.
#
@@ -134,9 +154,12 @@ def expect_parameter(name):
# (and execution stack!).
@app.errorhandler(Exception)
def internal_error(e):
- return flask.render_template("templates/error.html",
- message="Internal error",
- stack=traceback.format_exc())
+ return flask.render_template(
+ "templates/error.html",
+ message="Internal error",
+ stack=traceback.format_exc()
+ )
+
##
# Serve the main index page.
@@ -144,7 +167,10 @@ def internal_error(e):
# @return response object of the index page.
@app.route("/")
def index():
- return flask.render_template("templates/index.html", merchant_currency=CURRENCY)
+ return flask.render_template(
+ "templates/index.html", merchant_currency=CURRENCY
+ )
+
##
# Serve the "/javascript" page.
@@ -155,7 +181,6 @@ def javascript_licensing():
return flask.render_template("templates/javascript.html")
-
##
# Serve the "/checkout" page. This page lets the
# user pick the payment method they want to use,
@@ -172,7 +197,8 @@ def checkout():
donation_amount=amount,
donation_receiver=donation_receiver,
donation_donor=donation_donor,
- merchant_currency=CURRENCY)
+ merchant_currency=CURRENCY
+ )
##
@@ -182,8 +208,7 @@ def checkout():
# @return response object about the mentioned impossibility.
@app.route("/provider-not-supported")
def provider_not_supported():
- return flask.render_template( "templates/provider-not-supported.html")
-
+ return flask.render_template("templates/provider-not-supported.html")
##
@@ -201,18 +226,43 @@ def donate():
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)
+ 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),
+ extra=dict(
+ donor=donation_donor,
+ receiver=donation_receiver,
+ amount=donation_amount
+ ),
fulfillment_url=fulfillment_url,
instance=donation_receiver,
summary="Donation to {}".format(donation_receiver),
)
order_resp = backend_post("order", dict(order=order))
order_id = order_resp["order_id"]
- return flask.redirect(flask.url_for("fulfillment", receiver=donation_receiver, order_id=order_id))
+ return flask.redirect(
+ flask.url_for(
+ "fulfillment", receiver=donation_receiver, order_id=order_id
+ )
+ )
+
+
+##
+# This endpoint is used by the payment request page
+# to check if the payment has been completed via the QR code.
+@app.route("/check-status/<order_id>")
+def check_status(order_id, session_id):
+ pay_params = dict(instance=INSTANCE, order_id=order_id)
+ pay_status = backend_get("check-payment", pay_params)
+ return flask.jsonify(paid=pay_status["paid"])
+
+def get_qrcode_svg(data):
+ factory = qrcode.image.svg.SvgImage
+ img = qrcode.make(data, image_factory=factory)
+ return lxml.etree.tostring(img.get_image()).decode("utf-8")
##
@@ -226,12 +276,9 @@ def donate():
@app.route("/donation/<receiver>")
def fulfillment(receiver):
order_id = expect_parameter("order_id")
- pay_params = dict(instance=receiver,
- order_id=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"]
@@ -241,7 +288,23 @@ def fulfillment(receiver):
donation_amount=extra["amount"],
donation_donor=extra["donor"],
order_id=order_id,
- currency=CURRENCY)
+ currency=CURRENCY
+ )
+ else:
+ taler_pay_uri = pay_status["taler_pay_uri"]
+ qrcode_svg = get_qrcode_svg(taler_pay_uri)
+ check_status_url_enc = urllib.parse.quote(
+ flask.url_for("check_status", order_id=order_id)
+ )
+ content = flask.render_template(
+ "templates/request_payment.html",
+ taler_pay_uri=taler_pay_uri,
+ qrcode_svg=qrcode_svg,
+ check_status_url_enc=check_status_url_enc
+ )
+ headers = {"Taler": taler_pay_uri}
+ resp = flask.Response(content, status=402, headers=headers)
+ return resp
# no pay_redirect but article not paid, this should never happen!
err_abort(500, message="Internal error, invariant failed", json=pay_status)