Merge branch 'master' into goodreads

pull/1/head
Raymond Yee 2011-10-28 12:48:31 -07:00
commit db957c7ff6
7 changed files with 168 additions and 39 deletions

View File

@ -18,28 +18,28 @@
}
);
$j('.tabs1').click(function(){
$j('#tabs').find('.active').removeClass('active');
$j('.tabs').find('.active').removeClass('active');
$j(this).addClass('active');
$j('.content-block-content').find('.active').removeClass('active');
$j('#tabs-1').addClass('active').show(300);
$j('#tabs-2').hide(200);
$j('#tabs-3').hide(200);
$j('.tabs-1').addClass('active').show(300);
$j('.tabs-2').hide(200);
$j('.tabs-3').hide(200);
});
$j('.tabs2').click(function(){
$j('#tabs').find('.active').removeClass('active');
$j('.tabs').find('.active').removeClass('active');
$j(this).addClass('active');
$j('.content-block-content').find('.active').removeClass('active');
$j('#tabs-2').addClass('active').show(300);
$j('#tabs-1').hide(200);
$j('#tabs-3').hide(200);
$j('.tabs-2').addClass('active').show(300);
$j('.tabs-1').hide(200);
$j('.tabs-3').hide(200);
});
$j('.tabs3').click(function(){
$j('#tabs').find('.active').removeClass('active');
$j('.tabs').find('.active').removeClass('active');
$j(this).addClass('active');
$j('.content-block-content').find('.active').removeClass('active');
$j('#tabs-3').addClass('active').show(300);
$j('#tabs-2').hide(200);
$j('#tabs-1').hide(200);
$j('.tabs-3').addClass('active').show(300);
$j('.tabs-2').hide(200);
$j('.tabs-1').hide(200);
});
$j('.empty-wishlist span.bounce-search').click(function(){
$j('div.js-search-inner').effect("bounce", 500, function() {
@ -193,7 +193,7 @@ how do I integrate the your wishlist thing with the tabs thing?
<ul class="tabs">
<li class="tabs1"><a href="#">Unglued</a></li>
<li class="tabs2"><a href="#">Being Unglued</a></li>
<li class="tabs3"><a href="#">Want to Unglue</a></li>
<li class="tabs3 active"><a href="#">Want to Unglue</a></li>
</ul>
<ul class="book-list-view">
@ -232,7 +232,14 @@ how do I integrate the your wishlist thing with the tabs thing?
{% endifequal %}
{% else %}
{% for work in wishlist.works.all %}
<div id="tabs-1" class="tabs">
<!-- classify which tab depending on work.last_campaign_status -->
{% if work.last_campaign_status == 'SUCCESSFUL' %}
<div class="tabs tabs-1">
{% else %}{% if work.last_campaign_status == 'ACTIVE' %}
<div class="tabs tabs-2">
{% else %}
<div class="tabs tabs-3">
{% endif %}{% endif %}
<div class="book-list {% cycle 'row1' 'row2' %}">
<div class="book-thumb">
<a href="#"><img src="{{ work.cover_image_small }}" alt="Book name" title="book name" /></a>

View File

@ -2,17 +2,85 @@ from regluit.core.models import Campaign, Wishlist
from regluit.payment.models import Transaction, Receiver
from django.contrib.auth.models import User
from regluit.payment.parameters import *
from regluit.payment.paypal import Pay, IPN, IPN_TYPE_PAYMENT, IPN_TYPE_PREAPPROVAL, IPN_TYPE_ADJUSTMENT, Preapproval, IPN_PAY_STATUS_COMPLETED, CancelPreapproval, IPN_SENDER_STATUS_COMPLETED, IPN_TXN_STATUS_COMPLETED
from regluit.payment.paypal import Pay, IPN, IPN_TYPE_PAYMENT, IPN_TYPE_PREAPPROVAL, IPN_TYPE_ADJUSTMENT, Preapproval, IPN_PAY_STATUS_COMPLETED, CancelPreapproval, PaymentDetails, IPN_SENDER_STATUS_COMPLETED, IPN_TXN_STATUS_COMPLETED
import uuid
import traceback
from datetime import datetime
from dateutil.relativedelta import relativedelta
import logging
from decimal import Decimal as D
from xml.dom import minidom
logger = logging.getLogger(__name__)
def append_element(doc, parent, name, text):
element = doc.createElement(name)
parent.appendChild(element)
text_node = doc.createTextNode(text)
element.appendChild(text_node)
return element
# at this point, there is no internal context and therefore, the methods of PaymentManager can be recast into static methods
class PaymentManager( object ):
def checkStatus(self):
'''
Run through all pay transactions and verify that their current status is as we think.
For now this only checks things submitted to the PAY api using the PaymentDetails. We
Should also implement PreapprovalDetails for more info
'''
doc = minidom.Document()
head = doc.createElement('transactions')
doc.appendChild(head)
# look at all transacitons in the last 3 days
ref_date = datetime.now() - relativedelta(days=3)
transactions = Transaction.objects.filter(date_payment__gte=ref_date)
for t in transactions:
p = PaymentDetails(t)
if p.error():
logger.info("Error retrieving payment details for transaction %d" % t.id)
else:
tran = doc.createElement('transaction')
tran.setAttribute("id", str(t.id))
head.appendChild(tran)
# Check the transaction satus
if t.status != p.status:
append_element(doc, tran, "status_ours", t.status)
append_element(doc, tran, "status_theirs", p.status)
t.status = p.status
t.save()
for r in p.transactions:
try:
receiver = Receiver.objects.get(transaction=t, email=r['email'])
# Check for updates on each receiver's status
if receiver.status != r['status']:
append_element(doc, tran, "receiver_status_ours", receiver.status)
append_element(doc, tran, "receiver_status_theirs", r['status'])
receiver.status = r['status']
receiver.txn_id = r['txn_id']
receiver.save()
except:
traceback.print_exc()
return doc.toxml()
def processIPN(self, request):
'''
processIPN
@ -28,13 +96,14 @@ class PaymentManager( object ):
if ipn.success():
logger.info("Valid IPN")
logger.info("IPN Transaction Type: %s" % ipn.transaction_type)
if ipn.transaction_type == IPN_TYPE_PAYMENT:
# payment IPN
# payment IPN. we use our unique reference for the transaction as the key
# is only valid for 3 hours
key = ipn.key()
t = Transaction.objects.get(reference=key)
uniqueID = ipn.uniqueID()
t = Transaction.objects.get(secret=uniqueID)
# The status is always one of the IPN_PAY_STATUS codes defined in paypal.py
t.status = ipn.status
@ -52,14 +121,20 @@ class PaymentManager( object ):
except:
# Log an excecption if we have a receiver that is not found
traceback.print_exc()
t.save()
logger.info("Final transaction status: %s" % t.status)
elif ipn.transaction_type == IPN_TYPE_ADJUSTMENT:
# a chargeback, reversal or refund for an existng payment
key = ipn.key()
t = Transaction.objects.get(reference=key)
uniqueID = ipn.uniqueID()
if uniqueID:
t = Transaction.objects.get(secret=uniqueID)
else:
key = ipn.key()
t = Transaction.objects.get(reference=key)
# The status is always one of the IPN_PAY_STATUS codes defined in paypal.py
t.status = ipn.status
@ -70,7 +145,7 @@ class PaymentManager( object ):
elif ipn.transaction_type == IPN_TYPE_PREAPPROVAL:
# IPN for preapproval always uses the key to ref the transaction as this is always valid
key = ipn.key()
t = Transaction.objects.get(reference=key)
@ -229,7 +304,11 @@ class PaymentManager( object ):
transaction.receiver_set.all().delete()
transaction.create_receivers(receiver_list)
# Mark as payment attempted so we will poll this periodically for status changes
transaction.date_payment = datetime.now()
transaction.save()
p = Pay(transaction)
# We will update our transaction status when we receive the IPN
@ -285,7 +364,6 @@ class PaymentManager( object ):
type=PAYMENT_TYPE_AUTHORIZATION,
target=target,
currency=currency,
secret = str(uuid.uuid1()),
status='NONE',
campaign=campaign,
list=list,
@ -339,11 +417,11 @@ class PaymentManager( object ):
type=PAYMENT_TYPE_INSTANT,
target=target,
currency=currency,
secret = str(uuid.uuid1()),
status='NONE',
campaign=campaign,
list=list,
user=user
user=user,
date_payment=datetime.now()
)
t.create_receivers(receiver_list)

View File

@ -3,6 +3,7 @@ from django.contrib.auth.models import User
from regluit.core.models import Campaign, Wishlist
from regluit.payment.parameters import *
from decimal import Decimal
import uuid
class Transaction(models.Model):
@ -11,7 +12,7 @@ class Transaction(models.Model):
status = models.CharField(max_length=32, default='NONE', null=False)
amount = models.DecimalField(default=Decimal('0.00'), max_digits=14, decimal_places=2) # max 999,999,999,999.99
currency = models.CharField(max_length=10, default='USD', null=True)
secret = models.CharField(max_length=64, null=True)
secret = models.CharField(max_length=64)
reference = models.CharField(max_length=128, null=True)
receipt = models.CharField(max_length=256, null=True)
error = models.CharField(max_length=256, null=True)
@ -25,6 +26,11 @@ class Transaction(models.Model):
campaign = models.ForeignKey(Campaign, null=True)
list = models.ForeignKey(Wishlist, null=True)
def save(self, *args, **kwargs):
if not self.secret:
self.secret = str(uuid.uuid1())
super(Transaction, self).save(*args, **kwargs) # Call the "real" save() method.
def __unicode__(self):
return u"-- Transaction:\n \tstatus: %s\n \t amount: %s\n \treference: %s\n \terror: %s\n" % (self.status, str(self.amount), self.reference, self.error)

View File

@ -155,14 +155,17 @@ class Pay( object ):
'cancelUrl': cancel_url,
'requestEnvelope': { 'errorLanguage': 'en_US' },
'ipnNotificationUrl': settings.BASE_URL + reverse('PayPalIPN'),
'feesPayer': feesPayer
'feesPayer': feesPayer,
'trackingId': transaction.secret
}
logging.info("paypal PAY data: %s" % data)
# a Pay operation can be for a payment that goes through immediately or for setting up a preapproval.
# transaction.reference is not null if it represents a preapproved payment, which has a preapprovalKey.
if transaction.reference:
data['preapprovalKey'] = transaction.reference
self.raw_request = json.dumps(data)
self.raw_response = url_request(settings.PAYPAL_ENDPOINT, "/AdaptivePayments/Pay", data=self.raw_request, headers=headers ).content()
@ -211,7 +214,7 @@ class PaymentDetails(object):
# I think we've been tracking payKey. We might want to use our own trackingId (what's Transaction.secret for?)
data = {
'requestEnvelope': { 'errorLanguage': 'en_US' },
'payKey':transaction.reference
'trackingId':transaction.secret
}
self.raw_request = json.dumps(data)
@ -221,6 +224,25 @@ class PaymentDetails(object):
self.response = json.loads( self.raw_response )
logger.info(self.response)
self.status = self.response.get("status", None)
self.trackingId = self.response.get("trackingId", None)
self.feesPayer = self.response.get("feesPayer", None)
payment_info_list = self.response.get("paymentInfoList", None)
payment_info = payment_info_list.get("paymentInfo", None)
self.transactions = []
for payment in payment_info:
receiver = {}
receiver['status'] = payment.get("transactionStatus", None)
receiver['txn_id'] = payment.get("transactionId")
r = payment.get("receiver", None)
if r:
receiver['email'] = r.get('email')
self.transactions.append(receiver)
def error(self):
if self.response.has_key('error'):
error = self.response['error']
@ -228,10 +250,7 @@ class PaymentDetails(object):
return error[0]['message']
else:
return None
def status(self):
return self.response.get("status")
def compare(self):
"""compare current status information from what's in the current transaction object"""
# I don't think we do anything with fundingtypeList, memo
@ -411,6 +430,7 @@ class IPN( object ):
self.preapproval_key = request.POST.get('preapproval_key', None)
self.transaction_type = request.POST.get('transaction_type', None)
self.reason_code = request.POST.get('reason_code', None)
self.trackingId = request.POST.get('tracking_id', None)
self.process_transactions(request)
@ -418,6 +438,13 @@ class IPN( object ):
self.error = "Error: ServerError"
traceback.print_exc()
def uniqueID(self):
if self.trackingId:
return self.trackingId
else:
return None
def key(self):
# We only keep one reference, either a prapproval key, or a pay key, for the transaction. This avoids the
# race condition that may result if the IPN for an executed pre-approval(with both a pay key and preapproval key) is received

View File

@ -9,5 +9,6 @@ urlpatterns = patterns(
url(r"^querycampaign", "queryCampaign"),
url(r"^paypalipn", "paypalIPN", name="PayPalIPN"),
url(r"^runtests", "runTests"),
url(r"^paymentcomplete","paymentcomplete")
url(r"^paymentcomplete","paymentcomplete"),
url(r"^checkstatus", "checkStatus")
)

View File

@ -68,8 +68,8 @@ def testExecute(request):
for t in transactions:
# Note, set this to 1-5 different receivers with absolute amounts for each
receiver_list = [{'email':TEST_RECEIVERS[0], 'amount':t.amount * 0.80},
{'email':TEST_RECEIVERS[1], 'amount':t.amount * 0.20}]
receiver_list = [{'email':TEST_RECEIVERS[0], 'amount':float(t.amount) * 0.80},
{'email':TEST_RECEIVERS[1], 'amount':float(t.amount) * 0.20}]
p.execute_transaction(t, receiver_list)
output += str(t)
@ -225,5 +225,15 @@ def paymentcomplete(request):
output = "payment complete"
output += request.method + "\n" + str(request.REQUEST.items())
return HttpResponse(output)
def checkStatus(request):
# Check the status of all PAY transactions and flag any errors
p = PaymentManager()
error_data = p.checkStatus()
return HttpResponse(error_data, mimetype="text/xml")

View File

@ -78,7 +78,7 @@ PAYPAL_GLUEJAR_EMAIL = "glueja_1317336101_biz@gluejar.com"
PAYPAL_TEST_RH_EMAIL = "rh1_1317336251_biz@gluejar.com"
PAYPAL_TEST_NONPROFIT_PARTNER_EMAIL = "nppart_1318957063_per@gluejar.com"
BASE_URL = 'http://0.0.0.0/'
BASE_URL = 'http://0.0.0.0'
# use database as queuing service in development
BROKER_TRANSPORT = "djkombu.transport.DatabaseTransport"