From: Michael Tremer Date: Tue, 22 May 2018 14:32:04 +0000 (+0200) Subject: Implement donation redirection to backend X-Git-Url: http://git.ipfire.org/?p=ipfire.org.git;a=commitdiff_plain;h=2c361abc1a73 Implement donation redirection to backend This will enhance the donation form to form a request which is being sent to Zeiterfassung which handles the rest of the payment process. Signed-off-by: Michael Tremer --- diff --git a/templates/donate.html b/templates/donate.html index d4afd9c8..0e680a59 100644 --- a/templates/donate.html +++ b/templates/donate.html @@ -8,184 +8,188 @@

{{ _("Donate") }}

- -
-
-
-
-
-
-
-

Choose a currency

-
-
- - -
-
- - + +
+ {% raw xsrf_form_html() %} + +
+
+
+
+
+
+
+

Choose a currency

+
+
+ + +
+
+ + +
-
- -
-

Frequency

-
-
- - -
-
- - + +
+

Frequency

+
+
+ + +
+
+ + +
-
- -
-
-
-
-
-

Choose an amount

-
- - - - - - - - - - - - - - + +
+
+
+
+
+

Choose an amount

+
+ + + + + + + + + + + - - -
-
-
-
-

Enter your own

- + + + + + +
+
+
+
+

Enter your own

+ +
-
-
-
-

Choose payment

- -

Secure

-
-
- -
-
- - -
- -
- - +
+
+

Choose payment

+ +

Secure

+
-
- - +
+
+ + +
+ +
+ + +
+ +
+ + +
-
+ + - - -

- There should also be some area for the small-print since we need to tell people who they are donating to and that we don't do any refunds, etc. -

-
- -
-
-

IPFire is an Open Source software project.

- -

- IPFire is an Open Source software project. +

+ There should also be some area for the small-print since we need to tell people who they are donating to and that we don't do any refunds, etc.

- -

- Development and keeping the project healthy is carried out by a - group of volunteers who have built a very successful product, - which hundreds of thousands of people use every day. -

- -

- Your donation helps us to make IPFire even better... -

-
- - + +
+
+

IPFire is an Open Source software project.

+ +

+ IPFire is an Open Source software project. +

+ +

+ Development and keeping the project healthy is carried out by a + group of volunteers who have built a very successful product, + which hundreds of thousands of people use every day. +

-
-

How much should I give?

+

+ Your donation helps us to make IPFire even better... +

+
+ + + +
-
+
diff --git a/webapp/backend/base.py b/webapp/backend/base.py index 1474e984..2497f4db 100644 --- a/webapp/backend/base.py +++ b/webapp/backend/base.py @@ -18,6 +18,8 @@ import releases import settings import talk +from . import zeiterfassung + class Backend(object): def __init__(self, configfile, debug=False): # Read configuration file. @@ -46,6 +48,8 @@ class Backend(object): self.releases = releases.Releases(self) self.talk = talk.Talk(self) + self.zeiterfassung = zeiterfassung.ZeiterfassungClient(self) + def read_config(self, configfile): cp = configparser.ConfigParser() cp.read(configfile) diff --git a/webapp/backend/zeiterfassung.py b/webapp/backend/zeiterfassung.py new file mode 100644 index 00000000..626b3650 --- /dev/null +++ b/webapp/backend/zeiterfassung.py @@ -0,0 +1,72 @@ +#!/usr/bin/python + +import hashlib +import hmac +import json +import tornado.httpclient +import tornado.gen +import urllib +import urlparse + +from misc import Object + +class ZeiterfassungClient(Object): + algorithm = "Zeiterfassung-HMAC-SHA512" + + def init(self): + self.url = self.settings.get("zeiterfassung_url") + + # API credentials + self.api_key = self.settings.get("zeiterfassung_api_key") + self.api_secret = self.settings.get("zeiterfassung_api_secret") + + # Check if all configuration values are set + if not all((self.url, self.api_key, self.api_secret)): + raise RuntimeError("%s is not configured" % self.__class__.__name__) + + # Create HTTPClient + self.http_client = tornado.httpclient.AsyncHTTPClient( + defaults = { + "User-Agent" : "IPFireWebApp", + } + ) + + def _make_signature(self, method, path, body): + # Empty since we only support POST + canonical_query = "" + + # Put everything together + string_to_sign = "\n".join(( + method, path, canonical_query, + )).encode("utf-8") + body + + # Compute HMAC + h = hmac.new(self.api_secret.encode("utf-8"), string_to_sign, hashlib.sha512) + + return h.hexdigest() + + @tornado.gen.coroutine + def send_request(self, path, **kwargs): + url = urlparse.urljoin(self.url, path) + + # Query arguments are all keyword arguments + arguments = kwargs + + request = tornado.httpclient.HTTPRequest(url, method="POST") + request.body = urllib.urlencode(arguments) + + # Compose the signature + signature = self._make_signature("POST", path, request.body) + + # Add authorization header + request.headers["Authorization"] = " ".join( + (self.algorithm, self.api_key, signature) + ) + + # Send the request + response = yield self.http_client.fetch(request) + + # Decode the JSON response + d = json.loads(response.body.decode()) + + raise tornado.gen.Return(d) diff --git a/webapp/handlers.py b/webapp/handlers.py index bfadf619..d380d5f4 100644 --- a/webapp/handlers.py +++ b/webapp/handlers.py @@ -11,6 +11,7 @@ import os #import time #import tornado.database #import tornado.locale +import tornado.gen import tornado.web #import unicodedata @@ -122,6 +123,44 @@ class DonateHandler(BaseHandler): def get(self): self.render("donate.html") + @tornado.gen.coroutine + def post(self): + # Get form inputs + args = { + "amount" : self.get_argument("amount"), + "currency" : self.get_argument("currency", "EUR"), + "method" : self.get_argument("method", None), + + # Is this a recurring donation? + "recurring" : self.get_argument("frequency") == "monthly", + + # XXX Missing inputs + "email" : "john.doe@example.com", + "first_name" : "John", + "last_name" : "Doe", + "street1" : "Street 1", + "street2" : "Street 2", + "post_code" : "POST CODE", + "city" : "City", + "state" : "State", + "country_code" : "DE", + } + + # Send request to Zeiterfassung + try: + response = yield self.backend.zeiterfassung.send_request( + "/api/v1/donations/create/ipfire-project", **args) + + except Exception: + raise # XXX handle any problems when Zeiterfassung is unreachable + + # Redirect the user to the payment page + redirect_url = response.get("redirect_url") + if not redirect_url: + raise tornado.web.HTTPError(500, "Did not receive a redirect URL") + + self.redirect(redirect_url) + class DownloadHandler(BaseHandler): def get(self):