Skip to content

Commit

Permalink
resolve send_signals conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
mthornhill committed May 12, 2009
2 parents 616f56f + 671603f commit b0a6847
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 23 deletions.
8 changes: 4 additions & 4 deletions standard/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from django.contrib.auth.models import get_hexdigest


def duplicate_txn_id(ipn_obj):
"""Returns True if a record with this transaction id exists."""
return ipn_obj._default_manager.filter(txn_id=ipn_obj.txn_id).count() > 0
def duplicate_txn_id(ipn_obj, from_view='notify'):
"Returns True if a record with this transaction id exists."
return ipn_obj._default_manager.filter(txn_id=ipn_obj.txn_id, from_view=from_view).count() > 0

def make_secret(form_instance, secret_fields=None):
"""
Expand Down Expand Up @@ -45,4 +45,4 @@ def check_secret(form_instance, secret):
"""
# ### ToDo: add invoice & custom
# secret_fields = ['business', 'item_name']
return make_secret(form_instance) == secret
return make_secret(form_instance) == secret
36 changes: 24 additions & 12 deletions standard/ipn/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,10 @@
from paypal.standard.ipn.forms import PayPalIPNForm
from paypal.standard.ipn.models import PayPalIPN


@require_POST
def ipn(request, item_check_callable=None):
def handle_ipn_post(request, item_check_callable=None, from_view='notify'):
"""Naive wrapper for ipn view, so we can set the view whence the ipn came from
"""
PayPal IPN endpoint (notify_url).
Used by both PayPal Payments Pro and Payments Standard to confirm transactions.
http://tinyurl.com/d9vu9d
PayPal IPN Simulator:
https://developer.paypal.com/cgi-bin/devscr?cmd=_ipn-link-session
"""

form = PayPalIPNForm(request.POST)
if form.is_valid():
try:
Expand All @@ -27,6 +19,9 @@ def ipn(request, item_check_callable=None):
else:
ipn_obj.set_flag("Invalid form. (%s)" % form.errors)

# notify/return/cancel
ipn_obj.from_view = from_view

ipn_obj.initialize(request)
if not ipn_obj.flag:
# Secrets should only be used over SSL.
Expand All @@ -35,5 +30,22 @@ def ipn(request, item_check_callable=None):
else:
ipn_obj.verify(item_check_callable)

return ipn_obj

@require_POST
def ipn(request, item_check_callable=None):
"""
PayPal IPN endpoint (notify_url).
Used by both PayPal Payments Pro and Payments Standard to confirm transactions.
http://tinyurl.com/d9vu9d
PayPal IPN Simulator:
https://developer.paypal.com/cgi-bin/devscr?cmd=_ipn-link-session
"""

ipn_obj = handle_ipn_post(request, item_check_callable=item_check_callable, from_view='notify')

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

38 changes: 31 additions & 7 deletions standard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,10 @@ class PayPalStandardBase(models.Model):
response = models.TextField(blank=True) # What we got back.
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)


# Where did it come from?
from_view = models.CharField(max_length=6, null=True, blank=True)

class Meta:
abstract = True

Expand Down Expand Up @@ -212,7 +215,7 @@ def verify(self, item_check_callable=None):
if self.is_transaction():
if self.payment_status != "Completed":
self.set_flag("Invalid payment_status (%s). " % self.payment_status)
if duplicate_txn_id(self):
if duplicate_txn_id(self, self.from_view):
self.set_flag("Duplicate transaction ID (%s). " % self.txn_id)
if self.receiver_email != RECEIVER_EMAIL:
self.set_flag("Invalid receiver_email (%s). " % self.receiver_email)
Expand Down Expand Up @@ -241,19 +244,40 @@ def get_endpoint(self):
else:
return POSTBACK_ENDPOINT

def send_signals(self):
"""Shout for the world to hear whether a txn was successful."""

# Don't do anything if we're not notifying!
if self.from_view != 'notify':
return

# Transaction signals:
if self.is_transaction():
if self.flag:
payment_was_flagged.send(sender=self)
else:
payment_was_successful.send(sender=self)
# Subscription signals:
else:
if self.is_subscription_cancellation():
subscription_cancel.send(sender=self)
elif self.is_subscription_signup():
subscription_signup.send(sender=self)
elif self.is_subscription_end_of_term():
subscription_eot.send(sender=self)
elif self.is_subscription_modified():
subscription_modify.send(sender=self)


def initialize(self, request):
"""Store the data we'll need to make the postback from the request object."""
self.query = getattr(request, request.method).urlencode()
self.ipaddress = request.META.get('REMOTE_ADDR', '')

def send_signals(self):
"""After a transaction is completed use this to send success/fail signals"""
raise NotImplementedError

def _postback(self):
"""Perform postback to PayPal and store the response in self.response."""
raise NotImplementedError

def _verify_postback(self):
"""Check self.response is valid andcall self.set_flag if there is an error."""
raise NotImplementedError
raise NotImplementedError
25 changes: 25 additions & 0 deletions standard/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""
Note that sometimes you will get duplicate signals emitted, depending on configuration of your systems.
If you do encounter this, you will need to add the "dispatch_uid" to your connect handlers:
http://code.djangoproject.com/wiki/Signals#Helppost_saveseemstobeemittedtwiceforeachsave
"""
from django.dispatch import Signal

# Sent when a payment is successfully processed.
payment_was_successful = Signal()

# Sent when a payment is flagged.
payment_was_flagged = Signal()

# Sent when a subscription was cancelled.
subscription_cancel = Signal()

# Sent when a subscription expires.
subscription_eot = Signal()

# Sent when a subscription was modified.
subscription_modify = Signal()

# Sent when a subscription ends.
subscription_signup = Signal()

0 comments on commit b0a6847

Please sign in to comment.