]> git.ipfire.org Git - ipfire.org.git/blobdiff - src/web/donate.py
Merge branch 'master' into new-design
[ipfire.org.git] / src / web / donate.py
index 743e5bbc35eb3970b500c15f7ed024155f509932..bae215745af82879887c0c510f22a932b4f9e427 100644 (file)
@@ -1,29 +1,26 @@
 #!/usr/bin/python3
 
 import iso3166
-import tornado.gen
 import tornado.web
 
 from . import base
 
-class DonateHandler(base.BaseHandler):
-       @base.blacklisted
-       def prepare(self):
-               # Makes sure that we call blacklist for everything
-               pass
+SKUS = {
+       "monthly"   : "IPFIRE-DONATION-MONTHLY",
+       "quarterly" : "IPFIRE-DONATION-QUARTERLY",
+       "yearly"    : "IPFIRE-DONATION-YEARLY",
+}
+DEFAULT_SKU = "IPFIRE-DONATION"
 
+class DonateHandler(base.BaseHandler):
        def get(self):
-               location = self.get_remote_location()
-
-               if location:
-                       country = location.country
+               if self.current_user:
+                       country = self.current_user.country_code
                else:
-                       country = None
+                       country = self.current_country_code
 
                # Get defaults
-               first_name = self.get_argument("first_name", None)
-               last_name = self.get_argument("last_name", None)
-               amount    = self.get_argument_int("amount", None)
+               amount    = self.get_argument_float("amount", None)
                currency  = self.get_argument("currency", None)
                frequency = self.get_argument("frequency", None)
 
@@ -40,30 +37,39 @@ class DonateHandler(base.BaseHandler):
                        frequency = "one-time"
 
                self.render("donate/donate.html", countries=iso3166.countries,
-                       country=country, first_name=first_name, last_name=last_name,
-                       amount=amount, currency=currency, frequency=frequency)
+                       country=country, amount=amount, currency=currency, frequency=frequency)
+
+       @base.ratelimit(minutes=15, requests=5)
+       async def post(self):
+               type      = self.get_argument("type")
+               if not type in ("individual", "organization"):
+                       raise tornado.web.HTTPError(400, "type is of an invalid value: %s" % type)
 
-       @tornado.gen.coroutine
-       @base.ratelimit(minutes=24*60, requests=5)
-       def post(self):
                amount    = self.get_argument("amount")
                currency  = self.get_argument("currency", "EUR")
                frequency = self.get_argument("frequency")
 
-               # Get form inputs
-               args = {
-                       "amount"       : amount,
-                       "currency"     : currency,
+               organization = None
+               locale = self.get_browser_locale()
 
-                       # Is this a recurring donation?
-                       "recurring"    : frequency == "monthly",
+               # Get organization information
+               if type == "organization":
+                       organization = {
+                               "name"         : self.get_argument("organization"),
+                               "vat_number"   : self.get_argument("vat_number", None),
+                       }
 
-                       # Address
+               # Collect person information
+               person = {
                        "email"        : self.get_argument("email"),
                        "title"        : self.get_argument("title"),
                        "first_name"   : self.get_argument("first_name"),
                        "last_name"    : self.get_argument("last_name"),
-                       "company_name" : self.get_argument("company_name", None),
+                       "locale"       : locale.code,
+               }
+
+               # Collect address information
+               address = {
                        "street1"      : self.get_argument("street1"),
                        "street2"      : self.get_argument("street2", None),
                        "post_code"    : self.get_argument("post_code"),
@@ -72,28 +78,173 @@ class DonateHandler(base.BaseHandler):
                        "country_code" : self.get_argument("country_code"),
                }
 
-               # Add URLs to redirect the user back
-               args.update({
-                       "success_url"  : "https://%s/donate/thank-you" % self.request.host,
-                       "error_url"    : "https://%s/donate/error" % self.request.host,
-                       "back_url"     : "https://%s/donate?amount=%s&currency=%s&frequency=%s" %
-                               (self.request.host, amount, currency, frequency),
-               })
-
-               # Send request to Zeiterfassung
+               # Send everything to Zeiterfassung
                try:
-                       response = yield self.backend.zeiterfassung.send_request(
-                               "/api/v1/donations/create/ipfire-project", **args)
+                       # Create a new organization
+                       if organization:
+                               organization = await self._create_organization(organization, address)
+
+                       # Create a person
+                       person = await self._create_person(person, address, organization)
+
+                       # Create a new order
+                       order = await self._create_order(person=person, currency=currency)
+
+                       # Add donation to the order
+                       await self._create_donation(order, frequency, amount, currency,
+                               vat_included=(type == "individual"))
 
+                       # Submit the order
+                       needs_payment = await self._submit_order(order)
+
+                       # Pay the order
+                       if needs_payment:
+                               redirect_url = await self._pay_order(order)
+                       else:
+                               redirect_url = "https://%s/donate/thank-you" % self.request.host
+
+                       # Redirect the user to the payment page
+                       if not redirect_url:
+                               raise tornado.web.HTTPError(500, "Did not receive a redirect URL")
+
+                       self.redirect(redirect_url)
+
+               # XXX handle any problems when Zeiterfassung is unreachable
                except Exception:
-                       raise # XXX handle any problems when Zeiterfassung is unreachable
+                       raise
+
+       async def _create_organization(self, organization, address):
+               # Check if we have an existing organization
+               response = await self.backend.zeiterfassung.send_request(
+                       "/api/v1/organizations/search", **organization,
+               )
+
+               # Update details if we found a match
+               if response:
+                       number = response.get("number")
+
+                       # Update name
+                       await self.backend.zeiterfassung.send_request(
+                               "/api/v1/organizations/%s/name" % number, **organization
+                       )
+
+                       # Update VAT number
+                       vat_number = organization.get("vat_number", None)
+                       if vat_number:
+                               await self.backend.zeiterfassung.send_request(
+                                       "/api/v1/organizations/%s/vat-number" % number, vat_number=vat_number,
+                               )
+
+                       # Update address
+                       await self.backend.zeiterfassung.send_request(
+                               "/api/v1/organizations/%s/address" % number, **address,
+                       )
+
+                       return number
+
+               # Otherwise we will create a new one
+               response = await self.backend.zeiterfassung.send_request(
+                       "/api/v1/organizations/create", **organization, **address,
+               )
+
+               # Return the organization's number
+               return response.get("number")
+
+       async def _create_person(self, person, address, organization=None):
+               """
+                       Searches for a matching person or creates a new one
+               """
+               # Check if we have an existing person
+               response = await self.backend.zeiterfassung.send_request(
+                       "/api/v1/persons/search", **person
+               )
+
+               # Update details if we found a match
+               if response:
+                       number = response.get("number")
+
+                       # Update name
+                       await self.backend.zeiterfassung.send_request(
+                               "/api/v1/persons/%s/name" % number, **person,
+                       )
+
+                       # Update address
+                       await self.backend.zeiterfassung.send_request(
+                               "/api/v1/persons/%s/address" % number, **address,
+                       )
+
+                       return number
+
+               # If not, we will create a new one
+               response = await self.backend.zeiterfassung.send_request(
+                       "/api/v1/persons/create", organization=organization, **person, **address
+               )
+
+               # Return the persons's number
+               return response.get("number")
+
+       async def _create_order(self, person, currency=None):
+               """
+                       Creates a new order and returns its ID
+               """
+               response = await self.backend.zeiterfassung.send_request(
+                       "/api/v1/orders/create", person=person, currency=currency,
+               )
+
+               # Return the order number
+               return response.get("number")
+
+       async def _create_donation(self, order, frequency, amount, currency,
+                       vat_included=False):
+               """
+                       Creates a new donation
+               """
+               # Select the correct product
+               try:
+                       sku = SKUS[frequency]
+               except KeyError:
+                       sku = DEFAULT_SKU
+
+               # Add it to the order
+               await self.backend.zeiterfassung.send_request(
+                       "/api/v1/orders/%s/products/add" % order, sku=sku, quantity=1,
+               )
+
+               # Set the price
+               await self.backend.zeiterfassung.send_request(
+                       "/api/v1/orders/%s/products/%s/price" % (order, sku),
+                       price=amount, currency=currency, vat_included=vat_included,
+               )
+
+       async def _submit_order(self, order):
+               """
+                       Submits the order
+               """
+               response = await self.backend.zeiterfassung.send_request(
+                       "/api/v1/orders/%s/submit" % order,
+               )
+
+               # Return whether this needs payment
+               return not response.get("is_authorized")
+
+       async def _pay_order(self, order):
+               """
+                       Pay the order
+               """
+               # Add URLs to redirect the user back
+               urls = {
+                       "success_url" : "https://%s/donate/thank-you" % self.request.host,
+                       "error_url"   : "https://%s/donate/error" % self.request.host,
+                       "back_url"    : "https://%s/donate" % self.request.host,
+               }
 
-               # 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")
+               # Send request
+               response = await self.backend.zeiterfassung.send_request(
+                       "/api/v1/orders/%s/pay" % order, **urls,
+               )
 
-               self.redirect(redirect_url)
+               # Return redirect URL
+               return response.get("redirect_url", None)
 
 
 class ThankYouHandler(base.BaseHandler):