Mercurial > public > sg101
comparison gpp/donations/views.py @ 39:5dbfb7fec629
Donations; reworked the IPN handling and added logging.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Fri, 12 Jun 2009 01:06:05 +0000 |
parents | 296b610ee507 |
children | 8c9344e36813 |
comparison
equal
deleted
inserted
replaced
38:c14cfd6be87a | 39:5dbfb7fec629 |
---|---|
56 | 56 |
57 | 57 |
58 def ipn(request): | 58 def ipn(request): |
59 """ | 59 """ |
60 This function is the IPN listener and handles the IPN POST from Paypal. | 60 This function is the IPN listener and handles the IPN POST from Paypal. |
61 The algorithm here roughly follows the outline described in chapter 2 | |
62 of Paypal's IPNGuide.pdf "Implementing an IPN Listener". | |
63 """ | |
64 import logging | |
61 | 65 |
62 TODO: Add logging. | 66 # Log some info about this IPN event |
63 """ | 67 ip = request.META.get('REMOTE_ADDR', '?') |
64 parameters = None | 68 parameters = request.POST.copy() |
65 try: | 69 logging.info('IPN from %s; post data: %s' % (ip, parameters.urlencode())) |
66 if request['payment_status'] == 'Completed': | |
67 if request.POST: | |
68 parameters = request.POST.copy() | |
69 else: | |
70 parameters = request.GET.copy() | |
71 | 70 |
72 if parameters: | 71 # Now we follow the instructions in chapter 2 of the Paypal IPNGuide.pdf. |
73 parameters['cmd']='_notify-validate' | 72 # Create a request that contains exactly the same IPN variables and values in |
74 req = urllib2.Request(paypal_params()[0], parameters.urlencode()) | 73 # the same order, preceded with cmd=_notify-validate |
75 req.add_header("Content-type", "application/x-www-form-urlencoded") | 74 parameters['cmd']='_notify-validate' |
76 response = urllib2.urlopen(req) | |
77 status = response.read() | |
78 if status != "VERIFIED": | |
79 parameters = None | |
80 | 75 |
81 if parameters: | 76 # Post the request back to Paypal (either to the sandbox or the real deal). |
82 # is this a donation to the site? | 77 req = urllib2.Request(paypal_params()[0], parameters.urlencode()) |
83 try: | 78 req.add_header("Content-type", "application/x-www-form-urlencoded") |
84 item_number = int(parameters['item_number']) | 79 response = urllib2.urlopen(req) |
85 except ValueError: | |
86 pass | |
87 else: | |
88 if item_number == settings.DONATIONS_ITEM_NUM or \ | |
89 item_number == settings.DONATIONS_ITEM_ANON_NUM: | |
90 process_donation(item_number, parameters) | |
91 | 80 |
92 return HttpResponse("Ok") | 81 # Wait for the response from Paypal, which should be either VERIFIED or INVALID. |
82 status = response.read() | |
83 if status != 'VERIFIED': | |
84 logging.warning('IPN: Payapl did not verify; status was %s' % status) | |
85 return HttpResponse() | |
93 | 86 |
94 except Exception, e: | 87 # Response was VERIFIED; act on this if it is a Completed donation, |
95 # print "An exeption was caught: " + str(e) | 88 # otherwise don't handle it (we are just a donations application. Here |
96 pass | 89 # is where we could be expanded to be a more general payment processor). |
97 | 90 |
98 return HttpResponseServerError("Error") | 91 payment_status = parameters.get('payment_status') |
92 if payment_status != 'Completed': | |
93 logging.info('IPN: payment_status is %s; we are done.' % payment_status) | |
94 return HttpResponse() | |
95 | |
96 # Is this a donation to the site? | |
97 item_number = parameters.get('item_number') | |
98 if item_number == settings.DONATIONS_ITEM_NUM or \ | |
99 item_number == settings.DONATIONS_ITEM_ANON_NUM: | |
100 process_donation(item_number, parameters) | |
101 else: | |
102 logging.info('IPN: not a donation; done.') | |
103 | |
104 return HttpResponse() | |
99 | 105 |
100 | 106 |
101 def process_donation(item_number, params): | 107 def process_donation(item_number, params): |
102 """ | 108 """ |
103 Constructs a donation object from the parameters and stores | 109 A few validity and duplicate checks are made on the donation params. |
104 in the database. | 110 If everything is ok, construct a donation object from the parameters and |
111 store it in the database. | |
105 """ | 112 """ |
113 import logging | |
114 | |
106 # Has this transaction been processed before? | 115 # Has this transaction been processed before? |
107 if 'txn_id' not in params: | 116 txn_id = params.get('txn_id') |
117 if txn_id is None: | |
118 logging.error('IPN: missing txn_id') | |
108 return | 119 return |
109 txn_id = params['txn_id'] | 120 |
110 try: | 121 try: |
111 donation = Donation.objects.get(txn_id__exact=txn_id) | 122 donation = Donation.objects.get(txn_id__exact=txn_id) |
112 except Donation.DoesNotExist: | 123 except Donation.DoesNotExist: |
113 pass | 124 pass |
114 else: | 125 else: |
126 logging.warning('IPN: duplicate txn_id') | |
115 return # no exception, this is a duplicate | 127 return # no exception, this is a duplicate |
116 | 128 |
117 # Is the email address ours? | 129 # Is the email address ours? |
118 business = params.get('business') | 130 business = params.get('business') |
119 if business != paypal_params()[1]: | 131 if business != paypal_params()[1]: |
132 logging.warning('IPN: invalid business: %s' % business) | |
120 return | 133 return |
121 | 134 |
122 # is this a payment received? | 135 # is this a payment received? |
123 txn_type = params.get('txn_type') | 136 txn_type = params.get('txn_type') |
124 if txn_type != 'web_accept': | 137 if txn_type != 'web_accept': |
138 logging.warning('IPN: invalid txn_type: %s' % txn_type) | |
125 return | 139 return |
126 | 140 |
127 # Looks like a donation, save it to the database. | 141 # Looks like a donation, save it to the database. |
128 # Determine which user this came from, if any. | 142 # Determine which user this came from, if any. |
129 # The username is stored in the custom field if the user was logged in when | 143 # The username is stored in the custom field if the user was logged in when |
147 | 161 |
148 try: | 162 try: |
149 mc_gross = decimal.Decimal(params['mc_gross']) | 163 mc_gross = decimal.Decimal(params['mc_gross']) |
150 mc_fee = decimal.Decimal(params['mc_fee']) | 164 mc_fee = decimal.Decimal(params['mc_fee']) |
151 except KeyError, decimal.InvalidOperation: | 165 except KeyError, decimal.InvalidOperation: |
166 logging.error('IPN: invalid/missing mc_gross or mc_fee') | |
152 return | 167 return |
153 | 168 |
154 try: | 169 try: |
155 payment_date = datetime.datetime.strptime(params['payment_date'], | 170 payment_date = datetime.datetime.strptime(params['payment_date'], |
156 PP_DATE_FMT) | 171 PP_DATE_FMT) |
157 except KeyError, ValueError: | 172 except KeyError, ValueError: |
173 logging.error('IPN: invalid/missing payment_date') | |
158 return | 174 return |
159 | 175 |
160 donation = Donation( | 176 donation = Donation( |
161 user=user, | 177 user=user, |
162 is_anonymous=is_anonymous, | 178 is_anonymous=is_anonymous, |
171 mc_gross=mc_gross, | 187 mc_gross=mc_gross, |
172 mc_fee=mc_fee, | 188 mc_fee=mc_fee, |
173 payment_date=payment_date) | 189 payment_date=payment_date) |
174 | 190 |
175 donation.save() | 191 donation.save() |
192 logging.info('IPN: donation saved') | |
176 | 193 |