Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions Adyen/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand All @@ -171,6 +172,14 @@ def _determine_checkout_url(self, platform, action):
action = "payments/details"
if action == "paymentsResult":
action = "payments/result"
if action == "paymentsCancelsWithoutReference":
action = "payments/cancels"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you referring to this endpoint? https://docs.adyen.com/api-explorer/#/CheckoutService/v67/post/cancels
it should be just /cancels? without the payments prefix. Maybe we can call the action just cancels if this is the case.

if action == "paymentsCancelsWithReference":
action = f"payments/{path_param}/cancels"
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":
Expand Down Expand Up @@ -437,7 +446,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.
Expand All @@ -452,6 +461,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 service
"""
if not self.http_init:
self.http_client = HTTPClient(self.USER_AGENT_SUFFIX,
Expand Down Expand Up @@ -520,7 +530,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,
Expand Down
43 changes: 43 additions & 0 deletions Adyen/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -322,6 +329,42 @@ def payment_result(self, request=None, **kwargs):
action = "paymentsResult"
return self.client.call_checkout_api(request, action, **kwargs)

def payment_captures(self, path_param, request=None, idempotency_key=None, **kwargs):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def payment_captures(self, path_param, request=None, idempotency_key=None, **kwargs):
def payments_captures(self, path_param, request=None, idempotency_key=None, **kwargs):

if path_param == "":
raise ValueError(
'must contain a pspReference in the path_param, path_param cannot be empty'
)
action = "paymentsCapture"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please add this action in the client.py code? I think it's not there yet

return self.client.call_checkout_api(request, action, path_param, idempotency_key, **kwargs)

def payments_cancels_without_reference(self, request=None, idempotency_key=None, **kwargs):
action = "paymentsCancelsWithoutReference"
return self.client.call_checkout_api(request, action, idempotency_key, **kwargs)

def payments_cancels_with_reference(self, path_param, request=None, idempotency_key=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, path_param, idempotency_key, **kwargs)

def payments_reversals(self, path_param, request=None, idempotency_key=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, path_param, idempotency_key, **kwargs)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not work: call_checkout_api expects idempotency_key and path_param in a different order.

Same comment probably also applies to all other uses of call_checkout_api in this PR.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see :) Thanks for the comment!


def payments_refunds(self, path_param, request=None, idempotency_key=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, path_param, idempotency_key, **kwargs)

def origin_keys(self, request=None, **kwargs):
action = "originKeys"
return self.client.call_checkout_api(request, action, **kwargs)
Expand Down
195 changes: 195 additions & 0 deletions test/CheckoutTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,201 @@ 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_cancels_without_reference(request=requests, path_param=psp_reference)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
result = self.adyen.checkout.payments_cancels_without_reference(request=requests, path_param=psp_reference)
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_cancels_without_reference(request=requests, path_param=reference_id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
result = self.adyen.checkout.payments_cancels_without_reference(request=requests, path_param=reference_id)
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_vaulue_error(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def test_payments_refunds_raises_vaulue_error(self):
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_cancels_with_reference(request=requests, path_param="")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.adyen.checkout.payments_cancels_with_reference(request=requests, path_param="")
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.payment_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.payment_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,
Expand Down
11 changes: 11 additions & 0 deletions test/mocks/checkout/paymentcapture-success.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"merchantAccount": "YOUR_MERCHANT_ACCOUNT",
"paymentPspReference": "8536214160615591",
"pspReference": "8836226171638872",
"reference": "YOUR_UNIQUE_REFERENCE",
"status": "received",
"amount": {
"currency": "EUR",
"value": 2500
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"merchantAccount": "YOUR_MERCHANT_ACCOUNT",
"paymentReference": "Payment123",
"pspReference" : "8412534564722331",
"reference": "YourCancelReference",
"status" : "received"
}
7 changes: 7 additions & 0 deletions test/mocks/checkout/paymentscancels-success.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"merchantAccount": "YOUR_MERCHANT_ACCOUNT",
"paymentPspReference": "8836183819713023",
"pspReference" : "8412534564722331",
"reference": "Cancel123",
"status" : "received"
}
7 changes: 7 additions & 0 deletions test/mocks/checkout/paymentsreversals-success.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"merchantAccount": "YOUR_MERCHANT_ACCOUNT",
"paymentPspReference": "8836183819713023",
"pspReference" : "8863534564726784",
"reference": "YourReversalReference",
"status" : "received"
}