Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CC validation fix, deprecated function fix, pep8 fix #24

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion paypal/pro/creditcard.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@
"5105105105105100", "4111111111111111", "4012888888881881", "4222222222222"
]


def verify_credit_card(number):
"""Returns the card type for given card number or None if invalid."""
return CreditCard(number).verify()


class CreditCard(object):
def __init__(self, number):
self.number = number

def is_number(self):
"""True if there is at least one digit in number."""
self.number = re.sub(r'[^\d]', '', self.number)
Expand Down
3 changes: 2 additions & 1 deletion paypal/pro/exceptions.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
class PayPalFailure(Exception): pass
class PayPalFailure(Exception):
pass
13 changes: 7 additions & 6 deletions paypal/pro/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from calendar import monthrange
from datetime import date

from django.db import models
from django import forms
from django.utils.translation import ugettext as _

Expand All @@ -15,7 +14,7 @@ class CreditCardField(forms.CharField):
def __init__(self, *args, **kwargs):
kwargs.setdefault('max_length', 20)
super(CreditCardField, self).__init__(*args, **kwargs)

def clean(self, value):
"""Raises a ValidationError if the card is not valid and stashes card type."""
if value:
Expand All @@ -42,6 +41,7 @@ def format_output(self, rendered_widgets):
html = u' / '.join(rendered_widgets)
return u'<span style="white-space: nowrap">%s</span>' % html


class CreditCardExpiryField(forms.MultiValueField):
EXP_MONTH = [(x, x) for x in xrange(1, 13)]
EXP_YEAR = [(x, x) for x in xrange(date.today().year, date.today().year + 15)]
Expand All @@ -55,12 +55,12 @@ def __init__(self, *args, **kwargs):
errors = self.default_error_messages.copy()
if 'error_messages' in kwargs:
errors.update(kwargs['error_messages'])

fields = (
forms.ChoiceField(choices=self.EXP_MONTH, error_messages={'invalid': errors['invalid_month']}),
forms.ChoiceField(choices=self.EXP_YEAR, error_messages={'invalid': errors['invalid_year']}),
)

super(CreditCardExpiryField, self).__init__(fields, *args, **kwargs)
self.widget = CreditCardExpiryWidget(widgets=[fields[0].widget, fields[1].widget])

Expand Down Expand Up @@ -90,7 +90,7 @@ class CreditCardCVV2Field(forms.CharField):
def __init__(self, *args, **kwargs):
kwargs.setdefault('max_length', 4)
super(CreditCardCVV2Field, self).__init__(*args, **kwargs)


# Country Field from:
# http://www.djangosnippets.org/snippets/494/
Expand Down Expand Up @@ -337,7 +337,8 @@ def __init__(self, *args, **kwargs):
('ZW', _('Zimbabwe')),
)


class CountryField(forms.ChoiceField):
def __init__(self, *args, **kwargs):
kwargs.setdefault('choices', COUNTRIES)
super(CountryField, self).__init__(*args, **kwargs)
super(CountryField, self).__init__(*args, **kwargs)
8 changes: 4 additions & 4 deletions paypal/pro/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def __init__(self, item=None, payment_form_cls=PaymentForm,
self.context = context or {}
self.form_context_name = form_context_name

def __call__(self, request):
def __call__(self, request, initial=None):
"""Return the appropriate response for the state of the transaction."""
self.request = request
if request.method == "GET":
Expand All @@ -99,7 +99,7 @@ def __call__(self, request):
elif self.should_render_confirm_form():
return self.render_confirm_form()
elif self.should_render_payment_form():
return self.render_payment_form()
return self.render_payment_form(initial=initial)
else:
if self.should_validate_confirm_form():
return self.validate_confirm_form()
Expand Down Expand Up @@ -127,9 +127,9 @@ def should_validate_confirm_form(self):
def should_validate_payment_form(self):
return True

def render_payment_form(self):
def render_payment_form(self, *args, **kwargs):
"""Display the DirectPayment for entering payment information."""
self.context[self.form_context_name] = self.payment_form_cls()
self.context[self.form_context_name] = self.payment_form_cls(*args, **kwargs)
return render_to_response(self.payment_template, self.context, RequestContext(self.request))

def validate_payment_form(self):
Expand Down
4 changes: 2 additions & 2 deletions paypal/standard/conf.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from django.conf import settings


class PayPalSettingsError(Exception):
"""Raised when settings be bad."""


TEST = getattr(settings, "PAYPAL_TEST", True)

Expand All @@ -21,4 +22,3 @@ class PayPalSettingsError(Exception):
SANDBOX_IMAGE = getattr(settings, "PAYPAL_SANDBOX_IMAGE", "https://www.sandbox.paypal.com/en_US/i/btn/btn_buynowCC_LG.gif")
SUBSCRIPTION_SANDBOX_IMAGE = getattr(settings, "PAYPAL_SUBSCRIPTION_SANDBOX_IMAGE", "https://www.sandbox.paypal.com/en_US/i/btn/btn_subscribeCC_LG.gif")
DONATION_SANDBOX_IMAGE = getattr(settings, "PAYPAL_DONATION_SANDBOX_IMAGE", "https://www.sandbox.paypal.com/en_US/i/btn/btn_donateCC_LG.gif")

73 changes: 36 additions & 37 deletions paypal/standard/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
from django.utils.safestring import mark_safe
from paypal.standard.conf import *
from paypal.standard.widgets import ValueHiddenInput, ReservedValueHiddenInput
from paypal.standard.conf import (POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT,
RECEIVER_EMAIL)
from paypal.standard.conf import (POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT, RECEIVER_EMAIL)


# 20:18:05 Jan 30, 2009 PST - PST timezone support is not included out of the box.
Expand All @@ -17,33 +16,34 @@
"%H:%M:%S %b %d, %Y PST",
"%H:%M:%S %b %d, %Y PDT",)


class PayPalPaymentsForm(forms.Form):
"""
Creates a PayPal Payments Standard "Buy It Now" button, configured for a
selling a single item with no shipping.

For a full overview of all the fields you can set (there is a lot!) see:
http://tinyurl.com/pps-integration

Usage:
>>> f = PayPalPaymentsForm(initial={'item_name':'Widget 001', ...})
>>> f.render()
u'<form action="https://www.paypal.com/cgi-bin/webscr" method="post"> ...'
"""

"""
CMD_CHOICES = (
("_xclick", "Buy now or Donations"),
("_cart", "Shopping cart"),
("_xclick", "Buy now or Donations"),
("_cart", "Shopping cart"),
("_xclick-subscriptions", "Subscribe")
)
SHIPPING_CHOICES = ((1, "No shipping"), (0, "Shipping"))
NO_NOTE_CHOICES = ((1, "No Note"), (0, "Include Note"))
RECURRING_PAYMENT_CHOICES = (
(1, "Subscription Payments Recur"),
(1, "Subscription Payments Recur"),
(0, "Subscription payments do not recur")
)
REATTEMPT_ON_FAIL_CHOICES = (
(1, "reattempt billing on Failure"),
(1, "reattempt billing on Failure"),
(0, "Do Not reattempt on failure")
)

Expand All @@ -53,46 +53,46 @@ class PayPalPaymentsForm(forms.Form):

# Where the money goes.
business = forms.CharField(widget=ValueHiddenInput(), initial=RECEIVER_EMAIL)

# Item information.
amount = forms.IntegerField(widget=ValueHiddenInput())
item_name = forms.CharField(widget=ValueHiddenInput())
item_number = forms.CharField(widget=ValueHiddenInput())
quantity = forms.CharField(widget=ValueHiddenInput())

# Subscription Related.
a1 = forms.CharField(widget=ValueHiddenInput()) # Trial 1 Price
p1 = forms.CharField(widget=ValueHiddenInput()) # Trial 1 Duration
t1 = forms.CharField(widget=ValueHiddenInput()) # Trial 1 unit of Duration, default to Month
a2 = forms.CharField(widget=ValueHiddenInput()) # Trial 2 Price
p2 = forms.CharField(widget=ValueHiddenInput()) # Trial 2 Duration
t2 = forms.CharField(widget=ValueHiddenInput()) # Trial 2 unit of Duration, default to Month
a3 = forms.CharField(widget=ValueHiddenInput()) # Subscription Price
p3 = forms.CharField(widget=ValueHiddenInput()) # Subscription Duration
t3 = forms.CharField(widget=ValueHiddenInput()) # Subscription unit of Duration, default to Month
src = forms.CharField(widget=ValueHiddenInput()) # Is billing recurring? default to yes
sra = forms.CharField(widget=ValueHiddenInput()) # Reattempt billing on failed cc transaction
no_note = forms.CharField(widget=ValueHiddenInput())
a1 = forms.CharField(widget=ValueHiddenInput()) # Trial 1 Price
p1 = forms.CharField(widget=ValueHiddenInput()) # Trial 1 Duration
t1 = forms.CharField(widget=ValueHiddenInput()) # Trial 1 unit of Duration, default to Month
a2 = forms.CharField(widget=ValueHiddenInput()) # Trial 2 Price
p2 = forms.CharField(widget=ValueHiddenInput()) # Trial 2 Duration
t2 = forms.CharField(widget=ValueHiddenInput()) # Trial 2 unit of Duration, default to Month
a3 = forms.CharField(widget=ValueHiddenInput()) # Subscription Price
p3 = forms.CharField(widget=ValueHiddenInput()) # Subscription Duration
t3 = forms.CharField(widget=ValueHiddenInput()) # Subscription unit of Duration, default to Month
src = forms.CharField(widget=ValueHiddenInput()) # Is billing recurring? default to yes
sra = forms.CharField(widget=ValueHiddenInput()) # Reattempt billing on failed cc transaction
no_note = forms.CharField(widget=ValueHiddenInput())
# Can be either 1 or 2. 1 = modify or allow new subscription creation, 2 = modify only
modify = forms.IntegerField(widget=ValueHiddenInput()) # Are we modifying an existing subscription?
modify = forms.IntegerField(widget=ValueHiddenInput()) # Are we modifying an existing subscription?

# Localization / PayPal Setup
lc = forms.CharField(widget=ValueHiddenInput())
page_style = forms.CharField(widget=ValueHiddenInput())
cbt = forms.CharField(widget=ValueHiddenInput())

# IPN control.
notify_url = forms.CharField(widget=ValueHiddenInput())
cancel_return = forms.CharField(widget=ValueHiddenInput())
return_url = forms.CharField(widget=ReservedValueHiddenInput(attrs={"name":"return"}))
return_url = forms.CharField(widget=ReservedValueHiddenInput(attrs={"name": "return"}))
custom = forms.CharField(widget=ValueHiddenInput())
invoice = forms.CharField(widget=ValueHiddenInput())

# Default fields.
cmd = forms.ChoiceField(widget=forms.HiddenInput(), initial=CMD_CHOICES[0][0])
charset = forms.CharField(widget=forms.HiddenInput(), initial="utf-8")
currency_code = forms.CharField(widget=forms.HiddenInput(), initial="USD")
no_shipping = forms.ChoiceField(widget=forms.HiddenInput(), choices=SHIPPING_CHOICES,
no_shipping = forms.ChoiceField(widget=forms.HiddenInput(), choices=SHIPPING_CHOICES,
initial=SHIPPING_CHOICES[0][0])

def __init__(self, button_type="buy", *args, **kwargs):
Expand All @@ -104,14 +104,13 @@ def render(self):
%s
<input type="image" src="%s" border="0" name="submit" alt="Buy it Now" />
</form>""" % (POSTBACK_ENDPOINT, self.as_p(), self.get_image()))



def sandbox(self):
return mark_safe(u"""<form action="%s" method="post">
%s
<input type="image" src="%s" border="0" name="submit" alt="Buy it Now" />
</form>""" % (SANDBOX_POSTBACK_ENDPOINT, self.as_p(), self.get_image()))

def get_image(self):
return {
(True, self.SUBSCRIBE): SUBSCRIPTION_SANDBOX_IMAGE,
Expand Down Expand Up @@ -139,7 +138,7 @@ class PayPalEncryptedPaymentsForm(PayPalPaymentsForm):

Based on example at:
http://blog.mauveweb.co.uk/2007/10/10/paypal-with-django/

"""
def _encrypt(self):
"""Use your key thing to encrypt things."""
Expand All @@ -164,7 +163,7 @@ def _encrypt(self):
name = "return"
plaintext += u'%s=%s\n' % (name, value)
plaintext = plaintext.encode('utf-8')

# Begin crypto weirdness.
s = SMIME.SMIME()
s.load_key_bio(BIO.openfile(CERT), BIO.openfile(PUB_CERT))
Expand All @@ -180,7 +179,7 @@ def _encrypt(self):
out = BIO.MemoryBuffer()
p7.write(out)
return out.read()

def as_p(self):
return mark_safe(u"""
<input type="hidden" name="cmd" value="_s-xclick" />
Expand All @@ -192,7 +191,7 @@ class PayPalSharedSecretEncryptedPaymentsForm(PayPalEncryptedPaymentsForm):
"""
Creates a PayPal Encrypted Payments "Buy It Now" button with a Shared Secret.
Shared secrets should only be used when your IPN endpoint is on HTTPS.

Adds a secret to the notify_url based on the contents of the form.

"""
Expand Down
21 changes: 11 additions & 10 deletions paypal/standard/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,34 @@
# -*- coding: utf-8 -*-
from django.conf import settings


def duplicate_txn_id(ipn_obj):
"""Returns True if a record with this transaction id exists and it is not
"""
Returns True if a record with this transaction id exists and it is not
a payment which has gone from pending to completed.

"""
query = ipn_obj._default_manager.filter(txn_id = ipn_obj.txn_id)
query = ipn_obj._default_manager.filter(txn_id=ipn_obj.txn_id)

if ipn_obj.payment_status == "Completed":
# A payment that was pending and is now completed will have the same
# IPN transaction id, so don't flag them as duplicates because it
# means that the payment was finally successful!
query = query.exclude(payment_status = "Pending")
query = query.exclude(payment_status="Pending")

return query.count() > 0



def make_secret(form_instance, secret_fields=None):
"""
Returns a secret for use in a EWP form or an IPN verification based on a
selection of variables in params. Should only be used with SSL.

"""
# @@@ Moved here as temporary fix to avoid dependancy on auth.models.
from django.contrib.auth.models import get_hexdigest
# @@@ amount is mc_gross on the IPN - where should mapping logic go?
# @@@ amount / mc_gross is not nessecarily returned as it was sent - how to use it? 10.00 vs. 10.0
# @@@ the secret should be based on the invoice or custom fields as well - otherwise its always the same.

# Build the secret with fields availible in both PaymentForm and the IPN. Order matters.
if secret_fields is None:
secret_fields = ['business', 'item_name']
Expand All @@ -48,11 +49,11 @@ def make_secret(form_instance, secret_fields=None):
secret = get_hexdigest('sha1', settings.SECRET_KEY, data)
return secret


def check_secret(form_instance, secret):
"""
Returns true if received `secret` matches expected secret for form_instance.
Used to verify IPN.

"""
# @@@ add invoice & custom
# secret_fields = ['business', 'item_name']
Expand Down
Loading