diff options
author | ng0 <ng0@n0.is> | 2019-09-25 19:27:38 +0000 |
---|---|---|
committer | ng0 <ng0@n0.is> | 2019-09-25 19:27:38 +0000 |
commit | 6a346edfe7fad92abbf6d7f6fc2e04602eff3d10 (patch) | |
tree | 0584b8949ae7e7b382914ea9155f31a959030b73 /talerdonations/donations | |
parent | 3aeaaa760a4bc00d35b1faa0c7bde47e9a00b9b3 (diff) | |
parent | 38acb9f039fb01a7bc0aef8b93a277e919899ae9 (diff) | |
download | donations-6a346edfe7fad92abbf6d7f6fc2e04602eff3d10.tar.gz donations-6a346edfe7fad92abbf6d7f6fc2e04602eff3d10.tar.bz2 donations-6a346edfe7fad92abbf6d7f6fc2e04602eff3d10.zip |
Merge branch 'master' of git.taler.net:donations
Diffstat (limited to 'talerdonations/donations')
-rw-r--r-- | talerdonations/donations/donations.py | 113 | ||||
-rw-r--r-- | talerdonations/donations/templates/index.html | 17 | ||||
-rw-r--r-- | talerdonations/donations/templates/request_payment.html | 69 |
3 files changed, 158 insertions, 41 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) diff --git a/talerdonations/donations/templates/index.html b/talerdonations/donations/templates/index.html index fc956e5..68a0cb2 100644 --- a/talerdonations/donations/templates/index.html +++ b/talerdonations/donations/templates/index.html @@ -7,22 +7,7 @@ You are paying with an imaginary currency ({{ merchant_currency }}). </p> -<div class="taler-installed-hide"> - <h2>Installing the Taler wallet</h2> - First, you need to install the Taler wallet browser extension. - Install the wallet - <span id="install-done" style="visibility: hidden">(done)</span> - <ul> - <li>from the app store for <a href="https://chrome.google.com/webstore/detail/gnu-taler-wallet/millncjiddlpgdmkklmhfadpacifaonc">Google - Chrome and Chromium</a> - </li> - <li>By visiting our <a href="https://taler.net/wallet">installation page</a> for other platforms. - </li> - </ul> - Wallets for other browsers will be provided in the near future. -</div> - -<div class="taler-installed-show"> +<div> <p>Please select a project, the amount (*) of {{ merchant_currency }} you wish to donate, and enter the name that will appear on your receipt:</p> diff --git a/talerdonations/donations/templates/request_payment.html b/talerdonations/donations/templates/request_payment.html new file mode 100644 index 0000000..6e050d0 --- /dev/null +++ b/talerdonations/donations/templates/request_payment.html @@ -0,0 +1,69 @@ +{% extends "templates/base.html" %} + + +{% block meta %} +<noscript> + <meta http-equiv="refresh" content="1"> +</noscript> +{% endblock meta %} + + +{% block scripts %} +<script> + let checkUrl = decodeURIComponent("{{ check_status_url_enc }}"); + let delayMs = 500; + function check() { + let req = new XMLHttpRequest(); + req.onreadystatechange = function () { + if (req.readyState === XMLHttpRequest.DONE) { + if (req.status === 200) { + try { + let resp = JSON.parse(req.responseText); + if (resp.paid) { + document.location.reload(true); + } + } catch (e) { + console.error("could not parse response:", e); + } + } + setTimeout(check, delayMs); + } + }; + req.onerror = function () { + setTimeout(check, delayMs); + } + req.open("GET", checkUrl); + req.send(); + } + + setTimeout(check, delayMs); +</script> +{% endblock scripts %} + + +{% block main %} + +<h1>Payment Required</h1> + +<div class="taler-installed-hide"> + <p> + Looks like your browser doesn't support GNU Taler payments. You can try + installing a <a href="https://taler.net/en/wallet.html">wallet browser extension</a>. + </p> +</div> + +<div> + + <p> + You can use this QR code to pay with your mobile wallet: + </p> + + {{ qrcode_svg | safe }} + + <p> + Click <a href="{{ taler_pay_uri }}">this link</a> to open your system's Taler wallet if it exists. + </p> + +</div> + +{% endblock main %} |