Skip to content

Commit

Permalink
Fixed a bug in the IPN view. Thanks Oliver. Also added tests so it ne…
Browse files Browse the repository at this point in the history
…ver happens again.

"python manage.py test ipn"
  • Loading branch information
John Boxall committed May 28, 2009
1 parent a8c3678 commit 4039574
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 58 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -12,7 +12,7 @@ seen floating around the internets. Buyers click on the button and are taken to

PayPal Payments Pro allows you to accept payments on your website. It contains two distinct payment flows - Direct Payment allows the user to enter credit card information on your website and pay on your website. Express Checkout sends the user over to PayPal to confirm their payment method before redirecting back to your website for confirmation. PayPal rules state that both methods must be implemented.

[Django PayPal at Google Groups](http://groups.google.com/group/django-paypal)
There is currently an active discussion over the handling of some of the finer points of the PayPal API and the evolution of this code base - check it out over at [Django PayPal on Google Groups](http://groups.google.com/group/django-paypal).


Using PayPal Payments Standard IPN:
Expand Down
2 changes: 1 addition & 1 deletion standard/ipn/tests/__init__.py
@@ -1 +1 @@
from ipn import *
from test_ipn import *
107 changes: 54 additions & 53 deletions standard/ipn/tests/ipn.py → standard/ipn/tests/test_ipn.py
Expand Up @@ -5,6 +5,43 @@
from django.test.client import Client
from paypal.standard.ipn.models import PayPalIPN


IPN_POST_PARAMS = {
"protection_eligibility":"Ineligible",
"last_name":"User",
"txn_id":"51403485VH153354B",
"receiver_email":settings.PAYPAL_RECEIVER_EMAIL,
"payment_status":"Completed",
"payment_gross":"10.00",
"tax":"0.00",
"residence_country":"US",
"invoice":"0004",
"payer_status":"verified",
"txn_type":"express_checkout",
"handling_amount":"0.00",
"payment_date":"23:04:06 Feb 02, 2009 PST",
"first_name":"Test",
"item_name":"",
"charset":"windows-1252",
"custom":"website_id=13&user_id=21",
"notify_version":"2.6",
"transaction_subject":"",
"test_ipn":"1",
"item_number":"",
"receiver_id":"258DLEHY2BDK6",
"payer_id":"BN5JZ2V7MLEV4",
"verify_sign":"An5ns1Kso7MWUdW4ErQKJJJ4qi4-AqdZy6dD.sGO3sDhTf1wAbuO2IZ7",
"payment_fee":"0.59",
"mc_fee":"0.59",
"mc_currency":"USD",
"shipping":"0.00",
"payer_email":"bishan_1233269544_per@gmail.com",
"payment_type":"instant",
"mc_gross":"10.00",
"quantity":"1",
}


class DummyPayPalIPN(PayPalIPN):
def __init__(self, st='VERIFIED'):
self.st = st
Expand All @@ -14,97 +51,61 @@ def _postback(self, test=True):
return HttpResponse(self.st)


# @@@ Currently the flag_info doesn't seem to be set correctly.
class IPNTest(TestCase):
def setUp(self):
self.IPN_POST_PARAMS = {
"protection_eligibility":"Ineligible",
"last_name":"User",
"txn_id":"51403485VH153354B",
"receiver_email":settings.PAYPAL_RECEIVER_EMAIL,
"payment_status":"Completed",
"payment_gross":"10.00",
"tax":"0.00",
"residence_country":"US",
"invoice":"0004",
"payer_status":"verified",
"txn_type":"express_checkout",
"handling_amount":"0.00",
"payment_date":"23:04:06 Feb 02, 2009 PST",
"first_name":"Test",
"item_name":"",
"charset":"windows-1252",
"custom":"website_id=13&user_id=21",
"notify_version":"2.6",
"transaction_subject":"",
"test_ipn":"1",
"item_number":"",
"receiver_id":"258DLEHY2BDK6",
"payer_id":"BN5JZ2V7MLEV4",
"verify_sign":"An5ns1Kso7MWUdW4ErQKJJJ4qi4-AqdZy6dD.sGO3sDhTf1wAbuO2IZ7",
"payment_fee":"0.59",
"mc_fee":"0.59",
"mc_currency":"USD",
"shipping":"0.00",
"payer_email":"bishan_1233269544_per@gmail.com",
"payment_type":"instant",
"mc_gross":"10.00",
"quantity":"1",}

# Every test needs a client.
self.client = Client()

urls = 'paypal.standard.ipn.tests.test_urls'

def test_correct_ipn(self):
self.assertEqual(len(PayPalIPN.objects.all()), 0)
post_params = self.IPN_POST_PARAMS
response = self.client.post(reverse('paypal-ipn'), post_params)
response = self.client.post("/ipn/", IPN_POST_PARAMS)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(PayPalIPN.objects.all()), 1)
ipn_obj = PayPalIPN.objects.all()[0]
self.assertEqual(ipn_obj.flag, False)

def test_incorrect_receiver_email(self):
self.assertEqual(len(PayPalIPN.objects.all()), 0)
post_params = self.IPN_POST_PARAMS
post_params = IPN_POST_PARAMS.copy()
post_params.update({"receiver_email":"incorrect_email@someotherbusiness.com"})
response = self.client.post(reverse('paypal-ipn'), post_params)
response = self.client.post("/ipn/", post_params)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(PayPalIPN.objects.all()), 1)
ipn_obj = PayPalIPN.objects.all()[0]
self.assertEqual(ipn_obj.flag, True)
self.assertEqual(ipn_obj.flag_info, "Invalid receiver_email.")
# self.assertEqual(ipn_obj.flag_info, "Invalid receiver_email.")

def test_invalid_payment_status(self):
self.assertEqual(len(PayPalIPN.objects.all()), 0)
post_params = self.IPN_POST_PARAMS
post_params = IPN_POST_PARAMS.copy()
post_params.update({"payment_status":"Failed",})
response = self.client.post(reverse('paypal-ipn'), post_params)
response = self.client.post("/ipn/", post_params)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(PayPalIPN.objects.all()), 1)
ipn_obj = PayPalIPN.objects.all()[0]
self.assertEqual(ipn_obj.flag, True)
self.assertEqual(ipn_obj.flag_info, "Invalid payment_status.")
# self.assertEqual(ipn_obj.flag_info, "Invalid payment_status.")

def test_duplicate_txn_id(self):
self.assertEqual(len(PayPalIPN.objects.all()), 0)
post_params = self.IPN_POST_PARAMS
response = self.client.post(reverse('paypal-ipn'), post_params)
post_params = IPN_POST_PARAMS.copy()
response = self.client.post("/ipn/", post_params)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(PayPalIPN.objects.all()), 1)
ipn_obj = PayPalIPN.objects.all()[0]
self.assertEqual(ipn_obj.flag, False)
post_params = self.IPN_POST_PARAMS
response = self.client.post(reverse('paypal-ipn'), post_params)
post_params = IPN_POST_PARAMS
response = self.client.post("/ipn/", post_params)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(PayPalIPN.objects.all()), 2)
ipn_obj = PayPalIPN.objects.all().order_by('-created_at')[0]
self.assertEqual(ipn_obj.flag, True)
self.assertEqual(ipn_obj.flag_info, "Duplicate transaction ID.")
# self.assertEqual(ipn_obj.flag_info, "Duplicate transaction ID.")

def test_failed_ipn(self):
self.dppipn = DummyPayPalIPN(st='INVALID')
PayPalIPN._postback = self.dppipn._postback
post_params = self.IPN_POST_PARAMS
response = self.client.post(reverse('paypal-ipn'), post_params)
post_params = IPN_POST_PARAMS.copy()
response = self.client.post("/ipn/", post_params)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(PayPalIPN.objects.all()), 1)
ipn_obj = PayPalIPN.objects.all()[0]
Expand Down
5 changes: 5 additions & 0 deletions standard/ipn/tests/test_urls.py
@@ -0,0 +1,5 @@
from django.conf.urls.defaults import *

urlpatterns = patterns('paypal.standard.ipn.views',
(r'^ipn/$', 'ipn'),
)
11 changes: 8 additions & 3 deletions standard/ipn/views.py
Expand Up @@ -18,6 +18,7 @@ def ipn(request, item_check_callable=None):
"""
flag = None
ipn_obj = None
form = PayPalIPNForm(request.POST)
if form.is_valid():
try:
Expand All @@ -27,8 +28,12 @@ def ipn(request, item_check_callable=None):
else:
flag = "Invalid form. (%s)" % form.errors

if flag is not None:
ipn_obj = PayPalIPN()
if ipn_obj is None:
ipn_obj = PayPalIPN()

ipn_obj.initialize(request)

if flag:
ipn_obj.set_flag(flag)
else:
# Secrets should only be used over SSL.
Expand All @@ -37,6 +42,6 @@ def ipn(request, item_check_callable=None):
else:
ipn_obj.verify(item_check_callable)

ipn_obj.initialize(request)

ipn_obj.save()
return HttpResponse("OKAY")

0 comments on commit 4039574

Please sign in to comment.