import flask import requests from urllib.parse import urljoin, urlencode import taler.util.amount import base64 import os import logging import json from random import randint from datetime import datetime app = flask.Flask(__name__) app.secret_key = base64.b64encode(os.urandom(64)).decode('utf-8') logger = logging.getLogger(__name__) CURRENCY = "TESTKUDOS" BACKEND_URL = "http://backend.test.taler.net/" def make_url(page, *query_params): """ Return a URL to a page in the current Flask application with the given query parameters (sequence of key/value pairs). """ query = urlencode(query_params) if page.startswith("/"): root = flask.request.url_root page = page.lstrip("/") else: root = flask.request.base_url url = urljoin(root, "%s?%s" % (page, query)) # urlencode is overly eager with quoting, the wallet right now # needs some characters unquoted. return url.replace("%24", "$").replace("%7B", "{").replace("%7D", "}") @app.route("/") def index(): return flask.render_template("index.html") @app.route("/donate") def donate(): response = flask.Response(status=402) response.headers["X-Taler-Contract-Url"] = "/generate-proposal" return response @app.route("/generate-proposal") def generate_proposal(): DONATION = amount.string_to_amount("0.1:%s" % CURRENCY) MAX_FEE = amount.string_to_amount("0.05:%s" % CURRENCY) ORDER_ID = "tutorial-%X-%s" % (randint(0, 0xFFFFFFFF), datetime.today().strftime("%H_%M_%S")) order = dict( order_id=ORDER_ID, nonce=flask.request.args.get("nonce"), amount=DONATION, max_fee=MAX_FEE, products=[ dict( description="Donation", quantity=1, product_id=0, price=DONATION, ), ], fulfillment_url=make_url("/fulfillment", ("order_id", ORDER_ID)), pay_url=make_url("/pay"), merchant=dict( instance="tutorial", address="nowhere", name="Donation tutorial", jurisdiction="none", ), ) url = urljoin(BACKEND_URL, "proposal") r = requests.post(url, json=dict(order=order)) if r.status_code != 200: logger.error("failed to POST to '%s'", url) return r.text, r.status_code proposal_resp = r.json() return flask.jsonify(**proposal_resp) @app.route("/fulfillment") def fulfillment(): paid = flask.session.get("paid", False) if paid: return "Thank you! Your order id is: %s." % flask.session["order_id"] response = flask.Response(status=402) response.headers["X-Taler-Contract-Url"] = make_url("/generate-contract") response.headers["X-Taler-Contract-Query"] = "fulfillment_url" response.headers["X-Taler-Offer-Url"] = make_url("/donate") 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: logger.error("Backend said, status code: %d, object: %s" % (r.status_code, r.text)) return r.text, r.status_code contract_terms = r.json()["contract_terms"] flask.session["paid"] = True flask.session["order_id"] = contract_terms["order_id"] return flask.jsonify(r.json()), 200