├── __init__.py
├── pro
├── __init__.py
├── admin.py
├── templates
│ └── pro
│ │ ├── confirm.html
│ │ └── payment.html
├── signals.py
├── forms.py
├── creditcard.py
├── models.py
├── tests.py
├── helpers.py
├── views.py
└── fields.py
├── standard
├── __init__.py
├── ipn
│ ├── __init__.py
│ ├── migrations
│ │ ├── __init__.py
│ │ └── 0001_first_migration.py
│ ├── tests
│ │ ├── __init__.py
│ │ ├── test_urls.py
│ │ └── test_ipn.py
│ ├── urls.py
│ ├── templates
│ │ └── ipn
│ │ │ ├── paypal.html
│ │ │ ├── ipn.html
│ │ │ └── ipn_test.html
│ ├── forms.py
│ ├── signals.py
│ ├── views.py
│ ├── models.py
│ └── admin.py
├── pdt
│ ├── __init__.py
│ ├── migrations
│ │ ├── __init__.py
│ │ └── 0001_first_migration.py
│ ├── tests
│ │ ├── __init__.py
│ │ ├── test_urls.py
│ │ ├── templates
│ │ │ └── pdt
│ │ │ │ └── pdt.html
│ │ └── test_pdt.py
│ ├── urls.py
│ ├── forms.py
│ ├── templates
│ │ └── pdt
│ │ │ ├── pdt.html
│ │ │ └── test_pdt_response.html
│ ├── signals.py
│ ├── views.py
│ ├── admin.py
│ └── models.py
├── conf.py
├── widgets.py
├── helpers.py
├── forms.py
└── models.py
├── .gitignore
└── README.md
/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pro/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/standard/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/standard/ipn/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/standard/pdt/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/standard/ipn/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/standard/pdt/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/standard/ipn/tests/__init__.py:
--------------------------------------------------------------------------------
1 | from test_ipn import *
--------------------------------------------------------------------------------
/standard/pdt/tests/__init__.py:
--------------------------------------------------------------------------------
1 | from test_pdt import *
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | .svn
3 | .project
4 | .pydevproject
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **django-paypal is maintained at: https://github.com/spookylukey/django-paypal**
2 |
3 | This fork is no longer maintained.
4 |
--------------------------------------------------------------------------------
/standard/ipn/tests/test_urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls.defaults import *
2 |
3 | urlpatterns = patterns('paypal.standard.ipn.views',
4 | (r'^ipn/$', 'ipn'),
5 | )
6 |
--------------------------------------------------------------------------------
/standard/pdt/tests/test_urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls.defaults import *
2 |
3 | urlpatterns = patterns('paypal.standard.pdt.views',
4 | (r'^pdt/$', 'pdt'),
5 | )
6 |
--------------------------------------------------------------------------------
/standard/pdt/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls.defaults import *
2 |
3 | urlpatterns = patterns('paypal.standard.pdt.views',
4 | url(r'^$', 'pdt', name="paypal-pdt"),
5 | )
--------------------------------------------------------------------------------
/standard/ipn/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls.defaults import *
2 |
3 | urlpatterns = patterns('paypal.standard.ipn.views',
4 | url(r'^$', 'ipn', name="paypal-ipn"),
5 | )
--------------------------------------------------------------------------------
/standard/ipn/templates/ipn/paypal.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {{ form.sandbox }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/standard/pdt/forms.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from paypal.standard.forms import PayPalStandardBaseForm
4 | from paypal.standard.pdt.models import PayPalPDT
5 |
6 |
7 | class PayPalPDTForm(PayPalStandardBaseForm):
8 | class Meta:
9 | model = PayPalPDT
--------------------------------------------------------------------------------
/pro/admin.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from string import split as L
4 | from django.contrib import admin
5 | from paypal.pro.models import PayPalNVP
6 |
7 |
8 | class PayPalNVPAdmin(admin.ModelAdmin):
9 | list_display = L("user method flag flag_code created_at")
10 | admin.site.register(PayPalNVP, PayPalNVPAdmin)
11 |
--------------------------------------------------------------------------------
/pro/templates/pro/confirm.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/standard/ipn/templates/ipn/ipn.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/standard/ipn/forms.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from paypal.standard.forms import PayPalStandardBaseForm
4 | from paypal.standard.ipn.models import PayPalIPN
5 |
6 |
7 | class PayPalIPNForm(PayPalStandardBaseForm):
8 | """
9 | Form used to receive and record PayPal IPN notifications.
10 |
11 | PayPal IPN test tool:
12 | https://developer.paypal.com/us/cgi-bin/devscr?cmd=_tools-session
13 | """
14 | class Meta:
15 | model = PayPalIPN
16 |
17 |
--------------------------------------------------------------------------------
/pro/signals.py:
--------------------------------------------------------------------------------
1 | from django.dispatch import Signal
2 |
3 | """
4 | These signals are different from IPN signals in that they are sent the second
5 | the payment is failed or succeeds and come with the `item` object passed to
6 | PayPalPro rather than an IPN object.
7 |
8 | ### SENDER is the item? is that right???
9 |
10 | """
11 |
12 | # Sent when a payment is successfully processed.
13 | payment_was_successful = Signal() #providing_args=["item"])
14 |
15 | # Sent when a payment is flagged.
16 | payment_was_flagged = Signal() #providing_args=["item"])
17 |
--------------------------------------------------------------------------------
/pro/templates/pro/payment.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/standard/pdt/tests/templates/pdt/pdt.html:
--------------------------------------------------------------------------------
1 | {% ifequal pdt_obj.st 'SUCCESS' %}
2 | Transaction complete
3 | Thank you for your payment
4 | Please print this page for your records
5 |
6 |
7 |
8 | | Payer: | {{ pdt_obj.first_name }} {{ pdt_obj.last_name }} |
9 | | Payer Email: | {{ pdt_obj.payer_email }} |
10 | | Amount: | {{ pdt_obj.mc_currency }} {{ pdt_obj.mc_gross }} |
11 | | Reference: | {{ pdt_obj.txn_id }} |
12 |
13 |
14 |
15 |
16 | {% else %}
17 | Transaction Failed
18 | Sorry transaction failed, please try a different form of payment
19 | {% endifequal %}
--------------------------------------------------------------------------------
/standard/pdt/templates/pdt/pdt.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% block content %}
3 |
4 | {% ifequal pdt_obj.st 'SUCCESS' %}
5 | Transaction complete
6 | Thank you for your payment
7 | Please print this page for your records
8 |
9 |
10 |
11 | | Payer: | {{ pdt_obj.first_name }} {{ pdt_obj.last_name }} |
12 | | Payer Email: | {{ pdt_obj.payer_email }} |
13 | | Amount: | {{ pdt_obj.mc_currency }} {{ pdt_obj.mc_gross }} |
14 | | Reference: | {{ pdt_obj.txn_id }} |
15 |
16 |
17 |
18 |
19 | {% else %}
20 | Transaction Failed
21 | Sorry transaction failed, please try a different form of payment
22 | {% endifequal %}
23 |
24 | {% endblock %}
25 |
26 |
27 |
--------------------------------------------------------------------------------
/standard/conf.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 |
3 | class PayPalSettingsError(Exception):
4 | """Raised when settings be bad."""
5 |
6 |
7 | TEST = getattr(settings, "PAYPAL_TEST", True)
8 |
9 |
10 | RECEIVER_EMAIL = settings.PAYPAL_RECEIVER_EMAIL
11 |
12 |
13 | # API Endpoints.
14 | POSTBACK_ENDPOINT = "https://www.paypal.com/cgi-bin/webscr"
15 | SANDBOX_POSTBACK_ENDPOINT = "https://www.sandbox.paypal.com/cgi-bin/webscr"
16 |
17 | # Images
18 | IMAGE = getattr(settings, "PAYPAL_IMAGE", "http://images.paypal.com/images/x-click-but01.gif")
19 | SUBSCRIPTION_IMAGE = "https://www.paypal.com/en_US/i/btn/btn_subscribeCC_LG.gif"
20 | SANDBOX_IMAGE = getattr(settings, "PAYPAL_SANDBOX_IMAGE", "https://www.sandbox.paypal.com/en_US/i/btn/btn_buynowCC_LG.gif")
21 | SUBSCRIPTION_SANDBOX_IMAGE = "https://www.sandbox.paypal.com/en_US/i/btn/btn_subscribeCC_LG.gif"
--------------------------------------------------------------------------------
/standard/ipn/signals.py:
--------------------------------------------------------------------------------
1 | """
2 | Note that sometimes you will get duplicate signals emitted, depending on configuration of your systems.
3 | If you do encounter this, you will need to add the "dispatch_uid" to your connect handlers:
4 | http://code.djangoproject.com/wiki/Signals#Helppost_saveseemstobeemittedtwiceforeachsave
5 |
6 | """
7 | from django.dispatch import Signal
8 |
9 | # Sent when a payment is successfully processed.
10 | payment_was_successful = Signal()
11 |
12 | # Sent when a payment is flagged.
13 | payment_was_flagged = Signal()
14 |
15 | # Sent when a subscription was cancelled.
16 | subscription_cancel = Signal()
17 |
18 | # Sent when a subscription expires.
19 | subscription_eot = Signal()
20 |
21 | # Sent when a subscription was modified.
22 | subscription_modify = Signal()
23 |
24 | # Sent when a subscription is created.
25 | subscription_signup = Signal()
--------------------------------------------------------------------------------
/standard/pdt/signals.py:
--------------------------------------------------------------------------------
1 | """
2 | Note that sometimes you will get duplicate signals emitted, depending on configuration of your systems.
3 | If you do encounter this, you will need to add the "dispatch_uid" to your connect handlers:
4 | http://code.djangoproject.com/wiki/Signals#Helppost_saveseemstobeemittedtwiceforeachsave
5 |
6 | """
7 | from django.dispatch import Signal
8 |
9 | # Sent when a payment is successfully processed.
10 | pdt_successful = Signal()
11 |
12 | # Sent when a payment is flagged.
13 | pdt_failed = Signal()
14 |
15 | # # Sent when a subscription was cancelled.
16 | # subscription_cancel = Signal()
17 | #
18 | # # Sent when a subscription expires.
19 | # subscription_eot = Signal()
20 | #
21 | # # Sent when a subscription was modified.
22 | # subscription_modify = Signal()
23 | #
24 | # # Sent when a subscription ends.
25 | # subscription_signup = Signal()
--------------------------------------------------------------------------------
/standard/pdt/templates/pdt/test_pdt_response.html:
--------------------------------------------------------------------------------
1 | {{st}}
2 | mc_gross={{mc_gross}}
3 | invoice=66
4 | settle_amount=289.83
5 | protection_eligibility=Ineligible
6 | payer_id=8MZ9FQTSAMUPJ
7 | tax=0.00
8 | payment_date=04%3A53%3A52+Apr+12%2C+2009+PDT
9 | payment_status=Completed
10 | charset=windows-1252
11 | first_name=Test
12 | mc_fee=6.88
13 | exchange_rate=1.32876
14 | settle_currency=USD
15 | custom={{custom}}
16 | payer_status=verified
17 | business={{business}}
18 | quantity=1
19 | payer_email=buyer_1239119200_per%40yoursite.com
20 | txn_id={{txn_id}}
21 | payment_type=instant
22 | last_name=User
23 | receiver_email={{business}}
24 | payment_fee=
25 | receiver_id=746LDC2EQAP4W
26 | txn_type=web_accept
27 | item_name=Advertising+Campaign%3A+1+Month+%28225.00%29
28 | mc_currency=EUR
29 | item_number=
30 | residence_country=US
31 | handling_amount=0.00
32 | transaction_subject={{custom}}
33 | payment_gross=
34 | shipping=0.00
35 | -
--------------------------------------------------------------------------------
/standard/widgets.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from django import forms
4 | from django.forms.util import flatatt
5 | from django.utils.safestring import mark_safe
6 | from django.utils.encoding import force_unicode
7 |
8 |
9 | class ValueHiddenInput(forms.HiddenInput):
10 | """
11 | Widget that renders only if it has a value.
12 | Used to remove unused fields from PayPal buttons.
13 | """
14 | def render(self, name, value, attrs=None):
15 | if value is None:
16 | return u''
17 | else:
18 | return super(ValueHiddenInput, self).render(name, value, attrs)
19 |
20 | class ReservedValueHiddenInput(ValueHiddenInput):
21 | """
22 | Overrides the default name attribute of the form.
23 | Used for the PayPal `return` field.
24 | """
25 | def render(self, name, value, attrs=None):
26 | if value is None:
27 | value = ''
28 | final_attrs = self.build_attrs(attrs, type=self.input_type)
29 | if value != '':
30 | final_attrs['value'] = force_unicode(value)
31 | return mark_safe(u'' % flatatt(final_attrs))
--------------------------------------------------------------------------------
/standard/ipn/views.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from django.http import HttpResponse
4 | from django.views.decorators.http import require_POST
5 | from paypal.standard.ipn.forms import PayPalIPNForm
6 | from paypal.standard.ipn.models import PayPalIPN
7 |
8 |
9 | @require_POST
10 | def ipn(request, item_check_callable=None):
11 | """
12 | PayPal IPN endpoint (notify_url).
13 | Used by both PayPal Payments Pro and Payments Standard to confirm transactions.
14 | http://tinyurl.com/d9vu9d
15 |
16 | PayPal IPN Simulator:
17 | https://developer.paypal.com/cgi-bin/devscr?cmd=_ipn-link-session
18 | """
19 | flag = None
20 | ipn_obj = None
21 | form = PayPalIPNForm(request.POST)
22 | if form.is_valid():
23 | try:
24 | ipn_obj = form.save(commit=False)
25 | except Exception, e:
26 | flag = "Exception while processing. (%s)" % e
27 | else:
28 | flag = "Invalid form. (%s)" % form.errors
29 |
30 | if ipn_obj is None:
31 | ipn_obj = PayPalIPN()
32 |
33 | ipn_obj.initialize(request)
34 |
35 | if flag is not None:
36 | ipn_obj.set_flag(flag)
37 | else:
38 | # Secrets should only be used over SSL.
39 | if request.is_secure() and 'secret' in request.GET:
40 | ipn_obj.verify_secret(form, request.GET['secret'])
41 | else:
42 | ipn_obj.verify(item_check_callable)
43 |
44 | ipn_obj.save()
45 | return HttpResponse("OKAY")
--------------------------------------------------------------------------------
/standard/ipn/models.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import urllib2
4 | from paypal.standard.models import PayPalStandardBase
5 | from paypal.standard.ipn.signals import *
6 |
7 |
8 | class PayPalIPN(PayPalStandardBase):
9 | """Logs PayPal IPN interactions."""
10 | format = u""
11 |
12 | class Meta:
13 | db_table = "paypal_ipn"
14 | verbose_name = "PayPal IPN"
15 |
16 | def _postback(self):
17 | """Perform PayPal Postback validation."""
18 | return urllib2.urlopen(self.get_endpoint(), "cmd=_notify-validate&%s" % self.query).read()
19 |
20 | def _verify_postback(self):
21 | if self.response != "VERIFIED":
22 | self.set_flag("Invalid postback. (%s)" % self.response)
23 |
24 | def send_signals(self):
25 | """Shout for the world to hear whether a txn was successful."""
26 | # Transaction signals:
27 | if self.is_transaction():
28 | if self.flag:
29 | payment_was_flagged.send(sender=self)
30 | else:
31 | payment_was_successful.send(sender=self)
32 | # Subscription signals:
33 | else:
34 | if self.is_subscription_cancellation():
35 | subscription_cancel.send(sender=self)
36 | elif self.is_subscription_signup():
37 | subscription_signup.send(sender=self)
38 | elif self.is_subscription_end_of_term():
39 | subscription_eot.send(sender=self)
40 | elif self.is_subscription_modified():
41 | subscription_modify.send(sender=self)
--------------------------------------------------------------------------------
/pro/forms.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from django import forms
4 |
5 | from paypal.pro.fields import CreditCardField, CreditCardExpiryField, CreditCardCVV2Field, CountryField
6 |
7 |
8 | class PaymentForm(forms.Form):
9 | """Form used to process direct payments."""
10 | firstname = forms.CharField(255, label="First Name")
11 | lastname = forms.CharField(255, label="Last Name")
12 | street = forms.CharField(255, label="Street Address")
13 | city = forms.CharField(255, label="City")
14 | state = forms.CharField(255, label="State")
15 | countrycode = CountryField(label="Country", initial="US")
16 | zip = forms.CharField(32, label="Postal / Zip Code")
17 | acct = CreditCardField(label="Credit Card Number")
18 | expdate = CreditCardExpiryField(label="Expiration Date")
19 | cvv2 = CreditCardCVV2Field(label="Card Security Code")
20 |
21 | def process(self, request, item):
22 | """Process a PayPal direct payment."""
23 | from paypal.pro.helpers import PayPalWPP
24 | wpp = PayPalWPP(request)
25 | params = self.cleaned_data
26 | params['creditcardtype'] = self.fields['acct'].card_type
27 | params['expdate'] = self.cleaned_data['expdate'].strftime("%m%Y")
28 | params['ipaddress'] = request.META.get("REMOTE_ADDR", "")
29 | params.update(item)
30 |
31 | # Create single payment:
32 | if 'billingperiod' not in params:
33 | response = wpp.doDirectPayment(params)
34 |
35 | # Create recurring payment:
36 | else:
37 | response = wpp.createRecurringPaymentsProfile(params, direct=True)
38 |
39 | return response
40 |
41 |
42 | class ConfirmForm(forms.Form):
43 | """Hidden form used by ExpressPay flow to keep track of payer information."""
44 | token = forms.CharField(max_length=255, widget=forms.HiddenInput())
45 | PayerID = forms.CharField(max_length=255, widget=forms.HiddenInput())
--------------------------------------------------------------------------------
/standard/pdt/views.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from django.template import RequestContext
4 | from django.shortcuts import render_to_response
5 | from django.views.decorators.http import require_GET
6 | from paypal.standard.pdt.models import PayPalPDT
7 | from paypal.standard.pdt.forms import PayPalPDTForm
8 |
9 |
10 | @require_GET
11 | def pdt(request, item_check_callable=None, template="pdt/pdt.html", context=None):
12 | """Payment data transfer implementation: http://tinyurl.com/c9jjmw"""
13 | context = context or {}
14 | pdt_obj = None
15 | txn_id = request.GET.get('tx')
16 | failed = False
17 | if txn_id is not None:
18 | # If an existing transaction with the id tx exists: use it
19 | try:
20 | pdt_obj = PayPalPDT.objects.get(txn_id=txn_id)
21 | except PayPalPDT.DoesNotExist:
22 | # This is a new transaction so we continue processing PDT request
23 | pass
24 |
25 | if pdt_obj is None:
26 | form = PayPalPDTForm(request.GET)
27 | if form.is_valid():
28 | try:
29 | pdt_obj = form.save(commit=False)
30 | except Exception, e:
31 | error = repr(e)
32 | failed = True
33 | else:
34 | error = form.errors
35 | failed = True
36 |
37 | if failed:
38 | pdt_obj = PayPalPDT()
39 | pdt_obj.set_flag("Invalid form. %s" % error)
40 |
41 | pdt_obj.initialize(request)
42 |
43 | if not failed:
44 | # The PDT object gets saved during verify
45 | pdt_obj.verify(item_check_callable)
46 | else:
47 | pass # we ignore any PDT requests that don't have a transaction id
48 |
49 | context.update({"failed":failed, "pdt_obj":pdt_obj})
50 | return render_to_response(template, context, RequestContext(request))
--------------------------------------------------------------------------------
/standard/helpers.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from django.conf import settings
4 |
5 |
6 | def duplicate_txn_id(ipn_obj):
7 | """Returns True if a record with this transaction id exists."""
8 | return ipn_obj._default_manager.filter(txn_id=ipn_obj.txn_id).count() > 0
9 |
10 | def make_secret(form_instance, secret_fields=None):
11 | """
12 | Returns a secret for use in a EWP form or an IPN verification based on a
13 | selection of variables in params. Should only be used with SSL.
14 |
15 | """
16 | # @@@ Moved here as temporary fix to avoid dependancy on auth.models.
17 | from django.contrib.auth.models import get_hexdigest
18 | # @@@ amount is mc_gross on the IPN - where should mapping logic go?
19 | # @@@ amount / mc_gross is not nessecarily returned as it was sent - how to use it? 10.00 vs. 10.0
20 | # @@@ the secret should be based on the invoice or custom fields as well - otherwise its always the same.
21 |
22 | # Build the secret with fields availible in both PaymentForm and the IPN. Order matters.
23 | if secret_fields is None:
24 | secret_fields = ['business', 'item_name']
25 |
26 | data = ""
27 | for name in secret_fields:
28 | if hasattr(form_instance, 'cleaned_data'):
29 | if name in form_instance.cleaned_data:
30 | data += unicode(form_instance.cleaned_data[name])
31 | else:
32 | # Initial data passed into the constructor overrides defaults.
33 | if name in form_instance.initial:
34 | data += unicode(form_instance.initial[name])
35 | elif name in form_instance.fields and form_instance.fields[name].initial is not None:
36 | data += unicode(form_instance.fields[name].initial)
37 |
38 | secret = get_hexdigest('sha1', settings.SECRET_KEY, data)
39 | return secret
40 |
41 | def check_secret(form_instance, secret):
42 | """
43 | Returns true if received `secret` matches expected secret for form_instance.
44 | Used to verify IPN.
45 |
46 | """
47 | # @@@ add invoice & custom
48 | # secret_fields = ['business', 'item_name']
49 | return make_secret(form_instance) == secret
--------------------------------------------------------------------------------
/standard/ipn/templates/ipn/ipn_test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/standard/pdt/admin.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from string import split as L
4 | from django.contrib import admin
5 | from paypal.standard.pdt.models import PayPalPDT
6 |
7 |
8 | # ToDo: How similiar is this to PayPalIPNAdmin? Could we just inherit off one common admin model?
9 | class PayPalPDTAdmin(admin.ModelAdmin):
10 | date_hierarchy = 'payment_date'
11 | fieldsets = (
12 | (None, {
13 | "fields": L("flag txn_id txn_type payment_status payment_date transaction_entity reason_code pending_reason mc_gross mc_fee auth_status auth_amount auth_exp auth_id")
14 | }),
15 | ("Address", {
16 | "description": "The address of the Buyer.",
17 | 'classes': ('collapse',),
18 | "fields": L("address_city address_country address_country_code address_name address_state address_status address_street address_zip")
19 | }),
20 | ("Buyer", {
21 | "description": "The information about the Buyer.",
22 | 'classes': ('collapse',),
23 | "fields": L("first_name last_name payer_business_name payer_email payer_id payer_status contact_phone residence_country")
24 | }),
25 | ("Seller", {
26 | "description": "The information about the Seller.",
27 | 'classes': ('collapse',),
28 | "fields": L("business item_name item_number quantity receiver_email receiver_id custom invoice memo")
29 | }),
30 | ("Subscriber", {
31 | "description": "The information about the Subscription.",
32 | 'classes': ('collapse',),
33 | "fields": L("subscr_id subscr_date subscr_effective")
34 | }),
35 | ("Recurring", {
36 | "description": "Information about recurring Payments.",
37 | "classes": ("collapse",),
38 | "fields": L("profile_status initial_payment_amount amount_per_cycle outstanding_balance period_type product_name product_type recurring_payment_id receipt_id next_payment_date")
39 | }),
40 | ("Admin", {
41 | "description": "Additional Info.",
42 | "classes": ('collapse',),
43 | "fields": L("test_ipn ipaddress query flag_code flag_info")
44 | }),
45 | )
46 | list_display = L("__unicode__ flag invoice custom payment_status created_at")
47 | search_fields = L("txn_id recurring_payment_id")
48 | admin.site.register(PayPalPDT, PayPalPDTAdmin)
--------------------------------------------------------------------------------
/pro/creditcard.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | Adapted from:
5 | - http://www.djangosnippets.org/snippets/764/
6 | - http://www.satchmoproject.com/trac/browser/satchmo/trunk/satchmo/apps/satchmo_utils/views.py
7 | - http://tinyurl.com/shoppify-credit-cards
8 | """
9 | import re
10 |
11 |
12 | # Well known card regular expressions.
13 | CARDS = {
14 | 'Visa': re.compile(r"^4\d{12}(\d{3})?$"),
15 | 'Mastercard': re.compile(r"(5[1-5]\d{4}|677189)\d{10}$"),
16 | 'Dinersclub': re.compile(r"^3(0[0-5]|[68]\d)\d{11}"),
17 | 'Amex': re.compile("^3[47]\d{13}$"),
18 | 'Discover': re.compile("^(6011|65\d{2})\d{12}$"),
19 | }
20 |
21 | # Well known test numbers
22 | TEST_NUMBERS = [
23 | "378282246310005", "371449635398431", "378734493671000", "30569309025904",
24 | "38520000023237", "6011111111111117", "6011000990139424", "555555555554444",
25 | "5105105105105100", "4111111111111111", "4012888888881881", "4222222222222"
26 | ]
27 |
28 | def verify_credit_card(number):
29 | """Returns the card type for given card number or None if invalid."""
30 | return CreditCard(number).verify()
31 |
32 | class CreditCard(object):
33 | def __init__(self, number):
34 | self.number = number
35 |
36 | def is_number(self):
37 | """True if there is at least one digit in number."""
38 | self.number = re.sub(r'[^\d]', '', self.number)
39 | return self.number.isdigit()
40 |
41 | def is_mod10(self):
42 | """Returns True if number is valid according to mod10."""
43 | double = 0
44 | total = 0
45 | for i in range(len(self.number) - 1, -1, -1):
46 | for c in str((double + 1) * int(self.number[i])):
47 | total = total + int(c)
48 | double = (double + 1) % 2
49 | return (total % 10) == 0
50 |
51 | def is_test(self):
52 | """Returns True if number is a test card number."""
53 | return self.number in TEST_NUMBERS
54 |
55 | def get_type(self):
56 | """Return the type if it matches one of the cards."""
57 | for card, pattern in CARDS.iteritems():
58 | if pattern.match(self.number):
59 | return card
60 | return None
61 |
62 | def verify(self):
63 | """Returns the card type if valid else None."""
64 | if self.is_number() and not self.is_test() and self.is_mod10():
65 | return self.get_type()
66 | return None
--------------------------------------------------------------------------------
/standard/ipn/admin.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from django.contrib import admin
4 | from paypal.standard.ipn.models import PayPalIPN
5 |
6 |
7 | class PayPalIPNAdmin(admin.ModelAdmin):
8 | date_hierarchy = 'payment_date'
9 | fieldsets = (
10 | (None, {
11 | "fields": [
12 | "flag", "txn_id", "txn_type", "payment_status", "payment_date",
13 | "transaction_entity", "reason_code", "pending_reason",
14 | "mc_gross", "mc_fee", "auth_status", "auth_amount", "auth_exp",
15 | "auth_id"
16 | ]
17 | }),
18 | ("Address", {
19 | "description": "The address of the Buyer.",
20 | 'classes': ('collapse',),
21 | "fields": [
22 | "address_city", "address_country", "address_country_code",
23 | "address_name", "address_state", "address_status",
24 | "address_street", "address_zip"
25 | ]
26 | }),
27 | ("Buyer", {
28 | "description": "The information about the Buyer.",
29 | 'classes': ('collapse',),
30 | "fields": [
31 | "first_name", "last_name", "payer_business_name", "payer_email",
32 | "payer_id", "payer_status", "contact_phone", "residence_country"
33 | ]
34 | }),
35 | ("Seller", {
36 | "description": "The information about the Seller.",
37 | 'classes': ('collapse',),
38 | "fields": [
39 | "business", "item_name", "item_number", "quantity",
40 | "receiver_email", "receiver_id", "custom", "invoice", "memo"
41 | ]
42 | }),
43 | ("Recurring", {
44 | "description": "Information about recurring Payments.",
45 | "classes": ("collapse",),
46 | "fields": [
47 | "profile_status", "initial_payment_amount", "amount_per_cycle",
48 | "outstanding_balance", "period_type", "product_name",
49 | "product_type", "recurring_payment_id", "receipt_id",
50 | "next_payment_date"
51 | ]
52 | }),
53 | ("Admin", {
54 | "description": "Additional Info.",
55 | "classes": ('collapse',),
56 | "fields": [
57 | "test_ipn", "ipaddress", "query", "response", "flag_code",
58 | "flag_info"
59 | ]
60 | }),
61 | )
62 | list_display = [
63 | "__unicode__", "flag", "flag_info", "invoice", "custom",
64 | "payment_status", "created_at"
65 | ]
66 | search_fields = ["txn_id", "recurring_payment_id"]
67 |
68 |
69 | admin.site.register(PayPalIPN, PayPalIPNAdmin)
--------------------------------------------------------------------------------
/standard/pdt/models.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from urllib import unquote_plus
4 | import urllib2
5 | from django.db import models
6 | from django.conf import settings
7 | from django.http import QueryDict
8 | from django.utils.http import urlencode
9 | from paypal.standard.models import PayPalStandardBase
10 | from paypal.standard.conf import POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT
11 | from paypal.standard.pdt.signals import pdt_successful, pdt_failed
12 |
13 | # ### Todo: Move this logic to conf.py:
14 | # if paypal.standard.pdt is in installed apps
15 | # ... then check for this setting in conf.py
16 | class PayPalSettingsError(Exception):
17 | """Raised when settings are incorrect."""
18 |
19 | try:
20 | IDENTITY_TOKEN = settings.PAYPAL_IDENTITY_TOKEN
21 | except:
22 | raise PayPalSettingsError("You must set PAYPAL_IDENTITY_TOKEN in settings.py. Get this token by enabling PDT in your PayPal account.")
23 |
24 |
25 | class PayPalPDT(PayPalStandardBase):
26 | format = u""
27 |
28 | amt = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
29 | cm = models.CharField(max_length=255, blank=True)
30 | sig = models.CharField(max_length=255, blank=True)
31 | tx = models.CharField(max_length=255, blank=True)
32 | st = models.CharField(max_length=32, blank=True)
33 |
34 | class Meta:
35 | db_table = "paypal_pdt"
36 | verbose_name = "PayPal PDT"
37 |
38 | def _postback(self):
39 | """
40 | Perform PayPal PDT Postback validation.
41 | Sends the transaction ID and business token to PayPal which responses with
42 | SUCCESS or FAILED.
43 |
44 | """
45 | postback_dict = dict(cmd="_notify-synch", at=IDENTITY_TOKEN, tx=self.tx)
46 | postback_params = urlencode(postback_dict)
47 | return urllib2.urlopen(self.get_endpoint(), postback_params).read()
48 |
49 | def get_endpoint(self):
50 | """Use the sandbox when in DEBUG mode as we don't have a test_ipn variable in pdt."""
51 | if settings.DEBUG:
52 | return SANDBOX_POSTBACK_ENDPOINT
53 | else:
54 | return POSTBACK_ENDPOINT
55 |
56 | def _verify_postback(self):
57 | # ### Now we don't really care what result was, just whether a flag was set or not.
58 | from paypal.standard.pdt.forms import PayPalPDTForm
59 | result = False
60 | response_list = self.response.split('\n')
61 | response_dict = {}
62 | for i, line in enumerate(response_list):
63 | unquoted_line = unquote_plus(line).strip()
64 | if i == 0:
65 | self.st = unquoted_line
66 | if self.st == "SUCCESS":
67 | result = True
68 | else:
69 | if self.st != "SUCCESS":
70 | self.set_flag(line)
71 | break
72 | try:
73 | if not unquoted_line.startswith(' -'):
74 | k, v = unquoted_line.split('=')
75 | response_dict[k.strip()] = v.strip()
76 | except ValueError, e:
77 | pass
78 |
79 | qd = QueryDict('', mutable=True)
80 | qd.update(response_dict)
81 | qd.update(dict(ipaddress=self.ipaddress, st=self.st, flag_info=self.flag_info))
82 | pdt_form = PayPalPDTForm(qd, instance=self)
83 | pdt_form.save(commit=False)
84 |
85 | def send_signals(self):
86 | # Send the PDT signals...
87 | if self.flag:
88 | pdt_failed.send(sender=self)
89 | else:
90 | pdt_successful.send(sender=self)
--------------------------------------------------------------------------------
/standard/ipn/tests/test_ipn.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.http import HttpResponse
3 | from django.test import TestCase
4 | from django.test.client import Client
5 |
6 | from paypal.standard.ipn.models import PayPalIPN
7 | from paypal.standard.ipn.signals import (payment_was_successful,
8 | payment_was_flagged)
9 |
10 |
11 | IPN_POST_PARAMS = {
12 | "protection_eligibility": "Ineligible",
13 | "last_name": "User",
14 | "txn_id": "51403485VH153354B",
15 | "receiver_email": settings.PAYPAL_RECEIVER_EMAIL,
16 | "payment_status": "Completed",
17 | "payment_gross": "10.00",
18 | "tax": "0.00",
19 | "residence_country": "US",
20 | "invoice": "0004",
21 | "payer_status": "verified",
22 | "txn_type": "express_checkout",
23 | "handling_amount": "0.00",
24 | "payment_date": "23:04:06 Feb 02, 2009 PST",
25 | "first_name": "Test",
26 | "item_name": "",
27 | "charset": "windows-1252",
28 | "custom": "website_id=13&user_id=21",
29 | "notify_version": "2.6",
30 | "transaction_subject": "",
31 | "test_ipn": "1",
32 | "item_number": "",
33 | "receiver_id": "258DLEHY2BDK6",
34 | "payer_id": "BN5JZ2V7MLEV4",
35 | "verify_sign": "An5ns1Kso7MWUdW4ErQKJJJ4qi4-AqdZy6dD.sGO3sDhTf1wAbuO2IZ7",
36 | "payment_fee": "0.59",
37 | "mc_fee": "0.59",
38 | "mc_currency": "USD",
39 | "shipping": "0.00",
40 | "payer_email": "bishan_1233269544_per@gmail.com",
41 | "payment_type": "instant",
42 | "mc_gross": "10.00",
43 | "quantity": "1",
44 | }
45 |
46 |
47 | class IPNTest(TestCase):
48 | urls = 'paypal.standard.ipn.tests.test_urls'
49 |
50 | def setUp(self):
51 | self.old_debug = settings.DEBUG
52 | settings.DEBUG = True
53 |
54 | # Monkey patch over PayPalIPN to make it get a VERFIED response.
55 | self.old_postback = PayPalIPN._postback
56 | PayPalIPN._postback = lambda self: "VERIFIED"
57 |
58 | def tearDown(self):
59 | settings.DEBUG = self.old_debug
60 | PayPalIPN._postback = self.old_postback
61 |
62 | def assertGotSignal(self, signal, flagged):
63 | # Check the signal was sent. These get lost if they don't reference self.
64 | self.got_signal = False
65 | self.signal_obj = None
66 |
67 | def handle_signal(sender, **kwargs):
68 | self.got_signal = True
69 | self.signal_obj = sender
70 | signal.connect(handle_signal)
71 |
72 | response = self.client.post("/ipn/", IPN_POST_PARAMS)
73 | self.assertEqual(response.status_code, 200)
74 | ipns = PayPalIPN.objects.all()
75 | self.assertEqual(len(ipns), 1)
76 | ipn_obj = ipns[0]
77 | self.assertEqual(ipn_obj.flag, flagged)
78 |
79 | self.assertTrue(self.got_signal)
80 | self.assertEqual(self.signal_obj, ipn_obj)
81 |
82 | def test_correct_ipn(self):
83 | self.assertGotSignal(payment_was_successful, False)
84 |
85 | def test_failed_ipn(self):
86 | PayPalIPN._postback = lambda self: "INVALID"
87 | self.assertGotSignal(payment_was_flagged, True)
88 |
89 | def assertFlagged(self, updates, flag_info):
90 | params = IPN_POST_PARAMS.copy()
91 | params.update(updates)
92 | response = self.client.post("/ipn/", params)
93 | self.assertEqual(response.status_code, 200)
94 | ipn_obj = PayPalIPN.objects.all()[0]
95 | self.assertEqual(ipn_obj.flag, True)
96 | self.assertEqual(ipn_obj.flag_info, flag_info)
97 |
98 | def test_incorrect_receiver_email(self):
99 | update = {"receiver_email": "incorrect_email@someotherbusiness.com"}
100 | flag_info = "Invalid receiver_email. (incorrect_email@someotherbusiness.com)"
101 | self.assertFlagged(update, flag_info)
102 |
103 | def test_invalid_payment_status(self):
104 | update = {"payment_status": "Failed"}
105 | flag_info = "Invalid payment_status. (Failed)"
106 | self.assertFlagged(update, flag_info)
107 |
108 | def test_duplicate_txn_id(self):
109 | self.client.post("/ipn/", IPN_POST_PARAMS)
110 | self.client.post("/ipn/", IPN_POST_PARAMS)
111 | self.assertEqual(len(PayPalIPN.objects.all()), 2)
112 | ipn_obj = PayPalIPN.objects.order_by('-created_at')[1]
113 | self.assertEqual(ipn_obj.flag, True)
114 | self.assertEqual(ipn_obj.flag_info, "Duplicate txn_id. (51403485VH153354B)")
--------------------------------------------------------------------------------
/pro/models.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from string import split as L
4 | from django.db import models
5 | from django.utils.http import urlencode
6 | from django.forms.models import model_to_dict
7 | from django.contrib.auth.models import User
8 |
9 |
10 | class PayPalNVP(models.Model):
11 | """Record of a NVP interaction with PayPal."""
12 | TIMESTAMP_FORMAT = "%Y-%m-%dT%H:%M:%SZ" # 2009-02-03T17:47:41Z
13 | RESTRICTED_FIELDS = L("expdate cvv2 acct")
14 | ADMIN_FIELDS = L("id user flag flag_code flag_info query response created_at updated_at ")
15 | ITEM_FIELDS = L("amt custom invnum")
16 | DIRECT_FIELDS = L("firstname lastname street city state countrycode zip")
17 |
18 | # Response fields
19 | method = models.CharField(max_length=64, blank=True)
20 | ack = models.CharField(max_length=32, blank=True)
21 | profilestatus = models.CharField(max_length=32, blank=True)
22 | timestamp = models.DateTimeField(blank=True, null=True)
23 | profileid = models.CharField(max_length=32, blank=True) # I-E596DFUSD882
24 | profilereference = models.CharField(max_length=128, blank=True) # PROFILEREFERENCE
25 | correlationid = models.CharField(max_length=32, blank=True) # 25b380cda7a21
26 | token = models.CharField(max_length=64, blank=True)
27 | payerid = models.CharField(max_length=64, blank=True)
28 |
29 | # Transaction Fields
30 | firstname = models.CharField("First Name", max_length=255, blank=True)
31 | lastname = models.CharField("Last Name", max_length=255, blank=True)
32 | street = models.CharField("Street Address", max_length=255, blank=True)
33 | city = models.CharField("City", max_length=255, blank=True)
34 | state = models.CharField("State", max_length=255, blank=True)
35 | countrycode = models.CharField("Country", max_length=2,blank=True)
36 | zip = models.CharField("Postal / Zip Code", max_length=32, blank=True)
37 |
38 | # Custom fields
39 | invnum = models.CharField(max_length=255, blank=True)
40 | custom = models.CharField(max_length=255, blank=True)
41 |
42 | # Admin fields
43 | user = models.ForeignKey(User, blank=True, null=True)
44 | flag = models.BooleanField(default=False, blank=True)
45 | flag_code = models.CharField(max_length=32, blank=True)
46 | flag_info = models.TextField(blank=True)
47 | ipaddress = models.IPAddressField(blank=True)
48 | query = models.TextField(blank=True)
49 | response = models.TextField(blank=True)
50 | created_at = models.DateTimeField(auto_now_add=True)
51 | updated_at = models.DateTimeField(auto_now=True)
52 |
53 | class Meta:
54 | db_table = "paypal_nvp"
55 | verbose_name = "PayPal NVP"
56 |
57 | def init(self, request, paypal_request, paypal_response):
58 | """Initialize a PayPalNVP instance from a HttpRequest."""
59 | self.ipaddress = request.META.get('REMOTE_ADDR', '')
60 | if hasattr(request, "user") and request.user.is_authenticated():
61 | self.user = request.user
62 |
63 | # No storing credit card info.
64 | query_data = dict((k,v) for k, v in paypal_request.iteritems() if k not in self.RESTRICTED_FIELDS)
65 | self.query = urlencode(query_data)
66 | self.response = urlencode(paypal_response)
67 |
68 | # Was there a flag on the play?
69 | ack = paypal_response.get('ack', False)
70 | if ack != "Success":
71 | if ack == "SuccessWithWarning":
72 | self.flag_info = paypal_response.get('l_longmessage0', '')
73 | else:
74 | self.set_flag(paypal_response.get('l_longmessage0', ''), paypal_response.get('l_errorcode', ''))
75 |
76 | def set_flag(self, info, code=None):
77 | """Flag this instance for investigation."""
78 | self.flag = True
79 | self.flag_info += info
80 | if code is not None:
81 | self.flag_code = code
82 |
83 | def process(self, request, item):
84 | """Do a direct payment."""
85 | from paypal.pro.helpers import PayPalWPP
86 | wpp = PayPalWPP(request)
87 |
88 | # Change the model information into a dict that PayPal can understand.
89 | params = model_to_dict(self, exclude=self.ADMIN_FIELDS)
90 | params['acct'] = self.acct
91 | params['creditcardtype'] = self.creditcardtype
92 | params['expdate'] = self.expdate
93 | params['cvv2'] = self.cvv2
94 | params.update(item)
95 |
96 | # Create recurring payment:
97 | if 'billingperiod' in params:
98 | return wpp.createRecurringPaymentsProfile(params, direct=True)
99 | # Create single payment:
100 | else:
101 | return wpp.doDirectPayment(params)
102 |
--------------------------------------------------------------------------------
/pro/tests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | from django.conf import settings
4 | from django.core.handlers.wsgi import WSGIRequest
5 | from django.forms import ValidationError
6 | from django.http import QueryDict
7 | from django.test import TestCase
8 | from django.test.client import Client
9 |
10 | from paypal.pro.fields import CreditCardField
11 | from paypal.pro.helpers import PayPalWPP, PayPalError
12 |
13 |
14 | class RequestFactory(Client):
15 | # Used to generate request objects.
16 | def request(self, **request):
17 | environ = {
18 | 'HTTP_COOKIE': self.cookies,
19 | 'PATH_INFO': '/',
20 | 'QUERY_STRING': '',
21 | 'REQUEST_METHOD': 'GET',
22 | 'SCRIPT_NAME': '',
23 | 'SERVER_NAME': 'testserver',
24 | 'SERVER_PORT': 80,
25 | 'SERVER_PROTOCOL': 'HTTP/1.1',
26 | }
27 | environ.update(self.defaults)
28 | environ.update(request)
29 | return WSGIRequest(environ)
30 |
31 | RF = RequestFactory()
32 | REQUEST = RF.get("/pay/", REMOTE_ADDR="127.0.0.1:8000")
33 |
34 |
35 | class DummyPayPalWPP(PayPalWPP):
36 | pass
37 | # """Dummy class for testing PayPalWPP."""
38 | # responses = {
39 | # # @@@ Need some reals data here.
40 | # "DoDirectPayment": """ack=Success×tamp=2009-03-12T23%3A52%3A33Z&l_severitycode0=Error&l_shortmessage0=Security+error&l_longmessage0=Security+header+is+not+valid&version=54.0&build=854529&l_errorcode0=&correlationid=""",
41 | # }
42 | #
43 | # def _request(self, data):
44 | # return self.responses["DoDirectPayment"]
45 |
46 |
47 | class CreditCardFieldTest(TestCase):
48 | def testCreditCardField(self):
49 | field = CreditCardField()
50 | field.clean('4797503429879309')
51 | self.assertEquals(field.card_type, "Visa")
52 | self.assertRaises(ValidationError, CreditCardField().clean, '1234567890123455')
53 |
54 |
55 | class PayPalWPPTest(TestCase):
56 | def setUp(self):
57 |
58 | # Avoding blasting real requests at PayPal.
59 | self.old_debug = settings.DEBUG
60 | settings.DEBUG = True
61 |
62 | self.item = {
63 | 'amt': '9.95',
64 | 'inv': 'inv',
65 | 'custom': 'custom',
66 | 'next': 'http://www.example.com/next/',
67 | 'returnurl': 'http://www.example.com/pay/',
68 | 'cancelurl': 'http://www.example.com/cancel/'
69 | }
70 | self.wpp = DummyPayPalWPP(REQUEST)
71 |
72 | def tearDown(self):
73 | settings.DEBUG = self.old_debug
74 |
75 | def test_doDirectPayment_missing_params(self):
76 | data = {'firstname': 'Chewbacca'}
77 | self.assertRaises(PayPalError, self.wpp.doDirectPayment, data)
78 |
79 | def test_doDirectPayment_valid(self):
80 | data = {
81 | 'firstname': 'Brave',
82 | 'lastname': 'Star',
83 | 'street': '1 Main St',
84 | 'city': u'San Jos\xe9',
85 | 'state': 'CA',
86 | 'countrycode': 'US',
87 | 'zip': '95131',
88 | 'expdate': '012019',
89 | 'cvv2': '037',
90 | 'acct': '4797503429879309',
91 | 'creditcardtype': 'visa',
92 | 'ipaddress': '10.0.1.199',}
93 | data.update(self.item)
94 | self.assertTrue(self.wpp.doDirectPayment(data))
95 |
96 | def test_doDirectPayment_invalid(self):
97 | data = {
98 | 'firstname': 'Epic',
99 | 'lastname': 'Fail',
100 | 'street': '100 Georgia St',
101 | 'city': 'Vancouver',
102 | 'state': 'BC',
103 | 'countrycode': 'CA',
104 | 'zip': 'V6V 1V1',
105 | 'expdate': '012019',
106 | 'cvv2': '999',
107 | 'acct': '1234567890',
108 | 'creditcardtype': 'visa',
109 | 'ipaddress': '10.0.1.199',}
110 | data.update(self.item)
111 | self.assertFalse(self.wpp.doDirectPayment(data))
112 |
113 | def test_setExpressCheckout(self):
114 | # We'll have to stub out tests for doExpressCheckoutPayment and friends
115 | # because they're behind paypal's doors.
116 | nvp_obj = self.wpp.setExpressCheckout(self.item)
117 | self.assertTrue(nvp_obj.ack == "Success")
118 |
119 |
120 | ### DoExpressCheckoutPayment
121 | # PayPal Request:
122 | # {'amt': '10.00',
123 | # 'cancelurl': u'http://xxx.xxx.xxx.xxx/deploy/480/upgrade/?upgrade=cname',
124 | # 'custom': u'website_id=480&cname=1',
125 | # 'inv': u'website-480-cname',
126 | # 'method': 'DoExpressCheckoutPayment',
127 | # 'next': u'http://xxx.xxx.xxx.xxx/deploy/480/upgrade/?upgrade=cname',
128 | # 'payerid': u'BN5JZ2V7MLEV4',
129 | # 'paymentaction': 'Sale',
130 | # 'returnurl': u'http://xxx.xxx.xxx.xxx/deploy/480/upgrade/?upgrade=cname',
131 | # 'token': u'EC-6HW17184NE0084127'}
132 | #
133 | # PayPal Response:
134 | # {'ack': 'Success',
135 | # 'amt': '10.00',
136 | # 'build': '848077',
137 | # 'correlationid': '375f4773c3d34',
138 | # 'currencycode': 'USD',
139 | # 'feeamt': '0.59',
140 | # 'ordertime': '2009-03-04T20:56:08Z',
141 | # 'paymentstatus': 'Completed',
142 | # 'paymenttype': 'instant',
143 | # 'pendingreason': 'None',
144 | # 'reasoncode': 'None',
145 | # 'taxamt': '0.00',
146 | # 'timestamp': '2009-03-04T20:56:09Z',
147 | # 'token': 'EC-6HW17184NE0084127',
148 | # 'transactionid': '3TG42202A7335864V',
149 | # 'transactiontype': 'expresscheckout',
150 | # 'version': '54.0'}
--------------------------------------------------------------------------------
/standard/pdt/tests/test_pdt.py:
--------------------------------------------------------------------------------
1 | """
2 | run this with ./manage.py test website
3 | see http://www.djangoproject.com/documentation/testing/ for details
4 | """
5 | import os
6 | from django.conf import settings
7 | from django.shortcuts import render_to_response
8 | from django.test import TestCase
9 | from paypal.standard.pdt.forms import PayPalPDTForm
10 | from paypal.standard.pdt.models import PayPalPDT
11 | from paypal.standard.pdt.signals import pdt_successful, pdt_failed
12 |
13 |
14 | class DummyPayPalPDT(object):
15 |
16 | def __init__(self, update_context_dict={}):
17 | self.context_dict = {'st': 'SUCCESS', 'custom':'cb736658-3aad-4694-956f-d0aeade80194',
18 | 'txn_id':'1ED550410S3402306', 'mc_gross': '225.00',
19 | 'business': settings.PAYPAL_RECEIVER_EMAIL, 'error': 'Error code: 1234'}
20 |
21 | self.context_dict.update(update_context_dict)
22 |
23 | def update_with_get_params(self, get_params):
24 | if get_params.has_key('tx'):
25 | self.context_dict['txn_id'] = get_params.get('tx')
26 | if get_params.has_key('amt'):
27 | self.context_dict['mc_gross'] = get_params.get('amt')
28 | if get_params.has_key('cm'):
29 | self.context_dict['custom'] = get_params.get('cm')
30 |
31 | def _postback(self, test=True):
32 | """Perform a Fake PayPal PDT Postback request."""
33 | # @@@ would be cool if this could live in the test templates dir...
34 | return render_to_response("pdt/test_pdt_response.html", self.context_dict).content
35 |
36 | class PDTTest(TestCase):
37 | urls = "paypal.standard.pdt.tests.test_urls"
38 | template_dirs = [os.path.join(os.path.dirname(__file__), 'templates'),]
39 |
40 | def setUp(self):
41 | # set up some dummy PDT get parameters
42 | self.get_params = {"tx":"4WJ86550014687441", "st":"Completed", "amt":"225.00", "cc":"EUR",
43 | "cm":"a3e192b8-8fea-4a86-b2e8-d5bf502e36be", "item_number":"",
44 | "sig":"blahblahblah"}
45 |
46 | # monkey patch the PayPalPDT._postback function
47 | self.dpppdt = DummyPayPalPDT()
48 | self.dpppdt.update_with_get_params(self.get_params)
49 | PayPalPDT._postback = self.dpppdt._postback
50 |
51 | def test_verify_postback(self):
52 | dpppdt = DummyPayPalPDT()
53 | paypal_response = dpppdt._postback()
54 | assert('SUCCESS' in paypal_response)
55 | self.assertEqual(len(PayPalPDT.objects.all()), 0)
56 | pdt_obj = PayPalPDT()
57 | pdt_obj.ipaddress = '127.0.0.1'
58 | pdt_obj.response = paypal_response
59 | pdt_obj._verify_postback()
60 | self.assertEqual(len(PayPalPDT.objects.all()), 0)
61 | self.assertEqual(pdt_obj.txn_id, '1ED550410S3402306')
62 |
63 | def test_pdt(self):
64 | self.assertEqual(len(PayPalPDT.objects.all()), 0)
65 | self.dpppdt.update_with_get_params(self.get_params)
66 | paypal_response = self.client.get("/pdt/", self.get_params)
67 | self.assertContains(paypal_response, 'Transaction complete', status_code=200)
68 | self.assertEqual(len(PayPalPDT.objects.all()), 1)
69 |
70 | def test_pdt_signals(self):
71 | self.successful_pdt_fired = False
72 | self.failed_pdt_fired = False
73 |
74 | def successful_pdt(sender, **kwargs):
75 | self.successful_pdt_fired = True
76 | pdt_successful.connect(successful_pdt)
77 |
78 | def failed_pdt(sender, **kwargs):
79 | self.failed_pdt_fired = True
80 | pdt_failed.connect(failed_pdt)
81 |
82 | self.assertEqual(len(PayPalPDT.objects.all()), 0)
83 | paypal_response = self.client.get("/pdt/", self.get_params)
84 | self.assertContains(paypal_response, 'Transaction complete', status_code=200)
85 | self.assertEqual(len(PayPalPDT.objects.all()), 1)
86 | self.assertTrue(self.successful_pdt_fired)
87 | self.assertFalse(self.failed_pdt_fired)
88 | pdt_obj = PayPalPDT.objects.all()[0]
89 | self.assertEqual(pdt_obj.flag, False)
90 |
91 | def test_double_pdt_get(self):
92 | self.assertEqual(len(PayPalPDT.objects.all()), 0)
93 | paypal_response = self.client.get("/pdt/", self.get_params)
94 | self.assertContains(paypal_response, 'Transaction complete', status_code=200)
95 | self.assertEqual(len(PayPalPDT.objects.all()), 1)
96 | pdt_obj = PayPalPDT.objects.all()[0]
97 | self.assertEqual(pdt_obj.flag, False)
98 | paypal_response = self.client.get("/pdt/", self.get_params)
99 | self.assertContains(paypal_response, 'Transaction complete', status_code=200)
100 | self.assertEqual(len(PayPalPDT.objects.all()), 1) # we don't create a new pdt
101 | pdt_obj = PayPalPDT.objects.all()[0]
102 | self.assertEqual(pdt_obj.flag, False)
103 |
104 | def test_no_txn_id_in_pdt(self):
105 | self.dpppdt.context_dict.pop('txn_id')
106 | self.get_params={}
107 | paypal_response = self.client.get("/pdt/", self.get_params)
108 | self.assertContains(paypal_response, 'Transaction Failed', status_code=200)
109 | self.assertEqual(len(PayPalPDT.objects.all()), 0)
110 |
111 | def test_custom_passthrough(self):
112 | self.assertEqual(len(PayPalPDT.objects.all()), 0)
113 | self.dpppdt.update_with_get_params(self.get_params)
114 | paypal_response = self.client.get("/pdt/", self.get_params)
115 | self.assertContains(paypal_response, 'Transaction complete', status_code=200)
116 | self.assertEqual(len(PayPalPDT.objects.all()), 1)
117 | pdt_obj = PayPalPDT.objects.all()[0]
118 | self.assertEqual(pdt_obj.custom, self.get_params['cm'] )
--------------------------------------------------------------------------------
/pro/helpers.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import datetime
4 | import pprint
5 | import time
6 | import urllib
7 | import urllib2
8 |
9 | from django.conf import settings
10 | from django.forms.models import fields_for_model
11 | from django.utils.datastructures import MergeDict
12 | from django.utils.http import urlencode
13 |
14 | from paypal.pro.models import PayPalNVP, L
15 |
16 |
17 | TEST = settings.PAYPAL_TEST
18 | USER = settings.PAYPAL_WPP_USER
19 | PASSWORD = settings.PAYPAL_WPP_PASSWORD
20 | SIGNATURE = settings.PAYPAL_WPP_SIGNATURE
21 | VERSION = 54.0
22 | BASE_PARAMS = dict(USER=USER , PWD=PASSWORD, SIGNATURE=SIGNATURE, VERSION=VERSION)
23 | ENDPOINT = "https://api-3t.paypal.com/nvp"
24 | SANDBOX_ENDPOINT = "https://api-3t.sandbox.paypal.com/nvp"
25 | NVP_FIELDS = fields_for_model(PayPalNVP).keys()
26 |
27 |
28 | def paypal_time(time_obj=None):
29 | """Returns a time suitable for PayPal time fields."""
30 | if time_obj is None:
31 | time_obj = time.gmtime()
32 | return time.strftime(PayPalNVP.TIMESTAMP_FORMAT, time_obj)
33 |
34 | def paypaltime2datetime(s):
35 | """Convert a PayPal time string to a DateTime."""
36 | return datetime.datetime(*(time.strptime(s, PayPalNVP.TIMESTAMP_FORMAT)[:6]))
37 |
38 |
39 | class PayPalError(TypeError):
40 | """Error thrown when something be wrong."""
41 |
42 |
43 | class PayPalWPP(object):
44 | """
45 | Wrapper class for the PayPal Website Payments Pro.
46 |
47 | Website Payments Pro Integration Guide:
48 | https://cms.paypal.com/cms_content/US/en_US/files/developer/PP_WPP_IntegrationGuide.pdf
49 |
50 | Name-Value Pair API Developer Guide and Reference:
51 | https://cms.paypal.com/cms_content/US/en_US/files/developer/PP_NVPAPI_DeveloperGuide.pdf
52 | """
53 | def __init__(self, request, params=BASE_PARAMS):
54 | """Required - USER / PWD / SIGNATURE / VERSION"""
55 | self.request = request
56 | if TEST:
57 | self.endpoint = SANDBOX_ENDPOINT
58 | else:
59 | self.endpoint = ENDPOINT
60 | self.signature_values = params
61 | self.signature = urlencode(self.signature_values) + "&"
62 |
63 | def doDirectPayment(self, params):
64 | """Call PayPal DoDirectPayment method."""
65 | defaults = {"method": "DoDirectPayment", "paymentaction": "Sale"}
66 | required = L("creditcardtype acct expdate cvv2 ipaddress firstname lastname street city state countrycode zip amt")
67 | nvp_obj = self._fetch(params, required, defaults)
68 | # @@@ Could check cvv2match / avscode are both 'X' or '0'
69 | # qd = django.http.QueryDict(nvp_obj.response)
70 | # if qd.get('cvv2match') not in ['X', '0']:
71 | # nvp_obj.set_flag("Invalid cvv2match: %s" % qd.get('cvv2match')
72 | # if qd.get('avscode') not in ['X', '0']:
73 | # nvp_obj.set_flag("Invalid avscode: %s" % qd.get('avscode')
74 | return not nvp_obj.flag
75 |
76 | def setExpressCheckout(self, params):
77 | """
78 | Initiates an Express Checkout transaction.
79 | Optionally, the SetExpressCheckout API operation can set up billing agreements for
80 | reference transactions and recurring payments.
81 | Returns a NVP instance - check for token and payerid to continue!
82 | """
83 | if self._is_recurring(params):
84 | params = self._recurring_setExpressCheckout_adapter(params)
85 |
86 | defaults = {"method": "SetExpressCheckout", "noshipping": 1}
87 | required = L("returnurl cancelurl amt")
88 | return self._fetch(params, required, defaults)
89 |
90 | def doExpressCheckoutPayment(self, params):
91 | """
92 | Check the dude out:
93 | """
94 | defaults = {"method": "DoExpressCheckoutPayment", "paymentaction": "Sale"}
95 | required =L("returnurl cancelurl amt token payerid")
96 | nvp_obj = self._fetch(params, required, defaults)
97 | return not nvp_obj.flag
98 |
99 | def createRecurringPaymentsProfile(self, params, direct=False):
100 | """
101 | Set direct to True to indicate that this is being called as a directPayment.
102 | Returns True PayPal successfully creates the profile otherwise False.
103 | """
104 | defaults = {"method": "CreateRecurringPaymentsProfile"}
105 | required = L("profilestartdate billingperiod billingfrequency amt")
106 |
107 | # Direct payments require CC data
108 | if direct:
109 | required + L("creditcardtype acct expdate firstname lastname")
110 | else:
111 | required + L("token payerid")
112 |
113 | nvp_obj = self._fetch(params, required, defaults)
114 |
115 | # Flag if profile_type != ActiveProfile
116 | return not nvp_obj.flag
117 |
118 | def getExpressCheckoutDetails(self, params):
119 | raise NotImplementedError
120 |
121 | def setCustomerBillingAgreement(self, params):
122 | raise DeprecationWarning
123 |
124 | def getTransactionDetails(self, params):
125 | raise NotImplementedError
126 |
127 | def massPay(self, params):
128 | raise NotImplementedError
129 |
130 | def getRecurringPaymentsProfileDetails(self, params):
131 | raise NotImplementedError
132 |
133 | def updateRecurringPaymentsProfile(self, params):
134 | raise NotImplementedError
135 |
136 | def billOutstandingAmount(self, params):
137 | raise NotImplementedError
138 |
139 | def manangeRecurringPaymentsProfileStatus(self, params):
140 | raise NotImplementedError
141 |
142 | def refundTransaction(self, params):
143 | raise NotImplementedError
144 |
145 | def _is_recurring(self, params):
146 | """Returns True if the item passed is a recurring transaction."""
147 | return 'billingfrequency' in params
148 |
149 | def _recurring_setExpressCheckout_adapter(self, params):
150 | """
151 | The recurring payment interface to SEC is different than the recurring payment
152 | interface to ECP. This adapts a normal call to look like a SEC call.
153 | """
154 | params['l_billingtype0'] = "RecurringPayments"
155 | params['l_billingagreementdescription0'] = params['desc']
156 |
157 | REMOVE = L("billingfrequency billingperiod profilestartdate desc")
158 | for k in params.keys():
159 | if k in REMOVE:
160 | del params[k]
161 |
162 | return params
163 |
164 | def _fetch(self, params, required, defaults):
165 | """Make the NVP request and store the response."""
166 | defaults.update(params)
167 | pp_params = self._check_and_update_params(required, defaults)
168 | pp_string = self.signature + urlencode(pp_params)
169 | response = self._request(pp_string)
170 | response_params = self._parse_response(response)
171 |
172 | if settings.DEBUG:
173 | print 'PayPal Request:'
174 | pprint.pprint(defaults)
175 | print '\nPayPal Response:'
176 | pprint.pprint(response_params)
177 |
178 | # Gather all NVP parameters to pass to a new instance.
179 | nvp_params = {}
180 | for k, v in MergeDict(defaults, response_params).items():
181 | if k in NVP_FIELDS:
182 | nvp_params[k] = v
183 |
184 | # PayPal timestamp has to be formatted.
185 | if 'timestamp' in nvp_params:
186 | nvp_params['timestamp'] = paypaltime2datetime(nvp_params['timestamp'])
187 |
188 | nvp_obj = PayPalNVP(**nvp_params)
189 | nvp_obj.init(self.request, params, response_params)
190 | nvp_obj.save()
191 | return nvp_obj
192 |
193 | def _request(self, data):
194 | """Moved out to make testing easier."""
195 | return urllib2.urlopen(self.endpoint, data).read()
196 |
197 | def _check_and_update_params(self, required, params):
198 | """
199 | Ensure all required parameters were passed to the API call and format
200 | them correctly.
201 | """
202 | for r in required:
203 | if r not in params:
204 | raise PayPalError("Missing required param: %s" % r)
205 |
206 | # Upper case all the parameters for PayPal.
207 | return (dict((k.upper(), v) for k, v in params.iteritems()))
208 |
209 | def _parse_response(self, response):
210 | """Turn the PayPal response into a dict"""
211 | response_tokens = {}
212 | for kv in response.split('&'):
213 | key, value = kv.split("=")
214 | response_tokens[key.lower()] = urllib.unquote(value)
215 | return response_tokens
--------------------------------------------------------------------------------
/pro/views.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from django.template import RequestContext
4 | from django.shortcuts import render_to_response
5 | from django.http import HttpResponseRedirect
6 | from django.utils.http import urlencode
7 |
8 | from paypal.pro.forms import PaymentForm, ConfirmForm
9 | from paypal.pro.models import PayPalNVP
10 | from paypal.pro.helpers import PayPalWPP, TEST
11 | from paypal.pro.signals import payment_was_successful, payment_was_flagged
12 |
13 |
14 | # PayPal Edit IPN URL:
15 | # https://www.sandbox.paypal.com/us/cgi-bin/webscr?cmd=_profile-ipn-notify
16 | EXPRESS_ENDPOINT = "https://www.paypal.com/webscr?cmd=_express-checkout&%s"
17 | SANDBOX_EXPRESS_ENDPOINT = "https://www.sandbox.paypal.com/webscr?cmd=_express-checkout&%s"
18 |
19 |
20 | class PayPalPro(object):
21 | """
22 | This class-based view takes care of PayPal WebsitePaymentsPro (WPP).
23 | PayPalPro has two separate flows - DirectPayment and ExpressPayFlow. In
24 | DirectPayment the user buys on your site. In ExpressPayFlow the user is
25 | direct to PayPal to confirm their purchase. PayPalPro implements both
26 | flows. To it create an instance using the these parameters:
27 |
28 | item: a dictionary that holds information about the item being purchased.
29 |
30 | For single item purchase (pay once):
31 |
32 | Required Keys:
33 | * amt: Float amount of the item.
34 |
35 | Optional Keys:
36 | * custom: You can set this to help you identify a transaction.
37 | * invnum: Unique ID that identifies this transaction.
38 |
39 | For recurring billing:
40 |
41 | Required Keys:
42 | * amt: Float amount for each billing cycle.
43 | * billingperiod: String unit of measure for the billing cycle (Day|Week|SemiMonth|Month|Year)
44 | * billingfrequency: Integer number of periods that make up a cycle.
45 | * profilestartdate: The date to begin billing. "2008-08-05T17:00:00Z" UTC/GMT
46 | * desc: Description of what you're billing for.
47 |
48 | Optional Keys:
49 | * trialbillingperiod: String unit of measure for trial cycle (Day|Week|SemiMonth|Month|Year)
50 | * trialbillingfrequency: Integer # of periods in a cycle.
51 | * trialamt: Float amount to bill for the trial period.
52 | * trialtotalbillingcycles: Integer # of cycles for the trial payment period.
53 | * failedinitamtaction: set to continue on failure (ContinueOnFailure / CancelOnFailure)
54 | * maxfailedpayments: number of payments before profile is suspended.
55 | * autobilloutamt: automatically bill outstanding amount.
56 | * subscribername: Full name of the person who paid.
57 | * profilereference: Unique reference or invoice number.
58 | * taxamt: How much tax.
59 | * initamt: Initial non-recurring payment due upon creation.
60 | * currencycode: defaults to USD
61 | * + a bunch of shipping fields
62 |
63 | payment_form_cls: form class that will be used to display the payment form.
64 | It should inherit from `paypal.pro.forms.PaymentForm` if you're adding more.
65 |
66 | payment_template: template used to ask the dude for monies. To comply with
67 | PayPal standards it must include a link to PayPal Express Checkout.
68 |
69 | confirm_form_cls: form class that will be used to display the confirmation form.
70 | It should inherit from `paypal.pro.forms.ConfirmForm`. It is only used in the Express flow.
71 |
72 | success_url / fail_url: URLs to be redirected to when the payment successful or fails.
73 | """
74 | errors = {
75 | "processing": "There was an error processing your payment. Check your information and try again.",
76 | "form": "Please correct the errors below and try again.",
77 | "paypal": "There was a problem contacting PayPal. Please try again later."
78 | }
79 |
80 | def __init__(self, item=None, payment_form_cls=PaymentForm,
81 | payment_template="pro/payment.html", confirm_form_cls=ConfirmForm,
82 | confirm_template="pro/confirm.html", success_url="?success",
83 | fail_url=None, context=None, form_context_name="form"):
84 | self.item = item
85 | self.payment_form_cls = payment_form_cls
86 | self.payment_template = payment_template
87 | self.confirm_form_cls = confirm_form_cls
88 | self.confirm_template = confirm_template
89 | self.success_url = success_url
90 | self.fail_url = fail_url
91 | self.context = context or {}
92 | self.form_context_name = form_context_name
93 |
94 | def __call__(self, request):
95 | """Return the appropriate response for the state of the transaction."""
96 | self.request = request
97 | if request.method == "GET":
98 | if self.should_redirect_to_express():
99 | return self.redirect_to_express()
100 | elif self.should_render_confirm_form():
101 | return self.render_confirm_form()
102 | elif self.should_render_payment_form():
103 | return self.render_payment_form()
104 | else:
105 | if self.should_validate_confirm_form():
106 | return self.validate_confirm_form()
107 | elif self.should_validate_payment_form():
108 | return self.validate_payment_form()
109 |
110 | # Default to the rendering the payment form.
111 | return self.render_payment_form()
112 |
113 | def is_recurring(self):
114 | return self.item is not None and 'billingperiod' in self.item
115 |
116 | def should_redirect_to_express(self):
117 | return 'express' in self.request.GET
118 |
119 | def should_render_confirm_form(self):
120 | return 'token' in self.request.GET and 'PayerID' in self.request.GET
121 |
122 | def should_render_payment_form(self):
123 | return True
124 |
125 | def should_validate_confirm_form(self):
126 | return 'token' in self.request.POST and 'PayerID' in self.request.POST
127 |
128 | def should_validate_payment_form(self):
129 | return True
130 |
131 | def render_payment_form(self):
132 | """Display the DirectPayment for entering payment information."""
133 | self.context[self.form_context_name] = self.payment_form_cls()
134 | return render_to_response(self.payment_template, self.context, RequestContext(self.request))
135 |
136 | def validate_payment_form(self):
137 | """Try to validate and then process the DirectPayment form."""
138 | form = self.payment_form_cls(self.request.POST)
139 | if form.is_valid():
140 | success = form.process(self.request, self.item)
141 | if success:
142 | payment_was_successful.send(sender=self.item)
143 | return HttpResponseRedirect(self.success_url)
144 | else:
145 | self.context['errors'] = self.errors['processing']
146 |
147 | self.context[self.form_context_name] = form
148 | self.context.setdefault("errors", self.errors['form'])
149 | return render_to_response(self.payment_template, self.context, RequestContext(self.request))
150 |
151 | def get_endpoint(self):
152 | if TEST:
153 | return SANDBOX_EXPRESS_ENDPOINT
154 | else:
155 | return EXPRESS_ENDPOINT
156 |
157 | def redirect_to_express(self):
158 | """
159 | First step of ExpressCheckout. Redirect the request to PayPal using the
160 | data returned from setExpressCheckout.
161 | """
162 | wpp = PayPalWPP(self.request)
163 | nvp_obj = wpp.setExpressCheckout(self.item)
164 | if not nvp_obj.flag:
165 | pp_params = dict(token=nvp_obj.token, AMT=self.item['amt'],
166 | RETURNURL=self.item['returnurl'],
167 | CANCELURL=self.item['cancelurl'])
168 | pp_url = self.get_endpoint() % urlencode(pp_params)
169 | return HttpResponseRedirect(pp_url)
170 | else:
171 | self.context['errors'] = self.errors['paypal']
172 | return self.render_payment_form()
173 |
174 | def render_confirm_form(self):
175 | """
176 | Second step of ExpressCheckout. Display an order confirmation form which
177 | contains hidden fields with the token / PayerID from PayPal.
178 | """
179 | initial = dict(token=self.request.GET['token'], PayerID=self.request.GET['PayerID'])
180 | self.context[self.form_context_name] = self.confirm_form_cls(initial=initial)
181 | return render_to_response(self.confirm_template, self.context, RequestContext(self.request))
182 |
183 | def validate_confirm_form(self):
184 | """
185 | Third and final step of ExpressCheckout. Request has pressed the confirmation but
186 | and we can send the final confirmation to PayPal using the data from the POST'ed form.
187 | """
188 | wpp = PayPalWPP(self.request)
189 | pp_data = dict(token=self.request.POST['token'], payerid=self.request.POST['PayerID'])
190 | self.item.update(pp_data)
191 |
192 | # @@@ This check and call could be moved into PayPalWPP.
193 | if self.is_recurring():
194 | success = wpp.createRecurringPaymentsProfile(self.item)
195 | else:
196 | success = wpp.doExpressCheckoutPayment(self.item)
197 |
198 | if success:
199 | payment_was_successful.send(sender=self.item)
200 | return HttpResponseRedirect(self.success_url)
201 | else:
202 | self.context['errors'] = self.errors['processing']
203 | return self.render_payment_form()
204 |
--------------------------------------------------------------------------------
/standard/forms.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from django import forms
4 | from django.conf import settings
5 | from django.utils.safestring import mark_safe
6 | from paypal.standard.conf import *
7 | from paypal.standard.widgets import ValueHiddenInput, ReservedValueHiddenInput
8 | from paypal.standard.conf import (POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT,
9 | RECEIVER_EMAIL)
10 |
11 |
12 | # 20:18:05 Jan 30, 2009 PST - PST timezone support is not included out of the box.
13 | # PAYPAL_DATE_FORMAT = ("%H:%M:%S %b. %d, %Y PST", "%H:%M:%S %b %d, %Y PST",)
14 | # PayPal dates have been spotted in the wild with these formats, beware!
15 | PAYPAL_DATE_FORMAT = ("%H:%M:%S %b. %d, %Y PST",
16 | "%H:%M:%S %b. %d, %Y PDT",
17 | "%H:%M:%S %b %d, %Y PST",
18 | "%H:%M:%S %b %d, %Y PDT",)
19 |
20 | class PayPalPaymentsForm(forms.Form):
21 | """
22 | Creates a PayPal Payments Standard "Buy It Now" button, configured for a
23 | selling a single item with no shipping.
24 |
25 | For a full overview of all the fields you can set (there is a lot!) see:
26 | http://tinyurl.com/pps-integration
27 |
28 | Usage:
29 | >>> f = PayPalPaymentsForm(initial={'item_name':'Widget 001', ...})
30 | >>> f.render()
31 | u'""" % (POSTBACK_ENDPOINT, self.as_p(), self.get_image()))
103 |
104 |
105 | def sandbox(self):
106 | return mark_safe(u"""""" % (SANDBOX_POSTBACK_ENDPOINT, self.as_p(), self.get_image()))
110 |
111 | def get_image(self):
112 | return {
113 | (True, True): SUBSCRIPTION_SANDBOX_IMAGE,
114 | (True, False): SANDBOX_IMAGE,
115 | (False, True): SUBSCRIPTION_IMAGE,
116 | (False, False): IMAGE
117 | }[TEST, self.is_subscription()]
118 |
119 | def is_transaction(self):
120 | return self.button_type == "buy"
121 |
122 | def is_subscription(self):
123 | return self.button_type == "subscribe"
124 |
125 |
126 | class PayPalEncryptedPaymentsForm(PayPalPaymentsForm):
127 | """
128 | Creates a PayPal Encrypted Payments "Buy It Now" button.
129 | Requires the M2Crypto package.
130 |
131 | Based on example at:
132 | http://blog.mauveweb.co.uk/2007/10/10/paypal-with-django/
133 |
134 | """
135 | def _encrypt(self):
136 | """Use your key thing to encrypt things."""
137 | from M2Crypto import BIO, SMIME, X509
138 | # @@@ Could we move this to conf.py?
139 | CERT = settings.PAYPAL_PRIVATE_CERT
140 | PUB_CERT = settings.PAYPAL_PUBLIC_CERT
141 | PAYPAL_CERT = settings.PAYPAL_CERT
142 | CERT_ID = settings.PAYPAL_CERT_ID
143 |
144 | # Iterate through the fields and pull out the ones that have a value.
145 | plaintext = 'cert_id=%s\n' % CERT_ID
146 | for name, field in self.fields.iteritems():
147 | value = None
148 | if name in self.initial:
149 | value = self.initial[name]
150 | elif field.initial is not None:
151 | value = field.initial
152 | if value is not None:
153 | # @@@ Make this less hackish and put it in the widget.
154 | if name == "return_url":
155 | name = "return"
156 | plaintext += u'%s=%s\n' % (name, value)
157 | plaintext = plaintext.encode('utf-8')
158 |
159 | # Begin crypto weirdness.
160 | s = SMIME.SMIME()
161 | s.load_key_bio(BIO.openfile(CERT), BIO.openfile(PUB_CERT))
162 | p7 = s.sign(BIO.MemoryBuffer(plaintext), flags=SMIME.PKCS7_BINARY)
163 | x509 = X509.load_cert_bio(BIO.openfile(settings.PAYPAL_CERT))
164 | sk = X509.X509_Stack()
165 | sk.push(x509)
166 | s.set_x509_stack(sk)
167 | s.set_cipher(SMIME.Cipher('des_ede3_cbc'))
168 | tmp = BIO.MemoryBuffer()
169 | p7.write_der(tmp)
170 | p7 = s.encrypt(tmp, flags=SMIME.PKCS7_BINARY)
171 | out = BIO.MemoryBuffer()
172 | p7.write(out)
173 | return out.read()
174 |
175 | def as_p(self):
176 | return mark_safe(u"""
177 |
178 |
179 | """ % self._encrypt())
180 |
181 |
182 | class PayPalSharedSecretEncryptedPaymentsForm(PayPalEncryptedPaymentsForm):
183 | """
184 | Creates a PayPal Encrypted Payments "Buy It Now" button with a Shared Secret.
185 | Shared secrets should only be used when your IPN endpoint is on HTTPS.
186 |
187 | Adds a secret to the notify_url based on the contents of the form.
188 |
189 | """
190 | def __init__(self, *args, **kwargs):
191 | "Make the secret from the form initial data and slip it into the form."
192 | from paypal.standard.helpers import make_secret
193 | super(PayPalSharedSecretEncryptedPaymentsForm, self).__init__(self, *args, **kwargs)
194 | # @@@ Attach the secret parameter in a way that is safe for other query params.
195 | secret_param = "?secret=%s" % make_secret(self)
196 | # Initial data used in form construction overrides defaults
197 | if 'notify_url' in self.initial:
198 | self.initial['notify_url'] += secret_param
199 | else:
200 | self.fields['notify_url'].initial += secret_param
201 |
202 |
203 | class PayPalStandardBaseForm(forms.ModelForm):
204 | """Form used to receive and record PayPal IPN/PDT."""
205 | # PayPal dates have non-standard formats.
206 | time_created = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT)
207 | payment_date = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT)
208 | next_payment_date = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT)
209 | subscr_date = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT)
210 | subscr_effective = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT)
--------------------------------------------------------------------------------
/pro/fields.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from calendar import monthrange
4 | from datetime import date
5 |
6 | from django.db import models
7 | from django import forms
8 | from django.utils.translation import ugettext as _
9 |
10 | from paypal.pro.creditcard import verify_credit_card
11 |
12 |
13 | class CreditCardField(forms.CharField):
14 | """Form field for checking out a credit card."""
15 | def __init__(self, *args, **kwargs):
16 | kwargs.setdefault('max_length', 20)
17 | super(CreditCardField, self).__init__(*args, **kwargs)
18 |
19 | def clean(self, value):
20 | """Raises a ValidationError if the card is not valid and stashes card type."""
21 | self.card_type = verify_credit_card(value)
22 | if self.card_type is None:
23 | raise forms.ValidationError("Invalid credit card number.")
24 | return value
25 |
26 |
27 | # Credit Card Expiry Fields from:
28 | # http://www.djangosnippets.org/snippets/907/
29 | class CreditCardExpiryWidget(forms.MultiWidget):
30 | """MultiWidget for representing credit card expiry date."""
31 | def decompress(self, value):
32 | if value:
33 | return [value.month, value.year]
34 | else:
35 | return [None, None]
36 |
37 | def format_output(self, rendered_widgets):
38 | html = u' / '.join(rendered_widgets)
39 | return u'%s' % html
40 |
41 | class CreditCardExpiryField(forms.MultiValueField):
42 | EXP_MONTH = [(x, x) for x in xrange(1, 13)]
43 | EXP_YEAR = [(x, x) for x in xrange(date.today().year, date.today().year + 15)]
44 |
45 | default_error_messages = {
46 | 'invalid_month': u'Enter a valid month.',
47 | 'invalid_year': u'Enter a valid year.',
48 | }
49 |
50 | def __init__(self, *args, **kwargs):
51 | errors = self.default_error_messages.copy()
52 | if 'error_messages' in kwargs:
53 | errors.update(kwargs['error_messages'])
54 |
55 | fields = (
56 | forms.ChoiceField(choices=self.EXP_MONTH, error_messages={'invalid': errors['invalid_month']}),
57 | forms.ChoiceField(choices=self.EXP_YEAR, error_messages={'invalid': errors['invalid_year']}),
58 | )
59 |
60 | super(CreditCardExpiryField, self).__init__(fields, *args, **kwargs)
61 | self.widget = CreditCardExpiryWidget(widgets=[fields[0].widget, fields[1].widget])
62 |
63 | def clean(self, value):
64 | exp = super(CreditCardExpiryField, self).clean(value)
65 | if date.today() > exp:
66 | raise forms.ValidationError("The expiration date you entered is in the past.")
67 | return exp
68 |
69 | def compress(self, data_list):
70 | if data_list:
71 | if data_list[1] in forms.fields.EMPTY_VALUES:
72 | error = self.error_messages['invalid_year']
73 | raise forms.ValidationError(error)
74 | if data_list[0] in forms.fields.EMPTY_VALUES:
75 | error = self.error_messages['invalid_month']
76 | raise forms.ValidationError(error)
77 | year = int(data_list[1])
78 | month = int(data_list[0])
79 | # find last day of the month
80 | day = monthrange(year, month)[1]
81 | return date(year, month, day)
82 | return None
83 |
84 |
85 | class CreditCardCVV2Field(forms.CharField):
86 | def __init__(self, *args, **kwargs):
87 | kwargs.setdefault('max_length', 4)
88 | super(CreditCardCVV2Field, self).__init__(*args, **kwargs)
89 |
90 |
91 | # Country Field from:
92 | # http://www.djangosnippets.org/snippets/494/
93 | # http://xml.coverpages.org/country3166.html
94 | COUNTRIES = (
95 | ('US', _('United States of America')),
96 | ('CA', _('Canada')),
97 | ('AD', _('Andorra')),
98 | ('AE', _('United Arab Emirates')),
99 | ('AF', _('Afghanistan')),
100 | ('AG', _('Antigua & Barbuda')),
101 | ('AI', _('Anguilla')),
102 | ('AL', _('Albania')),
103 | ('AM', _('Armenia')),
104 | ('AN', _('Netherlands Antilles')),
105 | ('AO', _('Angola')),
106 | ('AQ', _('Antarctica')),
107 | ('AR', _('Argentina')),
108 | ('AS', _('American Samoa')),
109 | ('AT', _('Austria')),
110 | ('AU', _('Australia')),
111 | ('AW', _('Aruba')),
112 | ('AZ', _('Azerbaijan')),
113 | ('BA', _('Bosnia and Herzegovina')),
114 | ('BB', _('Barbados')),
115 | ('BD', _('Bangladesh')),
116 | ('BE', _('Belgium')),
117 | ('BF', _('Burkina Faso')),
118 | ('BG', _('Bulgaria')),
119 | ('BH', _('Bahrain')),
120 | ('BI', _('Burundi')),
121 | ('BJ', _('Benin')),
122 | ('BM', _('Bermuda')),
123 | ('BN', _('Brunei Darussalam')),
124 | ('BO', _('Bolivia')),
125 | ('BR', _('Brazil')),
126 | ('BS', _('Bahama')),
127 | ('BT', _('Bhutan')),
128 | ('BV', _('Bouvet Island')),
129 | ('BW', _('Botswana')),
130 | ('BY', _('Belarus')),
131 | ('BZ', _('Belize')),
132 | ('CC', _('Cocos (Keeling) Islands')),
133 | ('CF', _('Central African Republic')),
134 | ('CG', _('Congo')),
135 | ('CH', _('Switzerland')),
136 | ('CI', _('Ivory Coast')),
137 | ('CK', _('Cook Iislands')),
138 | ('CL', _('Chile')),
139 | ('CM', _('Cameroon')),
140 | ('CN', _('China')),
141 | ('CO', _('Colombia')),
142 | ('CR', _('Costa Rica')),
143 | ('CU', _('Cuba')),
144 | ('CV', _('Cape Verde')),
145 | ('CX', _('Christmas Island')),
146 | ('CY', _('Cyprus')),
147 | ('CZ', _('Czech Republic')),
148 | ('DE', _('Germany')),
149 | ('DJ', _('Djibouti')),
150 | ('DK', _('Denmark')),
151 | ('DM', _('Dominica')),
152 | ('DO', _('Dominican Republic')),
153 | ('DZ', _('Algeria')),
154 | ('EC', _('Ecuador')),
155 | ('EE', _('Estonia')),
156 | ('EG', _('Egypt')),
157 | ('EH', _('Western Sahara')),
158 | ('ER', _('Eritrea')),
159 | ('ES', _('Spain')),
160 | ('ET', _('Ethiopia')),
161 | ('FI', _('Finland')),
162 | ('FJ', _('Fiji')),
163 | ('FK', _('Falkland Islands (Malvinas)')),
164 | ('FM', _('Micronesia')),
165 | ('FO', _('Faroe Islands')),
166 | ('FR', _('France')),
167 | ('FX', _('France, Metropolitan')),
168 | ('GA', _('Gabon')),
169 | ('GB', _('United Kingdom (Great Britain)')),
170 | ('GD', _('Grenada')),
171 | ('GE', _('Georgia')),
172 | ('GF', _('French Guiana')),
173 | ('GH', _('Ghana')),
174 | ('GI', _('Gibraltar')),
175 | ('GL', _('Greenland')),
176 | ('GM', _('Gambia')),
177 | ('GN', _('Guinea')),
178 | ('GP', _('Guadeloupe')),
179 | ('GQ', _('Equatorial Guinea')),
180 | ('GR', _('Greece')),
181 | ('GS', _('South Georgia and the South Sandwich Islands')),
182 | ('GT', _('Guatemala')),
183 | ('GU', _('Guam')),
184 | ('GW', _('Guinea-Bissau')),
185 | ('GY', _('Guyana')),
186 | ('HK', _('Hong Kong')),
187 | ('HM', _('Heard & McDonald Islands')),
188 | ('HN', _('Honduras')),
189 | ('HR', _('Croatia')),
190 | ('HT', _('Haiti')),
191 | ('HU', _('Hungary')),
192 | ('ID', _('Indonesia')),
193 | ('IE', _('Ireland')),
194 | ('IL', _('Israel')),
195 | ('IN', _('India')),
196 | ('IO', _('British Indian Ocean Territory')),
197 | ('IQ', _('Iraq')),
198 | ('IR', _('Islamic Republic of Iran')),
199 | ('IS', _('Iceland')),
200 | ('IT', _('Italy')),
201 | ('JM', _('Jamaica')),
202 | ('JO', _('Jordan')),
203 | ('JP', _('Japan')),
204 | ('KE', _('Kenya')),
205 | ('KG', _('Kyrgyzstan')),
206 | ('KH', _('Cambodia')),
207 | ('KI', _('Kiribati')),
208 | ('KM', _('Comoros')),
209 | ('KN', _('St. Kitts and Nevis')),
210 | ('KP', _('Korea, Democratic People\'s Republic of')),
211 | ('KR', _('Korea, Republic of')),
212 | ('KW', _('Kuwait')),
213 | ('KY', _('Cayman Islands')),
214 | ('KZ', _('Kazakhstan')),
215 | ('LA', _('Lao People\'s Democratic Republic')),
216 | ('LB', _('Lebanon')),
217 | ('LC', _('Saint Lucia')),
218 | ('LI', _('Liechtenstein')),
219 | ('LK', _('Sri Lanka')),
220 | ('LR', _('Liberia')),
221 | ('LS', _('Lesotho')),
222 | ('LT', _('Lithuania')),
223 | ('LU', _('Luxembourg')),
224 | ('LV', _('Latvia')),
225 | ('LY', _('Libyan Arab Jamahiriya')),
226 | ('MA', _('Morocco')),
227 | ('MC', _('Monaco')),
228 | ('MD', _('Moldova, Republic of')),
229 | ('MG', _('Madagascar')),
230 | ('MH', _('Marshall Islands')),
231 | ('ML', _('Mali')),
232 | ('MN', _('Mongolia')),
233 | ('MM', _('Myanmar')),
234 | ('MO', _('Macau')),
235 | ('MP', _('Northern Mariana Islands')),
236 | ('MQ', _('Martinique')),
237 | ('MR', _('Mauritania')),
238 | ('MS', _('Monserrat')),
239 | ('MT', _('Malta')),
240 | ('MU', _('Mauritius')),
241 | ('MV', _('Maldives')),
242 | ('MW', _('Malawi')),
243 | ('MX', _('Mexico')),
244 | ('MY', _('Malaysia')),
245 | ('MZ', _('Mozambique')),
246 | ('NA', _('Namibia')),
247 | ('NC', _('New Caledonia')),
248 | ('NE', _('Niger')),
249 | ('NF', _('Norfolk Island')),
250 | ('NG', _('Nigeria')),
251 | ('NI', _('Nicaragua')),
252 | ('NL', _('Netherlands')),
253 | ('NO', _('Norway')),
254 | ('NP', _('Nepal')),
255 | ('NR', _('Nauru')),
256 | ('NU', _('Niue')),
257 | ('NZ', _('New Zealand')),
258 | ('OM', _('Oman')),
259 | ('PA', _('Panama')),
260 | ('PE', _('Peru')),
261 | ('PF', _('French Polynesia')),
262 | ('PG', _('Papua New Guinea')),
263 | ('PH', _('Philippines')),
264 | ('PK', _('Pakistan')),
265 | ('PL', _('Poland')),
266 | ('PM', _('St. Pierre & Miquelon')),
267 | ('PN', _('Pitcairn')),
268 | ('PR', _('Puerto Rico')),
269 | ('PT', _('Portugal')),
270 | ('PW', _('Palau')),
271 | ('PY', _('Paraguay')),
272 | ('QA', _('Qatar')),
273 | ('RE', _('Reunion')),
274 | ('RO', _('Romania')),
275 | ('RU', _('Russian Federation')),
276 | ('RW', _('Rwanda')),
277 | ('SA', _('Saudi Arabia')),
278 | ('SB', _('Solomon Islands')),
279 | ('SC', _('Seychelles')),
280 | ('SD', _('Sudan')),
281 | ('SE', _('Sweden')),
282 | ('SG', _('Singapore')),
283 | ('SH', _('St. Helena')),
284 | ('SI', _('Slovenia')),
285 | ('SJ', _('Svalbard & Jan Mayen Islands')),
286 | ('SK', _('Slovakia')),
287 | ('SL', _('Sierra Leone')),
288 | ('SM', _('San Marino')),
289 | ('SN', _('Senegal')),
290 | ('SO', _('Somalia')),
291 | ('SR', _('Suriname')),
292 | ('ST', _('Sao Tome & Principe')),
293 | ('SV', _('El Salvador')),
294 | ('SY', _('Syrian Arab Republic')),
295 | ('SZ', _('Swaziland')),
296 | ('TC', _('Turks & Caicos Islands')),
297 | ('TD', _('Chad')),
298 | ('TF', _('French Southern Territories')),
299 | ('TG', _('Togo')),
300 | ('TH', _('Thailand')),
301 | ('TJ', _('Tajikistan')),
302 | ('TK', _('Tokelau')),
303 | ('TM', _('Turkmenistan')),
304 | ('TN', _('Tunisia')),
305 | ('TO', _('Tonga')),
306 | ('TP', _('East Timor')),
307 | ('TR', _('Turkey')),
308 | ('TT', _('Trinidad & Tobago')),
309 | ('TV', _('Tuvalu')),
310 | ('TW', _('Taiwan, Province of China')),
311 | ('TZ', _('Tanzania, United Republic of')),
312 | ('UA', _('Ukraine')),
313 | ('UG', _('Uganda')),
314 | ('UM', _('United States Minor Outlying Islands')),
315 | ('UY', _('Uruguay')),
316 | ('UZ', _('Uzbekistan')),
317 | ('VA', _('Vatican City State (Holy See)')),
318 | ('VC', _('St. Vincent & the Grenadines')),
319 | ('VE', _('Venezuela')),
320 | ('VG', _('British Virgin Islands')),
321 | ('VI', _('United States Virgin Islands')),
322 | ('VN', _('Viet Nam')),
323 | ('VU', _('Vanuatu')),
324 | ('WF', _('Wallis & Futuna Islands')),
325 | ('WS', _('Samoa')),
326 | ('YE', _('Yemen')),
327 | ('YT', _('Mayotte')),
328 | ('YU', _('Yugoslavia')),
329 | ('ZA', _('South Africa')),
330 | ('ZM', _('Zambia')),
331 | ('ZR', _('Zaire')),
332 | ('ZW', _('Zimbabwe')),
333 | ('ZZ', _('Unknown or unspecified country')),
334 | )
335 |
336 | class CountryField(forms.ChoiceField):
337 | def __init__(self, *args, **kwargs):
338 | kwargs.setdefault('choices', COUNTRIES)
339 | super(CountryField, self).__init__(*args, **kwargs)
--------------------------------------------------------------------------------
/standard/models.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from django.db import models
4 | from django.conf import settings
5 | from paypal.standard.helpers import duplicate_txn_id, check_secret
6 | from paypal.standard.conf import RECEIVER_EMAIL, POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT
7 |
8 |
9 | class PayPalStandardBase(models.Model):
10 | """Meta class for common variables shared by IPN and PDT: http://tinyurl.com/cuq6sj"""
11 | # @@@ Might want to add all these one distant day.
12 | # FLAG_CODE_CHOICES = (
13 | # PAYMENT_STATUS_CHOICES = "Canceled_ Reversal Completed Denied Expired Failed Pending Processed Refunded Reversed Voided".split()
14 | # AUTH_STATUS_CHOICES = "Completed Pending Voided".split()
15 | # ADDRESS_STATUS_CHOICES = "confirmed unconfirmed".split()
16 | # PAYER_STATUS_CHOICES = "verified / unverified".split()
17 | # PAYMENT_TYPE_CHOICES = "echeck / instant.split()
18 | # PENDING_REASON = "address authorization echeck intl multi-currency unilateral upgrade verify other".split()
19 | # REASON_CODE = "chargeback guarantee buyer_complaint refund other".split()
20 | # TRANSACTION_ENTITY_CHOICES = "auth reauth order payment".split()
21 |
22 | # Transaction and Notification-Related Variables
23 | business = models.CharField(max_length=127, blank=True, help_text="Email where the money was sent.")
24 | charset=models.CharField(max_length=32, blank=True)
25 | custom = models.CharField(max_length=255, blank=True)
26 | notify_version = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
27 | parent_txn_id = models.CharField("Parent Transaction ID", max_length=19, blank=True)
28 | receiver_email = models.EmailField(max_length=127, blank=True)
29 | receiver_id = models.CharField(max_length=127, blank=True) # 258DLEHY2BDK6
30 | residence_country = models.CharField(max_length=2, blank=True)
31 | test_ipn = models.BooleanField(default=False, blank=True)
32 | txn_id = models.CharField("Transaction ID", max_length=19, blank=True, help_text="PayPal transaction ID.")
33 | txn_type = models.CharField("Transaction Type", max_length=128, blank=True, help_text="PayPal transaction type.")
34 | verify_sign = models.CharField(max_length=255, blank=True)
35 |
36 | # Buyer Information Variables
37 | address_country = models.CharField(max_length=64, blank=True)
38 | address_city = models.CharField(max_length=40, blank=True)
39 | address_country_code = models.CharField(max_length=64, blank=True, help_text="ISO 3166")
40 | address_name = models.CharField(max_length=128, blank=True)
41 | address_state = models.CharField(max_length=40, blank=True)
42 | address_status = models.CharField(max_length=11, blank=True)
43 | address_street = models.CharField(max_length=200, blank=True)
44 | address_zip = models.CharField(max_length=20, blank=True)
45 | contact_phone = models.CharField(max_length=20, blank=True)
46 | first_name = models.CharField(max_length=64, blank=True)
47 | last_name = models.CharField(max_length=64, blank=True)
48 | payer_business_name = models.CharField(max_length=127, blank=True)
49 | payer_email = models.CharField(max_length=127, blank=True)
50 | payer_id = models.CharField(max_length=13, blank=True)
51 |
52 | # Payment Information Variables
53 | auth_amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
54 | auth_exp = models.CharField(max_length=28, blank=True)
55 | auth_id = models.CharField(max_length=19, blank=True)
56 | auth_status = models.CharField(max_length=9, blank=True)
57 | exchange_rate = models.DecimalField(max_digits=64, decimal_places=16, default=0, blank=True, null=True)
58 | invoice = models.CharField(max_length=127, blank=True)
59 | item_name = models.CharField(max_length=127, blank=True)
60 | item_number = models.CharField(max_length=127, blank=True)
61 | mc_currency = models.CharField(max_length=32, default="USD", blank=True)
62 | mc_fee = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
63 | mc_gross = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
64 | mc_handling = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
65 | mc_shipping = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
66 | memo = models.CharField(max_length=255, blank=True)
67 | num_cart_items = models.IntegerField(blank=True, default=0, null=True)
68 | option_name1 = models.CharField(max_length=64, blank=True)
69 | option_name2 = models.CharField(max_length=64, blank=True)
70 | payer_status = models.CharField(max_length=10, blank=True)
71 | payment_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
72 | payment_gross = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
73 | payment_status = models.CharField(max_length=9, blank=True)
74 | payment_type = models.CharField(max_length=7, blank=True)
75 | pending_reason = models.CharField(max_length=14, blank=True)
76 | protection_eligibility=models.CharField(max_length=32, blank=True)
77 | quantity = models.IntegerField(blank=True, default=1, null=True)
78 | reason_code = models.CharField(max_length=15, blank=True)
79 | remaining_settle = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
80 | settle_amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
81 | settle_currency = models.CharField(max_length=32, blank=True)
82 | shipping = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
83 | shipping_method = models.CharField(max_length=255, blank=True)
84 | tax = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
85 | transaction_entity = models.CharField(max_length=7, blank=True)
86 |
87 | # Auction Variables
88 | auction_buyer_id = models.CharField(max_length=64, blank=True)
89 | auction_closing_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
90 | auction_multi_item = models.IntegerField(blank=True, default=0, null=True)
91 | for_auction = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
92 |
93 | # Recurring Payments Variables
94 | amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
95 | amount_per_cycle = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
96 | initial_payment_amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
97 | next_payment_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
98 | outstanding_balance = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
99 | payment_cycle= models.CharField(max_length=32, blank=True) #Monthly
100 | period_type = models.CharField(max_length=32, blank=True)
101 | product_name = models.CharField(max_length=128, blank=True)
102 | product_type= models.CharField(max_length=128, blank=True)
103 | profile_status = models.CharField(max_length=32, blank=True)
104 | recurring_payment_id = models.CharField(max_length=128, blank=True) # I-FA4XVST722B9
105 | rp_invoice_id= models.CharField(max_length=127, blank=True) # 1335-7816-2936-1451
106 | time_created = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
107 |
108 | # Subscription Variables
109 | amount1 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
110 | amount2 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
111 | amount3 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
112 | mc_amount1 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
113 | mc_amount2 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
114 | mc_amount3 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
115 | password = models.CharField(max_length=24, blank=True)
116 | period1 = models.CharField(max_length=32, blank=True)
117 | period2 = models.CharField(max_length=32, blank=True)
118 | period3 = models.CharField(max_length=32, blank=True)
119 | reattempt = models.CharField(max_length=1, blank=True)
120 | recur_times = models.IntegerField(blank=True, default=0, null=True)
121 | recurring = models.CharField(max_length=1, blank=True)
122 | retry_at = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
123 | subscr_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
124 | subscr_effective = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
125 | subscr_id = models.CharField(max_length=19, blank=True)
126 | username = models.CharField(max_length=64, blank=True)
127 |
128 | # Dispute Resolution Variables
129 | case_creation_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
130 | case_id = models.CharField(max_length=14, blank=True)
131 | case_type = models.CharField(max_length=24, blank=True)
132 |
133 | # Variables not categorized
134 | receipt_id= models.CharField(max_length=64, blank=True) # 1335-7816-2936-1451
135 | currency_code = models.CharField(max_length=32, default="USD", blank=True)
136 | handling_amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
137 | transaction_subject = models.CharField(max_length=255, blank=True)
138 |
139 | # @@@ Mass Pay Variables (Not Implemented, needs a separate model, for each transaction x)
140 | # fraud_managment_pending_filters_x = models.CharField(max_length=255, blank=True)
141 | # option_selection1_x = models.CharField(max_length=200, blank=True)
142 | # option_selection2_x = models.CharField(max_length=200, blank=True)
143 | # masspay_txn_id_x = models.CharField(max_length=19, blank=True)
144 | # mc_currency_x = models.CharField(max_length=32, default="USD", blank=True)
145 | # mc_fee_x = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
146 | # mc_gross_x = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
147 | # mc_handlingx = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
148 | # payment_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
149 | # payment_status = models.CharField(max_length=9, blank=True)
150 | # reason_code = models.CharField(max_length=15, blank=True)
151 | # receiver_email_x = models.EmailField(max_length=127, blank=True)
152 | # status_x = models.CharField(max_length=9, blank=True)
153 | # unique_id_x = models.CharField(max_length=13, blank=True)
154 |
155 | # Non-PayPal Variables - full IPN/PDT query and time fields.
156 | ipaddress = models.IPAddressField(blank=True)
157 | flag = models.BooleanField(default=False, blank=True)
158 | flag_code = models.CharField(max_length=16, blank=True)
159 | flag_info = models.TextField(blank=True)
160 | query = models.TextField(blank=True) # What we sent to PayPal.
161 | response = models.TextField(blank=True) # What we got back.
162 | created_at = models.DateTimeField(auto_now_add=True)
163 | updated_at = models.DateTimeField(auto_now=True)
164 |
165 | class Meta:
166 | abstract = True
167 |
168 | def __unicode__(self):
169 | if self.is_transaction():
170 | return self.format % ("Transaction", self.txn_id)
171 | else:
172 | return self.format % ("Recurring", self.recurring_payment_id)
173 |
174 | def is_transaction(self):
175 | return len(self.txn_id) > 0
176 |
177 | def is_recurring(self):
178 | return len(self.recurring_payment_id) > 0
179 |
180 | def is_subscription_cancellation(self):
181 | return self.txn_type == "subscr_cancel"
182 |
183 | def is_subscription_end_of_term(self):
184 | return self.txn_type == "subscr_eot"
185 |
186 | def is_subscription_modified(self):
187 | return self.txn_type == "subscr_modify"
188 |
189 | def is_subscription_signup(self):
190 | return self.txn_type == "subscr_signup"
191 |
192 | def set_flag(self, info, code=None):
193 | """Sets a flag on the transaction and also sets a reason."""
194 | self.flag = True
195 | self.flag_info += info
196 | if code is not None:
197 | self.flag_code = code
198 |
199 | def verify(self, item_check_callable=None):
200 | """
201 | Verifies an IPN and a PDT.
202 | Checks for obvious signs of weirdness in the payment and flags appropriately.
203 |
204 | Provide a callable that takes an instance of this class as a parameter and returns
205 | a tuple (False, None) if the item is valid. Should return (True, "reason") if the
206 | item isn't valid. Strange but backward compatible :) This function should check
207 | that `mc_gross`, `mc_currency` `item_name` and `item_number` are all correct.
208 |
209 | """
210 | self.response = self._postback()
211 | self._verify_postback()
212 | if not self.flag:
213 | if self.is_transaction():
214 | if self.payment_status != "Completed":
215 | self.set_flag("Invalid payment_status. (%s)" % self.payment_status)
216 | if duplicate_txn_id(self):
217 | self.set_flag("Duplicate txn_id. (%s)" % self.txn_id)
218 | if self.receiver_email != RECEIVER_EMAIL:
219 | self.set_flag("Invalid receiver_email. (%s)" % self.receiver_email)
220 | if callable(item_check_callable):
221 | flag, reason = item_check_callable(self)
222 | if flag:
223 | self.set_flag(reason)
224 | else:
225 | # @@@ Run a different series of checks on recurring payments.
226 | pass
227 |
228 | self.save()
229 | self.send_signals()
230 |
231 | def verify_secret(self, form_instance, secret):
232 | """Verifies an IPN payment over SSL using EWP."""
233 | if not check_secret(form_instance, secret):
234 | self.set_flag("Invalid secret. (%s)") % secret
235 | self.save()
236 | self.send_signals()
237 |
238 | def get_endpoint(self):
239 | """Set Sandbox endpoint if the test variable is present."""
240 | if self.test_ipn:
241 | return SANDBOX_POSTBACK_ENDPOINT
242 | else:
243 | return POSTBACK_ENDPOINT
244 |
245 | def initialize(self, request):
246 | """Store the data we'll need to make the postback from the request object."""
247 | self.query = getattr(request, request.method).urlencode()
248 | self.ipaddress = request.META.get('REMOTE_ADDR', '')
249 |
250 | def send_signals(self):
251 | """After a transaction is completed use this to send success/fail signals"""
252 | raise NotImplementedError
253 |
254 | def _postback(self):
255 | """Perform postback to PayPal and store the response in self.response."""
256 | raise NotImplementedError
257 |
258 | def _verify_postback(self):
259 | """Check self.response is valid andcall self.set_flag if there is an error."""
260 | raise NotImplementedError
--------------------------------------------------------------------------------
/standard/ipn/migrations/0001_first_migration.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from django.db import models
3 | from south.db import db
4 | from paypal.standard.ipn.models import *
5 |
6 |
7 | class Migration:
8 | def forwards(self, orm):
9 | # Adding model 'PayPalIPN'
10 | db.create_table('paypal_ipn', (
11 | ('id', models.AutoField(primary_key=True)),
12 | ('business', models.CharField(max_length=127, blank=True)),
13 | ('charset', models.CharField(max_length=32, blank=True)),
14 | ('custom', models.CharField(max_length=255, blank=True)),
15 | ('notify_version', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
16 | ('parent_txn_id', models.CharField("Parent Transaction ID", max_length=19, blank=True)),
17 | ('receiver_email', models.EmailField(max_length=127, blank=True)),
18 | ('receiver_id', models.CharField(max_length=127, blank=True)),
19 | ('residence_country', models.CharField(max_length=2, blank=True)),
20 | ('test_ipn', models.BooleanField(default=False, blank=True)),
21 | ('txn_id', models.CharField("Transaction ID", max_length=19, blank=True)),
22 | ('txn_type', models.CharField("Transaction Type", max_length=128, blank=True)),
23 | ('verify_sign', models.CharField(max_length=255, blank=True)),
24 | ('address_country', models.CharField(max_length=64, blank=True)),
25 | ('address_city', models.CharField(max_length=40, blank=True)),
26 | ('address_country_code', models.CharField(max_length=64, blank=True)),
27 | ('address_name', models.CharField(max_length=128, blank=True)),
28 | ('address_state', models.CharField(max_length=40, blank=True)),
29 | ('address_status', models.CharField(max_length=11, blank=True)),
30 | ('address_street', models.CharField(max_length=200, blank=True)),
31 | ('address_zip', models.CharField(max_length=20, blank=True)),
32 | ('contact_phone', models.CharField(max_length=20, blank=True)),
33 | ('first_name', models.CharField(max_length=64, blank=True)),
34 | ('last_name', models.CharField(max_length=64, blank=True)),
35 | ('payer_business_name', models.CharField(max_length=127, blank=True)),
36 | ('payer_email', models.CharField(max_length=127, blank=True)),
37 | ('payer_id', models.CharField(max_length=13, blank=True)),
38 | ('auth_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
39 | ('auth_exp', models.CharField(max_length=28, blank=True)),
40 | ('auth_id', models.CharField(max_length=19, blank=True)),
41 | ('auth_status', models.CharField(max_length=9, blank=True)),
42 | ('exchange_rate', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=16, blank=True)),
43 | ('invoice', models.CharField(max_length=127, blank=True)),
44 | ('item_name', models.CharField(max_length=127, blank=True)),
45 | ('item_number', models.CharField(max_length=127, blank=True)),
46 | ('mc_currency', models.CharField(default='USD', max_length=32, blank=True)),
47 | ('mc_fee', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
48 | ('mc_gross', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
49 | ('mc_handling', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
50 | ('mc_shipping', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
51 | ('memo', models.CharField(max_length=255, blank=True)),
52 | ('num_cart_items', models.IntegerField(default=0, null=True, blank=True)),
53 | ('option_name1', models.CharField(max_length=64, blank=True)),
54 | ('option_name2', models.CharField(max_length=64, blank=True)),
55 | ('payer_status', models.CharField(max_length=10, blank=True)),
56 | ('payment_date', models.DateTimeField(null=True, blank=True)),
57 | ('payment_gross', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
58 | ('payment_status', models.CharField(max_length=9, blank=True)),
59 | ('payment_type', models.CharField(max_length=7, blank=True)),
60 | ('pending_reason', models.CharField(max_length=14, blank=True)),
61 | ('protection_eligibility', models.CharField(max_length=32, blank=True)),
62 | ('quantity', models.IntegerField(default=1, null=True, blank=True)),
63 | ('reason_code', models.CharField(max_length=15, blank=True)),
64 | ('remaining_settle', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
65 | ('settle_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
66 | ('settle_currency', models.CharField(max_length=32, blank=True)),
67 | ('shipping', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
68 | ('shipping_method', models.CharField(max_length=255, blank=True)),
69 | ('tax', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
70 | ('transaction_entity', models.CharField(max_length=7, blank=True)),
71 | ('auction_buyer_id', models.CharField(max_length=64, blank=True)),
72 | ('auction_closing_date', models.DateTimeField(null=True, blank=True)),
73 | ('auction_multi_item', models.IntegerField(default=0, null=True, blank=True)),
74 | ('for_auction', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
75 | ('amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
76 | ('amount_per_cycle', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
77 | ('initial_payment_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
78 | ('next_payment_date', models.DateTimeField(null=True, blank=True)),
79 | ('outstanding_balance', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
80 | ('payment_cycle', models.CharField(max_length=32, blank=True)),
81 | ('period_type', models.CharField(max_length=32, blank=True)),
82 | ('product_name', models.CharField(max_length=128, blank=True)),
83 | ('product_type', models.CharField(max_length=128, blank=True)),
84 | ('profile_status', models.CharField(max_length=32, blank=True)),
85 | ('recurring_payment_id', models.CharField(max_length=128, blank=True)),
86 | ('rp_invoice_id', models.CharField(max_length=127, blank=True)),
87 | ('time_created', models.DateTimeField(null=True, blank=True)),
88 | ('amount1', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
89 | ('amount2', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
90 | ('amount3', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
91 | ('mc_amount1', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
92 | ('mc_amount2', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
93 | ('mc_amount3', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
94 | ('password', models.CharField(max_length=24, blank=True)),
95 | ('period1', models.CharField(max_length=32, blank=True)),
96 | ('period2', models.CharField(max_length=32, blank=True)),
97 | ('period3', models.CharField(max_length=32, blank=True)),
98 | ('reattempt', models.CharField(max_length=1, blank=True)),
99 | ('recur_times', models.IntegerField(default=0, null=True, blank=True)),
100 | ('recurring', models.CharField(max_length=1, blank=True)),
101 | ('retry_at', models.DateTimeField(null=True, blank=True)),
102 | ('subscr_date', models.DateTimeField(null=True, blank=True)),
103 | ('subscr_effective', models.DateTimeField(null=True, blank=True)),
104 | ('subscr_id', models.CharField(max_length=19, blank=True)),
105 | ('username', models.CharField(max_length=64, blank=True)),
106 | ('case_creation_date', models.DateTimeField(null=True, blank=True)),
107 | ('case_id', models.CharField(max_length=14, blank=True)),
108 | ('case_type', models.CharField(max_length=24, blank=True)),
109 | ('receipt_id', models.CharField(max_length=64, blank=True)),
110 | ('currency_code', models.CharField(default='USD', max_length=32, blank=True)),
111 | ('handling_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
112 | ('transaction_subject', models.CharField(max_length=255, blank=True)),
113 | ('ipaddress', models.IPAddressField(blank=True)),
114 | ('flag', models.BooleanField(default=False, blank=True)),
115 | ('flag_code', models.CharField(max_length=16, blank=True)),
116 | ('flag_info', models.TextField(blank=True)),
117 | ('query', models.TextField(blank=True)),
118 | ('response', models.TextField(blank=True)),
119 | ('created_at', models.DateTimeField(auto_now_add=True)),
120 | ('updated_at', models.DateTimeField(auto_now=True)),
121 | ('from_view', models.CharField(max_length=6, null=True, blank=True)),
122 | ))
123 | db.send_create_signal('ipn', ['PayPalIPN'])
124 |
125 | def backwards(self, orm):
126 | # Deleting model 'PayPalIPN'
127 | db.delete_table('paypal_ipn')
128 |
129 |
130 | models = {
131 | 'ipn.paypalipn': {
132 | 'Meta': {'db_table': '"paypal_ipn"'},
133 | 'address_city': ('models.CharField', [], {'max_length': '40', 'blank': 'True'}),
134 | 'address_country': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
135 | 'address_country_code': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
136 | 'address_name': ('models.CharField', [], {'max_length': '128', 'blank': 'True'}),
137 | 'address_state': ('models.CharField', [], {'max_length': '40', 'blank': 'True'}),
138 | 'address_status': ('models.CharField', [], {'max_length': '11', 'blank': 'True'}),
139 | 'address_street': ('models.CharField', [], {'max_length': '200', 'blank': 'True'}),
140 | 'address_zip': ('models.CharField', [], {'max_length': '20', 'blank': 'True'}),
141 | 'amount': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
142 | 'amount1': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
143 | 'amount2': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
144 | 'amount3': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
145 | 'amount_per_cycle': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
146 | 'auction_buyer_id': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
147 | 'auction_closing_date': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
148 | 'auction_multi_item': ('models.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}),
149 | 'auth_amount': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
150 | 'auth_exp': ('models.CharField', [], {'max_length': '28', 'blank': 'True'}),
151 | 'auth_id': ('models.CharField', [], {'max_length': '19', 'blank': 'True'}),
152 | 'auth_status': ('models.CharField', [], {'max_length': '9', 'blank': 'True'}),
153 | 'business': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
154 | 'case_creation_date': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
155 | 'case_id': ('models.CharField', [], {'max_length': '14', 'blank': 'True'}),
156 | 'case_type': ('models.CharField', [], {'max_length': '24', 'blank': 'True'}),
157 | 'charset': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
158 | 'contact_phone': ('models.CharField', [], {'max_length': '20', 'blank': 'True'}),
159 | 'created_at': ('models.DateTimeField', [], {'auto_now_add': 'True'}),
160 | 'currency_code': ('models.CharField', [], {'default': "'USD'", 'max_length': '32', 'blank': 'True'}),
161 | 'custom': ('models.CharField', [], {'max_length': '255', 'blank': 'True'}),
162 | 'exchange_rate': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '16', 'blank': 'True'}),
163 | 'first_name': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
164 | 'flag': ('models.BooleanField', [], {'default': 'False', 'blank': 'True'}),
165 | 'flag_code': ('models.CharField', [], {'max_length': '16', 'blank': 'True'}),
166 | 'flag_info': ('models.TextField', [], {'blank': 'True'}),
167 | 'for_auction': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
168 | 'from_view': ('models.CharField', [], {'max_length': '6', 'null': 'True', 'blank': 'True'}),
169 | 'handling_amount': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
170 | 'id': ('models.AutoField', [], {'primary_key': 'True'}),
171 | 'initial_payment_amount': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
172 | 'invoice': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
173 | 'ipaddress': ('models.IPAddressField', [], {'blank': 'True'}),
174 | 'item_name': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
175 | 'item_number': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
176 | 'last_name': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
177 | 'mc_amount1': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
178 | 'mc_amount2': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
179 | 'mc_amount3': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
180 | 'mc_currency': ('models.CharField', [], {'default': "'USD'", 'max_length': '32', 'blank': 'True'}),
181 | 'mc_fee': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
182 | 'mc_gross': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
183 | 'mc_handling': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
184 | 'mc_shipping': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
185 | 'memo': ('models.CharField', [], {'max_length': '255', 'blank': 'True'}),
186 | 'next_payment_date': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
187 | 'notify_version': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
188 | 'num_cart_items': ('models.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}),
189 | 'option_name1': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
190 | 'option_name2': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
191 | 'outstanding_balance': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
192 | 'parent_txn_id': ('models.CharField', ['"Parent Transaction ID"'], {'max_length': '19', 'blank': 'True'}),
193 | 'password': ('models.CharField', [], {'max_length': '24', 'blank': 'True'}),
194 | 'payer_business_name': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
195 | 'payer_email': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
196 | 'payer_id': ('models.CharField', [], {'max_length': '13', 'blank': 'True'}),
197 | 'payer_status': ('models.CharField', [], {'max_length': '10', 'blank': 'True'}),
198 | 'payment_cycle': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
199 | 'payment_date': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
200 | 'payment_gross': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
201 | 'payment_status': ('models.CharField', [], {'max_length': '9', 'blank': 'True'}),
202 | 'payment_type': ('models.CharField', [], {'max_length': '7', 'blank': 'True'}),
203 | 'pending_reason': ('models.CharField', [], {'max_length': '14', 'blank': 'True'}),
204 | 'period1': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
205 | 'period2': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
206 | 'period3': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
207 | 'period_type': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
208 | 'product_name': ('models.CharField', [], {'max_length': '128', 'blank': 'True'}),
209 | 'product_type': ('models.CharField', [], {'max_length': '128', 'blank': 'True'}),
210 | 'profile_status': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
211 | 'protection_eligibility': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
212 | 'quantity': ('models.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
213 | 'query': ('models.TextField', [], {'blank': 'True'}),
214 | 'reason_code': ('models.CharField', [], {'max_length': '15', 'blank': 'True'}),
215 | 'reattempt': ('models.CharField', [], {'max_length': '1', 'blank': 'True'}),
216 | 'receipt_id': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
217 | 'receiver_email': ('models.EmailField', [], {'max_length': '127', 'blank': 'True'}),
218 | 'receiver_id': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
219 | 'recur_times': ('models.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}),
220 | 'recurring': ('models.CharField', [], {'max_length': '1', 'blank': 'True'}),
221 | 'recurring_payment_id': ('models.CharField', [], {'max_length': '128', 'blank': 'True'}),
222 | 'remaining_settle': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
223 | 'residence_country': ('models.CharField', [], {'max_length': '2', 'blank': 'True'}),
224 | 'response': ('models.TextField', [], {'blank': 'True'}),
225 | 'retry_at': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
226 | 'rp_invoice_id': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
227 | 'settle_amount': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
228 | 'settle_currency': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
229 | 'shipping': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
230 | 'shipping_method': ('models.CharField', [], {'max_length': '255', 'blank': 'True'}),
231 | 'subscr_date': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
232 | 'subscr_effective': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
233 | 'subscr_id': ('models.CharField', [], {'max_length': '19', 'blank': 'True'}),
234 | 'tax': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
235 | 'test_ipn': ('models.BooleanField', [], {'default': 'False', 'blank': 'True'}),
236 | 'time_created': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
237 | 'transaction_entity': ('models.CharField', [], {'max_length': '7', 'blank': 'True'}),
238 | 'transaction_subject': ('models.CharField', [], {'max_length': '255', 'blank': 'True'}),
239 | 'txn_id': ('models.CharField', ['"Transaction ID"'], {'max_length': '19', 'blank': 'True'}),
240 | 'txn_type': ('models.CharField', ['"Transaction Type"'], {'max_length': '128', 'blank': 'True'}),
241 | 'updated_at': ('models.DateTimeField', [], {'auto_now': 'True'}),
242 | 'username': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
243 | 'verify_sign': ('models.CharField', [], {'max_length': '255', 'blank': 'True'})
244 | }
245 | }
246 |
247 | complete_apps = ['ipn']
--------------------------------------------------------------------------------
/standard/pdt/migrations/0001_first_migration.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from south.db import db
4 | from django.db import models
5 | from paypal.standard.pdt.models import *
6 |
7 | class Migration:
8 |
9 | def forwards(self, orm):
10 |
11 | # Adding model 'PayPalPDT'
12 | db.create_table('paypal_pdt', (
13 | ('id', models.AutoField(primary_key=True)),
14 | ('business', models.CharField(max_length=127, blank=True)),
15 | ('charset', models.CharField(max_length=32, blank=True)),
16 | ('custom', models.CharField(max_length=255, blank=True)),
17 | ('notify_version', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
18 | ('parent_txn_id', models.CharField("Parent Transaction ID", max_length=19, blank=True)),
19 | ('receiver_email', models.EmailField(max_length=127, blank=True)),
20 | ('receiver_id', models.CharField(max_length=127, blank=True)),
21 | ('residence_country', models.CharField(max_length=2, blank=True)),
22 | ('test_ipn', models.BooleanField(default=False, blank=True)),
23 | ('txn_id', models.CharField("Transaction ID", max_length=19, blank=True)),
24 | ('txn_type', models.CharField("Transaction Type", max_length=128, blank=True)),
25 | ('verify_sign', models.CharField(max_length=255, blank=True)),
26 | ('address_country', models.CharField(max_length=64, blank=True)),
27 | ('address_city', models.CharField(max_length=40, blank=True)),
28 | ('address_country_code', models.CharField(max_length=64, blank=True)),
29 | ('address_name', models.CharField(max_length=128, blank=True)),
30 | ('address_state', models.CharField(max_length=40, blank=True)),
31 | ('address_status', models.CharField(max_length=11, blank=True)),
32 | ('address_street', models.CharField(max_length=200, blank=True)),
33 | ('address_zip', models.CharField(max_length=20, blank=True)),
34 | ('contact_phone', models.CharField(max_length=20, blank=True)),
35 | ('first_name', models.CharField(max_length=64, blank=True)),
36 | ('last_name', models.CharField(max_length=64, blank=True)),
37 | ('payer_business_name', models.CharField(max_length=127, blank=True)),
38 | ('payer_email', models.CharField(max_length=127, blank=True)),
39 | ('payer_id', models.CharField(max_length=13, blank=True)),
40 | ('auth_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
41 | ('auth_exp', models.CharField(max_length=28, blank=True)),
42 | ('auth_id', models.CharField(max_length=19, blank=True)),
43 | ('auth_status', models.CharField(max_length=9, blank=True)),
44 | ('exchange_rate', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=16, blank=True)),
45 | ('invoice', models.CharField(max_length=127, blank=True)),
46 | ('item_name', models.CharField(max_length=127, blank=True)),
47 | ('item_number', models.CharField(max_length=127, blank=True)),
48 | ('mc_currency', models.CharField(default='USD', max_length=32, blank=True)),
49 | ('mc_fee', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
50 | ('mc_gross', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
51 | ('mc_handling', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
52 | ('mc_shipping', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
53 | ('memo', models.CharField(max_length=255, blank=True)),
54 | ('num_cart_items', models.IntegerField(default=0, null=True, blank=True)),
55 | ('option_name1', models.CharField(max_length=64, blank=True)),
56 | ('option_name2', models.CharField(max_length=64, blank=True)),
57 | ('payer_status', models.CharField(max_length=10, blank=True)),
58 | ('payment_date', models.DateTimeField(null=True, blank=True)),
59 | ('payment_gross', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
60 | ('payment_status', models.CharField(max_length=9, blank=True)),
61 | ('payment_type', models.CharField(max_length=7, blank=True)),
62 | ('pending_reason', models.CharField(max_length=14, blank=True)),
63 | ('protection_eligibility', models.CharField(max_length=32, blank=True)),
64 | ('quantity', models.IntegerField(default=1, null=True, blank=True)),
65 | ('reason_code', models.CharField(max_length=15, blank=True)),
66 | ('remaining_settle', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
67 | ('settle_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
68 | ('settle_currency', models.CharField(max_length=32, blank=True)),
69 | ('shipping', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
70 | ('shipping_method', models.CharField(max_length=255, blank=True)),
71 | ('tax', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
72 | ('transaction_entity', models.CharField(max_length=7, blank=True)),
73 | ('auction_buyer_id', models.CharField(max_length=64, blank=True)),
74 | ('auction_closing_date', models.DateTimeField(null=True, blank=True)),
75 | ('auction_multi_item', models.IntegerField(default=0, null=True, blank=True)),
76 | ('for_auction', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
77 | ('amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
78 | ('amount_per_cycle', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
79 | ('initial_payment_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
80 | ('next_payment_date', models.DateTimeField(null=True, blank=True)),
81 | ('outstanding_balance', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
82 | ('payment_cycle', models.CharField(max_length=32, blank=True)),
83 | ('period_type', models.CharField(max_length=32, blank=True)),
84 | ('product_name', models.CharField(max_length=128, blank=True)),
85 | ('product_type', models.CharField(max_length=128, blank=True)),
86 | ('profile_status', models.CharField(max_length=32, blank=True)),
87 | ('recurring_payment_id', models.CharField(max_length=128, blank=True)),
88 | ('rp_invoice_id', models.CharField(max_length=127, blank=True)),
89 | ('time_created', models.DateTimeField(null=True, blank=True)),
90 | ('amount1', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
91 | ('amount2', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
92 | ('amount3', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
93 | ('mc_amount1', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
94 | ('mc_amount2', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
95 | ('mc_amount3', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
96 | ('password', models.CharField(max_length=24, blank=True)),
97 | ('period1', models.CharField(max_length=32, blank=True)),
98 | ('period2', models.CharField(max_length=32, blank=True)),
99 | ('period3', models.CharField(max_length=32, blank=True)),
100 | ('reattempt', models.CharField(max_length=1, blank=True)),
101 | ('recur_times', models.IntegerField(default=0, null=True, blank=True)),
102 | ('recurring', models.CharField(max_length=1, blank=True)),
103 | ('retry_at', models.DateTimeField(null=True, blank=True)),
104 | ('subscr_date', models.DateTimeField(null=True, blank=True)),
105 | ('subscr_effective', models.DateTimeField(null=True, blank=True)),
106 | ('subscr_id', models.CharField(max_length=19, blank=True)),
107 | ('username', models.CharField(max_length=64, blank=True)),
108 | ('case_creation_date', models.DateTimeField(null=True, blank=True)),
109 | ('case_id', models.CharField(max_length=14, blank=True)),
110 | ('case_type', models.CharField(max_length=24, blank=True)),
111 | ('receipt_id', models.CharField(max_length=64, blank=True)),
112 | ('currency_code', models.CharField(default='USD', max_length=32, blank=True)),
113 | ('handling_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
114 | ('transaction_subject', models.CharField(max_length=255, blank=True)),
115 | ('ipaddress', models.IPAddressField(blank=True)),
116 | ('flag', models.BooleanField(default=False, blank=True)),
117 | ('flag_code', models.CharField(max_length=16, blank=True)),
118 | ('flag_info', models.TextField(blank=True)),
119 | ('query', models.TextField(blank=True)),
120 | ('response', models.TextField(blank=True)),
121 | ('created_at', models.DateTimeField(auto_now_add=True)),
122 | ('updated_at', models.DateTimeField(auto_now=True)),
123 | ('from_view', models.CharField(max_length=6, null=True, blank=True)),
124 | ('amt', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
125 | ('cm', models.CharField(max_length=255, blank=True)),
126 | ('sig', models.CharField(max_length=255, blank=True)),
127 | ('tx', models.CharField(max_length=255, blank=True)),
128 | ('st', models.CharField(max_length=32, blank=True)),
129 | ))
130 | db.send_create_signal('pdt', ['PayPalPDT'])
131 |
132 |
133 |
134 | def backwards(self, orm):
135 |
136 | # Deleting model 'PayPalPDT'
137 | db.delete_table('paypal_pdt')
138 |
139 |
140 |
141 | models = {
142 | 'pdt.paypalpdt': {
143 | 'Meta': {'db_table': '"paypal_pdt"'},
144 | 'address_city': ('models.CharField', [], {'max_length': '40', 'blank': 'True'}),
145 | 'address_country': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
146 | 'address_country_code': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
147 | 'address_name': ('models.CharField', [], {'max_length': '128', 'blank': 'True'}),
148 | 'address_state': ('models.CharField', [], {'max_length': '40', 'blank': 'True'}),
149 | 'address_status': ('models.CharField', [], {'max_length': '11', 'blank': 'True'}),
150 | 'address_street': ('models.CharField', [], {'max_length': '200', 'blank': 'True'}),
151 | 'address_zip': ('models.CharField', [], {'max_length': '20', 'blank': 'True'}),
152 | 'amount': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
153 | 'amount1': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
154 | 'amount2': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
155 | 'amount3': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
156 | 'amount_per_cycle': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
157 | 'amt': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
158 | 'auction_buyer_id': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
159 | 'auction_closing_date': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
160 | 'auction_multi_item': ('models.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}),
161 | 'auth_amount': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
162 | 'auth_exp': ('models.CharField', [], {'max_length': '28', 'blank': 'True'}),
163 | 'auth_id': ('models.CharField', [], {'max_length': '19', 'blank': 'True'}),
164 | 'auth_status': ('models.CharField', [], {'max_length': '9', 'blank': 'True'}),
165 | 'business': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
166 | 'case_creation_date': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
167 | 'case_id': ('models.CharField', [], {'max_length': '14', 'blank': 'True'}),
168 | 'case_type': ('models.CharField', [], {'max_length': '24', 'blank': 'True'}),
169 | 'charset': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
170 | 'cm': ('models.CharField', [], {'max_length': '255', 'blank': 'True'}),
171 | 'contact_phone': ('models.CharField', [], {'max_length': '20', 'blank': 'True'}),
172 | 'created_at': ('models.DateTimeField', [], {'auto_now_add': 'True'}),
173 | 'currency_code': ('models.CharField', [], {'default': "'USD'", 'max_length': '32', 'blank': 'True'}),
174 | 'custom': ('models.CharField', [], {'max_length': '255', 'blank': 'True'}),
175 | 'exchange_rate': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '16', 'blank': 'True'}),
176 | 'first_name': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
177 | 'flag': ('models.BooleanField', [], {'default': 'False', 'blank': 'True'}),
178 | 'flag_code': ('models.CharField', [], {'max_length': '16', 'blank': 'True'}),
179 | 'flag_info': ('models.TextField', [], {'blank': 'True'}),
180 | 'for_auction': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
181 | 'from_view': ('models.CharField', [], {'max_length': '6', 'null': 'True', 'blank': 'True'}),
182 | 'handling_amount': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
183 | 'id': ('models.AutoField', [], {'primary_key': 'True'}),
184 | 'initial_payment_amount': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
185 | 'invoice': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
186 | 'ipaddress': ('models.IPAddressField', [], {'blank': 'True'}),
187 | 'item_name': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
188 | 'item_number': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
189 | 'last_name': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
190 | 'mc_amount1': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
191 | 'mc_amount2': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
192 | 'mc_amount3': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
193 | 'mc_currency': ('models.CharField', [], {'default': "'USD'", 'max_length': '32', 'blank': 'True'}),
194 | 'mc_fee': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
195 | 'mc_gross': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
196 | 'mc_handling': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
197 | 'mc_shipping': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
198 | 'memo': ('models.CharField', [], {'max_length': '255', 'blank': 'True'}),
199 | 'next_payment_date': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
200 | 'notify_version': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
201 | 'num_cart_items': ('models.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}),
202 | 'option_name1': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
203 | 'option_name2': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
204 | 'outstanding_balance': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
205 | 'parent_txn_id': ('models.CharField', ['"Parent Transaction ID"'], {'max_length': '19', 'blank': 'True'}),
206 | 'password': ('models.CharField', [], {'max_length': '24', 'blank': 'True'}),
207 | 'payer_business_name': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
208 | 'payer_email': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
209 | 'payer_id': ('models.CharField', [], {'max_length': '13', 'blank': 'True'}),
210 | 'payer_status': ('models.CharField', [], {'max_length': '10', 'blank': 'True'}),
211 | 'payment_cycle': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
212 | 'payment_date': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
213 | 'payment_gross': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
214 | 'payment_status': ('models.CharField', [], {'max_length': '9', 'blank': 'True'}),
215 | 'payment_type': ('models.CharField', [], {'max_length': '7', 'blank': 'True'}),
216 | 'pending_reason': ('models.CharField', [], {'max_length': '14', 'blank': 'True'}),
217 | 'period1': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
218 | 'period2': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
219 | 'period3': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
220 | 'period_type': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
221 | 'product_name': ('models.CharField', [], {'max_length': '128', 'blank': 'True'}),
222 | 'product_type': ('models.CharField', [], {'max_length': '128', 'blank': 'True'}),
223 | 'profile_status': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
224 | 'protection_eligibility': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
225 | 'quantity': ('models.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}),
226 | 'query': ('models.TextField', [], {'blank': 'True'}),
227 | 'reason_code': ('models.CharField', [], {'max_length': '15', 'blank': 'True'}),
228 | 'reattempt': ('models.CharField', [], {'max_length': '1', 'blank': 'True'}),
229 | 'receipt_id': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
230 | 'receiver_email': ('models.EmailField', [], {'max_length': '127', 'blank': 'True'}),
231 | 'receiver_id': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
232 | 'recur_times': ('models.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}),
233 | 'recurring': ('models.CharField', [], {'max_length': '1', 'blank': 'True'}),
234 | 'recurring_payment_id': ('models.CharField', [], {'max_length': '128', 'blank': 'True'}),
235 | 'remaining_settle': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
236 | 'residence_country': ('models.CharField', [], {'max_length': '2', 'blank': 'True'}),
237 | 'response': ('models.TextField', [], {'blank': 'True'}),
238 | 'retry_at': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
239 | 'rp_invoice_id': ('models.CharField', [], {'max_length': '127', 'blank': 'True'}),
240 | 'settle_amount': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
241 | 'settle_currency': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
242 | 'shipping': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
243 | 'shipping_method': ('models.CharField', [], {'max_length': '255', 'blank': 'True'}),
244 | 'sig': ('models.CharField', [], {'max_length': '255', 'blank': 'True'}),
245 | 'st': ('models.CharField', [], {'max_length': '32', 'blank': 'True'}),
246 | 'subscr_date': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
247 | 'subscr_effective': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
248 | 'subscr_id': ('models.CharField', [], {'max_length': '19', 'blank': 'True'}),
249 | 'tax': ('models.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '64', 'decimal_places': '2', 'blank': 'True'}),
250 | 'test_ipn': ('models.BooleanField', [], {'default': 'False', 'blank': 'True'}),
251 | 'time_created': ('models.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
252 | 'transaction_entity': ('models.CharField', [], {'max_length': '7', 'blank': 'True'}),
253 | 'transaction_subject': ('models.CharField', [], {'max_length': '255', 'blank': 'True'}),
254 | 'tx': ('models.CharField', [], {'max_length': '255', 'blank': 'True'}),
255 | 'txn_id': ('models.CharField', ['"Transaction ID"'], {'max_length': '19', 'blank': 'True'}),
256 | 'txn_type': ('models.CharField', ['"Transaction Type"'], {'max_length': '128', 'blank': 'True'}),
257 | 'updated_at': ('models.DateTimeField', [], {'auto_now': 'True'}),
258 | 'username': ('models.CharField', [], {'max_length': '64', 'blank': 'True'}),
259 | 'verify_sign': ('models.CharField', [], {'max_length': '255', 'blank': 'True'})
260 | }
261 | }
262 |
263 | complete_apps = ['pdt']
264 |
--------------------------------------------------------------------------------