diff gpp/donations/views.py @ 36:296b610ee507

Added IPN logic to the donations module. Added a thank-you view. Need to add logging to the IPN function.
author Brian Neal <bgneal@gmail.com>
date Thu, 11 Jun 2009 00:54:44 +0000
parents f77a1cdd7a46
children 5dbfb7fec629
line wrap: on
line diff
--- a/gpp/donations/views.py	Sun Jun 07 00:22:50 2009 +0000
+++ b/gpp/donations/views.py	Thu Jun 11 00:54:44 2009 +0000
@@ -1,26 +1,43 @@
 """
 Views for the donations application.
 """
+import urllib2
+import decimal
+import datetime
+
 from django.shortcuts import render_to_response
 from django.template import RequestContext
-from django.http import Http404     # TODO: remove
 from django.conf import settings
 from django.contrib.sites.models import Site
+from django.http import HttpResponse
+from django.http import HttpResponseServerError
+from django.contrib.auth.models import User
 
 from donations.models import Donation
 
+PP_DATE_FMT = '%H:%M:%S %b %d, %Y PST'
 
-def index(request):
-    gross, net, donations = Donation.objects.monthly_stats()
-    current_site = Site.objects.get_current()
-
-    if settings.DEBUG:
+def paypal_params():
+    """
+    This function returns a tuple where the 1st element is the Paypal
+    URL and the 2nd element is the Paypal business email. This information
+    depends on the setting DONATIONS_DEBUG.
+    """
+    if settings.DONATIONS_DEBUG:
         form_action = 'https://www.sandbox.paypal.com/cgi-bin/webscr'
         business = settings.DONATIONS_BUSINESS_DEBUG
     else:
         form_action = 'https://www.paypal.com/cgi-bin/webscr'
         business = settings.DONATIONS_BUSINESS
 
+    return form_action, business
+
+
+def index(request):
+    gross, net, donations = Donation.objects.monthly_stats()
+    current_site = Site.objects.get_current()
+    form_action, business = paypal_params()
+
     return render_to_response('donations/index.html', {
         'goal': settings.DONATIONS_GOAL,
         'gross': gross,
@@ -39,8 +56,121 @@
 
 
 def ipn(request):
-    raise Http404       #TODO
+    """
+    This function is the IPN listener and handles the IPN POST from Paypal.
 
+    TODO: Add logging.
+    """
+    parameters = None
+    try:
+        if request['payment_status'] == 'Completed':
+            if request.POST:
+              parameters = request.POST.copy()
+            else:
+              parameters = request.GET.copy()
 
-def thanks(request):
-    raise Http404       #TODO
+        if parameters:
+            parameters['cmd']='_notify-validate'
+            req = urllib2.Request(paypal_params()[0], parameters.urlencode())
+            req.add_header("Content-type", "application/x-www-form-urlencoded")
+            response = urllib2.urlopen(req)
+            status = response.read()
+            if status != "VERIFIED":
+                parameters = None
+
+        if parameters:
+            # is this a donation to the site?
+            try:
+                item_number = int(parameters['item_number'])
+            except ValueError:
+                pass
+            else:
+                if item_number == settings.DONATIONS_ITEM_NUM or \
+                   item_number == settings.DONATIONS_ITEM_ANON_NUM:
+                    process_donation(item_number, parameters)
+
+            return HttpResponse("Ok")
+
+    except Exception, e:
+        # print "An exeption was caught: " + str(e)
+        pass
+
+    return HttpResponseServerError("Error")
+
+
+def process_donation(item_number, params):
+    """
+    Constructs a donation object from the parameters and stores
+    in the database.
+    """
+    # Has this transaction been processed before?
+    if 'txn_id' not in params:
+        return
+    txn_id = params['txn_id']
+    try:
+        donation = Donation.objects.get(txn_id__exact=txn_id)
+    except Donation.DoesNotExist:
+        pass
+    else:
+        return      # no exception, this is a duplicate
+
+    # Is the email address ours?
+    business = params.get('business')
+    if business != paypal_params()[1]:
+        return
+
+    # is this a payment received?
+    txn_type = params.get('txn_type')
+    if txn_type != 'web_accept':
+        return
+
+    # Looks like a donation, save it to the database.
+    # Determine which user this came from, if any.
+    # The username is stored in the custom field if the user was logged in when
+    # the donation was made.
+    user = None
+    if 'custom' in params:
+        try:
+            user = User.objects.get(username__exact=params['custom'])
+        except User.DoesNotExist:
+            pass
+
+    is_anonymous = item_number == settings.DONATIONS_ITEM_ANON_NUM
+    test_ipn = params.get('test_ipn') == '1'
+
+    first_name = params.get('first_name', '')
+    last_name = params.get('last_name', '')
+    payer_email = params.get('payer_email', '')
+    payer_id = params.get('payer_id', '')
+    memo = params.get('memo', '')
+    payer_status = params.get('payer_status', '')
+
+    try:
+        mc_gross = decimal.Decimal(params['mc_gross'])
+        mc_fee = decimal.Decimal(params['mc_fee'])
+    except KeyError, decimal.InvalidOperation:
+        return
+
+    try:
+        payment_date = datetime.datetime.strptime(params['payment_date'], 
+                PP_DATE_FMT)
+    except KeyError, ValueError:
+        return
+
+    donation = Donation(
+        user=user,
+        is_anonymous=is_anonymous,
+        test_ipn=test_ipn,
+        txn_id=txn_id,
+        first_name=first_name,
+        last_name=last_name,
+        payer_email=payer_email,
+        payer_id=payer_id,
+        memo=memo,
+        payer_status=payer_status,
+        mc_gross=mc_gross,
+        mc_fee=mc_fee,
+        payment_date=payment_date)
+
+    donation.save()
+