diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml new file mode 100644 index 0000000..7706eb7 --- /dev/null +++ b/.idea/codeStyleSettings.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..c23ecac --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..08e67b3 --- /dev/null +++ b/README.md @@ -0,0 +1,131 @@ +# bunq Java SDK +Version 0.9.0 **BETA** + +## Installation +In the root of your project, being in the correct virtual environment, run: +```shell +(bunq_sdk_python) $ pip install bunq_sdk && pip freeze > requirements.txt +``` + +## Usage + +### Creating an API context +In order to start making calls with the bunq API, you must first register your API key and device, +and create a session. In the SDKs, we group these actions and call it "creating an API context". The +context can be created by using the following code snippet: + +``` +apiContext = context.ApiContext(ENVIRONMENT_TYPE, API_KEY, + DEVICE_DESCRIPTION); +apiContext.save(API_CONTEXT_FILE_PATH); +``` + +#### Example +See [`api_context_save_example.py`](./examples/api_context_save_example.py) + +The API context can then be saved with: + +#### Safety considerations +The file storing the context details (i.e. `bunq.conf`) is a key to your account. Anyone having +access to it is able to perform any Public API actions with your account. Therefore, we recommend +choosing a truly safe place to store it. + +### Making API calls +There is a class for each endpoint. Each class has functions for each supported action. These +actions can be `create`, `get`, `update`, `delete` and `list`. + +Sometimes API calls have dependencies, for instance `MonetaryAccount`. Making changes to a monetary +account always also needs a reference to a `User`. These dependencies are required as arguments when +performing API calls. Take a look at [doc.bunq.com](https://doc.bunq.com) for the full +documentation. + +#### Creating objects +Creating objects through the API requires an `ApiContext`, a `requestMap` and identifiers of all +dependencies (such as User ID required for accessing a Monetary Account). Optionally, custom headers +can be passed to requests. + + +``` +request_map = { + generated.Payment.FIELD_AMOUNT: object_.Amount( + _PAYMENT_AMOUNT, + _PAYMENT_CURRENCY + ), + generated.Payment.FIELD_COUNTERPARTY_ALIAS: object_.Pointer( + _COUNTERPARTY_POINTER_TYPE, + _COUNTERPARTY_EMAIL + ), + generated.Payment.FIELD_DESCRIPTION: _PAYMENT_DESCRIPTION, +} + +payment_id = generated.Payment.create( + api_context, + request_map, + _USER_ITEM_ID, + _MONETARY_ACCOUNT_ITEM_ID +) +``` + +##### Example +See [`PaymentExample.py`](./examples/payment_example.py) + +#### Reading objects +Reading objects through the API requires an `ApiContext`, identifiers of all dependencies (such as +User ID required for accessing a Monetary Account), and the identifier of the object to read (ID or +UUID) Optionally, custom headers can be passed to requests. + +This type of calls always returns a model. + +``` +monetary_account = generated.MonetaryAccountBank.get( + api_context, + _USER_ITEM_ID, + _MONETARY_ACCOUNT_ITEM_ID +) +``` + +##### Example +See [`MonetaryAccountExample.py`](./examples/monetary_account_example.py) + +#### Updating objects +Updating objects through the API goes the same way as creating objects, except that also the object to update identifier +(ID or UUID) is needed. + +``` +request_update_map = { + generated.RequestInquiry.FIELD_STATUS: _STATUS_REVOKED, +} +generated.RequestInquiry.update( + api_context, + request_update_map, + _USER_ITEM_ID, + _MONETARY_ACCOUNT_ITEM_ID, + request_id +).to_json() +``` + +##### Example +See [`RequestExample.py`](./examples/request_example.py) + +#### Deleting objects +Deleting objects through the API requires an `ApiContext`, identifiers of all dependencies (such as User ID required for +accessing a Monetary Account), and the identifier of the object to delete (ID or UUID) Optionally, custom headers can be +passed to requests. + +``` +generated.CustomerStatementExport.delete(apiContext, userId, monetaryAccountId, customerStatementId); +``` + +##### Example +See [`CustomerStatementExportExample.py`](./examples/customer_statement_export_example.py) + +#### Listing objects +Listing objects through the API requires an `ApiContext` and identifiers of all dependencies (such as User ID required +for accessing a Monetary Account). Optionally, custom headers can be passed to requests. + +``` +users = generated.User.list(api_context) +``` + +##### Example +See [`UserListExample.py`](./examples/user_list_example.py) diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..899f24f --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.9.0 \ No newline at end of file diff --git a/assets/attachment.jpg b/assets/attachment.jpg new file mode 100644 index 0000000..8396e33 Binary files /dev/null and b/assets/attachment.jpg differ diff --git a/bunq/__init__.py b/bunq/__init__.py new file mode 100644 index 0000000..03b8034 --- /dev/null +++ b/bunq/__init__.py @@ -0,0 +1,3 @@ +from bunq.sdk.json import registry + +registry.initialize() diff --git a/bunq/sdk/__init__.py b/bunq/sdk/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bunq/sdk/client.py b/bunq/sdk/client.py new file mode 100644 index 0000000..aedc2c1 --- /dev/null +++ b/bunq/sdk/client.py @@ -0,0 +1,254 @@ +import uuid + +# Due to compatibility requirements, we are importing a class here. +try: + from json import JSONDecodeError +except ImportError: + from simplejson import JSONDecodeError + +import requests + +from bunq.sdk.json import converter +from bunq.sdk import security +from bunq.sdk import context +from bunq.sdk import exception + + +class ApiClient(object): + """ + :type _api_context: context.ApiContext + """ + + # Header constants + HEADER_ATTACHMENT_DESCRIPTION = 'X-Bunq-Attachment-Description' + HEADER_CONTENT_TYPE = 'Content-Type' + HEADER_CACHE_CONTROL = 'Cache-Control' + HEADER_USER_AGENT = 'User-Agent' + HEADER_LANGUAGE = 'X-Bunq-Language' + HEADER_REGION = 'X-Bunq-Region' + HEADER_REQUEST_ID = 'X-Bunq-Client-Request-Id' + HEADER_GEOLOCATION = 'X-Bunq-Geolocation' + HEADER_SIGNATURE = 'X-Bunq-Client-Signature' + HEADER_AUTHENTICATION = 'X-Bunq-Client-Authentication' + + # Default header values + _USER_AGENT_BUNQ = 'bunq-sdk-python/0.9' + _GEOLOCATION_ZERO = '0 0 0 0 NL' + _LANGUAGE_EN_US = 'en_US' + _REGION_NL_NL = 'nl_NL' + _CACHE_CONTROL_NONE = 'no-cache' + + # Request method names + _METHOD_POST = 'POST' + _METHOD_PUT = 'PUT' + _METHOD_GET = 'GET' + _METHOD_DELETE = 'DELETE' + + # Status code for successful execution + _STATUS_CODE_OK = 200 + + # Fields for fetching errors + _FIELD_ERROR = 'Error' + _FIELD_ERROR_DESCRIPTION = 'error_description' + + # Empty string + _STRING_EMPTY = '' + + # Empty bytes + _BYTES_EMPTY = b'' + + def __init__(self, api_context): + self._api_context = api_context + + def post(self, uri_relative, request_bytes, custom_headers): + """ + :type uri_relative: str + :type request_bytes: bytes + :type custom_headers: dict[str, str] + + :return: requests.Response + """ + + return self._request( + self._METHOD_POST, + uri_relative, + request_bytes, + custom_headers + ) + + def _request(self, method, uri_relative, request_bytes, custom_headers): + """ + :type method: str + :type uri_relative: str + :type request_bytes: bytes + :type custom_headers: dict[str, str] + + :return: requests.Response + """ + + self._api_context.ensure_session_active() + all_headers = self._get_all_headers( + method, + uri_relative, + request_bytes, + custom_headers + ) + + response = requests.request( + method, + self._get_uri_full(uri_relative), + data=request_bytes, + headers=all_headers + ) + + self._assert_response_success(response) + + return response + + def _get_all_headers(self, method, endpoint, request_bytes, custom_headers): + """ + :type method: str + :type endpoint: str + :type request_bytes: bytes + :type custom_headers: dict[str, str] + + :rtype: dict[str, str] + """ + + headers = self._get_default_headers() + headers.update(custom_headers) + + if self._api_context.token is not None: + headers[self.HEADER_AUTHENTICATION] = self._api_context.token + headers[self.HEADER_SIGNATURE] = security.sign_request( + self._api_context.installation_context.private_key_client, + method, + endpoint, + request_bytes, + headers + ) + + return headers + + @classmethod + def _get_default_headers(cls): + """ + :rtype: dict[str, str] + """ + + return { + cls.HEADER_USER_AGENT: cls._USER_AGENT_BUNQ, + cls.HEADER_REQUEST_ID: cls._generate_random_request_id(), + cls.HEADER_GEOLOCATION: cls._GEOLOCATION_ZERO, + cls.HEADER_LANGUAGE: cls._LANGUAGE_EN_US, + cls.HEADER_REGION: cls._REGION_NL_NL, + cls.HEADER_CACHE_CONTROL: cls._CACHE_CONTROL_NONE, + } + + @staticmethod + def _generate_random_request_id(): + """ + :rtype: str + """ + + return str(uuid.uuid4()) + + def _get_uri_full(self, uri_relative): + """ + :type uri_relative: str + + :rtype: str + """ + + return self._api_context.environment_type.uri_base + uri_relative + + def _assert_response_success(self, response): + """ + :type response: requests.Response + + :rtype: None + :raise ApiException: When the response is not successful. + """ + + if response.status_code != self._STATUS_CODE_OK: + raise exception.ApiException( + response.status_code, + self._fetch_error_messages(response) + ) + + def _fetch_error_messages(self, response): + """ + :type response: requests.Response + + :rtype: list[str] + """ + + response_content_string = response.content.decode() + + try: + error_dict = converter.json_to_class(dict, response_content_string) + + return self._fetch_error_descriptions(error_dict) + except JSONDecodeError: + return [response_content_string] + + def _fetch_error_descriptions(self, error_dict): + """ + :type error_dict: dict[str, list[dict[str, str]] + + :rtype: list[str] + """ + + error_descriptions = [] + + for error in error_dict[self._FIELD_ERROR]: + description = error[self._FIELD_ERROR_DESCRIPTION] + error_descriptions.append(description) + + return error_descriptions + + def put(self, uri_relative, request_bytes, custom_headers): + """ + :type uri_relative: str + :type request_bytes: bytes + :type custom_headers: dict[str, str] + + :rtype: requests.Response + """ + + return self._request( + self._METHOD_PUT, + uri_relative, + request_bytes, + custom_headers + ) + + def get(self, uri_relative, custom_headers): + """ + :type uri_relative: str + :type custom_headers: dict[str, str] + + :rtype: requests.Response + """ + + return self._request( + self._METHOD_GET, + uri_relative, + self._BYTES_EMPTY, + custom_headers + ) + + def delete(self, uri_relative, custom_headers): + """ + :type uri_relative: str + :type custom_headers: dict[str, str] + + :rtype: requests.Response + """ + + return self._request( + self._METHOD_DELETE, + uri_relative, + self._BYTES_EMPTY, + custom_headers + ) diff --git a/bunq/sdk/context.py b/bunq/sdk/context.py new file mode 100644 index 0000000..a3854bd --- /dev/null +++ b/bunq/sdk/context.py @@ -0,0 +1,350 @@ +import datetime + +import aenum +from Cryptodome.PublicKey import RSA + +from bunq.sdk import model +from bunq.sdk import security +from bunq.sdk.json import converter +from bunq.sdk.model import generated + + +class ApiEnvironmentType(aenum.AutoNumberEnum): + """ + :type PRODUCTION: ApiEnvironmentType + :type SANDBOX: ApiEnvironmentType + :type uri_base: str + """ + + PRODUCTION = 'https://public.api.bunq.com/v1/' + SANDBOX = 'https://sandbox.public.api.bunq.com/v1/' + + def __init__(self, uri_base): + """ + :type uri_base: str + """ + + self._uri_base = uri_base + + @property + def uri_base(self): + """ + :rtype: str + """ + + return self._uri_base + + +class ApiContext(object): + """ + :type _environment_type: ApiEnvironmentType + :type _api_key: str + :type _session_context: SessionContext + :type _installation_context: InstallationContext + """ + + # File mode for saving and restoring the context + _FILE_MODE_WRITE = 'w' + _FILE_MODE_READ = 'r' + + # Minimum time to session expiry not requiring session reset + _TIME_TO_SESSION_EXPIRY_MINIMUM_SECONDS = 30 + + # Dummy ID to pass to Session endpoint + _SESSION_ID_DUMMY = 0 + + # Default path to the file storing serialized API context + _PATH_API_CONTEXT_DEFAULT = 'bunq.conf' + + def __init__(self, environment_type, api_key, device_description, + permitted_ips=None): + """ + :type environment_type: ApiEnvironmentType + :type api_key: str + :type device_description: str + :type permitted_ips: list[str]|None + """ + + if permitted_ips is None: + permitted_ips = [] + + self._environment_type = environment_type + self._api_key = api_key + self._installation_context = None + self._session_context = None + self._initialize(device_description, permitted_ips) + + def _initialize(self, device_description, permitted_ips): + """ + :type device_description: str + :type permitted_ips: list[str] + + :rtype: None + """ + + self._initialize_installation() + self._register_device(device_description, permitted_ips) + self._initialize_session() + + def _initialize_installation(self): + """ + :rtype: None + """ + + private_key_client = security.generate_rsa_private_key() + installation = model.Installation.create( + self, + security.public_key_to_string(private_key_client.publickey()) + ) + token = installation.token.token + public_key_server_string = \ + installation.server_public_key.server_public_key + public_key_server = RSA.import_key(public_key_server_string) + + self._installation_context = InstallationContext( + token, + private_key_client, + public_key_server + ) + + def _register_device(self, device_description, + permitted_ips): + """ + :type device_description: str + :type permitted_ips: list[] + + :rtype: None + """ + + model.DeviceServer.create(self, device_description, permitted_ips) + + def _initialize_session(self): + """ + :rtype: None + """ + + session_server = model.SessionServer.create(self) + token = session_server.token.token + expiry_time = self._get_expiry_timestamp(session_server) + + self._session_context = SessionContext(token, expiry_time) + + @classmethod + def _get_expiry_timestamp(cls, session_server): + """ + :type session_server: model.SessionServer + + :rtype: datetime.datetime + """ + + timeout_seconds = cls._get_session_timeout_seconds(session_server) + time_now = datetime.datetime.now() + + return time_now + datetime.timedelta(seconds=timeout_seconds) + + @classmethod + def _get_session_timeout_seconds(cls, session_server): + """ + :type session_server: model.SessionServer + + :rtype: int + """ + + if session_server.user_company is not None: + return session_server.user_company.session_timeout + else: + return session_server.user_person.session_timeout + + def ensure_session_active(self): + if self.session_context is None: + return + + time_now = datetime.datetime.now() + time_to_expiry = self.session_context.expiry_time - time_now + time_to_expiry_minimum = datetime.timedelta( + seconds=self._TIME_TO_SESSION_EXPIRY_MINIMUM_SECONDS + ) + + if time_to_expiry < time_to_expiry_minimum: + self.reset_session() + + def reset_session(self): + """ + Closes the current session and opens a new one. + + :rtype: None + """ + + self._drop_session_context() + self._initialize_session() + + def _drop_session_context(self): + """ + :rtype: None + """ + + self._session_context = None + + def close_session(self): + """ + Closes the current session. + + :rtype: None + """ + + self._delete_session() + self._drop_session_context() + + def _delete_session(self): + """ + :rtype: None + """ + + generated.Session.delete(self, self._SESSION_ID_DUMMY) + + @property + def environment_type(self): + """ + :rtype: ApiEnvironmentType + """ + + return self._environment_type + + @property + def api_key(self): + """ + :rtype: str + """ + + return self._api_key + + @property + def token(self): + """ + :rtype: str + """ + + if self._session_context is not None: + return self.session_context.token + elif self._installation_context is not None: + return self.installation_context.token + else: + return None + + @property + def installation_context(self): + """ + :rtype: InstallationContext + """ + + return self._installation_context + + @property + def session_context(self): + """ + :rtype: SessionContext + """ + + return self._session_context + + def save(self, path=None): + """ + :type path: str + + :rtype: None + """ + + if path is None: + path = self._PATH_API_CONTEXT_DEFAULT + + with open(path, self._FILE_MODE_WRITE) as file: + file.write(converter.class_to_json(self)) + + @classmethod + def restore(cls, path=None): + """ + :type path: str + + :rtype: ApiContext + """ + + if path is None: + path = cls._PATH_API_CONTEXT_DEFAULT + + with open(path, cls._FILE_MODE_READ) as file: + return converter.json_to_class(ApiContext, file.read()) + + +class InstallationContext(object): + """ + :type _token: str + :type _private_key_client: RSA.RsaKey + :type _public_key_server: RSA.RsaKey + """ + + def __init__(self, token, private_key_client, public_key_server): + """ + :type token: str + :type private_key_client: RSA.RsaKey + :type public_key_server: RSA.RsaKey + """ + + self._token = token + self._private_key_client = private_key_client + self._public_key_server = public_key_server + + @property + def token(self): + """ + :rtype: str + """ + + return self._token + + @property + def private_key_client(self): + """ + :rtype: RSA.RsaKey + """ + + return self._private_key_client + + @property + def public_key_server(self): + """ + :rtype: RSA.RsaKey + """ + + return self._public_key_server + + +class SessionContext(object): + """ + :type _token: str + :type _expiry_time: datetime.datetime + """ + + def __init__(self, token, expiry_time): + """ + :type token: str + :type expiry_time: datetime.datetime + """ + + self._token = token + self._expiry_time = expiry_time + + @property + def token(self): + """ + :rtype: str + """ + + return self._token + + @property + def expiry_time(self): + """ + :rtype: datetime.datetime + """ + + return self._expiry_time diff --git a/bunq/sdk/exception.py b/bunq/sdk/exception.py new file mode 100644 index 0000000..6161ec8 --- /dev/null +++ b/bunq/sdk/exception.py @@ -0,0 +1,54 @@ +class ApiException(Exception): + """ + :type _response_code: int + """ + + # Constants for formatting messages + _FORMAT_RESPONSE_CODE_LINE = 'HTTP Response Code: {}' + _GLUE_ERROR_MESSAGES = '\n' + + def __init__(self, response_code, messages): + """ + :type response_code: int + :type messages: list[str] + """ + + super(ApiException, self).__init__( + self.generate_message_error(response_code, messages) + ) + self._response_code = response_code + + def generate_message_error(self, response_code, messages): + """ + :type response_code: int + :type messages: list[str] + + :rtype: str + """ + + line_response_code = self._FORMAT_RESPONSE_CODE_LINE \ + .format(response_code) + + return self.glue_messages([line_response_code] + messages) + + def glue_messages(self, messages): + """ + :type messages: list[str] + + :rtype: str + """ + + return self._GLUE_ERROR_MESSAGES.join(messages) + + @property + def response_code(self): + """ + :rtype: int + """ + + return self._response_code + + +class BunqException(Exception): + def __init__(self, message): + super(BunqException, self).__init__(message) diff --git a/bunq/sdk/json/__init__.py b/bunq/sdk/json/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bunq/sdk/json/adapters.py b/bunq/sdk/json/adapters.py new file mode 100644 index 0000000..edbb111 --- /dev/null +++ b/bunq/sdk/json/adapters.py @@ -0,0 +1,442 @@ +import datetime + +from bunq.sdk import context +from bunq.sdk import model +from bunq.sdk import security +from bunq.sdk.json import converter +from bunq.sdk.model import generated +from bunq.sdk.model.generated import object_ + + +class InstallationAdapter(converter.JsonAdapter): + # Id constants + _ATTRIBUTE_ID = '_id_' + _INDEX_ID = 0 + _FIELD_ID = 'Id' + + # Token constants + _ATTRIBUTE_TOKEN = '_token' + _INDEX_TOKEN = 1 + _FIELD_TOKEN = 'Token' + + # Server Public Key constants + _ATTRIBUTE_SERVER_PUBLIC_KEY = '_server_public_key' + _INDEX_SERVER_PUBLIC_KEY = 2 + _FIELD_SERVER_PUBLIC_KEY = 'ServerPublicKey' + + @classmethod + def deserialize(cls, target_class, array): + """ + :type target_class: model.Installation|type + :type array: list + + :rtype: model.Installation + """ + + installation = target_class.__new__(target_class) + server_public_key_wrapped = array[cls._INDEX_SERVER_PUBLIC_KEY] + installation.__dict__ = { + cls._ATTRIBUTE_ID: converter.deserialize( + model.Id, + array[cls._INDEX_ID][cls._FIELD_ID] + ), + cls._ATTRIBUTE_TOKEN: converter.deserialize( + model.SessionToken, + array[cls._INDEX_TOKEN][cls._FIELD_TOKEN] + ), + cls._ATTRIBUTE_SERVER_PUBLIC_KEY: converter.deserialize( + model.PublicKeyServer, + server_public_key_wrapped[cls._FIELD_SERVER_PUBLIC_KEY] + ), + } + + return installation + + @classmethod + def serialize(cls, installation): + """ + :type installation: model.Installation + + :rtype: list + """ + + return [ + {cls._FIELD_ID: converter.serialize(installation.id_)}, + {cls._FIELD_TOKEN: converter.serialize(installation.token)}, + { + cls._FIELD_SERVER_PUBLIC_KEY: converter.serialize( + installation.server_public_key + ), + }, + ] + + +class SessionServerAdapter(converter.JsonAdapter): + # Id constants + _ATTRIBUTE_ID = '_id_' + _INDEX_ID = 0 + _FIELD_ID = 'Id' + + # Token constants + _ATTRIBUTE_TOKEN = '_token' + _INDEX_TOKEN = 1 + _FIELD_TOKEN = 'Token' + + # User constants + _INDEX_USER = 2 + + # UserCompany constants + _ATTRIBUTE_USER_COMPANY = '_user_company' + _FIELD_USER_COMPANY = 'UserCompany' + + # UserPerson constants + _ATTRIBUTE_USER_PERSON = '_user_person' + _FIELD_USER_PERSON = 'UserPerson' + + @classmethod + def deserialize(cls, target_class, array): + """ + :type target_class: model.SessionServer|type + :type array: list + + :rtype: model.SessionServer + """ + + session_server = target_class.__new__(target_class) + session_server.__dict__ = { + cls._ATTRIBUTE_ID: converter.deserialize( + model.Id, + array[cls._INDEX_ID][cls._FIELD_ID] + ), + cls._ATTRIBUTE_TOKEN: converter.deserialize( + model.SessionToken, + array[cls._INDEX_TOKEN][cls._FIELD_TOKEN] + ), + cls._ATTRIBUTE_USER_COMPANY: None, + cls._ATTRIBUTE_USER_PERSON: None, + } + + user_dict_wrapped = array[cls._INDEX_USER] + + if cls._FIELD_USER_COMPANY in user_dict_wrapped: + session_server.__dict__[cls._ATTRIBUTE_USER_COMPANY] = \ + converter.deserialize( + generated.UserCompany, + user_dict_wrapped[cls._FIELD_USER_COMPANY] + ) + elif cls._FIELD_USER_PERSON in user_dict_wrapped: + session_server.__dict__[cls._ATTRIBUTE_USER_PERSON] = \ + converter.deserialize( + generated.UserPerson, + user_dict_wrapped[cls._FIELD_USER_PERSON] + ) + + return session_server + + @classmethod + def serialize(cls, session_server): + """ + :type session_server: model.SessionServer + + :rtype: list + """ + + return [ + {cls._FIELD_ID: converter.serialize(session_server.id_)}, + {cls._FIELD_TOKEN: converter.serialize(session_server.token)}, + { + cls._FIELD_USER_COMPANY: + converter.serialize(session_server.user_company), + }, + { + cls._FIELD_USER_PERSON: + converter.serialize(session_server.user_person), + }, + ] + + +class InstallationContextAdapter(converter.JsonAdapter): + # Attribute/Field constants + _ATTRIBUTE_TOKEN = '_token' + _FIELD_TOKEN = 'token' + + _ATTRIBUTE_PRIVATE_KEY_CLIENT = '_private_key_client' + _FIELD_PRIVATE_KEY_CLIENT = 'private_key_client' + + _ATTRIBUTE_PUBLIC_KEY_CLIENT = '_public_key_client' + _FIELD_PUBLIC_KEY_CLIENT = 'public_key_client' + + _ATTRIBUTE_PUBLIC_KEY_SERVER = '_public_key_server' + _FIELD_PUBLIC_KEY_SERVER = 'public_key_server' + + @classmethod + def deserialize(cls, target_class, obj): + """ + :type target_class: context.InstallationContext|type + :type obj: dict + + :rtype: context.InstallationContext + """ + + installation_context = target_class.__new__(target_class) + private_key_client = security.rsa_key_from_string( + obj[cls._FIELD_PRIVATE_KEY_CLIENT] + ) + public_key_client = security.rsa_key_from_string( + obj[cls._FIELD_PUBLIC_KEY_CLIENT] + ) + public_key_server = security.rsa_key_from_string( + obj[cls._FIELD_PUBLIC_KEY_SERVER] + ) + installation_context.__dict__ = { + cls._ATTRIBUTE_TOKEN: obj[cls._FIELD_TOKEN], + cls._ATTRIBUTE_PRIVATE_KEY_CLIENT: private_key_client, + cls._ATTRIBUTE_PUBLIC_KEY_CLIENT: public_key_client, + cls._ATTRIBUTE_PUBLIC_KEY_SERVER: public_key_server, + } + + return installation_context + + @classmethod + def serialize(cls, installation_context): + """ + :type installation_context: context.InstallationContext + + :rtype: dict + """ + + return { + cls._FIELD_TOKEN: installation_context.token, + cls._FIELD_PUBLIC_KEY_CLIENT: security.public_key_to_string( + installation_context.private_key_client.publickey() + ), + cls._FIELD_PRIVATE_KEY_CLIENT: security.private_key_to_string( + installation_context.private_key_client + ), + cls._FIELD_PUBLIC_KEY_SERVER: security.public_key_to_string( + installation_context.public_key_server + ), + } + + +class ApiEnvironmentTypeAdapter(converter.JsonAdapter): + @classmethod + def deserialize(cls, target_class, name): + """ + :type target_class: context.ApiEnvironmentType|type + :type name: str + + :rtype: context.ApiEnvironmentType + """ + + _ = target_class + + return context.ApiEnvironmentType[name] + + @classmethod + def serialize(cls, api_environment_type): + """ + :type api_environment_type: context.ApiEnvironmentType + + :rtype: str + """ + + return api_environment_type.name + + +class FloatAdapter(converter.JsonAdapter): + # Precision to round the floats to before outputting them + _PRECISION_FLOAT = 2 + + @classmethod + def deserialize(cls, target_class, string): + """ + :type target_class: float|type + :type string: str + + :rtype: float + """ + + _ = target_class + + return float(string) + + @classmethod + def serialize(cls, number): + """ + :type number: float + + :rtype: str + """ + + return str(round(number, cls._PRECISION_FLOAT)) + + +class GeolocationAdapter(converter.JsonAdapter): + # Field constants + _FIELD_LATITUDE = 'latitude' + _FIELD_LONGITUDE = 'longitude' + _FIELD_ALTITUDE = 'altitude' + _FIELD_RADIUS = 'radius' + + @classmethod + def can_deserialize(cls): + """ + :rtype: bool + """ + + return False + + @classmethod + def deserialize(cls, target_class, obj): + """ + :type target_class: float|type + :type obj: dict + + :raise: NotImplementedError + """ + + _ = target_class, obj + + raise NotImplementedError() + + @classmethod + def serialize(cls, geolocation): + """ + :type geolocation: object_.Geolocation + + :rtype: dict + """ + + obj = {} + + cls.add_if_not_none(obj, cls._FIELD_LATITUDE, geolocation.latitude) + cls.add_if_not_none(obj, cls._FIELD_LONGITUDE, geolocation.longitude) + cls.add_if_not_none(obj, cls._FIELD_ALTITUDE, geolocation.altitude) + cls.add_if_not_none(obj, cls._FIELD_RADIUS, geolocation.radius) + + return obj + + @classmethod + def add_if_not_none(cls, dict_, key, value): + """ + :type dict_: dict[str, str] + :type key: str + :type value: float + + :rtype: None + """ + + if value is not None: + dict_[key] = str(value) + + +class MonetaryAccountReferenceAdapter(converter.JsonAdapter): + @classmethod + def deserialize(cls, target_class, obj): + """ + :type target_class: object_.MonetaryAccountReference|type + :type obj: dict + + :rtype: object_.MonetaryAccountReference + """ + + label_monetary_account = converter.deserialize( + object_.LabelMonetaryAccount, + obj + ) + + return target_class.create_from_label_monetary_account( + label_monetary_account + ) + + @classmethod + def serialize(cls, monetary_account_reference): + """ + :type monetary_account_reference: object_.MonetaryAccountReference + + :rtype: dict + """ + + return converter.serialize(monetary_account_reference.pointer) + + +class ShareDetailAdapter(converter.JsonAdapter): + # Attribute/Field constants + _ATTRIBUTE_PAYMENT = 'payment' + _FIELD_PAYMENT = 'ShareDetailPayment' + + _ATTRIBUTE_READ_ONLY = 'read_only' + _FIELD_READ_ONLY = 'ShareDetailReadOnly' + + _ATTRIBUTE_DRAFT_PAYMENT = 'draft_payment' + _FIELD_DRAFT_PAYMENT = 'ShareDetailDraftPayment' + + @classmethod + def deserialize(cls, target_class, obj): + """ + :type target_class: object_.ShareDetail|type + :type obj: dict + + :rtype: object_.ShareDetail + """ + + share_detail = target_class.__new__(target_class) + share_detail.__dict__ = { + cls._ATTRIBUTE_PAYMENT: converter.deserialize( + object_.ShareDetailPayment, + obj[cls._FIELD_PAYMENT] + ), + cls._ATTRIBUTE_READ_ONLY: converter.deserialize( + object_.ShareDetailReadOnly, + obj[cls._FIELD_READ_ONLY] + ), + cls._ATTRIBUTE_DRAFT_PAYMENT: converter.deserialize( + object_.ShareDetailDraftPayment, + obj[cls._FIELD_DRAFT_PAYMENT] + ), + } + + return share_detail + + @classmethod + def serialize(cls, share_detail): + """ + :type share_detail: object_.ShareDetail + + :rtype: dict + """ + + return { + cls._FIELD_PAYMENT: converter.serialize(share_detail.payment), + cls._FIELD_READ_ONLY: converter.serialize(share_detail.read_only), + cls._FIELD_DRAFT_PAYMENT: converter.serialize( + share_detail.draft_payment + ), + } + + +class DateTimeAdapter(converter.JsonAdapter): + # bunq timestamp format + _FORMAT_TIMESTAMP = '%Y-%m-%d %H:%M:%S.%f' + + @classmethod + def deserialize(cls, target_class, string): + """ + :type target_class: datetime.datetime|type + :type string: str + + :rtype: datetime.datetime + """ + + return target_class.strptime(string, cls._FORMAT_TIMESTAMP) + + @classmethod + def serialize(cls, timestamp): + """ + :type timestamp: datetime.datetime + + :rtype: dict + """ + + return timestamp.strftime(cls._FORMAT_TIMESTAMP) diff --git a/bunq/sdk/json/converter.py b/bunq/sdk/json/converter.py new file mode 100644 index 0000000..65e460a --- /dev/null +++ b/bunq/sdk/json/converter.py @@ -0,0 +1,616 @@ +import inspect +import json +import re +import sys +import warnings + +from bunq.sdk import exception + +# Indentation size we use for the serialized JSON output +_JSON_INDENT = 4 + + +class JsonAdapter(object): + # Error constants + _ERROR_COULD_NOT_FIND_CLASS = 'Could not find class: {}' + + # Maps to store custom serializers and deserializers + _custom_serializers = {} + _custom_deserializers = {} + + # Warning for when a key from raw object is unknown + _WARNING_KEY_UNKNOWN = '[bunq SDK beta] Key "{}" in "{}" is unknown.' + + # Overlapping key names to be suffixed by and underscore + _KEYS_OVERLAPPING = {'id', 'type'} + + # Suffix to strip from the keys during serialization + _SUFFIX_KEY_OVERLAPPING = '_' + _PREFIX_KEY_PROTECTED = '_' + + # Constants to fetch param types from the docstrings + _PATTERN_PARAM_TYPES = ':type (_?{}): ([\w.]+)(?:\[([\w.]+)\])?' + _SUBMATCH_INDEX_NAME = 1 + _SUBMATCH_INDEX_TYPE_MAIN = 2 + _SUBMATCH_INDEX_TYPE_SUB = 3 + + # List of builtin type names + _TYPE_NAMES_BUILTIN = {'int', 'bool', 'float', 'str', 'list', 'dict', + 'bytes', 'unicode'} + + # Delimiter between modules in class paths + _DELIMITER_MODULE = '.' + + # List of byte-array type names + _TYPE_NAMES_BYTES = {'bytes', 'unicode'} + + @classmethod + def register_custom_adapter(cls, target_class, adapter): + """ + :type target_class: type + :type adapter: JsonAdapter|type + + :rtype: None + """ + + class_name = target_class.__name__ + + if adapter.can_serialize(): + cls._custom_serializers[class_name] = adapter + + if adapter.can_deserialize(): + cls._custom_deserializers[class_name] = adapter + + @classmethod + def _get_serializer(cls, cls_for): + """ + :type cls_for: type + + :rtype: JsonAdapter + """ + + if cls_for.__name__ in cls._custom_serializers: + return cls._custom_serializers[cls_for.__name__] + + return JsonAdapter + + @classmethod + def _get_deserializer(cls, cls_for): + """ + :type cls_for: type + + :rtype: JsonAdapter + """ + + if cls_for.__name__ in cls._custom_deserializers: + return cls._custom_deserializers[cls_for.__name__] + + return JsonAdapter + + @classmethod + def can_deserialize(cls): + """ + :rtype: bool + """ + + return True + + @classmethod + def deserialize(cls, cls_target, obj_raw): + """ + :type cls_target: T|type + :type obj_raw: int|str|bool|float|list|dict|None + + :rtype: T + """ + + deserializer = cls._get_deserializer(cls_target) + + if deserializer == cls: + return cls._deserialize_default(cls_target, obj_raw) + else: + return deserializer.deserialize(cls_target, obj_raw) + + @classmethod + def _deserialize_default(cls, cls_target, obj_raw): + """ + :type cls_target: T|type + :type obj_raw: int|str|bool|float|list|dict|None + + :rtype: T + """ + + if cls._is_deserialized(cls_target, obj_raw): + return obj_raw + elif type(obj_raw) == dict: + return cls._deserialize_dict(cls_target, obj_raw) + else: + return cls_target(obj_raw) + + @classmethod + def _is_deserialized(cls, cls_target, obj): + """ + :type cls_target: type + :type obj: int|str|bool|float|bytes|unicode|list|dict|object + + :rtype: bool + """ + + if cls_target is None: + return True + + if cls_target in {list, dict}: + return True + + if cls._is_bytes_type(cls_target): + return True + + if obj is None: + return True + + if type(obj) in {list, cls_target}: + return True + + return False + + @classmethod + def _deserialize_dict(cls, cls_target, dict_): + """ + :type cls_target: T|type + :type dict_: dict + + :rtype: T + """ + + instance = cls_target.__new__(cls_target, cls_target) + instance.__dict__ = cls._deserialize_dict_attributes(cls_target, dict_) + + return instance + + @classmethod + def _deserialize_dict_attributes(cls, cls_context, dict_): + """ + :type cls_context: type + :type dict_: dict + + :rtype: dict + """ + + dict_deserialized = {} + + for key in dict_.keys(): + key_deserialized = cls._deserialize_key(key) + value_specs = cls._get_value_specs(cls_context, key_deserialized) + + if value_specs is not None: + dict_deserialized[value_specs.name] = cls._deserialize_value( + value_specs.types, + dict_[key] + ) + else: + cls._warn_key_unknown(cls_context, key) + + return dict_deserialized + + @classmethod + def _warn_key_unknown(cls, cls_context, key): + """ + :type cls_context: type + :type key: str + + :rtype: None + """ + + context_name = cls_context.__name__ + warnings.warn(cls._WARNING_KEY_UNKNOWN.format(key, context_name)) + + @classmethod + def _deserialize_key(cls, key): + """ + :type key: str + + :rtype: str + """ + + if key in cls._KEYS_OVERLAPPING: + return key + cls._SUFFIX_KEY_OVERLAPPING + + return key + + @classmethod + def _get_value_specs(cls, cls_in, attribute_name): + """ + :type cls_in: type + :type attribute_name: str + + :rtype: ValueSpecs + """ + + if cls_in in {dict, list}: + return ValueSpecs(None, ValueTypes(None, None)) + else: + return cls._fetch_attribute_specs_from_doc(cls_in, attribute_name) + + @classmethod + def _fetch_attribute_specs_from_doc(cls, cls_in, attribute_name): + """ + :type cls_in: type + :type attribute_name: str + + :rtype: ValueSpecs + """ + + pattern = cls._PATTERN_PARAM_TYPES.format(attribute_name) + match = re.search(pattern, cls_in.__doc__) + + if match is not None: + return ValueSpecs( + cls._fetch_name(match), + ValueTypes( + cls._fetch_type_main(cls_in, match), + cls._fetch_type_sub(cls_in, match) + ) + ) + else: + return None + + @classmethod + def _fetch_name(cls, match): + """ + :type match: _sre.SRE_Match + + :rtype: str + """ + + return match.group(cls._SUBMATCH_INDEX_NAME) + + @classmethod + def _fetch_type_main(cls, cls_in, match): + """ + :type cls_in: type + :type match: _sre.SRE_Match + + :rtype: type + """ + + return cls._str_to_type( + cls_in, + match.group(cls._SUBMATCH_INDEX_TYPE_MAIN) + ) + + @classmethod + def _fetch_type_sub(cls, cls_in, match): + """ + :type cls_in: type + :type match: _sre.SRE_Match + + :rtype: type + """ + + if match.group(cls._SUBMATCH_INDEX_TYPE_SUB): + return cls._str_to_type( + cls_in, + match.group(cls._SUBMATCH_INDEX_TYPE_SUB) + ) + else: + return None + + @classmethod + def _str_to_type(cls, context_class, string): + """ + :type context_class: type + :type string: str + + :rtype: type + """ + + if string in cls._TYPE_NAMES_BUILTIN: + return eval(string) + + module_ = sys.modules[context_class.__module__] + + if hasattr(module_, string): + return getattr(module_, string) + + return cls._str_to_type_from_member_module(module_, string) + + @classmethod + def _str_to_type_from_member_module(cls, module_, string): + """ + :type module_: module + :type string: str + + :rtype: type + :raise: BunqException when could not find the class for the string. + """ + + module_name_short, class_name = string.split(cls._DELIMITER_MODULE) + members = inspect.getmembers(module_, inspect.ismodule) + + for name, module_member in members: + if module_name_short == name: + return getattr(module_member, class_name) + + error_message = cls._ERROR_COULD_NOT_FIND_CLASS.format(string) + + raise exception.BunqException(error_message) + + @classmethod + def _deserialize_value(cls, types, value): + """ + :type types: ValueTypes + :type value: int|str|bool|float|bytes|unicode|list|dict + + :rtype: int|str|bool|float|bytes|unicode|list|dict|object + """ + + if types.main == list and value is not None: + return cls._deserialize_list(types.sub, value) + else: + return cls.deserialize(types.main, value) + + @classmethod + def _deserialize_list(cls, type_item, list_): + """ + :type type_item: T|type + :type list_: list + + :rtype: list[T] + """ + + list_deserialized = [] + + for item in list_: + item_deserialized = cls.deserialize(type_item, item) + list_deserialized.append(item_deserialized) + + return list_deserialized + + @classmethod + def can_serialize(cls): + """ + :rtype: bool + """ + + return True + + @classmethod + def serialize(cls, obj): + """ + :type obj: int|str|bool|float|bytes|unicode|list|dict|object + + :rtype: int|str|bool|list|dict + """ + + serializer = cls._get_serializer(type(obj)) + + if serializer == cls: + return cls._serialize_default(obj) + else: + return serializer.serialize(obj) + + @classmethod + def _serialize_default(cls, obj): + """ + :type obj: int|str|bool|float|bytes|unicode|list|dict|object + + :rtype: int|str|bool|list|dict + """ + + if obj is None or cls._is_primitive(obj): + return obj + elif cls._is_bytes(obj): + return obj.decode() + elif type(obj) == list: + return cls._serialize_list(obj) + else: + dict_ = cls._get_obj_raw(obj) + + return cls._serialize_dict(dict_) + + @classmethod + def _is_primitive(cls, obj): + """ + :type obj: int|str|bool|float|bytes|unicode|list|dict|object + + :rtype: bool + """ + + return cls._is_type_primitive(type(obj)) + + @classmethod + def _is_type_primitive(cls, type_): + """ + :type type_: type + + :rtype: bool + """ + + return type_ in {int, str, bool, float} + + @classmethod + def _is_bytes(cls, obj): + """ + :type obj: int|str|bool|float|bytes|unicode|list|dict|object + + :rtype: bool + """ + + return cls._is_bytes_type(type(obj)) + + @classmethod + def _is_bytes_type(cls, type_): + """ + :type type_: type + + :rtype: bool + """ + + return type_.__name__ in cls._TYPE_NAMES_BYTES + + @classmethod + def _serialize_list(cls, list_): + """ + :type list_: list + + :rtype: list + """ + + list_serialized = [] + + for item in list_: + item_serialized = cls.serialize(item) + list_serialized.append(item_serialized) + + return list_serialized + + @classmethod + def _get_obj_raw(cls, obj): + """ + :type obj: int|str|bool|float|bytes|unicode|list|dict|object + + :rtype: dict + """ + + return obj if type(obj) == dict else obj.__dict__ + + @classmethod + def _serialize_dict(cls, dict_): + """ + :type dict_ dict + + :rtype: dict + """ + + obj_serialized = {} + + for key in dict_.keys(): + item_serialized = cls.serialize(dict_[key]) + + if item_serialized is not None: + key = key.rstrip(cls._SUFFIX_KEY_OVERLAPPING) + key = key.lstrip(cls._PREFIX_KEY_PROTECTED) + obj_serialized[key] = item_serialized + + return obj_serialized + + +class ValueTypes(object): + """ + :type _main: type|None + :type _sub: type|None + """ + + def __init__(self, main, sub): + """ + :type main: type|None + :type sub: type|None + """ + + self._main = main + self._sub = sub + + @property + def main(self): + """ + :rtype: type|None + """ + + return self._main + + @property + def sub(self): + """ + :rtype: type|None + """ + + return self._sub + + +class ValueSpecs(object): + """ + :type _name: str|None + :type _types: ValueTypes|None + """ + + def __init__(self, name, types): + """ + :type name: str|None + :type types: ValueTypes|None + """ + + self._name = name + self._types = types + + @property + def name(self): + """ + :rtype: str|None + """ + + return self._name + + @property + def types(self): + """ + :rtype: ValueTypes|None + """ + + return self._types + + +def register_adapter(target_class, adapter): + """ + :type target_class: type + :type adapter: JsonAdapter|type + + :rtype: None + """ + + JsonAdapter.register_custom_adapter(target_class, adapter) + + +def json_to_class(cls, json_str): + """ + :type cls: T|type + :type json_str: str + + :rtype: T + """ + + obj_raw = json.loads(json_str) + + return deserialize(cls, obj_raw) + + +def deserialize(cls, obj_raw): + """ + :type cls: T|type + :type obj_raw: int|str|bool|float|list|dict|None + + :rtype: T + """ + + return JsonAdapter.deserialize(cls, obj_raw) + + +def class_to_json(obj): + """ + :type obj: int|str|bool|float|bytes|unicode|list|dict|object + + :rtype: int|str|bool|list|dict + """ + + obj_raw = serialize(obj) + + return json.dumps(obj_raw, indent=_JSON_INDENT, sort_keys=True) + + +def serialize(obj_cls): + """ + :type obj_cls: int|str|bool|float|bytes|unicode|list|dict|object + + :rtype: int|str|bool|list|dict + """ + + return JsonAdapter.serialize(obj_cls) diff --git a/bunq/sdk/json/registry.py b/bunq/sdk/json/registry.py new file mode 100644 index 0000000..2883c4b --- /dev/null +++ b/bunq/sdk/json/registry.py @@ -0,0 +1,38 @@ +import datetime + +from bunq.sdk import context +from bunq.sdk import model +from bunq.sdk.json import adapters +from bunq.sdk.json import converter +from bunq.sdk.model.generated import object_ + + +def initialize(): + """ + :rtype: None + """ + + converter.register_adapter(model.Installation, adapters.InstallationAdapter) + converter.register_adapter( + model.SessionServer, + adapters.SessionServerAdapter + ) + converter.register_adapter( + context.InstallationContext, + adapters.InstallationContextAdapter + ) + converter.register_adapter( + context.ApiEnvironmentType, + adapters.ApiEnvironmentTypeAdapter + ) + converter.register_adapter(float, adapters.FloatAdapter) + converter.register_adapter( + object_.Geolocation, + adapters.GeolocationAdapter + ) + converter.register_adapter( + object_.MonetaryAccountReference, + adapters.MonetaryAccountReferenceAdapter + ) + converter.register_adapter(object_.ShareDetail, adapters.ShareDetailAdapter) + converter.register_adapter(datetime.datetime, adapters.DateTimeAdapter) diff --git a/bunq/sdk/model/__init__.py b/bunq/sdk/model/__init__.py new file mode 100644 index 0000000..ff4ffd1 --- /dev/null +++ b/bunq/sdk/model/__init__.py @@ -0,0 +1 @@ +from bunq.sdk.model.model import * diff --git a/bunq/sdk/model/generated/__init__.py b/bunq/sdk/model/generated/__init__.py new file mode 100644 index 0000000..faea808 --- /dev/null +++ b/bunq/sdk/model/generated/__init__.py @@ -0,0 +1 @@ +from bunq.sdk.model.generated.endpoint import * diff --git a/bunq/sdk/model/generated/endpoint.py b/bunq/sdk/model/generated/endpoint.py new file mode 100644 index 0000000..6882181 --- /dev/null +++ b/bunq/sdk/model/generated/endpoint.py @@ -0,0 +1,9894 @@ +# -*- coding: utf-8 -*- +from bunq.sdk import client +from bunq.sdk import context +from bunq.sdk import model +from bunq.sdk import security +from bunq.sdk.json import converter +from bunq.sdk.model.generated import object_ + + +class Invoice(model.BunqModel): + """ + Used to view a bunq invoice. + + :type _id_: int + :type _created: str + :type _updated: str + :type _invoice_date: str + :type _invoice_number: str + :type _status: str + :type _group: list[object_.InvoiceItemGroup] + :type _total_vat_inclusive: object_.Amount + :type _total_vat_exclusive: object_.Amount + :type _total_vat: object_.Amount + :type _alias: object_.MonetaryAccountReference + :type _address: object_.Address + :type _counterparty_alias: object_.MonetaryAccountReference + :type _counterparty_address: object_.Address + :type _chamber_of_commerce_number: str + :type _vat_number: str + """ + + # Field constants. + FIELD_STATUS = "status" + FIELD_DESCRIPTION = "description" + FIELD_EXTERNAL_URL = "external_url" + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/invoice" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/invoice/{}" + + # Object type. + _OBJECT_TYPE = "Invoice" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._invoice_date = None + self._invoice_number = None + self._status = None + self._group = None + self._total_vat_inclusive = None + self._total_vat_exclusive = None + self._total_vat = None + self._alias = None + self._address = None + self._counterparty_alias = None + self._counterparty_address = None + self._chamber_of_commerce_number = None + self._vat_number = None + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[Invoice] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, invoice_id, + custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type invoice_id: int + :type custom_headers: dict[str, str]|None + + :rtype: Invoice + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + invoice_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def invoice_date(self): + """ + :rtype: str + """ + + return self._invoice_date + + @property + def invoice_number(self): + """ + :rtype: str + """ + + return self._invoice_number + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def group(self): + """ + :rtype: list[object_.InvoiceItemGroup] + """ + + return self._group + + @property + def total_vat_inclusive(self): + """ + :rtype: object_.Amount + """ + + return self._total_vat_inclusive + + @property + def total_vat_exclusive(self): + """ + :rtype: object_.Amount + """ + + return self._total_vat_exclusive + + @property + def total_vat(self): + """ + :rtype: object_.Amount + """ + + return self._total_vat + + @property + def alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._alias + + @property + def address(self): + """ + :rtype: object_.Address + """ + + return self._address + + @property + def counterparty_alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._counterparty_alias + + @property + def counterparty_address(self): + """ + :rtype: object_.Address + """ + + return self._counterparty_address + + @property + def chamber_of_commerce_number(self): + """ + :rtype: str + """ + + return self._chamber_of_commerce_number + + @property + def vat_number(self): + """ + :rtype: str + """ + + return self._vat_number + + +class InvoiceByUser(model.BunqModel): + """ + Used to list bunq invoices by user. + + :type _id_: int + :type _created: str + :type _updated: str + :type _invoice_date: str + :type _invoice_number: str + :type _status: str + :type _group: list[object_.InvoiceItemGroup] + :type _total_vat_inclusive: object_.Amount + :type _total_vat_exclusive: object_.Amount + :type _total_vat: object_.Amount + :type _alias: object_.MonetaryAccountReference + :type _address: object_.Address + :type _counterparty_alias: object_.MonetaryAccountReference + :type _counterparty_address: object_.Address + :type _chamber_of_commerce_number: str + :type _vat_number: str + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "user/{}/invoice" + _ENDPOINT_URL_READ = "user/{}/invoice/{}" + + # Object type. + _OBJECT_TYPE = "Invoice" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._invoice_date = None + self._invoice_number = None + self._status = None + self._group = None + self._total_vat_inclusive = None + self._total_vat_exclusive = None + self._total_vat = None + self._alias = None + self._address = None + self._counterparty_alias = None + self._counterparty_address = None + self._chamber_of_commerce_number = None + self._vat_number = None + + @classmethod + def list(cls, api_context, user_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[InvoiceByUser] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @classmethod + def get(cls, api_context, user_id, invoice_by_user_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type invoice_by_user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: InvoiceByUser + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + invoice_by_user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def invoice_date(self): + """ + :rtype: str + """ + + return self._invoice_date + + @property + def invoice_number(self): + """ + :rtype: str + """ + + return self._invoice_number + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def group(self): + """ + :rtype: list[object_.InvoiceItemGroup] + """ + + return self._group + + @property + def total_vat_inclusive(self): + """ + :rtype: object_.Amount + """ + + return self._total_vat_inclusive + + @property + def total_vat_exclusive(self): + """ + :rtype: object_.Amount + """ + + return self._total_vat_exclusive + + @property + def total_vat(self): + """ + :rtype: object_.Amount + """ + + return self._total_vat + + @property + def alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._alias + + @property + def address(self): + """ + :rtype: object_.Address + """ + + return self._address + + @property + def counterparty_alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._counterparty_alias + + @property + def counterparty_address(self): + """ + :rtype: object_.Address + """ + + return self._counterparty_address + + @property + def chamber_of_commerce_number(self): + """ + :rtype: str + """ + + return self._chamber_of_commerce_number + + @property + def vat_number(self): + """ + :rtype: str + """ + + return self._vat_number + + +class ChatConversation(model.BunqModel): + """ + Manages user's conversations. + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "user/{}/chat-conversation" + _ENDPOINT_URL_READ = "user/{}/chat-conversation/{}" + + # Object type. + _OBJECT_TYPE = "ChatConversation" + + @classmethod + def list(cls, api_context, user_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[ChatConversation] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @classmethod + def get(cls, api_context, user_id, chat_conversation_id, + custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type chat_conversation_id: int + :type custom_headers: dict[str, str]|None + + :rtype: ChatConversation + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + chat_conversation_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + +class ChatMessageAttachment(model.BunqModel): + """ + Create new messages holding file attachments. + + :type _id_: int + """ + + # Field constants. + FIELD_CLIENT_MESSAGE_UUID = "client_message_uuid" + FIELD_ATTACHMENT = "attachment" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/chat-conversation/{}/message-attachment" + + # Object type. + _OBJECT_TYPE = "Id" + + def __init__(self): + self._id_ = None + + @classmethod + def create(cls, api_context, request_map, user_id, chat_conversation_id, + custom_headers=None): + """ + Create a new message holding a file attachment to a specific + conversation. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type chat_conversation_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + chat_conversation_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + +class ChatMessageText(model.BunqModel): + """ + Endpoint for the type of chat message that carries text. + + :type _id_: int + """ + + # Field constants. + FIELD_CLIENT_MESSAGE_UUID = "client_message_uuid" + FIELD_TEXT = "text" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/chat-conversation/{}/message-text" + + # Object type. + _OBJECT_TYPE = "Id" + + def __init__(self): + self._id_ = None + + @classmethod + def create(cls, api_context, request_map, user_id, chat_conversation_id, + custom_headers=None): + """ + Add a new text message to a specific conversation. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type chat_conversation_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + chat_conversation_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + +class ChatMessage(model.BunqModel): + """ + Endpoint for retrieving the messages that are part of a conversation. + + :type _id_: int + :type _created: str + :type _updated: str + :type _conversation_id: int + :type _ticket_id: int + :type _creator: object_.LabelUser + :type _displayed_sender: object_.LabelUser + :type _content: model.BunqModel + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "user/{}/chat-conversation/{}/message" + + # Object type. + _OBJECT_TYPE = "ChatMessage" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._conversation_id = None + self._ticket_id = None + self._creator = None + self._displayed_sender = None + self._content = None + + @classmethod + def list(cls, api_context, user_id, chat_conversation_id, + custom_headers=None): + """ + Get all the messages that are part of a specific conversation. + + :type api_context: context.ApiContext + :type user_id: int + :type chat_conversation_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[ChatMessage] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + chat_conversation_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def conversation_id(self): + """ + :rtype: int + """ + + return self._conversation_id + + @property + def ticket_id(self): + """ + :rtype: int + """ + + return self._ticket_id + + @property + def creator(self): + """ + :rtype: object_.LabelUser + """ + + return self._creator + + @property + def displayed_sender(self): + """ + :rtype: object_.LabelUser + """ + + return self._displayed_sender + + @property + def content(self): + """ + :rtype: model.BunqModel + """ + + return self._content + + +class AttachmentConversationContent(model.BunqModel): + """ + Fetch the raw content of an attachment with given ID. The raw content is the + base64 of a file, without any JSON wrapping. + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "user/{}/chat-conversation/{}/attachment/{}/content" + + # Object type. + _OBJECT_TYPE = "AttachmentConversationContent" + + @classmethod + def list(cls, api_context, user_id, chat_conversation_id, attachment_id, + custom_headers=None): + """ + Get the raw content of a specific attachment. + + :type api_context: context.ApiContext + :type user_id: int + :type chat_conversation_id: int + :type attachment_id: int + :type custom_headers: dict[str, str]|None + + :rtype: bytes + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + chat_conversation_id, + attachment_id) + + return api_client.get(endpoint_url, custom_headers).content + + +class AttachmentPublicContent(model.BunqModel): + """ + Fetch the raw content of a public attachment with given ID. The raw content + is the binary representation of a file, without any JSON wrapping. + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "attachment-public/{}/content" + + # Object type. + _OBJECT_TYPE = "AttachmentPublicContent" + + @classmethod + def list(cls, api_context, attachment_public_uuid, custom_headers=None): + """ + Get the raw content of a specific attachment. + + :type api_context: context.ApiContext + :type attachment_public_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype: bytes + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(attachment_public_uuid) + + return api_client.get(endpoint_url, custom_headers).content + + +class AttachmentTabContent(model.BunqModel): + """ + Fetch the raw content of a tab attachment with given ID. The raw content is + the binary representation of a file, without any JSON wrapping. + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/attachment-tab/{" \ + "}/content" + + # Object type. + _OBJECT_TYPE = "AttachmentTabContent" + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, attachment_tab_id, + custom_headers=None): + """ + Get the raw content of a specific attachment. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type attachment_tab_id: int + :type custom_headers: dict[str, str]|None + + :rtype: bytes + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + attachment_tab_id) + + return api_client.get(endpoint_url, custom_headers).content + + +class TabAttachmentTabContent(model.BunqModel): + """ + Fetch the raw content of a tab attachment with given ID. The raw content is + the binary representation of a file, without any JSON wrapping. + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "tab/{}/attachment/{}/content" + + # Object type. + _OBJECT_TYPE = "TabAttachmentTabContent" + + @classmethod + def list(cls, api_context, tab_uuid, attachment_id, custom_headers=None): + """ + Get the raw content of a specific attachment. + + :type api_context: context.ApiContext + :type tab_uuid: str + :type attachment_id: int + :type custom_headers: dict[str, str]|None + + :rtype: bytes + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(tab_uuid, attachment_id) + + return api_client.get(endpoint_url, custom_headers).content + + +class AttachmentMonetaryAccount(model.BunqModel): + """ + This call is used to upload an attachment that can be referenced to in + payment requests and payments sent from a specific monetary account. + Attachments supported are png, jpg and gif. + + :type _attachment: object_.Attachment + :type _id_: int + """ + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/attachment" + + # Object type. + _OBJECT_TYPE = "AttachmentMonetaryAccount" + + def __init__(self): + self._attachment = None + self._id_ = None + + @classmethod + def create(cls, api_context, request_bytes, user_id, monetary_account_id, + custom_headers=None): + """ + Create a new monetary account attachment. Create a POST request with a + payload that contains the binary representation of the file, without any + JSON wrapping. Make sure you define the MIME type (i.e. image/jpeg) in + the Content-Type header. You are required to provide a description of + the attachment using the X-Bunq-Attachment-Description header. + + :type api_context: context.ApiContext + :type request_bytes: bytes + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @property + def attachment(self): + """ + :rtype: object_.Attachment + """ + + return self._attachment + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + +class AttachmentPublic(model.BunqModel): + """ + This call is used to upload an attachment that can be referenced to as an + avatar (through the Avatar endpoint) or in a tab sent. Attachments supported + are png, jpg and gif. + + :type _uuid: str + :type _created: str + :type _updated: str + :type _attachment: object_.Attachment + """ + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "attachment-public" + _ENDPOINT_URL_READ = "attachment-public/{}" + + # Object type. + _OBJECT_TYPE = "AttachmentPublic" + + def __init__(self): + self._uuid = None + self._created = None + self._updated = None + self._attachment = None + + @classmethod + def create(cls, api_context, request_bytes, custom_headers=None): + """ + Create a new public attachment. Create a POST request with a payload + that contains a binary representation of the file, without any JSON + wrapping. Make sure you define the MIME type (i.e. image/jpeg, or + image/png) in the Content-Type header. You are required to provide a + description of the attachment using the X-Bunq-Attachment-Description + header. + + :type api_context: context.ApiContext + :type request_bytes: bytes + :type custom_headers: dict[str, str]|None + + :rtype: str + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_CREATE + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_uuid(response.text) + + @classmethod + def get(cls, api_context, attachment_public_uuid, custom_headers=None): + """ + Get a specific attachment's metadata through its UUID. The Content-Type + header of the response will describe the MIME type of the attachment + file. + + :type api_context: context.ApiContext + :type attachment_public_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype: AttachmentPublic + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(attachment_public_uuid) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @property + def uuid(self): + """ + :rtype: str + """ + + return self._uuid + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def attachment(self): + """ + :rtype: object_.Attachment + """ + + return self._attachment + + +class AttachmentTab(model.BunqModel): + """ + This call is used to upload an attachment that will be accessible only + through tabs. This can be used for example to upload special promotions or + other attachments. Attachments supported are png, jpg and gif. + + :type _id_: int + :type _created: str + :type _updated: str + :type _attachment: object_.Attachment + """ + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/attachment-tab" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/attachment-tab/{}" + + # Object type. + _OBJECT_TYPE = "AttachmentTab" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._attachment = None + + @classmethod + def create(cls, api_context, request_bytes, user_id, monetary_account_id, + custom_headers=None): + """ + Upload a new attachment to use with a tab, and to read its metadata. + Create a POST request with a payload that contains the binary + representation of the file, without any JSON wrapping. Make sure you + define the MIME type (i.e. image/jpeg) in the Content-Type header. You + are required to provide a description of the attachment using the + X-Bunq-Attachment-Description header. + + :type api_context: context.ApiContext + :type request_bytes: bytes + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, attachment_tab_id, + custom_headers=None): + """ + Get a specific attachment. The header of the response contains the + content-type of the attachment. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type attachment_tab_id: int + :type custom_headers: dict[str, str]|None + + :rtype: AttachmentTab + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + attachment_tab_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def attachment(self): + """ + :rtype: object_.Attachment + """ + + return self._attachment + + +class TabAttachmentTab(model.BunqModel): + """ + This call is used to view an attachment that is linked to a tab. + + :type _id_: int + :type _created: str + :type _updated: str + :type _attachment: object_.Attachment + """ + + # Endpoint constants. + _ENDPOINT_URL_READ = "tab/{}/attachment/{}" + + # Object type. + _OBJECT_TYPE = "TabAttachmentTab" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._attachment = None + + @classmethod + def get(cls, api_context, tab_uuid, tab_attachment_tab_id, + custom_headers=None): + """ + Get a specific attachment. The header of the response contains the + content-type of the attachment. + + :type api_context: context.ApiContext + :type tab_uuid: str + :type tab_attachment_tab_id: int + :type custom_headers: dict[str, str]|None + + :rtype: TabAttachmentTab + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(tab_uuid, + tab_attachment_tab_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def attachment(self): + """ + :rtype: object_.Attachment + """ + + return self._attachment + + +class Avatar(model.BunqModel): + """ + Avatars are public images used to represent you or your company. Avatars are + used to represent users, monetary accounts and cash registers. Avatars + cannot be deleted, only replaced. Avatars can be updated after uploading the + image you would like to use through AttachmentPublic. Using the + attachment_public_uuid which is returned you can update your Avatar. Avatars + used for cash registers and company accounts will be reviewed by bunq. + + :type _uuid: str + :type _image: list[object_.Image] + """ + + # Field constants. + FIELD_ATTACHMENT_PUBLIC_UUID = "attachment_public_uuid" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "avatar" + _ENDPOINT_URL_READ = "avatar/{}" + + # Object type. + _OBJECT_TYPE = "Avatar" + + def __init__(self): + self._uuid = None + self._image = None + + @classmethod + def create(cls, api_context, request_map, custom_headers=None): + """ + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type custom_headers: dict[str, str]|None + + :rtype: str + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_uuid(response.text) + + @classmethod + def get(cls, api_context, avatar_uuid, custom_headers=None): + """ + :type api_context: context.ApiContext + :type avatar_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype: Avatar + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(avatar_uuid) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @property + def uuid(self): + """ + :rtype: str + """ + + return self._uuid + + @property + def image(self): + """ + :rtype: list[object_.Image] + """ + + return self._image + + +class CardDebit(model.BunqModel): + """ + With bunq it is possible to order debit cards that can then be connected + with each one of the monetary accounts the user has access to (including + connected accounts). + + :type _id_: int + :type _created: str + :type _updated: str + :type _second_line: str + :type _name_on_card: str + :type _status: str + :type _order_status: str + :type _expiry_date: str + :type _limit: list[object_.CardLimit] + :type _country_permission: list[object_.CardCountryPermission] + :type _alias: object_.LabelUser + """ + + # Field constants. + FIELD_SECOND_LINE = "second_line" + FIELD_NAME_ON_CARD = "name_on_card" + FIELD_PIN_CODE = "pin_code" + FIELD_ALIAS = "alias" + FIELD_TYPE = "type" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/card-debit" + + # Object type. + _OBJECT_TYPE = "CardDebit" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._second_line = None + self._name_on_card = None + self._status = None + self._order_status = None + self._expiry_date = None + self._limit = None + self._country_permission = None + self._alias = None + + @classmethod + def create(cls, api_context, request_map, user_id, custom_headers=None): + """ + Create a new debit card request. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: CardDebit + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + request_bytes = security.encrypt(api_context, request_bytes, + custom_headers) + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def second_line(self): + """ + :rtype: str + """ + + return self._second_line + + @property + def name_on_card(self): + """ + :rtype: str + """ + + return self._name_on_card + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def order_status(self): + """ + :rtype: str + """ + + return self._order_status + + @property + def expiry_date(self): + """ + :rtype: str + """ + + return self._expiry_date + + @property + def limit(self): + """ + :rtype: list[object_.CardLimit] + """ + + return self._limit + + @property + def country_permission(self): + """ + :rtype: list[object_.CardCountryPermission] + """ + + return self._country_permission + + @property + def alias(self): + """ + :rtype: object_.LabelUser + """ + + return self._alias + + +class CardName(model.BunqModel): + """ + Endpoint for getting all the accepted card names for a user. As bunq do not + allow total freedom in choosing the name that is going to be printed on the + card, the following formats are accepted: Name Surname, N. Surname, N + Surname or Surname. + + :type _possible_card_name_array: list[str] + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "user/{}/card-name" + + # Object type. + _OBJECT_TYPE = "CardUserNameArray" + + def __init__(self): + self._possible_card_name_array = None + + @classmethod + def list(cls, api_context, user_id, custom_headers=None): + """ + Return all the accepted card names for a specific user. + + :type api_context: context.ApiContext + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[CardName] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def possible_card_name_array(self): + """ + :rtype: list[str] + """ + + return self._possible_card_name_array + + +class Card(model.BunqModel): + """ + Endpoint for retrieving details for the cards the user has access to. + + :type _id_: int + :type _created: str + :type _updated: str + :type _public_uuid: str + :type _second_line: str + :type _status: str + :type _order_status: str + :type _expiry_date: str + :type _name_on_card: str + :type _primary_account_number_four_digit: str + :type _limit: list[object_.CardLimit] + :type _mag_stripe_permission: object_.CardMagStripePermission + :type _country_permission: list[object_.CardCountryPermission] + :type _label_monetary_account_ordered: object_.MonetaryAccountReference + :type _label_monetary_account_current: object_.MonetaryAccountReference + """ + + # Field constants. + FIELD_PIN_CODE = "pin_code" + FIELD_ACTIVATION_CODE = "activation_code" + FIELD_STATUS = "status" + FIELD_LIMIT = "limit" + FIELD_MAG_STRIPE_PERMISSION = "mag_stripe_permission" + FIELD_COUNTRY_PERMISSION = "country_permission" + FIELD_MONETARY_ACCOUNT_CURRENT_ID = "monetary_account_current_id" + + # Endpoint constants. + _ENDPOINT_URL_UPDATE = "user/{}/card/{}" + _ENDPOINT_URL_READ = "user/{}/card/{}" + _ENDPOINT_URL_LISTING = "user/{}/card" + + # Object type. + _OBJECT_TYPE = "CardDebit" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._public_uuid = None + self._second_line = None + self._status = None + self._order_status = None + self._expiry_date = None + self._name_on_card = None + self._primary_account_number_four_digit = None + self._limit = None + self._mag_stripe_permission = None + self._country_permission = None + self._label_monetary_account_ordered = None + self._label_monetary_account_current = None + + @classmethod + def update(cls, api_context, request_map, user_id, card_id, + custom_headers=None): + """ + Update the card details. Allow to change pin code, status, limits, + country permissions and the monetary account connected to the card. When + the card has been received, it can be also activated through this + endpoint. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type card_id: int + :type custom_headers: dict[str, str]|None + + :rtype: Card + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + request_bytes = security.encrypt(api_context, request_bytes, + custom_headers) + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, card_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def get(cls, api_context, user_id, card_id, custom_headers=None): + """ + Return the details of a specific card. + + :type api_context: context.ApiContext + :type user_id: int + :type card_id: int + :type custom_headers: dict[str, str]|None + + :rtype: Card + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, card_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, custom_headers=None): + """ + Return all the cards available to the user. + + :type api_context: context.ApiContext + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[Card] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def public_uuid(self): + """ + :rtype: str + """ + + return self._public_uuid + + @property + def second_line(self): + """ + :rtype: str + """ + + return self._second_line + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def order_status(self): + """ + :rtype: str + """ + + return self._order_status + + @property + def expiry_date(self): + """ + :rtype: str + """ + + return self._expiry_date + + @property + def name_on_card(self): + """ + :rtype: str + """ + + return self._name_on_card + + @property + def primary_account_number_four_digit(self): + """ + :rtype: str + """ + + return self._primary_account_number_four_digit + + @property + def limit(self): + """ + :rtype: list[object_.CardLimit] + """ + + return self._limit + + @property + def mag_stripe_permission(self): + """ + :rtype: object_.CardMagStripePermission + """ + + return self._mag_stripe_permission + + @property + def country_permission(self): + """ + :rtype: list[object_.CardCountryPermission] + """ + + return self._country_permission + + @property + def label_monetary_account_ordered(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._label_monetary_account_ordered + + @property + def label_monetary_account_current(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._label_monetary_account_current + + +class CashRegisterQrCodeContent(model.BunqModel): + """ + Show the raw contents of a QR code. First you need to created a QR code + using ../cash-register/{id}/qr-code. + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/cash-register/{" \ + "}/qr-code/{}/content" + + # Object type. + _OBJECT_TYPE = "CashRegisterQrCodeContent" + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, cash_register_id, + qr_code_id, custom_headers=None): + """ + Show the raw contents of a QR code + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type qr_code_id: int + :type custom_headers: dict[str, str]|None + + :rtype: bytes + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + cash_register_id, + qr_code_id) + + return api_client.get(endpoint_url, custom_headers).content + + +class CashRegisterQrCode(model.BunqModel): + """ + Once your CashRegister has been activated you can create a QR code for it. + The visibility of a tab can be modified to be linked to this QR code. If a + user of the bunq app scans this QR code, the linked tab will be shown on his + device. + + :type _id_: int + :type _created: str + :type _updated: str + :type _status: str + :type _cash_register: CashRegister + :type _tab_object: Tab + """ + + # Field constants. + FIELD_STATUS = "status" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/cash-register/{" \ + "}/qr-code" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{}/cash-register/{" \ + "}/qr-code/{}" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/cash-register/{" \ + "}/qr-code/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/cash-register/{" \ + "}/qr-code" + + # Object type. + _OBJECT_TYPE = "TokenQrCashRegister" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._status = None + self._cash_register = None + self._tab_object = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + cash_register_id, custom_headers=None): + """ + Create a new QR code for this CashRegister. You can only have one ACTIVE + CashRegister QR code at the time. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id, + cash_register_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + cash_register_id, cash_register_qr_code_id, custom_headers=None): + """ + Modify a QR code in a given CashRegister. You can only have one ACTIVE + CashRegister QR code at the time. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type cash_register_qr_code_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + cash_register_id, + cash_register_qr_code_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, cash_register_id, + cash_register_qr_code_id, custom_headers=None): + """ + Get the information of a specific QR code. To get the RAW content of the + QR code use ../qr-code/{id}/content + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type cash_register_qr_code_id: int + :type custom_headers: dict[str, str]|None + + :rtype: CashRegisterQrCode + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + cash_register_id, + cash_register_qr_code_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, cash_register_id, + custom_headers=None): + """ + Get a collection of QR code information from a given CashRegister + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[CashRegisterQrCode] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + cash_register_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def cash_register(self): + """ + :rtype: CashRegister + """ + + return self._cash_register + + @property + def tab_object(self): + """ + :rtype: Tab + """ + + return self._tab_object + + +class CashRegister(model.BunqModel): + """ + CashRegisters act as an point of sale. They have a specific name and avatar, + and optionally a location. A CashRegister is used to create Tabs. A + CashRegister can have an QR code that links to one of its Tabs. + + :type _id_: int + :type _created: str + :type _updated: str + :type _name: str + :type _status: str + :type _avatar: object_.Avatar + :type _location: object_.Geolocation + :type _notification_filters: list[object_.NotificationFilter] + :type _tab_text_waiting_screen: list[object_.TabTextWaitingScreen] + """ + + # Field constants. + FIELD_NAME = "name" + FIELD_STATUS = "status" + FIELD_AVATAR_UUID = "avatar_uuid" + FIELD_LOCATION = "location" + FIELD_NOTIFICATION_FILTERS = "notification_filters" + FIELD_TAB_TEXT_WAITING_SCREEN = "tab_text_waiting_screen" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/cash-register" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/cash-register/{}" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{}/cash-register/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/cash-register" + + # Object type. + _OBJECT_TYPE = "CashRegister" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._name = None + self._status = None + self._avatar = None + self._location = None + self._notification_filters = None + self._tab_text_waiting_screen = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + custom_headers=None): + """ + Create a new CashRegister. Only an UserCompany can create a + CashRegisters. They need to be created with status PENDING_APPROVAL, an + bunq admin has to approve your CashRegister before you can use it. In + the sandbox testing environment an CashRegister will be automatically + approved immediately after creation. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, cash_register_id, + custom_headers=None): + """ + Get a specific CashRegister. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type custom_headers: dict[str, str]|None + + :rtype: CashRegister + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + cash_register_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + cash_register_id, custom_headers=None): + """ + Modify or close an existing CashRegister. You must set the status back + to PENDING_APPROVAL if you modify the name, avatar or location of a + CashRegister. To close a cash register put its status to CLOSED. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + cash_register_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + custom_headers=None): + """ + Get a collection of CashRegister for a given user and monetary account. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[CashRegister] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def name(self): + """ + :rtype: str + """ + + return self._name + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def avatar(self): + """ + :rtype: object_.Avatar + """ + + return self._avatar + + @property + def location(self): + """ + :rtype: object_.Geolocation + """ + + return self._location + + @property + def notification_filters(self): + """ + :rtype: list[object_.NotificationFilter] + """ + + return self._notification_filters + + @property + def tab_text_waiting_screen(self): + """ + :rtype: list[object_.TabTextWaitingScreen] + """ + + return self._tab_text_waiting_screen + + +class Tab(model.BunqModel): + """ + Once your CashRegister has been activated you can use it to create Tabs. A + Tab is a template for a payment. In contrast to requests a Tab is not + pointed towards a specific user. Any user can pay the Tab as long as it is + made visible by you. The creation of a Tab happens with /tab-usage-single or + /tab-usage-multiple. A TabUsageSingle is a Tab that can be paid once. A + TabUsageMultiple is a Tab that can be paid multiple times by different + users. + + :type _TabUsageSingle: TabUsageSingle + :type _TabUsageMultiple: TabUsageMultiple + """ + + # Endpoint constants. + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/cash-register/{}/tab/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/cash-register/{}/tab" + + # Object type. + _OBJECT_TYPE = "Tab" + + def __init__(self): + self._TabUsageSingle = None + self._TabUsageMultiple = None + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, cash_register_id, + tab_uuid, custom_headers=None): + """ + Get a specific tab. This returns a TabUsageSingle or TabUsageMultiple. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype: Tab + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + cash_register_id, tab_uuid) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, cash_register_id, + custom_headers=None): + """ + Get a collection of tabs. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[Tab] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + cash_register_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text) + + @property + def TabUsageSingle(self): + """ + :rtype: TabUsageSingle + """ + + return self._TabUsageSingle + + @property + def TabUsageMultiple(self): + """ + :rtype: TabUsageMultiple + """ + + return self._TabUsageMultiple + + +class TabUsageSingle(model.BunqModel): + """ + TabUsageSingle is a Tab that can be paid once. The TabUsageSingle is created + with the status OPEN. Optionally you can add TabItems to the tab using + /tab/_/tab-item, TabItems don't affect the total amount of the Tab. However, + if you've created any TabItems for a Tab the sum of the amounts of these + items must be equal to the total_amount of the Tab when you change its + status to WAITING_FOR_PAYMENT. By setting the visibility object a + TabUsageSingle with the status OPEN or WAITING_FOR_PAYMENT can be made + visible to customers. As soon as a customer pays the TabUsageSingle its + status changes to PAID, and it can't be paid again. + + :type _uuid: str + :type _created: str + :type _updated: str + :type _merchant_reference: str + :type _description: str + :type _status: str + :type _amount_total: object_.Amount + :type _amount_paid: object_.Amount + :type _qr_code_token: str + :type _tab_url: str + :type _visibility: object_.TabVisibility + :type _minimum_age: bool + :type _require_address: str + :type _redirect_url: str + :type _expiration: str + :type _alias: object_.MonetaryAccountReference + :type _cash_register_location: object_.Geolocation + :type _tab_item: list[TabItem] + :type _tab_attachment: list[object_.BunqId] + """ + + # Field constants. + FIELD_MERCHANT_REFERENCE = "merchant_reference" + FIELD_DESCRIPTION = "description" + FIELD_STATUS = "status" + FIELD_AMOUNT_TOTAL = "amount_total" + FIELD_ALLOW_AMOUNT_HIGHER = "allow_amount_higher" + FIELD_ALLOW_AMOUNT_LOWER = "allow_amount_lower" + FIELD_WANT_TIP = "want_tip" + FIELD_MINIMUM_AGE = "minimum_age" + FIELD_REQUIRE_ADDRESS = "require_address" + FIELD_REDIRECT_URL = "redirect_url" + FIELD_VISIBILITY = "visibility" + FIELD_EXPIRATION = "expiration" + FIELD_TAB_ATTACHMENT = "tab_attachment" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab-usage-single" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab-usage-single/{}" + _ENDPOINT_URL_DELETE = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab-usage-single/{}" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab-usage-single/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab-usage-single" + + # Object type. + _OBJECT_TYPE = "TabUsageSingle" + + def __init__(self): + self._uuid = None + self._created = None + self._updated = None + self._merchant_reference = None + self._description = None + self._status = None + self._amount_total = None + self._amount_paid = None + self._qr_code_token = None + self._tab_url = None + self._visibility = None + self._minimum_age = None + self._require_address = None + self._redirect_url = None + self._expiration = None + self._alias = None + self._cash_register_location = None + self._tab_item = None + self._tab_attachment = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + cash_register_id, custom_headers=None): + """ + Create a TabUsageSingle. The initial status must be OPEN + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type custom_headers: dict[str, str]|None + + :rtype: str + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id, + cash_register_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_uuid(response.text) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + cash_register_id, tab_usage_single_uuid, custom_headers=None): + """ + Modify a specific TabUsageSingle. You can change the amount_total, + status and visibility. Once you change the status to WAITING_FOR_PAYMENT + the TabUsageSingle will expire after 5 minutes (default) or up to 1 hour + if a different expiration is provided. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_usage_single_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype: str + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + cash_register_id, + tab_usage_single_uuid) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_uuid(response.text) + + @classmethod + def delete(cls, api_context, user_id, monetary_account_id, cash_register_id, + tab_usage_single_uuid, custom_headers=None): + """ + Cancel a specific TabUsageSingle. This request returns an empty + response. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_usage_single_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype None: + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_DELETE.format(user_id, + monetary_account_id, + cash_register_id, + tab_usage_single_uuid) + api_client.delete(endpoint_url, custom_headers) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, cash_register_id, + tab_usage_single_uuid, custom_headers=None): + """ + Get a specific TabUsageSingle. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_usage_single_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype: TabUsageSingle + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + cash_register_id, + tab_usage_single_uuid) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, cash_register_id, + custom_headers=None): + """ + Get a collection of TabUsageSingle. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[TabUsageSingle] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + cash_register_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def uuid(self): + """ + :rtype: str + """ + + return self._uuid + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def merchant_reference(self): + """ + :rtype: str + """ + + return self._merchant_reference + + @property + def description(self): + """ + :rtype: str + """ + + return self._description + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def amount_total(self): + """ + :rtype: object_.Amount + """ + + return self._amount_total + + @property + def amount_paid(self): + """ + :rtype: object_.Amount + """ + + return self._amount_paid + + @property + def qr_code_token(self): + """ + :rtype: str + """ + + return self._qr_code_token + + @property + def tab_url(self): + """ + :rtype: str + """ + + return self._tab_url + + @property + def visibility(self): + """ + :rtype: object_.TabVisibility + """ + + return self._visibility + + @property + def minimum_age(self): + """ + :rtype: bool + """ + + return self._minimum_age + + @property + def require_address(self): + """ + :rtype: str + """ + + return self._require_address + + @property + def redirect_url(self): + """ + :rtype: str + """ + + return self._redirect_url + + @property + def expiration(self): + """ + :rtype: str + """ + + return self._expiration + + @property + def alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._alias + + @property + def cash_register_location(self): + """ + :rtype: object_.Geolocation + """ + + return self._cash_register_location + + @property + def tab_item(self): + """ + :rtype: list[TabItem] + """ + + return self._tab_item + + @property + def tab_attachment(self): + """ + :rtype: list[object_.BunqId] + """ + + return self._tab_attachment + + +class TabItem(model.BunqModel): + """ + Used to get items on a tab. + + :type _id_: int + :type _description: str + :type _ean_code: str + :type _avatar_attachment: object_.AttachmentPublic + :type _tab_attachment: list[object_.AttachmentTab] + :type _quantity: str + :type _amount: object_.Amount + """ + + # Object type. + _OBJECT_TYPE = "TabItem" + + def __init__(self): + self._id_ = None + self._description = None + self._ean_code = None + self._avatar_attachment = None + self._tab_attachment = None + self._quantity = None + self._amount = None + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def description(self): + """ + :rtype: str + """ + + return self._description + + @property + def ean_code(self): + """ + :rtype: str + """ + + return self._ean_code + + @property + def avatar_attachment(self): + """ + :rtype: object_.AttachmentPublic + """ + + return self._avatar_attachment + + @property + def tab_attachment(self): + """ + :rtype: list[object_.AttachmentTab] + """ + + return self._tab_attachment + + @property + def quantity(self): + """ + :rtype: str + """ + + return self._quantity + + @property + def amount(self): + """ + :rtype: object_.Amount + """ + + return self._amount + + +class TabUsageMultiple(model.BunqModel): + """ + TabUsageMultiple is a Tab that can be paid by multiple users. Just like the + TabUsageSingle it is created with the status OPEN, the visibility can be + defined in the visibility object and TabItems can be added as long as the + status is OPEN. When you change the status to PAYABLE any bunq user can use + the tab to make a payment to your account. After an user has paid your + TabUsageMultiple the status will not change, it will stay PAYABLE. For + example: you can create a TabUsageMultiple with require_address set to true. + Now show the QR code of this Tab on your webshop, and any bunq user can + instantly pay and order something from your webshop. + + :type _uuid: str + :type _created: str + :type _updated: str + :type _description: str + :type _status: str + :type _amount_total: object_.Amount + :type _qr_code_token: str + :type _tab_url: str + :type _visibility: object_.TabVisibility + :type _minimum_age: bool + :type _require_address: str + :type _redirect_url: str + :type _expiration: str + :type _alias: object_.MonetaryAccountReference + :type _cash_register_location: object_.Geolocation + :type _tab_item: list[TabItem] + :type _tab_attachment: list[object_.BunqId] + """ + + # Field constants. + FIELD_DESCRIPTION = "description" + FIELD_STATUS = "status" + FIELD_AMOUNT_TOTAL = "amount_total" + FIELD_ALLOW_AMOUNT_HIGHER = "allow_amount_higher" + FIELD_ALLOW_AMOUNT_LOWER = "allow_amount_lower" + FIELD_WANT_TIP = "want_tip" + FIELD_MINIMUM_AGE = "minimum_age" + FIELD_REQUIRE_ADDRESS = "require_address" + FIELD_REDIRECT_URL = "redirect_url" + FIELD_VISIBILITY = "visibility" + FIELD_EXPIRATION = "expiration" + FIELD_TAB_ATTACHMENT = "tab_attachment" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab-usage-multiple" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab-usage-multiple/{}" + _ENDPOINT_URL_DELETE = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab-usage-multiple/{}" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab-usage-multiple/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab-usage-multiple" + + # Object type. + _OBJECT_TYPE = "TabUsageMultiple" + + def __init__(self): + self._uuid = None + self._created = None + self._updated = None + self._description = None + self._status = None + self._amount_total = None + self._qr_code_token = None + self._tab_url = None + self._visibility = None + self._minimum_age = None + self._require_address = None + self._redirect_url = None + self._expiration = None + self._alias = None + self._cash_register_location = None + self._tab_item = None + self._tab_attachment = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + cash_register_id, custom_headers=None): + """ + Create a TabUsageMultiple. On creation the status must be set to OPEN + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type custom_headers: dict[str, str]|None + + :rtype: str + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id, + cash_register_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_uuid(response.text) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + cash_register_id, tab_usage_multiple_uuid, custom_headers=None): + """ + Modify a specific TabUsageMultiple. You can change the amount_total, + status and visibility. Once you change the status to PAYABLE the + TabUsageMultiple will expire after a year (default). If you've created + any TabItems for a Tab the sum of the amounts of these items must be + equal to the total_amount of the Tab when you change its status to + PAYABLE. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_usage_multiple_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype: str + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + cash_register_id, + tab_usage_multiple_uuid) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_uuid(response.text) + + @classmethod + def delete(cls, api_context, user_id, monetary_account_id, cash_register_id, + tab_usage_multiple_uuid, custom_headers=None): + """ + Close a specific TabUsageMultiple. This request returns an empty + response. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_usage_multiple_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype None: + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_DELETE.format(user_id, + monetary_account_id, + cash_register_id, + tab_usage_multiple_uuid) + api_client.delete(endpoint_url, custom_headers) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, cash_register_id, + tab_usage_multiple_uuid, custom_headers=None): + """ + Get a specific TabUsageMultiple. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_usage_multiple_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype: TabUsageMultiple + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + cash_register_id, + tab_usage_multiple_uuid) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, cash_register_id, + custom_headers=None): + """ + Get a collection of TabUsageMultiple. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[TabUsageMultiple] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + cash_register_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def uuid(self): + """ + :rtype: str + """ + + return self._uuid + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def description(self): + """ + :rtype: str + """ + + return self._description + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def amount_total(self): + """ + :rtype: object_.Amount + """ + + return self._amount_total + + @property + def qr_code_token(self): + """ + :rtype: str + """ + + return self._qr_code_token + + @property + def tab_url(self): + """ + :rtype: str + """ + + return self._tab_url + + @property + def visibility(self): + """ + :rtype: object_.TabVisibility + """ + + return self._visibility + + @property + def minimum_age(self): + """ + :rtype: bool + """ + + return self._minimum_age + + @property + def require_address(self): + """ + :rtype: str + """ + + return self._require_address + + @property + def redirect_url(self): + """ + :rtype: str + """ + + return self._redirect_url + + @property + def expiration(self): + """ + :rtype: str + """ + + return self._expiration + + @property + def alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._alias + + @property + def cash_register_location(self): + """ + :rtype: object_.Geolocation + """ + + return self._cash_register_location + + @property + def tab_item(self): + """ + :rtype: list[TabItem] + """ + + return self._tab_item + + @property + def tab_attachment(self): + """ + :rtype: list[object_.BunqId] + """ + + return self._tab_attachment + + +class CertificatePinned(model.BunqModel): + """ + This endpoint allow you to pin the certificate chains to your account. These + certificate chains are used for SSL validation whenever a callback is + initiated to one of your https callback urls. + + :type _certificate_chain: list[object_.Certificate] + :type _id_: int + """ + + # Field constants. + FIELD_CERTIFICATE_CHAIN = "certificate_chain" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/certificate-pinned" + _ENDPOINT_URL_DELETE = "user/{}/certificate-pinned/{}" + _ENDPOINT_URL_LISTING = "user/{}/certificate-pinned" + _ENDPOINT_URL_READ = "user/{}/certificate-pinned/{}" + + # Object type. + _OBJECT_TYPE = "CertificatePinned" + + def __init__(self): + self._certificate_chain = None + self._id_ = None + + @classmethod + def create(cls, api_context, request_map, user_id, custom_headers=None): + """ + Pin the certificate chain. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def delete(cls, api_context, user_id, certificate_pinned_id, + custom_headers=None): + """ + Remove the pinned certificate chain with the specific ID. + + :type api_context: context.ApiContext + :type user_id: int + :type certificate_pinned_id: int + :type custom_headers: dict[str, str]|None + + :rtype None: + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_DELETE.format(user_id, + certificate_pinned_id) + api_client.delete(endpoint_url, custom_headers) + + @classmethod + def list(cls, api_context, user_id, custom_headers=None): + """ + List all the pinned certificate chain for the given user. + + :type api_context: context.ApiContext + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[CertificatePinned] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @classmethod + def get(cls, api_context, user_id, certificate_pinned_id, + custom_headers=None): + """ + Get the pinned certificate chain with the specified ID. + + :type api_context: context.ApiContext + :type user_id: int + :type certificate_pinned_id: int + :type custom_headers: dict[str, str]|None + + :rtype: CertificatePinned + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + certificate_pinned_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @property + def certificate_chain(self): + """ + :rtype: list[object_.Certificate] + """ + + return self._certificate_chain + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + +class DeviceServer(model.BunqModel): + """ + After having created an Installation you can now create a DeviceServer. A + DeviceServer is needed to do a login call with session-server. + + :type _id_: int + :type _created: str + :type _updated: str + :type _description: str + :type _ip: str + :type _status: str + """ + + # Field constants. + FIELD_DESCRIPTION = "description" + FIELD_SECRET = "secret" + FIELD_PERMITTED_IPS = "permitted_ips" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "device-server" + _ENDPOINT_URL_READ = "device-server/{}" + _ENDPOINT_URL_LISTING = "device-server" + + # Object type. + _OBJECT_TYPE = "DeviceServer" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._description = None + self._ip = None + self._status = None + + @classmethod + def create(cls, api_context, request_map, custom_headers=None): + """ + Create a new DeviceServer. Provide the Installation token in the + "X-Bunq-Client-Authentication" header. And sign this request with the + key of which you used the public part to create the Installation. Your + API key will be bound to the ip address of this DeviceServer. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def get(cls, api_context, device_server_id, custom_headers=None): + """ + Get one of your DeviceServers. + + :type api_context: context.ApiContext + :type device_server_id: int + :type custom_headers: dict[str, str]|None + + :rtype: DeviceServer + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(device_server_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, custom_headers=None): + """ + Get a collection of all the DeviceServers you have created. + + :type api_context: context.ApiContext + :type custom_headers: dict[str, str]|None + + :rtype: list[DeviceServer] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def description(self): + """ + :rtype: str + """ + + return self._description + + @property + def ip(self): + """ + :rtype: str + """ + + return self._ip + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + +class Device(model.BunqModel): + """ + Used to get a Device or a listing of Devices. Creating a DeviceServer should + happen via /device-server + + :type _DevicePhone: DevicePhone + :type _DeviceServer: DeviceServer + """ + + # Endpoint constants. + _ENDPOINT_URL_READ = "device/{}" + _ENDPOINT_URL_LISTING = "device" + + # Object type. + _OBJECT_TYPE = "Device" + + def __init__(self): + self._DevicePhone = None + self._DeviceServer = None + + @classmethod + def get(cls, api_context, device_id, custom_headers=None): + """ + Get a single Device. A Device is either a DevicePhone or a DeviceServer. + + :type api_context: context.ApiContext + :type device_id: int + :type custom_headers: dict[str, str]|None + + :rtype: Device + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(device_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text) + + @classmethod + def list(cls, api_context, custom_headers=None): + """ + Get a collection of Devices. A Device is either a DevicePhone or a + DeviceServer. + + :type api_context: context.ApiContext + :type custom_headers: dict[str, str]|None + + :rtype: list[Device] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text) + + @property + def DevicePhone(self): + """ + :rtype: DevicePhone + """ + + return self._DevicePhone + + @property + def DeviceServer(self): + """ + :rtype: DeviceServer + """ + + return self._DeviceServer + + +class DevicePhone(model.BunqModel): + """ + Used to register a device. This is the only unsigned/verified request. + + :type _id_: int + :type _created: str + :type _updated: str + :type _description: str + :type _phone_number: str + :type _os: str + :type _status: str + """ + + # Field constants. + FIELD_DESCRIPTION = "description" + FIELD_PHONE_NUMBER = "phone_number" + + # Object type. + _OBJECT_TYPE = "DevicePhone" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._description = None + self._phone_number = None + self._os = None + self._status = None + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def description(self): + """ + :rtype: str + """ + + return self._description + + @property + def phone_number(self): + """ + :rtype: str + """ + + return self._phone_number + + @property + def os(self): + """ + :rtype: str + """ + + return self._os + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + +class DraftShareInviteBankQrCodeContent(model.BunqModel): + """ + This call returns the raw content of the QR code that links to this draft + share invite. When a bunq user scans this QR code with the bunq app the + draft share invite will be shown on his/her device. + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "user/{}/draft-share-invite-bank/{}/qr-code-content" + + # Object type. + _OBJECT_TYPE = "DraftShareInviteBankQrCodeContent" + + @classmethod + def list(cls, api_context, user_id, draft_share_invite_bank_id, + custom_headers=None): + """ + Returns the raw content of the QR code that links to this draft share + invite. The raw content is the binary representation of a file, without + any JSON wrapping. + + :type api_context: context.ApiContext + :type user_id: int + :type draft_share_invite_bank_id: int + :type custom_headers: dict[str, str]|None + + :rtype: bytes + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + draft_share_invite_bank_id) + + return api_client.get(endpoint_url, custom_headers).content + + +class DraftShareInviteBank(model.BunqModel): + """ + Used to create a draft share invite for a monetary account with another bunq + user, as in the 'Connect' feature in the bunq app. The user that accepts the + invite can share one of their MonetaryAccounts with the user that created + the invite. + + :type _user_alias_created: object_.LabelUser + :type _status: str + :type _expiration: str + :type _share_invite_bank_response_id: int + :type _draft_share_url: str + :type _draft_share_settings: object_.DraftShareInviteBankEntry + :type _id_: int + """ + + # Field constants. + FIELD_STATUS = "status" + FIELD_EXPIRATION = "expiration" + FIELD_DRAFT_SHARE_SETTINGS = "draft_share_settings" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/draft-share-invite-bank" + _ENDPOINT_URL_READ = "user/{}/draft-share-invite-bank/{}" + _ENDPOINT_URL_UPDATE = "user/{}/draft-share-invite-bank/{}" + _ENDPOINT_URL_LISTING = "user/{}/draft-share-invite-bank" + + # Object type. + _OBJECT_TYPE = "DraftShareInviteBank" + + def __init__(self): + self._user_alias_created = None + self._status = None + self._expiration = None + self._share_invite_bank_response_id = None + self._draft_share_url = None + self._draft_share_settings = None + self._id_ = None + + @classmethod + def create(cls, api_context, request_map, user_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def get(cls, api_context, user_id, draft_share_invite_bank_id, + custom_headers=None): + """ + Get the details of a specific draft of a share invite. + + :type api_context: context.ApiContext + :type user_id: int + :type draft_share_invite_bank_id: int + :type custom_headers: dict[str, str]|None + + :rtype: DraftShareInviteBank + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + draft_share_invite_bank_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def update(cls, api_context, request_map, user_id, + draft_share_invite_bank_id, custom_headers=None): + """ + Update a draft share invite. When sending status CANCELLED it is + possible to cancel the draft share invite. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type draft_share_invite_bank_id: int + :type custom_headers: dict[str, str]|None + + :rtype: DraftShareInviteBank + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + draft_share_invite_bank_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[DraftShareInviteBank] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def user_alias_created(self): + """ + :rtype: object_.LabelUser + """ + + return self._user_alias_created + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def expiration(self): + """ + :rtype: str + """ + + return self._expiration + + @property + def share_invite_bank_response_id(self): + """ + :rtype: int + """ + + return self._share_invite_bank_response_id + + @property + def draft_share_url(self): + """ + :rtype: str + """ + + return self._draft_share_url + + @property + def draft_share_settings(self): + """ + :rtype: object_.DraftShareInviteBankEntry + """ + + return self._draft_share_settings + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + +class ExportAnnualOverviewContent(model.BunqModel): + """ + Fetch the raw content of an annual overview. The annual overview is always + in PDF format. Doc won't display the response of a request to get the + content of an annual overview. + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "user/{}/export-annual-overview/{}/content" + + # Object type. + _OBJECT_TYPE = "ExportAnnualOverviewContent" + + @classmethod + def list(cls, api_context, user_id, export_annual_overview_id, + custom_headers=None): + """ + Used to retrieve the raw content of an annual overview. + + :type api_context: context.ApiContext + :type user_id: int + :type export_annual_overview_id: int + :type custom_headers: dict[str, str]|None + + :rtype: bytes + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + export_annual_overview_id) + + return api_client.get(endpoint_url, custom_headers).content + + +class ExportAnnualOverview(model.BunqModel): + """ + Used to create new and read existing annual overviews of all the user's + monetary accounts. Once created, annual overviews can be downloaded in PDF + format via the 'export-annual-overview/{id}/content' endpoint. + + :type _id_: int + :type _created: str + :type _updated: str + :type _year: int + :type _alias_user: object_.LabelUser + """ + + # Field constants. + FIELD_YEAR = "year" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/export-annual-overview" + _ENDPOINT_URL_READ = "user/{}/export-annual-overview/{}" + _ENDPOINT_URL_LISTING = "user/{}/export-annual-overview" + + # Object type. + _OBJECT_TYPE = "ExportAnnualOverview" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._year = None + self._alias_user = None + + @classmethod + def create(cls, api_context, request_map, user_id, custom_headers=None): + """ + Create a new annual overview for a specific year. An overview can be + generated only for a past year. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def get(cls, api_context, user_id, export_annual_overview_id, + custom_headers=None): + """ + Get an annual overview for a user by its id. + + :type api_context: context.ApiContext + :type user_id: int + :type export_annual_overview_id: int + :type custom_headers: dict[str, str]|None + + :rtype: ExportAnnualOverview + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + export_annual_overview_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, custom_headers=None): + """ + List all the annual overviews for a user. + + :type api_context: context.ApiContext + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[ExportAnnualOverview] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def year(self): + """ + :rtype: int + """ + + return self._year + + @property + def alias_user(self): + """ + :rtype: object_.LabelUser + """ + + return self._alias_user + + +class CustomerStatementExportContent(model.BunqModel): + """ + Fetch the raw content of a statement export. The returned file format could + be MT940, CSV or PDF depending on the statement format specified during the + statement creation. The doc won't display the response of a request to get + the content of a statement export. + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/customer-statement/{" \ + "}/content" + + # Object type. + _OBJECT_TYPE = "CustomerStatementExportContent" + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + customer_statement_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type customer_statement_id: int + :type custom_headers: dict[str, str]|None + + :rtype: bytes + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + customer_statement_id) + + return api_client.get(endpoint_url, custom_headers).content + + +class CustomerStatementExport(model.BunqModel): + """ + Used to create new and read existing statement exports. Statement exports + can be created in either CSV, MT940 or PDF file format. + + :type _id_: int + :type _created: str + :type _updated: str + :type _date_start: str + :type _date_end: str + :type _status: str + :type _statement_number: int + :type _statement_format: str + :type _regional_format: str + :type _alias_monetary_account: object_.MonetaryAccountReference + """ + + # Field constants. + FIELD_STATEMENT_FORMAT = "statement_format" + FIELD_DATE_START = "date_start" + FIELD_DATE_END = "date_end" + FIELD_REGIONAL_FORMAT = "regional_format" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/customer-statement" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/customer-statement/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/customer-statement" + _ENDPOINT_URL_DELETE = "user/{}/monetary-account/{}/customer-statement/{}" + + # Object type. + _OBJECT_TYPE = "CustomerStatementExport" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._date_start = None + self._date_end = None + self._status = None + self._statement_number = None + self._statement_format = None + self._regional_format = None + self._alias_monetary_account = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + custom_headers=None): + """ + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, + customer_statement_export_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type customer_statement_export_id: int + :type custom_headers: dict[str, str]|None + + :rtype: CustomerStatementExport + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + customer_statement_export_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[CustomerStatementExport] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @classmethod + def delete(cls, api_context, user_id, monetary_account_id, + customer_statement_export_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type customer_statement_export_id: int + :type custom_headers: dict[str, str]|None + + :rtype None: + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_DELETE.format(user_id, + monetary_account_id, + customer_statement_export_id) + api_client.delete(endpoint_url, custom_headers) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def date_start(self): + """ + :rtype: str + """ + + return self._date_start + + @property + def date_end(self): + """ + :rtype: str + """ + + return self._date_end + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def statement_number(self): + """ + :rtype: int + """ + + return self._statement_number + + @property + def statement_format(self): + """ + :rtype: str + """ + + return self._statement_format + + @property + def regional_format(self): + """ + :rtype: str + """ + + return self._regional_format + + @property + def alias_monetary_account(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._alias_monetary_account + + +class InstallationServerPublicKey(model.BunqModel): + """ + Using /installation/_/server-public-key you can request the ServerPublicKey + again. This is done by referring to the id of the Installation. + + :type _server_public_key: str + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "installation/{}/server-public-key" + + # Object type. + _OBJECT_TYPE = "ServerPublicKey" + + def __init__(self): + self._server_public_key = None + + @classmethod + def list(cls, api_context, installation_id, custom_headers=None): + """ + Show the ServerPublicKey for this Installation. + + :type api_context: context.ApiContext + :type installation_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[InstallationServerPublicKey] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(installation_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def server_public_key(self): + """ + :rtype: str + """ + + return self._server_public_key + + +class ShareInviteBankAmountUsed(model.BunqModel): + """ + When you have connected your monetary account bank to a user, and given this + user a (for example) daily budget of 10 EUR. If this users has used his + entire budget or part of it, this call can be used to reset the amount he + used to 0. The user can then spend the daily budget of 10 EUR again. + """ + + # Endpoint constants. + _ENDPOINT_URL_DELETE = "user/{}/monetary-account/{" \ + "}/share-invite-bank-inquiry/{}/amount-used/{}" + + # Object type. + _OBJECT_TYPE = "ShareInviteBankAmountUsed" + + @classmethod + def delete(cls, api_context, user_id, monetary_account_id, + share_invite_bank_inquiry_id, share_invite_bank_amount_used_id, + custom_headers=None): + """ + Reset the available budget for a bank account share. To be called + without any ID at the end of the path. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type share_invite_bank_inquiry_id: int + :type share_invite_bank_amount_used_id: int + :type custom_headers: dict[str, str]|None + + :rtype None: + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_DELETE.format(user_id, + monetary_account_id, + share_invite_bank_inquiry_id, + share_invite_bank_amount_used_id) + api_client.delete(endpoint_url, custom_headers) + + +class ShareInviteBankInquiry(model.BunqModel): + """ + Used to share a monetary account with another bunq user, as in the 'Connect' + feature in the bunq app. Allow the creation of share inquiries that, in the + same way as request inquiries, can be revoked by the user creating them or + accepted/rejected by the other party. + + :type _alias: object_.MonetaryAccountReference + :type _user_alias_created: object_.LabelUser + :type _user_alias_revoked: object_.LabelUser + :type _counter_user_alias: object_.LabelUser + :type _monetary_account_id: int + :type _draft_share_invite_bank_id: int + :type _share_detail: object_.ShareDetail + :type _status: str + :type _start_date: str + :type _end_date: str + :type _id_: int + """ + + # Field constants. + FIELD_COUNTER_USER_ALIAS = "counter_user_alias" + FIELD_DRAFT_SHARE_INVITE_BANK_ID = "draft_share_invite_bank_id" + FIELD_SHARE_DETAIL = "share_detail" + FIELD_STATUS = "status" + FIELD_START_DATE = "start_date" + FIELD_END_DATE = "end_date" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{" \ + "}/share-invite-bank-inquiry" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{" \ + "}/share-invite-bank-inquiry/{}" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{" \ + "}/share-invite-bank-inquiry/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{" \ + "}/share-invite-bank-inquiry" + + # Object type. + _OBJECT_TYPE = "ShareInviteBankInquiry" + + def __init__(self): + self._alias = None + self._user_alias_created = None + self._user_alias_revoked = None + self._counter_user_alias = None + self._monetary_account_id = None + self._draft_share_invite_bank_id = None + self._share_detail = None + self._status = None + self._start_date = None + self._end_date = None + self._id_ = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + custom_headers=None): + """ + Create a new share inquiry for a monetary account, specifying the + permission the other bunq user will have on it. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, + share_invite_bank_inquiry_id, custom_headers=None): + """ + Get the details of a specific share inquiry. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type share_invite_bank_inquiry_id: int + :type custom_headers: dict[str, str]|None + + :rtype: ShareInviteBankInquiry + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + share_invite_bank_inquiry_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + share_invite_bank_inquiry_id, custom_headers=None): + """ + Update the details of a share. This includes updating status (revoking + or cancelling it), granted permission and validity period of this share. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type share_invite_bank_inquiry_id: int + :type custom_headers: dict[str, str]|None + + :rtype: ShareInviteBankInquiry + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + share_invite_bank_inquiry_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + custom_headers=None): + """ + Get a list with all the share inquiries for a monetary account, only if + the requesting user has permission to change the details of the various + ones. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[ShareInviteBankInquiry] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._alias + + @property + def user_alias_created(self): + """ + :rtype: object_.LabelUser + """ + + return self._user_alias_created + + @property + def user_alias_revoked(self): + """ + :rtype: object_.LabelUser + """ + + return self._user_alias_revoked + + @property + def counter_user_alias(self): + """ + :rtype: object_.LabelUser + """ + + return self._counter_user_alias + + @property + def monetary_account_id(self): + """ + :rtype: int + """ + + return self._monetary_account_id + + @property + def draft_share_invite_bank_id(self): + """ + :rtype: int + """ + + return self._draft_share_invite_bank_id + + @property + def share_detail(self): + """ + :rtype: object_.ShareDetail + """ + + return self._share_detail + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def start_date(self): + """ + :rtype: str + """ + + return self._start_date + + @property + def end_date(self): + """ + :rtype: str + """ + + return self._end_date + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + +class ShareInviteBankResponse(model.BunqModel): + """ + Used to view or respond to shares a user was invited to. See + 'share-invite-bank-inquiry' for more information about the inquiring + endpoint. + + :type _counter_alias: object_.MonetaryAccountReference + :type _user_alias_cancelled: object_.LabelUser + :type _monetary_account_id: int + :type _draft_share_invite_bank_id: int + :type _share_detail: object_.ShareDetail + :type _status: str + :type _start_date: str + :type _end_date: str + :type _description: str + """ + + # Field constants. + FIELD_STATUS = "status" + + # Endpoint constants. + _ENDPOINT_URL_READ = "user/{}/share-invite-bank-response/{}" + _ENDPOINT_URL_UPDATE = "user/{}/share-invite-bank-response/{}" + _ENDPOINT_URL_LISTING = "user/{}/share-invite-bank-response" + + # Object type. + _OBJECT_TYPE = "ShareInviteBankResponse" + + def __init__(self): + self._counter_alias = None + self._user_alias_cancelled = None + self._monetary_account_id = None + self._draft_share_invite_bank_id = None + self._share_detail = None + self._status = None + self._start_date = None + self._end_date = None + self._description = None + + @classmethod + def get(cls, api_context, user_id, share_invite_bank_response_id, + custom_headers=None): + """ + Return the details of a specific share a user was invited to. + + :type api_context: context.ApiContext + :type user_id: int + :type share_invite_bank_response_id: int + :type custom_headers: dict[str, str]|None + + :rtype: ShareInviteBankResponse + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + share_invite_bank_response_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def update(cls, api_context, request_map, user_id, + share_invite_bank_response_id, custom_headers=None): + """ + Accept or reject a share a user was invited to. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type share_invite_bank_response_id: int + :type custom_headers: dict[str, str]|None + + :rtype: ShareInviteBankResponse + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + share_invite_bank_response_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, custom_headers=None): + """ + Return all the shares a user was invited to. + + :type api_context: context.ApiContext + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[ShareInviteBankResponse] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def counter_alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._counter_alias + + @property + def user_alias_cancelled(self): + """ + :rtype: object_.LabelUser + """ + + return self._user_alias_cancelled + + @property + def monetary_account_id(self): + """ + :rtype: int + """ + + return self._monetary_account_id + + @property + def draft_share_invite_bank_id(self): + """ + :rtype: int + """ + + return self._draft_share_invite_bank_id + + @property + def share_detail(self): + """ + :rtype: object_.ShareDetail + """ + + return self._share_detail + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def start_date(self): + """ + :rtype: str + """ + + return self._start_date + + @property + def end_date(self): + """ + :rtype: str + """ + + return self._end_date + + @property + def description(self): + """ + :rtype: str + """ + + return self._description + + +class MonetaryAccountBank(model.BunqModel): + """ + With MonetaryAccountBank you can create a new bank account, retrieve + information regarding your existing MonetaryAccountBanks and update specific + fields of an existing MonetaryAccountBank. Examples of fields that can be + updated are the description, the daily limit and the avatar of the + account.

Notification filters can be set on a monetary account + level to receive callbacks. For more information check the dedicated callbacks page. + + :type _id_: int + :type _created: str + :type _updated: str + :type _avatar: object_.Avatar + :type _currency: str + :type _description: str + :type _daily_limit: object_.Amount + :type _daily_spent: object_.Amount + :type _overdraft_limit: object_.Amount + :type _balance: object_.Amount + :type _alias: list[object_.Pointer] + :type _public_uuid: str + :type _status: str + :type _sub_status: str + :type _reason: str + :type _reason_description: str + :type _user_id: int + :type _monetary_account_profile: MonetaryAccountProfile + :type _notification_filters: list[object_.NotificationFilter] + :type _setting: object_.MonetaryAccountSetting + """ + + # Field constants. + FIELD_CURRENCY = "currency" + FIELD_DESCRIPTION = "description" + FIELD_DAILY_LIMIT = "daily_limit" + FIELD_OVERDRAFT_LIMIT = "overdraft_limit" + FIELD_ALIAS = "alias" + FIELD_AVATAR_UUID = "avatar_uuid" + FIELD_STATUS = "status" + FIELD_SUB_STATUS = "sub_status" + FIELD_REASON = "reason" + FIELD_REASON_DESCRIPTION = "reason_description" + FIELD_SHARE = "share" + FIELD_NOTIFICATION_FILTERS = "notification_filters" + FIELD_SETTING = "setting" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account-bank" + _ENDPOINT_URL_READ = "user/{}/monetary-account-bank/{}" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account-bank/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account-bank" + + # Object type. + _OBJECT_TYPE = "MonetaryAccountBank" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._avatar = None + self._currency = None + self._description = None + self._daily_limit = None + self._daily_spent = None + self._overdraft_limit = None + self._balance = None + self._alias = None + self._public_uuid = None + self._status = None + self._sub_status = None + self._reason = None + self._reason_description = None + self._user_id = None + self._monetary_account_profile = None + self._notification_filters = None + self._setting = None + + @classmethod + def create(cls, api_context, request_map, user_id, custom_headers=None): + """ + Create new MonetaryAccountBank. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def get(cls, api_context, user_id, monetary_account_bank_id, + custom_headers=None): + """ + Get a specific MonetaryAccountBank. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_bank_id: int + :type custom_headers: dict[str, str]|None + + :rtype: MonetaryAccountBank + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_bank_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_bank_id, + custom_headers=None): + """ + Update a specific existing MonetaryAccountBank. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_bank_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_bank_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def list(cls, api_context, user_id, custom_headers=None): + """ + Gets a listing of all MonetaryAccountBanks of a given user. + + :type api_context: context.ApiContext + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[MonetaryAccountBank] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def avatar(self): + """ + :rtype: object_.Avatar + """ + + return self._avatar + + @property + def currency(self): + """ + :rtype: str + """ + + return self._currency + + @property + def description(self): + """ + :rtype: str + """ + + return self._description + + @property + def daily_limit(self): + """ + :rtype: object_.Amount + """ + + return self._daily_limit + + @property + def daily_spent(self): + """ + :rtype: object_.Amount + """ + + return self._daily_spent + + @property + def overdraft_limit(self): + """ + :rtype: object_.Amount + """ + + return self._overdraft_limit + + @property + def balance(self): + """ + :rtype: object_.Amount + """ + + return self._balance + + @property + def alias(self): + """ + :rtype: list[object_.Pointer] + """ + + return self._alias + + @property + def public_uuid(self): + """ + :rtype: str + """ + + return self._public_uuid + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def sub_status(self): + """ + :rtype: str + """ + + return self._sub_status + + @property + def reason(self): + """ + :rtype: str + """ + + return self._reason + + @property + def reason_description(self): + """ + :rtype: str + """ + + return self._reason_description + + @property + def user_id(self): + """ + :rtype: int + """ + + return self._user_id + + @property + def monetary_account_profile(self): + """ + :rtype: MonetaryAccountProfile + """ + + return self._monetary_account_profile + + @property + def notification_filters(self): + """ + :rtype: list[object_.NotificationFilter] + """ + + return self._notification_filters + + @property + def setting(self): + """ + :rtype: object_.MonetaryAccountSetting + """ + + return self._setting + + +class MonetaryAccountProfile(model.BunqModel): + """ + Used to update and read up monetary account profiles, to keep the balance + between specific thresholds. + + :type _profile_fill: object_.MonetaryAccountProfileFill + :type _profile_drain: object_.MonetaryAccountProfileDrain + """ + + # Field constants. + FIELD_PROFILE_FILL = "profile_fill" + FIELD_PROFILE_DRAIN = "profile_drain" + + # Object type. + _OBJECT_TYPE = "MonetaryAccountProfile" + + def __init__(self): + self._profile_fill = None + self._profile_drain = None + + @property + def profile_fill(self): + """ + :rtype: object_.MonetaryAccountProfileFill + """ + + return self._profile_fill + + @property + def profile_drain(self): + """ + :rtype: object_.MonetaryAccountProfileDrain + """ + + return self._profile_drain + + +class MonetaryAccount(model.BunqModel): + """ + Used to show the MonetaryAccounts that you can access. Currently the only + MonetaryAccount type is MonetaryAccountBank. See also: + monetary-account-bank.

Notification filters can be set on a + monetary account level to receive callbacks. For more information check the + dedicated callbacks page. + + :type _MonetaryAccountBank: MonetaryAccountBank + """ + + # Endpoint constants. + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account" + + # Object type. + _OBJECT_TYPE = "MonetaryAccount" + + def __init__(self): + self._MonetaryAccountBank = None + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, + custom_headers=None): + """ + Get a specific MonetaryAccount. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: MonetaryAccount + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text) + + @classmethod + def list(cls, api_context, user_id, custom_headers=None): + """ + Get a collection of all your MonetaryAccounts. + + :type api_context: context.ApiContext + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[MonetaryAccount] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text) + + @property + def MonetaryAccountBank(self): + """ + :rtype: MonetaryAccountBank + """ + + return self._MonetaryAccountBank + + +class PaymentBatch(model.BunqModel): + """ + Create a payment batch, or show the payment batches of a monetary account. + + :type _payments: list[Payment] + """ + + # Field constants. + FIELD_PAYMENTS = "payments" + FIELD_BUNQTO_STATUS = "bunqto_status" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/payment-batch" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{}/payment-batch/{}" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/payment-batch/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/payment-batch" + + # Object type. + _OBJECT_TYPE = "PaymentBatch" + + def __init__(self): + self._payments = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + custom_headers=None): + """ + Create a payment batch by sending an array of single payment objects, + that will become part of the batch. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + payment_batch_id, custom_headers=None): + """ + Revoke a bunq.to payment batch. The status of all the payments will be + set to REVOKED. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type payment_batch_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + payment_batch_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, payment_batch_id, + custom_headers=None): + """ + Return the details of a specific payment batch. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type payment_batch_id: int + :type custom_headers: dict[str, str]|None + + :rtype: PaymentBatch + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + payment_batch_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + custom_headers=None): + """ + Return all the payment batches for a monetary account. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[PaymentBatch] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def payments(self): + """ + :rtype: list[Payment] + """ + + return self._payments + + +class Payment(model.BunqModel): + """ + Using Payment, you can send payments to bunq and non-bunq users from your + bunq MonetaryAccounts. This can be done using bunq Aliases or IBAN Aliases. + When transferring money to other bunq MonetaryAccounts you can also refer to + Attachments. These will be received by the counter-party as part of the + Payment. You can also retrieve a single Payment or all executed Payments of + a specific monetary account. + + :type _id_: int + :type _created: str + :type _updated: str + :type _monetary_account_id: int + :type _amount: object_.Amount + :type _alias: object_.MonetaryAccountReference + :type _counterparty_alias: object_.MonetaryAccountReference + :type _description: str + :type _type_: str + :type _sub_type: str + :type _bunqto_status: str + :type _bunqto_sub_status: str + :type _bunqto_share_url: str + :type _bunqto_expiry: str + :type _bunqto_time_responded: str + :type _attachment: list[object_.AttachmentMonetaryAccountPayment] + :type _merchant_reference: str + :type _batch_id: int + :type _scheduled_id: int + :type _address_shipping: object_.Address + :type _address_billing: object_.Address + :type _geolocation: object_.Geolocation + :type _allow_chat: bool + """ + + # Field constants. + FIELD_AMOUNT = "amount" + FIELD_COUNTERPARTY_ALIAS = "counterparty_alias" + FIELD_DESCRIPTION = "description" + FIELD_ATTACHMENT = "attachment" + FIELD_MERCHANT_REFERENCE = "merchant_reference" + FIELD_ALLOW_BUNQTO = "allow_bunqto" + FIELD_BUNQTO_STATUS = "bunqto_status" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/payment" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/payment/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/payment" + + # Object type. + _OBJECT_TYPE = "Payment" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._monetary_account_id = None + self._amount = None + self._alias = None + self._counterparty_alias = None + self._description = None + self._type_ = None + self._sub_type = None + self._bunqto_status = None + self._bunqto_sub_status = None + self._bunqto_share_url = None + self._bunqto_expiry = None + self._bunqto_time_responded = None + self._attachment = None + self._merchant_reference = None + self._batch_id = None + self._scheduled_id = None + self._address_shipping = None + self._address_billing = None + self._geolocation = None + self._allow_chat = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + custom_headers=None): + """ + Create a new Payment. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, payment_id, + custom_headers=None): + """ + Get a specific previous Payment. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type payment_id: int + :type custom_headers: dict[str, str]|None + + :rtype: Payment + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + payment_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + custom_headers=None): + """ + Get a listing of all Payments performed on a given MonetaryAccount + (incoming and outgoing). + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[Payment] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def monetary_account_id(self): + """ + :rtype: int + """ + + return self._monetary_account_id + + @property + def amount(self): + """ + :rtype: object_.Amount + """ + + return self._amount + + @property + def alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._alias + + @property + def counterparty_alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._counterparty_alias + + @property + def description(self): + """ + :rtype: str + """ + + return self._description + + @property + def type_(self): + """ + :rtype: str + """ + + return self._type_ + + @property + def sub_type(self): + """ + :rtype: str + """ + + return self._sub_type + + @property + def bunqto_status(self): + """ + :rtype: str + """ + + return self._bunqto_status + + @property + def bunqto_sub_status(self): + """ + :rtype: str + """ + + return self._bunqto_sub_status + + @property + def bunqto_share_url(self): + """ + :rtype: str + """ + + return self._bunqto_share_url + + @property + def bunqto_expiry(self): + """ + :rtype: str + """ + + return self._bunqto_expiry + + @property + def bunqto_time_responded(self): + """ + :rtype: str + """ + + return self._bunqto_time_responded + + @property + def attachment(self): + """ + :rtype: list[object_.AttachmentMonetaryAccountPayment] + """ + + return self._attachment + + @property + def merchant_reference(self): + """ + :rtype: str + """ + + return self._merchant_reference + + @property + def batch_id(self): + """ + :rtype: int + """ + + return self._batch_id + + @property + def scheduled_id(self): + """ + :rtype: int + """ + + return self._scheduled_id + + @property + def address_shipping(self): + """ + :rtype: object_.Address + """ + + return self._address_shipping + + @property + def address_billing(self): + """ + :rtype: object_.Address + """ + + return self._address_billing + + @property + def geolocation(self): + """ + :rtype: object_.Geolocation + """ + + return self._geolocation + + @property + def allow_chat(self): + """ + :rtype: bool + """ + + return self._allow_chat + + +class PaymentChat(model.BunqModel): + """ + Manage the chat connected to a payment. + + :type _id_: int + :type _created: str + :type _updated: str + :type _unread_message_count: int + """ + + # Field constants. + FIELD_LAST_READ_MESSAGE_ID = "last_read_message_id" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/payment/{}/chat" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{}/payment/{}/chat/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/payment/{}/chat" + + # Object type. + _OBJECT_TYPE = "ChatConversationPayment" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._unread_message_count = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + payment_id, custom_headers=None): + """ + Create a chat for a specific payment. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type payment_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id, + payment_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + payment_id, payment_chat_id, custom_headers=None): + """ + Update the last read message in the chat of a specific payment. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type payment_id: int + :type payment_chat_id: int + :type custom_headers: dict[str, str]|None + + :rtype: PaymentChat + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + payment_id, + payment_chat_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, payment_id, + custom_headers=None): + """ + Get the chat for a specific payment. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type payment_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[PaymentChat] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + payment_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def unread_message_count(self): + """ + :rtype: int + """ + + return self._unread_message_count + + +class PermittedIp(model.BunqModel): + """ + Manage the IPs which may be used for a credential of a user for server + authentication. + + :type _ip: str + :type _status: str + """ + + # Field constants. + FIELD_IP = "ip" + FIELD_STATUS = "status" + + # Endpoint constants. + _ENDPOINT_URL_READ = "user/{}/credential-password-ip/{}/ip/{}" + _ENDPOINT_URL_CREATE = "user/{}/credential-password-ip/{}/ip" + _ENDPOINT_URL_LISTING = "user/{}/credential-password-ip/{}/ip" + _ENDPOINT_URL_UPDATE = "user/{}/credential-password-ip/{}/ip/{}" + + # Object type. + _OBJECT_TYPE = "PermittedIp" + + def __init__(self): + self._ip = None + self._status = None + + @classmethod + def get(cls, api_context, user_id, credential_password_ip_id, + permitted_ip_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type credential_password_ip_id: int + :type permitted_ip_id: int + :type custom_headers: dict[str, str]|None + + :rtype: PermittedIp + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + credential_password_ip_id, + permitted_ip_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def create(cls, api_context, request_map, user_id, + credential_password_ip_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type credential_password_ip_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + credential_password_ip_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def list(cls, api_context, user_id, credential_password_ip_id, + custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type credential_password_ip_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[PermittedIp] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + credential_password_ip_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @classmethod + def update(cls, api_context, request_map, user_id, + credential_password_ip_id, permitted_ip_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type credential_password_ip_id: int + :type permitted_ip_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + credential_password_ip_id, + permitted_ip_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @property + def ip(self): + """ + :rtype: str + """ + + return self._ip + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + +class RequestInquiryBatch(model.BunqModel): + """ + Create a batch of requests for payment, or show the request batches of a + monetary account. + + :type _request_inquiries: list[RequestInquiry] + :type _total_amount_inquired: object_.Amount + """ + + # Field constants. + FIELD_REQUEST_INQUIRIES = "request_inquiries" + FIELD_STATUS = "status" + FIELD_TOTAL_AMOUNT_INQUIRED = "total_amount_inquired" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/request-inquiry-batch" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{" \ + "}/request-inquiry-batch/{}" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/request-inquiry-batch/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/request-inquiry-batch" + + # Object type. + _OBJECT_TYPE = "RequestInquiryBatch" + + def __init__(self): + self._request_inquiries = None + self._total_amount_inquired = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + custom_headers=None): + """ + Create a request batch by sending an array of single request objects, + that will become part of the batch. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + request_inquiry_batch_id, custom_headers=None): + """ + Revoke a request batch. The status of all the requests will be set to + REVOKED. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type request_inquiry_batch_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + request_inquiry_batch_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, + request_inquiry_batch_id, custom_headers=None): + """ + Return the details of a specific request batch. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type request_inquiry_batch_id: int + :type custom_headers: dict[str, str]|None + + :rtype: RequestInquiryBatch + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + request_inquiry_batch_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + custom_headers=None): + """ + Return all the request batches for a monetary account. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[RequestInquiryBatch] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def request_inquiries(self): + """ + :rtype: list[RequestInquiry] + """ + + return self._request_inquiries + + @property + def total_amount_inquired(self): + """ + :rtype: object_.Amount + """ + + return self._total_amount_inquired + + +class RequestInquiry(model.BunqModel): + """ + RequestInquiry, aka 'RFP' (Request for Payment), is one of the innovative + features that bunq offers. To request payment from another bunq account a + new Request Inquiry is created. As with payments you can add attachments to + a RFP. Requests for Payment are the foundation for a number of consumer + features like 'Split the bill' and 'Request forwarding'. We invite you to + invent your own based on the bunq api! + + :type _id_: int + :type _created: str + :type _updated: str + :type _time_responded: str + :type _time_expiry: str + :type _monetary_account_id: int + :type _amount_inquired: object_.Amount + :type _amount_responded: object_.Amount + :type _user_alias_created: object_.LabelUser + :type _user_alias_revoked: object_.LabelUser + :type _counterparty_alias: object_.MonetaryAccountReference + :type _description: str + :type _merchant_reference: str + :type _attachment: list[object_.BunqId] + :type _status: str + :type _batch_id: int + :type _scheduled_id: int + :type _minimum_age: int + :type _require_address: str + :type _bunqme_share_url: str + :type _redirect_url: str + :type _address_shipping: object_.Address + :type _address_billing: object_.Address + :type _geolocation: object_.Geolocation + :type _allow_chat: bool + """ + + # Field constants. + FIELD_AMOUNT_INQUIRED = "amount_inquired" + FIELD_COUNTERPARTY_ALIAS = "counterparty_alias" + FIELD_DESCRIPTION = "description" + FIELD_ATTACHMENT = "attachment" + FIELD_MERCHANT_REFERENCE = "merchant_reference" + FIELD_STATUS = "status" + FIELD_MINIMUM_AGE = "minimum_age" + FIELD_REQUIRE_ADDRESS = "require_address" + FIELD_WANT_TIP = "want_tip" + FIELD_ALLOW_AMOUNT_LOWER = "allow_amount_lower" + FIELD_ALLOW_AMOUNT_HIGHER = "allow_amount_higher" + FIELD_ALLOW_BUNQME = "allow_bunqme" + FIELD_REDIRECT_URL = "redirect_url" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/request-inquiry" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{}/request-inquiry/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/request-inquiry" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/request-inquiry/{}" + + # Object type. + _OBJECT_TYPE = "RequestInquiry" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._time_responded = None + self._time_expiry = None + self._monetary_account_id = None + self._amount_inquired = None + self._amount_responded = None + self._user_alias_created = None + self._user_alias_revoked = None + self._counterparty_alias = None + self._description = None + self._merchant_reference = None + self._attachment = None + self._status = None + self._batch_id = None + self._scheduled_id = None + self._minimum_age = None + self._require_address = None + self._bunqme_share_url = None + self._redirect_url = None + self._address_shipping = None + self._address_billing = None + self._geolocation = None + self._allow_chat = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + custom_headers=None): + """ + Create a new payment request. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + request_inquiry_id, custom_headers=None): + """ + Revoke a request for payment, by updating the status to REVOKED. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type request_inquiry_id: int + :type custom_headers: dict[str, str]|None + + :rtype: RequestInquiry + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + request_inquiry_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + custom_headers=None): + """ + Get all payment requests for a user's monetary account. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[RequestInquiry] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, request_inquiry_id, + custom_headers=None): + """ + Get the details of a specific payment request, including its status. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type request_inquiry_id: int + :type custom_headers: dict[str, str]|None + + :rtype: RequestInquiry + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + request_inquiry_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def time_responded(self): + """ + :rtype: str + """ + + return self._time_responded + + @property + def time_expiry(self): + """ + :rtype: str + """ + + return self._time_expiry + + @property + def monetary_account_id(self): + """ + :rtype: int + """ + + return self._monetary_account_id + + @property + def amount_inquired(self): + """ + :rtype: object_.Amount + """ + + return self._amount_inquired + + @property + def amount_responded(self): + """ + :rtype: object_.Amount + """ + + return self._amount_responded + + @property + def user_alias_created(self): + """ + :rtype: object_.LabelUser + """ + + return self._user_alias_created + + @property + def user_alias_revoked(self): + """ + :rtype: object_.LabelUser + """ + + return self._user_alias_revoked + + @property + def counterparty_alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._counterparty_alias + + @property + def description(self): + """ + :rtype: str + """ + + return self._description + + @property + def merchant_reference(self): + """ + :rtype: str + """ + + return self._merchant_reference + + @property + def attachment(self): + """ + :rtype: list[object_.BunqId] + """ + + return self._attachment + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def batch_id(self): + """ + :rtype: int + """ + + return self._batch_id + + @property + def scheduled_id(self): + """ + :rtype: int + """ + + return self._scheduled_id + + @property + def minimum_age(self): + """ + :rtype: int + """ + + return self._minimum_age + + @property + def require_address(self): + """ + :rtype: str + """ + + return self._require_address + + @property + def bunqme_share_url(self): + """ + :rtype: str + """ + + return self._bunqme_share_url + + @property + def redirect_url(self): + """ + :rtype: str + """ + + return self._redirect_url + + @property + def address_shipping(self): + """ + :rtype: object_.Address + """ + + return self._address_shipping + + @property + def address_billing(self): + """ + :rtype: object_.Address + """ + + return self._address_billing + + @property + def geolocation(self): + """ + :rtype: object_.Geolocation + """ + + return self._geolocation + + @property + def allow_chat(self): + """ + :rtype: bool + """ + + return self._allow_chat + + +class RequestInquiryChat(model.BunqModel): + """ + Manage the chat connected to a request inquiry. In the same way a request + inquiry and a request response are created together, so that each side of + the interaction can work on a different object, also a request inquiry chat + and a request response chat are created at the same time. See + 'request-response-chat' for the chat endpoint for the responding user. + + :type _id_: int + :type _created: str + :type _updated: str + :type _unread_message_count: int + """ + + # Field constants. + FIELD_LAST_READ_MESSAGE_ID = "last_read_message_id" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/request-inquiry/{}/chat" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{}/request-inquiry/{" \ + "}/chat/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/request-inquiry/{" \ + "}/chat" + + # Object type. + _OBJECT_TYPE = "RequestInquiryChat" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._unread_message_count = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + request_inquiry_id, custom_headers=None): + """ + Create a chat for a specific request inquiry. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type request_inquiry_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id, + request_inquiry_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + request_inquiry_id, request_inquiry_chat_id, + custom_headers=None): + """ + Update the last read message in the chat of a specific request inquiry. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type request_inquiry_id: int + :type request_inquiry_chat_id: int + :type custom_headers: dict[str, str]|None + + :rtype: RequestInquiryChat + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + request_inquiry_id, + request_inquiry_chat_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, request_inquiry_id, + custom_headers=None): + """ + Get the chat for a specific request inquiry. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type request_inquiry_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[RequestInquiryChat] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + request_inquiry_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def unread_message_count(self): + """ + :rtype: int + """ + + return self._unread_message_count + + +class RequestResponseChat(model.BunqModel): + """ + Manage the chat connected to a request response. In the same way a request + inquiry and a request response are created together, so that each side of + the interaction can work on a different object, also a request inquiry chat + and a request response chat are created at the same time. See + 'request-inquiry-chat' for the chat endpoint for the inquiring user. + + :type _id_: int + :type _created: str + :type _updated: str + :type _unread_message_count: int + """ + + # Field constants. + FIELD_LAST_READ_MESSAGE_ID = "last_read_message_id" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/request-response/{" \ + "}/chat" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{}/request-response/{" \ + "}/chat/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/request-response/{" \ + "}/chat" + + # Object type. + _OBJECT_TYPE = "RequestResponseChat" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._unread_message_count = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + request_response_id, custom_headers=None): + """ + Create a chat for a specific request response. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type request_response_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id, + request_response_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + request_response_id, request_response_chat_id, + custom_headers=None): + """ + Update the last read message in the chat of a specific request response. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type request_response_id: int + :type request_response_chat_id: int + :type custom_headers: dict[str, str]|None + + :rtype: RequestResponseChat + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + request_response_id, + request_response_chat_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + request_response_id, custom_headers=None): + """ + Get the chat for a specific request response. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type request_response_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[RequestResponseChat] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + request_response_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def unread_message_count(self): + """ + :rtype: int + """ + + return self._unread_message_count + + +class RequestResponse(model.BunqModel): + """ + A RequestResponse is what a user on the other side of a RequestInquiry gets + when he is sent one. So a RequestInquiry is the initiator and visible for + the user that sent it and that wants to receive the money. A RequestResponse + is what the other side sees, i.e. the user that pays the money to accept the + request. The content is almost identical. + + :type _id_: int + :type _created: str + :type _updated: str + :type _time_responded: str + :type _time_expiry: str + :type _monetary_account_id: int + :type _amount_inquired: object_.Amount + :type _amount_responded: object_.Amount + :type _status: str + :type _description: str + :type _alias: object_.MonetaryAccountReference + :type _counterparty_alias: object_.MonetaryAccountReference + :type _attachment: list[object_.Attachment] + :type _minimum_age: int + :type _require_address: str + :type _geolocation: object_.Geolocation + :type _type_: str + :type _sub_type: str + :type _redirect_url: str + :type _address_billing: object_.Address + :type _address_shipping: object_.Address + :type _allow_chat: bool + :type _eligible_whitelist_id: int + """ + + # Field constants. + FIELD_AMOUNT_RESPONDED = "amount_responded" + FIELD_STATUS = "status" + FIELD_ADDRESS_SHIPPING = "address_shipping" + FIELD_ADDRESS_BILLING = "address_billing" + + # Endpoint constants. + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{}/request-response/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/request-response" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/request-response/{}" + + # Object type. + _OBJECT_TYPE = "RequestResponse" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._time_responded = None + self._time_expiry = None + self._monetary_account_id = None + self._amount_inquired = None + self._amount_responded = None + self._status = None + self._description = None + self._alias = None + self._counterparty_alias = None + self._attachment = None + self._minimum_age = None + self._require_address = None + self._geolocation = None + self._type_ = None + self._sub_type = None + self._redirect_url = None + self._address_billing = None + self._address_shipping = None + self._allow_chat = None + self._eligible_whitelist_id = None + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + request_response_id, custom_headers=None): + """ + Update the status to accept or reject the RequestResponse. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type request_response_id: int + :type custom_headers: dict[str, str]|None + + :rtype: RequestResponse + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + request_response_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + custom_headers=None): + """ + Get all RequestResponses for a MonetaryAccount. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[RequestResponse] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, request_response_id, + custom_headers=None): + """ + Get the details for a specific existing RequestResponse. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type request_response_id: int + :type custom_headers: dict[str, str]|None + + :rtype: RequestResponse + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + request_response_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def time_responded(self): + """ + :rtype: str + """ + + return self._time_responded + + @property + def time_expiry(self): + """ + :rtype: str + """ + + return self._time_expiry + + @property + def monetary_account_id(self): + """ + :rtype: int + """ + + return self._monetary_account_id + + @property + def amount_inquired(self): + """ + :rtype: object_.Amount + """ + + return self._amount_inquired + + @property + def amount_responded(self): + """ + :rtype: object_.Amount + """ + + return self._amount_responded + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def description(self): + """ + :rtype: str + """ + + return self._description + + @property + def alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._alias + + @property + def counterparty_alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._counterparty_alias + + @property + def attachment(self): + """ + :rtype: list[object_.Attachment] + """ + + return self._attachment + + @property + def minimum_age(self): + """ + :rtype: int + """ + + return self._minimum_age + + @property + def require_address(self): + """ + :rtype: str + """ + + return self._require_address + + @property + def geolocation(self): + """ + :rtype: object_.Geolocation + """ + + return self._geolocation + + @property + def type_(self): + """ + :rtype: str + """ + + return self._type_ + + @property + def sub_type(self): + """ + :rtype: str + """ + + return self._sub_type + + @property + def redirect_url(self): + """ + :rtype: str + """ + + return self._redirect_url + + @property + def address_billing(self): + """ + :rtype: object_.Address + """ + + return self._address_billing + + @property + def address_shipping(self): + """ + :rtype: object_.Address + """ + + return self._address_shipping + + @property + def allow_chat(self): + """ + :rtype: bool + """ + + return self._allow_chat + + @property + def eligible_whitelist_id(self): + """ + :rtype: int + """ + + return self._eligible_whitelist_id + + +class ScheduleInstance(model.BunqModel): + """ + view for reading, updating and listing the scheduled instance. + + :type _state: str + :type _time_start: str + :type _time_end: str + :type _error_message: list[object_.Error] + :type _scheduled_object: model.BunqModel + :type _result_object: model.BunqModel + """ + + # Field constants. + FIELD_STATE = "state" + + # Endpoint constants. + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/schedule/{" \ + "}/schedule-instance/{}" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{}/schedule/{" \ + "}/schedule-instance/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/schedule/{" \ + "}/schedule-instance" + + # Object type. + _OBJECT_TYPE = "ScheduleInstance" + + def __init__(self): + self._state = None + self._time_start = None + self._time_end = None + self._error_message = None + self._scheduled_object = None + self._result_object = None + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, schedule_id, + schedule_instance_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type schedule_id: int + :type schedule_instance_id: int + :type custom_headers: dict[str, str]|None + + :rtype: ScheduleInstance + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + schedule_id, + schedule_instance_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + schedule_id, schedule_instance_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type schedule_id: int + :type schedule_instance_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + schedule_id, + schedule_instance_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, schedule_id, + custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type schedule_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[ScheduleInstance] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + schedule_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def state(self): + """ + :rtype: str + """ + + return self._state + + @property + def time_start(self): + """ + :rtype: str + """ + + return self._time_start + + @property + def time_end(self): + """ + :rtype: str + """ + + return self._time_end + + @property + def error_message(self): + """ + :rtype: list[object_.Error] + """ + + return self._error_message + + @property + def scheduled_object(self): + """ + :rtype: model.BunqModel + """ + + return self._scheduled_object + + @property + def result_object(self): + """ + :rtype: model.BunqModel + """ + + return self._result_object + + +class SchedulePaymentBatch(model.BunqModel): + """ + Endpoint for schedule payment batches. + + :type _payments: list[object_.SchedulePaymentEntry] + :type _schedule: object_.Schedule + """ + + # Field constants. + FIELD_PAYMENTS = "payments" + FIELD_SCHEDULE = "schedule" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/schedule-payment-batch" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{" \ + "}/schedule-payment-batch/{}" + _ENDPOINT_URL_DELETE = "user/{}/monetary-account/{" \ + "}/schedule-payment-batch/{}" + + # Object type. + _OBJECT_TYPE = "SchedulePaymentBatch" + + def __init__(self): + self._payments = None + self._schedule = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + custom_headers=None): + """ + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + schedule_payment_batch_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type schedule_payment_batch_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + schedule_payment_batch_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def delete(cls, api_context, user_id, monetary_account_id, + schedule_payment_batch_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type schedule_payment_batch_id: int + :type custom_headers: dict[str, str]|None + + :rtype None: + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_DELETE.format(user_id, + monetary_account_id, + schedule_payment_batch_id) + api_client.delete(endpoint_url, custom_headers) + + @property + def payments(self): + """ + :rtype: list[object_.SchedulePaymentEntry] + """ + + return self._payments + + @property + def schedule(self): + """ + :rtype: object_.Schedule + """ + + return self._schedule + + +class SchedulePayment(model.BunqModel): + """ + Endpoint for schedule payments. + + :type _payment: object_.SchedulePaymentEntry + :type _schedule: object_.Schedule + """ + + # Field constants. + FIELD_PAYMENT = "payment" + FIELD_SCHEDULE = "schedule" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/schedule-payment" + _ENDPOINT_URL_DELETE = "user/{}/monetary-account/{}/schedule-payment/{}" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/schedule-payment/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/schedule-payment" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{}/schedule-payment/{}" + + # Object type. + _OBJECT_TYPE = "SchedulePayment" + + def __init__(self): + self._payment = None + self._schedule = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + custom_headers=None): + """ + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def delete(cls, api_context, user_id, monetary_account_id, + schedule_payment_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type schedule_payment_id: int + :type custom_headers: dict[str, str]|None + + :rtype None: + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_DELETE.format(user_id, + monetary_account_id, + schedule_payment_id) + api_client.delete(endpoint_url, custom_headers) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, schedule_payment_id, + custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type schedule_payment_id: int + :type custom_headers: dict[str, str]|None + + :rtype: SchedulePayment + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + schedule_payment_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[SchedulePayment] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + schedule_payment_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type schedule_payment_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + schedule_payment_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @property + def payment(self): + """ + :rtype: object_.SchedulePaymentEntry + """ + + return self._payment + + @property + def schedule(self): + """ + :rtype: object_.Schedule + """ + + return self._schedule + + +class Schedule(model.BunqModel): + """ + view for reading the scheduled definitions. + """ + + # Endpoint constants. + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/schedule/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/schedule" + + # Object type. + _OBJECT_TYPE = "Schedule" + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, schedule_id, + custom_headers=None): + """ + Get a specific schedule definition for a given monetary account. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type schedule_id: int + :type custom_headers: dict[str, str]|None + + :rtype: Schedule + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + schedule_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + custom_headers=None): + """ + Get a collection of scheduled definition for a given monetary account. + You can add the parameter type to filter the response. When + type={SCHEDULE_DEFINITION_PAYMENT,SCHEDULE_DEFINITION_PAYMENT_BATCH} is + provided only schedule definition object that relate to these + definitions are returned. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[Schedule] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + +class ScheduleUser(model.BunqModel): + """ + view for reading the scheduled definitions. + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "user/{}/schedule" + + # Object type. + _OBJECT_TYPE = "ScheduleUser" + + @classmethod + def list(cls, api_context, user_id, custom_headers=None): + """ + Get a collection of scheduled definition for all accessible monetary + accounts of the user. You can add the parameter type to filter the + response. When + type={SCHEDULE_DEFINITION_PAYMENT,SCHEDULE_DEFINITION_PAYMENT_BATCH} is + provided only schedule definition object that relate to these + definitions are returned. + + :type api_context: context.ApiContext + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[ScheduleUser] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + +class Session(model.BunqModel): + """ + Endpoint for operations over the current session. + """ + + # Endpoint constants. + _ENDPOINT_URL_DELETE = "session/{}" + + # Object type. + _OBJECT_TYPE = "Session" + + @classmethod + def delete(cls, api_context, session_id, custom_headers=None): + """ + Deletes the current session. No response is returned for this request. + + :type api_context: context.ApiContext + :type session_id: int + :type custom_headers: dict[str, str]|None + + :rtype None: + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_DELETE.format(session_id) + api_client.delete(endpoint_url, custom_headers) + + +class TabItemShopBatch(model.BunqModel): + """ + Create a batch of tab items. + + :type _tab_items: list[TabItemShop] + """ + + # Field constants. + FIELD_TAB_ITEMS = "tab_items" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab/{}/tab-item-batch" + + # Object type. + _OBJECT_TYPE = "TabItemShopBatch" + + def __init__(self): + self._tab_items = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + cash_register_id, tab_uuid, custom_headers=None): + """ + Create tab items as a batch. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id, + cash_register_id, + tab_uuid) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @property + def tab_items(self): + """ + :rtype: list[TabItemShop] + """ + + return self._tab_items + + +class TabItemShop(model.BunqModel): + """ + After you’ve created a Tab using /tab-usage-single or /tab-usage-multiple + you can add items and attachments using tab-item. You can only add or modify + TabItems of a Tab which status is OPEN. The amount of the TabItems will not + influence the total_amount of the corresponding Tab. However, if you've + created any TabItems for a Tab the sum of the amounts of these items must be + equal to the total_amount of the Tab when you change its status to + PAYABLE/WAITING_FOR_PAYMENT. + + :type _id_: int + :type _description: str + :type _ean_code: str + :type _avatar_attachment: object_.AttachmentPublic + :type _tab_attachment: list[object_.AttachmentTab] + :type _quantity: float + :type _amount: object_.Amount + """ + + # Field constants. + FIELD_DESCRIPTION = "description" + FIELD_EAN_CODE = "ean_code" + FIELD_AVATAR_ATTACHMENT_UUID = "avatar_attachment_uuid" + FIELD_TAB_ATTACHMENT = "tab_attachment" + FIELD_QUANTITY = "quantity" + FIELD_AMOUNT = "amount" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab/{}/tab-item" + _ENDPOINT_URL_UPDATE = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab/{}/tab-item/{}" + _ENDPOINT_URL_DELETE = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab/{}/tab-item/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab/{}/tab-item" + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/cash-register/{}/tab/{" \ + "}/tab-item/{}" + + # Object type. + _OBJECT_TYPE = "TabItem" + + def __init__(self): + self._id_ = None + self._description = None + self._ean_code = None + self._avatar_attachment = None + self._tab_attachment = None + self._quantity = None + self._amount = None + + @classmethod + def create(cls, api_context, request_map, user_id, monetary_account_id, + cash_register_id, tab_uuid, custom_headers=None): + """ + Create a new TabItem for a given Tab. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id, + monetary_account_id, + cash_register_id, + tab_uuid) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def update(cls, api_context, request_map, user_id, monetary_account_id, + cash_register_id, tab_uuid, tab_item_shop_id, + custom_headers=None): + """ + Modify a TabItem from a given Tab. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_uuid: str + :type tab_item_shop_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_id, + monetary_account_id, + cash_register_id, + tab_uuid, + tab_item_shop_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @classmethod + def delete(cls, api_context, user_id, monetary_account_id, cash_register_id, + tab_uuid, tab_item_shop_id, custom_headers=None): + """ + Delete a specific TabItem from a Tab. This request returns an empty + response. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_uuid: str + :type tab_item_shop_id: int + :type custom_headers: dict[str, str]|None + + :rtype None: + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_DELETE.format(user_id, + monetary_account_id, + cash_register_id, + tab_uuid, + tab_item_shop_id) + api_client.delete(endpoint_url, custom_headers) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, cash_register_id, + tab_uuid, custom_headers=None): + """ + Get a collection of TabItems from a given Tab. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype: list[TabItemShop] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + cash_register_id, + tab_uuid) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, cash_register_id, + tab_uuid, tab_item_shop_id, custom_headers=None): + """ + Get a specific TabItem from a given Tab. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_uuid: str + :type tab_item_shop_id: int + :type custom_headers: dict[str, str]|None + + :rtype: TabItemShop + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + cash_register_id, tab_uuid, + tab_item_shop_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def description(self): + """ + :rtype: str + """ + + return self._description + + @property + def ean_code(self): + """ + :rtype: str + """ + + return self._ean_code + + @property + def avatar_attachment(self): + """ + :rtype: object_.AttachmentPublic + """ + + return self._avatar_attachment + + @property + def tab_attachment(self): + """ + :rtype: list[object_.AttachmentTab] + """ + + return self._tab_attachment + + @property + def quantity(self): + """ + :rtype: float + """ + + return self._quantity + + @property + def amount(self): + """ + :rtype: object_.Amount + """ + + return self._amount + + +class TabResultInquiry(model.BunqModel): + """ + Used to view TabResultInquiry objects belonging to a tab. A TabResultInquiry + is an object that holds details on both the tab and a single payment made + for that tab. + + :type _tab: Tab + :type _payment: Payment + """ + + # Endpoint constants. + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/cash-register/{}/tab/{" \ + "}/tab-result-inquiry/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab/{}/tab-result-inquiry" + + # Object type. + _OBJECT_TYPE = "TabResultInquiry" + + def __init__(self): + self._tab = None + self._payment = None + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, cash_register_id, + tab_uuid, tab_result_inquiry_id, custom_headers=None): + """ + Used to view a single TabResultInquiry belonging to a tab. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_uuid: str + :type tab_result_inquiry_id: int + :type custom_headers: dict[str, str]|None + + :rtype: TabResultInquiry + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + cash_register_id, tab_uuid, + tab_result_inquiry_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, cash_register_id, + tab_uuid, custom_headers=None): + """ + Used to view a list of TabResultInquiry objects belonging to a tab. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype: list[TabResultInquiry] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + cash_register_id, + tab_uuid) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def tab(self): + """ + :rtype: Tab + """ + + return self._tab + + @property + def payment(self): + """ + :rtype: Payment + """ + + return self._payment + + +class TabResultResponse(model.BunqModel): + """ + Used to view TabResultResponse objects belonging to a tab. A + TabResultResponse is an object that holds details on a tab which has been + paid from the provided monetary account. + + :type _tab: Tab + :type _payment: Payment + """ + + # Endpoint constants. + _ENDPOINT_URL_READ = "user/{}/monetary-account/{}/tab-result-response/{}" + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/tab-result-response" + + # Object type. + _OBJECT_TYPE = "TabResultResponse" + + def __init__(self): + self._tab = None + self._payment = None + + @classmethod + def get(cls, api_context, user_id, monetary_account_id, + tab_result_response_id, custom_headers=None): + """ + Used to view a single TabResultResponse belonging to a tab. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type tab_result_response_id: int + :type custom_headers: dict[str, str]|None + + :rtype: TabResultResponse + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + monetary_account_id, + tab_result_response_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, + custom_headers=None): + """ + Used to view a list of TabResultResponse objects belonging to a tab. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[TabResultResponse] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def tab(self): + """ + :rtype: Tab + """ + + return self._tab + + @property + def payment(self): + """ + :rtype: Payment + """ + + return self._payment + + +class TabQrCodeContent(model.BunqModel): + """ + This call returns the raw content of the QR code that links to this Tab. + When a bunq user scans this QR code with the bunq app the Tab will be shown + on his/her device. + """ + + # Endpoint constants. + _ENDPOINT_URL_LISTING = "user/{}/monetary-account/{}/cash-register/{" \ + "}/tab/{}/qr-code-content" + + # Object type. + _OBJECT_TYPE = "TabQrCodeContent" + + @classmethod + def list(cls, api_context, user_id, monetary_account_id, cash_register_id, + tab_uuid, custom_headers=None): + """ + Returns the raw content of the QR code that links to this Tab. The raw + content is the binary representation of a file, without any JSON + wrapping. + + :type api_context: context.ApiContext + :type user_id: int + :type monetary_account_id: int + :type cash_register_id: int + :type tab_uuid: str + :type custom_headers: dict[str, str]|None + + :rtype: bytes + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id, + monetary_account_id, + cash_register_id, + tab_uuid) + + return api_client.get(endpoint_url, custom_headers).content + + +class TokenQrRequestIdeal(model.BunqModel): + """ + Using this call you create a request for payment from an external token + provided with an ideal transaction. Make sure your iDEAL payments are + compliant with the iDEAL standards, by following the following manual: https://www.bunq.com/files/media/legal + /en/20170315_ideal_standards_en.pdf. + It's very important to keep these points in mind when you are using the + endpoint to make iDEAL payments from your application. + + :type _time_responded: str + :type _time_expiry: str + :type _monetary_account_id: int + :type _amount_inquired: object_.Amount + :type _amount_responded: object_.Amount + :type _alias: object_.MonetaryAccountReference + :type _counterparty_alias: object_.MonetaryAccountReference + :type _description: str + :type _attachment: list[object_.Attachment] + :type _status: str + :type _minimum_age: int + :type _require_address: str + :type _address_shipping: object_.Address + :type _address_billing: object_.Address + :type _geolocation: object_.Geolocation + :type _redirect_url: str + :type _type_: str + :type _sub_type: str + :type _allow_chat: bool + :type _eligible_whitelist_id: int + """ + + # Field constants. + FIELD_TOKEN = "token" + + # Endpoint constants. + _ENDPOINT_URL_CREATE = "user/{}/token-qr-request-ideal" + + # Object type. + _OBJECT_TYPE = "TokenQrRequestIdeal" + + def __init__(self): + self._time_responded = None + self._time_expiry = None + self._monetary_account_id = None + self._amount_inquired = None + self._amount_responded = None + self._alias = None + self._counterparty_alias = None + self._description = None + self._attachment = None + self._status = None + self._minimum_age = None + self._require_address = None + self._address_shipping = None + self._address_billing = None + self._geolocation = None + self._redirect_url = None + self._type_ = None + self._sub_type = None + self._allow_chat = None + self._eligible_whitelist_id = None + + @classmethod + def create(cls, api_context, request_map, user_id, custom_headers=None): + """ + Create a request from an ideal transaction. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: TokenQrRequestIdeal + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_CREATE.format(user_id) + response = api_client.post(endpoint_url, request_bytes, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @property + def time_responded(self): + """ + :rtype: str + """ + + return self._time_responded + + @property + def time_expiry(self): + """ + :rtype: str + """ + + return self._time_expiry + + @property + def monetary_account_id(self): + """ + :rtype: int + """ + + return self._monetary_account_id + + @property + def amount_inquired(self): + """ + :rtype: object_.Amount + """ + + return self._amount_inquired + + @property + def amount_responded(self): + """ + :rtype: object_.Amount + """ + + return self._amount_responded + + @property + def alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._alias + + @property + def counterparty_alias(self): + """ + :rtype: object_.MonetaryAccountReference + """ + + return self._counterparty_alias + + @property + def description(self): + """ + :rtype: str + """ + + return self._description + + @property + def attachment(self): + """ + :rtype: list[object_.Attachment] + """ + + return self._attachment + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def minimum_age(self): + """ + :rtype: int + """ + + return self._minimum_age + + @property + def require_address(self): + """ + :rtype: str + """ + + return self._require_address + + @property + def address_shipping(self): + """ + :rtype: object_.Address + """ + + return self._address_shipping + + @property + def address_billing(self): + """ + :rtype: object_.Address + """ + + return self._address_billing + + @property + def geolocation(self): + """ + :rtype: object_.Geolocation + """ + + return self._geolocation + + @property + def redirect_url(self): + """ + :rtype: str + """ + + return self._redirect_url + + @property + def type_(self): + """ + :rtype: str + """ + + return self._type_ + + @property + def sub_type(self): + """ + :rtype: str + """ + + return self._sub_type + + @property + def allow_chat(self): + """ + :rtype: bool + """ + + return self._allow_chat + + @property + def eligible_whitelist_id(self): + """ + :rtype: int + """ + + return self._eligible_whitelist_id + + +class UserCompany(model.BunqModel): + """ + Show the authenticated user, if it is a company. + + :type _id_: int + :type _created: str + :type _updated: str + :type _public_uuid: str + :type _name: str + :type _display_name: str + :type _public_nick_name: str + :type _alias: list[object_.Pointer] + :type _chamber_of_commerce_number: str + :type _type_of_business_entity: str + :type _sector_of_industry: str + :type _counter_bank_iban: str + :type _avatar: object_.Avatar + :type _address_main: object_.Address + :type _address_postal: object_.Address + :type _version_terms_of_service: str + :type _director_alias: object_.LabelUser + :type _language: str + :type _region: str + :type _ubo: list[object_.Ubo] + :type _status: str + :type _sub_status: str + :type _session_timeout: int + :type _daily_limit_without_confirmation_login: object_.Amount + :type _notification_filters: list[object_.NotificationFilter] + """ + + # Field constants. + FIELD_NAME = "name" + FIELD_PUBLIC_NICK_NAME = "public_nick_name" + FIELD_AVATAR_UUID = "avatar_uuid" + FIELD_ADDRESS = "address" + FIELD_ADDRESS_MAIN = "address_main" + FIELD_ADDRESS_POSTAL = "address_postal" + FIELD_LANGUAGE = "language" + FIELD_REGION = "region" + FIELD_COUNTRY = "country" + FIELD_UBO = "ubo" + FIELD_CHAMBER_OF_COMMERCE_NUMBER = "chamber_of_commerce_number" + FIELD_STATUS = "status" + FIELD_SUB_STATUS = "sub_status" + FIELD_SESSION_TIMEOUT = "session_timeout" + FIELD_DAILY_LIMIT_WITHOUT_CONFIRMATION_LOGIN = \ + "daily_limit_without_confirmation_login" + FIELD_COUNTER_BANK_IBAN = "counter_bank_iban" + FIELD_NOTIFICATION_FILTERS = "notification_filters" + + # Endpoint constants. + _ENDPOINT_URL_READ = "user-company/{}" + _ENDPOINT_URL_UPDATE = "user-company/{}" + + # Object type. + _OBJECT_TYPE = "UserCompany" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._public_uuid = None + self._name = None + self._display_name = None + self._public_nick_name = None + self._alias = None + self._chamber_of_commerce_number = None + self._type_of_business_entity = None + self._sector_of_industry = None + self._counter_bank_iban = None + self._avatar = None + self._address_main = None + self._address_postal = None + self._version_terms_of_service = None + self._director_alias = None + self._language = None + self._region = None + self._ubo = None + self._status = None + self._sub_status = None + self._session_timeout = None + self._daily_limit_without_confirmation_login = None + self._notification_filters = None + + @classmethod + def get(cls, api_context, user_company_id, custom_headers=None): + """ + Get a specific company. + + :type api_context: context.ApiContext + :type user_company_id: int + :type custom_headers: dict[str, str]|None + + :rtype: UserCompany + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_company_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def update(cls, api_context, request_map, user_company_id, + custom_headers=None): + """ + Modify a specific company's data. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_company_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_company_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def public_uuid(self): + """ + :rtype: str + """ + + return self._public_uuid + + @property + def name(self): + """ + :rtype: str + """ + + return self._name + + @property + def display_name(self): + """ + :rtype: str + """ + + return self._display_name + + @property + def public_nick_name(self): + """ + :rtype: str + """ + + return self._public_nick_name + + @property + def alias(self): + """ + :rtype: list[object_.Pointer] + """ + + return self._alias + + @property + def chamber_of_commerce_number(self): + """ + :rtype: str + """ + + return self._chamber_of_commerce_number + + @property + def type_of_business_entity(self): + """ + :rtype: str + """ + + return self._type_of_business_entity + + @property + def sector_of_industry(self): + """ + :rtype: str + """ + + return self._sector_of_industry + + @property + def counter_bank_iban(self): + """ + :rtype: str + """ + + return self._counter_bank_iban + + @property + def avatar(self): + """ + :rtype: object_.Avatar + """ + + return self._avatar + + @property + def address_main(self): + """ + :rtype: object_.Address + """ + + return self._address_main + + @property + def address_postal(self): + """ + :rtype: object_.Address + """ + + return self._address_postal + + @property + def version_terms_of_service(self): + """ + :rtype: str + """ + + return self._version_terms_of_service + + @property + def director_alias(self): + """ + :rtype: object_.LabelUser + """ + + return self._director_alias + + @property + def language(self): + """ + :rtype: str + """ + + return self._language + + @property + def region(self): + """ + :rtype: str + """ + + return self._region + + @property + def ubo(self): + """ + :rtype: list[object_.Ubo] + """ + + return self._ubo + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def sub_status(self): + """ + :rtype: str + """ + + return self._sub_status + + @property + def session_timeout(self): + """ + :rtype: int + """ + + return self._session_timeout + + @property + def daily_limit_without_confirmation_login(self): + """ + :rtype: object_.Amount + """ + + return self._daily_limit_without_confirmation_login + + @property + def notification_filters(self): + """ + :rtype: list[object_.NotificationFilter] + """ + + return self._notification_filters + + +class UserCredentialPasswordIp(model.BunqModel): + """ + Create a credential of a user for server authentication, or delete the + credential of a user for server authentication. + + :type _id_: int + :type _created: str + :type _updated: str + :type _status: str + :type _expiry_time: str + :type _token_value: str + :type _permitted_device: object_.PermittedDevice + """ + + # Endpoint constants. + _ENDPOINT_URL_READ = "user/{}/credential-password-ip/{}" + _ENDPOINT_URL_LISTING = "user/{}/credential-password-ip" + + # Object type. + _OBJECT_TYPE = "CredentialPasswordIp" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._status = None + self._expiry_time = None + self._token_value = None + self._permitted_device = None + + @classmethod + def get(cls, api_context, user_id, user_credential_password_ip_id, + custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type user_credential_password_ip_id: int + :type custom_headers: dict[str, str]|None + + :rtype: UserCredentialPasswordIp + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id, + user_credential_password_ip_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def list(cls, api_context, user_id, custom_headers=None): + """ + :type api_context: context.ApiContext + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: list[UserCredentialPasswordIp] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING.format(user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def expiry_time(self): + """ + :rtype: str + """ + + return self._expiry_time + + @property + def token_value(self): + """ + :rtype: str + """ + + return self._token_value + + @property + def permitted_device(self): + """ + :rtype: object_.PermittedDevice + """ + + return self._permitted_device + + +class UserPerson(model.BunqModel): + """ + Show the authenticated user, if it is a person. + + :type _id_: int + :type _created: str + :type _updated: str + :type _public_uuid: str + :type _first_name: str + :type _middle_name: str + :type _last_name: str + :type _legal_name: str + :type _display_name: str + :type _public_nick_name: str + :type _alias: list[object_.Pointer] + :type _tax_resident: list[object_.TaxResident] + :type _document_type: str + :type _document_number: str + :type _document_country_of_issuance: str + :type _address_main: object_.Address + :type _address_postal: object_.Address + :type _date_of_birth: str + :type _place_of_birth: str + :type _country_of_birth: str + :type _nationality: str + :type _language: str + :type _region: str + :type _gender: str + :type _avatar: object_.Avatar + :type _version_terms_of_service: str + :type _status: str + :type _sub_status: str + :type _session_timeout: int + :type _daily_limit_without_confirmation_login: object_.Amount + :type _notification_filters: list[object_.NotificationFilter] + """ + + # Field constants. + FIELD_FIRST_NAME = "first_name" + FIELD_MIDDLE_NAME = "middle_name" + FIELD_LAST_NAME = "last_name" + FIELD_PUBLIC_NICK_NAME = "public_nick_name" + FIELD_ADDRESS = "address" + FIELD_ADDRESS_MAIN = "address_main" + FIELD_ADDRESS_POSTAL = "address_postal" + FIELD_AVATAR_UUID = "avatar_uuid" + FIELD_TAX_RESIDENT = "tax_resident" + FIELD_DOCUMENT_TYPE = "document_type" + FIELD_DOCUMENT_NUMBER = "document_number" + FIELD_DOCUMENT_COUNTRY_OF_ISSUANCE = "document_country_of_issuance" + FIELD_DOCUMENT_FRONT_ATTACHMENT_ID = "document_front_attachment_id" + FIELD_DOCUMENT_BACK_ATTACHMENT_ID = "document_back_attachment_id" + FIELD_DATE_OF_BIRTH = "date_of_birth" + FIELD_PLACE_OF_BIRTH = "place_of_birth" + FIELD_COUNTRY_OF_BIRTH = "country_of_birth" + FIELD_NATIONALITY = "nationality" + FIELD_LANGUAGE = "language" + FIELD_REGION = "region" + FIELD_GENDER = "gender" + FIELD_STATUS = "status" + FIELD_SUB_STATUS = "sub_status" + FIELD_LEGAL_GUARDIAN_ALIAS = "legal_guardian_alias" + FIELD_SESSION_TIMEOUT = "session_timeout" + FIELD_DAILY_LIMIT_WITHOUT_CONFIRMATION_LOGIN = \ + "daily_limit_without_confirmation_login" + FIELD_COUNTER_BANK_IBAN = "counter_bank_iban" + FIELD_NOTIFICATION_FILTERS = "notification_filters" + + # Endpoint constants. + _ENDPOINT_URL_READ = "user-person/{}" + _ENDPOINT_URL_UPDATE = "user-person/{}" + + # Object type. + _OBJECT_TYPE = "UserPerson" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._public_uuid = None + self._first_name = None + self._middle_name = None + self._last_name = None + self._legal_name = None + self._display_name = None + self._public_nick_name = None + self._alias = None + self._tax_resident = None + self._document_type = None + self._document_number = None + self._document_country_of_issuance = None + self._address_main = None + self._address_postal = None + self._date_of_birth = None + self._place_of_birth = None + self._country_of_birth = None + self._nationality = None + self._language = None + self._region = None + self._gender = None + self._avatar = None + self._version_terms_of_service = None + self._status = None + self._sub_status = None + self._session_timeout = None + self._daily_limit_without_confirmation_login = None + self._notification_filters = None + + @classmethod + def get(cls, api_context, user_person_id, custom_headers=None): + """ + Get a specific person. + + :type api_context: context.ApiContext + :type user_person_id: int + :type custom_headers: dict[str, str]|None + + :rtype: UserPerson + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_person_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @classmethod + def update(cls, api_context, request_map, user_person_id, + custom_headers=None): + """ + Modify a specific person object's data. + + :type api_context: context.ApiContext + :type request_map: dict[str, object] + :type user_person_id: int + :type custom_headers: dict[str, str]|None + + :rtype: int + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + request_bytes = converter.class_to_json(request_map).encode() + endpoint_url = cls._ENDPOINT_URL_UPDATE.format(user_person_id) + response = api_client.put(endpoint_url, request_bytes, custom_headers) + + return cls._process_for_id(response.text) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def public_uuid(self): + """ + :rtype: str + """ + + return self._public_uuid + + @property + def first_name(self): + """ + :rtype: str + """ + + return self._first_name + + @property + def middle_name(self): + """ + :rtype: str + """ + + return self._middle_name + + @property + def last_name(self): + """ + :rtype: str + """ + + return self._last_name + + @property + def legal_name(self): + """ + :rtype: str + """ + + return self._legal_name + + @property + def display_name(self): + """ + :rtype: str + """ + + return self._display_name + + @property + def public_nick_name(self): + """ + :rtype: str + """ + + return self._public_nick_name + + @property + def alias(self): + """ + :rtype: list[object_.Pointer] + """ + + return self._alias + + @property + def tax_resident(self): + """ + :rtype: list[object_.TaxResident] + """ + + return self._tax_resident + + @property + def document_type(self): + """ + :rtype: str + """ + + return self._document_type + + @property + def document_number(self): + """ + :rtype: str + """ + + return self._document_number + + @property + def document_country_of_issuance(self): + """ + :rtype: str + """ + + return self._document_country_of_issuance + + @property + def address_main(self): + """ + :rtype: object_.Address + """ + + return self._address_main + + @property + def address_postal(self): + """ + :rtype: object_.Address + """ + + return self._address_postal + + @property + def date_of_birth(self): + """ + :rtype: str + """ + + return self._date_of_birth + + @property + def place_of_birth(self): + """ + :rtype: str + """ + + return self._place_of_birth + + @property + def country_of_birth(self): + """ + :rtype: str + """ + + return self._country_of_birth + + @property + def nationality(self): + """ + :rtype: str + """ + + return self._nationality + + @property + def language(self): + """ + :rtype: str + """ + + return self._language + + @property + def region(self): + """ + :rtype: str + """ + + return self._region + + @property + def gender(self): + """ + :rtype: str + """ + + return self._gender + + @property + def avatar(self): + """ + :rtype: object_.Avatar + """ + + return self._avatar + + @property + def version_terms_of_service(self): + """ + :rtype: str + """ + + return self._version_terms_of_service + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def sub_status(self): + """ + :rtype: str + """ + + return self._sub_status + + @property + def session_timeout(self): + """ + :rtype: int + """ + + return self._session_timeout + + @property + def daily_limit_without_confirmation_login(self): + """ + :rtype: object_.Amount + """ + + return self._daily_limit_without_confirmation_login + + @property + def notification_filters(self): + """ + :rtype: list[object_.NotificationFilter] + """ + + return self._notification_filters + + +class User(model.BunqModel): + """ + Using this call you can retrieve information of the user you are logged in + as. This includes your user id, which is referred to in endpoints. + + :type _UserLight: UserLight + :type _UserPerson: UserPerson + :type _UserCompany: UserCompany + """ + + # Endpoint constants. + _ENDPOINT_URL_READ = "user/{}" + _ENDPOINT_URL_LISTING = "user" + + # Object type. + _OBJECT_TYPE = "User" + + def __init__(self): + self._UserLight = None + self._UserPerson = None + self._UserCompany = None + + @classmethod + def get(cls, api_context, user_id, custom_headers=None): + """ + Get a specific user. + + :type api_context: context.ApiContext + :type user_id: int + :type custom_headers: dict[str, str]|None + + :rtype: User + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text) + + @classmethod + def list(cls, api_context, custom_headers=None): + """ + Get a collection of all available users. + + :type api_context: context.ApiContext + :type custom_headers: dict[str, str]|None + + :rtype: list[User] + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_LISTING + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json_list(response.text) + + @property + def UserLight(self): + """ + :rtype: UserLight + """ + + return self._UserLight + + @property + def UserPerson(self): + """ + :rtype: UserPerson + """ + + return self._UserPerson + + @property + def UserCompany(self): + """ + :rtype: UserCompany + """ + + return self._UserCompany + + +class UserLight(model.BunqModel): + """ + Show the authenticated user, if it is a light user. + + :type _id_: int + :type _created: str + :type _updated: str + :type _public_uuid: str + :type _first_name: str + :type _middle_name: str + :type _last_name: str + :type _legal_name: str + :type _display_name: str + :type _public_nick_name: str + :type _alias: list[object_.Pointer] + :type _social_security_number: str + :type _tax_resident: list[object_.TaxResident] + :type _document_type: str + :type _document_number: str + :type _document_country_of_issuance: str + :type _address_main: object_.Address + :type _address_postal: object_.Address + :type _date_of_birth: str + :type _place_of_birth: str + :type _country_of_birth: str + :type _nationality: str + :type _language: str + :type _region: str + :type _gender: str + :type _avatar: object_.Avatar + :type _version_terms_of_service: str + :type _status: str + :type _sub_status: str + :type _session_timeout: int + :type _daily_limit_without_confirmation_login: object_.Amount + :type _notification_filters: list[object_.NotificationFilter] + """ + + # Field constants. + FIELD_FIRST_NAME = "first_name" + FIELD_MIDDLE_NAME = "middle_name" + FIELD_LAST_NAME = "last_name" + FIELD_PUBLIC_NICK_NAME = "public_nick_name" + FIELD_COUNTER_BANK_IBAN = "counter_bank_iban" + FIELD_ADDRESS = "address" + FIELD_ADDRESS_MAIN = "address_main" + FIELD_ADDRESS_POSTAL = "address_postal" + FIELD_AVATAR_UUID = "avatar_uuid" + FIELD_SOCIAL_SECURITY_NUMBER = "social_security_number" + FIELD_TAX_RESIDENT = "tax_resident" + FIELD_DOCUMENT_TYPE = "document_type" + FIELD_DOCUMENT_NUMBER = "document_number" + FIELD_DOCUMENT_COUNTRY_OF_ISSUANCE = "document_country_of_issuance" + FIELD_DOCUMENT_FRONT_ATTACHMENT_ID = "document_front_attachment_id" + FIELD_DOCUMENT_BACK_ATTACHMENT_ID = "document_back_attachment_id" + FIELD_DATE_OF_BIRTH = "date_of_birth" + FIELD_PLACE_OF_BIRTH = "place_of_birth" + FIELD_COUNTRY_OF_BIRTH = "country_of_birth" + FIELD_NATIONALITY = "nationality" + FIELD_LANGUAGE = "language" + FIELD_REGION = "region" + FIELD_GENDER = "gender" + FIELD_STATUS = "status" + FIELD_SUB_STATUS = "sub_status" + FIELD_LEGAL_GUARDIAN_ALIAS = "legal_guardian_alias" + FIELD_SESSION_TIMEOUT = "session_timeout" + FIELD_DAILY_LIMIT_WITHOUT_CONFIRMATION_LOGIN = \ + "daily_limit_without_confirmation_login" + FIELD_NOTIFICATION_FILTERS = "notification_filters" + + # Endpoint constants. + _ENDPOINT_URL_READ = "user-light/{}" + + # Object type. + _OBJECT_TYPE = "UserPerson" + + def __init__(self): + self._id_ = None + self._created = None + self._updated = None + self._public_uuid = None + self._first_name = None + self._middle_name = None + self._last_name = None + self._legal_name = None + self._display_name = None + self._public_nick_name = None + self._alias = None + self._social_security_number = None + self._tax_resident = None + self._document_type = None + self._document_number = None + self._document_country_of_issuance = None + self._address_main = None + self._address_postal = None + self._date_of_birth = None + self._place_of_birth = None + self._country_of_birth = None + self._nationality = None + self._language = None + self._region = None + self._gender = None + self._avatar = None + self._version_terms_of_service = None + self._status = None + self._sub_status = None + self._session_timeout = None + self._daily_limit_without_confirmation_login = None + self._notification_filters = None + + @classmethod + def get(cls, api_context, user_light_id, custom_headers=None): + """ + Get a specific bunq light user. + + :type api_context: context.ApiContext + :type user_light_id: int + :type custom_headers: dict[str, str]|None + + :rtype: UserLight + """ + + if custom_headers is None: + custom_headers = {} + + api_client = client.ApiClient(api_context) + endpoint_url = cls._ENDPOINT_URL_READ.format(user_light_id) + response = api_client.get(endpoint_url, custom_headers) + + return cls._from_json(response.text, cls._OBJECT_TYPE) + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + @property + def created(self): + """ + :rtype: str + """ + + return self._created + + @property + def updated(self): + """ + :rtype: str + """ + + return self._updated + + @property + def public_uuid(self): + """ + :rtype: str + """ + + return self._public_uuid + + @property + def first_name(self): + """ + :rtype: str + """ + + return self._first_name + + @property + def middle_name(self): + """ + :rtype: str + """ + + return self._middle_name + + @property + def last_name(self): + """ + :rtype: str + """ + + return self._last_name + + @property + def legal_name(self): + """ + :rtype: str + """ + + return self._legal_name + + @property + def display_name(self): + """ + :rtype: str + """ + + return self._display_name + + @property + def public_nick_name(self): + """ + :rtype: str + """ + + return self._public_nick_name + + @property + def alias(self): + """ + :rtype: list[object_.Pointer] + """ + + return self._alias + + @property + def social_security_number(self): + """ + :rtype: str + """ + + return self._social_security_number + + @property + def tax_resident(self): + """ + :rtype: list[object_.TaxResident] + """ + + return self._tax_resident + + @property + def document_type(self): + """ + :rtype: str + """ + + return self._document_type + + @property + def document_number(self): + """ + :rtype: str + """ + + return self._document_number + + @property + def document_country_of_issuance(self): + """ + :rtype: str + """ + + return self._document_country_of_issuance + + @property + def address_main(self): + """ + :rtype: object_.Address + """ + + return self._address_main + + @property + def address_postal(self): + """ + :rtype: object_.Address + """ + + return self._address_postal + + @property + def date_of_birth(self): + """ + :rtype: str + """ + + return self._date_of_birth + + @property + def place_of_birth(self): + """ + :rtype: str + """ + + return self._place_of_birth + + @property + def country_of_birth(self): + """ + :rtype: str + """ + + return self._country_of_birth + + @property + def nationality(self): + """ + :rtype: str + """ + + return self._nationality + + @property + def language(self): + """ + :rtype: str + """ + + return self._language + + @property + def region(self): + """ + :rtype: str + """ + + return self._region + + @property + def gender(self): + """ + :rtype: str + """ + + return self._gender + + @property + def avatar(self): + """ + :rtype: object_.Avatar + """ + + return self._avatar + + @property + def version_terms_of_service(self): + """ + :rtype: str + """ + + return self._version_terms_of_service + + @property + def status(self): + """ + :rtype: str + """ + + return self._status + + @property + def sub_status(self): + """ + :rtype: str + """ + + return self._sub_status + + @property + def session_timeout(self): + """ + :rtype: int + """ + + return self._session_timeout + + @property + def daily_limit_without_confirmation_login(self): + """ + :rtype: object_.Amount + """ + + return self._daily_limit_without_confirmation_login + + @property + def notification_filters(self): + """ + :rtype: list[object_.NotificationFilter] + """ + + return self._notification_filters diff --git a/bunq/sdk/model/generated/object_.py b/bunq/sdk/model/generated/object_.py new file mode 100644 index 0000000..88f8d10 --- /dev/null +++ b/bunq/sdk/model/generated/object_.py @@ -0,0 +1,717 @@ +# -*- coding: utf-8 -*- +from bunq.sdk import model + + +class InvoiceItemGroup(model.BunqModel): + """ + :type type_: str + :type type_description: str + :type type_description_translated: str + :type instance_description: str + :type product_vat_exclusive: Amount + :type product_vat_inclusive: Amount + :type item: InvoiceItem + """ + + def __init__(self): + self.type_ = None + self.type_description = None + self.type_description_translated = None + self.instance_description = None + self.product_vat_exclusive = None + self.product_vat_inclusive = None + self.item = None + + +class Amount(model.BunqModel): + """ + :type value: str + :type currency: str + """ + + def __init__(self, value, currency): + """ + :type value: str + :type currency: str + """ + + self.value = value + self.currency = currency + + +class InvoiceItem(model.BunqModel): + """ + :type billing_date: str + :type type_description: str + :type type_description_translated: str + :type unit_vat_exclusive: Amount + :type unit_vat_inclusive: Amount + :type vat: float + :type quantity: float + :type total_vat_exclusive: Amount + :type total_vat_inclusive: Amount + """ + + def __init__(self): + self.billing_date = None + self.type_description = None + self.type_description_translated = None + self.unit_vat_exclusive = None + self.unit_vat_inclusive = None + self.vat = None + self.quantity = None + self.total_vat_exclusive = None + self.total_vat_inclusive = None + + +class LabelMonetaryAccount(model.BunqModel): + """ + :type iban: str + :type display_name: str + :type avatar: Avatar + :type label_user: LabelUser + :type country: str + :type bunq_me: MonetaryAccountReference + """ + + def __init__(self): + self.iban = None + self.display_name = None + self.avatar = None + self.label_user = None + self.country = None + self.bunq_me = None + + +class Avatar(model.BunqModel): + """ + :type uuid: str + :type anchor_uuid: str + :type image: list[Image] + """ + + def __init__(self, uuid): + """ + :type uuid: str + """ + + self.uuid = uuid + self.anchor_uuid = None + self.image = None + + +class Image(model.BunqModel): + """ + :type attachment_public_uuid: str + :type content_type: str + :type height: int + :type width: int + """ + + def __init__(self): + self.attachment_public_uuid = None + self.content_type = None + self.height = None + self.width = None + + +class LabelUser(model.BunqModel): + """ + :type uuid: str + :type display_name: str + :type country: str + :type avatar: Avatar + :type public_nick_name: str + """ + + def __init__(self, uuid, display_name, country): + """ + :type uuid: str + :type display_name: str + :type country: str + """ + + self.uuid = uuid + self.display_name = display_name + self.country = country + self.avatar = None + self.public_nick_name = None + + +class Pointer(model.BunqModel): + """ + :type type_: str + :type value: str + :type name: str + """ + + def __init__(self, type_, value): + """ + :type type_: str + :type value: str + """ + + self.type_ = type_ + self.value = value + self.name = None + + +class Address(model.BunqModel): + """ + :type street: str + :type house_number: str + :type po_box: str + :type postal_code: str + :type city: str + :type country: str + """ + + def __init__(self, street, house_number, postal_code, city, country): + """ + :type street: str + :type house_number: str + :type postal_code: str + :type city: str + :type country: str + """ + + self.street = street + self.house_number = house_number + self.postal_code = postal_code + self.city = city + self.country = country + self.po_box = None + + +class BunqId(model.BunqModel): + """ + :type id_: int + """ + + def __init__(self, id_): + """ + :type id_: int + """ + + self.id_ = id_ + + +class Attachment(model.BunqModel): + """ + :type description: str + :type content_type: str + """ + + def __init__(self): + self.description = None + self.content_type = None + + +class CardLimit(model.BunqModel): + """ + :type daily_limit: str + :type currency: str + :type type_: str + """ + + def __init__(self, daily_limit, currency, type_): + """ + :type daily_limit: str + :type currency: str + :type type_: str + """ + + self.daily_limit = daily_limit + self.currency = currency + self.type_ = type_ + + +class CardCountryPermission(model.BunqModel): + """ + :type country: str + :type expiry_time: str + """ + + def __init__(self, country): + """ + :type country: str + """ + + self.country = country + self.expiry_time = None + + +class CardMagStripePermission(model.BunqModel): + """ + :type expiry_time: str + """ + + def __init__(self): + self.expiry_time = None + + +class Geolocation(model.BunqModel): + """ + :type latitude: float + :type longitude: float + :type altitude: float + :type radius: float + """ + + def __init__(self): + self.latitude = None + self.longitude = None + self.altitude = None + self.radius = None + + +class NotificationFilter(model.BunqModel): + """ + :type notification_delivery_method: str + :type notification_target: str + :type category: str + """ + + def __init__(self, notification_delivery_method, notification_target, + category): + """ + :type notification_delivery_method: str + :type notification_target: str + :type category: str + """ + + self.notification_delivery_method = notification_delivery_method + self.notification_target = notification_target + self.category = category + + +class TabTextWaitingScreen(model.BunqModel): + """ + :type language: str + :type description: str + """ + + def __init__(self, language, description): + """ + :type language: str + :type description: str + """ + + self.language = language + self.description = description + + +class TabVisibility(model.BunqModel): + """ + :type cash_register_qr_code: bool + :type tab_qr_code: bool + :type location: Geolocation + """ + + def __init__(self, cash_register_qr_code, tab_qr_code): + """ + :type cash_register_qr_code: bool + :type tab_qr_code: bool + """ + + self.cash_register_qr_code = cash_register_qr_code + self.tab_qr_code = tab_qr_code + self.location = None + + +class AttachmentPublic(model.BunqModel): + """ + :type uuid: str + :type description: str + :type content_type: str + """ + + def __init__(self): + self.uuid = None + self.description = None + self.content_type = None + + +class AttachmentTab(model.BunqModel): + """ + :type id_: int + :type description: str + :type content_type: str + """ + + def __init__(self): + self.id_ = None + self.description = None + self.content_type = None + + +class Certificate(model.BunqModel): + """ + :type certificate: str + """ + + def __init__(self, certificate): + """ + :type certificate: str + """ + + self.certificate = certificate + + +class DraftShareInviteBankEntry(model.BunqModel): + """ + :type share_detail: ShareDetail + :type start_date: str + :type end_date: str + """ + + def __init__(self, share_detail): + """ + :type share_detail: ShareDetail + """ + + self.share_detail = share_detail + self.start_date = None + self.end_date = None + + +class ShareDetail(model.BunqModel): + """ + :type payment: ShareDetailPayment + :type read_only: ShareDetailReadOnly + :type draft_payment: ShareDetailDraftPayment + """ + + def __init__(self): + self.payment = None + self.read_only = None + self.draft_payment = None + + +class ShareDetailPayment(model.BunqModel): + """ + :type make_payments: bool + :type make_draft_payments: bool + :type view_balance: bool + :type view_old_events: bool + :type view_new_events: bool + :type budget: BudgetRestriction + """ + + def __init__(self, make_payments, view_balance, view_old_events, + view_new_events): + """ + :type make_payments: bool + :type view_balance: bool + :type view_old_events: bool + :type view_new_events: bool + """ + + self.make_payments = make_payments + self.view_balance = view_balance + self.view_old_events = view_old_events + self.view_new_events = view_new_events + self.make_draft_payments = None + self.budget = None + + +class BudgetRestriction(model.BunqModel): + """ + :type amount: Amount + :type frequency: str + """ + + def __init__(self): + self.amount = None + self.frequency = None + + +class ShareDetailReadOnly(model.BunqModel): + """ + :type view_balance: bool + :type view_old_events: bool + :type view_new_events: bool + """ + + def __init__(self, view_balance, view_old_events, view_new_events): + """ + :type view_balance: bool + :type view_old_events: bool + :type view_new_events: bool + """ + + self.view_balance = view_balance + self.view_old_events = view_old_events + self.view_new_events = view_new_events + + +class ShareDetailDraftPayment(model.BunqModel): + """ + :type make_draft_payments: bool + :type view_balance: bool + :type view_old_events: bool + :type view_new_events: bool + """ + + def __init__(self, make_draft_payments, view_balance, view_old_events, + view_new_events): + """ + :type make_draft_payments: bool + :type view_balance: bool + :type view_old_events: bool + :type view_new_events: bool + """ + + self.make_draft_payments = make_draft_payments + self.view_balance = view_balance + self.view_old_events = view_old_events + self.view_new_events = view_new_events + + +class MonetaryAccountProfileFill(model.BunqModel): + """ + :type status: str + :type balance_preferred: Amount + :type balance_threshold_low: Amount + :type method_fill: str + :type issuer: Issuer + """ + + def __init__(self, status, balance_preferred, balance_threshold_low, + method_fill): + """ + :type status: str + :type balance_preferred: Amount + :type balance_threshold_low: Amount + :type method_fill: str + """ + + self.status = status + self.balance_preferred = balance_preferred + self.balance_threshold_low = balance_threshold_low + self.method_fill = method_fill + self.issuer = None + + +class Issuer(model.BunqModel): + """ + :type bic: str + :type name: str + """ + + def __init__(self, bic): + """ + :type bic: str + """ + + self.bic = bic + self.name = None + + +class MonetaryAccountProfileDrain(model.BunqModel): + """ + :type status: str + :type balance_preferred: Amount + :type balance_threshold_high: Amount + :type savings_account_alias: MonetaryAccountReference + """ + + def __init__(self, status, balance_preferred, balance_threshold_high, + savings_account_alias): + """ + :type status: str + :type balance_preferred: Amount + :type balance_threshold_high: Amount + :type savings_account_alias: MonetaryAccountReference + """ + + self.status = status + self.balance_preferred = balance_preferred + self.balance_threshold_high = balance_threshold_high + self.savings_account_alias = savings_account_alias + + +class MonetaryAccountSetting(model.BunqModel): + """ + :type color: str + :type default_avatar_status: str + :type restriction_chat: str + """ + + def __init__(self): + self.color = None + self.default_avatar_status = None + self.restriction_chat = None + + +class AttachmentMonetaryAccountPayment(model.BunqModel): + """ + :type id_: int + :type monetary_account_id: int + """ + + def __init__(self, id_): + """ + :type id_: int + """ + + self.id_ = id_ + self.monetary_account_id = None + + +class Error(model.BunqModel): + """ + :type error_description: str + :type error_description_translated: str + """ + + def __init__(self): + self.error_description = None + self.error_description_translated = None + + +class SchedulePaymentEntry(model.BunqModel): + """ + :type amount: Amount + :type counterparty_alias: MonetaryAccountReference + :type description: str + :type attachment: list[AttachmentMonetaryAccountPayment] + :type merchant_reference: str + :type allow_bunqto: bool + :type alias: MonetaryAccountReference + """ + + def __init__(self, amount, counterparty_alias, description): + """ + :type amount: Amount + :type counterparty_alias: MonetaryAccountReference + :type description: str + """ + + self.amount = amount + self.counterparty_alias = counterparty_alias + self.description = description + self.attachment = None + self.merchant_reference = None + self.allow_bunqto = None + self.alias = None + + +class Schedule(model.BunqModel): + """ + :type time_start: str + :type time_end: str + :type recurrence_unit: str + :type recurrence_size: int + :type status: str + :type object_: model.BunqModel + """ + + def __init__(self, time_start, recurrence_unit, recurrence_size): + """ + :type time_start: str + :type recurrence_unit: str + :type recurrence_size: int + """ + + self.time_start = time_start + self.recurrence_unit = recurrence_unit + self.recurrence_size = recurrence_size + self.time_end = None + self.status = None + self.object_ = None + + +class Ubo(model.BunqModel): + """ + :type name: str + :type date_of_birth: str + :type nationality: str + """ + + def __init__(self): + self.name = None + self.date_of_birth = None + self.nationality = None + + +class PermittedDevice(model.BunqModel): + """ + :type description: str + :type ip: str + """ + + def __init__(self): + self.description = None + self.ip = None + + +class TaxResident(model.BunqModel): + """ + :type country: str + :type tax_number: str + """ + + def __init__(self, country, tax_number): + """ + :type country: str + :type tax_number: str + """ + + self.country = country + self.tax_number = tax_number + + +class MonetaryAccountReference(model.BunqModel): + """ + :type pointer: Pointer + :type label_monetary_account: LabelMonetaryAccount + """ + + # Error constants + _ERROR_COULD_NOT_INSTANTIATE = 'Could not directly instantiate ' \ + 'MonetaryAccountReference. Please use ' \ + 'the class factory methods.' + + # Pointer type for creating pointer from LabelMonetaryAccount + _POINTER_TYPE_IBAN = 'IBAN' + + def __init__(self): + """ + :raise: TypeError always, to prevent instantiation via constructor. + """ + + self.pointer = None + self.label_monetary_account = None + + raise TypeError(self._ERROR_COULD_NOT_INSTANTIATE) + + @classmethod + def create_from_pointer(cls, pointer): + """ + :type pointer: Pointer + """ + + instance = cls.__new__(cls) + instance.pointer = pointer + instance.label_monetary_account = LabelMonetaryAccount() + instance.label_monetary_account.iban = pointer.value + instance.label_monetary_account.display_name = pointer.name + + return instance + + @classmethod + def create_from_label_monetary_account(cls, label_monetary_account): + """ + :type label_monetary_account: LabelMonetaryAccount + """ + + instance = cls.__new__(cls) + instance.label_monetary_account = label_monetary_account + instance.pointer = Pointer( + cls._POINTER_TYPE_IBAN, + label_monetary_account.iban + ) + instance.pointer.name = label_monetary_account.display_name + + return instance diff --git a/bunq/sdk/model/model.py b/bunq/sdk/model/model.py new file mode 100644 index 0000000..f04b922 --- /dev/null +++ b/bunq/sdk/model/model.py @@ -0,0 +1,381 @@ +from bunq.sdk import client +from bunq.sdk import context +from bunq.sdk.json import converter + + +class BunqModel(object): + # Field constants + _FIELD_RESPONSE = 'Response' + _FIELD_ID = 'Id' + _FIELD_UUID = 'Uuid' + + # The very first index of an array + _INDEX_FIRST = 0 + + def to_json(self): + """ + :rtype: str + """ + + return converter.class_to_json(self) + + @classmethod + def _from_json_array_nested(cls, response_json): + """ + :type response_json: str + + :rtype: cls + """ + + obj = converter.json_to_class(dict, response_json) + + return converter.deserialize(cls, obj[cls._FIELD_RESPONSE]) + + @classmethod + def _from_json(cls, response_json, wrapper=None): + """ + :type response_json: str + :type wrapper: str|None + + :rtype: cls + """ + + obj = converter.json_to_class(dict, response_json) + + return converter.deserialize( + cls, + cls._unwrap_response_single(obj, wrapper) + ) + + @classmethod + def _unwrap_response_single(cls, obj, wrapper=None): + """ + :type obj: dict + :type wrapper: str|None + + :rtype: dict + """ + + if wrapper is not None: + return obj[cls._FIELD_RESPONSE][cls._INDEX_FIRST][wrapper] + + return obj[cls._FIELD_RESPONSE][cls._INDEX_FIRST] + + @classmethod + def _process_for_id(cls, response_json): + """ + :type response_json: str + + :rtype: Id + """ + + obj = converter.json_to_class(dict, response_json) + id_ = converter.deserialize( + Id, + cls._unwrap_response_single(obj, cls._FIELD_ID) + ) + + return id_.id_ + + @classmethod + def _process_for_uuid(cls, response_json): + """ + :type response_json: str + + :rtype: Uuid + """ + + obj = converter.json_to_class(dict, response_json) + uuid = converter.deserialize( + Uuid, + cls._unwrap_response_single(obj, cls._FIELD_UUID) + ) + + return uuid.uuid + + @classmethod + def _from_json_list(cls, response_json, wrapper=None): + """ + :type response_json: str + :type wrapper: str|None + + :rtype: list[cls] + """ + + obj = converter.json_to_class(dict, response_json) + array = obj[cls._FIELD_RESPONSE] + array_deserialized = [] + + for item in array: + item_unwrapped = item if wrapper is None else item[wrapper] + item_deserialized = converter.deserialize(cls, item_unwrapped) + array_deserialized.append(item_deserialized) + + return array_deserialized + + +class Id(BunqModel): + """ + :type _id_: int + """ + + def __init__(self): + self._id_ = None + + @property + def id_(self): + """ + :rtype: int + """ + + return self._id_ + + +class Uuid(BunqModel): + """ + :type _uuid: str + """ + + def __init__(self): + self._uuid = None + + @property + def uuid(self): + """ + :rtype: str + """ + + return self._uuid + + +class SessionToken(BunqModel): + """ + :type _token: str + """ + + def __init__(self): + self._token = None + + @property + def token(self): + """ + :rtype: str + """ + + return self._token + + +class PublicKeyServer(BunqModel): + """ + :type _server_public_key: str + """ + + def __init__(self): + self._server_public_key = None + + @property + def server_public_key(self): + """ + :rtype: str + """ + + return self._server_public_key + + +class Installation(BunqModel): + """ + :type _id_: Id + :type _token: SessionToken + :type _server_public_key: PublicKeyServer + """ + + # Endpoint name. + _ENDPOINT_URL_POST = "installation" + + # Field constants. + FIELD_CLIENT_PUBLIC_KEY = "client_public_key" + + def __init__(self): + self._id_ = None + self._token = None + self._server_public_key = None + + @property + def id_(self): + """ + :rtype: Id + """ + + return self._id_ + + @property + def token(self): + """ + :rtype: SessionToken + """ + + return self._token + + @property + def server_public_key(self): + """ + :rtype: PublicKeyServer + """ + + return self._server_public_key + + @classmethod + def create(cls, api_context, public_key_string): + """ + :type api_context: context.ApiContext + :type public_key_string: str + + :rtype: Installation + """ + + api_client = client.ApiClient(api_context) + body_bytes = cls.generate_request_body_bytes( + public_key_string + ) + response = api_client.post(cls._ENDPOINT_URL_POST, body_bytes, {}) + + return cls._from_json_array_nested(response.text) + + @classmethod + def generate_request_body_bytes(cls, public_key_string): + """ + :type public_key_string: str + + :rtype: bytes + """ + + return converter.class_to_json( + { + cls.FIELD_CLIENT_PUBLIC_KEY: public_key_string, + } + ).encode() + + +class DeviceServer(BunqModel): + # Endpoint name. + _ENDPOINT_URL_POST = "device-server" + + # Field constants + FIELD_DESCRIPTION = "description" + FIELD_SECRET = "secret" + FIELD_PERMITTED_IPS = "permitted_ips" + + @classmethod + def create(cls, api_context, description, permitted_ips): + """ + :type api_context: context.ApiContext + :type description: str + :type permitted_ips: list[str] + + :rtype: int + """ + + api_client = client.ApiClient(api_context) + body_bytes = cls.generate_request_body_bytes( + description, + api_context.api_key, + permitted_ips + ) + response = api_client.post(cls._ENDPOINT_URL_POST, body_bytes, {}) + + return cls._process_for_id(response.text) + + @classmethod + def generate_request_body_bytes(cls, description, secret, permitted_ips): + """ + :type description: str + :type secret: str + :type permitted_ips: list[str] + + :rtype: bytes + """ + + return converter.class_to_json( + { + cls.FIELD_DESCRIPTION: description, + cls.FIELD_SECRET: secret, + cls.FIELD_PERMITTED_IPS: permitted_ips, + } + ).encode() + + +class SessionServer(BunqModel): + """ + :type _id_: Id + :type _token: SessionToken + :type _user_person: bunq.sdk.model.generated.UserPerson + :type _user_company: bunq.sdk.model.generated.UserCompany + """ + + # Endpoint name. + _ENDPOINT_URL_POST = "session-server" + + # Field constants + FIELD_SECRET = "secret" + + def __init__(self): + self._id_ = None + self._token = None + self._user_person = None + self._user_company = None + + @property + def id_(self): + """ + :rtype: Id + """ + + return self._id_ + + @property + def token(self): + """ + :rtype: SessionToken + """ + + return self._token + + @property + def user_person(self): + """ + :rtype: bunq.sdk.model.generated.UserPerson + """ + + return self._user_person + + @property + def user_company(self): + """ + :rtype: bunq.sdk.model.generated.UserCompany + """ + + return self._user_company + + @classmethod + def create(cls, api_context): + """ + :type api_context: context.ApiContext + + :rtype: SessionServer + """ + + api_client = client.ApiClient(api_context) + body_bytes = cls.generate_request_body_bytes(api_context.api_key) + response = api_client.post(cls._ENDPOINT_URL_POST, body_bytes, {}) + + return cls._from_json_array_nested(response.text) + + @classmethod + def generate_request_body_bytes(cls, secret): + """ + :type secret: str + + :rtype: bytes + """ + + return converter.class_to_json({cls.FIELD_SECRET: secret}).encode() diff --git a/bunq/sdk/security.py b/bunq/sdk/security.py new file mode 100644 index 0000000..d009b2d --- /dev/null +++ b/bunq/sdk/security.py @@ -0,0 +1,231 @@ +import base64 +import hmac +import re +from base64 import b64encode +from hashlib import sha1 + +from Cryptodome import Cipher +from Cryptodome import Random +from Cryptodome.Cipher import AES +from Cryptodome.Cipher import PKCS1_v1_5 as PKCS1_v1_5_Cipher +from Cryptodome.Hash import SHA256 +from Cryptodome.PublicKey import RSA +from Cryptodome.Signature import PKCS1_v1_5 + +from bunq.sdk import context + +# Size of private RSA key to generate +_RSA_KEY_SIZE = 2048 + +# Constants to perform re-encoding of the public key +_PATTERN_RSA = r' RSA ' +_REPLACEMENT_RSA = ' ' + +# Number of PKCS to use for exporting the private key +_PKCS_NUMBER_PRIVATE_KEY = 8 + +# Constants to generate request head string +_FORMAT_METHOD_AND_ENDPOINT = '{} /v1/{}\n' +_FORMAT_HEADER_STRING = '{}: {}\n' +_DELIMITER_NEWLINE = '\n' + +# Constants for building header string to sign +_PATTERN_HEADER_PREFIX_BUNQ = r'X-Bunq-' +_HEADER_CACHE_CONTROL = 'Cache-Control' +_HEADER_USER_AGENT = 'User-Agent' + +# Constants for AES encryption +_AES_KEY_SIZE = 32 +_BLOCK_SIZE = 16 + +# Encryption-specific headers +_HEADER_CLIENT_ENCRYPTION_KEY = 'X-Bunq-Client-Encryption-Key' +_HEADER_CLIENT_ENCRYPTION_IV = 'X-Bunq-Client-Encryption-Iv' +_HEADER_CLIENT_ENCRYPTION_HMAC = 'X-Bunq-Client-Encryption-Hmac' + + +def generate_rsa_private_key(): + """ + :rtype: RSA.RsaKey + """ + + return RSA.generate(_RSA_KEY_SIZE) + + +def public_key_to_string(public_key): + """ + :type public_key: RSA.RsaKey + + :rtype: str + """ + + return re.sub( + _PATTERN_RSA, + _REPLACEMENT_RSA, + public_key.exportKey().decode() + ) + + +def private_key_to_string(private_key): + """ + :type private_key: RSA.RsaKey + + :rtype: str + """ + + return private_key.exportKey(pkcs=_PKCS_NUMBER_PRIVATE_KEY).decode() + + +def rsa_key_from_string(string): + """ + :type string: str + + :rtype: RSA.RsaKey + """ + + return RSA.import_key(string) + + +def sign_request(private_key, method, endpoint, body_bytes, headers): + """ + :type private_key: RSA.RsaKey + :type method: str + :type endpoint: str + :type body_bytes: bytes + :type headers: dict[str, str] + + :rtype: str + """ + + head_bytes = _generate_head_bytes(method, endpoint, headers) + bytes_to_sign = head_bytes + body_bytes + signer = PKCS1_v1_5.new(private_key) + digest = SHA256.new() + digest.update(bytes_to_sign) + sign = signer.sign(digest) + + return b64encode(sign) + + +def _generate_head_bytes(method, endpoint, headers): + """ + :type method: str + :type endpoint: str + :type headers: dict[str, str] + + :rtype: bytes + """ + + header_tuples = sorted((k, headers[k]) for k in headers) + head_string = _FORMAT_METHOD_AND_ENDPOINT.format(method, endpoint) + + for name, value in header_tuples: + if _should_sign_header(name): + head_string += _FORMAT_HEADER_STRING.format(name, value) + + return (head_string + _DELIMITER_NEWLINE).encode() + + +def _should_sign_header(header_name): + """ + :type header_name: str + + :rtype: bool + """ + + if header_name in {_HEADER_USER_AGENT, _HEADER_CACHE_CONTROL}: + return True + + if re.match(_PATTERN_HEADER_PREFIX_BUNQ, header_name): + return True + + return False + + +def encrypt(api_context, request_bytes, custom_headers): + """ + :type api_context: context.ApiContext + :type request_bytes: bytes + :type custom_headers: dict[str, str] + + :rtype: bytes + """ + + key = Random.get_random_bytes(_AES_KEY_SIZE) + iv = Random.get_random_bytes(_BLOCK_SIZE) + _add_header_client_encryption_key(api_context, key, custom_headers) + _add_header_client_encryption_iv(iv, custom_headers) + request_bytes = _encrypt_request_bytes(request_bytes, key, iv) + _add_header_client_encryption_hmac(request_bytes, key, iv, custom_headers) + + return request_bytes + + +def _add_header_client_encryption_key(api_context, key, custom_headers): + """ + :type api_context: context.ApiContext + :type key: bytes + :type custom_headers: dict[str, str] + + :rtype: None + """ + + public_key_server = api_context.installation_context.public_key_server + key_cipher = PKCS1_v1_5_Cipher.new(public_key_server) + key_encrypted = key_cipher.encrypt(key) + key_encrypted_base64 = base64.b64encode(key_encrypted).decode() + custom_headers[_HEADER_CLIENT_ENCRYPTION_KEY] = key_encrypted_base64 + + +def _add_header_client_encryption_iv(iv, custom_headers): + """ + :type iv: bytes + :type custom_headers: dict[str, str] + + :rtype: None + """ + + custom_headers[_HEADER_CLIENT_ENCRYPTION_IV] = base64.b64encode(iv).decode() + + +def _encrypt_request_bytes(request_bytes, key, iv): + """ + :type request_bytes: bytes + :type key: bytes + :type iv: bytes + + :rtype: bytes + """ + + cipher = Cipher.AES.new(key, Cipher.AES.MODE_CBC, iv) + request_bytes_padded = _pad_bytes(request_bytes) + + return cipher.encrypt(request_bytes_padded) + + +def _pad_bytes(request_bytes): + """ + :type request_bytes: bytes + + :rtype: bytes + """ + + padding_length = (_BLOCK_SIZE - len(request_bytes) % _BLOCK_SIZE) + padding_character = bytes(bytearray([padding_length])) + + return request_bytes + padding_character * padding_length + + +def _add_header_client_encryption_hmac(request_bytes, key, iv, custom_headers): + """ + :type request_bytes: bytes + :type key: bytes + :type iv: bytes + :type custom_headers: dict[str, str] + + :rtype: None + """ + + hashed = hmac.new(key, iv + request_bytes, sha1) + hashed_base64 = base64.b64encode(hashed.digest()).decode() + custom_headers[_HEADER_CLIENT_ENCRYPTION_HMAC] = hashed_base64 diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 0000000..b54fde3 --- /dev/null +++ b/examples/__init__.py @@ -0,0 +1 @@ +from examples import * diff --git a/examples/api_context_save_example.py b/examples/api_context_save_example.py new file mode 100644 index 0000000..1153195 --- /dev/null +++ b/examples/api_context_save_example.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +from bunq.sdk import context +from bunq.sdk.json import converter + + +def run(): + ctx = context.ApiContext( + context.ApiEnvironmentType.SANDBOX, + '###YOUR_API_KEY###', # Put your API key here + 'test device python' + ) + + ctx.save() + ctx_restored = context.ApiContext.restore() + print('Is original context equal the one saved and restored?:', + converter.class_to_json(ctx) == converter.class_to_json(ctx_restored)) diff --git a/examples/attachment_public_example.py b/examples/attachment_public_example.py new file mode 100644 index 0000000..5dbbc42 --- /dev/null +++ b/examples/attachment_public_example.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +import errno +import os + +from bunq.sdk import client +from bunq.sdk import context +from bunq.sdk.model import generated + +_CONTENT_TYPE_IMAGE_JPEG = 'image/jpeg' +_DESCRIPTION_TEST_JPG_ATTACHMENT = 'A test JPG attachment.' +_PATH_ATTACHMENT_IN = 'assets/attachment.jpg' +_MODE_READ_BINARY = 'rb' +_PATH_ATTACHMENT_OUT = 'tmp/attachment_out.jpg' +_MODE_WRITE_BINARY = 'wb' + + +def run(): + api_context = context.ApiContext.restore() + custom_headers = { + client.ApiClient.HEADER_CONTENT_TYPE: _CONTENT_TYPE_IMAGE_JPEG, + client.ApiClient.HEADER_ATTACHMENT_DESCRIPTION: + _DESCRIPTION_TEST_JPG_ATTACHMENT, + } + + attachment_bytes = read_attachment_in_bytes() + attachment_uuid = generated.AttachmentPublic.create( + api_context, + attachment_bytes, + custom_headers + ) + attachment_bytes2 = generated.AttachmentPublicContent.list( + api_context, + attachment_uuid + ) + + if not os.path.exists(os.path.dirname(_PATH_ATTACHMENT_OUT)): + try: + os.makedirs(os.path.dirname(_PATH_ATTACHMENT_OUT)) + except OSError as exc: # Guard against race condition + if exc.errno != errno.EEXIST: + raise + + write_attachment_out_bytes(attachment_bytes2) + + +def read_attachment_in_bytes(): + """ + :rtype: bytes + """ + + with open(_PATH_ATTACHMENT_IN, _MODE_READ_BINARY) as attachment_file: + return attachment_file.read() + + +def write_attachment_out_bytes(bytes_): + """ + :type bytes_: bytes + """ + + with open(_PATH_ATTACHMENT_OUT, _MODE_WRITE_BINARY) as attachment_file2: + attachment_file2.write(bytes_) diff --git a/examples/card_debit_example.py b/examples/card_debit_example.py new file mode 100644 index 0000000..16b9aec --- /dev/null +++ b/examples/card_debit_example.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +import random +import string + +from bunq.sdk import context +from bunq.sdk.model import generated +from bunq.sdk.model.generated import object_ + +# Empty string to join the random values of second string +_STRING_EMPTY = '' + +# Example constants +_POINTER_TYPE_EMAIL = 'EMAIL' +_EMAIL_YOUR_COMPANY = 'COMPANY_EMAIL_HERE' # Put your company email here +_NAME_YOUR_COMPANY = 'COMPANY_NAME_HERE' # Put your company name here +_USER_ITEM_ID = 0 # Put your user ID here +_PIN_CODE = '9922' +_POINTER_NAME_TEST = 'test pointer' +_SECOND_LINE_LENGTH_MAXIMUM = 20 + + +def run(): + api_context = context.ApiContext.restore() + pointer = object_.Pointer(_POINTER_TYPE_EMAIL, _EMAIL_YOUR_COMPANY) + pointer.name = _POINTER_NAME_TEST + request_map = { + generated.CardDebit.FIELD_ALIAS: pointer, + generated.CardDebit.FIELD_NAME_ON_CARD: _NAME_YOUR_COMPANY, + generated.CardDebit.FIELD_PIN_CODE: _PIN_CODE, + generated.CardDebit.FIELD_SECOND_LINE: _make_second_line() + } + + print(generated.CardDebit.create(api_context, request_map, + _USER_ITEM_ID).to_json()) + + +def _make_second_line(): + second_line_characters = [] + + for _ in range(_SECOND_LINE_LENGTH_MAXIMUM): + next_char = random.choice(string.ascii_uppercase) + second_line_characters.append(next_char) + + return _STRING_EMPTY.join(second_line_characters) diff --git a/examples/customer_statement_export_example.py b/examples/customer_statement_export_example.py new file mode 100644 index 0000000..ae2cf1a --- /dev/null +++ b/examples/customer_statement_export_example.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +import datetime + +from bunq.sdk import context +from bunq.sdk.model import generated + +# Desired format of customer statement +_CUSTOMER_STATEMENT_FORMAT = 'PDF' + +# Number of days in a week +_DAYS_IN_WEEK = 7 + +# Date format required for customer statement export endpoint +_FORMAT_DATE = '%Y-%m-%d' + + +def run(): + api_context = context.ApiContext.restore() + user_id = generated.User.list(api_context)[0].UserCompany.id_ + monetary_account_id = generated.MonetaryAccountBank.list( + api_context, + user_id + )[0].id_ + + date_start = datetime.datetime.now() + date_start -= datetime.timedelta(_DAYS_IN_WEEK) + date_end = datetime.datetime.now() + customer_statement_map = { + generated.CustomerStatementExport.FIELD_STATEMENT_FORMAT: + _CUSTOMER_STATEMENT_FORMAT, + generated.CustomerStatementExport.FIELD_DATE_START: date_start.strftime( + _FORMAT_DATE + ), + generated.CustomerStatementExport.FIELD_DATE_END: date_end.strftime( + _FORMAT_DATE + ), + } + customer_statement_id = generated.CustomerStatementExport.create( + api_context, + customer_statement_map, + user_id, + monetary_account_id + ) + generated.CustomerStatementExport.delete( + api_context, + user_id, + monetary_account_id, + customer_statement_id + ) + + api_context.save() diff --git a/examples/monetary_account_example.py b/examples/monetary_account_example.py new file mode 100644 index 0000000..8ff0fae --- /dev/null +++ b/examples/monetary_account_example.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +from bunq.sdk import context +from bunq.sdk.model import generated + +_USER_ITEM_ID = 0 # Put your user ID here +_MONETARY_ACCOUNT_ITEM_ID = 0 # Put your monetary account ID here + + +def run(): + api_context = context.ApiContext.restore() + monetary_account = generated.MonetaryAccountBank.get( + api_context, + _USER_ITEM_ID, + _MONETARY_ACCOUNT_ITEM_ID + ) + print(monetary_account.to_json()) diff --git a/examples/payment_batch_example.py b/examples/payment_batch_example.py new file mode 100644 index 0000000..ed92f81 --- /dev/null +++ b/examples/payment_batch_example.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +from bunq.sdk import context +from bunq.sdk.model import generated +from bunq.sdk.model.generated import object_ + +_PAYMENT_AMOUNT = '0.01' +_PAYMENT_CURRENCY = 'EUR' +_COUNTERPARTY_POINTER_TYPE = 'EMAIL' +_COUNTERPARTY_EMAIL = 'bravo@bunq.com' # Put your counterparty email here +_PAYMENT_DESCRIPTION = 'This is a generated payment batch!' +_USER_ITEM_ID = 0 # Put your user ID here +_MONETARY_ACCOUNT_ITEM_ID = 0 # Put your monetary account ID here + + +def run(): + api_context = context.ApiContext.restore() + request_map = { + generated.PaymentBatch.FIELD_PAYMENTS: [ + { + generated.Payment.FIELD_AMOUNT: object_.Amount( + _PAYMENT_AMOUNT, + _PAYMENT_CURRENCY), + generated.Payment.FIELD_COUNTERPARTY_ALIAS: object_.Pointer( + _COUNTERPARTY_POINTER_TYPE, + _COUNTERPARTY_EMAIL + ), + generated.Payment.FIELD_DESCRIPTION: _PAYMENT_DESCRIPTION, + } + ] + } + + payment_id = generated.PaymentBatch.create( + api_context, + request_map, + _USER_ITEM_ID, + _MONETARY_ACCOUNT_ITEM_ID + ) + + print( + generated.PaymentBatch.get( + api_context, + _USER_ITEM_ID, + _MONETARY_ACCOUNT_ITEM_ID, + payment_id + ).to_json() + ) diff --git a/examples/payment_example.py b/examples/payment_example.py new file mode 100644 index 0000000..fabd328 --- /dev/null +++ b/examples/payment_example.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +from bunq.sdk import context +from bunq.sdk.model import generated +from bunq.sdk.model.generated import object_ + +_PAYMENT_AMOUNT = '0.01' +_PAYMENT_CURRENCY = 'EUR' +_COUNTERPARTY_POINTER_TYPE = 'EMAIL' +_COUNTERPARTY_EMAIL = 'bravo@bunq.com' # Put your counterparty email here +_PAYMENT_DESCRIPTION = 'This is a generated payment!' +_USER_ITEM_ID = 0 # Put your user ID here +_MONETARY_ACCOUNT_ITEM_ID = 0 # Put your monetary account ID here + + +def run(): + api_context = context.ApiContext.restore() + request_map = { + generated.Payment.FIELD_AMOUNT: object_.Amount( + _PAYMENT_AMOUNT, + _PAYMENT_CURRENCY + ), + generated.Payment.FIELD_COUNTERPARTY_ALIAS: object_.Pointer( + _COUNTERPARTY_POINTER_TYPE, + _COUNTERPARTY_EMAIL + ), + generated.Payment.FIELD_DESCRIPTION: _PAYMENT_DESCRIPTION, + } + + payment_id = generated.Payment.create( + api_context, + request_map, + _USER_ITEM_ID, + _MONETARY_ACCOUNT_ITEM_ID + ) + + print( + generated.Payment.get( + api_context, + _USER_ITEM_ID, + _MONETARY_ACCOUNT_ITEM_ID, + payment_id + ).to_json() + ) diff --git a/examples/payment_list_example.py b/examples/payment_list_example.py new file mode 100644 index 0000000..58da525 --- /dev/null +++ b/examples/payment_list_example.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 + +from bunq.sdk import context +from bunq.sdk.model import generated + +_USER_ITEM_ID = 0 # Put your user ID here +_MONETARY_ACCOUNT_ITEM_ID = 0 # Put your monetary account ID here + + +def run(): + api_context = context.ApiContext.restore() + payments = generated.Payment.list( + api_context, + _USER_ITEM_ID, + _MONETARY_ACCOUNT_ITEM_ID, + ) + + for payment in payments: + print(payment.id_) diff --git a/examples/request_example.py b/examples/request_example.py new file mode 100644 index 0000000..529b745 --- /dev/null +++ b/examples/request_example.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 + +from bunq.sdk import context +from bunq.sdk.model import generated +from bunq.sdk.model.generated import object_ + +_REQUEST_AMOUNT = '0.01' +_REQUEST_CURRENCY = 'EUR' +_COUNTERPARTY_POINTER_TYPE = 'EMAIL' +_COUNTERPARTY_EMAIL = 'bravo@bunq.com' +_REQUEST_DESCRIPTION = 'This is a generated request!' +_USER_ITEM_ID = 0 # Put your user ID here +_MONETARY_ACCOUNT_ITEM_ID = 0 # Put your monetary account ID here +_STATUS_REVOKED = 'REVOKED' + + +def run(): + api_context = context.ApiContext.restore() + request_map = { + generated.RequestInquiry.FIELD_AMOUNT_INQUIRED: object_.Amount( + _REQUEST_AMOUNT, + _REQUEST_CURRENCY + ), + generated.RequestInquiry.FIELD_COUNTERPARTY_ALIAS: object_.Pointer( + _COUNTERPARTY_POINTER_TYPE, + _COUNTERPARTY_EMAIL + ), + generated.RequestInquiry.FIELD_DESCRIPTION: _REQUEST_DESCRIPTION, + generated.RequestInquiry.FIELD_ALLOW_BUNQME: True, + } + request_id = generated.RequestInquiry.create( + api_context, + request_map, + _USER_ITEM_ID, + _MONETARY_ACCOUNT_ITEM_ID + ) + print( + generated.RequestInquiry.get( + api_context, + _USER_ITEM_ID, + _MONETARY_ACCOUNT_ITEM_ID, + request_id + ).to_json() + ) + + request_update_map = { + generated.RequestInquiry.FIELD_STATUS: _STATUS_REVOKED, + } + print( + generated.RequestInquiry.update( + api_context, + request_update_map, + _USER_ITEM_ID, + _MONETARY_ACCOUNT_ITEM_ID, + request_id + ).to_json() + ) diff --git a/examples/user_list_example.py b/examples/user_list_example.py new file mode 100644 index 0000000..68bb992 --- /dev/null +++ b/examples/user_list_example.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +from bunq.sdk import context +from bunq.sdk.model import generated + + +def run(): + api_context = context.ApiContext.restore() + users = generated.User.list(api_context) + api_context.save() + + for user in users: + print(user.UserCompany.to_json()) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..11d2e0b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +aenum==2.0.8 +chardet==3.0.4 +pycryptodomex==3.4.6 +requests==2.18.1 +simplejson==3.11.1 +urllib3==1.21.1 diff --git a/run.py b/run.py new file mode 100755 index 0000000..7cdc347 --- /dev/null +++ b/run.py @@ -0,0 +1,11 @@ +#! /usr/bin/env python3 +import sys + +if len(sys.argv) != 2: + print('Invalid argument count. Usage: python3 run.py ' + 'examples/example_name.py') + +path = sys.argv[1] +module_ = path.rstrip('.py').replace('/', '.') +exec('import {}'.format(module_)) +exec('{}.run()'.format(module_)) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e69de29