Welcome to django-paypal’s documentation!¶
Django PayPal is a pluggable application that implements with PayPal Payments Standard and Payments Pro.
Note
These docs are for django-paypal 2.0 - please ensure that corresponds to the version you are using!
Contents:
Install¶
Install into a virtualenv using pip:
pip install django-paypal
Or using the latest version from GitHub:
pip install git://github.com/spookylukey/django-paypal.git#egg=django-paypal
If you are using Django < 1.11, you should use django-paypal 0.5.x and refer to its documentation.
You will also need to edit your settings.py
, but the specifics depend on
whether you are using IPN/PDT/Pro.
In addition, you may need to take some precautions regarding REMOTE_ADDR
. In
all cases the user’s IP address is recorded when payments are recorded, since
this value can be useful in some cases. This value is taken from
request.META['REMOTE_ADDR']
. In some setups, however, it is possible that
this value is incorrect, or may not even validate as an IP address. If it is not
a valid IP address, then saving of IPN/PDT/NVP data will fail with a validation
error.
Due to the many different ways that systems can be configured, with different
proxies etc., correcting REMOTE_ADDR
is outside the scope of django-paypal.
You are advised to use a custom middleware or a solution like django-xff to ensure that
request.META['REMOTE_ADDR']
is correct or at least a valid IP address.
Overview¶
Before diving in, a quick review of PayPal’s payment methods is in order! PayPal Payments Standard is the “Buy it Now” buttons you may have seen floating around the internet. Buyers click on the button and are taken to PayPal’s website where they can pay for the product.
After this point, you can get notification of the payment using either Payment Data Transfer (PDT) or Instant Payment Notification (IPN).
For IPN, as soon as PayPal has taken payment details, it sends a message to a configured endpoint on your site in a separate HTTP request which you must handle. It will make multiple attempts for the case of connectivity issues. This method has the disadvantage that the user may arrive back at your site before your site has been notified about the transaction.
For PDT, PayPal redirects the user back to your website with a transaction ID in the query string. This has the disadvantage that if there is some kind of connection issue at this point, you won’t get notification. However, for the success case, you can be sure that the information about the transaction arrives at the same time as the users arrives back at your site.
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.
More recently, PayPal have implemented newer APIs, including “PayPal Payments Pro (Payflow Edition)”. This is not to be confused with the “Classic” PayPal Payments Pro that is implemented by django-paypal. “Payflow Edition” is not yet supported by django-paypal.
See also:
PayPal Payments Standard¶
Using PayPal Standard IPN¶
Edit
settings.py
and addpaypal.standard.ipn
to yourINSTALLED_APPS
:settings.py
:#... INSTALLED_APPS = [ #... 'paypal.standard.ipn', #... ]
For installations on which you want to use the sandbox, set PAYPAL_TEST to True.
PAYPAL_TEST = True
Create an instance of the
PayPalPaymentsForm
in the view where you would like to collect money.You must fill a dictionary with the information required to complete the payment, and pass it through the
initial
parameter when creating thePayPalPaymentsForm
.Please note: This form is not used like a normal Django form that posts back to a Django view. Rather it is a POST form that has a single button which sends all the data to PayPal. You simply need to call
render
on the instance in your template to write out the HTML, which includes the<form>
tag with the correct endpoint.views.py
:For a full list of variables that can be used in
paypal_dict
, see PayPal HTML variables documentation.Note
The names of these variables are not the same as the values returned on the IPN object.
payment.html
:... <h1>Show me the money!</h1> <!-- writes out the form tag automatically --> {{ form.render }}
The image used for the button can be customized using the Settings, or by subclassing
PayPalPaymentsForm
and overriding theget_image
method.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 correctnotify_url
add the following to yoururls.py
:from django.urls import path, include urlpatterns = [ path('paypal/', include("paypal.standard.ipn.urls")), ]
Whenever an IPN is processed a signal will be sent with the result of the transaction.
The IPN signals should be imported from
paypal.standard.ipn.signals
. They are:valid_ipn_received
This indicates a correct, non-duplicate IPN message from PayPal. The handler will receive a
paypal.standard.ipn.models.PayPalIPN
object as the sender. You must check:- the
payment_status
attribute, - the
business
attribute to make sure that the account receiving the payment is the expected one, - the amount and currency (see example below),
- any other attributes relevant for your case
- the
invalid_ipn_received
This is sent when a transaction was flagged - because of a failed check with PayPal, for example, or a duplicate transaction ID. You should never act on these, but might want to be notified of a problem.
Connect the signals to actions to perform the needed operations when a successful payment is received (as described in the Django Signals Documentation).
In the past there were more specific signals, but they were named confusingly, and used inconsistently, and are now deprecated. (See v0.1.5 docs for details)
Example code:
yourproject/hooks.py
from paypal.standard.models import ST_PP_COMPLETED from paypal.standard.ipn.signals import valid_ipn_received def show_me_the_money(sender, **kwargs): ipn_obj = sender if ipn_obj.payment_status == ST_PP_COMPLETED: # WARNING ! # Check that the receiver email is the same we previously # set on the `business` field. (The user could tamper with # that fields on the payment form before it goes to PayPal) if ipn_obj.receiver_email != "receiver_email@example.com": # Not a valid payment return # ALSO: for the same reason, you need to check the amount # received, `custom` etc. are all what you expect or what # is allowed. # Undertake some action depending upon `ipn_obj`. if ipn_obj.custom == "premium_plan": price = ... else: price = ... if ipn_obj.mc_gross == price and ipn_obj.mc_currency == 'USD': ... else: #... valid_ipn_received.connect(show_me_the_money)
Remember to ensure that import the hooks file is imported i.e. that you are connecting the signals when your project initializes. The standard way to do this is to create an AppConfig class and add a ready() method, in which you can register your signal handlers or import a module that does this.
See the IPN/PDT variables documentation for information about attributes on the IPN object that you can use.
You will also need to implement the
return
andcancel_return
views to handle someone returning from PayPal.Note that the
return
view may need@csrf_exempt
applied to it, because PayPal may POST to it (depending on the value of the rm parameter and possibly other settings), so it should be a custom view that doesn’t need to handle POSTs otherwise.When using PayPal Standard with Subscriptions this is not necessary since PayPal will route the user back to your site via GET.
For
return
, you need to cope with the possibility that the IPN has not yet been received and handled by the IPN listener you implemented (which can happen rarely), or that there was some kind of error with the IPN.
Testing¶
If you are attempting to test this in development, using the PayPal sandbox, and
your machine is behind a firewall/router and therefore is not publicly
accessible on the internet (this will be the case for most developer machines),
PayPal will not be able to post back to your view. You will need to use a tool
like https://ngrok.com/ to make your machine publicly accessible, and ensure
that you are sending PayPal your public URL, not localhost
, in the
notify_url
, return
and cancel_return
fields.
Simulator testing¶
The PayPal IPN simulator at https://developer.paypal.com/developer/ipnSimulator has some unfortunate bugs:
it doesn’t send the
encoding
parameter. django-paypal deals with this using a guess.the default ‘payment_date’ that is created for you is in the wrong format. You need to change it to something like:
23:04:06 Feb 02, 2015 PDT
Using PayPal 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.
However, PDT also has the disadvantage that you only get one chance to handle the notification - if there is a connection error for your user, the notification will never arrive at your site. For this reason, using PDT with django-paypal is not as well supported as IPN.
To use PDT:
Edit
settings.py
and addpaypal.standard.pdt
to yourINSTALLED_APPS
. Also setPAYPAL_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"
For installations on which you want to use the sandbox, set PAYPAL_TEST to True. While testing, ensure that when you create the PayPalPaymentsForm your receiver email (business parameter) is set to your sandbox account email too.
Create a view that uses
PayPalPaymentsForm
just like in Using PayPal Standard IPN.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.You will want to write a custom view that calls
paypal.standard.pdt.views.process_pdt
. This function returns a tuple containing(PDT object, flag)
, where theflag
is True if verification failed.Add the following to your urls.py:
from django.urls import path, include ... urlpatterns = [ path('your_return_url/', your_pdt_return_url_view, name="pdt_return_url"), ... ]
And then create a view that uses the
process_pdt
helper function:@require_GET def your_pdt_return_url_view(request): pdt_obj, failed = process_pdt(request) context = {"failed": failed, "pdt_obj": pdt_obj} if not failed: # WARNING! # Check that the receiver email is the same we previously # set on the business field request. (The user could tamper # with those fields on payment form before send it to PayPal) if pdt_obj.receiver_email == "receiver_email@example.com": # ALSO: for the same reason, you need to check the amount # received etc. are all what you expect. # Do whatever action is needed, then: return render(request, 'my_valid_payment_template', context) return render(request, 'my_non_valid_payment_template', context)
See the IPN/PDT variables documentation for information about attributes on the PDT object that you can use.
IPN/PDT variables¶
The data variables that are returned on the IPN object are documented here:
https://developer.paypal.com/docs/api-basics/notifications/ipn/IPNandPDTVariables/
Note
The names of these data variables are not the same as the values that you pass to PayPal - ensure you are looking at the right list!
The IPN/PDT objects are Django models with the same attributes as above,
converted to appropriate Python types e.g. Decimal
for money values.
Where a variable has multiple values represented with x in the above
documentation, the corresponding fields do not exist on the model objects.
However, you can still access the data using the posted_data_dict
attribute,
which returns a dictionary of all data sent by PayPal.
When processing these objects for handling payments, you need to pay particular
attention to payment_status
(docs).
You can use the ST_PP_*
constants in paypal.standard.models
to help.
Using PayPal Standard with Subscriptions¶
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": 'receiver_email@example.com',
"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": "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 Standard with Encrypted Buttons¶
Use this method to encrypt your button so values in the form can’t be tampered with. Thanks to Jon Atkinson for the tutorial.
Encrypted buttons require the M2Crypto library:
pip install M2Crypto
Encrypted buttons require certificates. Create a private key:
openssl genrsa -out paypal_private.pem 1024
Create a public key:
openssl req -new -key paypal_private.pem -x509 -days 365 -out paypal_public.pem
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.sandbox.paypal.com/us/cgi-bin/webscr?cmd=_profile-website-cert
Copy your
cert id
- you’ll need it in two steps. It’s on the screen where you uploaded your public key.Download PayPal’s public certificate - it’s also on that screen.
Edit your
settings.py
to include cert information:PAYPAL_PRIVATE_CERT = '/path/to/paypal_private.pem' PAYPAL_PUBLIC_CERT = '/path/to/paypal_public.pem' PAYPAL_CERT = '/path/to/paypal_cert.pem' PAYPAL_CERT_ID = 'get-from-paypal-website'
Swap out your unencrypted button for a
PayPalEncryptedPaymentsForm
:In views.py:
from paypal.standard.forms import PayPalEncryptedPaymentsForm def view_that_asks_for_money(request): ... # Create the instance. form = PayPalEncryptedPaymentsForm(initial=paypal_dict) # Works just like before! form.render()
If you need to use multiple certificates, you can pass the arguments directly to the PayPalEncryptedPaymentsForm as below:
In views.py:
from paypal.standard.forms import PayPalEncryptedPaymentsForm def view_that_asks_for_money(request): ... # Paypal Certificate Information paypal_private_cert = '/path/to/another/paypal_private.pem' paypal_public_cert = '/path/to/another/paypal_public.pem' paypal_cert = '/path/to/another/paypal_cert.pem' paypal_cert_id = 'another-paypal-id' # Create the instance. form = PayPalEncryptedPaymentsForm(initial=paypal_dict, private_cert=paypal_private_cert, public_cert=paypal_public_cert, paypal_cert=paypal_cert, cert_id=paypal_cert_id) ...
Using Website Payments Pro¶
Website Payments Pro models and helpers¶
-
class
paypal.pro.helpers.
PayPalWPP
¶ This class wraps the PayPal classic APIs, and sends data using Name-Value Pairs (NVP). The methods all take a
params
dictionary, the contents of which depend on the API being called. All parameter keys should be passed as lowercase values (unless otherwise specified), not the mixed case/upper case that is shown in PayPal docs.For API parameters, see the PayPal docs for more information:
The method calls all return a
paypal.pro.models.PayPalNVP
object on success. If an API call does not returnack=Success
orack=SuccessWithWarning
, aPayPalFailure
exception is raised. The NVP object is available as an attribute namednvp
on this exception object.-
__init__
(request=None, params=BASE_PARAMS)¶ Initialize the instance using an optional Django HTTP request object, and an optional parameter dictionary which should contain the keys
USER
,PWD
,SIGNATURE
andVERSION
. If the parameter dictionary is not supplied, these parameters will be taken from settingsPAYPAL_WPP_USER
,PAYPAL_WPP_PASSWORD
,PAYPAL_WPP_SIGNATURE
and the builtin version number.
-
createBillingAgreement
()¶ The CreateBillingAgreement API operation creates a billing agreement with a PayPal account holder. CreateBillingAgreement is only valid for reference transactions.
from paypal.pro.helpers import PayPalWPP def create_billing_agreement_view(request): wpp = PayPalWPP(request) token = request.GET.get('token') wpp.createBillingAgreement({'token': token})
-
createRecurringPaymentsProfile
()¶ The CreateRecurringPaymentsProfile API operation creates a recurring payments profile. You must invoke the CreateRecurringPaymentsProfile API operation for each profile you want to create. The API operation creates a profile and an associated billing agreement.
Note: There is a one-to-one correspondence between billing agreements and recurring payments profiles. To associate a recurring payments profile with its billing agreement, you must ensure that the description in the recurring payments profile matches the description of a billing agreement. For version 54.0 and later, use SetExpressCheckout to initiate creation of a billing agreement.
-
doDirectPayment
()¶ The DoDirectPayment API Operation enables you to process a credit card payment.
-
doExpressCheckoutPayment
()¶ The DoExpressCheckoutPayment API operation completes an Express Checkout transaction. If you set up a billing agreement in your SetExpressCheckout API call, the billing agreement is created when you call the DoExpressCheckoutPayment API operation.
The DoExpressCheckoutPayment API operation completes an Express Checkout transaction. If you set up a billing agreement in your SetExpressCheckout API call, the billing agreement is created when you call the DoExpressCheckoutPayment API operation.
-
doReferenceTransaction
()¶ The DoReferenceTransaction API operation processes a payment from a buyer’s account, which is identified by a previous transaction.
from paypal.pro.helpers import PayPalWPP def do_reference_transaction_view(request): wpp = PayPalWPP(request) reference_id = request.POST.get('reference_id') amount = request.POST.get('amount') wpp.doReferenceTransaction({'referenceid': reference_id, 'amt': amount})
-
getExpressCheckoutDetails
()¶ The GetExpressCheckoutDetails API operation obtains information about a specific Express Checkout transaction.
-
getTransactionDetails
()¶ The GetTransactionDetails API operation obtains information about a specific transaction.
-
manageRecurringPaymentsProfileStatus
()¶ The ManageRecurringPaymentsProfileStatus API operation cancels, suspends, or reactivates a recurring payments profile.
-
setExpressCheckout
()¶ The SetExpressCheckout API operation initiates an Express Checkout transaction. Returns an
PayPalNVP
object that has the token saved in the.token
attribute.This token can be converted into a URL to redirect to using the helper function
express_endpoint_for_token
in this module.See the SetExpressCheckout docs
-
updateRecurringPaymentsProfile
()¶ The UpdateRecurringPaymentsProfile API operation updates a recurring payments profile.
-
-
paypal.pro.helpers.
express_endpoint_for_token
(token, commit=False)¶ Returns the PayPal Express Checkout endpoint for a token. Pass
commit=True
if you will not prompt for confirmation when the user returns to your site.
-
class
paypal.pro.models.
PayPalNVP
¶ This stores the response returned by PayPal for any of the API calls above.
It has fields for all the common values. For other values, you can access
response_dict
which is a dictionary-like object containing everything PayPal returned.
Website Payments Pro is a version of PayPal that lets you accept payments on your site using server side calls. The branding of this is confusing. It was branded as “Paypal Payments Pro” at one point. Later “PayPal Payments Pro (Payflow Edition)” was introduced, and that was later renamed to “PayPal Payments Pro”, while the old “PayPal Payments Pro” was rebranded to “Website Payments Pro”. It is this older API (not Payflow) that is supported by django-paypal and documented here.
The PayPal Website Payments Pro solution reuses code from paypal.standard so
you’ll need to include both apps. django-paypal makes the whole process
incredibly easy to use through the provided PayPalPro
class.
Obtain PayPal Pro API credentials: login to PayPal, click My Account, Profile, Request API credentials, Set up PayPal API credentials and permissions, View API Signature.
Edit
settings.py
and addpaypal.standard
andpaypal.pro
to yourINSTALLED_APPS
and put in your PayPal Pro API credentials.INSTALLED_APPS = [ # .. 'paypal.standard', 'paypal.pro', ] PAYPAL_TEST = True PAYPAL_WPP_USER = "???" PAYPAL_WPP_PASSWORD = "???" PAYPAL_WPP_SIGNATURE = "???"
Write a wrapper view for
paypal.pro.views.PayPalPro
:In views.py:
from paypal.pro.views import PayPalPro def nvp_handler(nvp): # This is passed a PayPalNVP object when payment succeeds. # This should do something useful! pass def buy_my_item(request): item = {"paymentrequest_0_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 ppp = PayPalPro( 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 nvp_handler=nvp_handler) return ppp(request)
Create templates for payment and confirmation. By default both templates are populated with the context variable
form
which contains either aPaymentForm
or aConfirmation
form.payment.html:
<h1>Show me the money</h1> <form method="post" action=""> {{ form }} <input type="submit" value="Pay Up"> </form>
confirmation.html:
<!-- 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>
Add your view to
urls.py
, and add the IPN endpoint to receive callbacks from PayPal:from django.urls import path, include from myproject import views urlpatterns = [ ... path('payment-url/', views.buy_my_item), path('paypal/', include('paypal.standard.ipn.urls')), ]
Profit.
Alternatively, if you want to get down to the nitty gritty and perform some
more advanced operations with Payments Pro, use the paypal.pro.helpers.PayPalWPP
class directly.
If you are testing locally using the WPP sandbox and are having SSL problems, please see issue 145.
Tests¶
To run the django-paypal tests:
Download the source from GitHub or your fork.
Create a virtualenv for the django-paypal project.
Install tox:
pip install tox
Run tox:
tox
This will run all the tests on all supported combinations of Django/Python.
To run tests just in a single Python environment, do this in your venv:
pip install -e . pip install -r requirements-test.txt ./runtests.py
If you’re testing on Linux, due to m2crypto dependencies you’ll probably need various development header packages installed, plus
swig
tool.If you’re testing on a Mac, then, as m2crypto uses openssl, the command line should be:
env LDFLAGS=”-L”$(brew –prefix openssl)”/lib” CFLAGS=”-I”$(brew –prefix openssl)”/include” SWIG_FEATURES=”-cpperraswarn -includeall -I”$(brew –prefix openssl)”/include” tox
Release notes¶
Version 2.0 (2022-03-25)¶
- Better fix for Django 4.0 form rendering, enabling custom subclasses to work.
- Dropped support for old Python versions (< 3.6) and old Django versions (< 2.2)
Version 1.1.2 (2021-12-13)¶
- Fixed Django 4.0 support
Version 1.1.1 (2021-04-08)¶
- Corrected PayPal URL used in IPN/PDT forms. This is a correction of the fix in
1.1 for POSTBACK_ENDPOINT, which wrongly changed both the IPN postback URL and
the PayPal login URL. The fix introduces a pair of new settings (LOGIN_URL and
SANDBOX_LOGIN_URL). The fix also changes the (undocumented)
get_endpoint
method on the PayPalPaymentsForm toget_login_url()
, in case you are overriding that method.
Version 1.1 (2021-03-14)¶
- Fix PayPalSharedSecretEncryptedPaymentsForm in Python 3 - thanks Emilio Moretti
- Dropped Python 3.4 support
- Fixed some bugs with
CreditCard.get_type()
due to bad regexes - Fixed a bunch of warnings emitted under modern Django
- Changed default values of POSTBACK_ENDPOINT and SANDBOX_POSTBACK_ENDPOINT to ones now recommended by PayPal.
Version 1.0 (2019-03-22)¶
- Dropped support for versions of Django before 1.11
- Encrypted button corrections
- .encode() the encrypted result to avoid b’’ decoration under Python 3
- Fix the encrypted button examples in the documentation to use the encrypted form
- Fixed issue #206 - DB migration required by Django 2.1
- Support for almost all deprecated features removed, including:
- Signals deprecated in v0.2 (see notes below)
- Not passing
nvp_handler
toPayPalPro
(see notes under 0.2) - Using
"amt"
parameter withSetExpressCheckout
andDoExpressCheckoutPayment
(see notes under 0.1.4 below) - Settings deprecated in v0.4
setCustomerBillingAgreement
(pre 0.1.3 feature)PAYPAL_RECEIVER_EMAIL
(see notes under 0.3)pdt
view (see notes under 0.3)sandbox
method on forms (see notes under 0.2)
Version 0.5.0¶
Dropped official support for Python 3.3
Support for Django 2.0
Fixed bug with IPv6 addresses (thanks @alexcrawley)
Tidy up and update PayPalPaymentsForm. Specifically:
Where possible, remove explicit fields, leaving them to be handled by __init__(), which creates fields as required from the contents of
initial
.Deprecate field return_url - use field return instead. PayPal expects field
return
, but Python’s return keyword meant it wasn’t possible to set that field in the class’s definition. Later, code in __init__ was added to handle any value ininitial
, in particularinitial['return']
. As the work around which renamed ‘return’ to ‘return_url’ is not necessary, it is now being deprecated. To maintain backwards compatibility initial[‘return_url’] is remapped to initial[‘return’], with a deprecation warning.Thanks @JonathanRoach
Add cmd choices for _xclick-auto-billing and _xclick-payment-plan.
Version 0.4.1¶
- Added forgotten docs file
Version 0.4.0¶
- Cleaned up and documented all settings related to button images. Specifically:
- The default images have been updated to recent ones. This is backwards
incompatible if you were relying on the previous (very old) image and had
not set
PAYPAL_IMAGE
in your settings. - Removed separate settings for sandbox mode - these only meant more work when configuring, and production looked different from sandbox by default. This is backwards incompatible, but only affects development mode.
- Names of settings made clearer. The new names are:
PAYPAL_BUY_BUTTON_IMAGE
(was:PAYPAL_IMAGE
)PAYPAL_DONATION_BUTTON_IMAGE
(was:PAYPAL_DONATION_IMAGE
)PAYPAL_SUBSCRIPTION_BUTTON_IMAGE
(was:PAYPAL_SUBSCRIPTION_IMAGE
)
- The default images have been updated to recent ones. This is backwards
incompatible if you were relying on the previous (very old) image and had
not set
Version 0.3.6¶
- Version bump due to messed up version numbers in previous release.
Version 0.3.4¶
- Use multi certificates with PaypalEncryptedPaymentsForm
- Fixed issue #166 - regression from 0.2.7 when using
USE_TZ=False
- Django 1.11 compatibility.
- Added warnings for untested code.
Version 0.3.3¶
- Fixed issue #147 - compatibility with Django 1.10
Version 0.3.2¶
- Fixed
verify
method of IPN/PDT so that it can be re-run in the case of a PayPal server error. - Added ‘re-verify’ admin action for IPNs.
- Other IPN admin improvements.
- IMPORTANT: Removed the undocumented and untested
item_check_callable
parameter from several IPN and PDT processing functions. You should implement checks in signal handlers likevalid_ipn_received
or other calling code. - Fixed issue #119 - flagged IPNs not excluded from duplicate checking.
- Fixed issue #126 - documented need to check amount received.
Version 0.3.1¶
- Better handling of unknown datetime formats, thanks rebwok, PR #137
- Added pytz dependency
Version 0.3¶
- Dropped support for Django 1.4 and 1.5.
- Fixed crasher with AmbiguousTimeError.
- Better logging for paypal.pro.
- Fixed Django 1.7/1.8 compat for EmailField.
- Added missing migration for PDT model.
- Added missing South migrations
- Fixed max_length of IPN/PDT
custom
andtransaction_subject
fields - Fixed issue #105 - IPN failure when running under non-English locale
- Added missing fields
option_selection1
andoption_selection2
to IPN/PDT - IMPORTANT: Deprecated the
PAYPAL_RECEIVER_EMAIL
setting to allow multiple receiver emails in a single app. This has several consequences for your code, which must be fixed before upgrading to 0.4.x, when this setting will be dropped entirely:- When creating a
PayPalPaymentsForm
you must provide thebusiness
field in theinitial
parameter. - Validation of
receiver_email
must be done in yourvalid_ipn_received
signal handler and your PDT processing view. Take into account the fact that the user can tamper with the form fields before posting them to PayPal.
- When creating a
- The use of the
pdt
view for PDT payments is deprecated. Now you should provide your own view and use theprocess_pdt
helper function.
Version 0.2.7¶
- Small fix to logging, thanks frankier
Version 0.2.6¶
- Small fixes, including not depending on South.
Version 0.2.5¶
- Fixed some
PayPalIPN
DateTimeFields that were not being handled like the rest. Thanks thiagogds for the patch. - Fixed
PayPalNVP.timestamp
field so that it receives timezone-aware datetimes if you haveUSE_TZ = True
Version 0.2.4¶
Fixed timezone parsing of PalPal data so that
PayPalIPN.payment_date
and others are handled correctly (if you haveUSE_TZ = True
).This does not include a migration to fix old data - see the release notes if you need that.
Work-arounds for bugs in the IPN Simulator
Other small fixes
Regarding the handling of dates: If you want to fix historic data in your IPN tables, you need to apply a migration like the following:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import pytz
from datetime import datetime
from django.db import migrations
from django.utils import timezone
PAYPAL_DATE_FORMATS = [
"%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",
]
def parse_date(datestring):
for format in PAYPAL_DATE_FORMATS:
try:
return datetime.strptime(datestring, format)
except (ValueError, TypeError):
continue
def fix_ipn_dates(apps, schema_editor):
PayPalIPN = apps.get_model("ipn", "PayPalIPN")
for ipn in PayPalIPN.objects.all():
# Need to recreate PayPalIPN.posted_data_dict
posted_data_dict = None
if ipn.query:
from django.http import QueryDict
roughdecode = dict(item.split('=', 1) for item in ipn.query.split('&'))
encoding = roughdecode.get('charset', None)
if encoding is not None:
query = ipn.query.encode('ascii')
data = QueryDict(query, encoding=encoding)
posted_data_dict = data.dict()
if posted_data_dict is None:
continue
for field in ['time_created', 'payment_date', 'next_payment_date', 'subscr_date', 'subscr_effective',
'retry_at', 'case_creation_date', 'auction_closing_date']:
if field in posted_data_dict:
raw = posted_data_dict[field]
naive = parse_date(raw)
if naive is not None:
aware = timezone.make_aware(naive, pytz.timezone('US/Pacific'))
setattr(ipn, field, aware)
ipn.save()
class Migration(migrations.Migration):
dependencies = [
('ipn', '0003_auto_20141117_1647'),
]
operations = [
migrations.RunPython(fix_ipn_dates,
lambda apps, schema_editor: None) # allowing reverse migration is harmless)
]
Version 0.2.3¶
- Fixed various deprecation warnings when running under Django 1.8
Version 0.2.2¶
- Added ‘commit’ kwarg to
express_endpoint_for_token()
Version 0.2.1¶
- Added
PayPalNVP.response_dict
attribute. - Added
PayPalFailure.nvp
attribute to get full info - Switched to using
requests
library for HTTP calls.
Version 0.2¶
Introduced new, less confusing signals, and deprecated the old ones. This is a bit of an API overhaul, but the migration path is clear, don’t worry!
IPN:
Previously, there were IPN signals like
payment_was_successful
which fired even if thepayment_status
on the IPN was'Failed'
, and there were other signals likepayment_was_refunded
to cover other specific statuses, but not all of them. There were also bugs that meant that some signals would never fire.To sort out all these issues, and to future proof the design, the signals have been reduced to:
valid_ipn_received
invalid_ipn_received
The ‘invalid’ signals are sent when the transaction was flagged - because of a failed check with PayPal, for example, or a duplicate transaction ID. You should never act on these, but might want to be notified of a problem.
The ‘valid’ signals need to be handled. However, you will need to check the payment_status and other attributes to know what to do.
The old signals still exist and are used, but are deprecated. They will be removed in version 1.0.
Please see Using PayPal Standard IPN.
Pro:
This used signals even though they weren’t really appropriate.
Instead:
- If you are using
PayPalWPP
directly, the returnedPayPalNVP
objects from all method should just be used. Remember that you need to handlePayPalFailure
exceptions from all direct calls. - If you are using the
PayPalPro
wrapper, you should pass a callablenvp_handler
keyword argument.
Please see Using Website Payments Pro.
- If you are using
You must explicitly set
PAYPAL_TEST
toTrue
orFalse
in your settings, depending on whether you want production or sandbox PayPal. (The default isTrue
i.e. sandbox mode).The
sandbox()
method on any forms is deprecated. You should userender
and setPAYPAL_TEST
in your settings instead.
Version 0.1.5¶
Fixed support for custom User model in South migrations
If you:
- are using a custom AUTH_USER_MODEL
- are using the ‘pro’ app
- installed version 0.1.4 and ran the migrations,
you will need to reverse the migrations in the ‘pro’ app that were applied when you ran “./manage.py migrate”.
Version 0.1.4¶
- New docs!
- Python 3 support.
- Django 1.7 support.
- Support for custom User model via AUTH_USER_MODEL. If you change AUTH_USER_MODEL you will still need to write your own migrations.
- Support for all possible ‘initial’ options that could be wanted in PayPalStandardForm
- Support for PayPalPro CreateBillingAgreement method
- Support for PayPalPro DoReferenceTransaction method
- Upgraded to PayPal Pro API version 116.0
- This deprecates the “amt” parameter for SetExpressCheckout and DoExpressCheckoutPayment. paymentrequest_0_amt should be used instead. Use of amt will raise a DeprecationWarning for now.
- Various bug fixes, refactorings and small features.
- Removed PDT signals (which were never fired)
Version 0.1.3¶
- Missing payment types added
- Additional signals:
- payment_was_refunded
- payment_was_reversed
- Django 1.6 compatibility
- Various bug fixes, including:
- Fixes for non-ASCII characters
Update the database¶
django-paypal uses the built in Django migrations framework.
To update your database:
./manage.py migrate
If you using or upgrading from much older versions of Django (e.g. before 1.7 which didn’t have a built in migrations framework), please upgrade to django-paypal 0.5.x first and follow the docs found in that version.
Settings¶
Some settings are documented on other documentation pages. In addition, you can set the following values in your settings.py to customise the behaviour of django-paypal.
-
PAYPAL_BUY_BUTTON_IMAGE
¶ The URL of the image to be used for ‘buy’ buttons.
-
PAYPAL_DONATE_BUTTON_IMAGE
¶ The URL of the image to be used for ‘donate’ buttons.
-
PAYPAL_SUBSCRIPTION_BUTTON_IMAGE
¶ The URL of the image to be used for ‘subscription’ buttons.