annotate gpp/donations/views.py @ 133:c515b7401078

Use the new common way to apply markItUp to textareas and to get the smiley and markdown help dialogs for all the remaining apps except for forums and comments.
author Brian Neal <bgneal@gmail.com>
date Fri, 27 Nov 2009 00:21:47 +0000
parents 9fabeabd89d4
children a3b47d0f4df1
rev   line source
bgneal@35 1 """
bgneal@35 2 Views for the donations application.
bgneal@35 3 """
bgneal@36 4 import urllib2
bgneal@36 5 import decimal
bgneal@36 6 import datetime
bgneal@36 7
bgneal@35 8 from django.shortcuts import render_to_response
bgneal@35 9 from django.template import RequestContext
bgneal@35 10 from django.conf import settings
bgneal@35 11 from django.contrib.sites.models import Site
bgneal@36 12 from django.http import HttpResponse
bgneal@36 13 from django.http import HttpResponseServerError
bgneal@36 14 from django.contrib.auth.models import User
bgneal@35 15
bgneal@35 16 from donations.models import Donation
bgneal@35 17
bgneal@63 18 PP_DATE_FMT = '%H:%M:%S %b %d, %Y'
bgneal@35 19
bgneal@36 20 def paypal_params():
bgneal@36 21 """
bgneal@36 22 This function returns a tuple where the 1st element is the Paypal
bgneal@36 23 URL and the 2nd element is the Paypal business email. This information
bgneal@36 24 depends on the setting DONATIONS_DEBUG.
bgneal@36 25 """
bgneal@36 26 if settings.DONATIONS_DEBUG:
bgneal@35 27 form_action = 'https://www.sandbox.paypal.com/cgi-bin/webscr'
bgneal@35 28 business = settings.DONATIONS_BUSINESS_DEBUG
bgneal@35 29 else:
bgneal@35 30 form_action = 'https://www.paypal.com/cgi-bin/webscr'
bgneal@35 31 business = settings.DONATIONS_BUSINESS
bgneal@35 32
bgneal@36 33 return form_action, business
bgneal@36 34
bgneal@36 35
bgneal@36 36 def index(request):
bgneal@36 37 gross, net, donations = Donation.objects.monthly_stats()
bgneal@36 38 current_site = Site.objects.get_current()
bgneal@36 39 form_action, business = paypal_params()
bgneal@36 40
bgneal@35 41 return render_to_response('donations/index.html', {
bgneal@35 42 'goal': settings.DONATIONS_GOAL,
bgneal@35 43 'gross': gross,
bgneal@35 44 'net': net,
bgneal@35 45 'left': settings.DONATIONS_GOAL - net,
bgneal@35 46 'donations': donations,
bgneal@35 47 'form_action': form_action,
bgneal@35 48 'business': business,
bgneal@35 49 'anonymous': settings.DONATIONS_ANON_NAME,
bgneal@35 50 'item_name': settings.DONATIONS_ITEM_NAME,
bgneal@35 51 'item_number': settings.DONATIONS_ITEM_NUM,
bgneal@35 52 'item_anon_number': settings.DONATIONS_ITEM_ANON_NUM,
bgneal@35 53 'domain': current_site.domain,
bgneal@35 54 },
bgneal@35 55 context_instance = RequestContext(request))
bgneal@35 56
bgneal@35 57
bgneal@35 58 def ipn(request):
bgneal@36 59 """
bgneal@36 60 This function is the IPN listener and handles the IPN POST from Paypal.
bgneal@39 61 The algorithm here roughly follows the outline described in chapter 2
bgneal@39 62 of Paypal's IPNGuide.pdf "Implementing an IPN Listener".
bgneal@39 63 """
bgneal@39 64 import logging
bgneal@35 65
bgneal@39 66 # Log some info about this IPN event
bgneal@39 67 ip = request.META.get('REMOTE_ADDR', '?')
bgneal@39 68 parameters = request.POST.copy()
bgneal@39 69 logging.info('IPN from %s; post data: %s' % (ip, parameters.urlencode()))
bgneal@35 70
bgneal@39 71 # Now we follow the instructions in chapter 2 of the Paypal IPNGuide.pdf.
bgneal@39 72 # Create a request that contains exactly the same IPN variables and values in
bgneal@39 73 # the same order, preceded with cmd=_notify-validate
bgneal@39 74 parameters['cmd']='_notify-validate'
bgneal@36 75
bgneal@39 76 # Post the request back to Paypal (either to the sandbox or the real deal).
bgneal@39 77 req = urllib2.Request(paypal_params()[0], parameters.urlencode())
bgneal@39 78 req.add_header("Content-type", "application/x-www-form-urlencoded")
bgneal@39 79 response = urllib2.urlopen(req)
bgneal@36 80
bgneal@39 81 # Wait for the response from Paypal, which should be either VERIFIED or INVALID.
bgneal@39 82 status = response.read()
bgneal@39 83 if status != 'VERIFIED':
bgneal@39 84 logging.warning('IPN: Payapl did not verify; status was %s' % status)
bgneal@39 85 return HttpResponse()
bgneal@36 86
bgneal@39 87 # Response was VERIFIED; act on this if it is a Completed donation,
bgneal@39 88 # otherwise don't handle it (we are just a donations application. Here
bgneal@39 89 # is where we could be expanded to be a more general payment processor).
bgneal@36 90
bgneal@39 91 payment_status = parameters.get('payment_status')
bgneal@39 92 if payment_status != 'Completed':
bgneal@39 93 logging.info('IPN: payment_status is %s; we are done.' % payment_status)
bgneal@39 94 return HttpResponse()
bgneal@39 95
bgneal@39 96 # Is this a donation to the site?
bgneal@39 97 item_number = parameters.get('item_number')
bgneal@39 98 if item_number == settings.DONATIONS_ITEM_NUM or \
bgneal@39 99 item_number == settings.DONATIONS_ITEM_ANON_NUM:
bgneal@39 100 process_donation(item_number, parameters)
bgneal@39 101 else:
bgneal@39 102 logging.info('IPN: not a donation; done.')
bgneal@39 103
bgneal@39 104 return HttpResponse()
bgneal@36 105
bgneal@36 106
bgneal@36 107 def process_donation(item_number, params):
bgneal@36 108 """
bgneal@39 109 A few validity and duplicate checks are made on the donation params.
bgneal@39 110 If everything is ok, construct a donation object from the parameters and
bgneal@39 111 store it in the database.
bgneal@36 112 """
bgneal@39 113 import logging
bgneal@39 114
bgneal@36 115 # Has this transaction been processed before?
bgneal@39 116 txn_id = params.get('txn_id')
bgneal@39 117 if txn_id is None:
bgneal@39 118 logging.error('IPN: missing txn_id')
bgneal@36 119 return
bgneal@39 120
bgneal@36 121 try:
bgneal@36 122 donation = Donation.objects.get(txn_id__exact=txn_id)
bgneal@36 123 except Donation.DoesNotExist:
bgneal@36 124 pass
bgneal@36 125 else:
bgneal@39 126 logging.warning('IPN: duplicate txn_id')
bgneal@36 127 return # no exception, this is a duplicate
bgneal@36 128
bgneal@36 129 # Is the email address ours?
bgneal@36 130 business = params.get('business')
bgneal@36 131 if business != paypal_params()[1]:
bgneal@39 132 logging.warning('IPN: invalid business: %s' % business)
bgneal@36 133 return
bgneal@36 134
bgneal@36 135 # is this a payment received?
bgneal@36 136 txn_type = params.get('txn_type')
bgneal@36 137 if txn_type != 'web_accept':
bgneal@39 138 logging.warning('IPN: invalid txn_type: %s' % txn_type)
bgneal@36 139 return
bgneal@61 140
bgneal@36 141 # Looks like a donation, save it to the database.
bgneal@36 142 # Determine which user this came from, if any.
bgneal@36 143 # The username is stored in the custom field if the user was logged in when
bgneal@36 144 # the donation was made.
bgneal@36 145 user = None
bgneal@65 146 if 'custom' in params and params['custom']:
bgneal@36 147 try:
bgneal@36 148 user = User.objects.get(username__exact=params['custom'])
bgneal@36 149 except User.DoesNotExist:
bgneal@36 150 pass
bgneal@36 151
bgneal@36 152 is_anonymous = item_number == settings.DONATIONS_ITEM_ANON_NUM
bgneal@36 153 test_ipn = params.get('test_ipn') == '1'
bgneal@36 154
bgneal@36 155 first_name = params.get('first_name', '')
bgneal@36 156 last_name = params.get('last_name', '')
bgneal@36 157 payer_email = params.get('payer_email', '')
bgneal@36 158 payer_id = params.get('payer_id', '')
bgneal@36 159 memo = params.get('memo', '')
bgneal@36 160 payer_status = params.get('payer_status', '')
bgneal@36 161
bgneal@36 162 try:
bgneal@36 163 mc_gross = decimal.Decimal(params['mc_gross'])
bgneal@36 164 mc_fee = decimal.Decimal(params['mc_fee'])
bgneal@36 165 except KeyError, decimal.InvalidOperation:
bgneal@39 166 logging.error('IPN: invalid/missing mc_gross or mc_fee')
bgneal@36 167 return
bgneal@36 168
bgneal@61 169 payment_date = params.get('payment_date')
bgneal@61 170 if payment_date is None:
bgneal@61 171 logging.error('IPN: missing payment_date')
bgneal@36 172 return
bgneal@36 173
bgneal@61 174 # strip off the timezone
bgneal@61 175 payment_date = payment_date[:-4]
bgneal@61 176 try:
bgneal@61 177 payment_date = datetime.datetime.strptime(payment_date, PP_DATE_FMT)
bgneal@61 178 except ValueError:
bgneal@61 179 logging.error('IPN: invalid payment_date "%s"' % params['payment_date'])
bgneal@61 180 return
bgneal@36 181
bgneal@61 182 try:
bgneal@61 183 donation = Donation(
bgneal@61 184 user=user,
bgneal@61 185 is_anonymous=is_anonymous,
bgneal@61 186 test_ipn=test_ipn,
bgneal@61 187 txn_id=txn_id,
bgneal@61 188 txn_type=txn_type,
bgneal@61 189 first_name=first_name,
bgneal@61 190 last_name=last_name,
bgneal@61 191 payer_email=payer_email,
bgneal@61 192 payer_id=payer_id,
bgneal@61 193 memo=memo,
bgneal@61 194 payer_status=payer_status,
bgneal@61 195 mc_gross=mc_gross,
bgneal@61 196 mc_fee=mc_fee,
bgneal@61 197 payment_date=payment_date)
bgneal@61 198 except:
bgneal@61 199 logging.exception('IPN: exception during donation creation')
bgneal@61 200 else:
bgneal@61 201 donation.save()
bgneal@61 202 logging.info('IPN: donation saved')
bgneal@36 203