From 4e4f6dc9515a5e6bdeecf5c0c3b7ee03aef87a00 Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Sat, 10 Mar 2012 14:07:14 +0530 Subject: [PATCH 01/17] Read me and forms updated. Read me Added note for use in production and debug mode. Standard: forms.py render() will now point to sandbox url when TEST is on. Added tax and tax_rate properties. --- README.md | 6 +++++- paypal/standard/forms.py | 10 +++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1a0b063..035003f 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ PayPal Payments Pro allows you to accept payments on your website. It contains t 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). +**Note:** When using this module for production code, then set `PAYPAL_TEST` to `False`. If you do not set this then it is assumed to be `True`! When this flag is enabled then all traffics are directed towards [Paypal Sandbox](https://developer.paypal.com). Make sure you have an account on that and have created some test accounts. Using PayPal Payments Standard IPN: ------------------------------- @@ -66,6 +67,8 @@ Using PayPal Payments Standard IPN: {{ form.render }} + **Note:** Do not use `PayPalPaymentsForm` for production code. Instead at least use `PayPalEncryptedPaymentsForm`. (See the section - Using PayPal Payments Standard with Encrypted Buttons). If that is not possible then generate a [hosted button from Paypal](https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_ButtonMgrAPIIntro#id093VD0JE0Y4). + 1. When someone uses this button to buy something PayPal makes a HTTP POST to your "notify_url". PayPal calls this Instant Payment Notification (IPN). The view `paypal.standard.ipn.views.ipn` handles IPN processing. To set the @@ -128,6 +131,7 @@ Paypal Payment Data Transfer (PDT) allows you to display transaction details to INSTALLED_APPS = (... 'paypal.standard.pdt', ...) PAYPAL_IDENTITY_TOKEN = "xxx" + PAYPAL_RECEIVER_EMAIL = "yourpaypalemail@example.com" 1. Create a view that uses `PayPalPaymentsForm` just like in PayPal IPN. @@ -195,7 +199,7 @@ Use this method to encrypt your button so sneaky gits don't try to hack it. Than [https://www.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert](https://www.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert) - [https://www.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert](https://www.sandbox.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert) + [https://www.sandbox.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert](https://www.sandbox.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert) 1. Copy your `cert id` - you'll need it in two steps. It's on the screen where you uploaded your public key. diff --git a/paypal/standard/forms.py b/paypal/standard/forms.py index e9992a5..af816bd 100644 --- a/paypal/standard/forms.py +++ b/paypal/standard/forms.py @@ -59,6 +59,8 @@ class PayPalPaymentsForm(forms.Form): item_name = forms.CharField(widget=ValueHiddenInput()) item_number = forms.CharField(widget=ValueHiddenInput()) quantity = forms.CharField(widget=ValueHiddenInput()) + tax_rate = forms.FloatField(widget=ValueHiddenInput()) + tax = forms.FloatField(widget=ValueHiddenInput()) # Subscription Related. a1 = forms.CharField(widget=ValueHiddenInput()) # Trial 1 Price @@ -99,7 +101,13 @@ def __init__(self, button_type="buy", *args, **kwargs): super(PayPalPaymentsForm, self).__init__(*args, **kwargs) self.button_type = button_type - def render(self): + def render(self): + if TEST: + self.sandbox(); + else: + self.renderProd(); + + def renderProd(self): return mark_safe(u"""
%s From 2537d508f7374cb15da98467fe4df7f54dbdee44 Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Sat, 10 Mar 2012 14:13:54 +0530 Subject: [PATCH 02/17] Some indent fix --- paypal/standard/forms.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/paypal/standard/forms.py b/paypal/standard/forms.py index af816bd..d7e99c5 100644 --- a/paypal/standard/forms.py +++ b/paypal/standard/forms.py @@ -59,8 +59,8 @@ class PayPalPaymentsForm(forms.Form): item_name = forms.CharField(widget=ValueHiddenInput()) item_number = forms.CharField(widget=ValueHiddenInput()) quantity = forms.CharField(widget=ValueHiddenInput()) - tax_rate = forms.FloatField(widget=ValueHiddenInput()) - tax = forms.FloatField(widget=ValueHiddenInput()) + tax_rate = forms.FloatField(widget=ValueHiddenInput()) + tax = forms.FloatField(widget=ValueHiddenInput()) # Subscription Related. a1 = forms.CharField(widget=ValueHiddenInput()) # Trial 1 Price @@ -101,11 +101,11 @@ def __init__(self, button_type="buy", *args, **kwargs): super(PayPalPaymentsForm, self).__init__(*args, **kwargs) self.button_type = button_type - def render(self): - if TEST: - self.sandbox(); - else: - self.renderProd(); + def render(self): + if TEST: + self.sandbox(); + else: + self.renderProd(); def renderProd(self): return mark_safe(u""" From 4925f1dc290ce5db7656b0d65fc9737da069f07c Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Sat, 10 Mar 2012 14:15:58 +0530 Subject: [PATCH 03/17] forms.py fix --- paypal/standard/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paypal/standard/forms.py b/paypal/standard/forms.py index d7e99c5..68abc97 100644 --- a/paypal/standard/forms.py +++ b/paypal/standard/forms.py @@ -103,9 +103,9 @@ def __init__(self, button_type="buy", *args, **kwargs): def render(self): if TEST: - self.sandbox(); + return self.sandbox(); else: - self.renderProd(); + return self.renderProd(); def renderProd(self): return mark_safe(u""" From 53993a347858976fc86bcc64e01987b232e5af97 Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Sat, 10 Mar 2012 14:57:43 +0530 Subject: [PATCH 04/17] Updating clone url to point to dcramer --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 035003f..3519d8b 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Using PayPal Payments Standard IPN: 1. Download the code from GitHub: - git clone git://github.com/johnboxall/django-paypal.git paypal + git clone git://github.com/dcramer/django-paypal.git paypal 1. Edit `settings.py` and add `paypal.standard.ipn` to your `INSTALLED_APPS` and `PAYPAL_RECEIVER_EMAIL`: From 927e558c4f0a3ff2154650e3917fcd06e8901f85 Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Sat, 10 Mar 2012 18:19:00 +0530 Subject: [PATCH 05/17] shezi changes to add pdt() decorator for PDT --- paypal/standard/pdt/decorators.py | 56 +++++++++++++++++++++++++++++++ paypal/standard/pdt/views.py | 48 ++++---------------------- 2 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 paypal/standard/pdt/decorators.py diff --git a/paypal/standard/pdt/decorators.py b/paypal/standard/pdt/decorators.py new file mode 100644 index 0000000..3539fdf --- /dev/null +++ b/paypal/standard/pdt/decorators.py @@ -0,0 +1,56 @@ +# -*- encoding: utf-8 -*- + +from paypal.standard.pdt.models import PayPalPDT +from paypal.standard.pdt.forms import PayPalPDTForm + + +def pdt(f): + """Parses out GET parameters corresponding to a paypal PDT request and adds `pdt_active`, `pdt_failed` and `pdt` to the call **kwargs. + + Payment data transfer implementation: http://tinyurl.com/c9jjmw""" + + def aux(request, *args, **kwargs): + if request.method == 'POST': + return f(request, *args, **kwargs) + + pdt_obj = None + pdt_active = False + txn_id = request.GET.get('tx') + failed = False + if txn_id is not None: + pdt_active = True + # If an existing transaction with the id tx exists: use it + try: + pdt_obj = PayPalPDT.objects.get(txn_id=txn_id) + except PayPalPDT.DoesNotExist: + # This is a new transaction so we continue processing PDT request + pass + + if pdt_obj is None: + form = PayPalPDTForm(request.GET) + if form.is_valid(): + try: + pdt_obj = form.save(commit=False) + except Exception, e: + error = repr(e) + failed = True + else: + error = form.errors + failed = True + + if failed: + pdt_obj = PayPalPDT() + pdt_obj.set_flag("Invalid form. %s" % error) + + pdt_obj.initialize(request) + + if not failed: + # The PDT object gets saved during verify + pdt_obj.verify(item_check_callable) + else: + pass # we ignore any PDT requests that don't have a transaction id + + kwargs.update({'pdt_active': pdt_active, 'pdt_failed': failed, 'pdt_obj': pdt_obj}) + return f(request, *args, **kwargs) + + return aux diff --git a/paypal/standard/pdt/views.py b/paypal/standard/pdt/views.py index 0993411..e1e612b 100644 --- a/paypal/standard/pdt/views.py +++ b/paypal/standard/pdt/views.py @@ -3,48 +3,12 @@ from django.template import RequestContext from django.shortcuts import render_to_response from django.views.decorators.http import require_GET -from paypal.standard.pdt.models import PayPalPDT -from paypal.standard.pdt.forms import PayPalPDTForm - +from paypal.standard.pdt.decorators import pdt @require_GET -def pdt(request, item_check_callable=None, template="pdt/pdt.html", context=None): +@pdt +def pdt(request, pdt_active=True, pdt_failed=False, pdt_obj=None, item_check_callable=None, template="pdt/pdt.html", context=None): """Payment data transfer implementation: http://tinyurl.com/c9jjmw""" - context = context or {} - pdt_obj = None - txn_id = request.GET.get('tx') - failed = False - if txn_id is not None: - # If an existing transaction with the id tx exists: use it - try: - pdt_obj = PayPalPDT.objects.get(txn_id=txn_id) - except PayPalPDT.DoesNotExist: - # This is a new transaction so we continue processing PDT request - pass - - if pdt_obj is None: - form = PayPalPDTForm(request.GET) - if form.is_valid(): - try: - pdt_obj = form.save(commit=False) - except Exception, e: - error = repr(e) - failed = True - else: - error = form.errors - failed = True - - if failed: - pdt_obj = PayPalPDT() - pdt_obj.set_flag("Invalid form. %s" % error) - - pdt_obj.initialize(request) - - if not failed: - # The PDT object gets saved during verify - pdt_obj.verify(item_check_callable) - else: - pass # we ignore any PDT requests that don't have a transaction id - - context.update({"failed":failed, "pdt_obj":pdt_obj}) - return render_to_response(template, context, RequestContext(request)) \ No newline at end of file + context = context or {} + context.update({"failed":pdt_failed, "pdt_obj":pdt_obj}) + return render_to_response(template, context, RequestContext(request)) From bff45d9210f8d3f587b972eeca2165c53fcb3d27 Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Sat, 10 Mar 2012 18:20:01 +0530 Subject: [PATCH 06/17] Shezi changes doc for pdt() --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 3519d8b..88d33f2 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,16 @@ Paypal Payment Data Transfer (PDT) allows you to display transaction details to (r'^paypal/pdt/', include('paypal.standard.pdt.urls')), ... ) + Alternatively, you can use the pdt decorator to work with PDT information in one of your own views. + To do this, add the decorator to one of your views. + # views.py + from paypal.standard.pdt.decorators import pdt + + @pdt + def view_func(request, *args, **kwargs): + ... + The decorator checks for any GET parameters corresponding to a PDT call and adds the keyword arguments `pdt_active`, `pdt_failed` and `pdt` to the view call. + Using PayPal Payments Standard with Subscriptions: -------------------------------------------------- From 709378d9ee811ee45cd977b1db8cf7c0efe42d25 Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Sat, 10 Mar 2012 18:24:30 +0530 Subject: [PATCH 07/17] Doc formatting issue --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 88d33f2..64baca8 100644 --- a/README.md +++ b/README.md @@ -146,8 +146,9 @@ Paypal Payment Data Transfer (PDT) allows you to display transaction details to (r'^paypal/pdt/', include('paypal.standard.pdt.urls')), ... ) - Alternatively, you can use the pdt decorator to work with PDT information in one of your own views. + **Alternatively**, you can use the pdt decorator to work with PDT information in one of your own views. To do this, add the decorator to one of your views. + # views.py from paypal.standard.pdt.decorators import pdt From cd304ed0e53997e959f8737d6f39eb28542d2a62 Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Sat, 10 Mar 2012 18:33:14 +0530 Subject: [PATCH 08/17] Committing mij's changes on the comment page of the pull request --- paypal/standard/pdt/decorators.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/paypal/standard/pdt/decorators.py b/paypal/standard/pdt/decorators.py index 3539fdf..2b793ec 100644 --- a/paypal/standard/pdt/decorators.py +++ b/paypal/standard/pdt/decorators.py @@ -9,19 +9,19 @@ def pdt(f): Payment data transfer implementation: http://tinyurl.com/c9jjmw""" + @require_GET def aux(request, *args, **kwargs): - if request.method == 'POST': - return f(request, *args, **kwargs) - pdt_obj = None pdt_active = False txn_id = request.GET.get('tx') failed = False + pdt_duplicate = False if txn_id is not None: pdt_active = True # If an existing transaction with the id tx exists: use it try: pdt_obj = PayPalPDT.objects.get(txn_id=txn_id) + pdt_duplicate = True except PayPalPDT.DoesNotExist: # This is a new transaction so we continue processing PDT request pass @@ -46,10 +46,10 @@ def aux(request, *args, **kwargs): if not failed: # The PDT object gets saved during verify - pdt_obj.verify(item_check_callable) + pdt_obj.verify() else: pass # we ignore any PDT requests that don't have a transaction id - + kwargs.update({'pdt_active': pdt_active, 'pdt_failed': failed, 'pdt_obj': pdt_obj}) return f(request, *args, **kwargs) From 014c6bdb0e34e7b2a6cd33cb27529257b80dc5dc Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Sat, 10 Mar 2012 19:33:35 +0530 Subject: [PATCH 09/17] Image path fix and option to not save failed transactions. --- README.md | 2 ++ paypal/standard/conf.py | 5 ++--- paypal/standard/models.py | 9 ++++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 64baca8..d7024db 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,8 @@ Paypal Payment Data Transfer (PDT) allows you to display transaction details to ... The decorator checks for any GET parameters corresponding to a PDT call and adds the keyword arguments `pdt_active`, `pdt_failed` and `pdt` to the view call. +1. Set `PAYPAL_IGNORE_INVALID_PDT` to `True` to stop saving data about failed transactions. This might save you from an attack of bad inserts. + Using PayPal Payments Standard with Subscriptions: -------------------------------------------------- diff --git a/paypal/standard/conf.py b/paypal/standard/conf.py index 5c5fd45..2ac0869 100644 --- a/paypal/standard/conf.py +++ b/paypal/standard/conf.py @@ -5,8 +5,7 @@ class PayPalSettingsError(Exception): TEST = getattr(settings, "PAYPAL_TEST", True) - - +IGNORE_INVALID_PDT = getattr(settings, "PAYPAL_IGNORE_INVALID_PDT", True) RECEIVER_EMAIL = settings.PAYPAL_RECEIVER_EMAIL @@ -15,7 +14,7 @@ class PayPalSettingsError(Exception): SANDBOX_POSTBACK_ENDPOINT = "https://www.sandbox.paypal.com/cgi-bin/webscr" # Images -IMAGE = getattr(settings, "PAYPAL_IMAGE", "http://images.paypal.com/images/x-click-but01.gif") +IMAGE = getattr(settings, "PAYPAL_IMAGE", "https://www.paypal.com/en_US/i/btn/btn_paynowCC_LG.gif") SUBSCRIPTION_IMAGE = getattr(settings, "PAYPAL_SUBSCRIPTION_IMAGE", "https://www.paypal.com/en_US/i/btn/btn_subscribeCC_LG.gif") DONATION_IMAGE = getattr(settings, "PAYPAL_DONATION_IMAGE", "https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif") SANDBOX_IMAGE = getattr(settings, "PAYPAL_SANDBOX_IMAGE", "https://www.sandbox.paypal.com/en_US/i/btn/btn_buynowCC_LG.gif") diff --git a/paypal/standard/models.py b/paypal/standard/models.py index c07e2fd..3dd5d0b 100644 --- a/paypal/standard/models.py +++ b/paypal/standard/models.py @@ -245,8 +245,9 @@ def verify(self, item_check_callable=None): """ self.response = self._postback() - self._verify_postback() - if not self.flag: + self._verify_postback() + invalid_paypal_obj = self.flag + if not invalid_paypal_obj: if self.is_transaction(): if self.payment_status not in self.PAYMENT_STATUS_CHOICES: self.set_flag("Invalid payment_status. (%s)" % self.payment_status) @@ -262,7 +263,9 @@ def verify(self, item_check_callable=None): # @@@ Run a different series of checks on recurring payments. pass - self.save() + if not invalid_paypal_obj or not settings.IGNORE_INVALID_PDT: + self.save() + self.send_signals() def verify_secret(self, form_instance, secret): From 1fb6e9cec68c131c38627d62cfcf511b16fbb503 Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Sat, 10 Mar 2012 22:32:52 +0530 Subject: [PATCH 10/17] Added missing require_GET --- paypal/standard/pdt/decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paypal/standard/pdt/decorators.py b/paypal/standard/pdt/decorators.py index 2b793ec..e87d498 100644 --- a/paypal/standard/pdt/decorators.py +++ b/paypal/standard/pdt/decorators.py @@ -2,7 +2,7 @@ from paypal.standard.pdt.models import PayPalPDT from paypal.standard.pdt.forms import PayPalPDTForm - +from django.views.decorators.http import require_GET def pdt(f): """Parses out GET parameters corresponding to a paypal PDT request and adds `pdt_active`, `pdt_failed` and `pdt` to the call **kwargs. From ae1c67c16aeefdef0862709ac67299ee1ae0c345 Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Mon, 12 Mar 2012 11:41:22 +0530 Subject: [PATCH 11/17] Added support for item_check_callable --- paypal/standard/pdt/decorators.py | 100 +++++++++++++++++------------- paypal/standard/pdt/views.py | 2 +- 2 files changed, 59 insertions(+), 43 deletions(-) diff --git a/paypal/standard/pdt/decorators.py b/paypal/standard/pdt/decorators.py index e87d498..7f38c5c 100644 --- a/paypal/standard/pdt/decorators.py +++ b/paypal/standard/pdt/decorators.py @@ -4,53 +4,69 @@ from paypal.standard.pdt.forms import PayPalPDTForm from django.views.decorators.http import require_GET -def pdt(f): +def pdt(dummy=None, item_check_callable=None): """Parses out GET parameters corresponding to a paypal PDT request and adds `pdt_active`, `pdt_failed` and `pdt` to the call **kwargs. - Payment data transfer implementation: http://tinyurl.com/c9jjmw""" - - @require_GET - def aux(request, *args, **kwargs): - pdt_obj = None - pdt_active = False - txn_id = request.GET.get('tx') - failed = False - pdt_duplicate = False - if txn_id is not None: - pdt_active = True - # If an existing transaction with the id tx exists: use it - try: - pdt_obj = PayPalPDT.objects.get(txn_id=txn_id) - pdt_duplicate = True - except PayPalPDT.DoesNotExist: - # This is a new transaction so we continue processing PDT request - pass - - if pdt_obj is None: - form = PayPalPDTForm(request.GET) - if form.is_valid(): - try: - pdt_obj = form.save(commit=False) - except Exception, e: - error = repr(e) + Payment data transfer implementation: http://tinyurl.com/c9jjmw + + `item_check_callable` (Optional) is a callable that must take an instance of PayPalPDT + as a parameter and return a tuple (False, None) if the item is valid. Should return (True, "reason") + if the item isn't valid. This function should check that `mc_gross`, `mc_currency` `item_name` and + `item_number` are all correct. + + `dummy` DO NOT set value for this. So when you want to set value for `item_check_callable` use named param. + So it would be @pdt(item_check_callable=func). When the `dummy` is a callable `f` then it behaves as just @pdt(f). + """ + + def inner_pdt(f): + #{ + @require_GET + def aux(request, *args, **kwargs): + pdt_obj = None + pdt_active = False + txn_id = request.GET.get('tx') + failed = False + pdt_duplicate = False + if txn_id is not None: + pdt_active = True + # If an existing transaction with the id tx exists: use it + try: + pdt_obj = PayPalPDT.objects.get(txn_id=txn_id) + pdt_duplicate = True + except PayPalPDT.DoesNotExist: + # This is a new transaction so we continue processing PDT request + pass + + if pdt_obj is None: + form = PayPalPDTForm(request.GET) + if form.is_valid(): + try: + pdt_obj = form.save(commit=False) + except Exception, e: + error = repr(e) + failed = True + else: + error = form.errors failed = True - else: - error = form.errors - failed = True - if failed: - pdt_obj = PayPalPDT() - pdt_obj.set_flag("Invalid form. %s" % error) + if failed: + pdt_obj = PayPalPDT() + pdt_obj.set_flag("Invalid form. %s" % error) - pdt_obj.initialize(request) + pdt_obj.initialize(request) - if not failed: - # The PDT object gets saved during verify - pdt_obj.verify() - else: - pass # we ignore any PDT requests that don't have a transaction id + if not failed: + # The PDT object gets saved during verify + pdt_obj.verify(item_check_callable) + else: + pass # we ignore any PDT requests that don't have a transaction id - kwargs.update({'pdt_active': pdt_active, 'pdt_failed': failed, 'pdt_obj': pdt_obj}) - return f(request, *args, **kwargs) + kwargs.update({'pdt_active': pdt_active, 'pdt_failed': failed, 'pdt_obj': pdt_obj}) + return f(request, *args, **kwargs) - return aux + return aux + #} + if hasattr(dummy, '__call__'): #This is to make sure that we can call @pdt without any parenthesis. + return inner_pdt(dummy) #dummy is function now + else: + return inner_pdt diff --git a/paypal/standard/pdt/views.py b/paypal/standard/pdt/views.py index e1e612b..401e92f 100644 --- a/paypal/standard/pdt/views.py +++ b/paypal/standard/pdt/views.py @@ -7,7 +7,7 @@ @require_GET @pdt -def pdt(request, pdt_active=True, pdt_failed=False, pdt_obj=None, item_check_callable=None, template="pdt/pdt.html", context=None): +def pdt(request, pdt_active=True, pdt_failed=False, pdt_obj=None, template="pdt/pdt.html", context=None): """Payment data transfer implementation: http://tinyurl.com/c9jjmw""" context = context or {} context.update({"failed":pdt_failed, "pdt_obj":pdt_obj}) From d3f2178d5ac72b2a612d4c786166b3383870bd44 Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Mon, 12 Mar 2012 11:41:50 +0530 Subject: [PATCH 12/17] Some tab to space fix --- paypal/standard/pdt/decorators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paypal/standard/pdt/decorators.py b/paypal/standard/pdt/decorators.py index 7f38c5c..b4f9e9b 100644 --- a/paypal/standard/pdt/decorators.py +++ b/paypal/standard/pdt/decorators.py @@ -14,8 +14,8 @@ def pdt(dummy=None, item_check_callable=None): if the item isn't valid. This function should check that `mc_gross`, `mc_currency` `item_name` and `item_number` are all correct. - `dummy` DO NOT set value for this. So when you want to set value for `item_check_callable` use named param. - So it would be @pdt(item_check_callable=func). When the `dummy` is a callable `f` then it behaves as just @pdt(f). + `dummy` DO NOT set value for this. So when you want to set value for `item_check_callable` use named param. + So it would be @pdt(item_check_callable=func). When the `dummy` is a callable `f` then it behaves as just @pdt(f). """ def inner_pdt(f): From 659ecd0e5342c84f727096c2c6862940b0a49da6 Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Tue, 13 Mar 2012 00:22:29 +0530 Subject: [PATCH 13/17] Added pdt_duplicate to decorator's output. This signals already existing transaction. --- paypal/standard/pdt/decorators.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/paypal/standard/pdt/decorators.py b/paypal/standard/pdt/decorators.py index b4f9e9b..5a66d1d 100644 --- a/paypal/standard/pdt/decorators.py +++ b/paypal/standard/pdt/decorators.py @@ -61,7 +61,10 @@ def aux(request, *args, **kwargs): else: pass # we ignore any PDT requests that don't have a transaction id - kwargs.update({'pdt_active': pdt_active, 'pdt_failed': failed, 'pdt_obj': pdt_obj}) + #pdt_active = True => txn_id was not None + #pdt_failed = True => pdt_obj has invalid data + #pdt_duplicate = True => txn_id is known and already processed. pdt_obj contains that data. + kwargs.update({'pdt_active': pdt_active, 'pdt_failed': failed, 'pdt_obj': pdt_obj, 'pdt_duplicate': pdt_duplicate}) return f(request, *args, **kwargs) return aux From a2bcc4d3c9dd47ab47fe870af0b2079fdc35bcbe Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Tue, 13 Mar 2012 11:40:54 +0530 Subject: [PATCH 14/17] Better invalid txn_id handling and skip saving of invalid pet when opted in --- paypal/standard/conf.py | 2 +- paypal/standard/models.py | 2 +- paypal/standard/pdt/decorators.py | 7 ++++++- paypal/standard/pdt/models.py | 11 ++++++----- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/paypal/standard/conf.py b/paypal/standard/conf.py index 2ac0869..66d8bc8 100644 --- a/paypal/standard/conf.py +++ b/paypal/standard/conf.py @@ -5,7 +5,7 @@ class PayPalSettingsError(Exception): TEST = getattr(settings, "PAYPAL_TEST", True) -IGNORE_INVALID_PDT = getattr(settings, "PAYPAL_IGNORE_INVALID_PDT", True) +IGNORE_INVALID_PDT = getattr(settings, "PAYPAL_IGNORE_INVALID_PDT", False) RECEIVER_EMAIL = settings.PAYPAL_RECEIVER_EMAIL diff --git a/paypal/standard/models.py b/paypal/standard/models.py index 3dd5d0b..6f4a01b 100644 --- a/paypal/standard/models.py +++ b/paypal/standard/models.py @@ -263,7 +263,7 @@ def verify(self, item_check_callable=None): # @@@ Run a different series of checks on recurring payments. pass - if not invalid_paypal_obj or not settings.IGNORE_INVALID_PDT: + if not (invalid_paypal_obj and settings.IGNORE_INVALID_PDT): self.save() self.send_signals() diff --git a/paypal/standard/pdt/decorators.py b/paypal/standard/pdt/decorators.py index 5a66d1d..4e73d58 100644 --- a/paypal/standard/pdt/decorators.py +++ b/paypal/standard/pdt/decorators.py @@ -24,7 +24,12 @@ def inner_pdt(f): def aux(request, *args, **kwargs): pdt_obj = None pdt_active = False - txn_id = request.GET.get('tx') + txn_id = request.GET.get('tx', None) + if txn_id is not None: + txn_id = txn_id.strip() + if not txn_id: #i.e. empty txn_id + txn_id = None + failed = False pdt_duplicate = False if txn_id is not None: diff --git a/paypal/standard/pdt/models.py b/paypal/standard/pdt/models.py index 72e7d7d..b780179 100644 --- a/paypal/standard/pdt/models.py +++ b/paypal/standard/pdt/models.py @@ -76,11 +76,12 @@ def _verify_postback(self): except ValueError, e: pass - qd = QueryDict('', mutable=True) - qd.update(response_dict) - qd.update(dict(ipaddress=self.ipaddress, st=self.st, flag_info=self.flag_info)) - pdt_form = PayPalPDTForm(qd, instance=self) - pdt_form.save(commit=False) + if not self.flag: + qd = QueryDict('', mutable=True) + qd.update(response_dict) + qd.update(dict(ipaddress=self.ipaddress, st=self.st, flag_info=self.flag_info)) + pdt_form = PayPalPDTForm(qd, instance=self) + pdt_form.save(commit=False) def send_signals(self): # Send the PDT signals... From b7f0e853bbb13c24065638a8af350e23f30afa27 Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Tue, 13 Mar 2012 12:20:02 +0530 Subject: [PATCH 15/17] Some reference fixes --- paypal/standard/models.py | 4 ++-- paypal/standard/pdt/models.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/paypal/standard/models.py b/paypal/standard/models.py index 6f4a01b..c2f9dc2 100644 --- a/paypal/standard/models.py +++ b/paypal/standard/models.py @@ -3,7 +3,7 @@ from django.db import models from django.conf import settings from paypal.standard.helpers import duplicate_txn_id, check_secret -from paypal.standard.conf import RECEIVER_EMAIL, POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT +from paypal.standard.conf import RECEIVER_EMAIL, POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT, IGNORE_INVALID_PDT ST_PP_ACTIVE = 'Active' ST_PP_CANCELLED = 'Cancelled' @@ -263,7 +263,7 @@ def verify(self, item_check_callable=None): # @@@ Run a different series of checks on recurring payments. pass - if not (invalid_paypal_obj and settings.IGNORE_INVALID_PDT): + if not (invalid_paypal_obj and IGNORE_INVALID_PDT): self.save() self.send_signals() diff --git a/paypal/standard/pdt/models.py b/paypal/standard/pdt/models.py index b780179..0caac8b 100644 --- a/paypal/standard/pdt/models.py +++ b/paypal/standard/pdt/models.py @@ -7,7 +7,7 @@ from django.http import QueryDict from django.utils.http import urlencode from paypal.standard.models import PayPalStandardBase -from paypal.standard.conf import POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT +from paypal.standard.conf import POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT, IGNORE_INVALID_PDT from paypal.standard.pdt.signals import pdt_successful, pdt_failed # ### Todo: Move this logic to conf.py: @@ -76,7 +76,7 @@ def _verify_postback(self): except ValueError, e: pass - if not self.flag: + if not (self.flag and IGNORE_INVALID_PDT): qd = QueryDict('', mutable=True) qd.update(response_dict) qd.update(dict(ipaddress=self.ipaddress, st=self.st, flag_info=self.flag_info)) From 2ae23711a43d7c3fa4560741579720c50d27dce5 Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Fri, 16 Mar 2012 00:59:21 +0530 Subject: [PATCH 16/17] Readme update. Since pending dcramer changes are now in master. --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d7024db..3a87aa4 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,11 @@ Django PayPal ============= +Note +---- + +This is a fork of `dcramer/django-paypal`. This contains few of the fixes which are currently (as of 16Mar12) in the Pull Request of dcramer. I felt they were quite good changes, so did not wait and created my own version, topped with some of my own fixes. + About ----- @@ -22,7 +27,7 @@ Using PayPal Payments Standard IPN: 1. Download the code from GitHub: - git clone git://github.com/dcramer/django-paypal.git paypal + git clone git://github.com/applegrew/django-paypal.git paypal 1. Edit `settings.py` and add `paypal.standard.ipn` to your `INSTALLED_APPS` and `PAYPAL_RECEIVER_EMAIL`: From 87d08c04fc66bebdc5918d363b04bb6134230bbd Mon Sep 17 00:00:00 2001 From: "AppleGrew (applegrew)" Date: Fri, 16 Mar 2012 01:00:49 +0530 Subject: [PATCH 17/17] grammar fix. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a87aa4..a349c92 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Django PayPal Note ---- -This is a fork of `dcramer/django-paypal`. This contains few of the fixes which are currently (as of 16Mar12) in the Pull Request of dcramer. I felt they were quite good changes, so did not wait and created my own version, topped with some of my own fixes. +This is a fork of `dcramer/django-paypal`. This contains few of the fixes which are currently (as of 16Mar12) in the Pull Request queue of dcramer. I felt they were quite good changes, so I did not wait and created my own version; topped with some of my own fixes. About -----