From 4b0dd4107b77064e89bf05b2b7d2d6e46ef504fa Mon Sep 17 00:00:00 2001 From: ilugobayo Date: Thu, 26 Aug 2021 19:12:35 -0600 Subject: [PATCH 1/9] Removes the option to define an API version Removes the option to define an API version, now it is hardcoded, setting it as v2 --- bayonet/bayonet.py | 13 +------------ bayonet/bayonet_base.py | 3 +-- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/bayonet/bayonet.py b/bayonet/bayonet.py index d29f364..39da36e 100644 --- a/bayonet/bayonet.py +++ b/bayonet/bayonet.py @@ -13,7 +13,6 @@ class _BayonetTransport(object): Responsible for implementing the wire protocol for making requests to the Bayonet API. """ - _SUPPORTED_API_VERSIONS = ['1'] _DEFAULT_DOMAIN = '.bayonet.io' _HOST_API = 'api' @@ -25,7 +24,6 @@ class _BayonetTransport(object): def __init__(self, api_key, - api_version, user_agent=None, headers=None): """ @@ -40,15 +38,6 @@ def __init__(self, 'Expected dict, got %r' % headers self.api_key = api_key - - if not api_version: - raise InvalidClientSetupError("Please specify Api version") - elif api_version not in BayonetClient._SUPPORTED_API_VERSIONS: - raise InvalidClientSetupError( - "This library does not support version specified. Consider updating your dependencies") - else: - self.api_version = api_version - self._headers = headers base_user_agent = 'OfficialBayonetPythonSDK' @@ -67,7 +56,7 @@ def __init__(self, self._fingerprinting_api_hostname = os.environ.get('BAYONET_FINGERPRINTING_API_HOST', 'fingerprinting' + self._domain) - self._api_version_namespace = "v" + api_version + self._api_version_namespace = "v2" def fully_qualified_api_hostname(self): return 'https://{api_host_name}/{api_version_namespace}'.format( diff --git a/bayonet/bayonet_base.py b/bayonet/bayonet_base.py index 8cb4ee0..cfa2248 100644 --- a/bayonet/bayonet_base.py +++ b/bayonet/bayonet_base.py @@ -5,9 +5,8 @@ class BayonetBase(object): __metaclass__ = ABCMeta - def __init__(self, api_key, version): + def __init__(self, api_key): self.api_key = api_key - self.version = version @abstractmethod def request(self, route, arg): From 32df0693cb0db29b28f4808a8f85269758866e91 Mon Sep 17 00:00:00 2001 From: ilugobayo Date: Fri, 27 Aug 2021 07:41:31 -0600 Subject: [PATCH 2/9] Removes fingerprint code Removes everything regarding the fingerprint feature from the library code --- bayonet/bayonet.py | 13 ------------- bayonet/bayonet_base.py | 4 ---- bayonet/response.py | 4 ---- 3 files changed, 21 deletions(-) diff --git a/bayonet/bayonet.py b/bayonet/bayonet.py index 39da36e..065a3a5 100644 --- a/bayonet/bayonet.py +++ b/bayonet/bayonet.py @@ -16,7 +16,6 @@ class _BayonetTransport(object): _DEFAULT_DOMAIN = '.bayonet.io' _HOST_API = 'api' - _HOST_FINGERPRINTING = 'fingerprinting' # RPC style means that the argument and result of a route are contained in # the HTTP body. @@ -53,9 +52,6 @@ def __init__(self, BayonetClient._DEFAULT_DOMAIN) self._api_hostname = os.environ.get('BAYONET_API_HOST', 'api' + self._domain) - self._fingerprinting_api_hostname = os.environ.get('BAYONET_FINGERPRINTING_API_HOST', - 'fingerprinting' + self._domain) - self._api_version_namespace = "v2" def fully_qualified_api_hostname(self): @@ -64,12 +60,6 @@ def fully_qualified_api_hostname(self): api_version_namespace=self._api_version_namespace ) - def fully_qualified_fingerprinting_api_hostname(self): - return 'https://{fingerprinting_api_hostname}/{api_version_namespace}'.format( - fingerprinting_api_hostname=self._fingerprinting_api_hostname, - api_version_namespace=self._api_version_namespace - ) - def request(self, route, json_params): """ Makes a request to the Bayonet API and in the process validates @@ -97,11 +87,8 @@ def request_json_string(self, """ # Fully qualified hostname fq_hostname = self.fully_qualified_api_hostname() - if route == "/get-fingerprint-data": - fq_hostname = self.fully_qualified_fingerprinting_api_hostname() url = "{}{}".format(fq_hostname, route) - headers = {'User-Agent': self._user_agent, 'Content-Type': 'application/json'} if self._headers: diff --git a/bayonet/bayonet_base.py b/bayonet/bayonet_base.py index cfa2248..37ea5ac 100644 --- a/bayonet/bayonet_base.py +++ b/bayonet/bayonet_base.py @@ -24,10 +24,6 @@ def feedback_historical(self, params): serialized = self.json_from_params(params) return self.request('/feedback-historical', serialized) - def get_fingerprint_data(self, params): - serialized = self.json_from_params(params) - return self.request('/get-fingerprint-data', serialized) - def json_from_params(self, params): # Add api_key to params params['api_key'] = self.api_key diff --git a/bayonet/response.py b/bayonet/response.py index 21bf867..6e1c709 100644 --- a/bayonet/response.py +++ b/bayonet/response.py @@ -32,8 +32,4 @@ def __init__(self, response): self.request_body = parsed_response['request_body'] else: self.request_body = None - if 'bayonet_fingerprint' in parsed_response: - self.bayonet_fingerprint = parsed_response['bayonet_fingerprint'] - else: - self.bayonet_fingerprint = None self.raw = parsed_response From 6e2fe7ee6d61a742ea39318157797c0e41cd4f5c Mon Sep 17 00:00:00 2001 From: ilugobayo Date: Fri, 27 Aug 2021 07:45:58 -0600 Subject: [PATCH 3/9] Updates and adds API endpoints Updates the existing API endpoints, and adds the endpoints for the blocklist and whitelist --- bayonet/bayonet_base.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/bayonet/bayonet_base.py b/bayonet/bayonet_base.py index 37ea5ac..1ea24db 100644 --- a/bayonet/bayonet_base.py +++ b/bayonet/bayonet_base.py @@ -14,15 +14,31 @@ def request(self, route, arg): def consulting(self, params): serialized = self.json_from_params(params) - return self.request('/consulting', serialized) + return self.request('/sigma/consult', serialized) - def feedback(self, params): + def update_transaction(self, params): serialized = self.json_from_params(params) - return self.request('/feedback', serialized) + return self.request('/sigma/update-transaction', serialized) def feedback_historical(self, params): serialized = self.json_from_params(params) - return self.request('/feedback-historical', serialized) + return self.request('/sigma/feedback-historical', serialized) + + def blocklist_add(self, params): + serialized = self.json_from_params(params) + return self.request('/sigma/labels/block/add', serialized) + + def blocklist_remove(self, params): + serialized = self.json_from_params(params) + return self.request('/sigma/labels/block/remove', serialized) + + def whitelist_add(self, params): + serialized = self.json_from_params(params) + return self.request('/sigma/labels/whitelist/add', serialized) + + def whitelist_add(self, params): + serialized = self.json_from_params(params) + return self.request('/sigma/labels/whitelist/remove', serialized) def json_from_params(self, params): # Add api_key to params From c1b606e155fbe5fd65d401f37f5ddea04c87a444 Mon Sep 17 00:00:00 2001 From: ilugobayo Date: Fri, 27 Aug 2021 07:50:24 -0600 Subject: [PATCH 4/9] Updates feedback_api_trans_code to bayonet_tracking_id Updates the name of the field feedbak_api_trans_code to bayonet_tracking_id in order to match with the current version of the API --- bayonet/response.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bayonet/response.py b/bayonet/response.py index 6e1c709..19f1654 100644 --- a/bayonet/response.py +++ b/bayonet/response.py @@ -4,8 +4,8 @@ class BayonetResponse(object): def __init__(self, response): parsed_response = json.loads(response.content) - if 'feedback_api_trans_code' in parsed_response: - self.feedback_api_trans_code = parsed_response['feedback_api_trans_code'] + if 'bayonet_tracking_id' in parsed_response: + self.bayonet_tracking_id = parsed_response['bayonet_tracking_id'] else: self.feedback_api_trans_code = None if 'rules_triggered' in parsed_response: From b74e4112698a214327792d9284ad1c2886136805 Mon Sep 17 00:00:00 2001 From: ilugobayo Date: Fri, 27 Aug 2021 07:56:18 -0600 Subject: [PATCH 5/9] Updates request bodies for tests Updates the request bodies used for the tests, this in order to match with the required structure in the current version of the API. Also updates how the API key is added to the request body. --- bayonet/bayonet_base.py | 2 +- test/test_bayonet.py | 129 +++++++++++++++++++++++----------------- 2 files changed, 74 insertions(+), 57 deletions(-) diff --git a/bayonet/bayonet_base.py b/bayonet/bayonet_base.py index 1ea24db..691257e 100644 --- a/bayonet/bayonet_base.py +++ b/bayonet/bayonet_base.py @@ -42,5 +42,5 @@ def whitelist_add(self, params): def json_from_params(self, params): # Add api_key to params - params['api_key'] = self.api_key + params['auth'] = {'api_key': self.api_key} return json.dumps(params) diff --git a/test/test_bayonet.py b/test/test_bayonet.py index 77c2c24..f79d526 100644 --- a/test/test_bayonet.py +++ b/test/test_bayonet.py @@ -12,88 +12,105 @@ file=sys.stderr) sys.exit(1) -api_version = os.environ.get('API_VERSION') -if api_version is None: - print('Set API_VERSION environment variable to a valid token.', - file=sys.stderr) - sys.exit(1) - invalid_api_key = "da2da838-6311-4646-805f-2466954b1a11" params_consulting = { - "channel": "ecommerce", - "cardholder_name": "test_python_cardholder_name", - "product_name": "test_python_product_name", - "consumer_name": "test_python_consumer_name", - "transaction_time": "1476813671", - "payment_method": "card", - "transaction_amount": "500.00", + "email": "test_python@bayonet.io", + "consumer_name": "test python consumer name", + "consumer_internal_id": "test_python_1", + "cardholder_name": "test python cardholder name", + "telephone": "9999999999", "card_number": "4111111111111111", + "transaction_amount": 500.00, "currency_code": "MXN", - "coupon": "test_python_coupon", - "telephone": "9999999999", - "expedited_shipping": False, - "email": "test_python@bayonet.io", - "payment_gateway": "stripe", - "device_fingerprint": "test_python_d_f", "shipping_address": { - "address_line_1": "test_python_line_1", - "address_line_2": "test_python_line_2", + "line_1": "test_python_line_1", + "line_2": "test_python_line_2", "city": "Mexico DF", "state": "Mexico DF", "country": "MEX", "zip_code": "111111" - } -} - -params_feedback = { - "transaction_status": "success", - "transaction_id": "test_python", - "feedback_api_trans_code": "xxx" + }, + "billing_address": { + "line_1": "test_python_line_1", + "line_2": "test_python_line_2", + "city": "Mexico DF", + "state": "Mexico DF", + "country": "MEX", + "zip_code": "111111" + }, + "payment_method": "card", + "transaction_time": 1476813671, + "order_id": "test_python_123", + "payment_gateway": "stripe", + "channel": "ecommerce", + "coupon": "test_python_coupon", + "expedited_shipping": False, + "products": [ + { + "product_id": "1", + "product_name": "product_1", + "product_price": 500.00, + "product_category": "test" + } + ] } -params_chargeback_feedback = { - "type": "chargeback", - "chargeback_time": "1425518410", - "chargeback_reason": "fraud", - "transaction_id": "test_python" +params_update_transaction = { + "transaction_status": "bank_decline", + "bayonet_tracking_id": "test_python_123" } - params_feedback_historical = { - "channel": "ecommerce", - "type": "transaction", - "cardholder_name": "test_python_cardholder_name", - "product_name": "test_python_product_name", - "consumer_name": "test_python_consumer_name", - "transaction_time": "1476813671", - "transaction_id": "test_python_f_h", - "transaction_status": "success", - "payment_method": "card", - "transaction_amount": "500.00", + "email": "test_python@bayonet.io", + "consumer_name": "test python consumer name", + "consumer_internal_id": "test_python_1", + "cardholder_name": "test python cardholder name", + "telephone": "9999999999", "card_number": "4111111111111111", + "transaction_amount": 500.00, "currency_code": "MXN", - "coupon": "test_python_coupon", - "telephone": "9999999999", - "expedited_shipping": False, - "email": "test_python@bayonet.io", - "payment_gateway": "stripe", - "device_fingerprint": "test_python_df", "shipping_address": { - "address_line_1": "test_python_line_1", - "address_line_2": "test_python_line_2", + "line_1": "test_python_line_1", + "line_2": "test_python_line_2", "city": "Mexico DF", "state": "Mexico DF", "country": "MEX", "zip_code": "111111" - } + }, + "billing_address": { + "line_1": "test_python_line_1", + "line_2": "test_python_line_2", + "city": "Mexico DF", + "state": "Mexico DF", + "country": "MEX", + "zip_code": "111111" + }, + "payment_method": "card", + "transaction_time": 1476813671, + "order_id": "test_python_123", + "payment_gateway": "stripe", + "channel": "ecommerce", + "coupon": "test_python_coupon", + "expedited_shipping": False, + "products": [ + { + "product_id": "1", + "product_name": "product_1", + "product_price": 500.00, + "product_category": "test" + } + ], + "transaction_status": "success" } -params_get_fingerprint_data = { - "bayonet_fingerprint_token": "xxx" +params_blocklist_invalid = { + "email": "arandommailtotestxxx@xxx.com" } -feedback_api_trans_code = "" +params_blocklist_valid = { + "email": "test_python@bayonet.io" +} class TestBayonet(unittest.TestCase): From 90dc344b9c92695ab542c62295ccf8b9ceb43a4a Mon Sep 17 00:00:00 2001 From: ilugobayo Date: Fri, 27 Aug 2021 08:12:03 -0600 Subject: [PATCH 6/9] Updates existing tests Updates the existing tests to match with the changes made to use the version v2 of the API --- test/test_bayonet.py | 81 ++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 51 deletions(-) diff --git a/test/test_bayonet.py b/test/test_bayonet.py index f79d526..870f6ab 100644 --- a/test/test_bayonet.py +++ b/test/test_bayonet.py @@ -115,11 +115,7 @@ class TestBayonet(unittest.TestCase): def setUp(self): - self.client = bayonet.BayonetClient(api_key, api_version) - - def test_bad_client_setup(self): - with self.assertRaises(bayonet.InvalidClientSetupError): - bayonet.BayonetClient(api_key, '2.0') + self.client = bayonet.BayonetClient(api_key) def test_default_client_user_agent(self): self.assertIsNone(self.client._raw_user_agent) @@ -130,11 +126,11 @@ def test_default_api_hostname(self): self.assertEqual(self.client._api_hostname, 'api.bayonet.io') def test_client_api_version_namespace(self): - self.assertEqual(self.client._api_version_namespace, 'v1') + self.assertEqual(self.client._api_version_namespace, 'v2') def test_fully_qualified_api_host_name(self): self.assertEqual(self.client.fully_qualified_api_hostname(), - 'https://api.bayonet.io/v1') + 'https://api.bayonet.io/v2') def test_client_must_respond_to__request(self): assert type(self.client.request) is MethodType @@ -142,8 +138,8 @@ def test_client_must_respond_to__request(self): class TestConsult(unittest.TestCase): def setUp(self): - self.client = bayonet.BayonetClient(api_key, api_version) - self.invalid_client = bayonet.BayonetClient(invalid_api_key, api_version) + self.client = bayonet.BayonetClient(api_key) + self.invalid_client = bayonet.BayonetClient(invalid_api_key) def test_should_return_error_on_invalid_api_key(self): with self.assertRaises(bayonet.BayonetError): @@ -153,79 +149,62 @@ def test_should_validate_api_key(self): try: self.invalid_client.consulting(params_consulting) except bayonet.BayonetError as e: - self.assertEqual(e.reason_code, "11") + self.assertEqual(e.reason_code, 12) def test_should_return_success(self): r = self.client.consulting(params_consulting) - global feedback_api_trans_code - feedback_api_trans_code = r.feedback_api_trans_code - self.assertEqual(r.reason_code, "00") + global bayonet_tracking_id + bayonet_tracking_id = r.bayonet_tracking_id + self.assertEqual(r.reason_code, 0) - def test_should_return_feedback_api_trans_code(self): + def test_should_return_bayonet_tracking_id(self): r = self.client.consulting(params_consulting) - assert r.feedback_api_trans_code is not None + assert r.bayonet_tracking_id is not None -class TestFeedback(unittest.TestCase): +class TestUpdateTransaction(unittest.TestCase): def setUp(self): - self.client = bayonet.BayonetClient(api_key, api_version) - self.invalid_client = bayonet.BayonetClient(invalid_api_key, api_version) + self.client = bayonet.BayonetClient(api_key) + self.invalid_client = bayonet.BayonetClient(invalid_api_key) def test_should_validate_api_key(self): try: - self.invalid_client.feedback(params_feedback) + self.invalid_client.update_transaction(params_update_transaction) except bayonet.BayonetError as e: - self.assertEqual(e.reason_code, "11") + self.assertEqual(e.reason_code, 12) - def test_should_return_error_on_invalid_api_trans_code(self): - params_feedback['feedback_api_trans_code'] = 'xxx' + def test_should_return_error_on_invalid_bayonet_tracking_code(self): + params_update_transaction['bayonet_tracking_id'] = 'xxx' with self.assertRaises(bayonet.BayonetError): - self.client.feedback(params_feedback) + self.client.update_transaction(params_update_transaction) - def test_should_validate_api_trans_code(self): - params_feedback['feedback_api_trans_code'] = 'xxx' + def test_should_validate_bayonet_tracking_id(self): + params_update_transaction['bayonet_tracking_id'] = 'xxx' try: - self.client.feedback(params_feedback) + self.client.update_transaction(params_update_transaction) except bayonet.BayonetError as e: - self.assertEqual(e.reason_code, "87") + self.assertEqual(e.reason_code, 162) def test_should_return_success(self): - params_feedback['feedback_api_trans_code'] = feedback_api_trans_code - r = self.client.feedback(params_feedback) - self.assertEqual(r.reason_code, "00") + params_update_transaction['bayonet_tracking_id'] = bayonet_tracking_id + r = self.client.update_transaction(params_update_transaction) + self.assertEqual(r.reason_code, 0) class TestFeedbackHistorical(unittest.TestCase): def setUp(self): - self.client = bayonet.BayonetClient(api_key, api_version) - self.invalid_client = bayonet.BayonetClient(invalid_api_key, api_version) + self.client = bayonet.BayonetClient(api_key) + self.invalid_client = bayonet.BayonetClient(invalid_api_key) def test_should_validate_api_key(self): try: self.invalid_client.feedback_historical(params_feedback_historical) except bayonet.BayonetError as e: - self.assertEqual(e.reason_code, "11") - - def test_should_return_success_on_chargeback_feedback(self): - r = self.client.feedback_historical(params_chargeback_feedback) - self.assertEqual(r.reason_code, "00") + self.assertEqual(e.reason_code, 12) def test_should_return_success(self): r = self.client.feedback_historical(params_feedback_historical) - self.assertEqual(r.reason_code, "00") - - -class TestGetFingerprintData(unittest.TestCase): - def setUp(self): - self.client = bayonet.BayonetClient(api_key, api_version) - self.invalid_client = bayonet.BayonetClient(invalid_api_key, api_version) - - def test_should_validate_fingerprint_token(self): - try: - self.invalid_client.get_fingerprint_data(params_get_fingerprint_data) - except bayonet.BayonetError as e: - self.assertEqual(e.status, "Error: Invalid value for bayonet_fingerprint_token") - + self.assertEqual(r.reason_code, 0) if __name__ == "__main__": unittest.main() From 14bd561d297f389e6fd5e8bd5c72673cc0d92e35 Mon Sep 17 00:00:00 2001 From: ilugobayo Date: Fri, 27 Aug 2021 08:19:00 -0600 Subject: [PATCH 7/9] Adds tests for the lists Adds test for the blocklist and whitelists, only the blocklist endpoint is used, since the function of both lists is the same in terms of restrictions and limitations --- test/test_bayonet.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/test_bayonet.py b/test/test_bayonet.py index 870f6ab..56fcc4e 100644 --- a/test/test_bayonet.py +++ b/test/test_bayonet.py @@ -206,5 +206,28 @@ def test_should_return_success(self): r = self.client.feedback_historical(params_feedback_historical) self.assertEqual(r.reason_code, 0) +class TestLists(unittest.TestCase): + def setUp(self): + self.client = bayonet.BayonetClient(api_key) + self.invalid_client = bayonet.BayonetClient(invalid_api_key) + + def test_should_validate_api_key(self): + try: + self.invalid_client.blocklist_add(params_blocklist_valid) + except bayonet.BayonetError as e: + self.assertEqual(e.reason_code, 12) + + def test_should_validate_email_in_system(self): + try: + self.client.blocklist_add(params_blocklist_valid) + except bayonet.BayonetError as e: + self.assertEqual(e.reason_code, 152) + + def test_should_accept_email_when_adding_to_list(self): + try: + self.client.blocklist_add(params_blocklist_valid) + except: + self.assertEqual(e.reason_code, 0) + if __name__ == "__main__": unittest.main() From 96018c74ae4ebe994a8c4ae74b8f0cab8f5acc16 Mon Sep 17 00:00:00 2001 From: ilugobayo Date: Fri, 27 Aug 2021 09:01:48 -0600 Subject: [PATCH 8/9] Adds line to close Session in request Adds a line to close the Session object after being used for the request to the API, this to get rid of a warning when running tests in Python 3.x (ResourceWarning: unclosed Date: Mon, 30 Aug 2021 09:13:22 -0600 Subject: [PATCH 9/9] Updates version --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1b59493..7ac1f41 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ import warnings from setuptools import setup -version = "1.1.0" +version = "2.0.0" path, script = os.path.split(sys.argv[0]) os.chdir(os.path.abspath(path)) @@ -41,6 +41,8 @@ "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries :: Python Modules", ])