Skip to content

Commit

Permalink
Fixed crasher with Python 3 and IPN postbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
spookylukey committed Mar 20, 2014
1 parent c5371c9 commit 536415a
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 20 deletions.
7 changes: 5 additions & 2 deletions paypal/standard/ipn/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from six import b
from six.moves.urllib.request import urlopen

from paypal.standard.models import PayPalStandardBase
Expand All @@ -16,11 +19,11 @@ class Meta:

def _postback(self):
"""Perform PayPal Postback validation."""
return urlopen(self.get_endpoint(), "cmd=_notify-validate&%s" % self.query).read()
return urlopen(self.get_endpoint(), b("cmd=_notify-validate&%s" % self.query)).read()

def _verify_postback(self):
if self.response != "VERIFIED":
self.set_flag("Invalid postback. (%s)" % self.response)
self.set_flag("Invalid postback. ({0})".format(self.response))

def send_signals(self):
"""Shout for the world to hear whether a txn was successful."""
Expand Down
49 changes: 32 additions & 17 deletions paypal/standard/ipn/tests/test_ipn.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,13 @@
}


class IPNTest(TestCase):
class IPNTestBase(TestCase):
urls = 'paypal.standard.ipn.tests.test_urls'

def setUp(self):
self.old_debug = settings.DEBUG
settings.DEBUG = True

# Monkey patch over PayPalIPN to make it get a VERFIED response.
self.old_postback = PayPalIPN._postback
PayPalIPN._postback = lambda self: "VERIFIED"

self.payment_was_successful_receivers = payment_was_successful.receivers
self.payment_was_flagged_receivers = payment_was_flagged.receivers
self.payment_was_refunded_receivers = payment_was_refunded.receivers
Expand All @@ -81,10 +77,8 @@ def setUp(self):
recurring_payment.receivers = []
recurring_cancel.receivers = []


def tearDown(self):
settings.DEBUG = self.old_debug
PayPalIPN._postback = self.old_postback

payment_was_successful.receivers = self.payment_was_successful_receivers
payment_was_flagged.receivers = self.payment_was_flagged_receivers
Expand Down Expand Up @@ -126,13 +120,34 @@ def handle_signal(sender, **kwargs):
self.assertEqual(self.signal_obj, ipn_obj)
return ipn_obj

def assertFlagged(self, updates, flag_info):
params = IPN_POST_PARAMS.copy()
params.update(updates)
response = self.paypal_post(params)
self.assertEqual(response.status_code, 200)
ipn_obj = PayPalIPN.objects.all()[0]
self.assertEqual(ipn_obj.flag, True)
self.assertEqual(ipn_obj.flag_info, flag_info)
return ipn_obj


class IPNTest(IPNTestBase):

def setUp(self):
# Monkey patch over PayPalIPN to make it get a VERFIED response.
self.old_postback = PayPalIPN._postback
PayPalIPN._postback = lambda self: b("VERIFIED")

def tearDown(self):
PayPalIPN._postback = self.old_postback

def test_correct_ipn(self):
ipn_obj = self.assertGotSignal(payment_was_successful, False)
# Check some encoding issues:
self.assertEqual(ipn_obj.first_name, u"J\u00f6rg")

def test_failed_ipn(self):
PayPalIPN._postback = lambda self: "INVALID"
PayPalIPN._postback = lambda self: b("INVALID")
self.assertGotSignal(payment_was_flagged, True)

def test_refunded_ipn(self):
Expand Down Expand Up @@ -163,15 +178,6 @@ def test_reversed_ipn(self):

self.assertGotSignal(payment_was_reversed, False, params)

def assertFlagged(self, updates, flag_info):
params = IPN_POST_PARAMS.copy()
params.update(updates)
response = self.paypal_post(params)
self.assertEqual(response.status_code, 200)
ipn_obj = PayPalIPN.objects.all()[0]
self.assertEqual(ipn_obj.flag, True)
self.assertEqual(ipn_obj.flag_info, flag_info)

def test_incorrect_receiver_email(self):
update = {"receiver_email": "[email protected]"}
flag_info = "Invalid receiver_email. ([email protected])"
Expand Down Expand Up @@ -271,3 +277,12 @@ def handle_signal(sender, **kwargs):
ipns = PayPalIPN.objects.all()
self.assertEqual(len(ipns), 1)
self.assertFalse(self.got_signal)


class IPNPostbackTest(IPNTestBase):
"""
Tests an actual postback to PayPal server.
"""
def test_postback(self):
# Incorrect signature means we will always get failure
self.assertFlagged({}, u'Invalid postback. (INVALID)')
3 changes: 2 additions & 1 deletion paypal/standard/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django.db import models

from paypal.standard.helpers import duplicate_txn_id, check_secret
from paypal.standard.conf import RECEIVER_EMAIL, POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT

Expand Down Expand Up @@ -263,7 +264,7 @@ def verify(self, item_check_callable=None):
that `mc_gross`, `mc_currency` `item_name` and `item_number` are all correct.
"""
self.response = self._postback()
self.response = self._postback().decode('ascii')
self._verify_postback()
if not self.flag:
if self.is_transaction():
Expand Down

0 comments on commit 536415a

Please sign in to comment.