summaryrefslogtreecommitdiff
path: root/talerdonations/donations/donations.py
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2018-01-22 02:58:20 +0100
committerFlorian Dold <florian.dold@gmail.com>2018-01-22 02:59:38 +0100
commit73ec94f494b2f428d89aab703fd1608634bfd4e9 (patch)
tree8f2224300141229ea0e358fbc97f555f18eff72c /talerdonations/donations/donations.py
parent6faf7afb71328dae733858db78eb8c077e15536f (diff)
downloaddonations-73ec94f494b2f428d89aab703fd1608634bfd4e9.tar.gz
donations-73ec94f494b2f428d89aab703fd1608634bfd4e9.tar.bz2
donations-73ec94f494b2f428d89aab703fd1608634bfd4e9.zip
use new API, add donor
Diffstat (limited to 'talerdonations/donations/donations.py')
-rw-r--r--talerdonations/donations/donations.py225
1 files changed, 93 insertions, 132 deletions
diff --git a/talerdonations/donations/donations.py b/talerdonations/donations/donations.py
index 9b3208a..598cca6 100644
--- a/talerdonations/donations/donations.py
+++ b/talerdonations/donations/donations.py
@@ -20,9 +20,9 @@ import logging
import os
import base64
import random
-from datetime import datetime
import requests
import flask
+import traceback
from ..talerconfig import TalerConfig
from ..helpers import (make_url, \
expect_parameter, amount_from_float, \
@@ -40,17 +40,64 @@ 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)
-MAX_FEE = dict(value=3, fraction=0, currency=CURRENCY)
app.config.from_object(__name__)
@app.context_processor
def utility_processor():
- def url(my_url):
- return join_urlparts(flask.request.script_root, my_url)
def env(name, default=None):
return os.environ.get(name, default)
- return dict(url=url, env=env)
+ 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.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("/")
@@ -64,151 +111,65 @@ def javascript_licensing():
@app.route("/checkout", methods=["GET"])
def checkout():
- amount_str = expect_parameter("donation_amount")
+ amount = expect_parameter("donation_amount")
donation_receiver = expect_parameter("donation_receiver")
- try:
- float(amount_str)
- except ValueError:
- LOGGER.warning("Invalid amount ('%s')", amount_str)
- return flask.make_response(
- flask.jsonify(error="invalid amount"),
- 400)
- display_alert = flask.request.args.get("display_alert", None)
+ donation_receiver = expect_parameter("donation_donor")
return flask.render_template(
"templates/checkout.html",
donation_amount=amount_str,
donation_receiver=donation_receiver,
- merchant_currency=CURRENCY,
- display_alert=display_alert)
+ donation_donor=donation_donor,
+ merchant_currency=CURRENCY)
-@app.route("/generate-contract", methods=["GET"])
-def generate_contract():
- try:
- donation_receiver = expect_parameter("donation_receiver")
- donation_amount = expect_parameter("donation_amount")
- except MissingParameterException as exc:
- return flask.jsonify(
- dict(error="Missing parameter '%s'" % exc)), 400
- amount = amount_from_float(float(donation_amount))
- order_id = "donation-%s-%X-%s" % \
- (donation_receiver,
- random.randint(0, 0xFFFFFFFF),
- datetime.today().strftime('%H_%M_%S'))
- order = dict(
- summary="Donation!",
- nonce=flask.request.args.get("nonce"),
- amount=amount,
- max_fee=dict(value=1, fraction=0, currency=CURRENCY),
- order_id=order_id,
- products=[
- dict(
- description="Donation to %s" % (donation_receiver,),
- quantity=1,
- product_id=0,
- price=amount,
- ),
- ],
- fulfillment_url=make_url("/fulfillment", ("order_id", order_id)),
- pay_url=make_url("/pay"),
- merchant=dict(
- instance=donation_receiver,
- address="nowhere",
- name="Kudos Inc.",
- jurisdiction="none",
- ),
- )
- resp = requests.post(urljoin(BACKEND_URL, 'proposal'),
- json=dict(order=order))
- if resp.status_code != 200:
- # It is important to use 'backend_error()', as it handles
- # the case where the backend gives NO JSON as response.
- # For example, if it dies, or nginx hijacks somehow the
- # response.
- return backend_error(resp)
- return flask.jsonify(resp.json()), resp.status_code
+@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(make_url("checkout",
- ("donation_receiver", donation_receiver),
- ("donation_amount", donation_amount),
- ("display_alert", True)))
- response = flask.make_response(flask.render_template('templates/fallback.html'), 402)
- response.headers["X-Taler-Contract-Url"] = \
- make_url("/generate-contract",
- ("donation_receiver", donation_receiver),
- ("donation_amount", donation_amount))
- return response
-
-
-@app.route("/fulfillment")
-def fulfillment():
- order_id = expect_parameter("order_id")
- payed_order_ids = flask.session.get("payed_order_ids", [])
- print("order_id:", repr(order_id))
- print("session:", repr(flask.session))
- if order_id in payed_order_ids:
- data = payed_order_ids[order_id]
- return flask.render_template(
- "templates/fulfillment.html",
- donation_receiver=data["donation_receiver"],
- donation_amount=data["donation_amount"],
- order_id=order_id,
- currency=CURRENCY)
-
- response = flask.make_response(flask.render_template("templates/fallback.html"), 402)
- response.headers["X-Taler-Contract-Query"] = "fulfillment_url"
- response.headers["X-Taler-Offer-Url"] = make_url("/")
- return response
+ return flask.redirect(flask.url_for(provider_not_supported))
+ fulfillment_url = flask.url_for(fulfillment, order_id=order_id, receiver=donation_receiver, _external=True)
+ order = dict(
+ amount=donation_amount,
+ extra=dict(donor=donation_donor, receiver=donation_receiver),
+ 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, order_id=order_id))
-@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"]
- order_id = proposal_data["order_id"]
- payed_order_ids = flask.session["payed_order_ids"] \
- = flask.session.get("payed_order_ids", {})
- payed_order_ids[order_id] = dict(
- donation_receiver=proposal_data["merchant"]["instance"],
- donation_amount=amount_to_float(proposal_data["amount"])
+@app.route("/fulfillment/<receiver>/<int:order_id>")
+def fulfillment(order_id):
+ pay_params = dict(
+ instance=INSTANCE,
+ order_id=order_id,
+ resource_url=flask.request.base_url,
+ session_id=session_id,
+ session_sig=session_sig,
)
- print("received payment for", order_id)
- return flask.jsonify(resp.json()), resp.status_code
-
-@app.route("/backoffice")
-def track():
- response = flask.make_response(flask.render_template("templates/backoffice.html"))
- return response
+ pay_status = backend_get("check-payment", pay_params)
-@app.route("/history")
-def history():
- qs = get_query_string().decode("utf-8")
- url = urljoin(BACKEND_URL, "history")
- resp = requests.get(url, params=dict(parse_qsl(qs)))
- if resp.status_code != 200:
- return backend_error(resp)
- return flask.jsonify(resp.json()), resp.status_code
+ if pay_status.get("payment_redirect_url"):
+ return flask.redirect(pay_status["payment_redirect_url"])
+ if pay_status.get("paid"):
+ return flask.render_template(
+ "templates/fulfillment.html",
+ donation_receiver=data["donation_receiver"],
+ donation_amount=data["donation_amount"],
+ order_id=order_id,
+ currency=CURRENCY)
-@app.route("/track/order")
-def track_order():
- instance = expect_parameter("instance")
- order_id = expect_parameter("order_id")
- url = urljoin(BACKEND_URL, "track/transaction")
- resp = requests.get(url, params=dict(order_id=order_id, instance=instance))
- if resp.status_code != 200:
- return backend_error(resp)
- return flask.jsonify(resp.json()), resp.status_code
+ # no pay_redirect but article not paid, this should never happen!
+ err_abort(500, message="Internal error, invariant failed", json=pay_status)