PayPal's https://www.paypal.com/ipn is ridiculously easy to consume. You can tell PayPal to POST every single transaction on your account to a URL somewhere, then set up a handler at that URL which processes those transactions in some way. Security is ensured by POSTing the incoming data back to PayPal for confirmation that the transaction is legitimate.
These classes are probably over-engineered, but they were a fun experiment in creating class-based generic views.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | """
Classes for accepting PayPal's Instant Payment Notification messages in a
Django application (or Django-on-App-Engine):
https://www.paypal.com/ipn
Usage:
from paypal import Endpoint # Or AppEngineEndpoint as Endpoint
class MyEndPoint(Endpoint):
def process(self, data):
# Do something with valid data from PayPal - e-mail it to yourself,
# stick it in a database, generate a license key and e-mail it to the
# user... whatever
def process_invalid(self, data):
# Do something with invalid data (could be from anywhere) - you
# should probably log this somewhere
These methods can optionally return an HttpResponse - if they don't, a
default response will be sent.
Then in urls.py:
(r'^endpoint/$', MyEndPoint()),
"data" looks something like this:
{
'business': '[email protected]',
'charset': 'windows-1252',
'cmd': '_notify-validate',
'first_name': 'S',
'last_name': 'Willison',
'mc_currency': 'GBP',
'mc_fee': '0.01',
'mc_gross': '0.01',
'notify_version': '2.4',
'payer_business_name': 'Example Ltd',
'payer_email': '[email protected]',
'payer_id': '5YKXXXXXX6',
'payer_status': 'verified',
'payment_date': '11:45:00 Aug 13, 2008 PDT',
'payment_fee': '',
'payment_gross': '',
'payment_status': 'Completed',
'payment_type': 'instant',
'receiver_email': '[email protected]',
'receiver_id': 'CXZXXXXXQ',
'residence_country': 'GB',
'txn_id': '79F58253T2487374D',
'txn_type': 'send_money',
'verify_sign': 'AOH.JxXLRThnyE4toeuh-.oeurch23.QyBY-O1N'
}
"""
from django.http import HttpResponse
import urllib
class Endpoint:
default_response_text = 'Nothing to see here'
verify_url = "https://www.paypal.com/cgi-bin/webscr"
def do_post(self, url, args):
return urllib.urlopen(url, urllib.urlencode(args)).read()
def verify(self, data):
args = {
'cmd': '_notify-validate',
}
args.update(data)
return self.do_post(self.verify_url, args) == 'VERIFIED'
def default_response(self):
return HttpResponse(self.default_response_text)
def __call__(self, request):
r = None
if request.method == 'POST':
data = dict(request.POST.items())
# We need to post that BACK to PayPal to confirm it
if self.verify(data):
r = self.process(data)
else:
r = self.process_invalid(data)
if r:
return r
else:
return self.default_response()
def process(self, data):
pass
def process_invalid(self, data):
pass
class AppEngineEndpoint(Endpoint):
def do_post(self, url, args):
from google.appengine.api import urlfetch
return urlfetch.fetch(
url = url,
method = urlfetch.POST,
payload = urllib.urlencode(args)
).content
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
PayPal have an IPN simulation tool for testing, which you can use to send example requests to your endpoint URL:
https://developer.paypal.com/cgi-bin/devscr?cmd=_ipn-link-session
(You'll need to sign up for a PayPal developer account to use it)
Annoyingly, it doesn't look like it's possible to get that tool to send UTF8 rather than windows-1252.
#
Great snippet. Much cleaner than the paypalipn app I created.
One thing though, I think you should make the Endpoint class inherit 'object' at it's base..
class Endpoint(object):
So that when over writing methods like __init__ you can call super() without any problems.
For instance, I wanted to change the obj.verify_url when calling the class for testing purposes..
def __init__(self, *args, **kwargs): is_test = kwargs.pop('is_test', False) super(PaypalIPN, self).__init__(*args, **kwargs) if is_test: self.verify_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr'
Without object as the base inheritance this raises a TypeError.
Thanks again!
#
does anyone managed to make express checkout work?
#
I ran into Unicode errors when processing orders from some countries (Encode/Decode errors)..
I fixed it by replacing args.update(data) in the verify method with the following:
Seemed to solve my issues..
#
This snippet helps me to understand how IPN works, and eventually I am able to implement my own IPN handler (Django 1.6 on Python 2.6). Thank you.
One thing I am not sure is that how to make sure the cmd=_notify_validate is put in the beginning of the parameter list (as requested by PayPal) without the use of an OrderedDict. So I force it to the beginning by doing something similar to the following:
'cmd=_notify_validate&' + urllib.urlencode(data)
#
Please login first before commenting.