def thanks(request, question_id): question = Question.objects.get(pk = question_id) connection = FPSConnection( aws_access_key_id = AWS_KEY_ID, aws_secret_access_key = AWS_SECRET_KEY, is_secure = True, host = AMAZON_DOMAIN, path = '/', ) #verify signature returnUrl = 'http://' + SITE_DOMAIN + '/thanks/' + str(question_id) httpParameters = {} for k, v in dict(request.GET).items(): httpParameters[k] = v[0] LogPipelineResponse.objects.create(question = question, response = str(httpParameters)) httpParameters = urlencode(httpParameters) verifyResponse = connection.verify_signature(returnUrl, httpParameters).__dict__ #check for errors if 'errorMessage' in request.GET: messages.error(request, request.GET['errorMessage']) #check if they're authorized if verifyResponse['Status'] == 'Success': #charge payment callerTokenId = connection.install_caller_instruction() LogCallerToken.objects.create(question = question, token = str(callerTokenId)) recipientTokenId = connection.install_recipient_instruction() LogRecipientToken.objects.create(question = question, token = str(recipientTokenId)) result = connection.pay( callerReference = request.GET['callerReference'], callerTokenId = callerTokenId, recipientTokenId = recipientTokenId, senderTokenId = request.GET['tokenID'], transactionAmount = str(question.price), ) payResponse = result.__dict__ LogPaymentResponse.objects.create(question = question, response = str(payResponse)) #check for errors if 'Status' in payResponse and payResponse['Status']: question.published = True question.save() messages.success(request, 'Your question has been published!') context = Context({ 'questioner': question.user, 'url': 'http://' + SITE_DOMAIN + '/question/' + str(question.id), }) sendTemplateEmail('Question posted successfully!', question.user.email, 'questionerThanks', context) return HttpResponseRedirect('/question/' + str(question.id)) messages.error(request, 'A problem occurred when processing your payment, please try again.') return HttpResponseRedirect('/question/' + str(question.id))
class AmazonFpsIntegration(Integration): """ Fields required: transactionAmount: Amount to be charged/authorized paymentReason: Description of the transaction paymentPage: Page to direct the user on completion/failure of transaction """ display_name = "Amazon Flexible Payment Service" def __init__(self, options=None): if not options: options = {} merchant_settings = getattr(settings, "MERCHANT_SETTINGS") if not merchant_settings or not merchant_settings.get("amazon_fps"): raise IntegrationNotConfigured("The '%s' integration is not correctly " "configured." % self.display_name) amazon_fps_settings = merchant_settings["amazon_fps"] self.aws_access_key = options.get("aws_access_key", None) or amazon_fps_settings['AWS_ACCESS_KEY'] self.aws_secret_access_key = options.get("aws_secret_access_key", None) or amazon_fps_settings['AWS_SECRET_ACCESS_KEY'] super(AmazonFpsIntegration, self).__init__(options=options) self.fps_connection = FPSConnection(self.aws_access_key, self.aws_secret_access_key, **options) @property def service_url(self): if self.test_mode: return FPS_SANDBOX_API_ENDPOINT return FPS_PROD_API_ENDPOINT @property def link_url(self): tmp_fields = self.fields.copy() tmp_fields.pop("aws_access_key", None) tmp_fields.pop("aws_secret_access_key", None) tmp_fields.pop("paymentPage", None) return self.fps_connection.make_url(tmp_fields.pop("returnURL"), tmp_fields.pop("paymentReason"), tmp_fields.pop("pipelineName"), str(tmp_fields.pop("transactionAmount")), **tmp_fields) def purchase(self, amount, options=None): if not options: options = {} tmp_options = options.copy() permissible_options = ["senderTokenId", "recipientTokenId", "chargeFeeTo", "callerReference", "senderReference", "recipientReference", "senderDescription", "recipientDescription", "callerDescription", "metadata", "transactionDate", "reserve"] tmp_options["senderTokenId"] = options["tokenID"] for key in options: if key not in permissible_options: tmp_options.pop(key) resp = self.fps_connection.pay(amount, tmp_options.pop("senderTokenId"), callerReference=tmp_options.pop("callerReference"), **tmp_options) return {"status": resp[0].TransactionStatus, "response": resp[0]} def authorize(self, amount, options=None): if not options: options = {} options["reserve"] = True return self.purchase(amount, options) def capture(self, amount, options=None): if not options: options = {} assert "ReserveTransactionId" in options, "Expecting 'ReserveTransactionId' in options" resp = self.fps_connection.settle(options["ReserveTransactionId"], amount) return {"status": resp[0].TransactionStatus, "response": resp[0]} def credit(self, amount, options=None): if not options: options = {} assert "CallerReference" in options, "Expecting 'CallerReference' in options" assert "TransactionId" in options, "Expecting 'TransactionId' in options" resp = self.fps_connection.refund(options["CallerReference"], options["TransactionId"], refundAmount=amount, callerDescription=options.get("description", None)) return {"status": resp[0].TransactionStatus, "response": resp[0]} def void(self, identification, options=None): if not options: options = {} # Requires the TransactionID to be passed as 'identification' resp = self.fps_connection.cancel(identification, options.get("description", None)) return {"status": resp[0].TransactionStatus, "response": resp[0]} def get_urls(self): urlpatterns = patterns('', url(r'^fps-notify-handler/$', self.fps_ipn_handler, name="fps_ipn_handler"), url(r'^fps-return-url/$', self.fps_return_url, name="fps_return_url"), ) return urlpatterns @csrf_exempt_m @require_POST_m def fps_ipn_handler(self, request): uri = request.build_absolute_uri() parsed_url = urlparse.urlparse(uri) resp = self.fps_connection.verify_signature("%s://%s%s" %(parsed_url.scheme, parsed_url.netloc, parsed_url.path), request.raw_post_data) if not resp[0].VerificationStatus == "Success": return HttpResponseForbidden() data = dict(map(lambda x: x.split("="), request.raw_post_data.split("&"))) for (key, val) in data.iteritems(): data[key] = urllib.unquote_plus(val) if AmazonFPSResponse.objects.filter(transactionId=data["transactionId"]).count(): resp = AmazonFPSResponse.objects.get(transactionId=data["transactionId"]) else: resp = AmazonFPSResponse() for (key, val) in data.iteritems(): attr_exists = hasattr(resp, key) if attr_exists and not callable(getattr(resp, key, None)): if key == "transactionDate": val = datetime.datetime(*time.localtime(float(val))[:6]) setattr(resp, key, val) resp.save() if resp.statusCode == "Success": transaction_was_successful.send(sender=self.__class__, type=data["operation"], response=resp) else: if not "Pending" in resp.statusCode: transaction_was_unsuccessful.send(sender=self.__class__, type=data["operation"], response=resp) # Return a HttpResponse to prevent django from complaining return HttpResponse(resp.statusCode) def fps_return_url(self, request): uri = request.build_absolute_uri() parsed_url = urlparse.urlparse(uri) resp = self.fps_connection.verify_signature("%s://%s%s" %(parsed_url.scheme, parsed_url.netloc, parsed_url.path), parsed_url.query) if not resp[0].VerificationStatus == "Success": return HttpResponseForbidden() return self.transaction(request) def transaction(self, request): """Has to be overridden by the subclasses""" raise NotImplementedError
def test(): conn = FPSConnection() # example response from the docs params = 'expiry=08%2F2015&signature=ynDukZ9%2FG77uSJVb5YM0cadwHVwYKPMKOO3PNvgADbv6VtymgBxeOWEhED6KGHsGSvSJnMWDN%2FZl639AkRe9Ry%2F7zmn9CmiM%2FZkp1XtshERGTqi2YL10GwQpaH17MQqOX3u1cW4LlyFoLy4celUFBPq1WM2ZJnaNZRJIEY%2FvpeVnCVK8VIPdY3HMxPAkNi5zeF2BbqH%2BL2vAWef6vfHkNcJPlOuOl6jP4E%2B58F24ni%2B9ek%2FQH18O4kw%2FUJ7ZfKwjCCI13%2BcFybpofcKqddq8CuUJj5Ii7Pdw1fje7ktzHeeNhF0r9siWcYmd4JaxTP3NmLJdHFRq2T%2FgsF3vK9m3gw%3D%3D&signatureVersion=2&signatureMethod=RSA-SHA1&certificateUrl=https%3A%2F%2Ffps.sandbox.amazonaws.com%2Fcerts%2F090909%2FPKICert.pem&tokenID=A5BB3HUNAZFJ5CRXIPH72LIODZUNAUZIVP7UB74QNFQDSQ9MN4HPIKISQZWPLJXF&status=SC&callerReference=callerReferenceMultiUse1' endpoint = 'http://vamsik.desktop.amazon.com:8080/ipn.jsp' conn.verify_signature(endpoint, params)
class AmazonFpsIntegration(Integration): """ Fields required: transactionAmount: Amount to be charged/authorized paymentReason: Description of the transaction paymentPage: Page to direct the user on completion/failure of transaction """ display_name = "Amazon Flexible Payment Service" def __init__(self, options=None): if not options: options = {} merchant_settings = getattr(settings, "MERCHANT_SETTINGS") if not merchant_settings or not merchant_settings.get("amazon_fps"): raise IntegrationNotConfigured( "The '%s' integration is not correctly " "configured." % self.display_name) amazon_fps_settings = merchant_settings["amazon_fps"] self.aws_access_key = options.get( "aws_access_key", None) or amazon_fps_settings['AWS_ACCESS_KEY'] self.aws_secret_access_key = options.get( "aws_secret_access_key", None) or amazon_fps_settings['AWS_SECRET_ACCESS_KEY'] super(AmazonFpsIntegration, self).__init__(options=options) self.fps_connection = FPSConnection(self.aws_access_key, self.aws_secret_access_key, **options) @property def service_url(self): if self.test_mode: return FPS_SANDBOX_API_ENDPOINT return FPS_PROD_API_ENDPOINT @property def link_url(self): tmp_fields = self.fields.copy() tmp_fields.pop("aws_access_key", None) tmp_fields.pop("aws_secret_access_key", None) tmp_fields.pop("paymentPage", None) return self.fps_connection.make_url( tmp_fields.pop("returnURL"), tmp_fields.pop("paymentReason"), tmp_fields.pop("pipelineName"), str(tmp_fields.pop("transactionAmount")), **tmp_fields) def purchase(self, amount, options=None): if not options: options = {} tmp_options = options.copy() permissible_options = [ "senderTokenId", "recipientTokenId", "chargeFeeTo", "callerReference", "senderReference", "recipientReference", "senderDescription", "recipientDescription", "callerDescription", "metadata", "transactionDate", "reserve" ] tmp_options["senderTokenId"] = options["tokenID"] for key in options: if key not in permissible_options: tmp_options.pop(key) resp = self.fps_connection.pay( amount, tmp_options.pop("senderTokenId"), callerReference=tmp_options.pop("callerReference"), **tmp_options) return {"status": resp[0].TransactionStatus, "response": resp[0]} def authorize(self, amount, options=None): if not options: options = {} options["reserve"] = True return self.purchase(amount, options) def capture(self, amount, options=None): if not options: options = {} assert "ReserveTransactionId" in options, "Expecting 'ReserveTransactionId' in options" resp = self.fps_connection.settle(options["ReserveTransactionId"], amount) return {"status": resp[0].TransactionStatus, "response": resp[0]} def credit(self, amount, options=None): if not options: options = {} assert "CallerReference" in options, "Expecting 'CallerReference' in options" assert "TransactionId" in options, "Expecting 'TransactionId' in options" resp = self.fps_connection.refund(options["CallerReference"], options["TransactionId"], refundAmount=amount, callerDescription=options.get( "description", None)) return {"status": resp[0].TransactionStatus, "response": resp[0]} def void(self, identification, options=None): if not options: options = {} # Requires the TransactionID to be passed as 'identification' resp = self.fps_connection.cancel(identification, options.get("description", None)) return {"status": resp[0].TransactionStatus, "response": resp[0]} def get_urls(self): urlpatterns = patterns( '', url(r'^fps-notify-handler/$', self.fps_ipn_handler, name="fps_ipn_handler"), url(r'^fps-return-url/$', self.fps_return_url, name="fps_return_url"), ) return urlpatterns @csrf_exempt_m @require_POST_m def fps_ipn_handler(self, request): uri = request.build_absolute_uri() parsed_url = urlparse.urlparse(uri) resp = self.fps_connection.verify_signature( "%s://%s%s" % (parsed_url.scheme, parsed_url.netloc, parsed_url.path), request.raw_post_data) if not resp[0].VerificationStatus == "Success": return HttpResponseForbidden() data = dict( map(lambda x: x.split("="), request.raw_post_data.split("&"))) for (key, val) in data.iteritems(): data[key] = urllib.unquote_plus(val) if AmazonFPSResponse.objects.filter( transactionId=data["transactionId"]).count(): resp = AmazonFPSResponse.objects.get( transactionId=data["transactionId"]) else: resp = AmazonFPSResponse() for (key, val) in data.iteritems(): attr_exists = hasattr(resp, key) if attr_exists and not callable(getattr(resp, key, None)): if key == "transactionDate": val = datetime.datetime(*time.localtime(float(val))[:6]) setattr(resp, key, val) resp.save() if resp.statusCode == "Success": transaction_was_successful.send(sender=self.__class__, type=data["operation"], response=resp) else: if not "Pending" in resp.statusCode: transaction_was_unsuccessful.send(sender=self.__class__, type=data["operation"], response=resp) # Return a HttpResponse to prevent django from complaining return HttpResponse(resp.statusCode) def fps_return_url(self, request): uri = request.build_absolute_uri() parsed_url = urlparse.urlparse(uri) resp = self.fps_connection.verify_signature( "%s://%s%s" % (parsed_url.scheme, parsed_url.netloc, parsed_url.path), parsed_url.query) if not resp[0].VerificationStatus == "Success": return HttpResponseForbidden() return self.transaction(request) def transaction(self, request): """Has to be overridden by the subclasses""" raise NotImplementedError
from boto.fps.connection import FPSConnection conn = FPSConnection() # example response from the docs params = 'expiry=08%2F2015&signature=ynDukZ9%2FG77uSJVb5YM0cadwHVwYKPMKOO3PNvgADbv6VtymgBxeOWEhED6KGHsGSvSJnMWDN%2FZl639AkRe9Ry%2F7zmn9CmiM%2FZkp1XtshERGTqi2YL10GwQpaH17MQqOX3u1cW4LlyFoLy4celUFBPq1WM2ZJnaNZRJIEY%2FvpeVnCVK8VIPdY3HMxPAkNi5zeF2BbqH%2BL2vAWef6vfHkNcJPlOuOl6jP4E%2B58F24ni%2B9ek%2FQH18O4kw%2FUJ7ZfKwjCCI13%2BcFybpofcKqddq8CuUJj5Ii7Pdw1fje7ktzHeeNhF0r9siWcYmd4JaxTP3NmLJdHFRq2T%2FgsF3vK9m3gw%3D%3D&signatureVersion=2&signatureMethod=RSA-SHA1&certificateUrl=https%3A%2F%2Ffps.sandbox.amazonaws.com%2Fcerts%2F090909%2FPKICert.pem&tokenID=A5BB3HUNAZFJ5CRXIPH72LIODZUNAUZIVP7UB74QNFQDSQ9MN4HPIKISQZWPLJXF&status=SC&callerReference=callerReferenceMultiUse1' endpoint = 'http://vamsik.desktop.amazon.com:8080/ipn.jsp' conn.verify_signature(endpoint, params)