//django-paypal-adaptivebyaldarund

django-paypal-adaptive

Paypal Adaptive API support for Django

0
0
0
Python

Django Paypal Adaptive

Build Status
Downloads

The API and the modules in this repository might be subject to smaller
changes and not all Paypal Adaptive endpoints are covered. FundedByMe
will help make the covering of the Pay, Preapproval and IPN endpoints as good
as possible but we might not have the resources to perfect this project.

Making Preapprovals and using them to create Payments is fully supported
together with Paypal’s IPN push API. Please reach out to us if you’re
interested in helping maintaining this package.

Installation

Install package from PyPI:

$ pip install django-paypal-adaptive

Add to your project’s INSTALLED_APPS setting:

INSTALLED_APPS = (
    # …
    'paypaladaptive',
)

Add to your url config:

url(r'^paypaladaptive/', include('paypaladaptive.urls'))

Sync the database:

$ python manage.py syncdb

Or if you’re using South you might want to add an initial migration for future changes:

$ python manage.py schemamigration paypaladaptive --initial
$ python manage.py syncdb --migrate

Usage

Examples

Test the calls with sandbox account emails or you’ll probably get ‘Account is restricted’ errors.

Simple payment:
https://developer.paypal.com/webapps/developer/docs/classic/adaptive-payments/integration-guide/APIntro/#id0937N0Q0JY4

from paypaladaptive.models import Payment
from moneyed import Money, USD
from paypaladaptive.api import ReceiverList, Receiver

receiver = Receiver(amount=100, email="receiver1@example.com")
receivers = ReceiverList([receiver])

payment = Payment()
payment.money = Money(receivers.total_amount, USD)
payment.save()
payment.process(receivers)

# Redirect the user to the next_url() value
redirect_url = payment.next_url()

Implicit payment:
Implicit approval payments, in which your application is both the sender of a payment and the caller of the Adaptive Payments Pay API. In this case, PayPal makes the payment from your own account, which eliminates the need for approval.
Set the senderEmail to your account when you call the process method.

from paypaladaptive.models import Payment
from moneyed import Money, USD
from paypaladaptive.api import ReceiverList, Receiver
from paypaladaptive.settings import PAYPAL_EMAIL

receiver = Receiver(amount=10, email="receiver1@example.com")
receivers = ReceiverList([receiver])

payment = Payment()
payment.money = Money(receivers.total_amount, USD)
payment.save()
payment.process(
    receivers,
    senderEmail=PAYPAL_EMAIL,
    )

Specify allowed funding selections for a payment:
If the ‘fundingConstraing’ field is omitted, the payment can be funded by any funding type that is supported for Adaptive Payments. Allowable values are:
ECHECK – Electronic check
BALANCE – PayPal account balance
CREDITCARD – Credit card
Note: ECHECK and CREDITCARD include BALANCE implicitly.

from paypaladaptive.models import Payment
from moneyed import Money, USD
from paypaladaptive.api import ReceiverList, Receiver
from paypaladaptive.settings import PAYPAL_EMAIL

receiver = Receiver(amount=10, email="receiver1@example.com")
receivers = ReceiverList([receiver])

payment = Payment()
payment.money = Money(receivers.total_amount, USD)
payment.save()
# only PayPal account balance payment allowed
payment.process(
    receivers,
    fundingConstraint={'allowedFundingType': [{'fundingTypeInfo': {'fundingType': 'BALANCE'}}]},
    )

Parallel payment with 2 receivers:

from paypaladaptive.models import Payment
from moneyed import Money, USD
from paypaladaptive.api import ReceiverList, Receiver

receiver1 = Receiver(amount=100, email="receiver1@example.com")
receiver2 = Receiver(amount=1900, email="receiver2@example.com")
receivers = ReceiverList([receiver1, receiver2])

payment = Payment()
payment.money = Money(receivers.total_amount, USD)
payment.save()
payment.process(receivers)

# Redirect the user to the next_url() value
redirect_url = payment.next_url()

Parallel payment with 2 receivers where the sender pays the fees and we also set reverseAllParallelPaymentsOnError to True:
https://developer.paypal.com/webapps/developer/docs/classic/adaptive-payments/integration-guide/APIntro/#id091QF0
Learn more about PAY API operation fields: https://developer.paypal.com/webapps/developer/docs/classic/api/adaptive-payments/Pay_API_Operation/

from paypaladaptive.models import Payment
from moneyed import Money, USD
from paypaladaptive.api import ReceiverList, Receiver

receiver1 = Receiver(amount=100, email="receiver1@example.com")
receiver2 = Receiver(amount=1900, email="receiver2@example.com")
receivers = ReceiverList([receiver1, receiver2])

payment = Payment()
payment.money = Money(receivers.total_amount, USD)
payment.save()
payment.process(
    receivers,
    reverseAllParallelPaymentsOnError=True,
    feesPayer='SENDER',
    )

# Redirect the user to the next_url() value
redirect_url = payment.next_url()

response = payment.update(save=False)
payment.debug_response['feesPayer'] # SENDER
payment.debug_response['reverseAllParallelPaymentsOnError'] # true

Chained payment with 2 receivers:
https://developer.paypal.com/webapps/developer/docs/classic/adaptive-payments/integration-guide/APIntro/#id091QF0I30YK
Primary gets: $2
Secodaries get: $4

from paypaladaptive.models import Payment
from moneyed import Money, USD
from paypaladaptive.api import ReceiverList, Receiver

primary = Receiver(amount=10, email="primary@example.com", primary=True)
secondary1 = Receiver(amount=4, email="secondary1@example.com")
secondary2 = Receiver(amount=4, email="secondary2@example.com")
receivers = ReceiverList([primary, secondary1, secondary2])

payment = Payment()
payment.money = Money(primary.amount, USD)
payment.save()
payment.process(receivers)

# Redirect the user to the next_url() value
redirect_url = payment.next_url()

Create and process a preapproval for a payment.

from paypaladaptive.models import Preapproval
from moneyed import Money, USD

preapproval = Preapproval()
preapproval.money = Money(2000, USD)
preapproval.save()
preapproval.process(next='/home/', displayMaxTotalAmount=True)

# Redirect the user to the next_url() value
redirect_url = preapproval.next_url()

Create and process a payment to two receivers from a preapproval key.

from paypaladaptive.models import Payment
from paypaladaptive.api import ReceiverList, Receiver
from moneyed import Money, USD

key = 'PA-2MT146200X905683P'
platform = Receiver(amount=100, email="merchant@example.com", primary=False)
merchant = Receiver(amount=1900, email="mrbuyer@antonagestam.se", primary=True)
receivers = ReceiverList([platform, merchant])

p = Payment()
p.money = Money(2000, USD)
p.save()
p.process(receivers, preapproval_key=key)

Convert currencies:
In this case no models are involved, we just call the ConvertCurrency API endpoint.
https://developer.paypal.com/webapps/developer/docs/classic/api/adaptive-payments/ConvertCurrency_API_Operation/

from paypaladaptive.api import ConvertCurrency, MoneyList
from moneyed import Money, USD, GBP

convert_from = MoneyList([Money(10.55, USD), Money(20.64, GBP)])
convert_to = ['CAD', 'AUD', 'HUF']
response = ConvertCurrency(convert_from, convert_to).call()

specifiying conversionType and countryCode fields:
PayPal country codes: https://developer.paypal.com/webapps/developer/docs/classic/api/country_codes/

convert_from = MoneyList([Money(10.55, USD), Money(20.64, GBP)])
convert_to = ['CAD', 'AUD', 'HUF']
response = ConvertCurrency(
    convert_from, convert_to,
    conversionType='BALANCE_TRANSFER', countryCode='HUF'
    ).call()

IPN vs Delayed Updates

Paypal Adaptive uses IPN messages to ping your server about Payment and
Preapproval updates. Using IPN requires you to listen for incoming calls from
Paypal. Sometimes Paypal has issues with their IPN service and therefor you
might sometimes need to ping them for an update of the status instead. This
package comes with both and they can be used in parallel. That way you get both
the speed and asynchronous nature of IPN messages and the stability of delayed
lookups. Delayed lookups are disabled by default and requires Celery to be
installed. To install this requirement automatically, use:

$ pip install django-paypal-adaptive[delayed-updates]

And set PAYPAL_USE_DELAYED_UPDATES to True in your Django settings. Note
that this requires you to setup Celery on your own.

You can also implement your own background tasks and logic and call
Preapproval.update() and Payment.update() when you find it appropriate.

Models

The Payment and Preapproval inherit from an abstract model PaypalAdaptive and
therefor shares some data fields.

undefinedPaypalAdaptive.moneyundefined

money is a django-money MoneyField. MoneyField extends Django’s DecimalField
so has max_digits and decimal_places attributes that can be set with the
PAYPAL_MAX_DIGITS and PAYPAL_DECIMAL_PLACES settings.

undefinedPaypalAdaptive.created_dateundefined

DateTimeField with auto_now_add=True.

undefinedPaypalAdaptive.debug_requestundefined

Raw request body (JSON)

undefinedPaypalAdaptive.debug_responseundefined

Raw response body (JSON)

undefinedPaypalAdaptive.secret_uuidundefined

Secret identifier of each object.

Payment

undefinedPayment.statusundefined

Possible values are:

'new'  # Payment only exists locally
'created'  # Payment exists on Paypal
'error'  # Something along the process has gone wrong. Check status_detail
         # for more info.
'returned'  # User has returned via the Payment return_url
'completed'  # The Payment is complete
'refunded'  # The Payment is refunded
'canceled'  # The Payment has been canceled

undefinedPayment.status_detailundefined

Stores error messages from the latest transaction

undefinedPayment.pay_keyundefined

Corresponds to Paypal Adaptive’s payKey

Preapproval

undefinedPreapproval.statusundefined

Possible values are:

'new'  # Preapproval only exists locally — not known to Paypal
'created'  # Preapproval has been saved on Paypal
'error'  # Something has gone wrong, check status_detail for more info
'returned'  # User has returned via the Preapproval return_url
'approved'  # Preapproval is completed — ready to be used in payment
'canceled'  # Preapproval has been canceled
'used'  # Preapproval has been used in payment

undefinedPreapproval.status_detailundefined

Stores error messages from the latest transaction

undefinedPreapproval.valid_until_dateundefined

Preapproval expiry date

Settings

undefineddjango.conf.settings.PAYPAL_APPLICATION_IDundefined

Your Paypal application ID. Will default to APP-80W284485P519543T if
DEBUG is set to True.

undefineddjango.conf.settings.PAYPAL_USERIDundefined

Paypal User ID

undefineddjango.conf.settings.PAYPAL_PASSWORDundefined

Paypal password

undefineddjango.conf.settings.PAYPAL_SIGNATUREundefined

Paypal signature

undefineddjango.conf.settings.PAYPAL_EMAILundefined

Paypal Email

undefineddjango.conf.settings.PAYPAL_USE_IPNundefined

Whether or not to listen for incoming IPN messages. Defaults to True.

undefineddjango.conf.settings.PAYPAL_IPN_HTTP_PROTOCOLundefined

‘http’ or ‘https’. Defaults to settings.DEFAULT_HTTP_PROTOCOL. If settings.DEFAULT_HTTP_PROTOCOL is not set it defaults to ‘http’.

undefineddjango.conf.settings.PAYPAL_IPN_DOMAINundefined

Default is None in which case Site.objects.get_current().domain is used.
Useful if you want to test the IPN in localhost (e.g. https://ngrok.com/).

undefineddjango.conf.settings.PAYPAL_USE_DELAYED_UPDATESundefined

Whether or not to schedule update tasks for Preapprovals and Payments. Defaults
to False.

undefineddjango.conf.settings.DEFAULT_CURRENCYundefined

Used by python-money, will default to USD

undefineddjango.conf.settings.PAYPAL_DECIMAL_PLACESundefined

Number of decimal places assigned to the MoneyField (used by Payment and
Preapproval models).

undefineddjango.conf.settings.PAYPAL_MAX_DIGITSundefined

Max number of digits assigned to the Moneyfield (used by Payment and Preapproval
models).

undefineddjango.conf.settings.PAYPAL_TEST_WITH_MOCKundefined

Set whether tests should be run with built-in mocking responses and requests
or if the testing should spawn requests that hits Paypal’s APIs directly.
Defaults to True.

Run tests

To run the tests, first install the test requirements:

$ [sudo] pip install -r requirements_test.txt --use-mirrors

The script that runs the tests simulates an installed Django app and is located
in runtests.py. Execute it like this:

$ python runtests.py

Contributing

Do you want to contribute? We’ll gladly accept pull requests as long as your code
is well tested and contributes to the goal of this library.

License

Creative Commons-licens
django-paypal-adaptive av FundedByMe är licensierad under en Creative Commons Erkännande 3.0 Unported licens.
Based on a work at https://github.com/gmcguire/django-paypal-adaptive.

[beta]v0.14.0