From c624c3599b9582c1665e7c75a847193295073a20 Mon Sep 17 00:00:00 2001 From: Jeanderson Barros Candido <2225536+jeandersonbc@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:04:31 +0100 Subject: [PATCH] Introduce Capital API Service Add the generated capital-related service and exports to the client. In addition, the changes add test coverage for simple flows and assertions over endpoints. README file updated with Capital description. --- Adyen/__init__.py | 5 +- Adyen/client.py | 10 +- Adyen/services/__init__.py | 3 +- Adyen/services/capital/__init__.py | 18 +++ Adyen/services/capital/grant_accounts_api.py | 23 ++++ Adyen/services/capital/grant_offers_api.py | 31 +++++ Adyen/services/capital/grants_api.py | 63 ++++++++++ README.md | 1 + test/CapitalTest.py | 114 ++++++++++++++++++ test/DetermineEndpointTest.py | 10 ++ .../capital/get-grant-account-success.json | 21 ++++ .../get-grant-disbursement-success.json | 27 +++++ .../get-grant-disbursements-success.json | 31 +++++ .../capital/get-grant-offer-success.json | 32 +++++ test/mocks/capital/get-grant-success.json | 23 ++++ .../mocks/capital/grant-accounts-success.json | 12 ++ test/mocks/capital/grant-offers-success.json | 19 +++ test/mocks/capital/grants-success.json | 26 ++++ .../update-grant-disbursement-success.json | 27 +++++ 19 files changed, 492 insertions(+), 4 deletions(-) create mode 100644 Adyen/services/capital/__init__.py create mode 100644 Adyen/services/capital/grant_accounts_api.py create mode 100644 Adyen/services/capital/grant_offers_api.py create mode 100644 Adyen/services/capital/grants_api.py create mode 100644 test/CapitalTest.py create mode 100644 test/mocks/capital/get-grant-account-success.json create mode 100644 test/mocks/capital/get-grant-disbursement-success.json create mode 100644 test/mocks/capital/get-grant-disbursements-success.json create mode 100644 test/mocks/capital/get-grant-offer-success.json create mode 100644 test/mocks/capital/get-grant-success.json create mode 100644 test/mocks/capital/grant-accounts-success.json create mode 100644 test/mocks/capital/grant-offers-success.json create mode 100644 test/mocks/capital/grants-success.json create mode 100644 test/mocks/capital/update-grant-disbursement-success.json diff --git a/Adyen/__init__.py b/Adyen/__init__.py index 12ecab19..a9f76ca5 100644 --- a/Adyen/__init__.py +++ b/Adyen/__init__.py @@ -14,6 +14,7 @@ from .services import ( AdyenBase, AdyenPaymentsApi, + AdyenCapitalApi, AdyenBinlookupApi, AdyenRecurringApi, AdyenPayoutsApi, @@ -36,6 +37,7 @@ class Adyen(AdyenBase): def __init__(self, **kwargs): self.client = AdyenClient(**kwargs) self.payment = AdyenPaymentsApi(client=self.client) + self.capital = AdyenCapitalApi(client=self.client) self.binlookup = AdyenBinlookupApi(client=self.client) self.payout = AdyenPayoutsApi(client=self.client) self.recurring = AdyenRecurringApi(client=self.client) @@ -65,4 +67,5 @@ def __init__(self, **kwargs): storedValue = _base_adyen_obj.storedValue balancePlatform = _base_adyen_obj.balancePlatform disputes = _base_adyen_obj.disputes -sessionAuthentication = _base_adyen_obj.sessionAuthentication \ No newline at end of file +sessionAuthentication = _base_adyen_obj.sessionAuthentication +capital = _base_adyen_obj.capital diff --git a/Adyen/client.py b/Adyen/client.py index 2ad6a66f..5c8964b2 100644 --- a/Adyen/client.py +++ b/Adyen/client.py @@ -96,6 +96,7 @@ def __init__( api_balance_platform_version=None, api_disputes_version=None, api_session_authentication_version=None, + api_capital_version=None ): self.username = username @@ -131,6 +132,8 @@ def __init__( self.api_balance_platform_version = api_balance_platform_version self.api_disputes_version = api_disputes_version self.api_session_authentication_version = api_session_authentication_version + self.api_capital_version = api_capital_version + def _determine_api_url(self, platform, endpoint): if platform == "test": @@ -288,7 +291,8 @@ def _set_url_version(self, service, endpoint): "storedValue": self.api_stored_value_version, "balancePlatform": self.api_balance_platform_version, "disputes": self.api_disputes_version, - "sessionAuthentication": self.api_session_authentication_version + "sessionAuthentication": self.api_session_authentication_version, + "capital": self.api_capital_version, } new_version = f"v{version_lookup[service]}" @@ -345,7 +349,9 @@ def call_adyen_api( self.api_stored_value_version, self.api_balance_platform_version, self.api_disputes_version, - self.api_session_authentication_version] + self.api_session_authentication_version, + self.api_capital_version, + ] if any(versions): endpoint = self._set_url_version(service, endpoint) diff --git a/Adyen/services/__init__.py b/Adyen/services/__init__.py index 8db9bcf1..50b3e222 100644 --- a/Adyen/services/__init__.py +++ b/Adyen/services/__init__.py @@ -12,4 +12,5 @@ from .storedValue import AdyenStoredValueApi from .balancePlatform import AdyenBalancePlatformApi from .disputes import AdyenDisputesApi -from .sessionAuthentication import AdyenSessionAuthenticationApi \ No newline at end of file +from .sessionAuthentication import AdyenSessionAuthenticationApi +from .capital import AdyenCapitalApi \ No newline at end of file diff --git a/Adyen/services/capital/__init__.py b/Adyen/services/capital/__init__.py new file mode 100644 index 00000000..c6ecf3e8 --- /dev/null +++ b/Adyen/services/capital/__init__.py @@ -0,0 +1,18 @@ +from ..base import AdyenServiceBase +from .grant_accounts_api import GrantAccountsApi +from .grant_offers_api import GrantOffersApi +from .grants_api import GrantsApi + + +class AdyenCapitalApi(AdyenServiceBase): + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, client=None): + super(AdyenCapitalApi, self).__init__(client=client) + self.grant_accounts_api = GrantAccountsApi(client=client) + self.grant_offers_api = GrantOffersApi(client=client) + self.grants_api = GrantsApi(client=client) diff --git a/Adyen/services/capital/grant_accounts_api.py b/Adyen/services/capital/grant_accounts_api.py new file mode 100644 index 00000000..21ceb4e0 --- /dev/null +++ b/Adyen/services/capital/grant_accounts_api.py @@ -0,0 +1,23 @@ +from ..base import AdyenServiceBase + + +class GrantAccountsApi(AdyenServiceBase): + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, client=None): + super(GrantAccountsApi, self).__init__(client=client) + self.service = "capital" + self.baseUrl = "https://balanceplatform-api-test.adyen.com/capital/v1" + + def get_grant_account_information(self, id, idempotency_key=None, **kwargs): + """ + Get the information of your grant account + """ + endpoint = self.baseUrl + f"/grantAccounts/{id}" + method = "GET" + return self.client.call_adyen_api(None, self.service, method, endpoint, idempotency_key, **kwargs) + diff --git a/Adyen/services/capital/grant_offers_api.py b/Adyen/services/capital/grant_offers_api.py new file mode 100644 index 00000000..89bba82b --- /dev/null +++ b/Adyen/services/capital/grant_offers_api.py @@ -0,0 +1,31 @@ +from ..base import AdyenServiceBase + + +class GrantOffersApi(AdyenServiceBase): + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, client=None): + super(GrantOffersApi, self).__init__(client=client) + self.service = "capital" + self.baseUrl = "https://balanceplatform-api-test.adyen.com/capital/v1" + + def get_all_grant_offers(self, idempotency_key=None, **kwargs): + """ + Get all available grant offers + """ + endpoint = self.baseUrl + f"/grantOffers" + method = "GET" + return self.client.call_adyen_api(None, self.service, method, endpoint, idempotency_key, **kwargs) + + def get_grant_offer(self, id, idempotency_key=None, **kwargs): + """ + Get the details of a grant offer + """ + endpoint = self.baseUrl + f"/grantOffers/{id}" + method = "GET" + return self.client.call_adyen_api(None, self.service, method, endpoint, idempotency_key, **kwargs) + diff --git a/Adyen/services/capital/grants_api.py b/Adyen/services/capital/grants_api.py new file mode 100644 index 00000000..4c78429c --- /dev/null +++ b/Adyen/services/capital/grants_api.py @@ -0,0 +1,63 @@ +from ..base import AdyenServiceBase + + +class GrantsApi(AdyenServiceBase): + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, client=None): + super(GrantsApi, self).__init__(client=client) + self.service = "capital" + self.baseUrl = "https://balanceplatform-api-test.adyen.com/capital/v1" + + def get_all_grant_disbursements(self, grantId, idempotency_key=None, **kwargs): + """ + Get all the disbursements of a grant + """ + endpoint = self.baseUrl + f"/grants/{grantId}/disbursements" + method = "GET" + return self.client.call_adyen_api(None, self.service, method, endpoint, idempotency_key, **kwargs) + + def get_all_grants(self, idempotency_key=None, **kwargs): + """ + Get all the grants of an account holder + """ + endpoint = self.baseUrl + f"/grants" + method = "GET" + return self.client.call_adyen_api(None, self.service, method, endpoint, idempotency_key, **kwargs) + + def get_grant(self, grantId, idempotency_key=None, **kwargs): + """ + Get the details of a grant + """ + endpoint = self.baseUrl + f"/grants/{grantId}" + method = "GET" + return self.client.call_adyen_api(None, self.service, method, endpoint, idempotency_key, **kwargs) + + def get_grant_disbursement(self, grantId, disbursementId, idempotency_key=None, **kwargs): + """ + Get disbursement details + """ + endpoint = self.baseUrl + f"/grants/{grantId}/disbursements/{disbursementId}" + method = "GET" + return self.client.call_adyen_api(None, self.service, method, endpoint, idempotency_key, **kwargs) + + def request_grant(self, request, idempotency_key=None, **kwargs): + """ + Make a request for a grant + """ + endpoint = self.baseUrl + f"/grants" + method = "POST" + return self.client.call_adyen_api(request, self.service, method, endpoint, idempotency_key, **kwargs) + + def update_grant_disbursement(self, request, grantId, disbursementId, idempotency_key=None, **kwargs): + """ + Update the repayment configuration of a disbursement + """ + endpoint = self.baseUrl + f"/grants/{grantId}/disbursements/{disbursementId}" + method = "PATCH" + return self.client.call_adyen_api(request, self.service, method, endpoint, idempotency_key, **kwargs) + diff --git a/README.md b/README.md index aee78aae..39a87113 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ This is the officially supported Python library for using Adyen's APIs. | [BIN lookup API](https://docs.adyen.com/api-explorer/BinLookup/54/overview) | The BIN Lookup API provides endpoints for retrieving information based on a given BIN. | binLookup | **v54** | | [Balance Platform API](https://docs.adyen.com/api-explorer/balanceplatform/2/overview) | The Balance Platform API enables you to create a platform where you can onboard your users as account holders and create balance accounts, cards, and business accounts. | balancePlatform | **v2** | | [Checkout API](https://docs.adyen.com/api-explorer/Checkout/71/overview) | Our latest integration for accepting online payments. | checkout | **v71** | +| [Capital API](https://docs.adyen.com/api-explorer/capital/1/overview) | Provides endpoints for embedding Adyen Capital into your marketplace or platform. | capital | **v1** | | [Data Protection API](https://docs.adyen.com/development-resources/data-protection-api) | Endpoint for requesting data erasure. | dataProtection | **v1** | | [Legal Entity Management API](https://docs.adyen.com/api-explorer/legalentity/4/overview) | The Legal Entity Management API enables you to manage legal entities that contain information required for verification. | legalEntityManagement | **v4** | | [Management API](https://docs.adyen.com/api-explorer/Management/3/overview) | Configure and manage your Adyen company and merchant accounts, stores, and payment terminals. | management | **v3** | diff --git a/test/CapitalTest.py b/test/CapitalTest.py new file mode 100644 index 00000000..23851940 --- /dev/null +++ b/test/CapitalTest.py @@ -0,0 +1,114 @@ +import Adyen +import unittest +from Adyen import settings + +try: + from BaseTest import BaseTest +except ImportError: + from .BaseTest import BaseTest + + +class TestCapital(unittest.TestCase): + adyen = Adyen.Adyen() + + client = adyen.client + test = BaseTest(adyen) + client.xapikey = "YourXapikey" + client.platform = "test" + lib_version = settings.LIB_VERSION + + def test_request_grant(self): + request = {} + self.adyen.client = self.test.create_client_from_file( + 200, + request, + "test/mocks/capital/grants-success.json" + ) + result = self.adyen.capital.grants_api.request_grant(request) + self.assertEqual(1, len(result.message['grants'])) + self.assertEqual("GR00000000000000000000001", result.message['grants'][0]['id']) + + def test_get_all_grant_offers(self): + request = {} + self.adyen.client = self.test.create_client_from_file( + 200, + request, + "test/mocks/capital/grant-offers-success.json" + ) + result = self.adyen.capital.grant_offers_api.get_all_grant_offers(request) + self.assertEqual(1, len(result.message['grantOffers'])) + self.assertEqual("GO00000000000000000000001", result.message['grantOffers'][0]['id']) + + def test_get_all_grants(self): + request = {} + self.adyen.client = self.test.create_client_from_file( + 200, + request, + "test/mocks/capital/grants-success.json" + ) + result = self.adyen.capital.grants_api.get_all_grants(counterparty_account_holder_id="AH00000000000000000000001") + self.assertEqual(1, len(result.message['grants'])) + self.assertEqual("GR00000000000000000000001", result.message['grants'][0]['id']) + + def test_get_grant(self): + request = {} + self.adyen.client = self.test.create_client_from_file( + 200, + request, + "test/mocks/capital/get-grant-success.json" + ) + result = self.adyen.capital.grants_api.get_grant(grantId="GR00000000000000000000001") + self.assertEqual("GR00000000000000000000001", result.message['id']) + + def test_get_all_grant_disbursements(self): + request = {} + self.adyen.client = self.test.create_client_from_file( + 200, + request, + "test/mocks/capital/get-grant-disbursements-success.json" + ) + result = self.adyen.capital.grants_api.get_all_grant_disbursements(grantId="GR00000000000000000000001") + self.assertEqual(1, len(result.message['disbursements'])) + self.assertEqual("DI00000000000000000000001", result.message['disbursements'][0]['id']) + + def test_get_grant_disbursement(self): + request = {} + self.adyen.client = self.test.create_client_from_file( + 200, + request, + "test/mocks/capital/get-grant-disbursement-success.json" + ) + result = self.adyen.capital.grants_api.get_grant_disbursement(grantId="GR00000000000000000000001", + disbursementId="DI00000000000000000000001") + self.assertEqual("DI00000000000000000000001", result.message['id']) + + def test_update_grant_disbursement(self): + request = {} + self.adyen.client = self.test.create_client_from_file( + 200, + request, + "test/mocks/capital/update-grant-disbursement-success.json" + ) + result = self.adyen.capital.grants_api.update_grant_disbursement(request, grantId="GR00000000000000000000001", + disbursementId="DI00000000000000000000001") + self.assertEqual(1500, result.message['repayment']['basisPoints']) + + def test_get_grant_account_information(self): + request = {} + self.adyen.client = self.test.create_client_from_file( + 200, + request, + "test/mocks/capital/get-grant-account-success.json" + ) + result = self.adyen.capital.grant_accounts_api.get_grant_account_information(id="CG00000000000000000000001") + self.assertEqual("CG00000000000000000000001", result.message['id']) + + def test_get_grant_offer(self): + request = {} + self.adyen.client = self.test.create_client_from_file( + 200, + request, + "test/mocks/capital/get-grant-offer-success.json" + ) + result = self.adyen.capital.grant_offers_api.get_grant_offer(id="GO00000000000000000000001") + self.assertEqual("GO00000000000000000000001", result.message['id']) diff --git a/test/DetermineEndpointTest.py b/test/DetermineEndpointTest.py index eef76e0f..b8113143 100644 --- a/test/DetermineEndpointTest.py +++ b/test/DetermineEndpointTest.py @@ -25,6 +25,8 @@ class TestDetermineUrl(unittest.TestCase): management_url = adyen.management.account_merchant_level_api.baseUrl sessionauth_url = adyen.sessionAuthentication.session_authentication_api.baseUrl sessionauth_version = sessionauth_url.split('/')[-1] + capital_url = adyen.capital.grants_api.baseUrl + capital_version = capital_url.split('/')[-1] def test_checkout_api_url_custom(self): self.client.live_endpoint_prefix = "1797a841fbb37ca7-AdyenDemo" @@ -143,3 +145,11 @@ def test_secureauthentication_api_url(self): def test_live_secureauthentication_api_url(self): url = self.adyen.client._determine_api_url("live", self.sessionauth_url + "/sessions") self.assertEqual(url, f"https://authe-live.adyen.com/authe/api/{self.sessionauth_version}/sessions") + + def test_capital_api_url(self): + url = self.adyen.client._determine_api_url("test", self.capital_url) + self.assertEqual(url, self.capital_url) + + def test_live_capital_api_url(self): + url = self.adyen.client._determine_api_url("live", self.capital_url) + self.assertEqual(url, f"https://balanceplatform-api-live.adyen.com/capital/{self.capital_version}") diff --git a/test/mocks/capital/get-grant-account-success.json b/test/mocks/capital/get-grant-account-success.json new file mode 100644 index 00000000..c301b36d --- /dev/null +++ b/test/mocks/capital/get-grant-account-success.json @@ -0,0 +1,21 @@ + +{ + "id": "CG00000000000000000000001", + "fundingBalanceAccountId": "BA00000000000000000000001", + "limits": [ + { + "amount": { + "currency": "EUR", + "value": 100000 + } + } + ], + "balances": [ + { + "currency": "EUR", + "principal": 10000, + "fee": 1000, + "total": 11000 + } + ] +} diff --git a/test/mocks/capital/get-grant-disbursement-success.json b/test/mocks/capital/get-grant-disbursement-success.json new file mode 100644 index 00000000..bce3c043 --- /dev/null +++ b/test/mocks/capital/get-grant-disbursement-success.json @@ -0,0 +1,27 @@ + +{ + "id": "DI00000000000000000000001", + "grantId": "GR00000000000000000000001", + "accountHolderId": "AH00000000000000000000001", + "balanceAccountId": "BA00000000000000000000001", + "amount": { + "currency": "EUR", + "value": 10000 + }, + "fee": { + "amount": { + "currency": "EUR", + "value": 1000 + } + }, + "balances": { + "currency": "EUR", + "principal": 10000, + "fee": 1000, + "total": 11000 + }, + "repayment": { + "basisPoints": 1000, + "updateDescription": "string" + } +} diff --git a/test/mocks/capital/get-grant-disbursements-success.json b/test/mocks/capital/get-grant-disbursements-success.json new file mode 100644 index 00000000..8389bd8a --- /dev/null +++ b/test/mocks/capital/get-grant-disbursements-success.json @@ -0,0 +1,31 @@ + +{ + "disbursements": [ + { + "id": "DI00000000000000000000001", + "grantId": "GR00000000000000000000001", + "accountHolderId": "AH00000000000000000000001", + "balanceAccountId": "BA00000000000000000000001", + "amount": { + "currency": "EUR", + "value": 10000 + }, + "fee": { + "amount": { + "currency": "EUR", + "value": 1000 + } + }, + "balances": { + "currency": "EUR", + "principal": 10000, + "fee": 1000, + "total": 11000 + }, + "repayment": { + "basisPoints": 1000, + "updateDescription": "string" + } + } + ] +} diff --git a/test/mocks/capital/get-grant-offer-success.json b/test/mocks/capital/get-grant-offer-success.json new file mode 100644 index 00000000..0add7fdd --- /dev/null +++ b/test/mocks/capital/get-grant-offer-success.json @@ -0,0 +1,32 @@ + +{ + "id": "GO00000000000000000000001", + "accountHolderId": "AH00000000000000000000001", + "contractType": "cashAdvance", + "amount": { + "currency": "EUR", + "value": 10000 + }, + "fee": { + "amount": { + "currency": "EUR", + "value": 1000 + }, + "aprBasisPoints": 1200 + }, + "repayment": { + "basisPoints": 1000, + "term": { + "estimatedDays": 180, + "maximumDays": 365 + }, + "threshold": { + "amount": { + "currency": "EUR", + "value": 1000 + } + } + }, + "startsAt": "2024-01-01T00:00:00Z", + "expiresAt": "2024-01-31T23:59:59Z" +} diff --git a/test/mocks/capital/get-grant-success.json b/test/mocks/capital/get-grant-success.json new file mode 100644 index 00000000..6e0c8293 --- /dev/null +++ b/test/mocks/capital/get-grant-success.json @@ -0,0 +1,23 @@ + +{ + "id": "GR00000000000000000000001", + "grantAccountId": "CG00000000000000000000001", + "grantOfferId": "GO00000000000000000000001", + "counterparty": { + "accountHolderId": "AH00000000000000000000001", + "balanceAccountId": "BA00000000000000000000001" + }, + "amount": { + "currency": "EUR", + "value": 10000 + }, + "balances": { + "currency": "EUR", + "principal": 10000, + "fee": 1000, + "total": 11000 + }, + "status": { + "code": "Active" + } +} diff --git a/test/mocks/capital/grant-accounts-success.json b/test/mocks/capital/grant-accounts-success.json new file mode 100644 index 00000000..c2c20e20 --- /dev/null +++ b/test/mocks/capital/grant-accounts-success.json @@ -0,0 +1,12 @@ +{ + "grantAccounts": [ + { + "id": "GA00000000000000000000001", + "grantorAccountId": "CG00000000000000000000001", + "fundingSource": { + "type": "adyen", + "adyenAccountId": "BA00000000000000000000001" + } + } + ] +} diff --git a/test/mocks/capital/grant-offers-success.json b/test/mocks/capital/grant-offers-success.json new file mode 100644 index 00000000..d28bf6a0 --- /dev/null +++ b/test/mocks/capital/grant-offers-success.json @@ -0,0 +1,19 @@ +{ + "grantOffers": [ + { + "id": "GO00000000000000000000001", + "amount": { + "currency": "EUR", + "value": 10000 + }, + "fee": { + "currency": "EUR", + "value": 100 + }, + "repayment": { + "currency": "EUR", + "value": 10100 + } + } + ] +} diff --git a/test/mocks/capital/grants-success.json b/test/mocks/capital/grants-success.json new file mode 100644 index 00000000..414c9ca3 --- /dev/null +++ b/test/mocks/capital/grants-success.json @@ -0,0 +1,26 @@ +{ + "grants": [ + { + "id": "GR00000000000000000000001", + "grantAccountId": "CG00000000000000000000001", + "grantOfferId": "GO00000000000000000000001", + "counterparty": { + "accountHolderId": "AH00000000000000000000001", + "balanceAccountId": "BA00000000000000000000001" + }, + "amount": { + "currency": "EUR", + "value": 10000 + }, + "balances": { + "currency": "EUR", + "principal": 10000, + "fee": 1000, + "total": 11000 + }, + "status": { + "code": "Active" + } + } + ] +} \ No newline at end of file diff --git a/test/mocks/capital/update-grant-disbursement-success.json b/test/mocks/capital/update-grant-disbursement-success.json new file mode 100644 index 00000000..7aeaeada --- /dev/null +++ b/test/mocks/capital/update-grant-disbursement-success.json @@ -0,0 +1,27 @@ + +{ + "id": "DI00000000000000000000001", + "grantId": "GR00000000000000000000001", + "accountHolderId": "AH00000000000000000000001", + "balanceAccountId": "BA00000000000000000000001", + "amount": { + "currency": "EUR", + "value": 10000 + }, + "fee": { + "amount": { + "currency": "EUR", + "value": 1000 + } + }, + "balances": { + "currency": "EUR", + "principal": 10000, + "fee": 1000, + "total": 11000 + }, + "repayment": { + "basisPoints": 1500, + "updateDescription": "string" + } +}