diff options
author | Florian Dold <florian.dold@gmail.com> | 2018-01-05 12:00:50 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2018-01-05 12:00:50 +0100 |
commit | 58be785e4181b403f2d74ce7e92cde2784aae455 (patch) | |
tree | 3ce08b86fd7d0bcd864dd5689ca386d9cd737912 /talerblog | |
parent | d10579541c10ae122f542f502b1d4c1ae8888ea7 (diff) | |
download | blog-58be785e4181b403f2d74ce7e92cde2784aae455.tar.gz blog-58be785e4181b403f2d74ce7e92cde2784aae455.tar.bz2 blog-58be785e4181b403f2d74ce7e92cde2784aae455.zip |
use upcoming backend API
Diffstat (limited to 'talerblog')
-rw-r--r-- | talerblog/blog/blog.py | 135 |
1 files changed, 56 insertions, 79 deletions
diff --git a/talerblog/blog/blog.py b/talerblog/blog/blog.py index f5677d8..8cb5763 100644 --- a/talerblog/blog/blog.py +++ b/talerblog/blog/blog.py @@ -23,6 +23,7 @@ Implement URL handlers and payment logic for the blog merchant. from urllib.parse import urljoin, quote, parse_qsl import logging import os +import uuid import base64 import requests import flask @@ -33,6 +34,7 @@ from ..helpers import (make_url, \ from ..blog.content import (ARTICLES, \ get_article_file, get_image_file) + BASE_DIR = os.path.dirname(os.path.abspath(__file__)) app = flask.Flask(__name__, template_folder=BASE_DIR) app.debug = True @@ -47,6 +49,7 @@ ARTICLE_AMOUNT = dict(value=0, fraction=50000000, currency=CURRENCY) app.config.from_object(__name__) + @app.context_processor def utility_processor(): def url(my_url): @@ -55,48 +58,40 @@ def utility_processor(): 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, articles=ARTICLES.values()) + @app.route("/javascript") def javascript_licensing(): return flask.render_template("templates/javascript.html") + # Triggers the refund by serving /refund/test?order_id=XY. # Will be triggered by a "refund button". -@app.route("/refund", methods=["GET", "POST"]) +@app.route("/refund", methods=["POST"]) def refund(): - if flask.request.method == "POST": - payed_articles = flask.session["payed_articles"] = flask.session.get("payed_articles", {}) - article_name = flask.request.form.get("article_name") - LOGGER.info("Looking for article '%s' to refund" % article_name) - order_id = payed_articles.get(article_name) - if not order_id: - return flask.jsonify(dict(error="Aborting refund: article not payed")), 401 - resp = requests.post(urljoin(BACKEND_URL, "refund"), - json=dict(order_id=order_id, - refund=ARTICLE_AMOUNT, - reason="Demo reimbursement", - instance=INSTANCE)) - if resp.status_code != 200: - return backend_error(resp) - payed_articles[article_name] = "__refunded" - response = flask.make_response() - response.headers["X-Taler-Refund-Url"] = make_url("/refund", ("order_id", order_id)) - return response, 402 - - order_id = expect_parameter("order_id", False) + article_name = flask.request.form.get("article_name") + if not article_name: + return flask.jsonify(dict(error="No article_name found in form")), 400 + LOGGER.info("Looking for %s to refund" % article_name) + order_id = payed_articles.get(article_name) if not order_id: - LOGGER.error("Missing parameter 'order_id'") - return flask.jsonify(dict(error="Missing parameter 'order_id'")), 400 - resp = requests.get(urljoin(BACKEND_URL, "refund"), - params=dict(order_id=order_id, instance=INSTANCE)) + return flask.jsonify(dict(error="Aborting refund: article not payed")), 401 + resp = requests.post(urljoin(BACKEND_URL, "refund"), + json=dict(order_id=order_id, + refund=ARTICLE_AMOUNT, + reason="Demo reimbursement", + instance=INSTANCE)) if resp.status_code != 200: return backend_error(resp) - return flask.jsonify(resp.json()), resp.status_code + if pay_status.get("refund_redirect_url"): + return flask.redirect(pay_status["refund_redirect_url"]) + flask.abort(500) @app.route("/generate-contract", methods=["GET"]) @@ -134,24 +129,46 @@ def generate_contract(): return flask.jsonify(**proposal_resp) -@app.route("/cc-payment/<name>") -def cc_payment(name): - return flask.render_template("templates/cc-payment.html", - article_name=name) - @app.route("/essay/<name>") @app.route("/essay/<name>/data/<data>") def article(name, data=None): - LOGGER.info("processing %s" % name) - payed_articles = flask.session.get("payed_articles", {}) - if payed_articles.get(name, "") == "__refunded": + # We use an explicit session ID so that each payment (or payment replay) is + # bound to a browser. This forces re-play and prevents sharing the article + # by just sharing the URL. + session_id = flask.session.get("uid") + if not session_id: + session_id = flask.session["uid"] = uuid.uuid4() + + pay_params = dict( + instance=INSTANCE, + contract_url=make_url("/generate-contract", ("article_name", name)), + session_id=session_id, + session_sig=requests.args.get("session_sig"), + order_id=requests.args.get("order_id"), + # URL that the browser will navigate to after the user has paid (or + # proved they already paid) with this session + confirm_url = make_url("/essay/" + name, + ("order_id", "${order_id}"), + ("session_sig", "${session_sig}")) + ) + + resp = requests.get(urljoin(BACKEND_URL, "check-payment"), params=pay_params) + if resp.status_code != 200: + return backend_error(resp) + + pay_status = resp.json() + + if pay_status.get("payment_redirect_url"): + return flask.redirect(pay_status["payment_redirect_url"]) + + if pay_status.get("refunded"): return flask.render_template("templates/article_refunded.html", article_name=name) - if name in payed_articles: - article_info = ARTICLES[name] - if article_info is None: + if pay_status.get("paid"): + articleInfo = ARTICLES[name] + if articleInfo is None: flask.abort(500) if data is not None: if data in article_info.extra_files: @@ -161,45 +178,5 @@ def article(name, data=None): article_file=get_article_file(article_info), article_name=name) - contract_url = make_url("/generate-contract", - ("article_name", name)) - response = flask.make_response( - flask.render_template("templates/fallback.html"), 402) - response.headers["X-Taler-Contract-Url"] = contract_url - response.headers["X-Taler-Contract-Query"] = "fulfillment_url" - # Useless (?) header, as X-Taler-Contract-Url takes always (?) precedence - # over X-Offer-Url. This one might only be useful if the contract retrieval - # goes wrong. - response.headers["X-Taler-Offer-Url"] = make_url("/essay/" + quote(name)) - return response - - -@app.route("/pay", methods=["POST"]) -def pay(): - deposit_permission = flask.request.get_json() - if deposit_permission is None: - return flask.jsonify(error="no json in body"), 400 - resp = requests.post(urljoin(BACKEND_URL, "pay"), - json=deposit_permission) - if resp.status_code != 200: - return backend_error(resp) - proposal_data = resp.json()["contract_terms"] - article_name = proposal_data["extra"]["article_name"] - payed_articles = flask.session["payed_articles"] = flask.session.get("payed_articles", {}) - - try: - resp.json()["refund_permissions"].pop() - # we had some refunds on the article purchase already! - LOGGER.info("Article %s was refunded, before /pay" % article_name) - payed_articles[article_name] = "__refunded" - return flask.jsonify(resp.json()), 200 - except IndexError: - pass - - if not deposit_permission["order_id"]: - LOGGER.error("order_id missing from deposit_permission!") - return flask.jsonify(dict(error="internal error: ask for refund!")), 500 - if article_name not in payed_articles: - LOGGER.info("Article %s goes in state" % article_name) - payed_articles[article_name] = deposit_permission["order_id"] - return flask.jsonify(resp.json()), 200 + # no pay_redirect but article not paid, this should never happen! + flask.abort(500) |