class FPSTestCase(unittest.TestCase): def setUp(self): self.fps = FPSConnection(host='fps.sandbox.amazonaws.com') if advanced: self.activity = self.fps.get_account_activity(\ StartDate='2012-01-01') result = self.activity.GetAccountActivityResult self.transactions = result.Transaction @unittest.skipUnless(simple, "skipping simple test") def test_get_account_balance(self): response = self.fps.get_account_balance() self.assertTrue(hasattr(response, 'GetAccountBalanceResult')) self.assertTrue(hasattr(response.GetAccountBalanceResult, 'AccountBalance')) accountbalance = response.GetAccountBalanceResult.AccountBalance self.assertTrue(hasattr(accountbalance, 'TotalBalance')) self.assertIsInstance(accountbalance.TotalBalance, ComplexAmount) self.assertTrue(hasattr(accountbalance, 'AvailableBalances')) availablebalances = accountbalance.AvailableBalances self.assertTrue(hasattr(availablebalances, 'RefundBalance')) @unittest.skipUnless(simple, "skipping simple test") def test_complex_amount(self): response = self.fps.get_account_balance() accountbalance = response.GetAccountBalanceResult.AccountBalance asfloat = float(accountbalance.TotalBalance.Value) self.assertIn('.', str(asfloat)) @unittest.skipUnless(simple, "skipping simple test") def test_required_arguments(self): with self.assertRaises(KeyError): self.fps.write_off_debt(AdjustmentAmount=123.45) @unittest.skipUnless(simple, "skipping simple test") def test_cbui_url(self): inputs = { 'transactionAmount': 123.45, 'pipelineName': 'SingleUse', 'returnURL': 'https://localhost/', 'paymentReason': 'a reason for payment', 'callerReference': 'foo', } result = self.fps.cbui_url(**inputs) print "cbui_url() yields {0}".format(result) @unittest.skipUnless(simple, "skipping simple test") def test_get_account_activity(self): response = self.fps.get_account_activity(StartDate='2012-01-01') self.assertTrue(hasattr(response, 'GetAccountActivityResult')) result = response.GetAccountActivityResult self.assertTrue(hasattr(result, 'BatchSize')) try: int(result.BatchSize) except: self.assertTrue(False) @unittest.skipUnless(advanced, "skipping advanced test") def test_get_transaction(self): assert len(self.transactions) transactionid = self.transactions[0].TransactionId result = self.fps.get_transaction(TransactionId=transactionid) self.assertTrue(hasattr(result.GetTransactionResult, 'Transaction')) @unittest.skip('cosmetic') def test_bad_request(self): try: self.fps.write_off_debt(CreditInstrumentId='foo', AdjustmentAmount=123.45) except Exception as e: print e @unittest.skip('cosmetic') def test_repr(self): print self.fps.get_account_balance()
class FPSTestCase(unittest.TestCase): def setUp(self): self.fps = FPSConnection(host="fps.sandbox.amazonaws.com") if advanced: self.activity = self.fps.get_account_activity(StartDate="2012-01-01") result = self.activity.GetAccountActivityResult self.transactions = result.Transaction @unittest.skipUnless(simple, "skipping simple test") def test_get_account_balance(self): response = self.fps.get_account_balance() self.assertTrue(hasattr(response, "GetAccountBalanceResult")) self.assertTrue(hasattr(response.GetAccountBalanceResult, "AccountBalance")) accountbalance = response.GetAccountBalanceResult.AccountBalance self.assertTrue(hasattr(accountbalance, "TotalBalance")) self.assertIsInstance(accountbalance.TotalBalance, ComplexAmount) self.assertTrue(hasattr(accountbalance, "AvailableBalances")) availablebalances = accountbalance.AvailableBalances self.assertTrue(hasattr(availablebalances, "RefundBalance")) @unittest.skipUnless(simple, "skipping simple test") def test_complex_amount(self): response = self.fps.get_account_balance() accountbalance = response.GetAccountBalanceResult.AccountBalance asfloat = float(accountbalance.TotalBalance.Value) self.assertIn(".", str(asfloat)) @unittest.skipUnless(simple, "skipping simple test") def test_required_arguments(self): with self.assertRaises(KeyError): self.fps.write_off_debt(AdjustmentAmount=123.45) @unittest.skipUnless(simple, "skipping simple test") def test_cbui_url(self): inputs = { "transactionAmount": 123.45, "pipelineName": "SingleUse", "returnURL": "https://localhost/", "paymentReason": "a reason for payment", "callerReference": "foo", } result = self.fps.cbui_url(**inputs) print "cbui_url() yields {0}".format(result) @unittest.skipUnless(simple, "skipping simple test") def test_get_account_activity(self): response = self.fps.get_account_activity(StartDate="2012-01-01") self.assertTrue(hasattr(response, "GetAccountActivityResult")) result = response.GetAccountActivityResult self.assertTrue(hasattr(result, "BatchSize")) try: int(result.BatchSize) except: self.assertTrue(False) @unittest.skipUnless(advanced, "skipping advanced test") def test_get_transaction(self): assert len(self.transactions) transactionid = self.transactions[0].TransactionId result = self.fps.get_transaction(TransactionId=transactionid) self.assertTrue(hasattr(result.GetTransactionResult, "Transaction")) @unittest.skip("cosmetic") def test_bad_request(self): try: self.fps.write_off_debt(CreditInstrumentId="foo", AdjustmentAmount=123.45) except Exception, e: print e
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" template = "billing/amazon_fps.html" 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) options.setdefault('host', self.service_url) 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.cbui_url(returnURL=tmp_fields.pop("returnURL"), paymentReason=tmp_fields.pop("paymentReason"), pipelineName=tmp_fields.pop("pipelineName"), transactionAmount=str(tmp_fields.pop("transactionAmount")), **tmp_fields) def purchase(self, amount, options=None): if not options: options = {} tmp_options = options.copy() permissible_options = ["SenderTokenId", "CallerReference", "SenderDescription", "CallerDescription", "TransactionTimeoutInMins" "TransactionAmount", "OverrideIPNURL", "DescriptorPolicy"] tmp_options['TransactionAmount'] = amount if 'tokenID' in options: tmp_options["SenderTokenId"] = options["tokenID"] if 'callerReference' in options: tmp_options['CallerReference'] = options["callerReference"] for key in options: if key not in permissible_options: tmp_options.pop(key) resp = self.fps_connection.pay(**tmp_options) return {"status": resp.PayResult.TransactionStatus, "response": resp.PayResult} def authorize(self, amount, options=None): """ amount: the amount of money to authorize. options: Required: CallerReference SenderTokenId TransactionAmount Conditional: SenderDescription Optional: CallerDescription DescriptorPolicy OverrideIPNURL TransactionTimeoutInMins See: http://docs.aws.amazon.com/AmazonFPS/latest/FPSBasicGuide/Reserve.html for more info """ if not options: options = {} options['TransactionAmount'] = amount resp = self.fps_connection.reserve(**options) return {"status": resp.ReserveResult.TransactionStatus, "response": resp.ReserveResult} 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.SettleResult.TransactionStatus, "response": resp.SettleResult} 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.RefundResult.TransactionStatus, "response": resp.RefundResult} 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.CancelResult.TransactionStatus, "response": resp.CancelResult} 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(UrlEndPoint="%s://%s%s" % (parsed_url.scheme, parsed_url.netloc, parsed_url.path), HttpParameters=request.raw_post_data) if not resp.VerifySignatureResult.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(UrlEndPoint="%s://%s%s" % (parsed_url.scheme, parsed_url.netloc, parsed_url.path), HttpParameters=parsed_url.query) if not resp.VerifySignatureResult.VerificationStatus == "Success": return HttpResponseForbidden() return self.transaction(request) def transaction(self, request): """Has to be overridden by the subclasses""" raise NotImplementedError
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.cbui_url( returnURL=tmp_fields.pop("returnURL"), paymentReason=tmp_fields.pop("paymentReason"), pipelineName=tmp_fields.pop("pipelineName"), transactionAmount=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