Skip to content

Commit da8a059

Browse files
IEX-2498 add endpoint coverage for new capital, mandates, and recurring URL flows
Add serialization/deserialization tests and mock payloads for Capital Dynamic Offers and Balance Platform Direct Debit Mandates APIs, including tax form summary URL assertions. Also update recurring live URL resolution to use the paltokenization live domain format with required live endpoint prefix, and validate both test/live recurring endpoint behavior in DetermineEndpoint tests. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
1 parent ef08bef commit da8a059

11 files changed

Lines changed: 269 additions & 17 deletions

Adyen/client.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -142,41 +142,44 @@ def __init__(
142142
self.api_session_authentication_version = api_session_authentication_version
143143
self.api_capital_version = api_capital_version
144144

145+
def _require_live_endpoint_prefix(self):
146+
if self.live_endpoint_prefix is None:
147+
error_string = (
148+
"Please set your live suffix. You can set it by running "
149+
"adyen.client.live_endpoint_prefix = 'Your live suffix'"
150+
)
151+
raise AdyenEndpointInvalidFormat(error_string)
152+
return self.live_endpoint_prefix
153+
145154
def _determine_api_url(self, platform, endpoint):
146155
if platform == "test":
147156
# Replace live with test in base url is configured with live url by default
148157
return endpoint.replace("-live", "-test")
149158

150159
if "pal-" in endpoint:
151-
if self.live_endpoint_prefix is None:
152-
error_string = (
153-
"Please set your live suffix. You can set it by running "
154-
"adyen.client.live_endpoint_prefix = 'Your live suffix'"
155-
)
156-
raise AdyenEndpointInvalidFormat(error_string)
160+
live_endpoint_prefix = self._require_live_endpoint_prefix()
157161
endpoint = endpoint.replace(
158162
"https://pal-test.adyen.com/pal/servlet/",
159-
"https://" + self.live_endpoint_prefix + "-pal-live.adyenpayments.com/pal/servlet/",
163+
f"https://{live_endpoint_prefix}-pal-live.adyenpayments.com/pal/servlet/",
164+
)
165+
elif "paltokenization-" in endpoint:
166+
live_endpoint_prefix = self._require_live_endpoint_prefix()
167+
endpoint = endpoint.replace(
168+
"https://paltokenization-test.adyen.com/paltokenization/servlet/",
169+
f"https://{live_endpoint_prefix}-paltokenization-live.adyenpayments.com/paltokenization/servlet/",
160170
)
161171
elif "checkout-" in endpoint:
162-
if self.live_endpoint_prefix is None:
163-
error_string = (
164-
"Please set your live suffix. You can set it by running "
165-
"adyen.client.live_endpoint_prefix = 'Your live suffix'"
166-
)
167-
raise AdyenEndpointInvalidFormat(error_string)
172+
live_endpoint_prefix = self._require_live_endpoint_prefix()
168173

169174
if "possdk" in endpoint:
170175
endpoint = endpoint.replace(
171176
"https://checkout-test.adyen.com/",
172-
"https://" + self.live_endpoint_prefix + "-checkout-live.adyenpayments.com/",
177+
f"https://{live_endpoint_prefix}-checkout-live.adyenpayments.com/",
173178
)
174179
else:
175180
endpoint = endpoint.replace(
176181
"https://checkout-test.adyen.com/",
177-
"https://"
178-
+ self.live_endpoint_prefix
179-
+ "-checkout-live.adyenpayments.com/checkout/",
182+
f"https://{live_endpoint_prefix}-checkout-live.adyenpayments.com/checkout/",
180183
)
181184
elif "authe/api" in endpoint:
182185
endpoint = endpoint.replace("https://test.adyen.com", "https://authe-live.adyen.com")

test/BalancePlatformTest.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,108 @@ def test_update_network_token(self):
238238
json=request,
239239
xapikey="YourXapikey",
240240
)
241+
242+
def test_get_list_of_mandates(self):
243+
request = {}
244+
self.adyen.client = self.test.create_client_from_file(
245+
200, request, "test/mocks/configuration/get-mandates-success.json"
246+
)
247+
result = self.adyen.balancePlatform.direct_debit_mandates_api.get_list_of_mandates()
248+
self.assertEqual(1, len(result.message["mandates"]))
249+
self.assertEqual("MD00000000000000000000001", result.message["mandates"][0]["id"])
250+
self.adyen.client.http_client.request.assert_called_once_with(
251+
"GET",
252+
f"{self.balance_platform_url}/mandates",
253+
headers={
254+
"adyen-library-name": "adyen-python-api-library",
255+
"adyen-library-version": settings.LIB_VERSION,
256+
"User-Agent": "adyen-python-api-library/" + settings.LIB_VERSION,
257+
},
258+
json=None,
259+
xapikey="YourXapikey",
260+
)
261+
262+
def test_get_mandate_by_id(self):
263+
request = {}
264+
mandate_id = "MD00000000000000000000001"
265+
self.adyen.client = self.test.create_client_from_file(
266+
200, request, "test/mocks/configuration/get-mandate-success.json"
267+
)
268+
result = self.adyen.balancePlatform.direct_debit_mandates_api.get_mandate_by_id(mandate_id)
269+
self.assertEqual(mandate_id, result.message["id"])
270+
self.adyen.client.http_client.request.assert_called_once_with(
271+
"GET",
272+
f"{self.balance_platform_url}/mandates/{mandate_id}",
273+
headers={
274+
"adyen-library-name": "adyen-python-api-library",
275+
"adyen-library-version": settings.LIB_VERSION,
276+
"User-Agent": "adyen-python-api-library/" + settings.LIB_VERSION,
277+
},
278+
json=None,
279+
xapikey="YourXapikey",
280+
)
281+
282+
def test_cancel_mandate(self):
283+
mandate_id = "MD00000000000000000000001"
284+
self.adyen.client = self.test.create_client_from_file(202, None)
285+
result = self.adyen.balancePlatform.direct_debit_mandates_api.cancel_mandate(mandate_id)
286+
287+
self.assertEqual(202, result.status_code)
288+
self.assertEqual({}, result.message)
289+
self.assertEqual("", result.raw_response)
290+
self.adyen.client.http_client.request.assert_called_once_with(
291+
"POST",
292+
f"{self.balance_platform_url}/mandates/{mandate_id}/cancel",
293+
headers={
294+
"adyen-library-name": "adyen-python-api-library",
295+
"adyen-library-version": settings.LIB_VERSION,
296+
"User-Agent": "adyen-python-api-library/" + settings.LIB_VERSION,
297+
},
298+
json=None,
299+
xapikey="YourXapikey",
300+
)
301+
302+
def test_update_mandate(self):
303+
request = {"status": "active"}
304+
mandate_id = "MD00000000000000000000001"
305+
self.adyen.client = self.test.create_client_from_file(
306+
200, request, "test/mocks/configuration/update-mandate-success.json"
307+
)
308+
result = self.adyen.balancePlatform.direct_debit_mandates_api.update_mandate(
309+
request, mandate_id
310+
)
311+
self.assertEqual(mandate_id, result.message["id"])
312+
self.assertEqual("active", result.message["status"])
313+
self.adyen.client.http_client.request.assert_called_once_with(
314+
"PATCH",
315+
f"{self.balance_platform_url}/mandates/{mandate_id}",
316+
headers={
317+
"adyen-library-name": "adyen-python-api-library",
318+
"adyen-library-version": settings.LIB_VERSION,
319+
"User-Agent": "adyen-python-api-library/" + settings.LIB_VERSION,
320+
},
321+
json=request,
322+
xapikey="YourXapikey",
323+
)
324+
325+
def test_get_tax_form_summary(self):
326+
request = {}
327+
account_holder_id = "AH00000000000000000000001"
328+
self.adyen.client = self.test.create_client_from_file(
329+
200, request, "test/mocks/configuration/get-tax-form-summary-success.json"
330+
)
331+
result = self.adyen.balancePlatform.account_holders_api.get_tax_form_summary(
332+
account_holder_id
333+
)
334+
self.assertEqual("available", result.message["taxForms"][0]["status"])
335+
self.adyen.client.http_client.request.assert_called_once_with(
336+
"GET",
337+
f"{self.balance_platform_url}/accountHolders/{account_holder_id}/taxFormSummary",
338+
headers={
339+
"adyen-library-name": "adyen-python-api-library",
340+
"adyen-library-version": settings.LIB_VERSION,
341+
"User-Agent": "adyen-python-api-library/" + settings.LIB_VERSION,
342+
},
343+
json=None,
344+
xapikey="YourXapikey",
345+
)

test/CapitalTest.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,36 @@ def test_get_grant_offer(self):
103103
)
104104
result = self.adyen.capital.grant_offers_api.get_grant_offer(id="GO00000000000000000000001")
105105
self.assertEqual("GO00000000000000000000001", result.message["id"])
106+
107+
def test_get_all_dynamic_offers(self):
108+
request = {}
109+
self.adyen.client = self.test.create_client_from_file(
110+
200, request, "test/mocks/capital/get-dynamic-offers-success.json"
111+
)
112+
result = self.adyen.capital.dynamic_offers_api.get_all_dynamic_offers()
113+
self.assertEqual(1, len(result.message["dynamicOffers"]))
114+
self.assertEqual("DO00000000000000000000001", result.message["dynamicOffers"][0]["id"])
115+
116+
def test_calculate_preliminary_offer_from_dynamic_offer(self):
117+
request = {"amount": {"currency": "EUR", "value": 10000}}
118+
self.adyen.client = self.test.create_client_from_file(
119+
200, request, "test/mocks/capital/calculate-dynamic-offer-success.json"
120+
)
121+
result = (
122+
self.adyen.capital.dynamic_offers_api.calculate_preliminary_offer_from_dynamic_offer(
123+
request, id="DO00000000000000000000001"
124+
)
125+
)
126+
self.assertEqual("DO00000000000000000000001", result.message["id"])
127+
self.assertEqual(1000, result.message["repayment"]["basisPoints"])
128+
129+
def test_create_static_offer_from_dynamic_offer(self):
130+
request = {"amount": {"currency": "EUR", "value": 10000}}
131+
self.adyen.client = self.test.create_client_from_file(
132+
200, request, "test/mocks/capital/create-static-offer-from-dynamic-success.json"
133+
)
134+
result = self.adyen.capital.dynamic_offers_api.create_static_offer_from_dynamic_offer(
135+
request, id="DO00000000000000000000001"
136+
)
137+
self.assertEqual("GO00000000000000000000002", result.message["id"])
138+
self.assertEqual("cashAdvance", result.message["contractType"])

test/DetermineEndpointTest.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import Adyen
44
from Adyen.services.posMobile import AdyenPosMobileApi
55

6+
RECURRING_DETAILS = "/listRecurringDetails"
7+
68
try:
79
from BaseTest import BaseTest
810
except ImportError:
@@ -25,6 +27,8 @@ class TestDetermineUrl(unittest.TestCase):
2527
management_url = adyen.management.account_merchant_level_api.baseUrl
2628
sessionauth_url = adyen.sessionAuthentication.session_authentication_api.baseUrl
2729
sessionauth_version = sessionauth_url.split("/")[-1]
30+
recurring_url = adyen.recurring.recurring_api.baseUrl
31+
recurring_version = recurring_url.split("/")[-1]
2832
capital_url = adyen.capital.grants_api.baseUrl
2933
capital_version = capital_url.split("/")[-1]
3034

@@ -154,3 +158,38 @@ def test_live_capital_api_url(self):
154158
self.assertEqual(
155159
url, f"https://balanceplatform-api-live.adyen.com/capital/{self.capital_version}"
156160
)
161+
162+
def test_recurring_api_base_url(self):
163+
self.assertTrue(
164+
self.recurring_url.startswith(
165+
"https://paltokenization-test.adyen.com/paltokenization/servlet/Recurring/"
166+
)
167+
)
168+
169+
def test_recurring_api_url_test_platform(self):
170+
self.client.live_endpoint_prefix = None
171+
url = self.adyen.client._determine_api_url(
172+
"test", self.recurring_url + RECURRING_DETAILS
173+
)
174+
self.assertEqual(url, f"{self.recurring_url}{RECURRING_DETAILS}")
175+
176+
def test_recurring_api_url_live_with_prefix(self):
177+
self.client.live_endpoint_prefix = "1797a841fbb37ca7-AdyenDemo"
178+
url = self.adyen.client._determine_api_url(
179+
"live", self.recurring_url + RECURRING_DETAILS
180+
)
181+
self.assertEqual(
182+
url,
183+
"https://1797a841fbb37ca7-AdyenDemo-paltokenization-live.adyenpayments.com"
184+
f"/paltokenization/servlet/Recurring/{self.recurring_version}{RECURRING_DETAILS}",
185+
)
186+
187+
def test_recurring_api_url_live_no_prefix_raises(self):
188+
self.client.live_endpoint_prefix = None
189+
self.assertRaisesRegex(
190+
AdyenEndpointInvalidFormat,
191+
"Please set your live suffix",
192+
self.adyen.client._determine_api_url,
193+
"live",
194+
self.recurring_url + "RECURRING_DETAILS",
195+
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"id": "DO00000000000000000000001",
3+
"amount": {
4+
"currency": "EUR",
5+
"value": 10000
6+
},
7+
"fee": {
8+
"amount": {
9+
"currency": "EUR",
10+
"value": 1000
11+
}
12+
},
13+
"repayment": {
14+
"basisPoints": 1000
15+
}
16+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"id": "GO00000000000000000000002",
3+
"accountHolderId": "AH00000000000000000000001",
4+
"contractType": "cashAdvance",
5+
"amount": {
6+
"currency": "EUR",
7+
"value": 10000
8+
}
9+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"dynamicOffers": [
3+
{
4+
"id": "DO00000000000000000000001",
5+
"accountHolderId": "AH00000000000000000000001",
6+
"financing": {
7+
"minimum": {
8+
"currency": "EUR",
9+
"value": 5000
10+
},
11+
"maximum": {
12+
"currency": "EUR",
13+
"value": 25000
14+
}
15+
}
16+
}
17+
]
18+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"id": "MD00000000000000000000001",
3+
"status": "active",
4+
"accountHolderId": "AH00000000000000000000001",
5+
"balanceAccountId": "BA00000000000000000000001",
6+
"paymentInstrumentId": "PI00000000000000000000001"
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"mandates": [
3+
{
4+
"id": "MD00000000000000000000001",
5+
"status": "active"
6+
}
7+
],
8+
"itemsTotal": 1
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"taxForms": [
3+
{
4+
"year": 2025,
5+
"status": "available"
6+
}
7+
]
8+
}

0 commit comments

Comments
 (0)