diff --git a/Adyen/client.py b/Adyen/client.py index bcdb266b..e3315905 100644 --- a/Adyen/client.py +++ b/Adyen/client.py @@ -145,13 +145,14 @@ def _determine_hpp_url(platform, action): result = '/'.join([base_uri, service]) return result - def _determine_checkout_url(self, platform, action): + def _determine_checkout_url(self, platform, action, path_param=None): """This returns the Adyen API endpoint based on the provided platform, service and action. Args: platform (str): Adyen platform, ie 'live' or 'test'. action (str): the API action to perform. + path_param Optional[(str)]: a generic id that can be used to modify a payment e.g. paymentPspReference. """ api_version = settings.API_CHECKOUT_VERSION if platform == "test": @@ -171,6 +172,16 @@ def _determine_checkout_url(self, platform, action): action = "payments/details" if action == "paymentsResult": action = "payments/result" + if action == "cancels": + action = "/cancels" + if action == "paymentsCancelsWithReference": + action = f"payments/{path_param}/cancels" + if action == "paymentsCapture": + action = f"/payments/{path_param}/captures" + if action == "paymentsReversals": + action = f"payments/{path_param}/reversals" + if action == "payments/Refunds": + action = f"payments/{path_param}/refunds" if action == "originKeys": api_version = settings.API_CHECKOUT_UTILITY_VERSION if action == "paymentMethodsBalance": @@ -437,7 +448,7 @@ class instance. status_code, headers, message) return adyen_result - def call_checkout_api(self, request_data, action, idempotency_key=None, + def call_checkout_api(self, request_data, action, idempotency_key=None, path_param=None, **kwargs): """This will call the checkout adyen api. xapi key merchant_account, and platform are pulled from root module level and or self object. @@ -452,6 +463,7 @@ def call_checkout_api(self, request_data, action, idempotency_key=None, https://docs.adyen.com/developers/checkout/api-integration service (str): This is the API service to be called. action (str): The specific action of the API service to be called + path_param (str): This is used to pass the id or referenceID to the API sercie """ if not self.http_init: self.http_client = HTTPClient(self.USER_AGENT_SUFFIX, @@ -520,7 +532,7 @@ def call_checkout_api(self, request_data, action, idempotency_key=None, headers = {} if idempotency_key: headers[self.IDEMPOTENCY_HEADER_NAME] = idempotency_key - url = self._determine_checkout_url(platform, action) + url = self._determine_checkout_url(platform, action, path_param) raw_response, raw_request, status_code, headers = \ self.http_client.request(url, json=request_data, diff --git a/Adyen/services.py b/Adyen/services.py index 3c44273a..bba80ea5 100644 --- a/Adyen/services.py +++ b/Adyen/services.py @@ -280,6 +280,13 @@ class AdyenCheckoutApi(AdyenServiceBase): payments/details originKeys + Modifications: + capture + refunds + cancels + reversals + + Please refer to the checkout documentation for specifics around the API. https://docs.adyen.com/developers/checkout @@ -322,6 +329,42 @@ def payment_result(self, request=None, **kwargs): action = "paymentsResult" return self.client.call_checkout_api(request, action, **kwargs) + def payments_captures(self, request, idempotency_key=None, path_param=None, **kwargs): + if path_param == "": + raise ValueError( + 'must contain a pspReference in the path_param, path_param cannot be empty' + ) + action = "paymentsCapture" + return self.client.call_checkout_api(request, action, idempotency_key, path_param, **kwargs) + + def payments_cancels_without_reference(self, request, idempotency_key=None, **kwargs): + action = "cancels" + return self.client.call_checkout_api(request, action, idempotency_key, **kwargs) + + def payments_cancels_with_reference(self, request, idempotency_key=None, path_param=None, **kwargs): + if path_param == "": + raise ValueError( + 'must contain a pspReference in the path_param, path_param cannot be empty' + ) + action = "paymentsCancelsWithReference" + return self.client.call_checkout_api(request, action, idempotency_key, path_param, **kwargs) + + def payments_reversals(self, request, idempotency_key=None, path_param=None, **kwargs): + if path_param == "": + raise ValueError( + 'must contain a pspReference in the path_param, path_param cannot be empty' + ) + action = "paymentsReversals" + return self.client.call_checkout_api(request, action, idempotency_key, path_param, **kwargs) + + def payments_refunds(self, request, idempotency_key=None, path_param=None, **kwargs): + if path_param == "": + raise ValueError( + 'must contain a pspReference in the path_param, path_param cannot be empty' + ) + action = "paymentsRefunds" + return self.client.call_checkout_api(request, action, idempotency_key, path_param, **kwargs) + def origin_keys(self, request=None, **kwargs): action = "originKeys" return self.client.call_checkout_api(request, action, **kwargs) diff --git a/test/CheckoutTest.py b/test/CheckoutTest.py index 14bec1ab..4bff0b3d 100644 --- a/test/CheckoutTest.py +++ b/test/CheckoutTest.py @@ -236,6 +236,203 @@ def test_payments_result_error_mocked(self): self.assertEqual("Invalid payload provided", result.message['message']) self.assertEqual("validation", result.message['errorType']) + def test_payments_cancels_without_reference(self): + requests = { + "paymentReference": "Payment123", + "merchantAccount": "YOUR_MERCHANT_ACCOUNT", + "reference": "YourCancelReference", + } + self.adyen.client = self.test.create_client_from_file(200, requests, + "test/mocks/" + "checkout/" + "paymentscancel-" + "withoutreference-succes.json") + results = self.adyen.checkout.payments_cancels_without_reference(request=requests) + self.assertIsNotNone(results.message['paymentReference']) + self.assertEqual("8412534564722331", results.message['pspReference']) + self.assertEqual("received", results.message['status']) + + def test_payments_cancels_without_reference_error_mocked(self): + requests = { + "paymentReference": "Payment123", + "merchantAccount": "YOUR_MERCHANT_ACCOUNT", + "reference": "YourCancelReference", + } + self.adyen.client = self.test.create_client_from_file(200, requests, + "test/mocks/" + "checkout/" + "paymentsresult" + "-error-invalid-" + "data-payload-" + "422.json") + + result = self.adyen.checkout.payments_cancels_without_reference(requests) + self.assertEqual(422, result.message['status']) + self.assertEqual("14_018", result.message['errorCode']) + self.assertEqual("Invalid payload provided", result.message['message']) + self.assertEqual("validation", result.message['errorType']) + + def test_payments_cancels_success_mocked(self): + requests = {"reference": "Your wro order number", "merchantAccount": "YOUR_MERCHANT_ACCOUNT"} + + reference_id = "8836183819713023" + self.adyen.client = self.test.create_client_from_file(200, requests, + "test/mocks/" + "checkout/" + "paymentscancels" + "-success.json") + result = self.adyen.checkout.payments_cancels_with_reference(request=requests, path_param=reference_id) + self.assertEqual(reference_id, result.message["paymentPspReference"]) + self.assertEqual("received", result.message['status']) + + def test_payments_cancels_error_mocked(self): + requests = {"reference": "Your wro order number"} + psp_reference = "8836183819713023" + self.adyen.client = self.test.create_client_from_file(200, requests, + "test/mocks/" + "checkout/" + "paymentsresult-error-invalid-" + "data-payload-422.json") + result = self.adyen.checkout.payments_cancels_with_reference(request=requests, path_param=psp_reference) + self.assertEqual(422, result.message['status']) + self.assertEqual("14_018", result.message['errorCode']) + self.assertEqual("Invalid payload provided", result.message['message']) + self.assertEqual("validation", result.message['errorType']) + + def test_payments_refunds_success_mocked(self): + requests = { + "paymentReference": "Payment123", + "merchantAccount": "YOUR_MERCHANT_ACCOUNT", + "reference": "YourCancelReference", + } + psp_reference = "Payment123" + self.adyen.client = self.test.create_client_from_file(200, requests, + "test/mocks/" + "checkout/" + "paymentscancel-" + "withoutreference-succes.json") + + result = self.adyen.checkout.payments_refunds(request=requests, path_param=psp_reference) + self.assertEqual(psp_reference, result.message["paymentReference"]) + self.assertIsNotNone(result.message["pspReference"]) + self.assertEqual("received", result.message['status']) + + def test_payments_refunds_error_mocked(self): + requests = { + "paymentReference": "Payment123", + "merchantAccount": "YOUR_MERCHANT_ACCOUNT", + "reference": "YourCancelReference", + } + reference_id = "Payment123" + self.adyen.client = self.test.create_client_from_file(200, requests, + "test/mocks/" + "checkout/" + "paymentsresult-error-invalid-" + "data-payload-422.json") + + result = self.adyen.checkout.payments_refunds(request=requests, path_param=reference_id) + self.assertEqual(422, result.message['status']) + self.assertEqual("14_018", result.message['errorCode']) + self.assertEqual("Invalid payload provided", result.message['message']) + self.assertEqual("validation", result.message['errorType']) + + def test_payments_refunds_raises_value_error(self): + requests = { + "paymentReference": "Payment123", + "merchantAccount": "YOUR_MERCHANT_ACCOUNT", + "reference": "YourCancelReference", + } + self.adyen.client = self.test.create_client_from_file(200, requests, + "test/mocks/" + "checkout/" + "paymentscancel-" + "withoutreference-succes.json") + with self.assertRaises(ValueError) as exc: + self.adyen.checkout.payments_refunds(request=requests, path_param="") + self.assertEqual(exc.exception.__class__, ValueError) + self.assertEqual(exc.exception.__str__(), 'must contain a pspReference in the path_param, path_param cannot ' + 'be empty') + + def test_reversals_success_mocked(self): + requests = { + "reference": "YourReversalReference", + "merchantAccount": "YOUR_MERCHANT_ACCOUNT" + } + psp_reference = "8836183819713023" + self.adyen.client = self.test.create_client_from_file(200, requests, + "test/mocks/" + "checkout/" + "paymentsreversals-" + "success.json") + + result = self.adyen.checkout.payments_reversals(request=requests, path_param=psp_reference) + self.assertEqual(psp_reference, result.message["paymentPspReference"]) + self.assertIsNotNone(result.message["pspReference"]) + self.assertEqual("received", result.message['status']) + + def test_payments_reversals_failure_mocked(self): + requests = { + "reference": "YourReversalReference", + "merchantAccount": "YOUR_MERCHANT_ACCOUNT" + } + psp_reference = "8836183819713023" + + self.adyen.client = self.test.create_client_from_file(200, requests, + "test/mocks/" + "checkout/" + "paymentsresult-error-invalid-" + "data-payload-422.json") + + result = self.adyen.checkout.payments_reversals(request=requests, path_param=psp_reference) + self.assertEqual(422, result.message['status']) + self.assertEqual("14_018", result.message['errorCode']) + self.assertEqual("Invalid payload provided", result.message['message']) + self.assertEqual("validation", result.message['errorType']) + + def test_payments_capture_success_mocked(self): + request = { + "merchantAccount": "YOUR_MERCHANT_ACCOUNT", + "amount": { + "value": 2500, + "currency": "EUR" + }, + "reference": "YOUR_UNIQUE_REFERENCE" + } + psp_reference = "8536214160615591" + self.adyen.client = self.test.create_client_from_file(200, request, + "test/mocks/" + "checkout/" + "paymentcapture-" + "success.json") + + result = self.adyen.checkout.payments_captures(request=request, path_param=psp_reference) + self.assertEqual(psp_reference, result.message["paymentPspReference"]) + self.assertIsNotNone(result.message["pspReference"]) + self.assertEqual("received", result.message['status']) + self.assertEqual(2500, result.message['amount']['value']) + + def test_payments_capture_error_mocked(self): + request = { + "merchantAccount": "YOUR_MERCHANT_ACCOUNT", + "amount": { + "value": 2500, + "currency": "EUR" + }, + "reference": "YOUR_UNIQUE_REFERENCE" + } + psp_reference = "8536214160615591" + self.adyen.client = self.test.create_client_from_file(200, request, + "test/mocks/" + "checkout/" + "paymentsresult-error-invalid-" + "data-payload-422.json") + + result = self.adyen.checkout.payments_captures(request=request, path_param=psp_reference) + self.assertEqual(422, result.message['status']) + self.assertEqual("14_018", result.message['errorCode']) + self.assertEqual("Invalid payload provided", result.message['message']) + self.assertEqual("validation", result.message['errorType']) + def test_orders_success(self): request = {'merchantAccount': "YourMerchantAccount"} self.adyen.client = self.test.create_client_from_file(200, request, diff --git a/test/mocks/checkout/paymentcapture-success.json b/test/mocks/checkout/paymentcapture-success.json new file mode 100644 index 00000000..00b019d2 --- /dev/null +++ b/test/mocks/checkout/paymentcapture-success.json @@ -0,0 +1,11 @@ +{ + "merchantAccount": "YOUR_MERCHANT_ACCOUNT", + "paymentPspReference": "8536214160615591", + "pspReference": "8836226171638872", + "reference": "YOUR_UNIQUE_REFERENCE", + "status": "received", + "amount": { + "currency": "EUR", + "value": 2500 + } +} \ No newline at end of file diff --git a/test/mocks/checkout/paymentscancel-withoutreference-succes.json b/test/mocks/checkout/paymentscancel-withoutreference-succes.json new file mode 100644 index 00000000..744700ce --- /dev/null +++ b/test/mocks/checkout/paymentscancel-withoutreference-succes.json @@ -0,0 +1,7 @@ +{ + "merchantAccount": "YOUR_MERCHANT_ACCOUNT", + "paymentReference": "Payment123", + "pspReference" : "8412534564722331", + "reference": "YourCancelReference", + "status" : "received" +} \ No newline at end of file diff --git a/test/mocks/checkout/paymentscancels-success.json b/test/mocks/checkout/paymentscancels-success.json new file mode 100644 index 00000000..f9883a90 --- /dev/null +++ b/test/mocks/checkout/paymentscancels-success.json @@ -0,0 +1,7 @@ +{ + "merchantAccount": "YOUR_MERCHANT_ACCOUNT", + "paymentPspReference": "8836183819713023", + "pspReference" : "8412534564722331", + "reference": "Cancel123", + "status" : "received" +} \ No newline at end of file diff --git a/test/mocks/checkout/paymentsreversals-success.json b/test/mocks/checkout/paymentsreversals-success.json new file mode 100644 index 00000000..2503aed0 --- /dev/null +++ b/test/mocks/checkout/paymentsreversals-success.json @@ -0,0 +1,7 @@ +{ + "merchantAccount": "YOUR_MERCHANT_ACCOUNT", + "paymentPspReference": "8836183819713023", + "pspReference" : "8863534564726784", + "reference": "YourReversalReference", + "status" : "received" +} \ No newline at end of file