mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-09-18 21:50:56 +00:00
De-vendorizing django-paypal, adding to requirements.txt.
This commit is contained in:
parent
540587e01e
commit
cdda78d916
46 changed files with 9 additions and 2636 deletions
|
@ -28,9 +28,8 @@ from utils import json_functions as json
|
|||
from utils.user_functions import generate_secret_token
|
||||
from utils.feed_functions import chunks
|
||||
from vendor.timezones.fields import TimeZoneField
|
||||
from vendor.paypal.standard.ipn.signals import subscription_signup, payment_was_successful, recurring_payment
|
||||
from vendor.paypal.standard.ipn.signals import payment_was_flagged
|
||||
from vendor.paypal.standard.ipn.models import PayPalIPN
|
||||
from paypal.standard.ipn.signals import valid_ipn_received, invalid_ipn_received
|
||||
from paypal.standard.ipn.models import PayPalIPN
|
||||
from vendor.paypalapi.interface import PayPalInterface
|
||||
from vendor.paypalapi.exceptions import PayPalAPIResponseError
|
||||
from zebra.signals import zebra_webhook_customer_subscription_created
|
||||
|
@ -1078,7 +1077,7 @@ def paypal_signup(sender, **kwargs):
|
|||
user.profile.activate_premium()
|
||||
user.profile.cancel_premium_stripe()
|
||||
user.profile.cancel_premium_paypal(second_most_recent_only=True)
|
||||
subscription_signup.connect(paypal_signup)
|
||||
valid_ipn_received.connect(paypal_signup)
|
||||
|
||||
def paypal_payment_history_sync(sender, **kwargs):
|
||||
ipn_obj = sender
|
||||
|
@ -1091,7 +1090,7 @@ def paypal_payment_history_sync(sender, **kwargs):
|
|||
user.profile.setup_premium_history()
|
||||
except:
|
||||
return {"code": -1, "message": "User doesn't exist."}
|
||||
payment_was_successful.connect(paypal_payment_history_sync)
|
||||
valid_ipn_received.connect(paypal_payment_history_sync)
|
||||
|
||||
def paypal_payment_was_flagged(sender, **kwargs):
|
||||
ipn_obj = sender
|
||||
|
@ -1105,7 +1104,7 @@ def paypal_payment_was_flagged(sender, **kwargs):
|
|||
logging.user(user, "~BC~SB~FBPaypal subscription payment flagged")
|
||||
except:
|
||||
return {"code": -1, "message": "User doesn't exist."}
|
||||
payment_was_flagged.connect(paypal_payment_was_flagged)
|
||||
invalid_ipn_received.connect(paypal_payment_was_flagged)
|
||||
|
||||
def paypal_recurring_payment_history_sync(sender, **kwargs):
|
||||
ipn_obj = sender
|
||||
|
@ -1118,7 +1117,7 @@ def paypal_recurring_payment_history_sync(sender, **kwargs):
|
|||
user.profile.setup_premium_history()
|
||||
except:
|
||||
return {"code": -1, "message": "User doesn't exist."}
|
||||
recurring_payment.connect(paypal_recurring_payment_history_sync)
|
||||
valid_ipn_received.connect(paypal_recurring_payment_history_sync)
|
||||
|
||||
def stripe_signup(sender, full_json, **kwargs):
|
||||
stripe_id = full_json['data']['object']['customer']
|
||||
|
|
|
@ -31,7 +31,7 @@ from utils.view_functions import render_to, is_true
|
|||
from utils.user_functions import get_user
|
||||
from utils import log as logging
|
||||
from vendor.paypalapi.exceptions import PayPalAPIResponseError
|
||||
from vendor.paypal.standard.forms import PayPalPaymentsForm
|
||||
from paypal.standard.forms import PayPalPaymentsForm
|
||||
|
||||
SINGLE_FIELD_PREFS = ('timezone','feed_pane_size','hide_mobile','send_emails',
|
||||
'hide_getting_started', 'has_setup_feeds', 'has_found_friends',
|
||||
|
|
|
@ -13,6 +13,7 @@ django-extensions==1.6.7
|
|||
django-mailgun==0.9.1
|
||||
django-oauth-toolkit==0.7.2
|
||||
django-qurl==0.1.1
|
||||
django-paypal==1.0
|
||||
django-redis-cache==1.5.5
|
||||
django-redis-sessions==0.6.1
|
||||
django-ses==0.7.1
|
||||
|
|
|
@ -316,7 +316,7 @@ INSTALLED_APPS = (
|
|||
'django_extensions',
|
||||
'djcelery',
|
||||
# 'kombu.transport.django',
|
||||
'vendor.paypal.standard.ipn',
|
||||
'paypal.standard.ipn',
|
||||
'apps.rss_feeds',
|
||||
'apps.reader',
|
||||
'apps.analyzer',
|
||||
|
|
354
vendor/paypal/README.md
vendored
354
vendor/paypal/README.md
vendored
|
@ -1,354 +0,0 @@
|
|||
Django PayPal
|
||||
=============
|
||||
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
Django PayPal is a pluggable application that implements with PayPal Payments
|
||||
Standard and Payments Pro.
|
||||
|
||||
Before diving in, a quick review of PayPal's payment methods is in order! [PayPal Payments Standard](https://cms.paypal.com/cms_content/US/en_US/files/developer/PP_WebsitePaymentsStandard_IntegrationGuide.pdf) is the "Buy it Now" buttons you may have
|
||||
seen floating around the internets. Buyers click on the button and are taken to PayPal's website where they can pay for the product. After completing the purchase PayPal makes an HTTP POST to your `notify_url`. PayPal calls this process [Instant Payment Notification](https://cms.paypal.com/cms_content/US/en_US/files/developer/PP_OrderMgmt_IntegrationGuide.pdf) (IPN) but you may know it as [webhooks](http://blog.webhooks.org). This method kinda sucks because it drops your customers off at PayPal's website but it's easy to implement and doesn't require SSL.
|
||||
|
||||
PayPal Payments Pro allows you to accept payments on your website. It contains two distinct payment flows - Direct Payment allows the user to enter credit card information on your website and pay on your website. Express Checkout sends the user over to PayPal to confirm their payment method before redirecting back to your website for confirmation. PayPal rules state that both methods must be implemented.
|
||||
|
||||
There is currently an active discussion over the handling of some of the finer points of the PayPal API and the evolution of this code base - check it out over at [Django PayPal on Google Groups](http://groups.google.com/group/django-paypal).
|
||||
|
||||
|
||||
Using PayPal Payments Standard IPN:
|
||||
-------------------------------
|
||||
|
||||
1. Download the code from GitHub:
|
||||
|
||||
git clone git://github.com/johnboxall/django-paypal.git paypal
|
||||
|
||||
1. Edit `settings.py` and add `paypal.standard.ipn` to your `INSTALLED_APPS`
|
||||
and `PAYPAL_RECEIVER_EMAIL`:
|
||||
|
||||
# settings.py
|
||||
...
|
||||
INSTALLED_APPS = (... 'paypal.standard.ipn', ...)
|
||||
...
|
||||
PAYPAL_RECEIVER_EMAIL = "yourpaypalemail@example.com"
|
||||
|
||||
1. Create an instance of the `PayPalPaymentsForm` in the view where you would
|
||||
like to collect money. Call `render` on the instance in your template to
|
||||
write out the HTML.
|
||||
|
||||
# views.py
|
||||
...
|
||||
from vendor.paypal.standard.forms import PayPalPaymentsForm
|
||||
|
||||
def view_that_asks_for_money(request):
|
||||
|
||||
# What you want the button to do.
|
||||
paypal_dict = {
|
||||
"business": "yourpaypalemail@example.com",
|
||||
"amount": "10000000.00",
|
||||
"item_name": "name of the item",
|
||||
"invoice": "unique-invoice-id",
|
||||
"notify_url": "http://www.example.com/your-ipn-location/",
|
||||
"return_url": "http://www.example.com/your-return-location/",
|
||||
"cancel_return": "http://www.example.com/your-cancel-location/",
|
||||
|
||||
}
|
||||
|
||||
# Create the instance.
|
||||
form = PayPalPaymentsForm(initial=paypal_dict)
|
||||
context = {"form": form}
|
||||
return render_to_response("payment.html", context)
|
||||
|
||||
|
||||
<!-- payment.html -->
|
||||
...
|
||||
<h1>Show me the money!</h1>
|
||||
<!-- writes out the form tag automatically -->
|
||||
{{ form.render }}
|
||||
|
||||
1. When someone uses this button to buy something PayPal makes a HTTP POST to
|
||||
your "notify_url". PayPal calls this Instant Payment Notification (IPN).
|
||||
The view `paypal.standard.ipn.views.ipn` handles IPN processing. To set the
|
||||
correct `notify_url` add the following to your `urls.py`:
|
||||
|
||||
# urls.py
|
||||
...
|
||||
urlpatterns = patterns('',
|
||||
(r'^something/hard/to/guess/', include('paypal.standard.ipn.urls')),
|
||||
)
|
||||
|
||||
1. Whenever an IPN is processed a signal will be sent with the result of the
|
||||
transaction. Connect the signals to actions to perform the needed operations
|
||||
when a successful payment is recieved.
|
||||
|
||||
There are two signals for basic transactions:
|
||||
- `payment_was_succesful`
|
||||
- `payment_was_flagged`
|
||||
|
||||
And four signals for subscriptions:
|
||||
- `subscription_cancel` - Sent when a subscription is cancelled.
|
||||
- `subscription_eot` - Sent when a subscription expires.
|
||||
- `subscription_modify` - Sent when a subscription is modified.
|
||||
- `subscription_signup` - Sent when a subscription is created.
|
||||
|
||||
Connect to these signals and update your data accordingly. [Django Signals Documentation](http://docs.djangoproject.com/en/dev/topics/signals/).
|
||||
|
||||
# models.py
|
||||
...
|
||||
from vendor.paypal.standard.ipn.signals import payment_was_successful
|
||||
|
||||
def show_me_the_money(sender, **kwargs):
|
||||
ipn_obj = sender
|
||||
# Undertake some action depending upon `ipn_obj`.
|
||||
if ipn_obj.custom == "Upgrade all users!":
|
||||
Users.objects.update(paid=True)
|
||||
payment_was_successful.connect(show_me_the_money)
|
||||
|
||||
|
||||
Using PayPal Payments Standard PDT:
|
||||
-------------------------------
|
||||
|
||||
Paypal Payment Data Transfer (PDT) allows you to display transaction details to a customer immediately on return to your site unlike PayPal IPN which may take some seconds. [You will need to enable PDT in your PayPal account to use it.your PayPal account to use it](https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/howto_html_paymentdatatransfer).
|
||||
|
||||
1. Download the code from GitHub:
|
||||
|
||||
git clone git://github.com/johnboxall/django-paypal.git paypal
|
||||
|
||||
1. Edit `settings.py` and add `paypal.standard.pdt` to your `INSTALLED_APPS`. Also set `PAYPAL_IDENTITY_TOKEN` - you can find the correct value of this setting from the PayPal website:
|
||||
|
||||
# settings.py
|
||||
...
|
||||
INSTALLED_APPS = (... 'paypal.standard.pdt', ...)
|
||||
|
||||
PAYPAL_IDENTITY_TOKEN = "xxx"
|
||||
|
||||
1. Create a view that uses `PayPalPaymentsForm` just like in PayPal IPN.
|
||||
|
||||
1. After someone uses this button to buy something PayPal will return the user to your site at
|
||||
your "return_url" with some extra GET parameters. PayPal calls this Payment Data Transfer (PDT).
|
||||
The view `paypal.standard.pdt.views.pdt` handles PDT processing. to specify the correct
|
||||
`return_url` add the following to your `urls.py`:
|
||||
|
||||
# urls.py
|
||||
...
|
||||
urlpatterns = patterns('',
|
||||
(r'^paypal/pdt/', include('paypal.standard.pdt.urls')),
|
||||
...
|
||||
)
|
||||
|
||||
Using PayPal Payments Standard with Subscriptions:
|
||||
-------------------------------
|
||||
|
||||
1. For subscription actions, you'll need to add a parameter to tell it to use the subscription buttons and the command, plus any
|
||||
subscription-specific settings:
|
||||
|
||||
# views.py
|
||||
...
|
||||
paypal_dict = {
|
||||
"cmd": "_xclick-subscriptions",
|
||||
"business": "your_account@paypal",
|
||||
"a3": "9.99", # monthly price
|
||||
"p3": 1, # duration of each unit (depends on unit)
|
||||
"t3": "M", # duration unit ("M for Month")
|
||||
"src": "1", # make payments recur
|
||||
"sra": "1", # reattempt payment on payment error
|
||||
"no_note": "1", # remove extra notes (optional)
|
||||
"item_name": "my cool subscription",
|
||||
"notify_url": "http://www.example.com/your-ipn-location/",
|
||||
"return_url": "http://www.example.com/your-return-location/",
|
||||
"cancel_return": "http://www.example.com/your-cancel-location/",
|
||||
}
|
||||
|
||||
# Create the instance.
|
||||
form = PayPalPaymentsForm(initial=paypal_dict, button_type="subscribe")
|
||||
|
||||
# Output the button.
|
||||
form.render()
|
||||
|
||||
|
||||
Using PayPal Payments Standard with Encrypted Buttons:
|
||||
------------------------------------------------------
|
||||
|
||||
Use this method to encrypt your button so sneaky gits don't try to hack it. Thanks to [Jon Atkinson](http://jonatkinson.co.uk/) for the [tutorial](http://jonatkinson.co.uk/paypal-encrypted-buttons-django/).
|
||||
|
||||
1. Encrypted buttons require the `M2Crypto` library:
|
||||
|
||||
easy_install M2Crypto
|
||||
|
||||
|
||||
1. Encrypted buttons require certificates. Create a private key:
|
||||
|
||||
openssl genrsa -out paypal.pem 1024
|
||||
|
||||
1. Create a public key:
|
||||
|
||||
openssl req -new -key paypal.pem -x509 -days 365 -out pubpaypal.pem
|
||||
|
||||
1. Upload your public key to the paypal website (sandbox or live).
|
||||
|
||||
[https://www.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert](https://www.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert)
|
||||
|
||||
[https://www.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert](https://www.sandbox.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert)
|
||||
|
||||
1. Copy your `cert id` - you'll need it in two steps. It's on the screen where
|
||||
you uploaded your public key.
|
||||
|
||||
1. Download PayPal's public certificate - it's also on that screen.
|
||||
|
||||
1. Edit your `settings.py` to include cert information:
|
||||
|
||||
# settings.py
|
||||
PAYPAL_PRIVATE_CERT = '/path/to/paypal.pem'
|
||||
PAYPAL_PUBLIC_CERT = '/path/to/pubpaypal.pem'
|
||||
PAYPAL_CERT = '/path/to/paypal_cert.pem'
|
||||
PAYPAL_CERT_ID = 'get-from-paypal-website'
|
||||
|
||||
1. Swap out your unencrypted button for a `PayPalEncryptedPaymentsForm`:
|
||||
|
||||
# views.py
|
||||
from vendor.paypal.standard.forms import PayPalEncryptedPaymentsForm
|
||||
|
||||
def view_that_asks_for_money(request):
|
||||
...
|
||||
# Create the instance.
|
||||
form = PayPalPaymentsForm(initial=paypal_dict)
|
||||
# Works just like before!
|
||||
form.render()
|
||||
|
||||
|
||||
Using PayPal Payments Standard with Encrypted Buttons and Shared Secrets:
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
This method uses Shared secrets instead of IPN postback to verify that transactions
|
||||
are legit. PayPal recommends you should use Shared Secrets if:
|
||||
|
||||
* You are not using a shared website hosting service.
|
||||
* You have enabled SSL on your web server.
|
||||
* You are using Encrypted Website Payments.
|
||||
* You use the notify_url variable on each individual payment transaction.
|
||||
|
||||
Use postbacks for validation if:
|
||||
* You rely on a shared website hosting service
|
||||
* You do not have SSL enabled on your web server
|
||||
|
||||
1. Swap out your button for a `PayPalSharedSecretEncryptedPaymentsForm`:
|
||||
|
||||
# views.py
|
||||
from vendor.paypal.standard.forms import PayPalSharedSecretEncryptedPaymentsForm
|
||||
|
||||
def view_that_asks_for_money(request):
|
||||
...
|
||||
# Create the instance.
|
||||
form = PayPalSharedSecretEncryptedPaymentsForm(initial=paypal_dict)
|
||||
# Works just like before!
|
||||
form.render()
|
||||
|
||||
1. Verify that your IPN endpoint is running on SSL - `request.is_secure()` should return `True`!
|
||||
|
||||
|
||||
Using PayPal Payments Pro (WPP)
|
||||
-------------------------------
|
||||
|
||||
WPP is the more awesome version of PayPal that lets you accept payments on your
|
||||
site. WPP reuses code from `paypal.standard` so you'll need to include both
|
||||
apps. [There is an explanation of WPP in the PayPal Forums](http://www.pdncommunity.com/pdn/board/message?board.id=wppro&thread.id=192).
|
||||
|
||||
|
||||
1. Edit `settings.py` and add `paypal.standard` and `paypal.pro` to your
|
||||
`INSTALLED_APPS`, also set your PayPal settings:
|
||||
|
||||
# settings.py
|
||||
...
|
||||
INSTALLED_APPS = (... 'paypal.standard', 'paypal.pro', ...)
|
||||
PAYPAL_TEST = True # Testing mode on
|
||||
PAYPAL_WPP_USER = "???" # Get from vendor.paypal
|
||||
PAYPAL_WPP_PASSWORD = "???"
|
||||
PAYPAL_WPP_SIGNATURE = "???"
|
||||
|
||||
1. Run `python manage.py migrate` to add the required tables.
|
||||
|
||||
1. Write a wrapper view for `paypal.pro.views.PayPalPro`:
|
||||
|
||||
# views.py
|
||||
from vendor.paypal.pro.views import PayPalPro
|
||||
|
||||
def buy_my_item(request):
|
||||
item = {"amt": "10.00", # amount to charge for item
|
||||
"inv": "inventory", # unique tracking variable paypal
|
||||
"custom": "tracking", # custom tracking variable for you
|
||||
"cancelurl": "http://...", # Express checkout cancel url
|
||||
"returnurl": "http://..."} # Express checkout return url
|
||||
|
||||
kw = {"item": item, # what you're selling
|
||||
"payment_template": "payment.html", # template name for payment
|
||||
"confirm_template": "confirmation.html", # template name for confirmation
|
||||
"success_url": "/success/"} # redirect location after success
|
||||
|
||||
ppp = PayPalPro(**kw)
|
||||
return ppp(request)
|
||||
|
||||
|
||||
1. Create templates for payment and confirmation. By default both templates are
|
||||
populated with the context variable `form` which contains either a
|
||||
`PaymentForm` or a `Confirmation` form.
|
||||
|
||||
<!-- payment.html -->
|
||||
<h1>Show me the money</h1>
|
||||
<form method="post" action="">
|
||||
{{ form }}
|
||||
<input type="submit" value="Pay Up">
|
||||
</form>
|
||||
|
||||
<!-- confirmation.html -->
|
||||
<h1>Are you sure you want to buy this thing?</h1>
|
||||
<form method="post" action="">
|
||||
{{ form }}
|
||||
<input type="submit" value="Yes I Yams">
|
||||
</form>
|
||||
|
||||
1. Add your view to `urls.py`, and add the IPN endpoint to receive callbacks
|
||||
from vendor.paypal:
|
||||
|
||||
# urls.py
|
||||
...
|
||||
urlpatterns = ('',
|
||||
...
|
||||
(r'^payment-url/$', 'myproject.views.buy_my_item')
|
||||
(r'^some/obscure/name/', include('paypal.standard.ipn.urls')),
|
||||
)
|
||||
|
||||
1. Profit.
|
||||
|
||||
|
||||
Links:
|
||||
------
|
||||
|
||||
1. [Set your IPN Endpoint on the PayPal Sandbox](https://www.sandbox.paypal.com/us/cgi-bin/webscr?cmd=_profile-ipn-notify)
|
||||
|
||||
2. [Django PayPal on Google Groups](http://groups.google.com/group/django-paypal)
|
||||
|
||||
License (MIT)
|
||||
=============
|
||||
|
||||
Copyright (c) 2009 Handi Mobility Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
2
vendor/paypal/__init__.py
vendored
2
vendor/paypal/__init__.py
vendored
|
@ -1,2 +0,0 @@
|
|||
VERSION = (0, 1, 4, 'a', 0)
|
||||
__version__ = ".".join(map(str, VERSION[0:3])) + "".join(map(str, VERSION[3:]))
|
0
vendor/paypal/standard/__init__.py
vendored
0
vendor/paypal/standard/__init__.py
vendored
26
vendor/paypal/standard/conf.py
vendored
26
vendor/paypal/standard/conf.py
vendored
|
@ -1,26 +0,0 @@
|
|||
from django.conf import settings
|
||||
|
||||
|
||||
class PayPalSettingsError(Exception):
|
||||
"""Raised when settings be bad."""
|
||||
|
||||
|
||||
RECEIVER_EMAIL = settings.PAYPAL_RECEIVER_EMAIL
|
||||
|
||||
|
||||
# API Endpoints.
|
||||
POSTBACK_ENDPOINT = "https://www.paypal.com/cgi-bin/webscr"
|
||||
SANDBOX_POSTBACK_ENDPOINT = "https://www.sandbox.paypal.com/cgi-bin/webscr"
|
||||
|
||||
# Images
|
||||
IMAGE = getattr(settings, "PAYPAL_IMAGE", "http://images.paypal.com/images/x-click-but01.gif")
|
||||
SUBSCRIPTION_IMAGE = getattr(settings, "PAYPAL_SUBSCRIPTION_IMAGE",
|
||||
"https://www.paypal.com/en_US/i/btn/btn_subscribeCC_LG.gif")
|
||||
DONATION_IMAGE = getattr(settings, "PAYPAL_DONATION_IMAGE", "https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif")
|
||||
SANDBOX_IMAGE = getattr(settings, "PAYPAL_SANDBOX_IMAGE",
|
||||
"https://www.sandbox.paypal.com/en_US/i/btn/btn_buynowCC_LG.gif")
|
||||
SUBSCRIPTION_SANDBOX_IMAGE = getattr(settings, "PAYPAL_SUBSCRIPTION_SANDBOX_IMAGE",
|
||||
"https://www.sandbox.paypal.com/en_US/i/btn/btn_subscribeCC_LG.gif")
|
||||
DONATION_SANDBOX_IMAGE = getattr(settings, "PAYPAL_DONATION_SANDBOX_IMAGE",
|
||||
"https://www.sandbox.paypal.com/en_US/i/btn/btn_donateCC_LG.gif")
|
||||
|
242
vendor/paypal/standard/forms.py
vendored
242
vendor/paypal/standard/forms.py
vendored
|
@ -1,242 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from django import forms
|
||||
from django.utils.safestring import mark_safe
|
||||
from paypal.standard.widgets import ValueHiddenInput, ReservedValueHiddenInput
|
||||
from paypal.standard.conf import (POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT,
|
||||
RECEIVER_EMAIL,
|
||||
IMAGE, SUBSCRIPTION_IMAGE, DONATION_IMAGE,
|
||||
SANDBOX_IMAGE, SUBSCRIPTION_SANDBOX_IMAGE, DONATION_SANDBOX_IMAGE)
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
# 20:18:05 Jan 30, 2009 PST - PST timezone support is not included out of the box.
|
||||
# PAYPAL_DATE_FORMAT = ("%H:%M:%S %b. %d, %Y PST", "%H:%M:%S %b %d, %Y PST",)
|
||||
# PayPal dates have been spotted in the wild with these formats, beware!
|
||||
PAYPAL_DATE_FORMAT = ("%H:%M:%S %b. %d, %Y PST",
|
||||
"%H:%M:%S %b. %d, %Y PDT",
|
||||
"%H:%M:%S %b %d, %Y PST",
|
||||
"%H:%M:%S %b %d, %Y PDT",)
|
||||
|
||||
|
||||
class PayPalPaymentsForm(forms.Form):
|
||||
"""
|
||||
Creates a PayPal Payments Standard "Buy It Now" button, configured for a
|
||||
selling a single item with no shipping.
|
||||
|
||||
For a full overview of all the fields you can set (there is a lot!) see:
|
||||
http://tinyurl.com/pps-integration
|
||||
|
||||
Usage:
|
||||
>>> f = PayPalPaymentsForm(initial={'item_name':'Widget 001', ...})
|
||||
>>> f.render()
|
||||
u'<form action="https://www.paypal.com/cgi-bin/webscr" method="post"> ...'
|
||||
|
||||
"""
|
||||
CMD_CHOICES = (
|
||||
("_xclick", "Buy now or Donations"),
|
||||
("_donations", "Donations"),
|
||||
("_cart", "Shopping cart"),
|
||||
("_xclick-subscriptions", "Subscribe")
|
||||
)
|
||||
SHIPPING_CHOICES = ((1, "No shipping"), (0, "Shipping"))
|
||||
NO_NOTE_CHOICES = ((1, "No Note"), (0, "Include Note"))
|
||||
RECURRING_PAYMENT_CHOICES = (
|
||||
(1, "Subscription Payments Recur"),
|
||||
(0, "Subscription payments do not recur")
|
||||
)
|
||||
REATTEMPT_ON_FAIL_CHOICES = (
|
||||
(1, "reattempt billing on Failure"),
|
||||
(0, "Do Not reattempt on failure")
|
||||
)
|
||||
|
||||
BUY = 'buy'
|
||||
SUBSCRIBE = 'subscribe'
|
||||
DONATE = 'donate'
|
||||
|
||||
# Where the money goes.
|
||||
business = forms.CharField(widget=ValueHiddenInput(), initial=RECEIVER_EMAIL)
|
||||
|
||||
# Item information.
|
||||
amount = forms.IntegerField(widget=ValueHiddenInput())
|
||||
item_name = forms.CharField(widget=ValueHiddenInput())
|
||||
item_number = forms.CharField(widget=ValueHiddenInput())
|
||||
quantity = forms.CharField(widget=ValueHiddenInput())
|
||||
|
||||
# Subscription Related.
|
||||
a1 = forms.CharField(widget=ValueHiddenInput()) # Trial 1 Price
|
||||
p1 = forms.CharField(widget=ValueHiddenInput()) # Trial 1 Duration
|
||||
t1 = forms.CharField(widget=ValueHiddenInput()) # Trial 1 unit of Duration, default to Month
|
||||
a2 = forms.CharField(widget=ValueHiddenInput()) # Trial 2 Price
|
||||
p2 = forms.CharField(widget=ValueHiddenInput()) # Trial 2 Duration
|
||||
t2 = forms.CharField(widget=ValueHiddenInput()) # Trial 2 unit of Duration, default to Month
|
||||
a3 = forms.CharField(widget=ValueHiddenInput()) # Subscription Price
|
||||
p3 = forms.CharField(widget=ValueHiddenInput()) # Subscription Duration
|
||||
t3 = forms.CharField(widget=ValueHiddenInput()) # Subscription unit of Duration, default to Month
|
||||
src = forms.CharField(widget=ValueHiddenInput()) # Is billing recurring? default to yes
|
||||
sra = forms.CharField(widget=ValueHiddenInput()) # Reattempt billing on failed cc transaction
|
||||
no_note = forms.CharField(widget=ValueHiddenInput())
|
||||
# Can be either 1 or 2. 1 = modify or allow new subscription creation, 2 = modify only
|
||||
modify = forms.IntegerField(widget=ValueHiddenInput()) # Are we modifying an existing subscription?
|
||||
|
||||
# Localization / PayPal Setup
|
||||
lc = forms.CharField(widget=ValueHiddenInput())
|
||||
page_style = forms.CharField(widget=ValueHiddenInput())
|
||||
cbt = forms.CharField(widget=ValueHiddenInput())
|
||||
|
||||
# IPN control.
|
||||
notify_url = forms.CharField(widget=ValueHiddenInput())
|
||||
cancel_return = forms.CharField(widget=ValueHiddenInput())
|
||||
return_url = forms.CharField(widget=ReservedValueHiddenInput(attrs={"name": "return"}))
|
||||
custom = forms.CharField(widget=ValueHiddenInput())
|
||||
invoice = forms.CharField(widget=ValueHiddenInput())
|
||||
|
||||
# Default fields.
|
||||
cmd = forms.ChoiceField(widget=forms.HiddenInput(), initial=CMD_CHOICES[0][0])
|
||||
charset = forms.CharField(widget=forms.HiddenInput(), initial="utf-8")
|
||||
currency_code = forms.CharField(widget=forms.HiddenInput(), initial="USD")
|
||||
no_shipping = forms.ChoiceField(widget=forms.HiddenInput(), choices=SHIPPING_CHOICES,
|
||||
initial=SHIPPING_CHOICES[0][0])
|
||||
|
||||
def __init__(self, button_type="buy", *args, **kwargs):
|
||||
super(PayPalPaymentsForm, self).__init__(*args, **kwargs)
|
||||
self.button_type = button_type
|
||||
if 'initial' in kwargs:
|
||||
# Dynamically create, so we can support everything PayPal does.
|
||||
for k, v in kwargs['initial'].items():
|
||||
if k not in self.base_fields:
|
||||
self.fields[k] = forms.CharField(label=k, widget=ValueHiddenInput(), initial=v)
|
||||
|
||||
def test_mode(self):
|
||||
return getattr(settings, 'PAYPAL_TEST', True)
|
||||
|
||||
def get_endpoint(self):
|
||||
"Returns the endpoint url for the form."
|
||||
if self.test_mode():
|
||||
return SANDBOX_POSTBACK_ENDPOINT
|
||||
else:
|
||||
return POSTBACK_ENDPOINT
|
||||
|
||||
|
||||
def render(self):
|
||||
return mark_safe(u"""<form action="%s" method="post">
|
||||
%s
|
||||
<input type="image" src="%s" border="0" name="submit" alt="Buy it Now" />
|
||||
</form>""" % (self.get_endpoint(), self.as_p(), self.get_image()))
|
||||
|
||||
|
||||
def sandbox(self):
|
||||
"Deprecated. Use self.render() instead."
|
||||
import warnings
|
||||
warnings.warn("""PaypalPaymentsForm.sandbox() is deprecated.
|
||||
Use the render() method instead.""", DeprecationWarning)
|
||||
return self.render()
|
||||
|
||||
def get_image(self):
|
||||
return {
|
||||
(True, self.SUBSCRIBE): SUBSCRIPTION_SANDBOX_IMAGE,
|
||||
(True, self.BUY): SANDBOX_IMAGE,
|
||||
(True, self.DONATE): DONATION_SANDBOX_IMAGE,
|
||||
(False, self.SUBSCRIBE): SUBSCRIPTION_IMAGE,
|
||||
(False, self.BUY): IMAGE,
|
||||
(False, self.DONATE): DONATION_IMAGE,
|
||||
}[self.test_mode(), self.button_type]
|
||||
|
||||
def is_transaction(self):
|
||||
return not self.is_subscription()
|
||||
|
||||
def is_donation(self):
|
||||
return self.button_type == self.DONATE
|
||||
|
||||
def is_subscription(self):
|
||||
return self.button_type == self.SUBSCRIBE
|
||||
|
||||
|
||||
class PayPalEncryptedPaymentsForm(PayPalPaymentsForm):
|
||||
"""
|
||||
Creates a PayPal Encrypted Payments "Buy It Now" button.
|
||||
Requires the M2Crypto package.
|
||||
|
||||
Based on example at:
|
||||
http://blog.mauveweb.co.uk/2007/10/10/paypal-with-django/
|
||||
|
||||
"""
|
||||
|
||||
def _encrypt(self):
|
||||
"""Use your key thing to encrypt things."""
|
||||
from M2Crypto import BIO, SMIME, X509
|
||||
# @@@ Could we move this to conf.py?
|
||||
CERT = settings.PAYPAL_PRIVATE_CERT
|
||||
PUB_CERT = settings.PAYPAL_PUBLIC_CERT
|
||||
PAYPAL_CERT = settings.PAYPAL_CERT
|
||||
CERT_ID = settings.PAYPAL_CERT_ID
|
||||
|
||||
# Iterate through the fields and pull out the ones that have a value.
|
||||
plaintext = 'cert_id=%s\n' % CERT_ID
|
||||
for name, field in self.fields.items():
|
||||
value = None
|
||||
if name in self.initial:
|
||||
value = self.initial[name]
|
||||
elif field.initial is not None:
|
||||
value = field.initial
|
||||
if value is not None:
|
||||
# @@@ Make this less hackish and put it in the widget.
|
||||
if name == "return_url":
|
||||
name = "return"
|
||||
plaintext += u'%s=%s\n' % (name, value)
|
||||
plaintext = plaintext.encode('utf-8')
|
||||
|
||||
# Begin crypto weirdness.
|
||||
s = SMIME.SMIME()
|
||||
s.load_key_bio(BIO.openfile(CERT), BIO.openfile(PUB_CERT))
|
||||
p7 = s.sign(BIO.MemoryBuffer(plaintext), flags=SMIME.PKCS7_BINARY)
|
||||
x509 = X509.load_cert_bio(BIO.openfile(settings.PAYPAL_CERT))
|
||||
sk = X509.X509_Stack()
|
||||
sk.push(x509)
|
||||
s.set_x509_stack(sk)
|
||||
s.set_cipher(SMIME.Cipher('des_ede3_cbc'))
|
||||
tmp = BIO.MemoryBuffer()
|
||||
p7.write_der(tmp)
|
||||
p7 = s.encrypt(tmp, flags=SMIME.PKCS7_BINARY)
|
||||
out = BIO.MemoryBuffer()
|
||||
p7.write(out)
|
||||
return out.read()
|
||||
|
||||
def as_p(self):
|
||||
return mark_safe(u"""
|
||||
<input type="hidden" name="cmd" value="_s-xclick" />
|
||||
<input type="hidden" name="encrypted" value="%s" />
|
||||
""" % self._encrypt())
|
||||
|
||||
|
||||
class PayPalSharedSecretEncryptedPaymentsForm(PayPalEncryptedPaymentsForm):
|
||||
"""
|
||||
Creates a PayPal Encrypted Payments "Buy It Now" button with a Shared Secret.
|
||||
Shared secrets should only be used when your IPN endpoint is on HTTPS.
|
||||
|
||||
Adds a secret to the notify_url based on the contents of the form.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"Make the secret from the form initial data and slip it into the form."
|
||||
from paypal.standard.helpers import make_secret
|
||||
|
||||
super(PayPalSharedSecretEncryptedPaymentsForm, self).__init__(*args, **kwargs)
|
||||
# @@@ Attach the secret parameter in a way that is safe for other query params.
|
||||
secret_param = "?secret=%s" % make_secret(self)
|
||||
# Initial data used in form construction overrides defaults
|
||||
if 'notify_url' in self.initial:
|
||||
self.initial['notify_url'] += secret_param
|
||||
else:
|
||||
self.fields['notify_url'].initial += secret_param
|
||||
|
||||
|
||||
class PayPalStandardBaseForm(forms.ModelForm):
|
||||
"""Form used to receive and record PayPal IPN/PDT."""
|
||||
# PayPal dates have non-standard formats.
|
||||
time_created = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT)
|
||||
payment_date = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT)
|
||||
next_payment_date = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT)
|
||||
subscr_date = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT)
|
||||
subscr_effective = forms.DateTimeField(required=False, input_formats=PAYPAL_DATE_FORMAT)
|
72
vendor/paypal/standard/helpers.py
vendored
72
vendor/paypal/standard/helpers.py
vendored
|
@ -1,72 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import hashlib
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.encoding import smart_str
|
||||
|
||||
|
||||
def get_sha1_hexdigest(salt, raw_password):
|
||||
return hashlib.sha1(smart_str(salt) + smart_str(raw_password)).hexdigest()
|
||||
|
||||
|
||||
def duplicate_txn_id(ipn_obj):
|
||||
"""
|
||||
Returns True if a record with this transaction id exists and its
|
||||
payment_status has not changed.
|
||||
This function has been completely changed from its previous implementation
|
||||
where it used to specifically only check for a Pending->Completed
|
||||
transition.
|
||||
|
||||
"""
|
||||
|
||||
# get latest similar transaction(s)
|
||||
similars = ipn_obj._default_manager.filter(txn_id=ipn_obj.txn_id).order_by('-created_at')[:1]
|
||||
|
||||
if len(similars) > 0:
|
||||
# we have a similar transaction, has the payment_status changed?
|
||||
return similars[0].payment_status == ipn_obj.payment_status
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def make_secret(form_instance, secret_fields=None):
|
||||
"""
|
||||
Returns a secret for use in a EWP form or an IPN verification based on a
|
||||
selection of variables in params. Should only be used with SSL.
|
||||
|
||||
"""
|
||||
# @@@ Moved here as temporary fix to avoid dependancy on auth.models.
|
||||
# @@@ amount is mc_gross on the IPN - where should mapping logic go?
|
||||
# @@@ amount / mc_gross is not nessecarily returned as it was sent - how to use it? 10.00 vs. 10.0
|
||||
# @@@ the secret should be based on the invoice or custom fields as well - otherwise its always the same.
|
||||
|
||||
# Build the secret with fields availible in both PaymentForm and the IPN. Order matters.
|
||||
if secret_fields is None:
|
||||
secret_fields = ['business', 'item_name']
|
||||
|
||||
data = ""
|
||||
for name in secret_fields:
|
||||
if hasattr(form_instance, 'cleaned_data'):
|
||||
if name in form_instance.cleaned_data:
|
||||
data += unicode(form_instance.cleaned_data[name])
|
||||
else:
|
||||
# Initial data passed into the constructor overrides defaults.
|
||||
if name in form_instance.initial:
|
||||
data += unicode(form_instance.initial[name])
|
||||
elif name in form_instance.fields and form_instance.fields[name].initial is not None:
|
||||
data += unicode(form_instance.fields[name].initial)
|
||||
|
||||
secret = get_sha1_hexdigest(settings.SECRET_KEY, data)
|
||||
return secret
|
||||
|
||||
|
||||
def check_secret(form_instance, secret):
|
||||
"""
|
||||
Returns true if received `secret` matches expected secret for form_instance.
|
||||
Used to verify IPN.
|
||||
|
||||
"""
|
||||
# @@@ add invoice & custom
|
||||
# secret_fields = ['business', 'item_name']
|
||||
return make_secret(form_instance) == secret
|
0
vendor/paypal/standard/ipn/__init__.py
vendored
0
vendor/paypal/standard/ipn/__init__.py
vendored
69
vendor/paypal/standard/ipn/admin.py
vendored
69
vendor/paypal/standard/ipn/admin.py
vendored
|
@ -1,69 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.contrib import admin
|
||||
from vendor.paypal.standard.ipn.models import PayPalIPN
|
||||
|
||||
|
||||
class PayPalIPNAdmin(admin.ModelAdmin):
|
||||
date_hierarchy = 'payment_date'
|
||||
fieldsets = (
|
||||
(None, {
|
||||
"fields": [
|
||||
"flag", "txn_id", "txn_type", "payment_status", "payment_date",
|
||||
"transaction_entity", "reason_code", "pending_reason",
|
||||
"mc_currency", "mc_gross", "mc_fee", "mc_handling", "mc_shipping",
|
||||
"auth_status", "auth_amount", "auth_exp", "auth_id"
|
||||
]
|
||||
}),
|
||||
("Address", {
|
||||
"description": "The address of the Buyer.",
|
||||
'classes': ('collapse',),
|
||||
"fields": [
|
||||
"address_city", "address_country", "address_country_code",
|
||||
"address_name", "address_state", "address_status",
|
||||
"address_street", "address_zip"
|
||||
]
|
||||
}),
|
||||
("Buyer", {
|
||||
"description": "The information about the Buyer.",
|
||||
'classes': ('collapse',),
|
||||
"fields": [
|
||||
"first_name", "last_name", "payer_business_name", "payer_email",
|
||||
"payer_id", "payer_status", "contact_phone", "residence_country"
|
||||
]
|
||||
}),
|
||||
("Seller", {
|
||||
"description": "The information about the Seller.",
|
||||
'classes': ('collapse',),
|
||||
"fields": [
|
||||
"business", "item_name", "item_number", "quantity",
|
||||
"receiver_email", "receiver_id", "custom", "invoice", "memo"
|
||||
]
|
||||
}),
|
||||
("Recurring", {
|
||||
"description": "Information about recurring Payments.",
|
||||
"classes": ("collapse",),
|
||||
"fields": [
|
||||
"profile_status", "initial_payment_amount", "amount_per_cycle",
|
||||
"outstanding_balance", "period_type", "product_name",
|
||||
"product_type", "recurring_payment_id", "receipt_id",
|
||||
"next_payment_date"
|
||||
]
|
||||
}),
|
||||
("Admin", {
|
||||
"description": "Additional Info.",
|
||||
"classes": ('collapse',),
|
||||
"fields": [
|
||||
"test_ipn", "ipaddress", "query", "response", "flag_code",
|
||||
"flag_info"
|
||||
]
|
||||
}),
|
||||
)
|
||||
list_display = [
|
||||
"__unicode__", "flag", "flag_info", "invoice", "custom",
|
||||
"payment_status", "created_at"
|
||||
]
|
||||
search_fields = ["txn_id", "recurring_payment_id"]
|
||||
|
||||
|
||||
admin.site.register(PayPalIPN, PayPalIPNAdmin)
|
|
@ -1,134 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PayPalIPN',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('business', models.CharField(help_text=b'Email where the money was sent.', max_length=127, blank=True)),
|
||||
('charset', models.CharField(max_length=32, blank=True)),
|
||||
('custom', models.CharField(max_length=255, blank=True)),
|
||||
('notify_version', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('parent_txn_id', models.CharField(max_length=19, verbose_name=b'Parent Transaction ID', blank=True)),
|
||||
('receiver_email', models.EmailField(max_length=127, blank=True)),
|
||||
('receiver_id', models.CharField(max_length=127, blank=True)),
|
||||
('residence_country', models.CharField(max_length=2, blank=True)),
|
||||
('test_ipn', models.BooleanField(default=False)),
|
||||
('txn_id', models.CharField(help_text=b'PayPal transaction ID.', max_length=19, verbose_name=b'Transaction ID', db_index=True, blank=True)),
|
||||
('txn_type', models.CharField(help_text=b'PayPal transaction type.', max_length=128, verbose_name=b'Transaction Type', blank=True)),
|
||||
('verify_sign', models.CharField(max_length=255, blank=True)),
|
||||
('address_country', models.CharField(max_length=64, blank=True)),
|
||||
('address_city', models.CharField(max_length=40, blank=True)),
|
||||
('address_country_code', models.CharField(help_text=b'ISO 3166', max_length=64, blank=True)),
|
||||
('address_name', models.CharField(max_length=128, blank=True)),
|
||||
('address_state', models.CharField(max_length=40, blank=True)),
|
||||
('address_status', models.CharField(max_length=11, blank=True)),
|
||||
('address_street', models.CharField(max_length=200, blank=True)),
|
||||
('address_zip', models.CharField(max_length=20, blank=True)),
|
||||
('contact_phone', models.CharField(max_length=20, blank=True)),
|
||||
('first_name', models.CharField(max_length=64, blank=True)),
|
||||
('last_name', models.CharField(max_length=64, blank=True)),
|
||||
('payer_business_name', models.CharField(max_length=127, blank=True)),
|
||||
('payer_email', models.CharField(max_length=127, blank=True)),
|
||||
('payer_id', models.CharField(max_length=13, blank=True)),
|
||||
('auth_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('auth_exp', models.CharField(max_length=28, blank=True)),
|
||||
('auth_id', models.CharField(max_length=19, blank=True)),
|
||||
('auth_status', models.CharField(max_length=9, blank=True)),
|
||||
('exchange_rate', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=16, blank=True)),
|
||||
('invoice', models.CharField(max_length=127, blank=True)),
|
||||
('item_name', models.CharField(max_length=127, blank=True)),
|
||||
('item_number', models.CharField(max_length=127, blank=True)),
|
||||
('mc_currency', models.CharField(default=b'USD', max_length=32, blank=True)),
|
||||
('mc_fee', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('mc_gross', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('mc_handling', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('mc_shipping', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('memo', models.CharField(max_length=255, blank=True)),
|
||||
('num_cart_items', models.IntegerField(default=0, null=True, blank=True)),
|
||||
('option_name1', models.CharField(max_length=64, blank=True)),
|
||||
('option_name2', models.CharField(max_length=64, blank=True)),
|
||||
('payer_status', models.CharField(max_length=10, blank=True)),
|
||||
('payment_date', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('payment_gross', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('payment_status', models.CharField(max_length=17, blank=True)),
|
||||
('payment_type', models.CharField(max_length=7, blank=True)),
|
||||
('pending_reason', models.CharField(max_length=14, blank=True)),
|
||||
('protection_eligibility', models.CharField(max_length=32, blank=True)),
|
||||
('quantity', models.IntegerField(default=1, null=True, blank=True)),
|
||||
('reason_code', models.CharField(max_length=15, blank=True)),
|
||||
('remaining_settle', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('settle_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('settle_currency', models.CharField(max_length=32, blank=True)),
|
||||
('shipping', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('shipping_method', models.CharField(max_length=255, blank=True)),
|
||||
('tax', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('transaction_entity', models.CharField(max_length=7, blank=True)),
|
||||
('auction_buyer_id', models.CharField(max_length=64, blank=True)),
|
||||
('auction_closing_date', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('auction_multi_item', models.IntegerField(default=0, null=True, blank=True)),
|
||||
('for_auction', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('amount_per_cycle', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('initial_payment_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('next_payment_date', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('outstanding_balance', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('payment_cycle', models.CharField(max_length=32, blank=True)),
|
||||
('period_type', models.CharField(max_length=32, blank=True)),
|
||||
('product_name', models.CharField(max_length=128, blank=True)),
|
||||
('product_type', models.CharField(max_length=128, blank=True)),
|
||||
('profile_status', models.CharField(max_length=32, blank=True)),
|
||||
('recurring_payment_id', models.CharField(max_length=128, blank=True)),
|
||||
('rp_invoice_id', models.CharField(max_length=127, blank=True)),
|
||||
('time_created', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('amount1', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('amount2', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('amount3', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('mc_amount1', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('mc_amount2', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('mc_amount3', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('password', models.CharField(max_length=24, blank=True)),
|
||||
('period1', models.CharField(max_length=32, blank=True)),
|
||||
('period2', models.CharField(max_length=32, blank=True)),
|
||||
('period3', models.CharField(max_length=32, blank=True)),
|
||||
('reattempt', models.CharField(max_length=1, blank=True)),
|
||||
('recur_times', models.IntegerField(default=0, null=True, blank=True)),
|
||||
('recurring', models.CharField(max_length=1, blank=True)),
|
||||
('retry_at', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('subscr_date', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('subscr_effective', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('subscr_id', models.CharField(max_length=19, blank=True)),
|
||||
('username', models.CharField(max_length=64, blank=True)),
|
||||
('case_creation_date', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('case_id', models.CharField(max_length=14, blank=True)),
|
||||
('case_type', models.CharField(max_length=24, blank=True)),
|
||||
('receipt_id', models.CharField(max_length=64, blank=True)),
|
||||
('currency_code', models.CharField(default=b'USD', max_length=32, blank=True)),
|
||||
('handling_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('transaction_subject', models.CharField(max_length=255, blank=True)),
|
||||
('ipaddress', models.IPAddressField(blank=True)),
|
||||
('flag', models.BooleanField(default=False)),
|
||||
('flag_code', models.CharField(max_length=16, blank=True)),
|
||||
('flag_info', models.TextField(blank=True)),
|
||||
('query', models.TextField(blank=True)),
|
||||
('response', models.TextField(blank=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('from_view', models.CharField(max_length=6, null=True, blank=True)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'paypal_ipn',
|
||||
'verbose_name': 'PayPal IPN',
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
17
vendor/paypal/standard/ipn/forms.py
vendored
17
vendor/paypal/standard/ipn/forms.py
vendored
|
@ -1,17 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from vendor.paypal.standard.forms import PayPalStandardBaseForm
|
||||
from vendor.paypal.standard.ipn.models import PayPalIPN
|
||||
|
||||
|
||||
class PayPalIPNForm(PayPalStandardBaseForm):
|
||||
"""
|
||||
Form used to receive and record PayPal IPN notifications.
|
||||
|
||||
PayPal IPN test tool:
|
||||
https://developer.paypal.com/us/cgi-bin/devscr?cmd=_tools-session
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = PayPalIPN
|
||||
# fields = "__all__"
|
|
@ -1,133 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PayPalIPN',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('business', models.CharField(help_text=b'Email where the money was sent.', max_length=127, blank=True)),
|
||||
('charset', models.CharField(max_length=32, blank=True)),
|
||||
('custom', models.CharField(max_length=255, blank=True)),
|
||||
('notify_version', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('parent_txn_id', models.CharField(max_length=19, verbose_name=b'Parent Transaction ID', blank=True)),
|
||||
('receiver_email', models.EmailField(max_length=127, blank=True)),
|
||||
('receiver_id', models.CharField(max_length=127, blank=True)),
|
||||
('residence_country', models.CharField(max_length=2, blank=True)),
|
||||
('test_ipn', models.BooleanField(default=False)),
|
||||
('txn_id', models.CharField(help_text=b'PayPal transaction ID.', max_length=19, verbose_name=b'Transaction ID', db_index=True, blank=True)),
|
||||
('txn_type', models.CharField(help_text=b'PayPal transaction type.', max_length=128, verbose_name=b'Transaction Type', blank=True)),
|
||||
('verify_sign', models.CharField(max_length=255, blank=True)),
|
||||
('address_country', models.CharField(max_length=64, blank=True)),
|
||||
('address_city', models.CharField(max_length=40, blank=True)),
|
||||
('address_country_code', models.CharField(help_text=b'ISO 3166', max_length=64, blank=True)),
|
||||
('address_name', models.CharField(max_length=128, blank=True)),
|
||||
('address_state', models.CharField(max_length=40, blank=True)),
|
||||
('address_status', models.CharField(max_length=11, blank=True)),
|
||||
('address_street', models.CharField(max_length=200, blank=True)),
|
||||
('address_zip', models.CharField(max_length=20, blank=True)),
|
||||
('contact_phone', models.CharField(max_length=20, blank=True)),
|
||||
('first_name', models.CharField(max_length=64, blank=True)),
|
||||
('last_name', models.CharField(max_length=64, blank=True)),
|
||||
('payer_business_name', models.CharField(max_length=127, blank=True)),
|
||||
('payer_email', models.CharField(max_length=127, blank=True)),
|
||||
('payer_id', models.CharField(max_length=13, blank=True)),
|
||||
('auth_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('auth_exp', models.CharField(max_length=28, blank=True)),
|
||||
('auth_id', models.CharField(max_length=19, blank=True)),
|
||||
('auth_status', models.CharField(max_length=9, blank=True)),
|
||||
('exchange_rate', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=16, blank=True)),
|
||||
('invoice', models.CharField(max_length=127, blank=True)),
|
||||
('item_name', models.CharField(max_length=127, blank=True)),
|
||||
('item_number', models.CharField(max_length=127, blank=True)),
|
||||
('mc_currency', models.CharField(default=b'USD', max_length=32, blank=True)),
|
||||
('mc_fee', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('mc_gross', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('mc_handling', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('mc_shipping', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('memo', models.CharField(max_length=255, blank=True)),
|
||||
('num_cart_items', models.IntegerField(default=0, null=True, blank=True)),
|
||||
('option_name1', models.CharField(max_length=64, blank=True)),
|
||||
('option_name2', models.CharField(max_length=64, blank=True)),
|
||||
('payer_status', models.CharField(max_length=10, blank=True)),
|
||||
('payment_date', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('payment_gross', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('payment_status', models.CharField(max_length=17, blank=True)),
|
||||
('payment_type', models.CharField(max_length=7, blank=True)),
|
||||
('pending_reason', models.CharField(max_length=14, blank=True)),
|
||||
('protection_eligibility', models.CharField(max_length=32, blank=True)),
|
||||
('quantity', models.IntegerField(default=1, null=True, blank=True)),
|
||||
('reason_code', models.CharField(max_length=15, blank=True)),
|
||||
('remaining_settle', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('settle_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('settle_currency', models.CharField(max_length=32, blank=True)),
|
||||
('shipping', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('shipping_method', models.CharField(max_length=255, blank=True)),
|
||||
('tax', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('transaction_entity', models.CharField(max_length=7, blank=True)),
|
||||
('auction_buyer_id', models.CharField(max_length=64, blank=True)),
|
||||
('auction_closing_date', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('auction_multi_item', models.IntegerField(default=0, null=True, blank=True)),
|
||||
('for_auction', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('amount_per_cycle', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('initial_payment_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('next_payment_date', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('outstanding_balance', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('payment_cycle', models.CharField(max_length=32, blank=True)),
|
||||
('period_type', models.CharField(max_length=32, blank=True)),
|
||||
('product_name', models.CharField(max_length=128, blank=True)),
|
||||
('product_type', models.CharField(max_length=128, blank=True)),
|
||||
('profile_status', models.CharField(max_length=32, blank=True)),
|
||||
('recurring_payment_id', models.CharField(max_length=128, blank=True)),
|
||||
('rp_invoice_id', models.CharField(max_length=127, blank=True)),
|
||||
('time_created', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('amount1', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('amount2', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('amount3', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('mc_amount1', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('mc_amount2', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('mc_amount3', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('password', models.CharField(max_length=24, blank=True)),
|
||||
('period1', models.CharField(max_length=32, blank=True)),
|
||||
('period2', models.CharField(max_length=32, blank=True)),
|
||||
('period3', models.CharField(max_length=32, blank=True)),
|
||||
('reattempt', models.CharField(max_length=1, blank=True)),
|
||||
('recur_times', models.IntegerField(default=0, null=True, blank=True)),
|
||||
('recurring', models.CharField(max_length=1, blank=True)),
|
||||
('retry_at', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('subscr_date', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('subscr_effective', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('subscr_id', models.CharField(max_length=19, blank=True)),
|
||||
('username', models.CharField(max_length=64, blank=True)),
|
||||
('case_creation_date', models.DateTimeField(help_text=b'HH:MM:SS DD Mmm YY, YYYY PST', null=True, blank=True)),
|
||||
('case_id', models.CharField(max_length=14, blank=True)),
|
||||
('case_type', models.CharField(max_length=24, blank=True)),
|
||||
('receipt_id', models.CharField(max_length=64, blank=True)),
|
||||
('currency_code', models.CharField(default=b'USD', max_length=32, blank=True)),
|
||||
('handling_amount', models.DecimalField(default=0, null=True, max_digits=64, decimal_places=2, blank=True)),
|
||||
('transaction_subject', models.CharField(max_length=255, blank=True)),
|
||||
('ipaddress', models.IPAddressField(blank=True)),
|
||||
('flag', models.BooleanField(default=False)),
|
||||
('flag_code', models.CharField(max_length=16, blank=True)),
|
||||
('flag_info', models.TextField(blank=True)),
|
||||
('query', models.TextField(blank=True)),
|
||||
('response', models.TextField(blank=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'paypal_ipn',
|
||||
'verbose_name': 'PayPal IPN',
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
|
@ -1,19 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ipn', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='paypalipn',
|
||||
name='ipaddress',
|
||||
field=models.GenericIPAddressField(null=True, blank=True),
|
||||
),
|
||||
]
|
63
vendor/paypal/standard/ipn/models.py
vendored
63
vendor/paypal/standard/ipn/models.py
vendored
|
@ -1,63 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from six import b
|
||||
from six.moves.urllib.request import urlopen
|
||||
|
||||
from paypal.standard.models import PayPalStandardBase
|
||||
from vendor.paypal.standard.ipn.signals import payment_was_flagged, payment_was_refunded, payment_was_reversed, payment_was_successful, recurring_create, recurring_payment, recurring_cancel, recurring_skipped, recurring_failed, subscription_cancel, subscription_signup, subscription_eot, subscription_modify
|
||||
|
||||
|
||||
class PayPalIPN(PayPalStandardBase):
|
||||
"""Logs PayPal IPN interactions."""
|
||||
format = u"<IPN: %s %s>"
|
||||
|
||||
class Meta:
|
||||
db_table = "paypal_ipn"
|
||||
verbose_name = "PayPal IPN"
|
||||
|
||||
def _postback(self):
|
||||
"""Perform PayPal Postback validation."""
|
||||
return urlopen(self.get_endpoint(), b("cmd=_notify-validate&%s" % self.query)).read()
|
||||
|
||||
def _verify_postback(self):
|
||||
if self.response != "VERIFIED":
|
||||
self.set_flag("Invalid postback. ({0})".format(self.response))
|
||||
|
||||
def send_signals(self):
|
||||
"""Shout for the world to hear whether a txn was successful."""
|
||||
if self.flag:
|
||||
payment_was_flagged.send(sender=self)
|
||||
|
||||
# Transaction signals:
|
||||
if self.is_transaction():
|
||||
if self.is_refund():
|
||||
payment_was_refunded.send(sender=self)
|
||||
elif self.is_reversed():
|
||||
payment_was_reversed.send(sender=self)
|
||||
else:
|
||||
payment_was_successful.send(sender=self)
|
||||
# Recurring payment signals:
|
||||
# XXX: Should these be merged with subscriptions?
|
||||
elif self.is_recurring():
|
||||
if self.is_recurring_create():
|
||||
recurring_create.send(sender=self)
|
||||
elif self.is_recurring_payment():
|
||||
recurring_payment.send(sender=self)
|
||||
elif self.is_recurring_cancel():
|
||||
recurring_cancel.send(sender=self)
|
||||
elif self.is_recurring_skipped():
|
||||
recurring_skipped.send(sender=self)
|
||||
elif self.is_recurring_failed():
|
||||
recurring_failed.send(sender=self)
|
||||
# Subscription signals:
|
||||
else:
|
||||
if self.is_subscription_cancellation():
|
||||
subscription_cancel.send(sender=self)
|
||||
elif self.is_subscription_signup():
|
||||
subscription_signup.send(sender=self)
|
||||
elif self.is_subscription_end_of_term():
|
||||
subscription_eot.send(sender=self)
|
||||
elif self.is_subscription_modified():
|
||||
subscription_modify.send(sender=self)
|
43
vendor/paypal/standard/ipn/signals.py
vendored
43
vendor/paypal/standard/ipn/signals.py
vendored
|
@ -1,43 +0,0 @@
|
|||
"""
|
||||
Note that sometimes you will get duplicate signals emitted, depending on configuration of your systems.
|
||||
If you do encounter this, you will need to add the "dispatch_uid" to your connect handlers:
|
||||
http://code.djangoproject.com/wiki/Signals#Helppost_saveseemstobeemittedtwiceforeachsave
|
||||
|
||||
"""
|
||||
from django.dispatch import Signal
|
||||
|
||||
# Sent when a payment is successfully processed.
|
||||
payment_was_successful = Signal()
|
||||
|
||||
# Sent when a payment is flagged.
|
||||
payment_was_flagged = Signal()
|
||||
|
||||
# Sent when a payment was refunded by the seller.
|
||||
payment_was_refunded = Signal()
|
||||
|
||||
# Sent when a payment was reversed by the buyer.
|
||||
payment_was_reversed = Signal()
|
||||
|
||||
# Sent when a subscription was cancelled.
|
||||
subscription_cancel = Signal()
|
||||
|
||||
# Sent when a subscription expires.
|
||||
subscription_eot = Signal()
|
||||
|
||||
# Sent when a subscription was modified.
|
||||
subscription_modify = Signal()
|
||||
|
||||
# Sent when a subscription is created.
|
||||
subscription_signup = Signal()
|
||||
|
||||
# recurring_payment_profile_created
|
||||
recurring_create = Signal()
|
||||
|
||||
# recurring_payment
|
||||
recurring_payment = Signal()
|
||||
|
||||
recurring_cancel = Signal()
|
||||
|
||||
recurring_skipped = Signal()
|
||||
|
||||
recurring_failed = Signal()
|
|
@ -1,26 +0,0 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<form action="" method="post">
|
||||
<table>
|
||||
{{ form.as_p }}
|
||||
<tr>
|
||||
<td colspan="2" align="right">
|
||||
<input type="submit"/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
</table>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
||||
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<head>
|
||||
<body>
|
||||
|
||||
<form action="http://216.19.180.83:8000/ipn/" method="post">
|
||||
<input type="text" name="protection_eligibility" value="Ineligible"/>
|
||||
<input type="text" name="last_name" value="User"/>
|
||||
<input type="text" name="txn_id" value="51403485VH153354B"/>
|
||||
<input type="text" name="receiver_email" value="bishan_1233270560_biz@gmail.com"/>
|
||||
<input type="text" name="payment_status" value="Completed"/>
|
||||
<input type="text" name="payment_gross" value="10.00"/>
|
||||
<input type="text" name="tax" value="0.00"/>
|
||||
<input type="text" name="residence_country" value="US"/>
|
||||
<input type="text" name="invoice" value="0004"/>
|
||||
<input type="text" name="payer_status" value="verified"/>
|
||||
<input type="text" name="txn_type" value="express_checkout"/>
|
||||
<input type="text" name="handling_amount" value="0.00"/>
|
||||
<input type="text" name="payment_date" value="23:04:06 Feb 02, 2009 PST"/>
|
||||
<input type="text" name="first_name" value="Test"/>
|
||||
<input type="text" name="item_name" value=""/>
|
||||
<input type="text" name="charset" value="windows-1252"/>
|
||||
<input type="text" name="custom" value="website_id=13&user_id=21"/>
|
||||
<input type="text" name="notify_version" value="2.6"/>
|
||||
<input type="text" name="transaction_subject" value=""/>
|
||||
<input type="text" name="test_ipn" value="1"/>
|
||||
<input type="text" name="item_number" value=""/>
|
||||
<input type="text" name="receiver_id" value="258DLEHY2BDK6"/>
|
||||
<input type="text" name="payer_id" value="BN5JZ2V7MLEV4"/>
|
||||
<input type="text" name="verify_sign" value="An5ns1Kso7MWUdW4ErQKJJJ4qi4-AqdZy6dD.sGO3sDhTf1wAbuO2IZ7"/>
|
||||
<input type="text" name="payment_fee" value="0.59"/>
|
||||
<input type="text" name="mc_fee" value="0.59"/>
|
||||
<input type="text" name="mc_currency" value="USD"/>
|
||||
<input type="text" name="shipping" value="0.00"/>
|
||||
<input type="text" name="payer_email" value="bishan_1233269544_per@gmail.com"/>
|
||||
<input type="text" name="payment_type" value="instant"/>
|
||||
<input type="text" name="mc_gross" value="10.00"/>
|
||||
<input type="text" name="quantity" value="1"/>
|
||||
<input type="submit"/>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,17 +0,0 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
{{ form.sandbox }}
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
||||
|
||||
|
2
vendor/paypal/standard/ipn/tests/__init__.py
vendored
2
vendor/paypal/standard/ipn/tests/__init__.py
vendored
|
@ -1,2 +0,0 @@
|
|||
from .test_ipn import *
|
||||
from .test_forms import *
|
22
vendor/paypal/standard/ipn/tests/test_forms.py
vendored
22
vendor/paypal/standard/ipn/tests/test_forms.py
vendored
|
@ -1,22 +0,0 @@
|
|||
from django.test import TestCase
|
||||
|
||||
from paypal.standard.forms import PayPalPaymentsForm
|
||||
|
||||
class PaymentsFormTest(TestCase):
|
||||
|
||||
def test_form_render(self):
|
||||
f = PayPalPaymentsForm(initial={'business':'me@mybusiness.com',
|
||||
'amount': '10.50',
|
||||
'shipping': '2.00',
|
||||
})
|
||||
rendered = f.render()
|
||||
self.assertIn('''action="https://www.sandbox.paypal.com/cgi-bin/webscr"''', rendered)
|
||||
self.assertIn('''value="me@mybusiness.com"''', rendered)
|
||||
self.assertIn('''value="2.00"''', rendered)
|
||||
self.assertIn('''value="10.50"''', rendered)
|
||||
self.assertIn('''buynowCC''', rendered)
|
||||
|
||||
def test_form_endpont(self):
|
||||
with self.settings(PAYPAL_TEST=False):
|
||||
f = PayPalPaymentsForm(initial={})
|
||||
self.assertNotIn('sandbox', f.render())
|
332
vendor/paypal/standard/ipn/tests/test_ipn.py
vendored
332
vendor/paypal/standard/ipn/tests/test_ipn.py
vendored
|
@ -1,332 +0,0 @@
|
|||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from six import b
|
||||
from six.moves.urllib.parse import urlencode
|
||||
|
||||
from paypal.standard.models import ST_PP_CANCELLED
|
||||
from paypal.standard.ipn.models import PayPalIPN
|
||||
from paypal.standard.ipn.signals import (payment_was_successful,
|
||||
payment_was_flagged, payment_was_refunded, payment_was_reversed,
|
||||
recurring_skipped, recurring_failed,
|
||||
recurring_create, recurring_payment, recurring_cancel)
|
||||
|
||||
|
||||
# Parameters are all bytestrings, so we can construct a bytestring
|
||||
# request the same way that Paypal does.
|
||||
|
||||
IPN_POST_PARAMS = {
|
||||
"protection_eligibility": b("Ineligible"),
|
||||
"last_name": b("User"),
|
||||
"txn_id": b("51403485VH153354B"),
|
||||
"receiver_email": b(settings.PAYPAL_RECEIVER_EMAIL),
|
||||
"payment_status": b("Completed"),
|
||||
"payment_gross": b("10.00"),
|
||||
"tax": b("0.00"),
|
||||
"residence_country": b("US"),
|
||||
"invoice": b("0004"),
|
||||
"payer_status": b("verified"),
|
||||
"txn_type": b("express_checkout"),
|
||||
"handling_amount": b("0.00"),
|
||||
"payment_date": b("23:04:06 Feb 02, 2009 PST"),
|
||||
"first_name": b("J\xF6rg"),
|
||||
"item_name": b(""),
|
||||
"charset": b("windows-1252"),
|
||||
"custom": b("website_id=13&user_id=21"),
|
||||
"notify_version": b("2.6"),
|
||||
"transaction_subject": b(""),
|
||||
"test_ipn": b("1"),
|
||||
"item_number": b(""),
|
||||
"receiver_id": b("258DLEHY2BDK6"),
|
||||
"payer_id": b("BN5JZ2V7MLEV4"),
|
||||
"verify_sign": b("An5ns1Kso7MWUdW4ErQKJJJ4qi4-AqdZy6dD.sGO3sDhTf1wAbuO2IZ7"),
|
||||
"payment_fee": b("0.59"),
|
||||
"mc_fee": b("0.59"),
|
||||
"mc_currency": b("USD"),
|
||||
"shipping": b("0.00"),
|
||||
"payer_email": b("bishan_1233269544_per@gmail.com"),
|
||||
"payment_type": b("instant"),
|
||||
"mc_gross": b("10.00"),
|
||||
"quantity": b("1"),
|
||||
}
|
||||
|
||||
|
||||
class IPNTestBase(TestCase):
|
||||
urls = 'paypal.standard.ipn.tests.test_urls'
|
||||
|
||||
def setUp(self):
|
||||
self.payment_was_successful_receivers = payment_was_successful.receivers
|
||||
self.payment_was_flagged_receivers = payment_was_flagged.receivers
|
||||
self.payment_was_refunded_receivers = payment_was_refunded.receivers
|
||||
self.payment_was_reversed_receivers = payment_was_reversed.receivers
|
||||
self.recurring_skipped_receivers = recurring_skipped.receivers
|
||||
self.recurring_failed_receivers = recurring_failed.receivers
|
||||
self.recurring_create_receivers = recurring_create.receivers
|
||||
self.recurring_payment_receivers = recurring_payment.receivers
|
||||
self.recurring_cancel_receivers = recurring_cancel.receivers
|
||||
|
||||
payment_was_successful.receivers = []
|
||||
payment_was_flagged.receivers = []
|
||||
payment_was_refunded.receivers = []
|
||||
payment_was_reversed.receivers = []
|
||||
recurring_skipped.receivers = []
|
||||
recurring_failed.receivers = []
|
||||
recurring_create.receivers = []
|
||||
recurring_payment.receivers = []
|
||||
recurring_cancel.receivers = []
|
||||
|
||||
def tearDown(self):
|
||||
payment_was_successful.receivers = self.payment_was_successful_receivers
|
||||
payment_was_flagged.receivers = self.payment_was_flagged_receivers
|
||||
payment_was_refunded.receivers = self.payment_was_refunded_receivers
|
||||
payment_was_reversed.receivers = self.payment_was_reversed_receivers
|
||||
recurring_skipped.receivers = self.recurring_skipped_receivers
|
||||
recurring_failed.receivers = self.recurring_failed_receivers
|
||||
recurring_create.receivers = self.recurring_create_receivers
|
||||
recurring_payment.receivers = self.recurring_payment_receivers
|
||||
recurring_cancel.receivers = self.recurring_cancel_receivers
|
||||
|
||||
def paypal_post(self, params):
|
||||
"""
|
||||
Does an HTTP POST the way that PayPal does, using the params given.
|
||||
"""
|
||||
# We build params into a bytestring ourselves, to avoid some encoding
|
||||
# processing that is done by the test client.
|
||||
post_data = urlencode(params)
|
||||
return self.client.post("/ipn/", post_data, content_type='application/x-www-form-urlencoded')
|
||||
|
||||
def assertGotSignal(self, signal, flagged, params=IPN_POST_PARAMS):
|
||||
# Check the signal was sent. These get lost if they don't reference self.
|
||||
self.got_signal = False
|
||||
self.signal_obj = None
|
||||
|
||||
def handle_signal(sender, **kwargs):
|
||||
self.got_signal = True
|
||||
self.signal_obj = sender
|
||||
|
||||
signal.connect(handle_signal)
|
||||
response = self.paypal_post(params)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
ipns = PayPalIPN.objects.all()
|
||||
self.assertEqual(len(ipns), 1)
|
||||
ipn_obj = ipns[0]
|
||||
self.assertEqual(ipn_obj.flag, flagged)
|
||||
|
||||
self.assertTrue(self.got_signal)
|
||||
self.assertEqual(self.signal_obj, ipn_obj)
|
||||
return ipn_obj
|
||||
|
||||
def assertFlagged(self, updates, flag_info):
|
||||
params = IPN_POST_PARAMS.copy()
|
||||
params.update(updates)
|
||||
response = self.paypal_post(params)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
ipn_obj = PayPalIPN.objects.all()[0]
|
||||
self.assertEqual(ipn_obj.flag, True)
|
||||
self.assertEqual(ipn_obj.flag_info, flag_info)
|
||||
return ipn_obj
|
||||
|
||||
|
||||
class IPNTest(IPNTestBase):
|
||||
|
||||
def setUp(self):
|
||||
# Monkey patch over PayPalIPN to make it get a VERFIED response.
|
||||
self.old_postback = PayPalIPN._postback
|
||||
PayPalIPN._postback = lambda self: b("VERIFIED")
|
||||
|
||||
def tearDown(self):
|
||||
PayPalIPN._postback = self.old_postback
|
||||
|
||||
def test_correct_ipn(self):
|
||||
ipn_obj = self.assertGotSignal(payment_was_successful, False)
|
||||
# Check some encoding issues:
|
||||
self.assertEqual(ipn_obj.first_name, u"J\u00f6rg")
|
||||
|
||||
def test_failed_ipn(self):
|
||||
PayPalIPN._postback = lambda self: b("INVALID")
|
||||
self.assertGotSignal(payment_was_flagged, True)
|
||||
|
||||
def test_ipn_missing_charset(self):
|
||||
params = IPN_POST_PARAMS.copy()
|
||||
del params['charset']
|
||||
self.assertGotSignal(payment_was_flagged, True, params=params)
|
||||
|
||||
def test_refunded_ipn(self):
|
||||
update = {
|
||||
"payment_status": "Refunded"
|
||||
}
|
||||
params = IPN_POST_PARAMS.copy()
|
||||
params.update(update)
|
||||
|
||||
self.assertGotSignal(payment_was_refunded, False, params)
|
||||
|
||||
def test_with_na_date(self):
|
||||
update = {
|
||||
"payment_status": "Refunded",
|
||||
"time_created": "N/A"
|
||||
}
|
||||
params = IPN_POST_PARAMS.copy()
|
||||
params.update(update)
|
||||
|
||||
self.assertGotSignal(payment_was_refunded, False, params)
|
||||
|
||||
def test_reversed_ipn(self):
|
||||
update = {
|
||||
"payment_status": "Reversed"
|
||||
}
|
||||
params = IPN_POST_PARAMS.copy()
|
||||
params.update(update)
|
||||
|
||||
self.assertGotSignal(payment_was_reversed, False, params)
|
||||
|
||||
def test_incorrect_receiver_email(self):
|
||||
update = {"receiver_email": "incorrect_email@someotherbusiness.com"}
|
||||
flag_info = "Invalid receiver_email. (incorrect_email@someotherbusiness.com)"
|
||||
self.assertFlagged(update, flag_info)
|
||||
|
||||
def test_invalid_payment_status(self):
|
||||
update = {"payment_status": "Failure"}
|
||||
flag_info = u"Invalid payment_status. (Failure)"
|
||||
self.assertFlagged(update, flag_info)
|
||||
|
||||
def test_vaid_payment_status_cancelled(self):
|
||||
update = {"payment_status": ST_PP_CANCELLED}
|
||||
params = IPN_POST_PARAMS.copy()
|
||||
params.update(update)
|
||||
response = self.paypal_post(params)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
ipn_obj = PayPalIPN.objects.all()[0]
|
||||
self.assertEqual(ipn_obj.flag, False)
|
||||
|
||||
def test_duplicate_txn_id(self):
|
||||
self.paypal_post(IPN_POST_PARAMS)
|
||||
self.paypal_post(IPN_POST_PARAMS)
|
||||
self.assertEqual(len(PayPalIPN.objects.all()), 2)
|
||||
ipn_obj = PayPalIPN.objects.order_by('-created_at', '-pk')[0]
|
||||
self.assertEqual(ipn_obj.flag, True)
|
||||
self.assertEqual(ipn_obj.flag_info, "Duplicate txn_id. (51403485VH153354B)")
|
||||
|
||||
def test_recurring_payment_skipped_ipn(self):
|
||||
update = {
|
||||
"recurring_payment_id": "BN5JZ2V7MLEV4",
|
||||
"txn_type": "recurring_payment_skipped",
|
||||
"txn_id": ""
|
||||
}
|
||||
params = IPN_POST_PARAMS.copy()
|
||||
params.update(update)
|
||||
|
||||
self.assertGotSignal(recurring_skipped, False, params)
|
||||
|
||||
def test_recurring_payment_failed_ipn(self):
|
||||
update = {
|
||||
"recurring_payment_id": "BN5JZ2V7MLEV4",
|
||||
"txn_type": "recurring_payment_failed",
|
||||
"txn_id": ""
|
||||
}
|
||||
params = IPN_POST_PARAMS.copy()
|
||||
params.update(update)
|
||||
|
||||
self.assertGotSignal(recurring_failed, False, params)
|
||||
|
||||
def test_recurring_payment_create_ipn(self):
|
||||
update = {
|
||||
"recurring_payment_id": "BN5JZ2V7MLEV4",
|
||||
"txn_type": "recurring_payment_profile_created",
|
||||
"txn_id": ""
|
||||
}
|
||||
params = IPN_POST_PARAMS.copy()
|
||||
params.update(update)
|
||||
|
||||
self.assertGotSignal(recurring_create, False, params)
|
||||
|
||||
def test_recurring_payment_cancel_ipn(self):
|
||||
update = {
|
||||
"recurring_payment_id": "BN5JZ2V7MLEV4",
|
||||
"txn_type": "recurring_payment_profile_cancel",
|
||||
"txn_id": ""
|
||||
}
|
||||
params = IPN_POST_PARAMS.copy()
|
||||
params.update(update)
|
||||
|
||||
self.assertGotSignal(recurring_cancel, False, params)
|
||||
|
||||
def test_recurring_payment_ipn(self):
|
||||
"""
|
||||
The wat the code is written in
|
||||
PayPalIPN.send_signals the recurring_payment
|
||||
will never be sent because the paypal ipn
|
||||
contains a txn_id, if this test failes you
|
||||
might break some compatibility
|
||||
"""
|
||||
update = {
|
||||
"recurring_payment_id": "BN5JZ2V7MLEV4",
|
||||
"txn_type": "recurring_payment",
|
||||
}
|
||||
params = IPN_POST_PARAMS.copy()
|
||||
params.update(update)
|
||||
|
||||
self.got_signal = False
|
||||
self.signal_obj = None
|
||||
|
||||
def handle_signal(sender, **kwargs):
|
||||
self.got_signal = True
|
||||
self.signal_obj = sender
|
||||
|
||||
recurring_payment.connect(handle_signal)
|
||||
response = self.paypal_post(params)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
ipns = PayPalIPN.objects.all()
|
||||
self.assertEqual(len(ipns), 1)
|
||||
self.assertFalse(self.got_signal)
|
||||
|
||||
def test_posted_params_attribute(self):
|
||||
params = {'btn_id1': b('3453595'),
|
||||
'business': b('email-facilitator@gmail.com'),
|
||||
'charset': b('windows-1252'),
|
||||
'custom': b('blahblah'),
|
||||
"first_name": b("J\xF6rg"),
|
||||
'ipn_track_id': b('a48170aadb705'),
|
||||
'item_name1': b('Romanescoins'),
|
||||
'item_number1': b(''),
|
||||
'last_name': b('LASTNAME'),
|
||||
'mc_currency': b('EUR'),
|
||||
'mc_fee': b('0.35'),
|
||||
'mc_gross': b('3.00'),
|
||||
'mc_gross_1': b('3.00'),
|
||||
'mc_handling': b('0.00'),
|
||||
'mc_handling1': b('0.00'),
|
||||
'mc_shipping': b('0.00'),
|
||||
'mc_shipping1': b('0.00'),
|
||||
'notify_version': b('3.8'),
|
||||
'num_cart_items': b('1'),
|
||||
'payer_email': b('email@gmail.com'),
|
||||
'payer_id': b('6EQ6SKDFMPU36'),
|
||||
'payer_status': b('verified'),
|
||||
'payment_date': b('03:06:57 Jun 27, 2014 PDT'),
|
||||
'payment_fee': b(''),
|
||||
'payment_gross': b(''),
|
||||
'payment_status': b('Completed'),
|
||||
'payment_type': b('instant'),
|
||||
'protection_eligibility': b('Ineligible'),
|
||||
'quantity1': b('3'),
|
||||
'receiver_email': b('email-facilitator@gmail.com'),
|
||||
'receiver_id': b('UCWM6R2TARF36'),
|
||||
'residence_country': b('FR'),
|
||||
'tax': b('0.00'),
|
||||
'tax1': b('0.00'),
|
||||
'test_ipn': b('1'),
|
||||
'transaction_subject': b('blahblah'),
|
||||
'txn_id': b('KW31266C37C2593K4'),
|
||||
'txn_type': b('cart'),
|
||||
'verify_sign': b('A_SECRET_CODE')}
|
||||
self.paypal_post(params)
|
||||
ipn = PayPalIPN.objects.get()
|
||||
self.assertEqual(ipn.posted_data_dict['quantity1'], '3')
|
||||
self.assertEqual(ipn.posted_data_dict['first_name'], u"J\u00f6rg")
|
||||
|
||||
class IPNPostbackTest(IPNTestBase):
|
||||
"""
|
||||
Tests an actual postback to PayPal server.
|
||||
"""
|
||||
def test_postback(self):
|
||||
# Incorrect signature means we will always get failure
|
||||
self.assertFlagged({}, u'Invalid postback. (INVALID)')
|
|
@ -1,5 +0,0 @@
|
|||
from django.conf.urls import patterns
|
||||
|
||||
urlpatterns = patterns('paypal.standard.ipn.views',
|
||||
(r'^ipn/$', 'ipn'),
|
||||
)
|
5
vendor/paypal/standard/ipn/urls.py
vendored
5
vendor/paypal/standard/ipn/urls.py
vendored
|
@ -1,5 +0,0 @@
|
|||
from django.conf.urls import patterns, url
|
||||
|
||||
urlpatterns = patterns('paypal.standard.ipn.views',
|
||||
url(r'^$', 'ipn', name="paypal-ipn"),
|
||||
)
|
78
vendor/paypal/standard/ipn/views.py
vendored
78
vendor/paypal/standard/ipn/views.py
vendored
|
@ -1,78 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.http import HttpResponse, QueryDict
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from vendor.paypal.standard.ipn.forms import PayPalIPNForm
|
||||
from vendor.paypal.standard.ipn.models import PayPalIPN
|
||||
|
||||
|
||||
@require_POST
|
||||
@csrf_exempt
|
||||
def ipn(request, item_check_callable=None):
|
||||
"""
|
||||
PayPal IPN endpoint (notify_url).
|
||||
Used by both PayPal Payments Pro and Payments Standard to confirm transactions.
|
||||
http://tinyurl.com/d9vu9d
|
||||
|
||||
PayPal IPN Simulator:
|
||||
https://developer.paypal.com/cgi-bin/devscr?cmd=_ipn-link-session
|
||||
"""
|
||||
#TODO: Clean up code so that we don't need to set None here and have a lot
|
||||
# of if checks just to determine if flag is set.
|
||||
flag = None
|
||||
ipn_obj = None
|
||||
|
||||
# Clean up the data as PayPal sends some weird values such as "N/A"
|
||||
# Also, need to cope with custom encoding, which is stored in the body (!).
|
||||
# Assuming the tolerant parsing of QueryDict and an ASCII-like encoding,
|
||||
# such as windows-1252, latin1 or UTF8, the following will work:
|
||||
|
||||
encoding = request.REQUEST.get('charset', None)
|
||||
|
||||
if encoding is None:
|
||||
flag = "Invalid form - no charset passed, can't decode"
|
||||
data = None
|
||||
else:
|
||||
try:
|
||||
data = QueryDict(request.body, encoding=encoding).copy()
|
||||
except LookupError:
|
||||
data = None
|
||||
flag = "Invalid form - invalid charset"
|
||||
|
||||
if data is not None:
|
||||
date_fields = ('time_created', 'payment_date', 'next_payment_date',
|
||||
'subscr_date', 'subscr_effective')
|
||||
for date_field in date_fields:
|
||||
if data.get(date_field) == 'N/A':
|
||||
del data[date_field]
|
||||
|
||||
form = PayPalIPNForm(data)
|
||||
if form.is_valid():
|
||||
try:
|
||||
#When commit = False, object is returned without saving to DB.
|
||||
ipn_obj = form.save(commit=False)
|
||||
except Exception as e:
|
||||
flag = "Exception while processing. (%s)" % e
|
||||
else:
|
||||
flag = "Invalid form. (%s)" % form.errors
|
||||
|
||||
if ipn_obj is None:
|
||||
ipn_obj = PayPalIPN()
|
||||
|
||||
#Set query params and sender's IP address
|
||||
ipn_obj.initialize(request)
|
||||
|
||||
if flag is not None:
|
||||
#We save errors in the flag field
|
||||
ipn_obj.set_flag(flag)
|
||||
else:
|
||||
# Secrets should only be used over SSL.
|
||||
if request.is_secure() and 'secret' in request.GET:
|
||||
ipn_obj.verify_secret(form, request.GET['secret'])
|
||||
else:
|
||||
ipn_obj.verify(item_check_callable)
|
||||
|
||||
ipn_obj.save()
|
||||
ipn_obj.send_signals()
|
||||
return HttpResponse("OKAY")
|
337
vendor/paypal/standard/models.py
vendored
337
vendor/paypal/standard/models.py
vendored
|
@ -1,337 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.db import models
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
from paypal.standard.helpers import duplicate_txn_id, check_secret
|
||||
from paypal.standard.conf import RECEIVER_EMAIL, POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT
|
||||
|
||||
ST_PP_ACTIVE = 'Active'
|
||||
ST_PP_CANCELLED = 'Cancelled'
|
||||
ST_PP_CANCELED_REVERSAL = 'Canceled_Reversal'
|
||||
ST_PP_CLEARED = 'Cleared'
|
||||
ST_PP_COMPLETED = 'Completed'
|
||||
ST_PP_CREATED = 'Created'
|
||||
ST_PP_DENIED = 'Denied'
|
||||
ST_PP_EXPIRED = 'Expired'
|
||||
ST_PP_FAILED = 'Failed'
|
||||
ST_PP_PAID = 'Paid'
|
||||
ST_PP_PENDING = 'Pending'
|
||||
ST_PP_PROCESSED = 'Processed'
|
||||
ST_PP_REFUNDED = 'Refunded'
|
||||
ST_PP_REFUSED = 'Refused'
|
||||
ST_PP_REVERSED = 'Reversed'
|
||||
ST_PP_REWARDED = 'Rewarded'
|
||||
ST_PP_UNCLAIMED = 'Unclaimed'
|
||||
ST_PP_UNCLEARED = 'Uncleared'
|
||||
ST_PP_VOIDED = 'Voided'
|
||||
|
||||
try:
|
||||
from idmapper.models import SharedMemoryModel as Model
|
||||
except ImportError:
|
||||
Model = models.Model
|
||||
|
||||
|
||||
class PayPalStandardBase(Model):
|
||||
"""Meta class for common variables shared by IPN and PDT: http://tinyurl.com/cuq6sj"""
|
||||
# @@@ Might want to add all these one distant day.
|
||||
# FLAG_CODE_CHOICES = (
|
||||
# PAYMENT_STATUS_CHOICES = "Canceled_ Reversal Completed Denied Expired Failed Pending Processed Refunded Reversed Voided".split()
|
||||
PAYMENT_STATUS_CHOICES = (ST_PP_ACTIVE, ST_PP_CANCELLED, ST_PP_CANCELED_REVERSAL,
|
||||
ST_PP_CLEARED,
|
||||
ST_PP_COMPLETED, ST_PP_CREATED, ST_PP_DENIED,
|
||||
ST_PP_EXPIRED, ST_PP_FAILED, ST_PP_PAID,
|
||||
ST_PP_PENDING, ST_PP_PROCESSED, ST_PP_REFUNDED,
|
||||
ST_PP_REFUSED, ST_PP_REVERSED, ST_PP_REWARDED,
|
||||
ST_PP_UNCLAIMED, ST_PP_UNCLEARED, ST_PP_VOIDED,)
|
||||
# AUTH_STATUS_CHOICES = "Completed Pending Voided".split()
|
||||
# ADDRESS_STATUS_CHOICES = "confirmed unconfirmed".split()
|
||||
# PAYER_STATUS_CHOICES = "verified / unverified".split()
|
||||
# PAYMENT_TYPE_CHOICES = "echeck / instant.split()
|
||||
# PENDING_REASON = "address authorization echeck intl multi-currency unilateral upgrade verify other".split()
|
||||
# REASON_CODE = "chargeback guarantee buyer_complaint refund other".split()
|
||||
# TRANSACTION_ENTITY_CHOICES = "auth reauth order payment".split()
|
||||
|
||||
# Transaction and Notification-Related Variables
|
||||
business = models.CharField(max_length=127, blank=True, help_text="Email where the money was sent.")
|
||||
charset = models.CharField(max_length=32, blank=True)
|
||||
custom = models.CharField(max_length=255, blank=True)
|
||||
notify_version = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
parent_txn_id = models.CharField("Parent Transaction ID", max_length=19, blank=True)
|
||||
receiver_email = models.EmailField(max_length=127, blank=True)
|
||||
receiver_id = models.CharField(max_length=127, blank=True) # 258DLEHY2BDK6
|
||||
residence_country = models.CharField(max_length=2, blank=True)
|
||||
test_ipn = models.BooleanField(default=False, blank=True)
|
||||
txn_id = models.CharField("Transaction ID", max_length=19, blank=True, help_text="PayPal transaction ID.",
|
||||
db_index=True)
|
||||
txn_type = models.CharField("Transaction Type", max_length=128, blank=True, help_text="PayPal transaction type.")
|
||||
verify_sign = models.CharField(max_length=255, blank=True)
|
||||
|
||||
# Buyer Information Variables
|
||||
address_country = models.CharField(max_length=64, blank=True)
|
||||
address_city = models.CharField(max_length=40, blank=True)
|
||||
address_country_code = models.CharField(max_length=64, blank=True, help_text="ISO 3166")
|
||||
address_name = models.CharField(max_length=128, blank=True)
|
||||
address_state = models.CharField(max_length=40, blank=True)
|
||||
address_status = models.CharField(max_length=11, blank=True)
|
||||
address_street = models.CharField(max_length=200, blank=True)
|
||||
address_zip = models.CharField(max_length=20, blank=True)
|
||||
contact_phone = models.CharField(max_length=20, blank=True)
|
||||
first_name = models.CharField(max_length=64, blank=True)
|
||||
last_name = models.CharField(max_length=64, blank=True)
|
||||
payer_business_name = models.CharField(max_length=127, blank=True)
|
||||
payer_email = models.CharField(max_length=127, blank=True)
|
||||
payer_id = models.CharField(max_length=13, blank=True)
|
||||
|
||||
# Payment Information Variables
|
||||
auth_amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
auth_exp = models.CharField(max_length=28, blank=True)
|
||||
auth_id = models.CharField(max_length=19, blank=True)
|
||||
auth_status = models.CharField(max_length=9, blank=True)
|
||||
exchange_rate = models.DecimalField(max_digits=64, decimal_places=16, default=0, blank=True, null=True)
|
||||
invoice = models.CharField(max_length=127, blank=True)
|
||||
item_name = models.CharField(max_length=127, blank=True)
|
||||
item_number = models.CharField(max_length=127, blank=True)
|
||||
mc_currency = models.CharField(max_length=32, default="USD", blank=True)
|
||||
mc_fee = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
mc_gross = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
mc_handling = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
mc_shipping = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
memo = models.CharField(max_length=255, blank=True)
|
||||
num_cart_items = models.IntegerField(blank=True, default=0, null=True)
|
||||
option_name1 = models.CharField(max_length=64, blank=True)
|
||||
option_name2 = models.CharField(max_length=64, blank=True)
|
||||
payer_status = models.CharField(max_length=10, blank=True)
|
||||
payment_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
|
||||
payment_gross = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
payment_status = models.CharField(max_length=17, blank=True)
|
||||
payment_type = models.CharField(max_length=7, blank=True)
|
||||
pending_reason = models.CharField(max_length=14, blank=True)
|
||||
protection_eligibility = models.CharField(max_length=32, blank=True)
|
||||
quantity = models.IntegerField(blank=True, default=1, null=True)
|
||||
reason_code = models.CharField(max_length=15, blank=True)
|
||||
remaining_settle = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
settle_amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
settle_currency = models.CharField(max_length=32, blank=True)
|
||||
shipping = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
shipping_method = models.CharField(max_length=255, blank=True)
|
||||
tax = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
transaction_entity = models.CharField(max_length=7, blank=True)
|
||||
|
||||
# Auction Variables
|
||||
auction_buyer_id = models.CharField(max_length=64, blank=True)
|
||||
auction_closing_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
|
||||
auction_multi_item = models.IntegerField(blank=True, default=0, null=True)
|
||||
for_auction = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
|
||||
# Recurring Payments Variables
|
||||
amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
amount_per_cycle = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
initial_payment_amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
next_payment_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
|
||||
outstanding_balance = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
payment_cycle = models.CharField(max_length=32, blank=True) #Monthly
|
||||
period_type = models.CharField(max_length=32, blank=True)
|
||||
product_name = models.CharField(max_length=128, blank=True)
|
||||
product_type = models.CharField(max_length=128, blank=True)
|
||||
profile_status = models.CharField(max_length=32, blank=True)
|
||||
recurring_payment_id = models.CharField(max_length=128, blank=True) # I-FA4XVST722B9
|
||||
rp_invoice_id = models.CharField(max_length=127, blank=True) # 1335-7816-2936-1451
|
||||
time_created = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
|
||||
|
||||
# Subscription Variables
|
||||
amount1 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
amount2 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
amount3 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
mc_amount1 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
mc_amount2 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
mc_amount3 = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
password = models.CharField(max_length=24, blank=True)
|
||||
period1 = models.CharField(max_length=32, blank=True)
|
||||
period2 = models.CharField(max_length=32, blank=True)
|
||||
period3 = models.CharField(max_length=32, blank=True)
|
||||
reattempt = models.CharField(max_length=1, blank=True)
|
||||
recur_times = models.IntegerField(blank=True, default=0, null=True)
|
||||
recurring = models.CharField(max_length=1, blank=True)
|
||||
retry_at = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
|
||||
subscr_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
|
||||
subscr_effective = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
|
||||
subscr_id = models.CharField(max_length=19, blank=True)
|
||||
username = models.CharField(max_length=64, blank=True)
|
||||
|
||||
# Dispute Resolution Variables
|
||||
case_creation_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
|
||||
case_id = models.CharField(max_length=14, blank=True)
|
||||
case_type = models.CharField(max_length=24, blank=True)
|
||||
|
||||
# Variables not categorized
|
||||
receipt_id = models.CharField(max_length=64, blank=True) # 1335-7816-2936-1451
|
||||
currency_code = models.CharField(max_length=32, default="USD", blank=True)
|
||||
handling_amount = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
transaction_subject = models.CharField(max_length=255, blank=True)
|
||||
|
||||
# @@@ Mass Pay Variables (Not Implemented, needs a separate model, for each transaction x)
|
||||
# fraud_managment_pending_filters_x = models.CharField(max_length=255, blank=True)
|
||||
# option_selection1_x = models.CharField(max_length=200, blank=True)
|
||||
# option_selection2_x = models.CharField(max_length=200, blank=True)
|
||||
# masspay_txn_id_x = models.CharField(max_length=19, blank=True)
|
||||
# mc_currency_x = models.CharField(max_length=32, default="USD", blank=True)
|
||||
# mc_fee_x = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
# mc_gross_x = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
# mc_handlingx = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
# payment_date = models.DateTimeField(blank=True, null=True, help_text="HH:MM:SS DD Mmm YY, YYYY PST")
|
||||
# payment_status = models.CharField(max_length=9, blank=True)
|
||||
# reason_code = models.CharField(max_length=15, blank=True)
|
||||
# receiver_email_x = models.EmailField(max_length=127, blank=True)
|
||||
# status_x = models.CharField(max_length=9, blank=True)
|
||||
# unique_id_x = models.CharField(max_length=13, blank=True)
|
||||
|
||||
# Non-PayPal Variables - full IPN/PDT query and time fields.
|
||||
ipaddress = models.GenericIPAddressField(blank=True, null=True)
|
||||
flag = models.BooleanField(default=False, blank=True)
|
||||
flag_code = models.CharField(max_length=16, blank=True)
|
||||
flag_info = models.TextField(blank=True)
|
||||
query = models.TextField(blank=True) # What Paypal sent to us initially
|
||||
response = models.TextField(blank=True) # What we got back from our request
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
# Where did it come from?
|
||||
# from_view = models.CharField(max_length=6, null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def __unicode__(self):
|
||||
if self.is_transaction():
|
||||
return self.format % ("Transaction", self.txn_id)
|
||||
else:
|
||||
return self.format % ("Recurring", self.recurring_payment_id)
|
||||
|
||||
@cached_property
|
||||
def posted_data_dict(self):
|
||||
"""
|
||||
All the data that PayPal posted to us, as a correctly parsed dictionary of values.
|
||||
"""
|
||||
if not self.query:
|
||||
return None
|
||||
from django.http import QueryDict
|
||||
roughdecode = dict(item.split('=', 1) for item in self.query.split('&'))
|
||||
encoding = roughdecode.get('charset', None)
|
||||
if encoding is None:
|
||||
return None
|
||||
query = self.query.encode('ascii')
|
||||
data = QueryDict(query, encoding=encoding)
|
||||
return data.dict()
|
||||
|
||||
def is_transaction(self):
|
||||
return len(self.txn_id) > 0
|
||||
|
||||
def is_refund(self):
|
||||
return self.payment_status == ST_PP_REFUNDED
|
||||
|
||||
def is_reversed(self):
|
||||
return self.payment_status == ST_PP_REVERSED
|
||||
|
||||
def is_recurring(self):
|
||||
return len(self.recurring_payment_id) > 0
|
||||
|
||||
def is_subscription_cancellation(self):
|
||||
return self.txn_type == "subscr_cancel"
|
||||
|
||||
def is_subscription_end_of_term(self):
|
||||
return self.txn_type == "subscr_eot"
|
||||
|
||||
def is_subscription_modified(self):
|
||||
return self.txn_type == "subscr_modify"
|
||||
|
||||
def is_subscription_signup(self):
|
||||
return self.txn_type == "subscr_signup"
|
||||
|
||||
def is_recurring_create(self):
|
||||
return self.txn_type == "recurring_payment_profile_created"
|
||||
|
||||
def is_recurring_payment(self):
|
||||
return self.txn_type == "recurring_payment"
|
||||
|
||||
def is_recurring_cancel(self):
|
||||
return self.txn_type == "recurring_payment_profile_cancel"
|
||||
|
||||
def is_recurring_skipped(self):
|
||||
return self.txn_type == "recurring_payment_skipped"
|
||||
|
||||
def is_recurring_failed(self):
|
||||
return self.txn_type == "recurring_payment_failed"
|
||||
|
||||
def set_flag(self, info, code=None):
|
||||
"""Sets a flag on the transaction and also sets a reason."""
|
||||
self.flag = True
|
||||
self.flag_info += info
|
||||
if code is not None:
|
||||
self.flag_code = code
|
||||
|
||||
def verify(self, item_check_callable=None):
|
||||
"""
|
||||
Verifies an IPN and a PDT.
|
||||
Checks for obvious signs of weirdness in the payment and flags appropriately.
|
||||
|
||||
Provide a callable that takes an instance of this class as a parameter and returns
|
||||
a tuple (False, None) if the item is valid. Should return (True, "reason") if the
|
||||
item isn't valid. Strange but backward compatible :) This function should check
|
||||
that `mc_gross`, `mc_currency` `item_name` and `item_number` are all correct.
|
||||
|
||||
"""
|
||||
self.response = self._postback().decode('ascii')
|
||||
self._verify_postback()
|
||||
if not self.flag:
|
||||
if self.is_transaction():
|
||||
if self.payment_status not in self.PAYMENT_STATUS_CHOICES:
|
||||
self.set_flag("Invalid payment_status. (%s)" % self.payment_status)
|
||||
if duplicate_txn_id(self):
|
||||
self.set_flag("Duplicate txn_id. (%s)" % self.txn_id)
|
||||
if self.receiver_email != RECEIVER_EMAIL:
|
||||
self.set_flag("Invalid receiver_email. (%s)" % self.receiver_email)
|
||||
if callable(item_check_callable):
|
||||
flag, reason = item_check_callable(self)
|
||||
if flag:
|
||||
self.set_flag(reason)
|
||||
else:
|
||||
# @@@ Run a different series of checks on recurring payments.
|
||||
pass
|
||||
|
||||
self.save()
|
||||
|
||||
def verify_secret(self, form_instance, secret):
|
||||
"""Verifies an IPN payment over SSL using EWP."""
|
||||
if not check_secret(form_instance, secret):
|
||||
self.set_flag("Invalid secret. (%s)") % secret
|
||||
self.save()
|
||||
|
||||
def get_endpoint(self):
|
||||
"""Set Sandbox endpoint if the test variable is present."""
|
||||
if self.test_ipn:
|
||||
return SANDBOX_POSTBACK_ENDPOINT
|
||||
else:
|
||||
return POSTBACK_ENDPOINT
|
||||
|
||||
def send_signals(self):
|
||||
"""Shout for the world to hear whether a txn was successful."""
|
||||
raise NotImplementedError
|
||||
|
||||
def initialize(self, request):
|
||||
"""Store the data we'll need to make the postback from the request object."""
|
||||
if request.method == 'GET':
|
||||
# PDT only - this data is currently unused
|
||||
self.query = request.META.get('QUERY_STRING', '')
|
||||
elif request.method == 'POST':
|
||||
# The following works if paypal sends an ASCII bytestring, which it does.
|
||||
self.query = request.body.decode('ascii')
|
||||
self.ipaddress = request.META.get('REMOTE_ADDR', '')
|
||||
|
||||
def _postback(self):
|
||||
"""Perform postback to PayPal and store the response in self.response."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _verify_postback(self):
|
||||
"""Check self.response is valid andcall self.set_flag if there is an error."""
|
||||
raise NotImplementedError
|
0
vendor/paypal/standard/pdt/__init__.py
vendored
0
vendor/paypal/standard/pdt/__init__.py
vendored
121
vendor/paypal/standard/pdt/admin.py
vendored
121
vendor/paypal/standard/pdt/admin.py
vendored
|
@ -1,121 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.contrib import admin
|
||||
from paypal.standard.pdt.models import PayPalPDT
|
||||
|
||||
|
||||
# ToDo: How similiar is this to PayPalIPNAdmin? Could we just inherit off one common admin model?
|
||||
class PayPalPDTAdmin(admin.ModelAdmin):
|
||||
date_hierarchy = 'payment_date'
|
||||
fieldsets = (
|
||||
(None, {
|
||||
"fields":
|
||||
['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',
|
||||
],
|
||||
}),
|
||||
("Address", {
|
||||
"description": "The address of the Buyer.",
|
||||
'classes': ('collapse',),
|
||||
"fields":
|
||||
['address_city',
|
||||
'address_country',
|
||||
'address_country_code',
|
||||
'address_name',
|
||||
'address_state',
|
||||
'address_status',
|
||||
'address_street',
|
||||
'address_zip',
|
||||
],
|
||||
}),
|
||||
("Buyer", {
|
||||
"description": "The information about the Buyer.",
|
||||
'classes': ('collapse',),
|
||||
"fields":
|
||||
['first_name',
|
||||
'last_name',
|
||||
'payer_business_name',
|
||||
'payer_email',
|
||||
'payer_id',
|
||||
'payer_status',
|
||||
'contact_phone',
|
||||
'residence_country'
|
||||
],
|
||||
}),
|
||||
("Seller", {
|
||||
"description": "The information about the Seller.",
|
||||
'classes': ('collapse',),
|
||||
"fields":
|
||||
['business',
|
||||
'item_name',
|
||||
'item_number',
|
||||
'quantity',
|
||||
'receiver_email',
|
||||
'receiver_id',
|
||||
'custom',
|
||||
'invoice',
|
||||
'memo',
|
||||
],
|
||||
}),
|
||||
("Subscriber", {
|
||||
"description": "The information about the Subscription.",
|
||||
'classes': ('collapse',),
|
||||
"fields":
|
||||
['subscr_id',
|
||||
'subscr_date',
|
||||
'subscr_effective',
|
||||
],
|
||||
}),
|
||||
("Recurring", {
|
||||
"description": "Information about recurring Payments.",
|
||||
"classes": ("collapse",),
|
||||
"fields":
|
||||
['profile_status',
|
||||
'initial_payment_amount',
|
||||
'amount_per_cycle',
|
||||
'outstanding_balance',
|
||||
'period_type',
|
||||
'product_name',
|
||||
'product_type',
|
||||
'recurring_payment_id',
|
||||
'receipt_id',
|
||||
'next_payment_date',
|
||||
],
|
||||
}),
|
||||
("Admin", {
|
||||
"description": "Additional Info.",
|
||||
"classes": ('collapse',),
|
||||
"fields":
|
||||
['test_ipn',
|
||||
'ipaddress',
|
||||
'query',
|
||||
'flag_code',
|
||||
'flag_info',
|
||||
],
|
||||
}),
|
||||
)
|
||||
list_display = ["__unicode__",
|
||||
"flag",
|
||||
"invoice",
|
||||
"custom",
|
||||
"payment_status",
|
||||
"created_at",
|
||||
]
|
||||
search_fields = ["txn_id",
|
||||
"recurring_payment_id",
|
||||
]
|
||||
|
||||
|
||||
admin.site.register(PayPalPDT, PayPalPDTAdmin)
|
9
vendor/paypal/standard/pdt/forms.py
vendored
9
vendor/paypal/standard/pdt/forms.py
vendored
|
@ -1,9 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from paypal.standard.forms import PayPalStandardBaseForm
|
||||
from paypal.standard.pdt.models import PayPalPDT
|
||||
|
||||
|
||||
class PayPalPDTForm(PayPalStandardBaseForm):
|
||||
class Meta:
|
||||
model = PayPalPDT
|
95
vendor/paypal/standard/pdt/models.py
vendored
95
vendor/paypal/standard/pdt/models.py
vendored
|
@ -1,95 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.http import QueryDict
|
||||
from django.utils.http import urlencode
|
||||
from six.moves.urllib.request import urlopen
|
||||
from six.moves.urllib.parse import unquote_plus
|
||||
|
||||
from paypal.standard.models import PayPalStandardBase
|
||||
from paypal.standard.conf import POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT
|
||||
from paypal.standard.pdt.signals import pdt_successful, pdt_failed
|
||||
|
||||
# ### Todo: Move this logic to conf.py:
|
||||
# if paypal.standard.pdt is in installed apps
|
||||
# ... then check for this setting in conf.py
|
||||
class PayPalSettingsError(Exception):
|
||||
"""Raised when settings are incorrect."""
|
||||
|
||||
|
||||
try:
|
||||
IDENTITY_TOKEN = settings.PAYPAL_IDENTITY_TOKEN
|
||||
except:
|
||||
raise PayPalSettingsError(
|
||||
"You must set PAYPAL_IDENTITY_TOKEN in settings.py. Get this token by enabling PDT in your PayPal account.")
|
||||
|
||||
|
||||
class PayPalPDT(PayPalStandardBase):
|
||||
format = u"<PDT: %s %s>"
|
||||
|
||||
amt = models.DecimalField(max_digits=64, decimal_places=2, default=0, blank=True, null=True)
|
||||
cm = models.CharField(max_length=255, blank=True)
|
||||
sig = models.CharField(max_length=255, blank=True)
|
||||
tx = models.CharField(max_length=255, blank=True)
|
||||
st = models.CharField(max_length=32, blank=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "paypal_pdt"
|
||||
verbose_name = "PayPal PDT"
|
||||
|
||||
def _postback(self):
|
||||
"""
|
||||
Perform PayPal PDT Postback validation.
|
||||
Sends the transaction ID and business token to PayPal which responses with
|
||||
SUCCESS or FAILED.
|
||||
|
||||
"""
|
||||
postback_dict = dict(cmd="_notify-synch", at=IDENTITY_TOKEN, tx=self.tx)
|
||||
postback_params = urlencode(postback_dict)
|
||||
return urlopen(self.get_endpoint(), postback_params).read()
|
||||
|
||||
def get_endpoint(self):
|
||||
if getattr(settings, 'PAYPAL_TEST', True):
|
||||
return SANDBOX_POSTBACK_ENDPOINT
|
||||
else:
|
||||
return POSTBACK_ENDPOINT
|
||||
|
||||
def _verify_postback(self):
|
||||
# ### Now we don't really care what result was, just whether a flag was set or not.
|
||||
from paypal.standard.pdt.forms import PayPalPDTForm
|
||||
|
||||
# TODO: this needs testing and probably fixing under Python 3
|
||||
result = False
|
||||
response_list = self.response.split('\n')
|
||||
response_dict = {}
|
||||
for i, line in enumerate(response_list):
|
||||
unquoted_line = unquote_plus(line).strip()
|
||||
if i == 0:
|
||||
self.st = unquoted_line
|
||||
if self.st == "SUCCESS":
|
||||
result = True
|
||||
else:
|
||||
if self.st != "SUCCESS":
|
||||
self.set_flag(line)
|
||||
break
|
||||
try:
|
||||
if not unquoted_line.startswith(' -'):
|
||||
k, v = unquoted_line.split('=')
|
||||
response_dict[k.strip()] = v.strip()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
qd = QueryDict('', mutable=True)
|
||||
qd.update(response_dict)
|
||||
qd.update(dict(ipaddress=self.ipaddress, st=self.st, flag_info=self.flag_info, flag=self.flag,
|
||||
flag_code=self.flag_code))
|
||||
pdt_form = PayPalPDTForm(qd, instance=self)
|
||||
pdt_form.save(commit=False)
|
||||
|
||||
def send_signals(self):
|
||||
# Send the PDT signals...
|
||||
if self.flag:
|
||||
pdt_failed.send(sender=self)
|
||||
else:
|
||||
pdt_successful.send(sender=self)
|
25
vendor/paypal/standard/pdt/signals.py
vendored
25
vendor/paypal/standard/pdt/signals.py
vendored
|
@ -1,25 +0,0 @@
|
|||
"""
|
||||
Note that sometimes you will get duplicate signals emitted, depending on configuration of your systems.
|
||||
If you do encounter this, you will need to add the "dispatch_uid" to your connect handlers:
|
||||
http://code.djangoproject.com/wiki/Signals#Helppost_saveseemstobeemittedtwiceforeachsave
|
||||
|
||||
"""
|
||||
from django.dispatch import Signal
|
||||
|
||||
# Sent when a payment is successfully processed.
|
||||
pdt_successful = Signal()
|
||||
|
||||
# Sent when a payment is flagged.
|
||||
pdt_failed = Signal()
|
||||
|
||||
# # Sent when a subscription was cancelled.
|
||||
# subscription_cancel = Signal()
|
||||
#
|
||||
# # Sent when a subscription expires.
|
||||
# subscription_eot = Signal()
|
||||
#
|
||||
# # Sent when a subscription was modified.
|
||||
# subscription_modify = Signal()
|
||||
#
|
||||
# # Sent when a subscription ends.
|
||||
# subscription_signup = Signal()
|
|
@ -1,38 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
{% ifequal pdt_obj.st 'SUCCESS' %}
|
||||
<h1>Transaction complete</h1>
|
||||
<p>Thank you for your payment</p>
|
||||
<p>Please print this page for your records</p>
|
||||
|
||||
<div>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Payer:</td>
|
||||
<td>{{ pdt_obj.first_name }} {{ pdt_obj.last_name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Payer Email:</td>
|
||||
<td>{{ pdt_obj.payer_email }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Amount:</td>
|
||||
<td>{{ pdt_obj.mc_currency }} {{ pdt_obj.mc_gross }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Reference:</td>
|
||||
<td>{{ pdt_obj.txn_id }}</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<h1>Transaction Failed</h1>
|
||||
<p>Sorry transaction failed, please try a different form of payment</p>
|
||||
{% endifequal %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
{{st}}
|
||||
mc_gross={{mc_gross}}
|
||||
invoice=66
|
||||
settle_amount=289.83
|
||||
protection_eligibility=Ineligible
|
||||
payer_id=8MZ9FQTSAMUPJ
|
||||
tax=0.00
|
||||
payment_date=04%3A53%3A52+Apr+12%2C+2009+PDT
|
||||
payment_status=Completed
|
||||
charset=windows-1252
|
||||
first_name=Test
|
||||
mc_fee=6.88
|
||||
exchange_rate=1.32876
|
||||
settle_currency=USD
|
||||
custom={{custom}}
|
||||
payer_status=verified
|
||||
business={{business}}
|
||||
quantity=1
|
||||
payer_email=buyer_1239119200_per%40yoursite.com
|
||||
txn_id={{txn_id}}
|
||||
payment_type=instant
|
||||
last_name=User
|
||||
receiver_email={{business}}
|
||||
payment_fee=
|
||||
receiver_id=746LDC2EQAP4W
|
||||
txn_type=web_accept
|
||||
item_name=Advertising+Campaign%3A+1+Month+%28225.00%29
|
||||
mc_currency=EUR
|
||||
item_number=
|
||||
residence_country=US
|
||||
handling_amount=0.00
|
||||
transaction_subject={{custom}}
|
||||
payment_gross=
|
||||
shipping=0.00
|
||||
-
|
1
vendor/paypal/standard/pdt/tests/__init__.py
vendored
1
vendor/paypal/standard/pdt/tests/__init__.py
vendored
|
@ -1 +0,0 @@
|
|||
from test_pdt import *
|
|
@ -1,31 +0,0 @@
|
|||
{% ifequal pdt_obj.st 'SUCCESS' %}
|
||||
<h1>Transaction complete</h1>
|
||||
<p>Thank you for your payment</p>
|
||||
<p>Please print this page for your records</p>
|
||||
|
||||
<div>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Payer:</td>
|
||||
<td>{{ pdt_obj.first_name }} {{ pdt_obj.last_name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Payer Email:</td>
|
||||
<td>{{ pdt_obj.payer_email }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Amount:</td>
|
||||
<td>{{ pdt_obj.mc_currency }} {{ pdt_obj.mc_gross }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Reference:</td>
|
||||
<td>{{ pdt_obj.txn_id }}</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<h1>Transaction Failed</h1>
|
||||
<p>Sorry transaction failed, please try a different form of payment</p>
|
||||
{% endifequal %}
|
120
vendor/paypal/standard/pdt/tests/test_pdt.py
vendored
120
vendor/paypal/standard/pdt/tests/test_pdt.py
vendored
|
@ -1,120 +0,0 @@
|
|||
"""
|
||||
run this with ./manage.py test website
|
||||
see http://www.djangoproject.com/documentation/testing/ for details
|
||||
"""
|
||||
import os
|
||||
from django.conf import settings
|
||||
from django.shortcuts import render_to_response
|
||||
from django.test import TestCase
|
||||
from paypal.standard.pdt.models import PayPalPDT
|
||||
from paypal.standard.pdt.signals import pdt_successful, pdt_failed
|
||||
|
||||
|
||||
class DummyPayPalPDT(object):
|
||||
def __init__(self, update_context_dict={}):
|
||||
self.context_dict = {'st': 'SUCCESS', 'custom': 'cb736658-3aad-4694-956f-d0aeade80194',
|
||||
'txn_id': '1ED550410S3402306', 'mc_gross': '225.00',
|
||||
'business': settings.PAYPAL_RECEIVER_EMAIL, 'error': 'Error code: 1234'}
|
||||
|
||||
self.context_dict.update(update_context_dict)
|
||||
self.response = ''
|
||||
|
||||
def update_with_get_params(self, get_params):
|
||||
if get_params.has_key('tx'):
|
||||
self.context_dict['txn_id'] = get_params.get('tx')
|
||||
if get_params.has_key('amt'):
|
||||
self.context_dict['mc_gross'] = get_params.get('amt')
|
||||
if get_params.has_key('cm'):
|
||||
self.context_dict['custom'] = get_params.get('cm')
|
||||
|
||||
def _postback(self, test=True):
|
||||
"""Perform a Fake PayPal PDT Postback request."""
|
||||
# @@@ would be cool if this could live in the test templates dir...
|
||||
return render_to_response("pdt/test_pdt_response.html", self.context_dict).content
|
||||
|
||||
|
||||
class PDTTest(TestCase):
|
||||
urls = "paypal.standard.pdt.tests.test_urls"
|
||||
template_dirs = [os.path.join(os.path.dirname(__file__), 'templates'), ]
|
||||
|
||||
def setUp(self):
|
||||
# set up some dummy PDT get parameters
|
||||
self.get_params = {"tx": "4WJ86550014687441", "st": "Completed", "amt": "225.00", "cc": "EUR",
|
||||
"cm": "a3e192b8-8fea-4a86-b2e8-d5bf502e36be", "item_number": "",
|
||||
"sig": "blahblahblah"}
|
||||
|
||||
# monkey patch the PayPalPDT._postback function
|
||||
self.dpppdt = DummyPayPalPDT()
|
||||
self.dpppdt.update_with_get_params(self.get_params)
|
||||
PayPalPDT._postback = self.dpppdt._postback
|
||||
|
||||
def test_verify_postback(self):
|
||||
dpppdt = DummyPayPalPDT()
|
||||
paypal_response = dpppdt._postback()
|
||||
assert ('SUCCESS' in paypal_response)
|
||||
self.assertEqual(len(PayPalPDT.objects.all()), 0)
|
||||
pdt_obj = PayPalPDT()
|
||||
pdt_obj.ipaddress = '127.0.0.1'
|
||||
pdt_obj.response = paypal_response
|
||||
pdt_obj._verify_postback()
|
||||
self.assertEqual(len(PayPalPDT.objects.all()), 0)
|
||||
self.assertEqual(pdt_obj.txn_id, '1ED550410S3402306')
|
||||
|
||||
def test_pdt(self):
|
||||
self.assertEqual(len(PayPalPDT.objects.all()), 0)
|
||||
self.dpppdt.update_with_get_params(self.get_params)
|
||||
paypal_response = self.client.get("/pdt/", self.get_params)
|
||||
self.assertContains(paypal_response, 'Transaction complete', status_code=200)
|
||||
self.assertEqual(len(PayPalPDT.objects.all()), 1)
|
||||
|
||||
def test_pdt_signals(self):
|
||||
self.successful_pdt_fired = False
|
||||
self.failed_pdt_fired = False
|
||||
|
||||
def successful_pdt(sender, **kwargs):
|
||||
self.successful_pdt_fired = True
|
||||
|
||||
pdt_successful.connect(successful_pdt)
|
||||
|
||||
def failed_pdt(sender, **kwargs):
|
||||
self.failed_pdt_fired = True
|
||||
|
||||
pdt_failed.connect(failed_pdt)
|
||||
|
||||
self.assertEqual(len(PayPalPDT.objects.all()), 0)
|
||||
paypal_response = self.client.get("/pdt/", self.get_params)
|
||||
self.assertContains(paypal_response, 'Transaction complete', status_code=200)
|
||||
self.assertEqual(len(PayPalPDT.objects.all()), 1)
|
||||
self.assertTrue(self.successful_pdt_fired)
|
||||
self.assertFalse(self.failed_pdt_fired)
|
||||
pdt_obj = PayPalPDT.objects.all()[0]
|
||||
self.assertEqual(pdt_obj.flag, False)
|
||||
|
||||
def test_double_pdt_get(self):
|
||||
self.assertEqual(len(PayPalPDT.objects.all()), 0)
|
||||
paypal_response = self.client.get("/pdt/", self.get_params)
|
||||
self.assertContains(paypal_response, 'Transaction complete', status_code=200)
|
||||
self.assertEqual(len(PayPalPDT.objects.all()), 1)
|
||||
pdt_obj = PayPalPDT.objects.all()[0]
|
||||
self.assertEqual(pdt_obj.flag, False)
|
||||
paypal_response = self.client.get("/pdt/", self.get_params)
|
||||
self.assertContains(paypal_response, 'Transaction complete', status_code=200)
|
||||
self.assertEqual(len(PayPalPDT.objects.all()), 1) # we don't create a new pdt
|
||||
pdt_obj = PayPalPDT.objects.all()[0]
|
||||
self.assertEqual(pdt_obj.flag, False)
|
||||
|
||||
def test_no_txn_id_in_pdt(self):
|
||||
self.dpppdt.context_dict.pop('txn_id')
|
||||
self.get_params = {}
|
||||
paypal_response = self.client.get("/pdt/", self.get_params)
|
||||
self.assertContains(paypal_response, 'Transaction Failed', status_code=200)
|
||||
self.assertEqual(len(PayPalPDT.objects.all()), 0)
|
||||
|
||||
def test_custom_passthrough(self):
|
||||
self.assertEqual(len(PayPalPDT.objects.all()), 0)
|
||||
self.dpppdt.update_with_get_params(self.get_params)
|
||||
paypal_response = self.client.get("/pdt/", self.get_params)
|
||||
self.assertContains(paypal_response, 'Transaction complete', status_code=200)
|
||||
self.assertEqual(len(PayPalPDT.objects.all()), 1)
|
||||
pdt_obj = PayPalPDT.objects.all()[0]
|
||||
self.assertEqual(pdt_obj.custom, self.get_params['cm'])
|
|
@ -1,5 +0,0 @@
|
|||
from django.conf.urls import patterns
|
||||
|
||||
urlpatterns = patterns('paypal.standard.pdt.views',
|
||||
(r'^pdt/$', 'pdt'),
|
||||
)
|
5
vendor/paypal/standard/pdt/urls.py
vendored
5
vendor/paypal/standard/pdt/urls.py
vendored
|
@ -1,5 +0,0 @@
|
|||
from django.conf.urls import patterns, url
|
||||
|
||||
urlpatterns = patterns('paypal.standard.pdt.views',
|
||||
url(r'^$', 'pdt', name="paypal-pdt"),
|
||||
)
|
66
vendor/paypal/standard/pdt/views.py
vendored
66
vendor/paypal/standard/pdt/views.py
vendored
|
@ -1,66 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render_to_response
|
||||
from django.views.decorators.http import require_GET
|
||||
from paypal.standard.pdt.models import PayPalPDT
|
||||
from paypal.standard.pdt.forms import PayPalPDTForm
|
||||
|
||||
|
||||
@require_GET
|
||||
def pdt(request, item_check_callable=None, template="pdt/pdt.html", context=None):
|
||||
"""Standard implementation of a view that processes PDT and then renders a template
|
||||
For more advanced uses, create your own view and call process_pdt.
|
||||
"""
|
||||
pdt_obj, failed = process_pdt(request, item_check_callable)
|
||||
|
||||
context = context or {}
|
||||
context.update({"failed": failed, "pdt_obj": pdt_obj})
|
||||
return render_to_response(template, context, RequestContext(request))
|
||||
|
||||
|
||||
def process_pdt(request, item_check_callable=None):
|
||||
"""
|
||||
Payment data transfer implementation: http://tinyurl.com/c9jjmw
|
||||
This function returns a tuple of pdt_obj and failed
|
||||
pdt_obj is an object of type PayPalPDT
|
||||
failed is a flag that indeicates whether the transaction was processed successfully
|
||||
"""
|
||||
|
||||
pdt_obj = None
|
||||
txn_id = request.GET.get('tx')
|
||||
failed = False
|
||||
if txn_id is not None:
|
||||
# If an existing transaction with the id tx exists: use it
|
||||
try:
|
||||
pdt_obj = PayPalPDT.objects.get(txn_id=txn_id)
|
||||
except PayPalPDT.DoesNotExist:
|
||||
# This is a new transaction so we continue processing PDT request
|
||||
pass
|
||||
|
||||
if pdt_obj is None:
|
||||
form = PayPalPDTForm(request.GET)
|
||||
if form.is_valid():
|
||||
try:
|
||||
pdt_obj = form.save(commit=False)
|
||||
except Exception as e:
|
||||
error = repr(e)
|
||||
failed = True
|
||||
else:
|
||||
error = form.errors
|
||||
failed = True
|
||||
|
||||
if failed:
|
||||
pdt_obj = PayPalPDT()
|
||||
pdt_obj.set_flag("Invalid form. %s" % error)
|
||||
|
||||
pdt_obj.initialize(request)
|
||||
|
||||
if not failed:
|
||||
# The PDT object gets saved during verify
|
||||
pdt_obj.verify(item_check_callable)
|
||||
else:
|
||||
pass # we ignore any PDT requests that don't have a transaction id
|
||||
|
||||
return (pdt_obj, failed)
|
||||
|
34
vendor/paypal/standard/widgets.py
vendored
34
vendor/paypal/standard/widgets.py
vendored
|
@ -1,34 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from django import forms
|
||||
from django.forms.utils import flatatt # Django 1.7 and later
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
|
||||
class ValueHiddenInput(forms.HiddenInput):
|
||||
"""
|
||||
Widget that renders only if it has a value.
|
||||
Used to remove unused fields from PayPal buttons.
|
||||
"""
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
if value is None:
|
||||
return u''
|
||||
else:
|
||||
return super(ValueHiddenInput, self).render(name, value, attrs)
|
||||
|
||||
|
||||
class ReservedValueHiddenInput(ValueHiddenInput):
|
||||
"""
|
||||
Overrides the default name attribute of the form.
|
||||
Used for the PayPal `return` field.
|
||||
"""
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
if value is None:
|
||||
value = ''
|
||||
final_attrs = self.build_attrs(attrs, type=self.input_type)
|
||||
if value != '':
|
||||
final_attrs['value'] = force_text(value)
|
||||
return mark_safe(u'<input%s />' % flatatt(final_attrs))
|
Loading…
Add table
Reference in a new issue