Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions paypaladaptive/api/endpoints.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
"""Endpoints for (parts of) Paypal Adaptive API."""

from datetime import datetime, timedelta
import json
import logging

from django.utils import simplejson as json

from money.Money import Money
from moneyed import Money

from paypaladaptive import settings

Expand Down Expand Up @@ -99,12 +98,12 @@ def prepare_data(self, money, return_url, cancel_url, receivers,

if (not isinstance(receivers, ReceiverList) or len(receivers) < 1):
raise ValueError("receivers must be an instance of ReceiverList")

data = {'actionType': 'PAY',
'currencyCode': money.currency.code,
'returnUrl': return_url,
'cancelUrl': cancel_url}

receiverList = {'receiver': receivers.to_dict()}
data.update({'receiverList': receiverList})

Expand Down Expand Up @@ -160,7 +159,7 @@ def prepare_data(self, payKey=None, transactionId=None, trackingId=None):
class Refund(PaypalAdaptiveEndpoint):
"""
Models the Refund API operation

Currently only a full refund is supported.

"""
Expand All @@ -171,14 +170,14 @@ class Refund(PaypalAdaptiveEndpoint):
def prepare_data(self, pay_key):
if not pay_key:
raise ValueError("a payKey must be provided")

return {'payKey': pay_key}


class CancelPreapproval(PaypalAdaptiveEndpoint):
"""
Models the Cancel Preapproval API operation

Currently only a full refund is supported.

"""
Expand All @@ -189,14 +188,14 @@ class CancelPreapproval(PaypalAdaptiveEndpoint):
def prepare_data(self, preapproval_key):
if not preapproval_key:
raise ValueError("must provide a preapprovalKey")

return {'preapprovalKey': preapproval_key}


class Preapprove(PaypalAdaptiveEndpoint):
"""
Models the Preapproval API operation

Currently only a single payment with a simple date range is supported

"""
Expand Down Expand Up @@ -225,7 +224,7 @@ def prepare_data(self, money, return_url, cancel_url, ipn_url=None,

if ipn_url:
data['ipnNotificationUrl'] = ipn_url

return data

@property
Expand Down
38 changes: 18 additions & 20 deletions paypaladaptive/api/ipn/endpoints.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import logging
import urllib

from django.utils import simplejson
import urllib

from dateutil.parser import parse
from money.Money import Money, Currency
from moneyed import Money, Currency
from pytz import utc

from constants import *
Expand All @@ -19,7 +17,7 @@
class IPN(object):
"""
Models the IPN API response

Note that this model is specific to the Paypal Adaptive API; it cannot
handle IPNs from the standard Paypal checkout.

Expand All @@ -38,11 +36,11 @@ def __init__(self, **kwargs):
self.refund_account_charged = kwargs.get('refund_account_charged',
None)
self.receiver = kwargs.get('receiver', None)
self.invoiceId = kwargs.get('invoiceId', None)
self.invoiceId = kwargs.get('invoiceId', None)
self.amount = IPN.process_money(kwargs.get('amount', None))
self.is_primary_receiver = (kwargs.get('is_primary_receiver', '')
== 'true')

@classmethod
def slicedict(cls, d, s):
"""
Expand Down Expand Up @@ -82,7 +80,7 @@ def __init__(self, request):
self.type = raw_type
else:
raise IpnError('Unknown transaction_type received: %s' % raw_type)

self.process_transactions(request)

try:
Expand All @@ -97,13 +95,13 @@ def __init__(self, request):
self.ipn_notification_url = request.POST.get('ipn_notification_url', None)
self.pay_key = request.POST.get('pay_key', None)
self.memo = request.POST.get('memo', None)
self.fees_payer = request.POST.get('fees_payer', None)
self.fees_payer = request.POST.get('fees_payer', None)
self.trackingId = request.POST.get('trackingId', None)
self.preapproval_key = request.POST.get('preapproval_key', None)
self.reason_code = request.POST.get('reason_code', None)

# preapprovals define these
self.approved = request.POST.get('approved', 'false') == 'true'
self.approved = request.POST.get('approved', 'false') == 'true'
self.current_number_of_payments = IPN.process_int(request.POST.get('current_number_of_payments', None))
self.current_total_amount_of_all_payments = IPN.process_money(request.POST.get('current_total_amount_of_all_payments', None))
self.current_period_attempts = IPN.process_int(request.POST.get('current_period_attempts', None))
Expand All @@ -112,16 +110,16 @@ def __init__(self, request):
self.day_of_week = IPN.process_int(request.POST.get('day_of_week', None), None)
self.starting_date = IPN.process_date(request.POST.get('starting_date', None))
self.ending_date = IPN.process_date(request.POST.get('ending_date', None))
self.max_total_amount_of_all_payments = Money(request.POST.get('max_total_amount_of_all_payments', None),
request.POST.get('currency_code', None))
self.max_total_amount_of_all_payments = Money(request.POST.get('max_total_amount_of_all_payments', 0.0),
request.POST.get('currency_code', settings.DEFAULT_CURRENCY))
self.max_amount_per_payment = IPN.process_money(request.POST.get('max_amount_per_payment', None))
self.max_number_of_payments = IPN.process_int(request.POST.get('max_number_of_payments', None))
self.payment_period = request.POST.get('payment_period', None)
self.pin_type = request.POST.get('pin_type', None)
except Exception, e:
logger.error('Could not parse request')
raise e

# Verify enumerations
allowed_statuses = [IPN_STATUS_CREATED,
IPN_STATUS_COMPLETED,
Expand All @@ -135,7 +133,7 @@ def __init__(self, request):

if self.status and self.status not in allowed_statuses:
raise IpnError("unknown status: %s" % self.status)

if (self.action_type
and self.action_type not in [IPN_ACTION_TYPE_PAY,
IPN_ACTION_TYPE_CREATE]):
Expand All @@ -158,9 +156,9 @@ def process_int(cls, int_str, default='null'):
if default == 'null':
raise e


return val

@classmethod
def process_money(cls, money_str):
"""
Expand All @@ -174,7 +172,7 @@ def process_money(cls, money_str):
money_args.reverse()
if money_args and len(money_args) == 2:
return Money(*money_args)

return None

@classmethod
Expand All @@ -187,9 +185,9 @@ def process_date(cls, date_str):

if not date_str:
return None

return parse(date_str, tzinfos=IPN_TIMEZONES).astimezone(utc)

def process_transactions(self, request):
"""
Paypal sends transactions in the form transaction[n].[attribute], where
Expand All @@ -205,4 +203,4 @@ def process_transactions(self, request):
for transaction_num in transaction_nums:
transdict = IPN.Transaction.slicedict(request.POST, 'transaction[%s].' % transaction_num)
if len(transdict) > 0:
self.transactions.append(IPN.Transaction(**transdict))
self.transactions.append(IPN.Transaction(**transdict))
46 changes: 23 additions & 23 deletions paypaladaptive/models.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
"""Models to support Paypal Adaptive API"""
import json
import logging
from datetime import datetime, timedelta

from django.core.urlresolvers import reverse
from django.db import models, transaction
from django.utils.translation import ugettext_lazy as _
from django.contrib.sites.models import Site
from django.utils import simplejson as json

from money.contrib.django.models.fields import MoneyField
from djmoney.models.fields import MoneyField

import settings
import api
Expand All @@ -22,13 +22,13 @@
logger = logging.getLogger(__name__)


class UUIDField(models.CharField) :
class UUIDField(models.CharField):
"""Django db field using python's uuid4 library"""

def __init__(self, *args, **kwargs):
kwargs['max_length'] = kwargs.get('max_length', 32)
models.CharField.__init__(self, *args, **kwargs)

def pre_save(self, model_instance, add):
if add:
value = getattr(model_instance, self.attname)
Expand Down Expand Up @@ -86,7 +86,7 @@ def ipn_url(self):
'object_secret_uuid': self.secret_uuid}
ipn_url = reverse('paypal-adaptive-ipn', kwargs=kwargs)
return "http://%s%s" % (current_site, ipn_url)

class Meta:
abstract = True

Expand Down Expand Up @@ -241,9 +241,9 @@ def process(self, receivers, preapproval=None, **kwargs):
self.status = 'created'
else:
self.status = 'error'

self.save()

return self.status in ['created', 'completed']

@transaction.autocommit
Expand All @@ -253,15 +253,15 @@ def refund(self):
# TODO: flow should create a Refund object and call Refund.process()

self.save()

if self.status != 'completed':
raise ValueError('Cannot refund a Payment until it is completed.')

res, refund_call = self.call(api.Refund, self.pay_key)

self.status = 'refunded'
self.save()

refund = Refund(payment=self,
debug_request=json.dumps(refund_call.data),
debug_response=refund_call.raw_response)
Expand All @@ -287,7 +287,7 @@ def _parse_update_status(self, response):
def next_url(self):
return ('%s?cmd=_ap-payment&paykey=%s'
% (settings.PAYPAL_PAYMENT_HOST, self.pay_key))

def __unicode__(self):
return self.pay_key

Expand All @@ -296,10 +296,10 @@ class Refund(PaypalAdaptive):
"""Models a refund make using Paypal"""

STATUS_CHOICES = (
('new', _(u'New')),
('created', _(u'Created')),
('error', _(u'Error')),
('canceled', _(u'Canceled')),
('new', _(u'New')),
('created', _(u'Created')),
('error', _(u'Error')),
('canceled', _(u'Canceled')),
('returned', _(u'Returned')),
('completed', _(u'Completed')),
)
Expand All @@ -308,7 +308,7 @@ class Refund(PaypalAdaptive):
status = models.CharField(_(u'status'), max_length=10,
choices=STATUS_CHOICES, default='new')
status_detail = models.CharField(_(u'detailed status'), max_length=2048)

# TODO: finish model


Expand All @@ -329,7 +329,7 @@ class Preapproval(PaypalAdaptive):
('used', _(u'Used')),
('returned', _(u'Returned')),
)

valid_until_date = models.DateTimeField(_(u'valid until'),
default=default_valid_date)
preapproval_key = models.CharField(_(u'preapprovalkey'), max_length=255)
Expand Down Expand Up @@ -390,17 +390,17 @@ def process(self, **kwargs):
endpoint_kwargs.update(**kwargs)

res, preapprove = self.call(api.Preapprove, **endpoint_kwargs)

if preapprove.preapprovalkey:
self.preapproval_key = preapprove.preapprovalkey
self.status = 'created'
else:
self.status = 'error'

self.save()

return self.status == 'created'

@transaction.autocommit
def cancel_preapproval(self):
res, cancel = self.call(api.CancelPreapproval,
Expand All @@ -411,12 +411,12 @@ def cancel_preapproval(self):
self.status = 'canceled'
self.save()
return self.status == 'canceled'

@transaction.autocommit
def mark_as_used(self):
self.status = 'used'
self.save()

return self.status == 'used'

def get_update_kwargs(self):
Expand Down Expand Up @@ -446,7 +446,7 @@ def next_url(self):
"""Custom next URL"""
return ('%s?cmd=_ap-preapproval&preapprovalkey=%s'
% (settings.PAYPAL_PAYMENT_HOST, self.preapproval_key))

def __unicode__(self):
return self.preapproval_key

Expand Down
2 changes: 0 additions & 2 deletions paypaladaptive/settings.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from datetime import timedelta
from django.conf import settings
from money import set_default_currency


DEBUG = getattr(settings, "DEBUG", False)
Expand Down Expand Up @@ -34,7 +33,6 @@
SHIPPING = getattr(settings, 'PAYPAL_USE_SHIPPING', False)

DEFAULT_CURRENCY = getattr(settings, 'DEFAULT_CURRENCY', 'USD')
set_default_currency(code=DEFAULT_CURRENCY)

DECIMAL_PLACES = getattr(settings, 'PAYPAL_DECIMAL_PLACES', 2)
MAX_DIGITS = getattr(settings, 'PAYPAL_MAX_DIGITS', 10)
Expand Down
Loading