2011-09-27 12:48:11 +00:00
from regluit . payment . parameters import *
2011-10-14 17:52:17 +00:00
from django . core . urlresolvers import reverse
2011-10-06 23:10:20 +00:00
from django . conf import settings
2011-09-27 12:48:11 +00:00
from regluit . payment . models import Transaction
from django . contrib . auth . models import User
from django . utils import simplejson as json
from django . utils . xmlutils import SimplerXMLGenerator
from django . db import IntegrityError
from django . db . models . query_utils import Q
from django . shortcuts import render_to_response
from django . template import RequestContext
import datetime
import dateutil . parser
import hashlib
import httplib
import traceback
import uuid
import os
import urllib
import urllib2
import logging
import random
import commands
import smtplib
import urlparse
import decimal
2011-11-23 17:12:37 +00:00
import sys
2011-09-27 12:48:11 +00:00
2011-10-06 00:56:20 +00:00
logger = logging . getLogger ( __name__ )
2011-09-27 12:48:11 +00:00
# transaction_type constants
IPN_TYPE_PAYMENT = ' Adaptive Payment PAY '
IPN_TYPE_ADJUSTMENT = ' Adjustment '
2011-09-29 07:50:07 +00:00
IPN_TYPE_PREAPPROVAL = ' Adaptive Payment PREAPPROVAL '
2011-09-27 12:48:11 +00:00
2011-10-01 12:01:40 +00:00
#pay API status constants
2011-10-18 14:42:56 +00:00
# I think 'NONE' is not something the API produces but is particular to our implementation
2011-10-18 23:27:20 +00:00
# couldn't we use the Python None?
2011-10-01 12:01:40 +00:00
IPN_PAY_STATUS_NONE = ' NONE '
2011-10-18 14:42:56 +00:00
2011-11-29 01:33:49 +00:00
# The following apply to INSTANT PAYMENTS
2011-10-01 12:01:40 +00:00
IPN_PAY_STATUS_CREATED = ' CREATED '
IPN_PAY_STATUS_COMPLETED = ' COMPLETED '
IPN_PAY_STATUS_INCOMPLETE = ' INCOMPLETE '
IPN_PAY_STATUS_ERROR = ' ERROR '
IPN_PAY_STATUS_REVERSALERROR = ' REVERSALERROR '
IPN_PAY_STATUS_PROCESSING = ' PROCESSING '
IPN_PAY_STATUS_PENDING = ' PENDING '
2011-10-18 14:42:56 +00:00
2011-10-18 23:27:20 +00:00
# particular to preapprovals -- may want to rename these constants to IPN_PREAPPROVAL_STATUS_*
2011-10-01 12:01:40 +00:00
IPN_PAY_STATUS_ACTIVE = " ACTIVE "
IPN_PAY_STATUS_CANCELED = " CANCELED "
2011-10-18 23:27:20 +00:00
IPN_PAY_STATUS_DEACTIVED = " DEACTIVED "
2011-10-18 14:42:56 +00:00
2011-10-01 12:01:40 +00:00
IPN_SENDER_STATUS_COMPLETED = ' COMPLETED '
IPN_SENDER_STATUS_PENDING = ' PENDING '
IPN_SENDER_STATUS_CREATED = ' CREATED '
IPN_SENDER_STATUS_PARTIALLY_REFUNDED = ' PARTIALLY_REFUNDED '
IPN_SENDER_STATUS_DENIED = ' DENIED '
IPN_SENDER_STATUS_PROCESSING = ' PROCESSING '
IPN_SENDER_STATUS_REVERSED = ' REVERSED '
IPN_SENDER_STATUS_REFUNDED = ' REFUNDED '
IPN_SENDER_STATUS_FAILED = ' FAILED '
2011-09-27 12:48:11 +00:00
# action_type constants
IPN_ACTION_TYPE_PAY = ' PAY '
IPN_ACTION_TYPE_CREATE = ' CREATE '
# individual sender transaction constants
IPN_TXN_STATUS_COMPLETED = ' Completed '
IPN_TXN_STATUS_PENDING = ' Pending '
IPN_TXN_STATUS_REFUNDED = ' Refunded '
2011-11-28 23:39:06 +00:00
# adaptive payment adjusted IPN reason codes
2011-10-01 12:01:40 +00:00
IPN_REASON_CODE_CHARGEBACK_SETTLEMENT = ' Chargeback Settlement '
2011-09-27 12:48:11 +00:00
IPN_REASON_CODE_ADMIN_REVERSAL = ' Admin reversal '
IPN_REASON_CODE_REFUND = ' Refund '
2011-10-01 12:01:40 +00:00
class PaypalError ( RuntimeError ) :
pass
2011-09-27 12:48:11 +00:00
class url_request ( object ) :
def __init__ ( self , base , url , data = None , headers = { } ) :
conn = httplib . HTTPSConnection ( base )
conn . request ( " POST " , url , data , headers )
#Check the response - should be 200 OK.
self . response = conn . getresponse ( )
def content ( self ) :
return self . response . read ( )
def code ( self ) :
return self . response . status
class Pay ( object ) :
2011-10-13 21:13:37 +00:00
def __init__ ( self , transaction , return_url = None , cancel_url = None ) :
2011-09-27 12:48:11 +00:00
headers = {
2011-10-06 23:10:20 +00:00
' X-PAYPAL-SECURITY-USERID ' : settings . PAYPAL_USERNAME ,
' X-PAYPAL-SECURITY-PASSWORD ' : settings . PAYPAL_PASSWORD ,
' X-PAYPAL-SECURITY-SIGNATURE ' : settings . PAYPAL_SIGNATURE ,
' X-PAYPAL-APPLICATION-ID ' : settings . PAYPAL_APPID ,
2011-09-27 12:48:11 +00:00
' X-PAYPAL-REQUEST-DATA-FORMAT ' : ' JSON ' ,
' X-PAYPAL-RESPONSE-DATA-FORMAT ' : ' JSON '
}
2011-10-13 21:13:37 +00:00
if return_url is None :
return_url = settings . BASE_URL + COMPLETE_URL
if cancel_url is None :
cancel_url = settings . BASE_URL + CANCEL_URL
2011-10-06 00:56:20 +00:00
logger . info ( " Return URL: " + return_url )
logger . info ( " Cancel URL: " + cancel_url )
2011-09-27 12:48:11 +00:00
2011-10-01 12:01:40 +00:00
receiver_list = [ ]
receivers = transaction . receiver_set . all ( )
if len ( receivers ) == 0 :
raise Exception
2011-10-14 17:52:17 +00:00
# by setting primary_string of the first receiver to 'true', we are doing a Chained payment
2011-10-01 12:01:40 +00:00
for r in receivers :
if len ( receivers ) > 1 :
if r . primary :
primary_string = ' true '
else :
primary_string = ' false '
receiver_list . append ( { ' email ' : r . email , ' amount ' : str ( r . amount ) , ' primary ' : primary_string } )
else :
receiver_list . append ( { ' email ' : r . email , ' amount ' : str ( r . amount ) } )
2011-10-06 00:56:20 +00:00
logger . info ( receiver_list )
2011-10-01 12:01:40 +00:00
2011-10-18 14:42:56 +00:00
# actionType can be 'PAY', 'CREATE', or 'PAY_PRIMARY'
# PAY_PRIMARY': "For chained payments only, specify this value to delay payments to the secondary receivers; only the payment to the primary receiver is processed"
# feesPayer: SENDER, PRIMARYRECEIVER, EACHRECEIVER, SECONDARYONLY
# if only one receiver, set to EACHRECEIVER, otherwise set to SECONDARYONLY
if len ( receivers ) == 1 :
feesPayer = ' EACHRECEIVER '
else :
feesPayer = ' SECONDARYONLY '
2011-09-27 12:48:11 +00:00
data = {
2011-09-29 07:50:07 +00:00
' actionType ' : ' PAY ' ,
' receiverList ' : { ' receiver ' : receiver_list } ,
2011-09-27 12:48:11 +00:00
' currencyCode ' : transaction . currency ,
' returnUrl ' : return_url ,
' cancelUrl ' : cancel_url ,
' requestEnvelope ' : { ' errorLanguage ' : ' en_US ' } ,
2011-10-18 14:42:56 +00:00
' ipnNotificationUrl ' : settings . BASE_URL + reverse ( ' PayPalIPN ' ) ,
2011-10-27 06:35:46 +00:00
' feesPayer ' : feesPayer ,
' trackingId ' : transaction . secret
2011-09-27 12:48:11 +00:00
}
2011-09-29 07:50:07 +00:00
2011-10-27 06:35:46 +00:00
logging . info ( " paypal PAY data: %s " % data )
2011-11-23 19:39:51 +00:00
print >> sys . stderr , " paypal PAY data: " , data
2011-11-23 17:07:07 +00:00
# Is ipnNotificationUrl being computed properly
print >> sys . stderr , ' ipnNotificationUrl ' , settings . BASE_URL + reverse ( ' PayPalIPN ' )
2011-10-27 06:35:46 +00:00
2011-10-14 17:52:17 +00:00
# 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.
2011-09-29 07:50:07 +00:00
if transaction . reference :
data [ ' preapprovalKey ' ] = transaction . reference
2011-10-27 08:05:21 +00:00
2011-09-27 12:48:11 +00:00
self . raw_request = json . dumps ( data )
2011-10-06 23:10:20 +00:00
self . raw_response = url_request ( settings . PAYPAL_ENDPOINT , " /AdaptivePayments/Pay " , data = self . raw_request , headers = headers ) . content ( )
2011-11-23 19:39:51 +00:00
print >> sys . stderr , " PAY request " , settings . PAYPAL_ENDPOINT , " /AdaptivePayments/Pay " , self . raw_request , headers
2011-10-06 00:56:20 +00:00
logger . info ( " paypal PAY response was: %s " % self . raw_response )
2011-11-23 17:41:11 +00:00
print >> sys . stderr , " paypal PAY response was: " , self . raw_response
2011-09-27 12:48:11 +00:00
self . response = json . loads ( self . raw_response )
2011-10-06 00:56:20 +00:00
logger . info ( self . response )
2011-09-27 12:48:11 +00:00
def status ( self ) :
if self . response . has_key ( ' paymentExecStatus ' ) :
return self . response [ ' paymentExecStatus ' ]
else :
return None
def error ( self ) :
if self . response . has_key ( ' error ' ) :
error = self . response [ ' error ' ]
2011-10-06 00:56:20 +00:00
logger . info ( error )
2011-09-27 12:48:11 +00:00
return error [ 0 ] [ ' message ' ]
else :
2011-10-01 12:01:40 +00:00
return ' Paypal PAY: Unknown Error '
2011-09-27 12:48:11 +00:00
def amount ( self ) :
return decimal . Decimal ( self . results [ ' payment_gross ' ] )
def paykey ( self ) :
return self . response [ ' payKey ' ]
def next_url ( self ) :
2011-10-06 23:10:20 +00:00
return ' %s ?cmd=_ap-payment&paykey= %s ' % ( settings . PAYPAL_PAYMENT_HOST , self . response [ ' payKey ' ] )
2011-09-27 12:48:11 +00:00
2011-11-22 11:14:58 +00:00
def embedded_url ( self ) :
return ' %s /webapps/adaptivepayment/flow/pay?paykey= %s &expType=light ' % ( settings . PAYPAL_PAYMENT_HOST , self . response [ ' payKey ' ] )
2011-10-14 17:52:17 +00:00
class PaymentDetails ( object ) :
def __init__ ( self , transaction = None ) :
2011-10-18 14:42:56 +00:00
2011-12-06 14:36:18 +00:00
try :
self . transaction = transaction
self . errorMessage = None
self . response = None
2011-10-27 08:05:21 +00:00
2011-12-06 14:36:18 +00:00
headers = {
' X-PAYPAL-SECURITY-USERID ' : settings . PAYPAL_USERNAME ,
' X-PAYPAL-SECURITY-PASSWORD ' : settings . PAYPAL_PASSWORD ,
' X-PAYPAL-SECURITY-SIGNATURE ' : settings . PAYPAL_SIGNATURE ,
' X-PAYPAL-APPLICATION-ID ' : settings . PAYPAL_APPID ,
' X-PAYPAL-REQUEST-DATA-FORMAT ' : ' JSON ' ,
' X-PAYPAL-RESPONSE-DATA-FORMAT ' : ' JSON '
}
# we can feed any of payKey, transactionId, and trackingId to identify transaction in question
# 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 ' } ,
' trackingId ' : transaction . secret
}
self . raw_request = json . dumps ( data )
self . connection = url_request ( settings . PAYPAL_ENDPOINT , " /AdaptivePayments/PaymentDetails " , data = self . raw_request , headers = headers )
self . raw_response = self . connection . content ( )
self . code = self . connection . code ( )
if self . code != 200 :
self . errorMessage = ' PayPal response code was %i ' % self . code
return
logger . info ( " paypal PaymentDetails response was: %s " % self . raw_response )
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 )
2011-10-27 08:05:21 +00:00
2011-12-06 14:36:18 +00:00
except :
self . errorMessage = " Error: ServerError "
traceback . print_exc ( )
2011-10-27 08:05:21 +00:00
2011-10-14 17:52:17 +00:00
def error ( self ) :
2011-12-06 14:36:18 +00:00
if self . response and self . response . has_key ( ' error ' ) :
2011-10-14 17:52:17 +00:00
error = self . response [ ' error ' ]
logger . info ( error )
return error [ 0 ] [ ' message ' ]
2011-12-06 14:36:18 +00:00
elif self . errorMessage :
return self . errorMessage
2011-10-14 17:52:17 +00:00
else :
2011-10-18 14:42:56 +00:00
return None
2011-10-27 08:05:21 +00:00
2011-10-18 14:42:56 +00:00
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
# status can be:
# transaction.type should be PAYMENT_TYPE_INSTANT
# actionType can be: 'PAY', 'CREATE', 'PAY_PRIMARY' -- I think we are doing only 'PAY' right now
comp = [ ( self . transaction . status , self . response . get ( ' status ' ) ) ,
( self . transaction . type , self . response . get ( ' actionType ' ) ) ,
( self . transaction . currency , self . response . get ( ' currencyCode ' ) ) ,
( ' EACHRECEIVER ' if len ( self . transaction . receiver_set . all ( ) ) == 1 else ' SECONDARYONLY ' , self . response . get ( ' feesPayer ' ) ) ,
( self . transaction . reference , self . response . get ( ' payKey ' ) ) , # payKey supposedly expires after 3 hours
( ' false ' , self . response . get ( ' reverseAllParallelPaymentsOnError ' ) ) ,
( None , self . response . get ( ' sender ' ) )
]
# loop through recipients
return comp
# also getting sender / senderEmail info too here that we don't currently hold in transaction. Want to save? Does that info come in IPN?
# responseEnvelope
# reverseAllParallelPaymentsOnError
# self.response.get('responseEnvelope')['ack'] should be 'Success' Can also be 'Failure', 'Warning', 'SuccessWithWarning', 'FailureWithWarning'
# can possibly use self.response.get('responseEnvelope')['timestamp'] to update self.transaction.date_modified
# preapprovalKey -- self.transaction doesn't hold that info right now
# paymentInfoList -- holds info for each recipient
2011-09-27 12:48:11 +00:00
2011-10-01 12:01:40 +00:00
class CancelPreapproval ( object ) :
def __init__ ( self , transaction ) :
headers = {
2011-10-06 23:10:20 +00:00
' X-PAYPAL-SECURITY-USERID ' : settings . PAYPAL_USERNAME ,
' X-PAYPAL-SECURITY-PASSWORD ' : settings . PAYPAL_PASSWORD ,
' X-PAYPAL-SECURITY-SIGNATURE ' : settings . PAYPAL_SIGNATURE ,
' X-PAYPAL-APPLICATION-ID ' : settings . PAYPAL_APPID ,
2011-10-01 12:01:40 +00:00
' X-PAYPAL-REQUEST-DATA-FORMAT ' : ' JSON ' ,
' X-PAYPAL-RESPONSE-DATA-FORMAT ' : ' JSON ' ,
}
data = {
' preapprovalKey ' : transaction . reference ,
' requestEnvelope ' : { ' errorLanguage ' : ' en_US ' }
}
self . raw_request = json . dumps ( data )
2011-10-06 23:10:20 +00:00
self . raw_response = url_request ( settings . PAYPAL_ENDPOINT , " /AdaptivePayments/CancelPreapproval " , data = self . raw_request , headers = headers ) . content ( )
2011-10-06 00:56:20 +00:00
logger . info ( " paypal CANCEL PREAPPROBAL response was: %s " % self . raw_response )
2011-10-01 12:01:40 +00:00
self . response = json . loads ( self . raw_response )
2011-10-06 00:56:20 +00:00
logger . info ( self . response )
2011-10-01 12:01:40 +00:00
def success ( self ) :
if self . status ( ) == ' Success ' or self . status ( ) == " SuccessWithWarning " :
return True
else :
return False
def error ( self ) :
if self . response . has_key ( ' error ' ) :
error = self . response [ ' error ' ]
2011-10-06 00:56:20 +00:00
logger . info ( error )
2011-10-01 12:01:40 +00:00
return error [ 0 ] [ ' message ' ]
else :
return ' Paypal Preapproval Cancel: Unknown Error '
def status ( self ) :
if self . response . has_key ( ' responseEnvelope ' ) and self . response [ ' responseEnvelope ' ] . has_key ( ' ack ' ) :
return self . response [ ' responseEnvelope ' ] [ ' ack ' ]
else :
return None
2011-09-29 07:50:07 +00:00
class Preapproval ( object ) :
2011-10-18 23:27:20 +00:00
def __init__ ( self , transaction , amount , return_url = None , cancel_url = None ) :
2011-09-29 07:50:07 +00:00
headers = {
2011-10-06 23:10:20 +00:00
' X-PAYPAL-SECURITY-USERID ' : settings . PAYPAL_USERNAME ,
' X-PAYPAL-SECURITY-PASSWORD ' : settings . PAYPAL_PASSWORD ,
' X-PAYPAL-SECURITY-SIGNATURE ' : settings . PAYPAL_SIGNATURE ,
' X-PAYPAL-APPLICATION-ID ' : settings . PAYPAL_APPID ,
2011-09-29 07:50:07 +00:00
' X-PAYPAL-REQUEST-DATA-FORMAT ' : ' JSON ' ,
' X-PAYPAL-RESPONSE-DATA-FORMAT ' : ' JSON ' ,
}
2011-10-18 23:27:20 +00:00
if return_url is None :
return_url = settings . BASE_URL + COMPLETE_URL
if cancel_url is None :
cancel_url = settings . BASE_URL + CANCEL_URL
2011-09-29 07:50:07 +00:00
2011-10-01 12:01:40 +00:00
# set the expiration date for the preapproval
2011-09-29 07:50:07 +00:00
now = datetime . datetime . utcnow ( )
2011-10-01 12:01:40 +00:00
expiry = now + datetime . timedelta ( days = PREAPPROVAL_PERIOD )
transaction . date_authorized = now
transaction . date_expired = expiry
transaction . save ( )
2011-09-29 07:50:07 +00:00
data = {
' endingDate ' : expiry . isoformat ( ) ,
' startingDate ' : now . isoformat ( ) ,
' maxTotalAmountOfAllPayments ' : ' %.2f ' % transaction . amount ,
2011-10-01 12:01:40 +00:00
' maxNumberOfPayments ' : 1 ,
' maxAmountPerPayment ' : ' %.2f ' % transaction . amount ,
2011-09-29 07:50:07 +00:00
' currencyCode ' : transaction . currency ,
' returnUrl ' : return_url ,
' cancelUrl ' : cancel_url ,
' requestEnvelope ' : { ' errorLanguage ' : ' en_US ' } ,
2011-10-14 17:52:17 +00:00
' ipnNotificationUrl ' : settings . BASE_URL + reverse ( ' PayPalIPN ' )
2011-09-29 07:50:07 +00:00
}
2011-11-23 17:07:07 +00:00
# Is ipnNotificationUrl being computed properly
print >> sys . stderr , ' ipnNotificationUrl ' , settings . BASE_URL + reverse ( ' PayPalIPN ' )
2011-09-29 07:50:07 +00:00
self . raw_request = json . dumps ( data )
2011-10-06 23:10:20 +00:00
self . raw_response = url_request ( settings . PAYPAL_ENDPOINT , " /AdaptivePayments/Preapproval " , data = self . raw_request , headers = headers ) . content ( )
2011-10-06 00:56:20 +00:00
logger . info ( " paypal PREAPPROVAL response was: %s " % self . raw_response )
2011-11-23 17:41:11 +00:00
print >> sys . stderr , " paypal PREAPPROVAL response was: " , self . raw_response
2011-09-29 07:50:07 +00:00
self . response = json . loads ( self . raw_response )
2011-10-06 00:56:20 +00:00
logger . info ( self . response )
2011-09-29 07:50:07 +00:00
def paykey ( self ) :
if self . response . has_key ( ' preapprovalKey ' ) :
return self . response [ ' preapprovalKey ' ]
else :
return None
2011-11-22 11:14:58 +00:00
2011-09-29 07:50:07 +00:00
def next_url ( self ) :
2011-10-06 23:10:20 +00:00
return ' %s ?cmd=_ap-preapproval&preapprovalkey= %s ' % ( settings . PAYPAL_PAYMENT_HOST , self . response [ ' preapprovalKey ' ] )
2011-09-29 07:50:07 +00:00
def error ( self ) :
if self . response . has_key ( ' error ' ) :
error = self . response [ ' error ' ]
2011-10-06 00:56:20 +00:00
logger . info ( error )
2011-09-29 07:50:07 +00:00
return error [ 0 ] [ ' message ' ]
else :
2011-10-01 12:01:40 +00:00
return ' Paypal Preapproval: Unknown Error '
2011-09-29 07:50:07 +00:00
def status ( self ) :
if self . response . has_key ( ' responseEnvelope ' ) and self . response [ ' responseEnvelope ' ] . has_key ( ' ack ' ) :
return self . response [ ' responseEnvelope ' ] [ ' ack ' ]
else :
return None
2011-10-14 17:52:17 +00:00
class PreapprovalDetails ( object ) :
2011-12-06 14:36:18 +00:00
def __init__ ( self , transaction ) :
try :
self . transaction = transaction
self . response = None
self . errorMessage = None
headers = {
' X-PAYPAL-SECURITY-USERID ' : settings . PAYPAL_USERNAME ,
' X-PAYPAL-SECURITY-PASSWORD ' : settings . PAYPAL_PASSWORD ,
' X-PAYPAL-SECURITY-SIGNATURE ' : settings . PAYPAL_SIGNATURE ,
' X-PAYPAL-APPLICATION-ID ' : settings . PAYPAL_APPID ,
' X-PAYPAL-REQUEST-DATA-FORMAT ' : ' JSON ' ,
' X-PAYPAL-RESPONSE-DATA-FORMAT ' : ' JSON '
}
# we can feed any of payKey, transactionId, and trackingId to identify transaction in question
# 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 ' } ,
' preapprovalKey ' : transaction . reference
}
self . raw_request = json . dumps ( data )
self . connection = url_request ( settings . PAYPAL_ENDPOINT , " /AdaptivePayments/PreapprovalDetails " , data = self . raw_request , headers = headers )
self . raw_response = self . connection . content ( )
self . code = self . connection . code ( )
if self . code != 200 :
self . errorMessage = ' PayPal response code was %i ' % self . code
return
logger . info ( " paypal PreapprovalDetails response was: %s " % self . raw_response )
self . response = json . loads ( self . raw_response )
logger . info ( self . response )
self . status = self . response . get ( " status " , None )
self . amount = self . response . get ( " maxTotalAmountOfAllPayments " , None )
self . currency = self . response . get ( " currencyCode " , None )
self . approved = self . response . get ( " approved " , None )
self . expiration = self . response . get ( " endingDate " , None )
self . date = self . response . get ( " startingDate " , None )
except :
self . errorMessage = " Error: ServerError "
traceback . print_exc ( )
def error ( self ) :
if self . response and self . response . has_key ( ' error ' ) :
error = self . response [ ' error ' ]
logger . info ( error )
return error [ 0 ] [ ' message ' ]
elif self . errorMessage :
return self . errorMessage
else :
return None
2011-10-14 17:52:17 +00:00
2011-09-27 12:48:11 +00:00
class IPN ( object ) :
def __init__ ( self , request ) :
try :
# verify that the request is paypal's
self . error = None
2011-10-06 23:10:20 +00:00
url = " %s ?cmd=_notify-validate " % settings . PAYPAL_PAYMENT_HOST
2011-09-27 12:48:11 +00:00
data = urllib . urlencode ( request . POST . copy ( ) )
req = urllib2 . Request ( url , data )
response = urllib2 . urlopen ( req )
raw_response = response . read ( )
status = response . code
# check code
if status != 200 :
self . error = ' PayPal response code was %i ' % verify_response . code ( )
return
# check response
if raw_response != ' VERIFIED ' :
self . error = ' PayPal response was " %s " ' % raw_response
return
# Process the details
self . status = request . POST . get ( ' status ' , None )
self . sender_email = request . POST . get ( ' sender_email ' , None )
self . action_type = request . POST . get ( ' action_type ' , None )
2011-10-01 12:01:40 +00:00
self . pay_key = request . POST . get ( ' pay_key ' , None )
2011-09-29 07:50:07 +00:00
self . preapproval_key = request . POST . get ( ' preapproval_key ' , None )
2011-09-27 12:48:11 +00:00
self . transaction_type = request . POST . get ( ' transaction_type ' , None )
2011-10-01 12:01:40 +00:00
self . reason_code = request . POST . get ( ' reason_code ' , None )
2011-10-27 06:35:46 +00:00
self . trackingId = request . POST . get ( ' tracking_id ' , None )
2011-09-27 12:48:11 +00:00
self . process_transactions ( request )
except :
self . error = " Error: ServerError "
traceback . print_exc ( )
2011-10-27 06:35:46 +00:00
def uniqueID ( self ) :
if self . trackingId :
return self . trackingId
else :
return None
2011-10-01 12:01:40 +00:00
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
# before we have time to store the pay key
if self . preapproval_key :
return self . preapproval_key
elif self . pay_key :
return self . pay_key
else :
return None
2011-09-27 12:48:11 +00:00
def success ( self ) :
return self . error == None
2011-10-01 12:01:40 +00:00
2011-09-27 12:48:11 +00:00
@classmethod
def slicedict ( cls , d , s ) :
return dict ( ( str ( k . replace ( s , ' ' , 1 ) ) , v ) for k , v in d . iteritems ( ) if k . startswith ( s ) )
def process_transactions ( self , request ) :
self . transactions = [ ]
transaction_nums = range ( 6 )
for transaction_num in transaction_nums :
transdict = IPN . slicedict ( request . POST , ' transaction[ %s ]. ' % transaction_num )
if len ( transdict ) > 0 :
self . transactions . append ( transdict )
2011-10-06 00:56:20 +00:00
logger . info ( transdict )
2011-09-27 12:48:11 +00:00