From d370091db71ae3cea7bb74e07963dedb0b331c33 Mon Sep 17 00:00:00 2001 From: icellama21 Date: Wed, 18 Apr 2012 16:22:00 -0400 Subject: [PATCH] Adding amazon FPS return view, execute and preapproval support --- payment/amazon.py | 200 +++++++++++++++++++++++++++++++++++++++++---- payment/manager.py | 8 +- payment/urls.py | 7 +- payment/views.py | 19 +++-- 4 files changed, 203 insertions(+), 31 deletions(-) diff --git a/payment/amazon.py b/payment/amazon.py index 99ddcdb3..511afc9a 100644 --- a/payment/amazon.py +++ b/payment/amazon.py @@ -10,7 +10,7 @@ from django.db.models.query_utils import Q from django.shortcuts import render_to_response from django.template import RequestContext from boto.fps.connection import FPSConnection - +from django.http import HttpResponse, HttpRequest, HttpResponseRedirect, HttpResponseBadRequest from datetime import timedelta from regluit.utils.localdatetime import now, zuluformat import dateutil @@ -29,10 +29,108 @@ import commands import smtplib import urlparse import decimal -import sys +import time logger = logging.getLogger(__name__) +AMAZON_STATUS_SUCCESS_ABT = 'SA' +AMAZON_STATUS_SUCCESS_ACH = 'SB' +AMAZON_STATUS_SUCCESS_CREDIT = 'SC' +AMAZON_STATUS_ERROR = 'SE' +AMAZON_STATUS_ADBANDONED = 'A' +AMAZON_STATUS_EXCEPTION = 'CE' +AMAZON_STATUS_PAYMENT_MISMATCH = 'PE' +AMAZON_STATUS_INCOMPLETE = 'NP' +AMAZON_STATUS_NOT_REGISTERED = 'NM' + +AMAZON_STATUS_CANCLLED = 'Cancelled' +AMAZON_STATUS_FAILURE = 'Failure' +AMAZON_STATUS_PENDING = 'Pending' +AMAZON_STATUS_RESERVED = 'Reserved' +AMAZON_STATUS_SUCCESS = 'Success' + +AMAZON_IPN_STATUS_CANCELLED = 'CANCELLED' +AMAZON_IPN_STATUS_FAILURE = 'FAILURE' +AMAZON_IPN_STATUS_PENDING = 'PENDING' +AMAZON_IPN_STATUS_RESERVED = 'RESERVED' +AMAZON_IPN_STATUS_SUCCESS = 'SUCCESS' + +def amazonPaymentReturn(request): + + try: + + # pick up all get and post parameters and display + output = "payment complete" + output += request.method + "\n" + str(request.REQUEST.items()) + print output + + signature = request.GET['signature'] + reference = request.GET['callerReference'] + token = request.GET['tokenID'] + status = request.GET['status'] + + # BUGUBG - Should we verify the signature here? + # + # Find the transaction by reference, there should only be one + # We will catch the exception if it does not exist + # + transaction = Transaction.objects.get(secret=reference) + + # + # BUGBUG, for now lets map amazon status code to paypal, just to keep things uninform + # + if transaction.type == PAYMENT_TYPE_INSTANT: + # Instant payments need to be executed now + + if status == AMAZON_STATUS_SUCCESS_ABT or status == AMAZON_STATUS_SUCCESS_ACH or status == AMAZON_STATUS_SUCCESS_CREDIT: + # The above status code are unique to the return URL and are different than the pay API codes + + # Store the token + transaction.pay_key = token + + # + # BUGBUG, need to handle multiple recipients + # Send the pay request now to ourselves + # + e = Execute(transaction=transaction) + + transaction.status = e.status + + if e.success() and not e.error(): + # Success case, save the ID + print "Amazon Execute returned succesfully" + else: + print "Amazon Execute returned an error, status %s" % e.status + # Failure case + + # Save some context for this transaction + + else: + transaction.status = AMAZON_STATUS_FAILURE + + elif transaction.type == PAYMENT_TYPE_AUTHORIZATION: + # + # Future payments, we only need to store the token. The authorization was requested with the default expidation + # date set in our settings. When we are ready, we can call execute on this + # + if status == AMAZON_STATUS_SUCCESS_ABT or status == AMAZON_STATUS_SUCCESS_ACH or status == AMAZON_STATUS_SUCCESS_CREDIT: + + # The above status code are unique to the return URL and are different than the pay API codes + transaction.status = AMAZON_STATUS_PENDING + transaction.approved = True + transaction.pay_key = token + + else: + transaction.status = AMAZON_STATUS_FAILURE + + transaction.save() + return HttpResponse("Success") + + except: + traceback.print_exc() + return HttpResponseBadRequest("Error") + + class AmazonRequest: ''' Handles common information that is processed from the response envelope of the amazon request. @@ -43,12 +141,25 @@ class AmazonRequest: response = None raw_response = None errorMessage = None + status = None url = None def ack( self ): return None def success(self): + + print "CALLING SUCCESS" + if self.status: + # + # process the boto response if we have one. The status codes here are only boto response codes, not + # return_url codes + # + if self.status == AMAZON_STATUS_SUCCESS: + return True + else: + return False + if self.errorMessage: return False else: @@ -71,17 +182,23 @@ class AmazonRequest: return self.errorMessage def envelope(self): - return None + + # The envelope is used to store info about this request + if self.response: + return str(self.response) + else: + return None def correlation_id(self): return None def timestamp(self): return None - - + + class Pay( AmazonRequest ): - def __init__( self, transaction, return_url=None, cancel_url=None, options=None): + + def __init__( self, transaction, return_url=None, cancel_url=None, options=None, amount=None): try: @@ -94,22 +211,29 @@ class Pay( AmazonRequest ): receiver_list = [] receivers = transaction.receiver_set.all() - if len(receivers) == 0: - raise Exception - - # by setting primary_string of the first receiver to 'true', we are doing a Chained payment - total_amount = 0 - for r in receivers: - total_amount += r.amount + if not amount: + # by setting primary_string of the first receiver to 'true', we are doing a Chained payment + amount = 0 + for r in receivers: + amount += r.amount - logger.info(receiver_list) + logger.info(receiver_list) # Data fields for amazon - data = {} + + expiry = now() + timedelta( days=settings.PREAPPROVAL_PERIOD ) + + data = { + 'amountType':'Maximum', # The transaction amount is the maximum amount + 'callerReference': transaction.secret, + 'currencyCode': 'USD', + 'globalAmountLimit': str(amount), + 'validityExpiry': str(int(time.mktime(expiry.timetuple()))), # use the preapproval date by default + } print "Amazon PURCHASE url request data: %s" % data - self.url = self.connection.make_url(return_url, "Test Payment", "SingleUse", str(total_amount), **data) + self.url = self.connection.make_url(return_url, "Test Payment", "MultiUse", str(amount), **data) print "Amazon PURCHASE url was: %s" % self.url except: @@ -133,4 +257,46 @@ class Pay( AmazonRequest ): def embedded_url(self): return None - \ No newline at end of file + +class Preapproval(Pay): + + def __init__( self, transaction, amount, expiry=None, return_url=None, cancel_url=None): + + # Call into our parent class + Pay.__init__(self, transaction, return_url=return_url, cancel_url=cancel_url, options=None, amount=amount) + +class Execute(AmazonRequest): + + def __init__(self, transaction=None): + + try: + + # Use the boto class top open a connection + self.connection = FPSConnection(settings.FPS_ACCESS_KEY, settings.FPS_SECRET_KEY) + + # BUGBUG, handle multiple receivers! For now we just send the money to ourselves + + self.raw_response = self.connection.pay(transaction.amount, + transaction.pay_key, + recipientTokenId=None, + callerReference=transaction.secret, + senderReference=None, + recipientReference=None, + senderDescription=None, + recipientDescription=None, + callerDescription=None, + metadata=None, + transactionDate=None, + reserve=False) + + print "Amazon EXECUTE response was: %s" % self.raw_response + + self.response = self.raw_response[0] + print "RESPONSE: %s" % self.response + self.status = self.response.TransactionStatus + print "STATUS: %s" % self.status + + except: + traceback.print_exc() + self.errorMessage = "Error: Server Error" + \ No newline at end of file diff --git a/payment/manager.py b/payment/manager.py index 89f5e4e9..a3f2e969 100644 --- a/payment/manager.py +++ b/payment/manager.py @@ -6,13 +6,13 @@ from django.conf import settings from regluit.payment.parameters import * if settings.PAYMENT_PROCESSOR == 'paypal': - from regluit.payment.paypal import Pay + from regluit.payment.paypal import Pay, Execute, Preapproval elif settings.PAYMENT_PROCESSOR == 'amazon': - from regluit.payment.amazon import Pay + from regluit.payment.amazon import Pay, Execute, Preapproval -from regluit.payment.paypal import Execute, IPN, IPN_TYPE_PAYMENT, IPN_TYPE_PREAPPROVAL, IPN_TYPE_ADJUSTMENT, IPN_PREAPPROVAL_STATUS_ACTIVE, IPN_PAY_STATUS_INCOMPLETE, IPN_PAY_STATUS_NONE -from regluit.payment.paypal import Preapproval, IPN_PAY_STATUS_COMPLETED, CancelPreapproval, PaymentDetails, PreapprovalDetails, IPN_SENDER_STATUS_COMPLETED, IPN_TXN_STATUS_COMPLETED +from regluit.payment.paypal import IPN, IPN_TYPE_PAYMENT, IPN_TYPE_PREAPPROVAL, IPN_TYPE_ADJUSTMENT, IPN_PREAPPROVAL_STATUS_ACTIVE, IPN_PAY_STATUS_INCOMPLETE, IPN_PAY_STATUS_NONE +from regluit.payment.paypal import IPN_PAY_STATUS_COMPLETED, CancelPreapproval, PaymentDetails, PreapprovalDetails, IPN_SENDER_STATUS_COMPLETED, IPN_TXN_STATUS_COMPLETED from regluit.payment.paypal import RefundPayment import uuid import traceback diff --git a/payment/urls.py b/payment/urls.py index ff7b2c8a..5ddd06c3 100644 --- a/payment/urls.py +++ b/payment/urls.py @@ -10,7 +10,12 @@ fps_recur_obj = get_integration("fps") urlpatterns = patterns( "regluit.payment.views", url(r"^paypalipn", "paypalIPN", name="PayPalIPN"), - url(r"^amazonpaymentreturn", "amazonPaymentReturn", name="AmazonPaymentReturn"), +) + +# Amazon payment URLs +urlpatterns += patterns( + "regluit.payment.amazon", + url(r"^amazonpaymentreturn", "amazonPaymentReturn", name="AmazonPaymentReturn"), ) if not settings.IS_PREVIEW: diff --git a/payment/views.py b/payment/views.py index ba8b3bfb..abe05403 100644 --- a/payment/views.py +++ b/payment/views.py @@ -8,7 +8,7 @@ from django.shortcuts import render_to_response from django.contrib.auth.models import User from django.contrib.sites.models import RequestSite from regluit.payment.parameters import * -from django.http import HttpResponse, HttpRequest, HttpResponseRedirect +from django.http import HttpResponse, HttpRequest, HttpResponseRedirect, HttpResponseBadRequest from django.views.decorators.csrf import csrf_exempt from django.test.utils import setup_test_environment from django.template import RequestContext @@ -114,12 +114,18 @@ def testAuthorize(request): receiver_list = [{'email': TEST_RECEIVERS[0], 'amount':20.00}, {'email': TEST_RECEIVERS[1], 'amount':10.00}] + # Set the return url for the processor + if settings.PAYMENT_PROCESSOR == 'amazon': + return_url = settings.BASE_URL + reverse('AmazonPaymentReturn') + else: + return_url = None + if campaign_id: campaign = Campaign.objects.get(id=int(campaign_id)) - t, url = p.authorize('USD', TARGET_TYPE_CAMPAIGN, amount, campaign=campaign, list=None, user=None) + t, url = p.authorize('USD', TARGET_TYPE_CAMPAIGN, amount, campaign=campaign, return_url=return_url, list=None, user=None) else: - t, url = p.authorize('USD', TARGET_TYPE_NONE, amount, campaign=None, list=None, user=None) + t, url = p.authorize('USD', TARGET_TYPE_NONE, amount, campaign=None, return_url=return_url, list=None, user=None) if url: logger.info("testAuthorize: " + url) @@ -309,12 +315,7 @@ def paypalIPN(request): logger.info(str(request.POST)) return HttpResponse("ipn") - -def amazonPaymentReturn(request): - # pick up all get and post parameters and display - output = "payment complete" - output += request.method + "\n" + str(request.REQUEST.items()) - return HttpResponse(output) + def paymentcomplete(request): # pick up all get and post parameters and display