From a9dd9dc7606172a9a54593d93d2f8d29c127b1c3 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 2 Feb 2022 16:46:00 -0800 Subject: [PATCH 001/137] bug patches --- unit/models/__init__.py | 2 +- unit/models/application.py | 36 +++++++++++++++++++++++++++++------- unit/models/codecs.py | 2 ++ unit/models/customer.py | 2 +- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index 18404c62..70246cf5 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -176,7 +176,7 @@ def __init__(self, full_name: FullName, email: str, phone: Phone): self.phone = phone @staticmethod - def from_json_api(l: List): + def from_json_api(l: List) -> List: authorized_users = [] for data in l: authorized_users.append(AuthorizedUser(data.get("fullName"), data.get("email"), data.get("phone"))) diff --git a/unit/models/application.py b/unit/models/application.py index daa2bfa0..bc395f00 100644 --- a/unit/models/application.py +++ b/unit/models/application.py @@ -67,7 +67,8 @@ def from_json_api(_id, _type, attributes, relationships): class CreateIndividualApplicationRequest(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, email: str, phone: Phone, - ip: str = None, ein: str = None, dba: str = None, sole_proprietorship: bool = None, ssn = None): + ip: str = None, ein: str = None, dba: str = None, sole_proprietorship: bool = None, ssn = None, + tags: Optional[Dict[str, str]] = None): self.full_name = full_name self.date_of_birth = date_of_birth self.address = address @@ -78,6 +79,7 @@ def __init__(self, full_name: FullName, date_of_birth: date, address: Address, e self.dba = dba self.sole_proprietorship = sole_proprietorship self.ssn = ssn + self.tags = tags def to_json_api(self) -> Dict: payload = { @@ -108,6 +110,9 @@ def to_json_api(self) -> Dict: if self.ssn: payload["data"]["attributes"]["ssn"] = self.ssn + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + return payload def __repr__(self): @@ -115,9 +120,22 @@ def __repr__(self): class CreateBusinessApplicationRequest(UnitRequest): - def __init__(self, name: str, address: Address, phone: Phone, state_of_incorporation: str, ein: str, - contact: BusinessContact, officer: Officer, beneficial_owners: [BeneficialOwner], - entity_type: EntityType, dba: str = None, ip: str = None, website: str = None): + def __init__( + self, + name: str, + address: Address, + phone: Phone, + state_of_incorporation: str, + ein: str, + contact: BusinessContact, + officer: Officer, + beneficial_owners: [BeneficialOwner], + entity_type: EntityType, + tags: Optional[Dict[str, str]] = None, + dba: str = None, + ip: str = None, + website: str = None, + ): self.name = name self.address = address self.phone = phone @@ -130,8 +148,9 @@ def __init__(self, name: str, address: Address, phone: Phone, state_of_incorpora self.dba = dba self.ip = ip self.website = website + self.tags = tags - def to_json_api(self) -> Dict: + def to_json_api(self) -> dict: payload = { "data": { "type": "businessApplication", @@ -144,14 +163,17 @@ def to_json_api(self) -> Dict: "contact": self.contact, "officer": self.officer, "beneficialOwners": self.beneficial_owners, - "entityType": self.entity_type - } + "entityType": self.entity_type, + }, } } if self.dba: payload["data"]["attributes"]["dba"] = self.dba + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + if self.ip: payload["data"]["attributes"]["ip"] = self.ip diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 904a5078..c8778aae 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -312,6 +312,8 @@ def default(self, obj): return addr if isinstance(obj, BusinessContact): return {"fullName": obj.full_name, "email": obj.email, "phone": obj.phone} + if isinstance(obj, AuthorizedUser): + return {"fullName": obj.full_name, "email": obj.email, "phone": obj.phone} if isinstance(obj, Officer): officer = {"fullName": obj.full_name, "dateOfBirth": date_utils.to_date_str(obj.date_of_birth), "address": obj.address, "phone": obj.phone, "email": obj.email} diff --git a/unit/models/customer.py b/unit/models/customer.py index bd82690a..b540d042 100644 --- a/unit/models/customer.py +++ b/unit/models/customer.py @@ -43,7 +43,7 @@ def from_json_api(_id, _type, attributes, relationships): Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), attributes["stateOfIncorporation"], attributes["ein"], attributes["entityType"], BusinessContact.from_json_api(attributes["contact"]), - [AuthorizedUser.from_json_api(user) for user in attributes["authorizedUsers"]], + AuthorizedUser.from_json_api(attributes["authorizedUsers"]), attributes.get("dba"), attributes.get("tags"), relationships) CustomerDTO = Union[IndividualCustomerDTO, BusinessCustomerDTO] From a07b7a4b486bc7fa11c4b0b1cd990025c3ae7bed Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 10 Feb 2022 14:01:10 -0800 Subject: [PATCH 002/137] fixed inheritence and changed pdf response to content --- unit/api/statement_resource.py | 2 +- unit/models/statement.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/api/statement_resource.py b/unit/api/statement_resource.py index 212a590f..1ba5342c 100644 --- a/unit/api/statement_resource.py +++ b/unit/api/statement_resource.py @@ -15,7 +15,7 @@ def get(self, params: GetStatementParams) -> Union[UnitResponse[str], UnitError] response = super().get(f"{self.resource}/{params.statement_id}/{params.output_type}", parameters) if response.status_code == 200: - return UnitResponse[str](response.text, None) + return UnitResponse[bytes](response.content, None) else: return UnitError.from_json_api(response.json()) diff --git a/unit/models/statement.py b/unit/models/statement.py index 90e40382..03043188 100644 --- a/unit/models/statement.py +++ b/unit/models/statement.py @@ -16,7 +16,7 @@ def from_json_api(_id, _type, attributes, relationships): OutputType = Literal["html", "pdf"] -class GetStatementParams(object): +class GetStatementParams(UnitRequest): def __init__(self, statement_id: str, output_type: Optional[OutputType] = "html", language: Optional[str] = "en", customer_id: Optional[str] = None): self.statement_id = statement_id From ef2c0d6eaf19036e4ce4176ff1aefd74d08f5af3 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Mon, 14 Feb 2022 14:38:50 -0800 Subject: [PATCH 003/137] add auth user to sol prop --- unit/models/customer.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/unit/models/customer.py b/unit/models/customer.py index b540d042..39580df2 100644 --- a/unit/models/customer.py +++ b/unit/models/customer.py @@ -51,12 +51,14 @@ def from_json_api(_id, _type, attributes, relationships): class PatchIndividualCustomerRequest(UnitRequest): def __init__(self, customer_id: str, address: Optional[Address] = None, phone: Optional[Phone] = None, - email: Optional[str] = None, dba: Optional[str] = None, tags: Optional[Dict[str, str]] = None): + email: Optional[str] = None, dba: Optional[str] = None, + authorized_users: Optional[List[AuthorizedUser]] = None, tags: Optional[Dict[str, str]] = None): self.customer_id = customer_id self.address = address self.phone = phone self.email = email self.dba = dba + self.authorized_users = authorized_users self.tags = tags def to_json_api(self) -> Dict: @@ -79,6 +81,9 @@ def to_json_api(self) -> Dict: if self.dba: payload["data"]["attributes"]["dba"] = self.dba + if self.authorized_users: + payload["data"]["attributes"]["authorizedUsers"] = self.authorized_users + if self.tags: payload["data"]["attributes"]["tags"] = self.tags From 8994d1323ddf5bb0e6e6c7bf4208540378d11f27 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 16 Feb 2022 20:20:23 -0800 Subject: [PATCH 004/137] added payment rejected event --- unit/models/event.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/unit/models/event.py b/unit/models/event.py index 74b213ac..c891a801 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -304,6 +304,18 @@ def from_json_api(_id, _type, attributes, relationships): return PaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], attributes.get("tags"), relationships) +class PaymentRejectedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'payment.rejected' + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], + attributes.get("tags"), relationships) + class StatementsCreatedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, period: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): @@ -348,5 +360,6 @@ def from_json_api(_id, _type, attributes, relationships): AuthorizationRequestApprovedEvent, DocumentApprovedEvent, DocumentRejectedEvent, CheckDepositCreatedEvent, CheckDepositClearingEvent, CheckDepositSentEvent, CheckDepositReturnedEvent, CustomerCreatedEvent, PaymentClearingEvent, PaymentSentEvent, - PaymentReturnedEvent, StatementsCreatedEvent, TransactionCreatedEvent, AccountReopenedEvent] + PaymentReturnedEvent, PaymentReturnedEvent, StatementsCreatedEvent, TransactionCreatedEvent, + AccountReopenedEvent] From d5c15b29fb530302539590c50ffbc60adf37f630 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Fri, 18 Feb 2022 10:35:34 -0800 Subject: [PATCH 005/137] added auth users to individual customer dto --- unit/models/__init__.py | 3 +++ unit/models/customer.py | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index 70246cf5..0d5a3a1e 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -79,6 +79,9 @@ def __init__(self, first: str, last: str): self.first = first self.last = last + def __str__(self): + return f"{self.first} {self.last}" + @staticmethod def from_json_api(data: Dict): return FullName(data.get("first"), data.get("last")) diff --git a/unit/models/customer.py b/unit/models/customer.py index 39580df2..4af73886 100644 --- a/unit/models/customer.py +++ b/unit/models/customer.py @@ -5,12 +5,13 @@ class IndividualCustomerDTO(object): def __init__(self, id: str, created_at: datetime, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, ssn: Optional[str], passport: Optional[str], nationality: Optional[str], - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + authorized_users: [AuthorizedUser], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): self.id = id self.type = 'individualCustomer' self.attributes = {"createdAt": created_at, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, "phone": phone, "email": email, "ssn": ssn, "passport": passport, - "nationality": nationality, "tags": tags} + "nationality": nationality, "authorizedUsers": authorized_users, "tags": tags} self.relationships = relationships @staticmethod @@ -20,7 +21,7 @@ def from_json_api(_id, _type, attributes, relationships): FullName.from_json_api(attributes["fullName"]), date_utils.to_date(attributes["dateOfBirth"]), Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), attributes["email"], attributes.get("ssn"), attributes.get("passport"), attributes.get("nationality"), - attributes.get("tags"), relationships + AuthorizedUser.from_json_api(attributes["authorizedUsers"]), attributes.get("tags"), relationships ) From a20ad0530979ced0c9e4b778fa9f6c6d09156305 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 23 Feb 2022 15:25:19 -0800 Subject: [PATCH 006/137] added nsf to ach return reasons --- unit/models/returnAch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/models/returnAch.py b/unit/models/returnAch.py index ecd9eb2c..7f22934d 100644 --- a/unit/models/returnAch.py +++ b/unit/models/returnAch.py @@ -2,10 +2,10 @@ from typing import Literal from unit.models import * -AchReturnReason = Literal["Unauthorized"] +AchReturnReason = Literal["InsufficientFunds", "Unauthorized"] -class ReturnReceivedAchTransactionRequest(object): +class ReturnReceivedAchTransactionRequest(UnitRequest): def __init__(self, transaction_id: str, reason: AchReturnReason, relationships: [Dict[str, Relationship]]): self.transaction_id = transaction_id self.reason = reason From 4bf5ad3cc25881182445d56ac5b7c39e73242a46 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 2 Mar 2022 17:15:58 -0800 Subject: [PATCH 007/137] fixed transaction dto --- unit/models/transaction.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/unit/models/transaction.py b/unit/models/transaction.py index 48e3bc22..41c93e2e 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -57,7 +57,6 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) self.type = 'returnedAchTransaction' - self.attributes["addenda"] = addenda self.attributes["companyName"] = company_name self.attributes["counterpartyRoutingNumber"] = counterparty_routing_number self.attributes["reason"] = reason @@ -113,7 +112,7 @@ def from_json_api(_id, _type, attributes, relationships): class BookTransactionDTO(BaseTransactionDTO): def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, description: str, addenda: Optional[str], counterparty: Counterparty, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + tags: Optional[Dict[str, str]] = {}, relationships: Optional[Dict[str, Relationship]] = {}): BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) self.description = description self.type = 'bookTransaction' @@ -122,9 +121,12 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b @staticmethod def from_json_api(_id, _type, attributes, relationships): return BookTransactionDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], - Counterparty.from_json_api(attributes["counterparty"]), attributes.get("tags"), relationships) + id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), direction=attributes["direction"], + amount=attributes["amount"], balance=attributes["balance"], summary=attributes["summary"], + description=attributes["summary"], addenda=None, + counterparty=Counterparty.from_json_api(attributes["counterparty"]), + tags=attributes.get("tags"), relationships=relationships + ) class PurchaseTransactionDTO(BaseTransactionDTO): From 28d000443d40825919a75e565724b8e0f8bc38ea Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 9 Mar 2022 17:40:29 -0800 Subject: [PATCH 008/137] update --- unit/api/transaction_resource.py | 5 +++-- unit/models/returnAch.py | 2 +- unit/models/transaction.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/unit/api/transaction_resource.py b/unit/api/transaction_resource.py index da9f3587..24df54f1 100644 --- a/unit/api/transaction_resource.py +++ b/unit/api/transaction_resource.py @@ -9,8 +9,9 @@ def __init__(self, api_url, token): super().__init__(api_url, token) self.resource = "transactions" - def get(self, transaction_id: str) -> Union[UnitResponse[TransactionDTO], UnitError]: - response = super().get(f"{self.resource}/{transaction_id}") + def get(self, transaction_id: str, account_id: str) -> Union[UnitResponse[TransactionDTO], UnitError]: + params = {"filter[accountId]": account_id} + response = super().get(f"{self.resource}/{transaction_id}", params) if response.status_code == 200: data = response.json().get("data") included = response.json().get("included") diff --git a/unit/models/returnAch.py b/unit/models/returnAch.py index 7f22934d..6c972246 100644 --- a/unit/models/returnAch.py +++ b/unit/models/returnAch.py @@ -2,7 +2,7 @@ from typing import Literal from unit.models import * -AchReturnReason = Literal["InsufficientFunds", "Unauthorized"] +AchReturnReason = Literal["InsufficientFunds", "Unauthorized", "UncollectedFunds"] class ReturnReceivedAchTransactionRequest(UnitRequest): diff --git a/unit/models/transaction.py b/unit/models/transaction.py index 41c93e2e..eca55760 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -231,7 +231,7 @@ def from_json_api(_id, _type, attributes, relationships): return WireTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], attributes["amount"], attributes["balance"], attributes["summary"], Counterparty.from_json_api(attributes["counterparty"]), attributes["description"], - attributes["senderReference"], attributes["referenceForBeneficiary"], + attributes.get("senderReference"), attributes.get("referenceForBeneficiary"), attributes.get("tags"), relationships) From eabb2433cca3b0539a05fc2b0d89109dbe0510b2 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 2 Feb 2022 16:46:00 -0800 Subject: [PATCH 009/137] bug patches --- unit/models/__init__.py | 2 +- unit/models/application.py | 33 +++++++++++++++++++++++++-------- unit/models/codecs.py | 2 ++ unit/models/customer.py | 2 +- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index 4e367816..6cf1a4bf 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -176,7 +176,7 @@ def __init__(self, full_name: FullName, email: str, phone: Phone): self.phone = phone @staticmethod - def from_json_api(l: List): + def from_json_api(l: List) -> List: authorized_users = [] for data in l: authorized_users.append(AuthorizedUser(data.get("fullName"), data.get("email"), data.get("phone"))) diff --git a/unit/models/application.py b/unit/models/application.py index 530b6d4b..60f5a5c9 100644 --- a/unit/models/application.py +++ b/unit/models/application.py @@ -69,7 +69,7 @@ class CreateIndividualApplicationRequest(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, email: str, phone: Phone, ip: str = None, ein: str = None, dba: str = None, sole_proprietorship: bool = None, passport: str = None, nationality: str = None, ssn = None, - device_fingerprints: Optional[List[DeviceFingerprint]] = None, idempotency_key: str = None, + device_fingerprints: Optional[List[DeviceFingerprint]] = None, idempotency_key: str = None, tags: Optional[Dict[str, str]] = None): self.full_name = full_name self.date_of_birth = date_of_birth @@ -129,7 +129,7 @@ def to_json_api(self) -> Dict: payload["data"]["attributes"]["deviceFingerprints"] = [e.to_json_api() for e in self.device_fingerprints] if self.tags: - payload["data"]["attributes"]["tags"] = self.tags + payload["data"]["attributes"]["tags"] = self.tags return payload @@ -138,9 +138,22 @@ def __repr__(self): class CreateBusinessApplicationRequest(UnitRequest): - def __init__(self, name: str, address: Address, phone: Phone, state_of_incorporation: str, ein: str, - contact: BusinessContact, officer: Officer, beneficial_owners: [BeneficialOwner], - entity_type: EntityType, dba: str = None, ip: str = None, website: str = None): + def __init__( + self, + name: str, + address: Address, + phone: Phone, + state_of_incorporation: str, + ein: str, + contact: BusinessContact, + officer: Officer, + beneficial_owners: [BeneficialOwner], + entity_type: EntityType, + tags: Optional[Dict[str, str]] = None, + dba: str = None, + ip: str = None, + website: str = None, + ): self.name = name self.address = address self.phone = phone @@ -153,8 +166,9 @@ def __init__(self, name: str, address: Address, phone: Phone, state_of_incorpora self.dba = dba self.ip = ip self.website = website + self.tags = tags - def to_json_api(self) -> Dict: + def to_json_api(self) -> dict: payload = { "data": { "type": "businessApplication", @@ -167,14 +181,17 @@ def to_json_api(self) -> Dict: "contact": self.contact, "officer": self.officer, "beneficialOwners": self.beneficial_owners, - "entityType": self.entity_type - } + "entityType": self.entity_type, + }, } } if self.dba: payload["data"]["attributes"]["dba"] = self.dba + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + if self.ip: payload["data"]["attributes"]["ip"] = self.ip diff --git a/unit/models/codecs.py b/unit/models/codecs.py index aa70d2fd..93c50803 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -314,6 +314,8 @@ def default(self, obj): return addr if isinstance(obj, BusinessContact): return {"fullName": obj.full_name, "email": obj.email, "phone": obj.phone} + if isinstance(obj, AuthorizedUser): + return {"fullName": obj.full_name, "email": obj.email, "phone": obj.phone} if isinstance(obj, Officer): officer = {"fullName": obj.full_name, "dateOfBirth": date_utils.to_date_str(obj.date_of_birth), "address": obj.address, "phone": obj.phone, "email": obj.email} diff --git a/unit/models/customer.py b/unit/models/customer.py index 4d9e7e34..f87889d8 100644 --- a/unit/models/customer.py +++ b/unit/models/customer.py @@ -43,7 +43,7 @@ def from_json_api(_id, _type, attributes, relationships): Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), attributes["stateOfIncorporation"], attributes["ein"], attributes["entityType"], BusinessContact.from_json_api(attributes["contact"]), - [AuthorizedUser.from_json_api(user) for user in attributes["authorizedUsers"]], + AuthorizedUser.from_json_api(attributes["authorizedUsers"]), attributes.get("dba"), attributes.get("tags"), relationships) CustomerDTO = Union[IndividualCustomerDTO, BusinessCustomerDTO] From 4a6771add8397b3071be591c02630de498fc742c Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 10 Feb 2022 14:01:10 -0800 Subject: [PATCH 010/137] fixed inheritence and changed pdf response to content --- unit/api/statement_resource.py | 2 +- unit/models/statement.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/api/statement_resource.py b/unit/api/statement_resource.py index 8d0f47b1..be711e1f 100644 --- a/unit/api/statement_resource.py +++ b/unit/api/statement_resource.py @@ -15,7 +15,7 @@ def get(self, params: GetStatementParams) -> Union[UnitResponse[str], UnitError] response = super().get(f"{self.resource}/{params.statement_id}/{params.output_type}", parameters) if response.status_code == 200: - return UnitResponse[str](response.text, None) + return UnitResponse[bytes](response.content, None) else: return UnitError.from_json_api(response.json()) diff --git a/unit/models/statement.py b/unit/models/statement.py index 0b519ade..590e2604 100644 --- a/unit/models/statement.py +++ b/unit/models/statement.py @@ -16,7 +16,7 @@ def from_json_api(_id, _type, attributes, relationships): OutputType = Literal["html", "pdf"] -class GetStatementParams(object): +class GetStatementParams(UnitRequest): def __init__(self, statement_id: str, output_type: Optional[OutputType] = "html", language: Optional[str] = "en", customer_id: Optional[str] = None): self.statement_id = statement_id From 86914a33ab06b6310e152394e7f9a3de4da9e4b7 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Mon, 14 Feb 2022 14:38:50 -0800 Subject: [PATCH 011/137] add auth user to sol prop --- unit/models/customer.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/unit/models/customer.py b/unit/models/customer.py index f87889d8..0868ef9c 100644 --- a/unit/models/customer.py +++ b/unit/models/customer.py @@ -51,12 +51,14 @@ def from_json_api(_id, _type, attributes, relationships): class PatchIndividualCustomerRequest(UnitRequest): def __init__(self, customer_id: str, address: Optional[Address] = None, phone: Optional[Phone] = None, - email: Optional[str] = None, dba: Optional[str] = None, tags: Optional[Dict[str, str]] = None): + email: Optional[str] = None, dba: Optional[str] = None, + authorized_users: Optional[List[AuthorizedUser]] = None, tags: Optional[Dict[str, str]] = None): self.customer_id = customer_id self.address = address self.phone = phone self.email = email self.dba = dba + self.authorized_users = authorized_users self.tags = tags def to_json_api(self) -> Dict: @@ -79,6 +81,9 @@ def to_json_api(self) -> Dict: if self.dba: payload["data"]["attributes"]["dba"] = self.dba + if self.authorized_users: + payload["data"]["attributes"]["authorizedUsers"] = self.authorized_users + if self.tags: payload["data"]["attributes"]["tags"] = self.tags From e1293d715b04ff5ffcc2ee6633c49284a89ccc9d Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 16 Feb 2022 20:20:23 -0800 Subject: [PATCH 012/137] added payment rejected event --- unit/models/event.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/unit/models/event.py b/unit/models/event.py index eaa775d0..c36eb79e 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -304,6 +304,18 @@ def from_json_api(_id, _type, attributes, relationships): return PaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], attributes.get("tags"), relationships) +class PaymentRejectedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'payment.rejected' + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], + attributes.get("tags"), relationships) + class StatementsCreatedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, period: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): @@ -348,7 +360,8 @@ def from_json_api(_id, _type, attributes, relationships): AuthorizationRequestApprovedEvent, DocumentApprovedEvent, DocumentRejectedEvent, CheckDepositCreatedEvent, CheckDepositClearingEvent, CheckDepositSentEvent, CheckDepositReturnedEvent, CustomerCreatedEvent, PaymentClearingEvent, PaymentSentEvent, - PaymentReturnedEvent, StatementsCreatedEvent, TransactionCreatedEvent, AccountReopenedEvent] + PaymentReturnedEvent, PaymentReturnedEvent, StatementsCreatedEvent, TransactionCreatedEvent, + AccountReopenedEvent] class ListEventParams(UnitParams): From addd9bdd0aef29cca35e40bc469fd9f6d75c8e11 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Fri, 18 Feb 2022 10:35:34 -0800 Subject: [PATCH 013/137] added auth users to individual customer dto --- unit/models/__init__.py | 3 +++ unit/models/customer.py | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index 6cf1a4bf..1bf9bf04 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -79,6 +79,9 @@ def __init__(self, first: str, last: str): self.first = first self.last = last + def __str__(self): + return f"{self.first} {self.last}" + @staticmethod def from_json_api(data: Dict): return FullName(data.get("first"), data.get("last")) diff --git a/unit/models/customer.py b/unit/models/customer.py index 0868ef9c..83ebb29b 100644 --- a/unit/models/customer.py +++ b/unit/models/customer.py @@ -5,12 +5,13 @@ class IndividualCustomerDTO(object): def __init__(self, id: str, created_at: datetime, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, ssn: Optional[str], passport: Optional[str], nationality: Optional[str], - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + authorized_users: [AuthorizedUser], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): self.id = id self.type = 'individualCustomer' self.attributes = {"createdAt": created_at, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, "phone": phone, "email": email, "ssn": ssn, "passport": passport, - "nationality": nationality, "tags": tags} + "nationality": nationality, "authorizedUsers": authorized_users, "tags": tags} self.relationships = relationships @staticmethod @@ -20,7 +21,7 @@ def from_json_api(_id, _type, attributes, relationships): FullName.from_json_api(attributes["fullName"]), date_utils.to_date(attributes["dateOfBirth"]), Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), attributes["email"], attributes.get("ssn"), attributes.get("passport"), attributes.get("nationality"), - attributes.get("tags"), relationships + AuthorizedUser.from_json_api(attributes["authorizedUsers"]), attributes.get("tags"), relationships ) From 3652df24312a2f1105a40f151aa4c227389e7b9b Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 23 Feb 2022 15:25:19 -0800 Subject: [PATCH 014/137] added nsf to ach return reasons --- unit/models/returnAch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/models/returnAch.py b/unit/models/returnAch.py index ecd9eb2c..7f22934d 100644 --- a/unit/models/returnAch.py +++ b/unit/models/returnAch.py @@ -2,10 +2,10 @@ from typing import Literal from unit.models import * -AchReturnReason = Literal["Unauthorized"] +AchReturnReason = Literal["InsufficientFunds", "Unauthorized"] -class ReturnReceivedAchTransactionRequest(object): +class ReturnReceivedAchTransactionRequest(UnitRequest): def __init__(self, transaction_id: str, reason: AchReturnReason, relationships: [Dict[str, Relationship]]): self.transaction_id = transaction_id self.reason = reason From 16490932e89c95a14b17fdc4d2588c2e48dd407d Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 2 Mar 2022 17:15:58 -0800 Subject: [PATCH 015/137] fixed transaction dto --- unit/models/transaction.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/unit/models/transaction.py b/unit/models/transaction.py index daafb9f0..973e4699 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -57,7 +57,6 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) self.type = 'returnedAchTransaction' - self.attributes["addenda"] = addenda self.attributes["companyName"] = company_name self.attributes["counterpartyRoutingNumber"] = counterparty_routing_number self.attributes["reason"] = reason @@ -113,7 +112,7 @@ def from_json_api(_id, _type, attributes, relationships): class BookTransactionDTO(BaseTransactionDTO): def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, description: str, addenda: Optional[str], counterparty: Counterparty, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + tags: Optional[Dict[str, str]] = {}, relationships: Optional[Dict[str, Relationship]] = {}): BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) self.description = description self.type = 'bookTransaction' @@ -122,9 +121,12 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b @staticmethod def from_json_api(_id, _type, attributes, relationships): return BookTransactionDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], - Counterparty.from_json_api(attributes["counterparty"]), attributes.get("tags"), relationships) + id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), direction=attributes["direction"], + amount=attributes["amount"], balance=attributes["balance"], summary=attributes["summary"], + description=attributes["summary"], addenda=None, + counterparty=Counterparty.from_json_api(attributes["counterparty"]), + tags=attributes.get("tags"), relationships=relationships + ) class PurchaseTransactionDTO(BaseTransactionDTO): From 6d1dd0892458f0761476b280fe5b64fb9f39f8f8 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 9 Mar 2022 17:40:29 -0800 Subject: [PATCH 016/137] update --- unit/api/transaction_resource.py | 5 +++-- unit/models/returnAch.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/unit/api/transaction_resource.py b/unit/api/transaction_resource.py index 2ab9d369..abcb8e38 100644 --- a/unit/api/transaction_resource.py +++ b/unit/api/transaction_resource.py @@ -9,8 +9,9 @@ def __init__(self, api_url, token): super().__init__(api_url, token) self.resource = "transactions" - def get(self, transaction_id: str, include: Optional[str] = "") -> Union[UnitResponse[TransactionDTO], UnitError]: - response = super().get(f"{self.resource}/{transaction_id}", {"include": include}) + def get(self, transaction_id: str, account_id: str, include: Optional[str] = "") -> Union[UnitResponse[TransactionDTO], UnitError]: + params = {"filter[accountId]": account_id, "include": include} + response = super().get(f"{self.resource}/{transaction_id}", params) if response.status_code == 200: data = response.json().get("data") included = response.json().get("included") diff --git a/unit/models/returnAch.py b/unit/models/returnAch.py index 7f22934d..6c972246 100644 --- a/unit/models/returnAch.py +++ b/unit/models/returnAch.py @@ -2,7 +2,7 @@ from typing import Literal from unit.models import * -AchReturnReason = Literal["InsufficientFunds", "Unauthorized"] +AchReturnReason = Literal["InsufficientFunds", "Unauthorized", "UncollectedFunds"] class ReturnReceivedAchTransactionRequest(UnitRequest): From a82b7838a5538b6e35f5e9be22dc6e8d31bcc639 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 10 Mar 2022 12:27:20 -0800 Subject: [PATCH 017/137] added coded --- unit/models/codecs.py | 3 +++ unit/models/event.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 93c50803..a5d6be4d 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -193,6 +193,9 @@ "payment.clearing": lambda _id, _type, attributes, relationships: PaymentClearingEvent.from_json_api(_id, _type, attributes, relationships), + "payment.created": lambda _id, _type, attributes, relationships: + PaymentCreatedEvent.from_json_api(_id, _type, attributes, relationships), + "payment.sent": lambda _id, _type, attributes, relationships: PaymentSentEvent.from_json_api(_id, _type, attributes, relationships), diff --git a/unit/models/event.py b/unit/models/event.py index c36eb79e..2fb0b58e 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -267,6 +267,29 @@ def from_json_api(_id, _type, attributes, relationships): attributes["reason"], attributes["reasonCode"], attributes.get("tags"), relationships) +class PaymentCreatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'payment.created' + self.attributes["status"] = status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentClearingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes.get("tags"), relationships) + +class PaymentRejectedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'payment.created' + self.attributes["status"] = status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentClearingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes.get("tags"), relationships) class PaymentClearingEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], From 517fb306c16a8624c2c1283b9e4818d6ebd04796 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 10 Mar 2022 14:19:59 -0800 Subject: [PATCH 018/137] added codecs --- unit/models/codecs.py | 6 ++++++ unit/models/event.py | 19 ++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index a5d6be4d..78d11d8c 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -202,6 +202,9 @@ "payment.returned": lambda _id, _type, attributes, relationships: PaymentReturnedEvent.from_json_api(_id, _type, attributes, relationships), + "payment.rejected": lambda _id, _type, attributes, relationships: + PaymentRejectedEvent.from_json_api(_id, _type, attributes, relationships), + "statements.created": lambda _id, _type, attributes, relationships: StatementsCreatedEvent.from_json_api(_id, _type, attributes, relationships), @@ -211,6 +214,9 @@ "customer.created": lambda _id, _type, attributes, relationships: CustomerCreatedEvent.from_json_api(_id, _type, attributes, relationships), + "customer.updated": lambda _id, _type, attributes, relationships: + CustomerUpdatedEvent.from_json_api(_id, _type, attributes, relationships), + "account.reopened": lambda _id, _type, attributes, relationships: AccountReopenedEvent.from_json_api(_id, _type, attributes, relationships), diff --git a/unit/models/event.py b/unit/models/event.py index 2fb0b58e..54f35824 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -242,6 +242,19 @@ def from_json_api(_id, _type, attributes, relationships): return CustomerCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), relationships) + +class CustomerUpdatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'customer.updated' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CustomerCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes.get("tags"), relationships) + + class DocumentApprovedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): @@ -328,15 +341,15 @@ def from_json_api(_id, _type, attributes, relationships): attributes.get("tags"), relationships) class PaymentRejectedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + def __init__(self, id: str, created_at: datetime, reason: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'payment.rejected' - self.attributes["previousStatus"] = previous_status + self.attributes["reason"] = previous_status @staticmethod def from_json_api(_id, _type, attributes, relationships): - return PaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], + return PaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["reason"], attributes.get("tags"), relationships) class StatementsCreatedEvent(BaseEvent): From 2a8cfd194b00d962d141204b6e5e02793719806a Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 10 Mar 2022 14:41:30 -0800 Subject: [PATCH 019/137] coded update --- unit/models/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/event.py b/unit/models/event.py index 54f35824..34fc444f 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -345,7 +345,7 @@ def __init__(self, id: str, created_at: datetime, reason: str, tags: Optional[Di relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'payment.rejected' - self.attributes["reason"] = previous_status + self.attributes["reason"] = reason @staticmethod def from_json_api(_id, _type, attributes, relationships): From 4b89d1314d5fa280db6d06b415f62218b48a98c2 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Fri, 18 Mar 2022 14:02:38 -0700 Subject: [PATCH 020/137] adds support for /sandbox/applications/application_id/approve --- unit/api/application_resource.py | 18 ++++++++++++++++++ unit/models/application.py | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/unit/api/application_resource.py b/unit/api/application_resource.py index 683dfff6..9e4b7570 100644 --- a/unit/api/application_resource.py +++ b/unit/api/application_resource.py @@ -61,3 +61,21 @@ def upload(self, request: UploadDocumentRequest): return UnitResponse[ApplicationDocumentDTO](DtoDecoder.decode(data), None) else: return UnitError.from_json_api(response.json()) + + def approve_sb(self, request: ApproveApplicationSB): + url = f"sandbox/{self.resource}/{request.application_id}/approve" + + payload = request.to_json_api() + response = super().post(url, payload) + + if response.ok: + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse(data, included) + # TODO need DTOs for this response + # if data["type"] == "individualApplication": + # return UnitResponse[IndividualApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + # else: + # return UnitResponse[BusinessApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) \ No newline at end of file diff --git a/unit/models/application.py b/unit/models/application.py index 60f5a5c9..7abb33ef 100644 --- a/unit/models/application.py +++ b/unit/models/application.py @@ -258,3 +258,21 @@ def to_dict(self) -> Dict: if self.sort: parameters["sort"] = self.sort return parameters + +class ApproveApplicationSB(UnitRequest): + def __init__(self, application_id: str): + self.application_id = application_id + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "applicationApprove", + "attributes": { + "reason": "sandbox" + } + } + } + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) From e3b8a836dfa80992040d24498081fef66d101f4b Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Fri, 18 Mar 2022 14:16:45 -0700 Subject: [PATCH 021/137] ApproveApplicationSBRequest --- unit/api/application_resource.py | 2 +- unit/models/application.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/api/application_resource.py b/unit/api/application_resource.py index 9e4b7570..29051e0f 100644 --- a/unit/api/application_resource.py +++ b/unit/api/application_resource.py @@ -62,7 +62,7 @@ def upload(self, request: UploadDocumentRequest): else: return UnitError.from_json_api(response.json()) - def approve_sb(self, request: ApproveApplicationSB): + def approve_sb(self, request: ApproveApplicationSBRequest): url = f"sandbox/{self.resource}/{request.application_id}/approve" payload = request.to_json_api() diff --git a/unit/models/application.py b/unit/models/application.py index 7abb33ef..98744681 100644 --- a/unit/models/application.py +++ b/unit/models/application.py @@ -259,7 +259,7 @@ def to_dict(self) -> Dict: parameters["sort"] = self.sort return parameters -class ApproveApplicationSB(UnitRequest): +class ApproveApplicationSBRequest(UnitRequest): def __init__(self, application_id: str): self.application_id = application_id From e4fd4c3ec70197bc432123d22bdb9f50881d1fe4 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Tue, 22 Mar 2022 18:22:42 -0700 Subject: [PATCH 022/137] update to authorization requests --- unit/models/authorization_request.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unit/models/authorization_request.py b/unit/models/authorization_request.py index 6eed39a4..f654701d 100644 --- a/unit/models/authorization_request.py +++ b/unit/models/authorization_request.py @@ -34,7 +34,7 @@ def from_json_api(_id, _type, attributes, relationships): attributes.get("tags"), relationships) -class ListPurchaseAuthorizationRequestParams(object): +class ListPurchaseAuthorizationRequestParams(UnitParams): def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, customer_id: Optional[str] = None): self.limit = limit @@ -51,7 +51,7 @@ def to_dict(self) -> Dict: return parameters -class ApproveAuthorizationRequest(object): +class ApproveAuthorizationRequest(UnitRequest): def __init__(self, authorization_id: str, amount: Optional[int] = None, tags: Optional[Dict[str, str]] = None): self.authorization_id = authorization_id self.amount = amount @@ -77,7 +77,7 @@ def __repr__(self): json.dumps(self.to_json_api()) -class DeclineAuthorizationRequest(object): +class DeclineAuthorizationRequest(UnitRequest): def __init__(self, authorization_id: str, reason: DeclineReason): self.authorization_id = authorization_id self.reason = reason From 37dc1742d300140e3431d1730d1bf2ee22fc27f5 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 24 Mar 2022 15:20:36 -0700 Subject: [PATCH 023/137] card dto update --- unit/models/card.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/unit/models/card.py b/unit/models/card.py index 5e7b7699..1919acca 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -70,15 +70,15 @@ def from_json_api(_id, _type, attributes, relationships): class BusinessVirtualDebitCardDTO(object): def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, ssn: str, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, passport: Optional[str], nationality: Optional[str], - relationships: Optional[Dict[str, Relationship]]): + status: CardStatus, passport: Optional[str] = None, nationality: Optional[str] = None, + relationships: Optional[Dict[str, Relationship]] = None): self.id = id self.type = "businessVirtualDebitCard" self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, "ssn": ssn, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, "phone": phone, "email": email, "status": status, "passport": passport, "nationality": nationality} - self.relationships = relationships + self.relationships = relationships or {} def from_json_api(_id, _type, attributes, relationships): return BusinessVirtualDebitCardDTO( @@ -93,7 +93,7 @@ def from_json_api(_id, _type, attributes, relationships): Card = Union[IndividualDebitCardDTO, BusinessDebitCardDTO, IndividualVirtualDebitCardDTO, BusinessVirtualDebitCardDTO] -class CreateIndividualDebitCard(object): +class CreateIndividualDebitCard(UnitRequest): def __init__(self, relationships: Dict[str, Relationship], shipping_address: Optional[Address] = None, design: Optional[str] = None, idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None): @@ -129,7 +129,7 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) -class CreateBusinessDebitCard(object): +class CreateBusinessDebitCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, status: CardStatus, shipping_address: Optional[Address], ssn: Optional[str], passport: Optional[str], nationality: Optional[str], design: Optional[str], idempotency_key: Optional[str], @@ -190,7 +190,7 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) -class CreateIndividualVirtualDebitCard(object): +class CreateIndividualVirtualDebitCard(UnitRequest): def __init__(self, relationships: Dict[str, Relationship], idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None): self.idempotency_key = idempotency_key @@ -218,11 +218,11 @@ def __repr__(self): json.dumps(self.to_json_api()) -class CreateBusinessVirtualDebitCard(object): +class CreateBusinessVirtualDebitCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, ssn: Optional[str], passport: Optional[str], nationality: Optional[str], - idempotency_key: Optional[str], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): + status: CardStatus, ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, + idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, + relationships: Optional[Dict[str, Relationship]] = None): self.full_name = full_name self.date_of_birth = date_of_birth self.address = address @@ -233,8 +233,8 @@ def __init__(self, full_name: FullName, date_of_birth: date, address: Address, p self.passport = passport self.nationality = nationality self.idempotency_key = idempotency_key - self.tags = tags - self.relationships = relationships + self.tags = tags or {} + self.relationships = relationships or {} def to_json_api(self) -> Dict: payload = { From d047997af805f7196ac492991d4b55c2854f45b5 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Fri, 1 Apr 2022 09:14:57 -0700 Subject: [PATCH 024/137] fix bug --- unit/models/card.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/card.py b/unit/models/card.py index 1919acca..baa74b69 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -83,7 +83,7 @@ def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration def from_json_api(_id, _type, attributes, relationships): return BusinessVirtualDebitCardDTO( _id, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], - attributes["expirationDate"], attributes["ssn"], FullName.from_json_api(attributes["fullName"]), + attributes["expirationDate"], attributes.get("ssn"), FullName.from_json_api(attributes["fullName"]), attributes["dateOfBirth"], Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), attributes["email"], attributes["status"], attributes.get("passport"), attributes.get("nationality"), relationships From 6d99a252d3c01d4f30f96d8b1531a697fc79c6c6 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 6 Apr 2022 15:52:41 +0100 Subject: [PATCH 025/137] added authorization canceled event --- unit/models/event.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/unit/models/event.py b/unit/models/event.py index 34fc444f..b4655d19 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -73,20 +73,38 @@ def from_json_api(_id, _type, attributes, relationships): return ApplicationAwaitingDocumentsEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), relationships) +class AuthorizationCanceledEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, + recurring: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'authorization.canceled' + self.attributes["cardLast4Digits"] = card_last_4_digits + self.attributes["amount"] = amount + self.attributes["recurring"] = recurring + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AuthorizationCanceledEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["amount"], attributes["cardLast4Digits"], + attributes["recurring"], attributes.get("tags"), + relationships) + class AuthorizationCreatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, card_last_4_digits: str, recurring: str, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, merchant: Dict[str, str], + recurring: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'authorization.created' self.attributes["cardLast4Digits"] = card_last_4_digits + self.attributes["amount"] = amount + self.attributes["merchant"] = merchant self.attributes["recurring"] = recurring @staticmethod def from_json_api(_id, _type, attributes, relationships): return AuthorizationCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["cardLast4Digits"], attributes["recurring"], - attributes.get("tags"), relationships) + attributes["amount"], attributes["cardLast4Digits"], attributes["merchant"], + attributes["recurring"], attributes.get("tags"), relationships) class AuthorizationRequestApprovedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, amount: str, status: str, approved_amount: str, @@ -392,7 +410,7 @@ def from_json_api(_id, _type, attributes, relationships): EventDTO = Union[AccountClosedEvent, AccountFrozenEvent, ApplicationDeniedEvent, ApplicationAwaitingDocumentsEvent, ApplicationPendingReviewEvent, CardActivatedEvent, CardStatusChangedEvent, - AuthorizationCreatedEvent, AuthorizationRequestDeclinedEvent, AuthorizationRequestPendingEvent, + AuthorizationCreatedEvent, AuthorizationCanceledEvent, AuthorizationRequestDeclinedEvent, AuthorizationRequestPendingEvent, AuthorizationRequestApprovedEvent, DocumentApprovedEvent, DocumentRejectedEvent, CheckDepositCreatedEvent, CheckDepositClearingEvent, CheckDepositSentEvent, CheckDepositReturnedEvent, CustomerCreatedEvent, PaymentClearingEvent, PaymentSentEvent, From 09d9aaedde72eb356763ecbdced860ead6f5f40d Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Wed, 6 Apr 2022 14:24:53 -0700 Subject: [PATCH 026/137] Adds support for simulating incoming ACH --- unit/api/payment_resource.py | 12 +++++++++++- unit/models/codecs.py | 2 +- unit/models/payment.py | 37 ++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/unit/api/payment_resource.py b/unit/api/payment_resource.py index 6ef30a43..33c13fda 100644 --- a/unit/api/payment_resource.py +++ b/unit/api/payment_resource.py @@ -1,6 +1,6 @@ from unit.api.base_resource import BaseResource from unit.models.payment import * -from unit.models.codecs import DtoDecoder +from unit.models.codecs import DtoDecoder, split_json_api_single_response class PaymentResource(BaseResource): @@ -45,3 +45,13 @@ def list(self, params: ListPaymentParams = None) -> Union[UnitResponse[List[Paym else: return UnitError.from_json_api(response.json()) + def simulate_incoming_ach(self, request: SimulateIncomingAchRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"sandbox/{self.resource}", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + # TODO Fix dto + _id, _type, attributes, relationships = split_json_api_single_response(data) + return UnitResponse[SimulateIncomingAchPaymentDTO](SimulateIncomingAchPaymentDTO.from_json_api(_id, _type, attributes, relationships), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 78d11d8c..bccb4852 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -9,7 +9,7 @@ from unit.models.card import IndividualDebitCardDTO, BusinessDebitCardDTO, IndividualVirtualDebitCardDTO,\ BusinessVirtualDebitCardDTO, PinStatusDTO, CardLimitsDTO from unit.models.transaction import * -from unit.models.payment import AchPaymentDTO, BookPaymentDTO, WirePaymentDTO +from unit.models.payment import AchPaymentDTO, BookPaymentDTO, WirePaymentDTO, SimulateIncomingAchPaymentDTO from unit.models.customerToken import CustomerTokenDTO, CustomerVerificationTokenDTO from unit.models.fee import FeeDTO from unit.models.event import * diff --git a/unit/models/payment.py b/unit/models/payment.py index 3e80116d..80c1161c 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -35,6 +35,29 @@ def from_json_api(_id, _type, attributes, relationships): attributes["amount"], attributes.get("addenda"), attributes.get("reason"), settlement_date, attributes.get("tags"), relationships) +class SimulateIncomingAchPaymentDTO(BasePayment): + def __init__(self, id: str, created_at: datetime, status: AchStatus, direction: str, + description: str, amount: int, reason: Optional[str], + settlement_date: Optional[datetime], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BasePayment.__init__(self, id, created_at, direction, description, amount, reason, tags, relationships) + self.type = 'achPayment' + self.attributes["status"] = status + self.settlement_date = settlement_date + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BasePayment( + _id, + date_utils.to_datetime(attributes["createdAt"]), + attributes.get("direction"), + attributes["description"], + attributes["amount"], + attributes.get("reason"), + attributes.get("tags"), + relationships + ) + class BookPaymentDTO(BasePayment): def __init__(self, id: str, created_at: datetime, status: str, direction: Optional[str], description: str, amount: int, reason: Optional[str], tags: Optional[Dict[str, str]], @@ -140,6 +163,20 @@ def to_json_api(self) -> Dict: return payload +class SimulateIncomingAchRequest(CreatePaymentBaseRequest): + def __init__( + self, amount: int, description: str, + relationships: Dict[str, Relationship], + direction: str = "Credit" + ): + CreatePaymentBaseRequest.__init__(self, amount, description, relationships, None, None, direction) + self.verify_counterparty_balance = False + + def to_json_api(self) -> Dict: + payload = CreatePaymentBaseRequest.to_json_api(self) + + return payload + class CreateVerifiedPaymentRequest(CreatePaymentBaseRequest): def __init__(self, amount: int, description: str, plaid_processor_token: str, relationships: Dict[str, Relationship], counterparty_name: Optional[str], verify_counterparty_balance: Optional[bool], From 32ca1a7a84319973c1fcb1adf11cc840ed87ee79 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 7 Apr 2022 13:30:44 +0100 Subject: [PATCH 027/137] added limits to card create card request --- unit/models/card.py | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/unit/models/card.py b/unit/models/card.py index baa74b69..d647967f 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -94,11 +94,12 @@ def from_json_api(_id, _type, attributes, relationships): class CreateIndividualDebitCard(UnitRequest): - def __init__(self, relationships: Dict[str, Relationship], shipping_address: Optional[Address] = None, - design: Optional[str] = None, idempotency_key: Optional[str] = None, - tags: Optional[Dict[str, str]] = None): + def __init__(self, relationships: Dict[str, Relationship], limits: Optional[Cardlimits] = None, + shipping_address: Optional[Address] = None, design: Optional[str] = None, + idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None): self.shipping_address = shipping_address self.design = design + self.limits = limits self.idempotency_key = idempotency_key self.tags = tags self.relationships = relationships @@ -115,6 +116,9 @@ def to_json_api(self) -> Dict: if self.shipping_address: payload["data"]["attributes"]["shippingAddress"] = self.shipping_address + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.design: payload["data"]["attributes"]["design"] = self.design @@ -131,23 +135,25 @@ def __repr__(self): class CreateBusinessDebitCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, shipping_address: Optional[Address], ssn: Optional[str], passport: Optional[str], - nationality: Optional[str], design: Optional[str], idempotency_key: Optional[str], - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + status: CardStatus, limits: Optional[CardLimits] = None, shipping_address: Optional[Address] = None, + ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, + design: Optional[str] = None, idempotency_key: Optional[str] = None, + tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None): self.full_name = full_name self.date_of_birth = date_of_birth self.address = address self.phone = phone self.email = email self.status = status + self.limits = limits self.shipping_address = shipping_address self.ssn = ssn self.passport = passport self.nationality = nationality self.design = design self.idempotency_key = idempotency_key - self.tags = tags - self.relationships = relationships + self.tags = tags or {} + self.relationships = relationships or {} def to_json_api(self) -> Dict: payload = { @@ -167,6 +173,9 @@ def to_json_api(self) -> Dict: if self.shipping_address: payload["data"]["attributes"]["shippingAddress"] = self.shipping_address + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.ssn: payload["data"]["attributes"]["ssn"] = self.ssn @@ -192,8 +201,9 @@ def __repr__(self): class CreateIndividualVirtualDebitCard(UnitRequest): def __init__(self, relationships: Dict[str, Relationship], idempotency_key: Optional[str] = None, - tags: Optional[Dict[str, str]] = None): + limits: Optional[CardLimits] = None, tags: Optional[Dict[str, str]] = None): self.idempotency_key = idempotency_key + self.limits = limits self.tags = tags self.relationships = relationships @@ -209,6 +219,9 @@ def to_json_api(self) -> Dict: if self.idempotency_key: payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.tags: payload["data"]["attributes"]["tags"] = self.tags @@ -220,7 +233,8 @@ def __repr__(self): class CreateBusinessVirtualDebitCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, + status: CardStatus, limits: Optional[CardLimits] = None, ssn: Optional[str] = None, + passport: Optional[str] = None, nationality: Optional[str] = None, idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None): self.full_name = full_name @@ -231,6 +245,7 @@ def __init__(self, full_name: FullName, date_of_birth: date, address: Address, p self.status = status self.ssn = ssn self.passport = passport + self.limits = limits self.nationality = nationality self.idempotency_key = idempotency_key self.tags = tags or {} @@ -254,6 +269,9 @@ def to_json_api(self) -> Dict: if self.ssn: payload["data"]["attributes"]["ssn"] = self.ssn + if self.limits: + limits["data"]["attributes"]["limits"] = self.limits + if self.passport: payload["data"]["attributes"]["passport"] = self.passport From 8fdcc9ad2236204a1ce51b77e5f774f268615562 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Fri, 8 Apr 2022 12:36:14 +0100 Subject: [PATCH 028/137] card limits --- unit/models/card.py | 40 ++++++++++++++++++++++++++++------------ unit/models/codecs.py | 7 +++++++ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/unit/models/card.py b/unit/models/card.py index d647967f..fca40cd4 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -94,7 +94,7 @@ def from_json_api(_id, _type, attributes, relationships): class CreateIndividualDebitCard(UnitRequest): - def __init__(self, relationships: Dict[str, Relationship], limits: Optional[Cardlimits] = None, + def __init__(self, relationships: Dict[str, Relationship], limits: Optional[CardLevelLimits] = None, shipping_address: Optional[Address] = None, design: Optional[str] = None, idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None): self.shipping_address = shipping_address @@ -135,7 +135,7 @@ def __repr__(self): class CreateBusinessDebitCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, limits: Optional[CardLimits] = None, shipping_address: Optional[Address] = None, + status: CardStatus, limits: Optional[CardLevelLimits] = None, shipping_address: Optional[Address] = None, ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, design: Optional[str] = None, idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None): @@ -201,7 +201,7 @@ def __repr__(self): class CreateIndividualVirtualDebitCard(UnitRequest): def __init__(self, relationships: Dict[str, Relationship], idempotency_key: Optional[str] = None, - limits: Optional[CardLimits] = None, tags: Optional[Dict[str, str]] = None): + limits: Optional[CardLevelLimits] = None, tags: Optional[Dict[str, str]] = None): self.idempotency_key = idempotency_key self.limits = limits self.tags = tags @@ -233,7 +233,7 @@ def __repr__(self): class CreateBusinessVirtualDebitCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, limits: Optional[CardLimits] = None, ssn: Optional[str] = None, + status: CardStatus, limits: Optional[CardLevelLimits] = None, ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None): @@ -293,12 +293,13 @@ def __repr__(self): CreateCardRequest = Union[CreateIndividualDebitCard, CreateBusinessDebitCard, CreateIndividualVirtualDebitCard, CreateBusinessVirtualDebitCard] -class PatchIndividualDebitCard(object): +class PatchIndividualDebitCard(UnitRequest): def __init__(self,card_id: str, shipping_address: Optional[Address] = None, design: Optional[str] = None, - tags: Optional[Dict[str, str]] = None): + limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): self.card_id = card_id self.shipping_address = shipping_address self.design = design + self.limits = limits self.tags = tags def to_json_api(self) -> Dict: @@ -312,6 +313,9 @@ def to_json_api(self) -> Dict: if self.shipping_address: payload["data"]["attributes"]["shippingAddress"] = self.shipping_address + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.design: payload["data"]["attributes"]["design"] = self.design @@ -324,12 +328,13 @@ def __repr__(self): json.dumps(self.to_json_api()) -class PatchBusinessDebitCard(object): +class PatchBusinessDebitCard(UnitRequest): def __init__(self, card_id: str, shipping_address: Optional[Address] = None, address: Optional[Address] = None, phone: Optional[Phone] = None, email: Optional[str] = None, design: Optional[str] = None, - tags: Optional[Dict[str, str]] = None): + limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): self.card_id = card_id self.shipping_address = shipping_address + self.limits = limits self.design = design self.tags = tags @@ -344,6 +349,9 @@ def to_json_api(self) -> Dict: if self.shipping_address: payload["data"]["attributes"]["shippingAddress"] = self.shipping_address + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.address: payload["data"]["attributes"]["address"] = self.address @@ -364,9 +372,10 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) -class PatchIndividualVirtualDebitCard(object): - def __init__(self, card_id: str, tags: Optional[Dict[str, str]] = None): +class PatchIndividualVirtualDebitCard(UnitRequest): + def __init__(self, card_id: str, limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): self.card_id = card_id + self.limits = limits self.tags = tags def to_json_api(self) -> Dict: @@ -377,6 +386,9 @@ def to_json_api(self) -> Dict: } } + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.tags: payload["data"]["attributes"]["tags"] = self.tags @@ -385,11 +397,12 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) -class PatchBusinessVirtualDebitCard(object): +class PatchBusinessVirtualDebitCard(UnitRequest): def __init__(self, card_id: str, address: Optional[Address] = None, phone: Optional[Phone] = None, - email: Optional[str] = None, tags: Optional[Dict[str, str]] = None): + email: Optional[str] = None, limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): self.card_id = card_id self.address = address + self.limits = limits self.phone = phone self.email = email self.tags = tags @@ -402,6 +415,9 @@ def to_json_api(self) -> Dict: } } + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.address: payload["data"]["attributes"]["address"] = self.address diff --git a/unit/models/codecs.py b/unit/models/codecs.py index bccb4852..752acdc2 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -305,6 +305,13 @@ def decode(payload): class UnitEncoder(json.JSONEncoder): def default(self, obj): + if isinstance(obj, CardLevelLimits): + return { + "dailyWithdrawal": obj.daily_withdrawal, + "dailyPurchase": obj.daily_purchase, + "monthlyWithdrawal": obj.monthly_withdrawal, + "monthlyPurchase": obj.monthly_purchase + } if isinstance(obj, FullName): return {"first": obj.first, "last": obj.last} if isinstance(obj, Phone): From b51dc563b73df5e21f77827a96409ff57fa0456c Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Mon, 11 Apr 2022 16:47:20 +0100 Subject: [PATCH 029/137] patch card payload --- unit/models/card.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/card.py b/unit/models/card.py index fca40cd4..993eb6e0 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -270,7 +270,7 @@ def to_json_api(self) -> Dict: payload["data"]["attributes"]["ssn"] = self.ssn if self.limits: - limits["data"]["attributes"]["limits"] = self.limits + payload["data"]["attributes"]["limits"] = self.limits if self.passport: payload["data"]["attributes"]["passport"] = self.passport From ceb8c42805d12520f3fedddb2ba905fe6c74b8d9 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Tue, 12 Apr 2022 12:18:45 +0100 Subject: [PATCH 030/137] fix unit bug --- unit/models/card.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/unit/models/card.py b/unit/models/card.py index 993eb6e0..09341f5c 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -389,8 +389,7 @@ def to_json_api(self) -> Dict: if self.limits: payload["data"]["attributes"]["limits"] = self.limits - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags + payload["data"]["attributes"]["tags"] = self.tags or {} return payload @@ -427,8 +426,7 @@ def to_json_api(self) -> Dict: if self.email: payload["data"]["attributes"]["email"] = self.email - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags + payload["data"]["attributes"]["tags"] = self.tags or {} return payload From 7bca682fdbcbbc7492109e778bb0c2ce47d30e5d Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Fri, 22 Apr 2022 10:11:34 -0700 Subject: [PATCH 031/137] fix payment rejected type --- unit/models/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/event.py b/unit/models/event.py index b4655d19..0ea16ce1 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -314,7 +314,7 @@ class PaymentRejectedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'payment.created' + self.type = 'payment.rejected' self.attributes["status"] = status @staticmethod From 8f90e126869126f081aedae9a378fd3c948a7082 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Fri, 22 Apr 2022 10:26:24 -0700 Subject: [PATCH 032/137] PaymentCreated event returning Payment clearing event --- unit/models/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/event.py b/unit/models/event.py index 0ea16ce1..04076185 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -307,7 +307,7 @@ def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Di @staticmethod def from_json_api(_id, _type, attributes, relationships): - return PaymentClearingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + return PaymentCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], attributes.get("tags"), relationships) class PaymentRejectedEvent(BaseEvent): From cf574d91edffba17be5a6fda4c3322c1df5971fb Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Mon, 2 May 2022 17:39:22 -0700 Subject: [PATCH 033/137] fix benificial owner bug --- unit/models/benificial_owner.py | 26 ++++++++++++++++++++++++++ unit/models/codecs.py | 6 +++++- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 unit/models/benificial_owner.py diff --git a/unit/models/benificial_owner.py b/unit/models/benificial_owner.py new file mode 100644 index 00000000..3b3fcd12 --- /dev/null +++ b/unit/models/benificial_owner.py @@ -0,0 +1,26 @@ +import json +from typing import Optional +from unit.models import * +from unit.utils import date_utils + + +class BenificialOwnerDTO(object): + def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, + status: Optional[Status] = None, ssn: Optional[str] = None, passport: Optional[str] = None, + nationality: Optional[str] = None, percentage: Optional[int] = None): + self.full_name = full_name + self.date_of_birth = date_of_birth + self.address = address + self.phone = phone + self.email = email + self.status = status + self.ssn = ssn + self.passport = passport + self.nationality = nationality + self.percentage = percentage + + @staticmethod + def from_json_api(attributes): + return BenificialOwnerDTO(attributes.get("fullName"), attributes.get("dateOfBirth"), attributes.get("address"), + attributes.get("phone"), attributes.get("email"), attributes.get("status"), attributes.get("ssn"), + attributes.get("passport"), attributes.get("nationality"), attributes.get("percentage")) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 752acdc2..dc06ad79 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -23,6 +23,7 @@ from unit.models.authorization import AuthorizationDTO from unit.models.authorization_request import PurchaseAuthorizationRequestDTO from unit.models.account_end_of_day import AccountEndOfDayDTO +from unit.models.benificial_owner import BenificialOwnerDTO mappings = { "individualApplication": lambda _id, _type, attributes, relationships: @@ -249,7 +250,10 @@ "pinStatus": lambda _id, _type, attributes, relationships: PinStatusDTO.from_json_api(attributes), - } + + "beneficialOwner": lambda _id, _type, attributes, relationships: + BenificialOwnerDTO.from_json_api(attributes), +} def split_json_api_single_response(payload: Dict): From 3513c592cd3cba3da6b81613f637f81d328c83ec Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Wed, 4 May 2022 17:28:46 -0700 Subject: [PATCH 034/137] Removes deprecated SSN field from card api --- unit/models/card.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/unit/models/card.py b/unit/models/card.py index 09341f5c..b368ad20 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -25,7 +25,7 @@ def from_json_api(_id, _type, attributes, relationships): class BusinessDebitCardDTO(object): - def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, ssn: str, + def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, status: CardStatus, passport: Optional[str], nationality: Optional[str], shipping_address: Optional[Address], design: Optional[str], @@ -33,7 +33,7 @@ def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration self.id = id self.type = "businessDebitCard" self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, - "ssn": ssn, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, + "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, "phone": phone, "email": email, "status": status, "passport": passport, "nationality": nationality, "shippingAddress": shipping_address, "design": design} self.relationships = relationships @@ -42,7 +42,7 @@ def from_json_api(_id, _type, attributes, relationships): shipping_address = Address.from_json_api(attributes.get("shippingAddress")) if attributes.get("shippingAddress") else None return BusinessDebitCardDTO( _id, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], - attributes["expirationDate"], attributes["ssn"], FullName.from_json_api(attributes["fullName"]), + attributes["expirationDate"], FullName.from_json_api(attributes["fullName"]), attributes["dateOfBirth"], Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), attributes["email"], attributes["status"], attributes.get("passport"), attributes.get("nationality"), @@ -68,14 +68,14 @@ def from_json_api(_id, _type, attributes, relationships): class BusinessVirtualDebitCardDTO(object): - def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, ssn: str, + def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, status: CardStatus, passport: Optional[str] = None, nationality: Optional[str] = None, relationships: Optional[Dict[str, Relationship]] = None): self.id = id self.type = "businessVirtualDebitCard" self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, - "ssn": ssn, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, + "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, "phone": phone, "email": email, "status": status, "passport": passport, "nationality": nationality} self.relationships = relationships or {} @@ -83,7 +83,7 @@ def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration def from_json_api(_id, _type, attributes, relationships): return BusinessVirtualDebitCardDTO( _id, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], - attributes["expirationDate"], attributes.get("ssn"), FullName.from_json_api(attributes["fullName"]), + attributes["expirationDate"], FullName.from_json_api(attributes["fullName"]), attributes["dateOfBirth"], Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), attributes["email"], attributes["status"], attributes.get("passport"), attributes.get("nationality"), relationships @@ -233,7 +233,7 @@ def __repr__(self): class CreateBusinessVirtualDebitCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, limits: Optional[CardLevelLimits] = None, ssn: Optional[str] = None, + status: CardStatus, limits: Optional[CardLevelLimits] = None, passport: Optional[str] = None, nationality: Optional[str] = None, idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None): @@ -243,7 +243,6 @@ def __init__(self, full_name: FullName, date_of_birth: date, address: Address, p self.phone = phone self.email = email self.status = status - self.ssn = ssn self.passport = passport self.limits = limits self.nationality = nationality @@ -266,9 +265,6 @@ def to_json_api(self) -> Dict: } } - if self.ssn: - payload["data"]["attributes"]["ssn"] = self.ssn - if self.limits: payload["data"]["attributes"]["limits"] = self.limits From 6dd43fc77d2b175f08da0f925c07a3aa66263f05 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 2 Feb 2022 16:46:00 -0800 Subject: [PATCH 035/137] bug patches --- unit/models/__init__.py | 2 +- unit/models/application.py | 29 +++++++++++++++++++++++------ unit/models/codecs.py | 2 ++ unit/models/customer.py | 2 +- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index e7fe6e81..b92c301f 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -183,7 +183,7 @@ def __init__(self, full_name: FullName, email: str, phone: Phone): self.phone = phone @staticmethod - def from_json_api(l: List): + def from_json_api(l: List) -> List: authorized_users = [] for data in l: authorized_users.append(AuthorizedUser(data.get("fullName"), data.get("email"), data.get("phone"))) diff --git a/unit/models/application.py b/unit/models/application.py index d8edfec6..cca5a2d1 100644 --- a/unit/models/application.py +++ b/unit/models/application.py @@ -141,9 +141,22 @@ def __repr__(self): class CreateBusinessApplicationRequest(UnitRequest): - def __init__(self, name: str, address: Address, phone: Phone, state_of_incorporation: str, ein: str, - contact: BusinessContact, officer: Officer, beneficial_owners: [BeneficialOwner], - entity_type: EntityType, dba: str = None, ip: str = None, website: str = None): + def __init__( + self, + name: str, + address: Address, + phone: Phone, + state_of_incorporation: str, + ein: str, + contact: BusinessContact, + officer: Officer, + beneficial_owners: [BeneficialOwner], + entity_type: EntityType, + tags: Optional[Dict[str, str]] = None, + dba: str = None, + ip: str = None, + website: str = None, + ): self.name = name self.address = address self.phone = phone @@ -156,8 +169,9 @@ def __init__(self, name: str, address: Address, phone: Phone, state_of_incorpora self.dba = dba self.ip = ip self.website = website + self.tags = tags - def to_json_api(self) -> Dict: + def to_json_api(self) -> dict: payload = { "data": { "type": "businessApplication", @@ -170,14 +184,17 @@ def to_json_api(self) -> Dict: "contact": self.contact, "officer": self.officer, "beneficialOwners": self.beneficial_owners, - "entityType": self.entity_type - } + "entityType": self.entity_type, + }, } } if self.dba: payload["data"]["attributes"]["dba"] = self.dba + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + if self.ip: payload["data"]["attributes"]["ip"] = self.ip diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 45bd1f6c..12cdce47 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -332,6 +332,8 @@ def default(self, obj): return addr if isinstance(obj, BusinessContact): return {"fullName": obj.full_name, "email": obj.email, "phone": obj.phone} + if isinstance(obj, AuthorizedUser): + return {"fullName": obj.full_name, "email": obj.email, "phone": obj.phone} if isinstance(obj, Officer): officer = {"fullName": obj.full_name, "dateOfBirth": date_utils.to_date_str(obj.date_of_birth), "address": obj.address, "phone": obj.phone, "email": obj.email} diff --git a/unit/models/customer.py b/unit/models/customer.py index 4d9e7e34..f87889d8 100644 --- a/unit/models/customer.py +++ b/unit/models/customer.py @@ -43,7 +43,7 @@ def from_json_api(_id, _type, attributes, relationships): Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), attributes["stateOfIncorporation"], attributes["ein"], attributes["entityType"], BusinessContact.from_json_api(attributes["contact"]), - [AuthorizedUser.from_json_api(user) for user in attributes["authorizedUsers"]], + AuthorizedUser.from_json_api(attributes["authorizedUsers"]), attributes.get("dba"), attributes.get("tags"), relationships) CustomerDTO = Union[IndividualCustomerDTO, BusinessCustomerDTO] From 7604c3a6290acfd3171a8d6ffbc2801f5ed53432 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 10 Feb 2022 14:01:10 -0800 Subject: [PATCH 036/137] fixed inheritence and changed pdf response to content --- unit/api/statement_resource.py | 2 +- unit/models/statement.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/api/statement_resource.py b/unit/api/statement_resource.py index 8d0f47b1..be711e1f 100644 --- a/unit/api/statement_resource.py +++ b/unit/api/statement_resource.py @@ -15,7 +15,7 @@ def get(self, params: GetStatementParams) -> Union[UnitResponse[str], UnitError] response = super().get(f"{self.resource}/{params.statement_id}/{params.output_type}", parameters) if response.status_code == 200: - return UnitResponse[str](response.text, None) + return UnitResponse[bytes](response.content, None) else: return UnitError.from_json_api(response.json()) diff --git a/unit/models/statement.py b/unit/models/statement.py index 0b519ade..590e2604 100644 --- a/unit/models/statement.py +++ b/unit/models/statement.py @@ -16,7 +16,7 @@ def from_json_api(_id, _type, attributes, relationships): OutputType = Literal["html", "pdf"] -class GetStatementParams(object): +class GetStatementParams(UnitRequest): def __init__(self, statement_id: str, output_type: Optional[OutputType] = "html", language: Optional[str] = "en", customer_id: Optional[str] = None): self.statement_id = statement_id From f82564e2c17c159e2d38ae9151e7af1a9b510bed Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Mon, 14 Feb 2022 14:38:50 -0800 Subject: [PATCH 037/137] add auth user to sol prop --- unit/models/customer.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/unit/models/customer.py b/unit/models/customer.py index f87889d8..0868ef9c 100644 --- a/unit/models/customer.py +++ b/unit/models/customer.py @@ -51,12 +51,14 @@ def from_json_api(_id, _type, attributes, relationships): class PatchIndividualCustomerRequest(UnitRequest): def __init__(self, customer_id: str, address: Optional[Address] = None, phone: Optional[Phone] = None, - email: Optional[str] = None, dba: Optional[str] = None, tags: Optional[Dict[str, str]] = None): + email: Optional[str] = None, dba: Optional[str] = None, + authorized_users: Optional[List[AuthorizedUser]] = None, tags: Optional[Dict[str, str]] = None): self.customer_id = customer_id self.address = address self.phone = phone self.email = email self.dba = dba + self.authorized_users = authorized_users self.tags = tags def to_json_api(self) -> Dict: @@ -79,6 +81,9 @@ def to_json_api(self) -> Dict: if self.dba: payload["data"]["attributes"]["dba"] = self.dba + if self.authorized_users: + payload["data"]["attributes"]["authorizedUsers"] = self.authorized_users + if self.tags: payload["data"]["attributes"]["tags"] = self.tags From 4b6f09c21af95f6d850fda8734a738db4d8dd091 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 16 Feb 2022 20:20:23 -0800 Subject: [PATCH 038/137] added payment rejected event --- unit/models/event.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/unit/models/event.py b/unit/models/event.py index cf922d1d..568e664f 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -304,6 +304,18 @@ def from_json_api(_id, _type, attributes, relationships): return PaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], attributes.get("tags"), relationships) +class PaymentRejectedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'payment.rejected' + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], + attributes.get("tags"), relationships) + class StatementsCreatedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, period: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): From 1f8895de056246c0c02530ae71f9bb755c85c37e Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Fri, 18 Feb 2022 10:35:34 -0800 Subject: [PATCH 039/137] added auth users to individual customer dto --- unit/models/__init__.py | 3 +++ unit/models/customer.py | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index b92c301f..f5589981 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -86,6 +86,9 @@ def __init__(self, first: str, last: str): self.first = first self.last = last + def __str__(self): + return f"{self.first} {self.last}" + @staticmethod def from_json_api(data: Dict): return FullName(data.get("first"), data.get("last")) diff --git a/unit/models/customer.py b/unit/models/customer.py index 0868ef9c..83ebb29b 100644 --- a/unit/models/customer.py +++ b/unit/models/customer.py @@ -5,12 +5,13 @@ class IndividualCustomerDTO(object): def __init__(self, id: str, created_at: datetime, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, ssn: Optional[str], passport: Optional[str], nationality: Optional[str], - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + authorized_users: [AuthorizedUser], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): self.id = id self.type = 'individualCustomer' self.attributes = {"createdAt": created_at, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, "phone": phone, "email": email, "ssn": ssn, "passport": passport, - "nationality": nationality, "tags": tags} + "nationality": nationality, "authorizedUsers": authorized_users, "tags": tags} self.relationships = relationships @staticmethod @@ -20,7 +21,7 @@ def from_json_api(_id, _type, attributes, relationships): FullName.from_json_api(attributes["fullName"]), date_utils.to_date(attributes["dateOfBirth"]), Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), attributes["email"], attributes.get("ssn"), attributes.get("passport"), attributes.get("nationality"), - attributes.get("tags"), relationships + AuthorizedUser.from_json_api(attributes["authorizedUsers"]), attributes.get("tags"), relationships ) From 86bb4204997788418df954f65928bc514385b5b8 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 23 Feb 2022 15:25:19 -0800 Subject: [PATCH 040/137] added nsf to ach return reasons --- unit/models/returnAch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/models/returnAch.py b/unit/models/returnAch.py index ecd9eb2c..7f22934d 100644 --- a/unit/models/returnAch.py +++ b/unit/models/returnAch.py @@ -2,10 +2,10 @@ from typing import Literal from unit.models import * -AchReturnReason = Literal["Unauthorized"] +AchReturnReason = Literal["InsufficientFunds", "Unauthorized"] -class ReturnReceivedAchTransactionRequest(object): +class ReturnReceivedAchTransactionRequest(UnitRequest): def __init__(self, transaction_id: str, reason: AchReturnReason, relationships: [Dict[str, Relationship]]): self.transaction_id = transaction_id self.reason = reason From 669d152ce7d1653269fb32c69b2a8aa59eba7fd9 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 2 Mar 2022 17:15:58 -0800 Subject: [PATCH 041/137] fixed transaction dto --- unit/models/transaction.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/unit/models/transaction.py b/unit/models/transaction.py index 67d40f70..b0d7efe6 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -113,8 +113,8 @@ def from_json_api(_id, _type, attributes, relationships): class BookTransactionDTO(BaseTransactionDTO): def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, counterparty: Counterparty, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): + summary: str, counterparty: Counterparty, tags: Optional[Dict[str, str]] = None, + relationships: Optional[Dict[str, Relationship]] = None): BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) self.type = 'bookTransaction' self.attributes["counterparty"] = counterparty @@ -122,9 +122,11 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b @staticmethod def from_json_api(_id, _type, attributes, relationships): return BookTransactionDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], - Counterparty.from_json_api(attributes["counterparty"]), attributes.get("tags"), relationships) + id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), direction=attributes["direction"], + amount=attributes["amount"], balance=attributes["balance"], summary=attributes["summary"], + counterparty=Counterparty.from_json_api(attributes["counterparty"]), + tags=attributes.get("tags"), relationships=relationships + ) class PurchaseTransactionDTO(BaseTransactionDTO): From b57a1acbd2b5f06fdb47e0d34b28168639885960 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 9 Mar 2022 17:40:29 -0800 Subject: [PATCH 042/137] update --- unit/api/transaction_resource.py | 5 +++-- unit/models/returnAch.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/unit/api/transaction_resource.py b/unit/api/transaction_resource.py index 2ab9d369..abcb8e38 100644 --- a/unit/api/transaction_resource.py +++ b/unit/api/transaction_resource.py @@ -9,8 +9,9 @@ def __init__(self, api_url, token): super().__init__(api_url, token) self.resource = "transactions" - def get(self, transaction_id: str, include: Optional[str] = "") -> Union[UnitResponse[TransactionDTO], UnitError]: - response = super().get(f"{self.resource}/{transaction_id}", {"include": include}) + def get(self, transaction_id: str, account_id: str, include: Optional[str] = "") -> Union[UnitResponse[TransactionDTO], UnitError]: + params = {"filter[accountId]": account_id, "include": include} + response = super().get(f"{self.resource}/{transaction_id}", params) if response.status_code == 200: data = response.json().get("data") included = response.json().get("included") diff --git a/unit/models/returnAch.py b/unit/models/returnAch.py index 7f22934d..6c972246 100644 --- a/unit/models/returnAch.py +++ b/unit/models/returnAch.py @@ -2,7 +2,7 @@ from typing import Literal from unit.models import * -AchReturnReason = Literal["InsufficientFunds", "Unauthorized"] +AchReturnReason = Literal["InsufficientFunds", "Unauthorized", "UncollectedFunds"] class ReturnReceivedAchTransactionRequest(UnitRequest): From 46f86058b939302d97f94d4b5d9d50cd8d9c19e2 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 10 Mar 2022 12:27:20 -0800 Subject: [PATCH 043/137] added coded --- unit/models/codecs.py | 3 +++ unit/models/event.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 12cdce47..7b84e713 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -206,6 +206,9 @@ "payment.clearing": lambda _id, _type, attributes, relationships: PaymentClearingEvent.from_json_api(_id, _type, attributes, relationships), + "payment.created": lambda _id, _type, attributes, relationships: + PaymentCreatedEvent.from_json_api(_id, _type, attributes, relationships), + "payment.sent": lambda _id, _type, attributes, relationships: PaymentSentEvent.from_json_api(_id, _type, attributes, relationships), diff --git a/unit/models/event.py b/unit/models/event.py index 568e664f..51a9239a 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -267,6 +267,29 @@ def from_json_api(_id, _type, attributes, relationships): attributes["reason"], attributes["reasonCode"], attributes.get("tags"), relationships) +class PaymentCreatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'payment.created' + self.attributes["status"] = status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentClearingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes.get("tags"), relationships) + +class PaymentRejectedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'payment.created' + self.attributes["status"] = status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentClearingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes.get("tags"), relationships) class PaymentClearingEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], From 4c1b76958fec7c0d72e969e53372ce666fd94a2f Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 10 Mar 2022 14:19:59 -0800 Subject: [PATCH 044/137] added codecs --- unit/models/codecs.py | 6 ++++++ unit/models/event.py | 19 ++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 7b84e713..30462550 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -215,6 +215,9 @@ "payment.returned": lambda _id, _type, attributes, relationships: PaymentReturnedEvent.from_json_api(_id, _type, attributes, relationships), + "payment.rejected": lambda _id, _type, attributes, relationships: + PaymentRejectedEvent.from_json_api(_id, _type, attributes, relationships), + "statements.created": lambda _id, _type, attributes, relationships: StatementsCreatedEvent.from_json_api(_id, _type, attributes, relationships), @@ -224,6 +227,9 @@ "customer.created": lambda _id, _type, attributes, relationships: CustomerCreatedEvent.from_json_api(_id, _type, attributes, relationships), + "customer.updated": lambda _id, _type, attributes, relationships: + CustomerUpdatedEvent.from_json_api(_id, _type, attributes, relationships), + "account.reopened": lambda _id, _type, attributes, relationships: AccountReopenedEvent.from_json_api(_id, _type, attributes, relationships), diff --git a/unit/models/event.py b/unit/models/event.py index 51a9239a..c9757553 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -242,6 +242,19 @@ def from_json_api(_id, _type, attributes, relationships): return CustomerCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), relationships) + +class CustomerUpdatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'customer.updated' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CustomerCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes.get("tags"), relationships) + + class DocumentApprovedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): @@ -328,15 +341,15 @@ def from_json_api(_id, _type, attributes, relationships): attributes.get("tags"), relationships) class PaymentRejectedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + def __init__(self, id: str, created_at: datetime, reason: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'payment.rejected' - self.attributes["previousStatus"] = previous_status + self.attributes["reason"] = previous_status @staticmethod def from_json_api(_id, _type, attributes, relationships): - return PaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], + return PaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["reason"], attributes.get("tags"), relationships) class StatementsCreatedEvent(BaseEvent): From 1a5fdbc81f35fd4d3513de35c9d291f92b4901ec Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 10 Mar 2022 14:41:30 -0800 Subject: [PATCH 045/137] coded update --- unit/models/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/event.py b/unit/models/event.py index c9757553..a560db3c 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -345,7 +345,7 @@ def __init__(self, id: str, created_at: datetime, reason: str, tags: Optional[Di relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'payment.rejected' - self.attributes["reason"] = previous_status + self.attributes["reason"] = reason @staticmethod def from_json_api(_id, _type, attributes, relationships): From 723e89afe325a34e0c066561a869503e45329e77 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Fri, 18 Mar 2022 14:02:38 -0700 Subject: [PATCH 046/137] adds support for /sandbox/applications/application_id/approve --- unit/api/application_resource.py | 17 +++++++++++++++++ unit/models/application.py | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/unit/api/application_resource.py b/unit/api/application_resource.py index 7156c7d3..7c0a6f22 100644 --- a/unit/api/application_resource.py +++ b/unit/api/application_resource.py @@ -71,3 +71,20 @@ def update(self, request: PatchApplicationRequest) -> Union[UnitResponse[Applica else: return UnitError.from_json_api(response.json()) + def approve_sb(self, request: ApproveApplicationSB): + url = f"sandbox/{self.resource}/{request.application_id}/approve" + + payload = request.to_json_api() + response = super().post(url, payload) + + if response.ok: + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse(data, included) + # TODO need DTOs for this response + # if data["type"] == "individualApplication": + # return UnitResponse[IndividualApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + # else: + # return UnitResponse[BusinessApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) diff --git a/unit/models/application.py b/unit/models/application.py index cca5a2d1..458c7d84 100644 --- a/unit/models/application.py +++ b/unit/models/application.py @@ -262,6 +262,7 @@ def to_dict(self) -> Dict: parameters["sort"] = self.sort return parameters + class PatchApplicationRequest(UnitRequest): def __init__(self, application_id: str, type: ApplicationTypes = "individualApplication", tags: Optional[Dict[str, str]] = None): @@ -285,3 +286,21 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) + +class ApproveApplicationSB(UnitRequest): + def __init__(self, application_id: str): + self.application_id = application_id + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "applicationApprove", + "attributes": { + "reason": "sandbox" + } + } + } + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) From 93768ff2eb0d6753b71e77a93da6da7925b321d1 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Fri, 18 Mar 2022 14:16:45 -0700 Subject: [PATCH 047/137] ApproveApplicationSBRequest --- unit/api/application_resource.py | 2 +- unit/models/application.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/api/application_resource.py b/unit/api/application_resource.py index 7c0a6f22..82d041e7 100644 --- a/unit/api/application_resource.py +++ b/unit/api/application_resource.py @@ -71,7 +71,7 @@ def update(self, request: PatchApplicationRequest) -> Union[UnitResponse[Applica else: return UnitError.from_json_api(response.json()) - def approve_sb(self, request: ApproveApplicationSB): + def approve_sb(self, request: ApproveApplicationSBRequest): url = f"sandbox/{self.resource}/{request.application_id}/approve" payload = request.to_json_api() diff --git a/unit/models/application.py b/unit/models/application.py index 458c7d84..db22406a 100644 --- a/unit/models/application.py +++ b/unit/models/application.py @@ -287,7 +287,7 @@ def __repr__(self): json.dumps(self.to_json_api()) -class ApproveApplicationSB(UnitRequest): +class ApproveApplicationSBRequest(UnitRequest): def __init__(self, application_id: str): self.application_id = application_id From 0b6783168283d94141775fc7c9423f8a23d342d1 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Tue, 22 Mar 2022 18:22:42 -0700 Subject: [PATCH 048/137] update to authorization requests --- unit/models/authorization_request.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unit/models/authorization_request.py b/unit/models/authorization_request.py index 6eed39a4..f654701d 100644 --- a/unit/models/authorization_request.py +++ b/unit/models/authorization_request.py @@ -34,7 +34,7 @@ def from_json_api(_id, _type, attributes, relationships): attributes.get("tags"), relationships) -class ListPurchaseAuthorizationRequestParams(object): +class ListPurchaseAuthorizationRequestParams(UnitParams): def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, customer_id: Optional[str] = None): self.limit = limit @@ -51,7 +51,7 @@ def to_dict(self) -> Dict: return parameters -class ApproveAuthorizationRequest(object): +class ApproveAuthorizationRequest(UnitRequest): def __init__(self, authorization_id: str, amount: Optional[int] = None, tags: Optional[Dict[str, str]] = None): self.authorization_id = authorization_id self.amount = amount @@ -77,7 +77,7 @@ def __repr__(self): json.dumps(self.to_json_api()) -class DeclineAuthorizationRequest(object): +class DeclineAuthorizationRequest(UnitRequest): def __init__(self, authorization_id: str, reason: DeclineReason): self.authorization_id = authorization_id self.reason = reason From 5f115cd7c652a5af406ce33813c4506953ad1d55 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 24 Mar 2022 15:20:36 -0700 Subject: [PATCH 049/137] card dto update --- unit/models/card.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/unit/models/card.py b/unit/models/card.py index 5e7b7699..1919acca 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -70,15 +70,15 @@ def from_json_api(_id, _type, attributes, relationships): class BusinessVirtualDebitCardDTO(object): def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, ssn: str, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, passport: Optional[str], nationality: Optional[str], - relationships: Optional[Dict[str, Relationship]]): + status: CardStatus, passport: Optional[str] = None, nationality: Optional[str] = None, + relationships: Optional[Dict[str, Relationship]] = None): self.id = id self.type = "businessVirtualDebitCard" self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, "ssn": ssn, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, "phone": phone, "email": email, "status": status, "passport": passport, "nationality": nationality} - self.relationships = relationships + self.relationships = relationships or {} def from_json_api(_id, _type, attributes, relationships): return BusinessVirtualDebitCardDTO( @@ -93,7 +93,7 @@ def from_json_api(_id, _type, attributes, relationships): Card = Union[IndividualDebitCardDTO, BusinessDebitCardDTO, IndividualVirtualDebitCardDTO, BusinessVirtualDebitCardDTO] -class CreateIndividualDebitCard(object): +class CreateIndividualDebitCard(UnitRequest): def __init__(self, relationships: Dict[str, Relationship], shipping_address: Optional[Address] = None, design: Optional[str] = None, idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None): @@ -129,7 +129,7 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) -class CreateBusinessDebitCard(object): +class CreateBusinessDebitCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, status: CardStatus, shipping_address: Optional[Address], ssn: Optional[str], passport: Optional[str], nationality: Optional[str], design: Optional[str], idempotency_key: Optional[str], @@ -190,7 +190,7 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) -class CreateIndividualVirtualDebitCard(object): +class CreateIndividualVirtualDebitCard(UnitRequest): def __init__(self, relationships: Dict[str, Relationship], idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None): self.idempotency_key = idempotency_key @@ -218,11 +218,11 @@ def __repr__(self): json.dumps(self.to_json_api()) -class CreateBusinessVirtualDebitCard(object): +class CreateBusinessVirtualDebitCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, ssn: Optional[str], passport: Optional[str], nationality: Optional[str], - idempotency_key: Optional[str], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): + status: CardStatus, ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, + idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, + relationships: Optional[Dict[str, Relationship]] = None): self.full_name = full_name self.date_of_birth = date_of_birth self.address = address @@ -233,8 +233,8 @@ def __init__(self, full_name: FullName, date_of_birth: date, address: Address, p self.passport = passport self.nationality = nationality self.idempotency_key = idempotency_key - self.tags = tags - self.relationships = relationships + self.tags = tags or {} + self.relationships = relationships or {} def to_json_api(self) -> Dict: payload = { From 5c77ebb643be1b2c8bc884b1c54b4283da4321eb Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Fri, 1 Apr 2022 09:14:57 -0700 Subject: [PATCH 050/137] fix bug --- unit/models/card.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/card.py b/unit/models/card.py index 1919acca..baa74b69 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -83,7 +83,7 @@ def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration def from_json_api(_id, _type, attributes, relationships): return BusinessVirtualDebitCardDTO( _id, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], - attributes["expirationDate"], attributes["ssn"], FullName.from_json_api(attributes["fullName"]), + attributes["expirationDate"], attributes.get("ssn"), FullName.from_json_api(attributes["fullName"]), attributes["dateOfBirth"], Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), attributes["email"], attributes["status"], attributes.get("passport"), attributes.get("nationality"), relationships From 1c78ee17319257a285cb111d0713546456dd3650 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 6 Apr 2022 15:52:41 +0100 Subject: [PATCH 051/137] added authorization canceled event --- unit/models/event.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/unit/models/event.py b/unit/models/event.py index a560db3c..6326c055 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -73,20 +73,38 @@ def from_json_api(_id, _type, attributes, relationships): return ApplicationAwaitingDocumentsEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), relationships) +class AuthorizationCanceledEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, + recurring: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'authorization.canceled' + self.attributes["cardLast4Digits"] = card_last_4_digits + self.attributes["amount"] = amount + self.attributes["recurring"] = recurring + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AuthorizationCanceledEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["amount"], attributes["cardLast4Digits"], + attributes["recurring"], attributes.get("tags"), + relationships) + class AuthorizationCreatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, card_last_4_digits: str, recurring: str, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, merchant: Dict[str, str], + recurring: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'authorization.created' self.attributes["cardLast4Digits"] = card_last_4_digits + self.attributes["amount"] = amount + self.attributes["merchant"] = merchant self.attributes["recurring"] = recurring @staticmethod def from_json_api(_id, _type, attributes, relationships): return AuthorizationCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["cardLast4Digits"], attributes["recurring"], - attributes.get("tags"), relationships) + attributes["amount"], attributes["cardLast4Digits"], attributes["merchant"], + attributes["recurring"], attributes.get("tags"), relationships) class AuthorizationRequestApprovedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, amount: str, status: str, approved_amount: str, @@ -392,7 +410,7 @@ def from_json_api(_id, _type, attributes, relationships): EventDTO = Union[AccountClosedEvent, AccountFrozenEvent, ApplicationDeniedEvent, ApplicationAwaitingDocumentsEvent, ApplicationPendingReviewEvent, CardActivatedEvent, CardStatusChangedEvent, - AuthorizationCreatedEvent, AuthorizationRequestDeclinedEvent, AuthorizationRequestPendingEvent, + AuthorizationCreatedEvent, AuthorizationCanceledEvent, AuthorizationRequestDeclinedEvent, AuthorizationRequestPendingEvent, AuthorizationRequestApprovedEvent, DocumentApprovedEvent, DocumentRejectedEvent, CheckDepositCreatedEvent, CheckDepositClearingEvent, CheckDepositSentEvent, CheckDepositReturnedEvent, CustomerCreatedEvent, PaymentClearingEvent, PaymentSentEvent, From fd64f5d7fe57db5bb3b404b06cac6e4d477bcf00 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 7 Apr 2022 13:30:44 +0100 Subject: [PATCH 052/137] added limits to card create card request --- unit/models/card.py | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/unit/models/card.py b/unit/models/card.py index baa74b69..d647967f 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -94,11 +94,12 @@ def from_json_api(_id, _type, attributes, relationships): class CreateIndividualDebitCard(UnitRequest): - def __init__(self, relationships: Dict[str, Relationship], shipping_address: Optional[Address] = None, - design: Optional[str] = None, idempotency_key: Optional[str] = None, - tags: Optional[Dict[str, str]] = None): + def __init__(self, relationships: Dict[str, Relationship], limits: Optional[Cardlimits] = None, + shipping_address: Optional[Address] = None, design: Optional[str] = None, + idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None): self.shipping_address = shipping_address self.design = design + self.limits = limits self.idempotency_key = idempotency_key self.tags = tags self.relationships = relationships @@ -115,6 +116,9 @@ def to_json_api(self) -> Dict: if self.shipping_address: payload["data"]["attributes"]["shippingAddress"] = self.shipping_address + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.design: payload["data"]["attributes"]["design"] = self.design @@ -131,23 +135,25 @@ def __repr__(self): class CreateBusinessDebitCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, shipping_address: Optional[Address], ssn: Optional[str], passport: Optional[str], - nationality: Optional[str], design: Optional[str], idempotency_key: Optional[str], - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + status: CardStatus, limits: Optional[CardLimits] = None, shipping_address: Optional[Address] = None, + ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, + design: Optional[str] = None, idempotency_key: Optional[str] = None, + tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None): self.full_name = full_name self.date_of_birth = date_of_birth self.address = address self.phone = phone self.email = email self.status = status + self.limits = limits self.shipping_address = shipping_address self.ssn = ssn self.passport = passport self.nationality = nationality self.design = design self.idempotency_key = idempotency_key - self.tags = tags - self.relationships = relationships + self.tags = tags or {} + self.relationships = relationships or {} def to_json_api(self) -> Dict: payload = { @@ -167,6 +173,9 @@ def to_json_api(self) -> Dict: if self.shipping_address: payload["data"]["attributes"]["shippingAddress"] = self.shipping_address + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.ssn: payload["data"]["attributes"]["ssn"] = self.ssn @@ -192,8 +201,9 @@ def __repr__(self): class CreateIndividualVirtualDebitCard(UnitRequest): def __init__(self, relationships: Dict[str, Relationship], idempotency_key: Optional[str] = None, - tags: Optional[Dict[str, str]] = None): + limits: Optional[CardLimits] = None, tags: Optional[Dict[str, str]] = None): self.idempotency_key = idempotency_key + self.limits = limits self.tags = tags self.relationships = relationships @@ -209,6 +219,9 @@ def to_json_api(self) -> Dict: if self.idempotency_key: payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.tags: payload["data"]["attributes"]["tags"] = self.tags @@ -220,7 +233,8 @@ def __repr__(self): class CreateBusinessVirtualDebitCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, + status: CardStatus, limits: Optional[CardLimits] = None, ssn: Optional[str] = None, + passport: Optional[str] = None, nationality: Optional[str] = None, idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None): self.full_name = full_name @@ -231,6 +245,7 @@ def __init__(self, full_name: FullName, date_of_birth: date, address: Address, p self.status = status self.ssn = ssn self.passport = passport + self.limits = limits self.nationality = nationality self.idempotency_key = idempotency_key self.tags = tags or {} @@ -254,6 +269,9 @@ def to_json_api(self) -> Dict: if self.ssn: payload["data"]["attributes"]["ssn"] = self.ssn + if self.limits: + limits["data"]["attributes"]["limits"] = self.limits + if self.passport: payload["data"]["attributes"]["passport"] = self.passport From b394e8c5dc0983fea4e69347c6a8177e76530eb5 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Wed, 6 Apr 2022 14:24:53 -0700 Subject: [PATCH 053/137] Adds support for simulating incoming ACH --- unit/api/payment_resource.py | 12 +++++++++++- unit/models/codecs.py | 4 ++-- unit/models/payment.py | 37 ++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/unit/api/payment_resource.py b/unit/api/payment_resource.py index 6ef30a43..33c13fda 100644 --- a/unit/api/payment_resource.py +++ b/unit/api/payment_resource.py @@ -1,6 +1,6 @@ from unit.api.base_resource import BaseResource from unit.models.payment import * -from unit.models.codecs import DtoDecoder +from unit.models.codecs import DtoDecoder, split_json_api_single_response class PaymentResource(BaseResource): @@ -45,3 +45,13 @@ def list(self, params: ListPaymentParams = None) -> Union[UnitResponse[List[Paym else: return UnitError.from_json_api(response.json()) + def simulate_incoming_ach(self, request: SimulateIncomingAchRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"sandbox/{self.resource}", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + # TODO Fix dto + _id, _type, attributes, relationships = split_json_api_single_response(data) + return UnitResponse[SimulateIncomingAchPaymentDTO](SimulateIncomingAchPaymentDTO.from_json_api(_id, _type, attributes, relationships), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 30462550..6c5eb98e 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -9,8 +9,8 @@ from unit.models.card import IndividualDebitCardDTO, BusinessDebitCardDTO, IndividualVirtualDebitCardDTO,\ BusinessVirtualDebitCardDTO, PinStatusDTO, CardLimitsDTO from unit.models.transaction import * -from unit.models.payment import AchPaymentDTO, BookPaymentDTO, WirePaymentDTO, AchReceivedPaymentDTO -from unit.models.payment import AchPaymentDTO, BookPaymentDTO, WirePaymentDTO, BillPaymentDTO +from unit.models.payment import AchPaymentDTO, BookPaymentDTO, WirePaymentDTO, AchReceivedPaymentDTO, BillPaymentDTO, \ + SimulateIncomingAchPaymentDTO from unit.models.customerToken import CustomerTokenDTO, CustomerVerificationTokenDTO from unit.models.fee import FeeDTO from unit.models.event import * diff --git a/unit/models/payment.py b/unit/models/payment.py index 2548d980..694f7388 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -33,6 +33,29 @@ def from_json_api(_id, _type, attributes, relationships): attributes["amount"], attributes.get("addenda"), attributes.get("reason"), settlement_date, attributes.get("tags"), relationships) +class SimulateIncomingAchPaymentDTO(BasePayment): + def __init__(self, id: str, created_at: datetime, status: AchStatus, direction: str, + description: str, amount: int, reason: Optional[str], + settlement_date: Optional[datetime], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BasePayment.__init__(self, id, created_at, direction, description, amount, reason, tags, relationships) + self.type = 'achPayment' + self.attributes["status"] = status + self.settlement_date = settlement_date + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BasePayment( + _id, + date_utils.to_datetime(attributes["createdAt"]), + attributes.get("direction"), + attributes["description"], + attributes["amount"], + attributes.get("reason"), + attributes.get("tags"), + relationships + ) + class BookPaymentDTO(BasePayment): def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: Optional[str], description: str, amount: int, reason: Optional[str], tags: Optional[Dict[str, str]], @@ -172,6 +195,20 @@ def to_json_api(self) -> Dict: return payload +class SimulateIncomingAchRequest(CreatePaymentBaseRequest): + def __init__( + self, amount: int, description: str, + relationships: Dict[str, Relationship], + direction: str = "Credit" + ): + CreatePaymentBaseRequest.__init__(self, amount, description, relationships, None, None, direction) + self.verify_counterparty_balance = False + + def to_json_api(self) -> Dict: + payload = CreatePaymentBaseRequest.to_json_api(self) + + return payload + class CreateVerifiedPaymentRequest(CreatePaymentBaseRequest): def __init__(self, amount: int, description: str, plaid_processor_token: str, relationships: Dict[str, Relationship], counterparty_name: Optional[str], verify_counterparty_balance: Optional[bool], From 6d87d0c114c9ff3ec853ae2200440dc3a0667b28 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Fri, 8 Apr 2022 12:36:14 +0100 Subject: [PATCH 054/137] card limits --- unit/models/card.py | 40 ++++++++++++++++++++++++++++------------ unit/models/codecs.py | 7 +++++++ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/unit/models/card.py b/unit/models/card.py index d647967f..fca40cd4 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -94,7 +94,7 @@ def from_json_api(_id, _type, attributes, relationships): class CreateIndividualDebitCard(UnitRequest): - def __init__(self, relationships: Dict[str, Relationship], limits: Optional[Cardlimits] = None, + def __init__(self, relationships: Dict[str, Relationship], limits: Optional[CardLevelLimits] = None, shipping_address: Optional[Address] = None, design: Optional[str] = None, idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None): self.shipping_address = shipping_address @@ -135,7 +135,7 @@ def __repr__(self): class CreateBusinessDebitCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, limits: Optional[CardLimits] = None, shipping_address: Optional[Address] = None, + status: CardStatus, limits: Optional[CardLevelLimits] = None, shipping_address: Optional[Address] = None, ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, design: Optional[str] = None, idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None): @@ -201,7 +201,7 @@ def __repr__(self): class CreateIndividualVirtualDebitCard(UnitRequest): def __init__(self, relationships: Dict[str, Relationship], idempotency_key: Optional[str] = None, - limits: Optional[CardLimits] = None, tags: Optional[Dict[str, str]] = None): + limits: Optional[CardLevelLimits] = None, tags: Optional[Dict[str, str]] = None): self.idempotency_key = idempotency_key self.limits = limits self.tags = tags @@ -233,7 +233,7 @@ def __repr__(self): class CreateBusinessVirtualDebitCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, limits: Optional[CardLimits] = None, ssn: Optional[str] = None, + status: CardStatus, limits: Optional[CardLevelLimits] = None, ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None): @@ -293,12 +293,13 @@ def __repr__(self): CreateCardRequest = Union[CreateIndividualDebitCard, CreateBusinessDebitCard, CreateIndividualVirtualDebitCard, CreateBusinessVirtualDebitCard] -class PatchIndividualDebitCard(object): +class PatchIndividualDebitCard(UnitRequest): def __init__(self,card_id: str, shipping_address: Optional[Address] = None, design: Optional[str] = None, - tags: Optional[Dict[str, str]] = None): + limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): self.card_id = card_id self.shipping_address = shipping_address self.design = design + self.limits = limits self.tags = tags def to_json_api(self) -> Dict: @@ -312,6 +313,9 @@ def to_json_api(self) -> Dict: if self.shipping_address: payload["data"]["attributes"]["shippingAddress"] = self.shipping_address + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.design: payload["data"]["attributes"]["design"] = self.design @@ -324,12 +328,13 @@ def __repr__(self): json.dumps(self.to_json_api()) -class PatchBusinessDebitCard(object): +class PatchBusinessDebitCard(UnitRequest): def __init__(self, card_id: str, shipping_address: Optional[Address] = None, address: Optional[Address] = None, phone: Optional[Phone] = None, email: Optional[str] = None, design: Optional[str] = None, - tags: Optional[Dict[str, str]] = None): + limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): self.card_id = card_id self.shipping_address = shipping_address + self.limits = limits self.design = design self.tags = tags @@ -344,6 +349,9 @@ def to_json_api(self) -> Dict: if self.shipping_address: payload["data"]["attributes"]["shippingAddress"] = self.shipping_address + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.address: payload["data"]["attributes"]["address"] = self.address @@ -364,9 +372,10 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) -class PatchIndividualVirtualDebitCard(object): - def __init__(self, card_id: str, tags: Optional[Dict[str, str]] = None): +class PatchIndividualVirtualDebitCard(UnitRequest): + def __init__(self, card_id: str, limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): self.card_id = card_id + self.limits = limits self.tags = tags def to_json_api(self) -> Dict: @@ -377,6 +386,9 @@ def to_json_api(self) -> Dict: } } + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.tags: payload["data"]["attributes"]["tags"] = self.tags @@ -385,11 +397,12 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) -class PatchBusinessVirtualDebitCard(object): +class PatchBusinessVirtualDebitCard(UnitRequest): def __init__(self, card_id: str, address: Optional[Address] = None, phone: Optional[Phone] = None, - email: Optional[str] = None, tags: Optional[Dict[str, str]] = None): + email: Optional[str] = None, limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): self.card_id = card_id self.address = address + self.limits = limits self.phone = phone self.email = email self.tags = tags @@ -402,6 +415,9 @@ def to_json_api(self) -> Dict: } } + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + if self.address: payload["data"]["attributes"]["address"] = self.address diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 6c5eb98e..cbcfadd9 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -323,6 +323,13 @@ def decode(payload): class UnitEncoder(json.JSONEncoder): def default(self, obj): + if isinstance(obj, CardLevelLimits): + return { + "dailyWithdrawal": obj.daily_withdrawal, + "dailyPurchase": obj.daily_purchase, + "monthlyWithdrawal": obj.monthly_withdrawal, + "monthlyPurchase": obj.monthly_purchase + } if isinstance(obj, FullName): return {"first": obj.first, "last": obj.last} if isinstance(obj, Phone): From b160a0e9f36741a5cd5b1f568465bca329ba7701 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Mon, 11 Apr 2022 16:47:20 +0100 Subject: [PATCH 055/137] patch card payload --- unit/models/card.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/card.py b/unit/models/card.py index fca40cd4..993eb6e0 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -270,7 +270,7 @@ def to_json_api(self) -> Dict: payload["data"]["attributes"]["ssn"] = self.ssn if self.limits: - limits["data"]["attributes"]["limits"] = self.limits + payload["data"]["attributes"]["limits"] = self.limits if self.passport: payload["data"]["attributes"]["passport"] = self.passport From 82f41ccc46e303c2cf91e29e7073f7f7dd5205b2 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Tue, 12 Apr 2022 12:18:45 +0100 Subject: [PATCH 056/137] fix unit bug --- unit/models/card.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/unit/models/card.py b/unit/models/card.py index 993eb6e0..09341f5c 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -389,8 +389,7 @@ def to_json_api(self) -> Dict: if self.limits: payload["data"]["attributes"]["limits"] = self.limits - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags + payload["data"]["attributes"]["tags"] = self.tags or {} return payload @@ -427,8 +426,7 @@ def to_json_api(self) -> Dict: if self.email: payload["data"]["attributes"]["email"] = self.email - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags + payload["data"]["attributes"]["tags"] = self.tags or {} return payload From 129a913acdca6eb97dee5425d52a6f943d1fdb29 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Fri, 22 Apr 2022 10:11:34 -0700 Subject: [PATCH 057/137] fix payment rejected type --- unit/models/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/event.py b/unit/models/event.py index 6326c055..c690e22c 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -314,7 +314,7 @@ class PaymentRejectedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'payment.created' + self.type = 'payment.rejected' self.attributes["status"] = status @staticmethod From 3d562a718164779f1e241c3881e7b085ffbfb980 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Fri, 22 Apr 2022 10:26:24 -0700 Subject: [PATCH 058/137] PaymentCreated event returning Payment clearing event --- unit/models/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/event.py b/unit/models/event.py index c690e22c..84b90b93 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -307,7 +307,7 @@ def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Di @staticmethod def from_json_api(_id, _type, attributes, relationships): - return PaymentClearingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + return PaymentCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], attributes.get("tags"), relationships) class PaymentRejectedEvent(BaseEvent): From c91391223100e4a2902083a326c8f0c363e1138f Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Mon, 2 May 2022 17:39:22 -0700 Subject: [PATCH 059/137] fix benificial owner bug --- unit/models/benificial_owner.py | 26 ++++++++++++++++++++++++++ unit/models/codecs.py | 6 +++++- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 unit/models/benificial_owner.py diff --git a/unit/models/benificial_owner.py b/unit/models/benificial_owner.py new file mode 100644 index 00000000..3b3fcd12 --- /dev/null +++ b/unit/models/benificial_owner.py @@ -0,0 +1,26 @@ +import json +from typing import Optional +from unit.models import * +from unit.utils import date_utils + + +class BenificialOwnerDTO(object): + def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, + status: Optional[Status] = None, ssn: Optional[str] = None, passport: Optional[str] = None, + nationality: Optional[str] = None, percentage: Optional[int] = None): + self.full_name = full_name + self.date_of_birth = date_of_birth + self.address = address + self.phone = phone + self.email = email + self.status = status + self.ssn = ssn + self.passport = passport + self.nationality = nationality + self.percentage = percentage + + @staticmethod + def from_json_api(attributes): + return BenificialOwnerDTO(attributes.get("fullName"), attributes.get("dateOfBirth"), attributes.get("address"), + attributes.get("phone"), attributes.get("email"), attributes.get("status"), attributes.get("ssn"), + attributes.get("passport"), attributes.get("nationality"), attributes.get("percentage")) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index cbcfadd9..63b52fcb 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -24,6 +24,7 @@ from unit.models.authorization import AuthorizationDTO from unit.models.authorization_request import PurchaseAuthorizationRequestDTO from unit.models.account_end_of_day import AccountEndOfDayDTO +from unit.models.benificial_owner import BenificialOwnerDTO mappings = { "individualApplication": lambda _id, _type, attributes, relationships: @@ -262,7 +263,10 @@ "pinStatus": lambda _id, _type, attributes, relationships: PinStatusDTO.from_json_api(attributes), - } + + "beneficialOwner": lambda _id, _type, attributes, relationships: + BenificialOwnerDTO.from_json_api(attributes), +} def split_json_api_single_response(payload: Dict): From dfddb0f449a31b9f47949422cc0ba65f2a532006 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Tue, 10 May 2022 10:34:31 -0700 Subject: [PATCH 060/137] remove duplicate decoder --- unit/models/codecs.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 558883c7..4e91c277 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -189,9 +189,6 @@ "authorizationRequest.approved": lambda _id, _type, attributes, relationships: AuthorizationRequestApprovedEvent.from_json_api(_id, _type, attributes, relationships), - "document.approved": lambda _id, _type, attributes, relationships: - DocumentApprovedEvent.from_json_api(_id, _type, attributes, relationships), - "document.rejected": lambda _id, _type, attributes, relationships: DocumentRejectedEvent.from_json_api(_id, _type, attributes, relationships), From b2a008d916127151efc94093c60adb9474ff1e3f Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Tue, 10 May 2022 11:35:46 -0700 Subject: [PATCH 061/137] fix package --- unit/models/payment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index 694f7388..4c05d6a1 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -34,7 +34,7 @@ def from_json_api(_id, _type, attributes, relationships): attributes.get("tags"), relationships) class SimulateIncomingAchPaymentDTO(BasePayment): - def __init__(self, id: str, created_at: datetime, status: AchStatus, direction: str, + def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: str, description: str, amount: int, reason: Optional[str], settlement_date: Optional[datetime], tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): @@ -87,7 +87,7 @@ def from_json_api(_id, _type, attributes, relationships): class BillPaymentDTO(BasePayment): def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: str, description: str, amount: int, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BasePayment.__init__(self, id, created_at, status, direction, description, amount, reason, tags, relationships) + BasePayment.__init__(self, id, created_at, status, direction, description, amount, tags, relationships) self.type = 'billPayment' @staticmethod From 2e4a95db3a20d48021179175ad40f9d0229fd900 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Tue, 10 May 2022 16:37:12 -0700 Subject: [PATCH 062/137] fix func signature --- unit/models/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/event.py b/unit/models/event.py index 5acbcb40..11e33c03 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -402,7 +402,7 @@ def from_json_api(_id, _type, attributes, relationships): attributes.get("tags"), relationships) class TransactionCreatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, summary: str, direction: str, amount: str, + def __init__(self, id: str, created_at: datetime, summary: str, direction: str, amount: int, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'transaction.created' From 62f6318a4cf2585b062e32750bf797db3cc6a76a Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Mon, 30 May 2022 12:51:03 -0700 Subject: [PATCH 063/137] card authorization patches --- unit/models/__init__.py | 5 ++++- unit/models/authorization.py | 13 +++++------- unit/models/event.py | 38 ++++++++++++++++++++++++------------ 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index f5589981..ecef06b8 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -222,7 +222,10 @@ def __init__(self, longitude: int, latitude: int): @staticmethod def from_json_api(data: Dict): - return Coordinates(data["longitude"], data["latitude"]) + if data: + return Coordinates(data["longitude"], data["latitude"]) + else: + return None class Merchant(object): diff --git a/unit/models/authorization.py b/unit/models/authorization.py index a737cb2d..9f6de45b 100644 --- a/unit/models/authorization.py +++ b/unit/models/authorization.py @@ -7,23 +7,20 @@ class AuthorizationDTO(object): def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, status: AuthorizationStatus, - merchant_name: str, - merchant_type: int, merchant_category: str, merchant_location: Optional[str], recurring: bool, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + merchant: Merchant, recurring: bool, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): self.id = id self.type = "authorization" self.attributes = {"createdAt": created_at, "amount": amount, "cardLast4Digits": card_last_4_digits, - "status": status, "merchant": {"name": merchant_name, "type": merchant_type, - "category": merchant_category, "location": merchant_location}, + "status": status, "merchant": merchant, "recurring": recurring, "tags": tags} self.relationships = relationships @staticmethod def from_json_api(_id, _type, attributes, relationships): return AuthorizationDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["amount"], - attributes["cardLast4Digits"], attributes["status"], attributes["merchant"]["name"], - attributes["merchant"]["type"], attributes["merchant"]["category"], - attributes["merchant"].get("location"), attributes["recurring"], + attributes["cardLast4Digits"], attributes["status"], + Merchant.from_json_api(attributes["merchant"]), attributes["recurring"], attributes.get("tags"), relationships) diff --git a/unit/models/event.py b/unit/models/event.py index 11e33c03..e7399f10 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -90,7 +90,7 @@ def from_json_api(_id, _type, attributes, relationships): relationships) class AuthorizationDeclinedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, merchant: Dict[str, str], + def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, merchant: Merchant, reason: str, recurring: str, tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None): BaseEvent.__init__(self, id, created_at, tags, relationships) @@ -106,11 +106,11 @@ def from_json_api(_id, _type, attributes, relationships): return AuthorizationDeclinedEvent( id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), amount=attributes["amount"], card_last_4_digits=attributes["cardLast4Digits"], - merchant=attributes["merchant"], reason=attributes.get("reason"), recurring=attributes["recurring"], + merchant=Merchant.from_json_api(attributes["merchant"]), reason=attributes.get("reason"), recurring=attributes["recurring"], tags=attributes.get("tags"), relationships=relationships) class AuthorizationCreatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, merchant: Dict[str, str], + def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, merchant: Merchant, recurring: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'authorization.created' @@ -122,7 +122,7 @@ def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digit @staticmethod def from_json_api(_id, _type, attributes, relationships): return AuthorizationCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["amount"], attributes["cardLast4Digits"], attributes["merchant"], + attributes["amount"], attributes["cardLast4Digits"], Merchant.from_json_api(attributes["merchant"]), attributes["recurring"], attributes.get("tags"), relationships) class AuthorizationRequestApprovedEvent(BaseEvent): @@ -172,13 +172,16 @@ def from_json_api(_id, _type, attributes, relationships): class AuthorizationRequestPendingEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, amount: str, status: str, partial_approval_allowed: str, - merchant: Dict[str, str], recurring: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): + def __init__(self, id: str, created_at: datetime, amount: int, status: str, partial_approval_allowed: str, + card_present: bool, digital_wallet: str, ecommerce: bool, merchant: Merchant, recurring: str, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'authorizationRequest.pending' self.attributes["amount"] = amount self.attributes["status"] = status + self.attributes["cardPresent"] = card_present + self.attributes["digitalWallet"] = digital_wallet + self.attributes["ecommerce"] = ecommerce self.attributes["partialApprovalAllowed"] = partial_approval_allowed self.attributes["merchant"] = merchant self.attributes["recurring"] = recurring @@ -186,10 +189,20 @@ def __init__(self, id: str, created_at: datetime, amount: str, status: str, part @staticmethod def from_json_api(_id, _type, attributes, relationships): - return AuthorizationRequestPendingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["amount"], attributes["status"], - attributes["partialApprovalAllowed"], attributes["merchant"], - attributes["recurring"], attributes.get("tags"), relationships) + return AuthorizationRequestPendingEvent( + id=_id, + created_at=date_utils.to_datetime(attributes["createdAt"]), + amount=attributes["amount"], + status=attributes["status"], + ecommerce=attributes.get("ecommerce"), + card_present=attributes.get("cardPresent"), + digital_wallet=attributes.get("digitalWallet"), + partial_approval_allowed=attributes["partialApprovalAllowed"], + merchant=Merchant.from_json_api(attributes["merchant"]), + recurring=attributes["recurring"], + tags=attributes.get("tags"), + relationships=relationships + ) class CardActivatedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], @@ -429,7 +442,8 @@ def from_json_api(_id, _type, attributes, relationships): EventDTO = Union[AccountClosedEvent, AccountFrozenEvent, ApplicationDeniedEvent, ApplicationAwaitingDocumentsEvent, ApplicationPendingReviewEvent, CardActivatedEvent, CardStatusChangedEvent, - AuthorizationCreatedEvent, AuthorizationCanceledEvent, AuthorizationRequestDeclinedEvent, AuthorizationRequestPendingEvent, + AuthorizationCreatedEvent, AuthorizationCanceledEvent, AuthorizationDeclinedEvent, + AuthorizationRequestDeclinedEvent, AuthorizationRequestPendingEvent, AuthorizationRequestApprovedEvent, DocumentApprovedEvent, DocumentRejectedEvent, CheckDepositCreatedEvent, CheckDepositClearingEvent, CheckDepositSentEvent, CheckDepositReturnedEvent, CustomerCreatedEvent, PaymentClearingEvent, PaymentSentEvent, From cd2378fd9038bbb1c93d402862b789bd497220ad Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Mon, 6 Jun 2022 17:27:55 -0700 Subject: [PATCH 064/137] fix merchant type bug --- unit/models/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index ecef06b8..fb288cbe 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -229,7 +229,7 @@ def from_json_api(data: Dict): class Merchant(object): - def __init__(self, name: str, type: int, category: str, location: Optional[str]): + def __init__(self, name: str, type: int, category: Optional[str], location: Optional[str]): self.name = name self.type = type self.category = category @@ -237,7 +237,7 @@ def __init__(self, name: str, type: int, category: str, location: Optional[str]) @staticmethod def from_json_api(data: Dict): - return Merchant(data["name"], data["type"], data["category"], data.get("location")) + return Merchant(data["name"], data["type"], data.get("category"), data.get("location")) class CardLevelLimits(object): def __init__(self, daily_withdrawal: int, daily_purchase: int, monthly_withdrawal: int, monthly_purchase: int): From d363d96f1113c791cfc07d9714c92cb9ff80f6a8 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Tue, 7 Jun 2022 11:19:58 -0700 Subject: [PATCH 065/137] added authorization canceled to coded --- unit/models/codecs.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 4e91c277..2fa48f45 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -177,6 +177,9 @@ "authorization.created": lambda _id, _type, attributes, relationships: AuthorizationCreatedEvent.from_json_api(_id, _type, attributes, relationships), + "authorization.canceled": lambda _id, _type, attributes, relationships: + AuthorizationCanceledEvent.from_json_api(_id, _type, attributes, relationships), + "authorization.declined": lambda _id, _type, attributes, relationships: AuthorizationDeclinedEvent.from_json_api(_id, _type, attributes, relationships), From 38c8208e03e44b02cbfdb3f8c3d24651eac66623 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Thu, 9 Jun 2022 12:01:00 -0700 Subject: [PATCH 066/137] fix coordinate bug --- unit/models/transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/transaction.py b/unit/models/transaction.py index b0d7efe6..58aec0fa 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -154,7 +154,7 @@ def from_json_api(_id, _type, attributes, relationships): return PurchaseTransactionDTO( _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], attributes["amount"], attributes["balance"], attributes["summary"], attributes["cardLast4Digits"], - Merchant.from_json_api(attributes["merchant"]), Coordinates.from_json_api(attributes["coordinates"]), + Merchant.from_json_api(attributes["merchant"]), Coordinates.from_json_api(attributes.get("coordinates")), attributes["recurring"], attributes.get("interchange"), attributes.get("ecommerce"), attributes.get("cardPresent"), attributes.get("paymentMethod"), attributes.get("digitalWallet"), attributes.get("cardVerificationData"), attributes.get("cardNetwork"), attributes.get("tags"), From 37da55f778f5d537f72cd0e412df758e1521c0df Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Tue, 14 Jun 2022 12:31:19 -0700 Subject: [PATCH 067/137] added rewards to sdk --- unit/api/reward_resource.py | 39 ++++++++++ unit/models/codecs.py | 6 ++ unit/models/reward.py | 138 ++++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 unit/api/reward_resource.py create mode 100644 unit/models/reward.py diff --git a/unit/api/reward_resource.py b/unit/api/reward_resource.py new file mode 100644 index 00000000..8cafa463 --- /dev/null +++ b/unit/api/reward_resource.py @@ -0,0 +1,39 @@ +from typing import Union, List + +from unit.api.base_resource import BaseResource +from unit.models import UnitResponse, UnitError +from unit.models.reward import RewardDTO, ListRewardsParams, CreateRewardRequest + +from unit.models.codecs import DtoDecoder + + +class RewardResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "rewards" + + def create(self, request: CreateRewardRequest) -> Union[UnitResponse[RewardDTO], UnitError]: + payload = request.to_json_api() + response = super().post(self.resource, payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[RewardDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def get(self, reward_id: str) -> Union[UnitResponse[RewardDTO], UnitError]: + response = super().get(f"{self.resource}/{reward_id}") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[RewardDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListRewardsParams = None) -> Union[UnitResponse[List[RewardDTO]], UnitError]: + params = params or ListRewardsParams() + response = super().get(self.resource, params.to_dict()) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[List[RewardDTO]](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 2fa48f45..ed7d296e 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -1,6 +1,8 @@ import json from unit.models import * from datetime import datetime, date + +from unit.models.reward import RewardDTO from unit.utils import date_utils from unit.models.applicationForm import ApplicationFormDTO from unit.models.application import IndividualApplicationDTO, BusinessApplicationDTO, ApplicationDocumentDTO @@ -269,6 +271,10 @@ "beneficialOwner": lambda _id, _type, attributes, relationships: BenificialOwnerDTO.from_json_api(attributes), + + "reward": lambda _id, _type, attributes, relationships: + RewardDTO.from_json_api(_id, attributes, relationships), + } diff --git a/unit/models/reward.py b/unit/models/reward.py new file mode 100644 index 00000000..995fa25f --- /dev/null +++ b/unit/models/reward.py @@ -0,0 +1,138 @@ +import json +from typing import Optional, Literal, Dict, List +from datetime import datetime + +from unit.models import Relationship, UnitRequest, UnitParams + + +SORT_ORDERS = Literal["created_at", "-created_at"] +RELATED_RESOURCES = Literal["customer", "account", "transaction"] + + +class RewardDTO(object): + def __init__(self, id: str, amount: int, description: str, tags: Optional[Dict[str, str]] = None, + relationships: Optional[Dict[str, Relationship]] = None): + self.id = id + self.type = "reward" + self.attributes = {"amount": amount, "description": description, "tags": tags} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, attributes, relationships): + return RewardDTO(_id, attributes["amount"], attributes["description"], attributes.get("tags"), relationships) + + +class CreateRewardRequest(UnitRequest): + def __init__( + self, + amount: int, + description: str, + receiving_account_id: str, + rewarded_transaction_id: Optional[str], + funding_account_id: Optional[str] = None, + idempotency_key: Optional[str] = None, + tags: Optional[Dict[str, str]] = None + ): + self.type = "reward" + self.amount = amount + self.description = description + self.rewarded_transaction_id = rewarded_transaction_id + self.receiving_account_id = receiving_account_id + self.funding_account_id = funding_account_id + self.idempotency_key = idempotency_key + self.tags = tags + + def to_json_api(self) -> Dict: + relationships = { + "receivingAccount": Relationship(_type="depositAccount", _id=self.receiving_account_id) + } + + if self.rewarded_transaction_id: + relationships["rewardedTransaction"] = Relationship(_type="transaction", _id=self.rewarded_transaction_id) + + if self.funding_account_id: + relationships["fundingAccount"] = Relationship(_type="depositAccount", _id=self.funding_account_id) + + payload = { + "data": { + "type": self.type, + "attributes": { + "amount": self.amount, + "description": self.description + }, + "relationships": relationships + } + } + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class ListRewardsParams(UnitParams): + def __init__( + self, + limit: int = 100, + offset: int = 0, + transaction_id: Optional[str] = None, + rewarded_transaction_id: Optional[str] = None, + receiving_account_id: Optional[str] = None, + customer_id: Optional[str] = None, + card_id: Optional[str] = None, + status: Optional[str] = None, + since: Optional[datetime] = None, + until: Optional[datetime] = None, + sort: Optional[SORT_ORDERS] = None, + include: Optional[List[RELATED_RESOURCES]] = None, + ): + self.limit = limit + self.offset = offset + self.transaction_id = transaction_id + self.rewarded_transaction_id = rewarded_transaction_id + self.receiving_account_id = receiving_account_id + self.customer_id = customer_id + self.card_id = card_id + self.status = status + self.since = since + self.until = until + self.sort = sort + self.include = include + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + + if self.transaction_id: + parameters["filter[transactionId]"] = self.transaction_id + + if self.rewarded_transaction_id: + parameters["filter[rewardedTransactionId]"] = self.rewarded_transaction_id + + if self.receiving_account_id: + parameters["filter[receivingAccountId]"] = self.receiving_account_id + + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + + if self.card_id: + parameters["filter[cardId]"] = self.card_id + + if self.status: + parameters["filter[status]"] = self.status + + if self.since: + parameters["filter[since]"] = self.since + + if self.unitl: + parameters["filter[until]"] = self.until + + if self.sort: + parameters["sort"] = self.sort + + return parameters From 7246c06fadc44bec79c1869b3cba1948d2498256 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Wed, 15 Jun 2022 15:19:27 -0700 Subject: [PATCH 068/137] reward patches --- unit/__init__.py | 2 ++ unit/models/reward.py | 21 ++++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/unit/__init__.py b/unit/__init__.py index 8076f8c6..968dc249 100644 --- a/unit/__init__.py +++ b/unit/__init__.py @@ -19,6 +19,7 @@ from unit.api.authorization_resource import AuthorizationResource from unit.api.authorization_request_resource import AuthorizationRequestResource from unit.api.account_end_of_day_resource import AccountEndOfDayResource +from unit.api.reward_resource import RewardResource __all__ = ["api", "models", "utils"] @@ -46,3 +47,4 @@ def __init__(self, api_url, token): self.authorizations = AuthorizationResource(api_url, token) self.authorization_requests = AuthorizationRequestResource(api_url, token) self.account_end_of_day = AccountEndOfDayResource(api_url, token) + self.rewards = RewardResource(api_url, token) diff --git a/unit/models/reward.py b/unit/models/reward.py index 995fa25f..3e3a4246 100644 --- a/unit/models/reward.py +++ b/unit/models/reward.py @@ -10,16 +10,16 @@ class RewardDTO(object): - def __init__(self, id: str, amount: int, description: str, tags: Optional[Dict[str, str]] = None, + def __init__(self, id: str, amount: int, description: str, status: str, tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None): self.id = id self.type = "reward" - self.attributes = {"amount": amount, "description": description, "tags": tags} + self.attributes = {"amount": amount, "description": description, "status": status, "tags": tags} self.relationships = relationships @staticmethod def from_json_api(_id, attributes, relationships): - return RewardDTO(_id, attributes["amount"], attributes["description"], attributes.get("tags"), relationships) + return RewardDTO(_id, attributes["amount"], attributes["description"], attributes["status"], attributes.get("tags"), relationships) class CreateRewardRequest(UnitRequest): @@ -28,7 +28,7 @@ def __init__( amount: int, description: str, receiving_account_id: str, - rewarded_transaction_id: Optional[str], + rewarded_transaction_id: Optional[str] = None, funding_account_id: Optional[str] = None, idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None @@ -42,17 +42,16 @@ def __init__( self.idempotency_key = idempotency_key self.tags = tags - def to_json_api(self) -> Dict: - relationships = { - "receivingAccount": Relationship(_type="depositAccount", _id=self.receiving_account_id) + self.relationships = { + "receivingAccount": Relationship(_type="depositaccount", _id=self.receiving_account_id) } - if self.rewarded_transaction_id: - relationships["rewardedTransaction"] = Relationship(_type="transaction", _id=self.rewarded_transaction_id) + self.relationships["rewardedTransaction"] = Relationship(_type="transaction", _id=self.rewarded_transaction_id) if self.funding_account_id: - relationships["fundingAccount"] = Relationship(_type="depositAccount", _id=self.funding_account_id) + self.relationships["fundingAccount"] = Relationship(_type="depositAccount", _id=self.funding_account_id) + def to_json_api(self) -> Dict: payload = { "data": { "type": self.type, @@ -60,7 +59,7 @@ def to_json_api(self) -> Dict: "amount": self.amount, "description": self.description }, - "relationships": relationships + "relationships": self.relationships } } From 85af67ba1591eb2e190f7821f28ab2c6a298984f Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Mon, 27 Jun 2022 17:17:34 -0700 Subject: [PATCH 069/137] reward bug fix --- unit/models/reward.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/reward.py b/unit/models/reward.py index 3e3a4246..07f1535b 100644 --- a/unit/models/reward.py +++ b/unit/models/reward.py @@ -43,7 +43,7 @@ def __init__( self.tags = tags self.relationships = { - "receivingAccount": Relationship(_type="depositaccount", _id=self.receiving_account_id) + "receivingAccount": Relationship(_type="depositAccount", _id=self.receiving_account_id) } if self.rewarded_transaction_id: self.relationships["rewardedTransaction"] = Relationship(_type="transaction", _id=self.rewarded_transaction_id) From 29feabecdb11e42e8c811bcb010b4d2d7196f200 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Tue, 26 Jul 2022 18:04:31 -0700 Subject: [PATCH 070/137] fixed payment rejected bug --- unit/models/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/event.py b/unit/models/event.py index e7399f10..5fabc739 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -399,7 +399,7 @@ def __init__(self, id: str, created_at: datetime, reason: str, tags: Optional[Di @staticmethod def from_json_api(_id, _type, attributes, relationships): - return PaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["reason"], + return PaymentRejectedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["reason"], attributes.get("tags"), relationships) class StatementsCreatedEvent(BaseEvent): From ce9b05aa87f664636ac69166cab88bd284e2e484 Mon Sep 17 00:00:00 2001 From: Dragon Prevost Date: Fri, 29 Jul 2022 10:09:19 -0700 Subject: [PATCH 071/137] fix customer updated codec bug --- unit/models/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/event.py b/unit/models/event.py index 5fabc739..e6fcfa72 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -301,7 +301,7 @@ def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]] @staticmethod def from_json_api(_id, _type, attributes, relationships): - return CustomerCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + return CustomerUpdatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), relationships) From 915e4d0d118358cd50d6d9f735aebfe41e345536 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Fri, 16 Sep 2022 17:46:18 -0700 Subject: [PATCH 072/137] Patch CreateCounterpartyRequest to inherit UnitRequest --- unit/models/counterparty.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/counterparty.py b/unit/models/counterparty.py index c91ecb8d..e043b414 100644 --- a/unit/models/counterparty.py +++ b/unit/models/counterparty.py @@ -23,7 +23,7 @@ def from_json_api(_id, _type, attributes, relationships): attributes["accountType"], attributes["type"], attributes["permissions"], relationships) -class CreateCounterpartyRequest(object): +class CreateCounterpartyRequest(UnitRequest): def __init__(self, name: str, routing_number: str, account_number: str, account_type: str, type: str, relationships: [Dict[str, Relationship]], tags: Optional[object] = None, idempotency_key: Optional[str] = None): From cc30baadeea24605856b4f77a32df068e969841d Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Wed, 11 Jan 2023 17:31:52 -0800 Subject: [PATCH 073/137] Simulate transmitting and clearing ach payments --- unit/__init__.py | 2 ++ unit/api/ach_resource.py | 34 +++++++++++++++++++ unit/models/payment.py | 72 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 unit/api/ach_resource.py diff --git a/unit/__init__.py b/unit/__init__.py index 968dc249..99bda338 100644 --- a/unit/__init__.py +++ b/unit/__init__.py @@ -4,6 +4,7 @@ from unit.api.card_resource import CardResource from unit.api.transaction_resource import TransactionResource from unit.api.payment_resource import PaymentResource +from unit.api.ach_resource import AchResource from unit.api.statement_resource import StatementResource from unit.api.customerToken_resource import CustomerTokenResource from unit.api.counterparty_resource import CounterpartyResource @@ -32,6 +33,7 @@ def __init__(self, api_url, token): self.cards = CardResource(api_url, token) self.transactions = TransactionResource(api_url, token) self.payments = PaymentResource(api_url, token) + self.ach = AchResource(api_url, token) self.statements = StatementResource(api_url, token) self.customerTokens = CustomerTokenResource(api_url, token) self.counterparty = CounterpartyResource(api_url, token) diff --git a/unit/api/ach_resource.py b/unit/api/ach_resource.py new file mode 100644 index 00000000..f19835db --- /dev/null +++ b/unit/api/ach_resource.py @@ -0,0 +1,34 @@ +from unit.api.base_resource import BaseResource +from unit.models.payment import * +from unit.models.codecs import DtoDecoder, split_json_api_single_response + + +class AchResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "ach" + + def simulate_transmit(self, request: SimulateTransmitAchRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"sandbox/{self.resource}/transmit", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + # TODO Fix dto + _id, _type, attributes, relationships = split_json_api_single_response(data) + print("simulate_transmit") + print("data", data) + print("_id, _type, attributes, relationships", _id, _type, attributes, relationships) + return UnitResponse[SimulateAchPaymentDTO](SimulateAchPaymentDTO.from_json_api(_id, _type, attributes, relationships), None) + else: + return UnitError.from_json_api(response.json()) + + def simulate_clear(self, request: SimulateClearAchRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"sandbox/{self.resource}/clear", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + # TODO Fix dto + _id, _type, attributes, relationships = split_json_api_single_response(data) + return UnitResponse[SimulateAchPaymentDTO](SimulateAchPaymentDTO.from_json_api(_id, _type, attributes, relationships), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/unit/models/payment.py b/unit/models/payment.py index 4c05d6a1..502fb3f0 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -56,6 +56,30 @@ def from_json_api(_id, _type, attributes, relationships): relationships ) +class SimulateAchPaymentDTO(object): + def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: str, + description: str, amount: int, reason: Optional[str], + settlement_date: Optional[datetime], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BasePayment.__init__(self, id, created_at, direction, description, amount, reason, tags, relationships) + self.type = 'achPayment' + self.attributes["status"] = status + self.settlement_date = settlement_date + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BasePayment( + _id, + date_utils.to_datetime(attributes["createdAt"]), + attributes.get("status"), + attributes.get("direction"), + attributes["description"], + attributes["amount"], + attributes.get("reason"), + attributes.get("tags"), + relationships + ) + class BookPaymentDTO(BasePayment): def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: Optional[str], description: str, amount: int, reason: Optional[str], tags: Optional[Dict[str, str]], @@ -209,6 +233,54 @@ def to_json_api(self) -> Dict: return payload +class SimulateTransmitAchRequest(UnitRequest): + def __init__( + self, payment_id: int + ): + self.type = "transmitAchPayment" + self.id = payment_id + self.relationships = { + "payment": { + "data": { + "type": "achPayment", + "id": self.id + } + } + } + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": self.type, + "relationships": self.relationships + } + } + return payload + +class SimulateClearAchRequest(UnitRequest): + def __init__( + self, payment_id: int + ): + self.type = "clearAchPayment" + self.id = payment_id + self.relationships = { + "payment": { + "data": { + "type": "achPayment", + "id": self.id + } + } + } + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": self.type, + "relationships": self.relationships + } + } + return payload + class CreateVerifiedPaymentRequest(CreatePaymentBaseRequest): def __init__(self, amount: int, description: str, plaid_processor_token: str, relationships: Dict[str, Relationship], counterparty_name: Optional[str], verify_counterparty_balance: Optional[bool], From a3c64d0d538cb9dfaf19807149bf8e958e1b2a5a Mon Sep 17 00:00:00 2001 From: Eric Ghildyal Date: Tue, 13 Jun 2023 17:02:07 -0400 Subject: [PATCH 074/137] Update CreateBusinessApplicationRequest with new required compliance fields --- unit/models/application.py | 436 +++++++++++++++++++++++++++++-------- 1 file changed, 347 insertions(+), 89 deletions(-) diff --git a/unit/models/application.py b/unit/models/application.py index db22406a..bfb450b7 100644 --- a/unit/models/application.py +++ b/unit/models/application.py @@ -4,76 +4,265 @@ ApplicationStatus = Literal["Approved", "Denied", "Pending", "PendingReview"] -DocumentType = Literal["IdDocument", "Passport", "AddressVerification", "CertificateOfIncorporation", - "EmployerIdentificationNumberConfirmation"] +DocumentType = Literal[ + "IdDocument", + "Passport", + "AddressVerification", + "CertificateOfIncorporation", + "EmployerIdentificationNumberConfirmation", +] + +ReasonCode = Literal[ + "PoorQuality", + "NameMismatch", + "SSNMismatch", + "AddressMismatch", + "DOBMismatch", + "ExpiredId", + "EINMismatch", + "StateMismatch", + "Other", +] + +ApplicationTypes = Literal[ + "individualApplication", "businessApplication", "trustApplication" +] + + +Industry = Literal[ + "Retail", + "Wholesale", + "Restaurants", + "Hospitals", + "Construction", + "Insurance", + "Unions", + "RealEstate", + "FreelanceProfessional", + "OtherProfessionalServices", + "OnlineRetailer", + "OtherEducationServices", +] + +AnnualRevenue = Literal[ + "UpTo250k", + "Between250kAnd500k", + "Between500kAnd1m", + "Between1mAnd5m", + "Over5m", + "UpTo50k", + "Between50kAnd100k", + "Between100kAnd200k", + "Between200kAnd500k", + "Over500k", +] + +NumberOfEmployees = Literal[ + "One", + "Between2And5", + "Between5And10", + "Over10", + "UpTo10", + "Between10And50", + "Between50And100", + "Between100And500", + "Over500", +] + +CashFlow = Literal["Unpredictable", "Predictable"] + +BusinessVertical = Literal[ + "AdultEntertainmentDatingOrEscortServices", + "AgricultureForestryFishingOrHunting", + "ArtsEntertainmentAndRecreation", + "BusinessSupportOrBuildingServices", + "Cannabis", + "Construction", + "DirectMarketingOrTelemarketing", + "EducationalServices", + "FinancialServicesCryptocurrency", + "FinancialServicesDebitCollectionOrConsolidation", + "FinancialServicesMoneyServicesBusinessOrCurrencyExchange", + "FinancialServicesOther", + "FinancialServicesPaydayLending", + "GamingOrGambling", + "HealthCareAndSocialAssistance", + "HospitalityAccommodationOrFoodServices", + "LegalAccountingConsultingOrComputerProgramming", + "Manufacturing", + "Mining", + "Nutraceuticals", + "PersonalCareServices", + "PublicAdministration", + "RealEstate", + "ReligiousCivicAndSocialOrganizations", + "RepairAndMaintenance", + "RetailTrade", + "TechnologyMediaOrTelecom", + "TransportationOrWarehousing", + "Utilities", + "WholesaleTrade", +] + +Revocability = Literal["Revocable", "Irrevocable"] +SourceOfFunds = Literal[ + "Inheritance", "Salary", "Savings", "InvestmentReturns", "Gifts" +] -ReasonCode = Literal["PoorQuality", "NameMismatch", "SSNMismatch", "AddressMismatch", "DOBMismatch", "ExpiredId", - "EINMismatch", "StateMismatch", "Other"] - -ApplicationTypes = Literal["individualApplication", "businessApplication"] class IndividualApplicationDTO(object): - def __init__(self, id: str, created_at: datetime, full_name: FullName, address: Address, date_of_birth: date, - email: str, phone: Phone, status: ApplicationStatus, ssn: Optional[str], message: Optional[str], - ip: Optional[str], ein: Optional[str], dba: Optional[str], - sole_proprietorship: Optional[bool], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): + def __init__( + self, + id: str, + created_at: datetime, + full_name: FullName, + address: Address, + date_of_birth: date, + email: str, + phone: Phone, + status: ApplicationStatus, + ssn: Optional[str], + message: Optional[str], + ip: Optional[str], + ein: Optional[str], + dba: Optional[str], + sole_proprietorship: Optional[bool], + tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]], + ): self.id = id self.type = "individualApplication" - self.attributes = {"createdAt": created_at, "fullName": full_name, "address": address, - "dateOfBirth": date_of_birth, "email": email, "phone": phone, "status": status, "ssn": ssn, - "message": message, "ip": ip, "ein": ein, "dba": dba, - "soleProprietorship": sole_proprietorship, "tags": tags} + self.attributes = { + "createdAt": created_at, + "fullName": full_name, + "address": address, + "dateOfBirth": date_of_birth, + "email": email, + "phone": phone, + "status": status, + "ssn": ssn, + "message": message, + "ip": ip, + "ein": ein, + "dba": dba, + "soleProprietorship": sole_proprietorship, + "tags": tags, + } self.relationships = relationships @staticmethod def from_json_api(_id, _type, attributes, relationships): return IndividualApplicationDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), - FullName.from_json_api(attributes["fullName"]), Address.from_json_api(attributes["address"]), + _id, + date_utils.to_datetime(attributes["createdAt"]), + FullName.from_json_api(attributes["fullName"]), + Address.from_json_api(attributes["address"]), date_utils.to_date(attributes["dateOfBirth"]), - attributes["email"], Phone.from_json_api(attributes["phone"]), attributes["status"], - attributes.get("ssn"), attributes.get("message"), attributes.get("ip"), - attributes.get("ein"), attributes.get("dba"), attributes.get("soleProprietorship"), - attributes.get("tags"), relationships + attributes["email"], + Phone.from_json_api(attributes["phone"]), + attributes["status"], + attributes.get("ssn"), + attributes.get("message"), + attributes.get("ip"), + attributes.get("ein"), + attributes.get("dba"), + attributes.get("soleProprietorship"), + attributes.get("tags"), + relationships, ) class BusinessApplicationDTO(object): - def __init__(self, id: str, created_at: datetime, name: str, address: Address, phone: Phone, - status: ApplicationStatus, state_of_incorporation: str, entity_type: EntityType, - contact: BusinessContact, officer: Officer, beneficial_owners: [BeneficialOwner], ssn: Optional[str], - message: Optional[str], ip: Optional[str], ein: Optional[str], dba: Optional[str], - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + def __init__( + self, + id: str, + created_at: datetime, + name: str, + address: Address, + phone: Phone, + status: ApplicationStatus, + state_of_incorporation: str, + entity_type: EntityType, + contact: BusinessContact, + officer: Officer, + beneficial_owners: [BeneficialOwner], + ssn: Optional[str], + message: Optional[str], + ip: Optional[str], + ein: Optional[str], + dba: Optional[str], + tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]], + ): self.id = id self.type = "businessApplication" - self.attributes = {"createdAt": created_at, "name": name, "address": address, "phone": phone, - "status": status, "ssn": ssn, "stateOfIncorporation": state_of_incorporation, "ssn": ssn, - "message": message, "ip": ip, "ein": ein, "entityType": entity_type, "dba": dba, - "contact": contact, "officer": officer, "beneficialOwners":beneficial_owners, "tags": tags} + self.attributes = { + "createdAt": created_at, + "name": name, + "address": address, + "phone": phone, + "status": status, + "ssn": ssn, + "stateOfIncorporation": state_of_incorporation, + "ssn": ssn, + "message": message, + "ip": ip, + "ein": ein, + "entityType": entity_type, + "dba": dba, + "contact": contact, + "officer": officer, + "beneficialOwners": beneficial_owners, + "tags": tags, + } self.relationships = relationships - @staticmethod def from_json_api(_id, _type, attributes, relationships): return BusinessApplicationDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("name"), - Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), - attributes["status"], attributes.get("stateOfIncorporation"), attributes.get("entityType"), - BusinessContact.from_json_api(attributes["contact"]), Officer.from_json_api(attributes["officer"]), - BeneficialOwner.from_json_api(attributes["beneficialOwners"]), attributes.get("ssn"), - attributes.get("message"), attributes.get("ip"), attributes.get("ein"), attributes.get("dba"), - attributes.get("tags"), relationships + _id, + date_utils.to_datetime(attributes["createdAt"]), + attributes.get("name"), + Address.from_json_api(attributes["address"]), + Phone.from_json_api(attributes["phone"]), + attributes["status"], + attributes.get("stateOfIncorporation"), + attributes.get("entityType"), + BusinessContact.from_json_api(attributes["contact"]), + Officer.from_json_api(attributes["officer"]), + BeneficialOwner.from_json_api(attributes["beneficialOwners"]), + attributes.get("ssn"), + attributes.get("message"), + attributes.get("ip"), + attributes.get("ein"), + attributes.get("dba"), + attributes.get("tags"), + relationships, ) + ApplicationDTO = Union[IndividualApplicationDTO, BusinessApplicationDTO] + class CreateIndividualApplicationRequest(UnitRequest): - def __init__(self, full_name: FullName, date_of_birth: date, address: Address, email: str, phone: Phone, - ip: str = None, ein: str = None, dba: str = None, sole_proprietorship: bool = None, - passport: str = None, nationality: str = None, ssn = None, - device_fingerprints: Optional[List[DeviceFingerprint]] = None, idempotency_key: str = None, - tags: Optional[Dict[str, str]] = None): + def __init__( + self, + full_name: FullName, + date_of_birth: date, + address: Address, + email: str, + phone: Phone, + ip: str = None, + ein: str = None, + dba: str = None, + sole_proprietorship: bool = None, + passport: str = None, + nationality: str = None, + ssn=None, + device_fingerprints: Optional[List[DeviceFingerprint]] = None, + idempotency_key: str = None, + tags: Optional[Dict[str, str]] = None, + ): self.full_name = full_name self.date_of_birth = date_of_birth self.address = address @@ -100,7 +289,7 @@ def to_json_api(self) -> Dict: "address": self.address, "email": self.email, "phone": self.phone, - } + }, } } @@ -114,7 +303,9 @@ def to_json_api(self) -> Dict: payload["data"]["attributes"]["dba"] = self.dba if self.sole_proprietorship: - payload["data"]["attributes"]["soleProprietorship"] = self.sole_proprietorship + payload["data"]["attributes"][ + "soleProprietorship" + ] = self.sole_proprietorship if self.ssn: payload["data"]["attributes"]["ssn"] = self.ssn @@ -129,7 +320,9 @@ def to_json_api(self) -> Dict: payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key if self.device_fingerprints: - payload["data"]["attributes"]["deviceFingerprints"] = [e.to_json_api() for e in self.device_fingerprints] + payload["data"]["attributes"]["deviceFingerprints"] = [ + e.to_json_api() for e in self.device_fingerprints + ] if self.tags: payload["data"]["attributes"]["tags"] = self.tags @@ -142,20 +335,29 @@ def __repr__(self): class CreateBusinessApplicationRequest(UnitRequest): def __init__( - self, - name: str, - address: Address, - phone: Phone, - state_of_incorporation: str, - ein: str, - contact: BusinessContact, - officer: Officer, - beneficial_owners: [BeneficialOwner], - entity_type: EntityType, - tags: Optional[Dict[str, str]] = None, - dba: str = None, - ip: str = None, - website: str = None, + self, + name: str, + address: Address, + phone: Phone, + state_of_incorporation: str, + ein: str, + contact: BusinessContact, + officer: Officer, + beneficial_owners: [BeneficialOwner], + entity_type: EntityType, + tags: Optional[Dict[str, str]] = None, + dba: str = None, + ip: str = None, + website: str = None, + industry: Optional[Industry] = None, + annual_revenue: Optional[AnnualRevenue] = None, + number_of_employees: Optional[NumberOfEmployees] = None, + cash_flow: Optional[CashFlow] = None, + year_of_incorporation: Optional[str] = None, + countries_of_operation: Optional[List[str]] = None, + stock_symbol: Optional[str] = None, + business_vertical: Optional[BusinessVertical] = None, + device_fingerprints: Optional[List[DeviceFingerprint]] = None ): self.name = name self.address = address @@ -170,6 +372,15 @@ def __init__( self.ip = ip self.website = website self.tags = tags + self.industry = industry + self.annual_revenue = annual_revenue + self.number_of_employees = number_of_employees + self.cash_flow = cash_flow + self.year_of_incorporation = year_of_incorporation + self.countries_of_operation = countries_of_operation + self.stock_symbol = stock_symbol + self.business_vertical = business_vertical + self.device_fingerprints = device_fingerprints def to_json_api(self) -> dict: payload = { @@ -185,6 +396,14 @@ def to_json_api(self) -> dict: "officer": self.officer, "beneficialOwners": self.beneficial_owners, "entityType": self.entity_type, + "industry": self.industry, + "annualRevenue": self.annual_revenue, + "numberOfEmployees": self.number_of_employees, + "cashFlow": self.cash_flow, + "yearOfIncorporation": self.year_of_incorporation, + "countriesOfOperation": self.countries_of_operation, + "stockSymbol": self.stock_symbol, + "businessVertical": self.business_vertical, }, } } @@ -208,30 +427,69 @@ def __repr__(self): class ApplicationDocumentDTO(object): - def __init__(self, id: str, status: ApplicationStatus, document_type: DocumentType, description: str, name: str, - address: Optional[Address], date_of_birth: Optional[date], passport: Optional[str], ein: Optional[str], - reason_code: Optional[ReasonCode], reason: Optional[str]): + def __init__( + self, + id: str, + status: ApplicationStatus, + document_type: DocumentType, + description: str, + name: str, + address: Optional[Address], + date_of_birth: Optional[date], + passport: Optional[str], + ein: Optional[str], + reason_code: Optional[ReasonCode], + reason: Optional[str], + ): self.id = id self.type = "document" - self.attributes = {"status": status, "documentType": document_type, "description": description, "name": name, - "address": address, "dateOfBirth": date_of_birth, "passport": passport, "ein": ein, - "reasonCode": reason_code, "reason": reason} + self.attributes = { + "status": status, + "documentType": document_type, + "description": description, + "name": name, + "address": address, + "dateOfBirth": date_of_birth, + "passport": passport, + "ein": ein, + "reasonCode": reason_code, + "reason": reason, + } @staticmethod def from_json_api(_id, _type, attributes): - address = Address.from_json_api(attributes.get("address")) if attributes.get("address") else None + address = ( + Address.from_json_api(attributes.get("address")) + if attributes.get("address") + else None + ) return ApplicationDocumentDTO( - _id, attributes["status"], attributes["documentType"], attributes["description"], attributes["name"], - address, attributes.get("dateOfBirth"), attributes.get("passport"), - attributes.get("ein"), attributes.get("reasonCode"), attributes.get("reason") + _id, + attributes["status"], + attributes["documentType"], + attributes["description"], + attributes["name"], + address, + attributes.get("dateOfBirth"), + attributes.get("passport"), + attributes.get("ein"), + attributes.get("reasonCode"), + attributes.get("reason"), ) + FileType = Literal["jpeg", "png", "pdf"] class UploadDocumentRequest(object): - def __init__(self, application_id: str, document_id: str, file: IO, file_type: FileType, - is_back_side: Optional[bool] = False): + def __init__( + self, + application_id: str, + document_id: str, + file: IO, + file_type: FileType, + is_back_side: Optional[bool] = False, + ): self.application_id = application_id self.document_id = document_id self.file = file @@ -240,9 +498,15 @@ def __init__(self, application_id: str, document_id: str, file: IO, file_type: F class ListApplicationParams(UnitParams): - def __init__(self, offset: int = 0, limit: int = 100, email: Optional[str] = None, - tags: Optional[object] = None, query: Optional[str] = None, - sort: Optional[Literal["createdAt", "-createdAt"]] = None): + def __init__( + self, + offset: int = 0, + limit: int = 100, + email: Optional[str] = None, + tags: Optional[object] = None, + query: Optional[str] = None, + sort: Optional[Literal["createdAt", "-createdAt"]] = None, + ): self.offset = offset self.limit = limit self.email = email @@ -264,19 +528,18 @@ def to_dict(self) -> Dict: class PatchApplicationRequest(UnitRequest): - def __init__(self, application_id: str, type: ApplicationTypes = "individualApplication", - tags: Optional[Dict[str, str]] = None): + def __init__( + self, + application_id: str, + type: ApplicationTypes = "individualApplication", + tags: Optional[Dict[str, str]] = None, + ): self.application_id = application_id self.type = type self.tags = tags def to_json_api(self) -> Dict: - payload = { - "data": { - "type": self.type, - "attributes": {} - } - } + payload = {"data": {"type": self.type, "attributes": {}}} if self.tags: payload["data"]["attributes"]["tags"] = self.tags @@ -293,12 +556,7 @@ def __init__(self, application_id: str): def to_json_api(self) -> Dict: payload = { - "data": { - "type": "applicationApprove", - "attributes": { - "reason": "sandbox" - } - } + "data": {"type": "applicationApprove", "attributes": {"reason": "sandbox"}} } return payload From 064660f1a2f5cedbca4c7baac3b545381b94ab52 Mon Sep 17 00:00:00 2001 From: Eric Ghildyal Date: Fri, 7 Jul 2023 15:02:29 -0400 Subject: [PATCH 075/137] Add relevant models and operations for charge cards and repayments --- build/lib/unit/__init__.py | 52 ++ build/lib/unit/api/__init__.py | 0 .../unit/api/account_end_of_day_resource.py | 19 + build/lib/unit/api/account_resource.py | 71 ++ build/lib/unit/api/ach_resource.py | 34 + build/lib/unit/api/api_token_resource.py | 34 + .../lib/unit/api/applicationForm_resource.py | 37 ++ build/lib/unit/api/application_resource.py | 90 +++ build/lib/unit/api/atmLocation_resource.py | 34 + .../api/authorization_request_resource.py | 46 ++ build/lib/unit/api/authorization_resource.py | 28 + build/lib/unit/api/base_resource.py | 44 ++ build/lib/unit/api/bill_pay_resource.py | 22 + build/lib/unit/api/card_resource.py | 113 ++++ build/lib/unit/api/counterparty_resource.py | 59 ++ build/lib/unit/api/customerToken_resource.py | 29 + build/lib/unit/api/customer_resource.py | 40 ++ build/lib/unit/api/event_resource.py | 33 + build/lib/unit/api/fee_resource.py | 19 + build/lib/unit/api/institution_resource.py | 17 + build/lib/unit/api/payment_resource.py | 57 ++ .../lib/unit/api/received_payment_resource.py | 45 ++ build/lib/unit/api/repayment_resource.py | 38 ++ build/lib/unit/api/returnAch_resource.py | 20 + build/lib/unit/api/reward_resource.py | 39 ++ build/lib/unit/api/statement_resource.py | 38 ++ build/lib/unit/api/transaction_resource.py | 41 ++ build/lib/unit/api/webhook_resource.py | 72 ++ build/lib/unit/models/__init__.py | 301 +++++++++ build/lib/unit/models/account.py | 344 ++++++++++ build/lib/unit/models/account_end_of_day.py | 41 ++ build/lib/unit/models/api_token.py | 48 ++ build/lib/unit/models/application.py | 564 ++++++++++++++++ build/lib/unit/models/applicationForm.py | 103 +++ build/lib/unit/models/atm_location.py | 28 + build/lib/unit/models/authorization.py | 61 ++ .../lib/unit/models/authorization_request.py | 98 +++ build/lib/unit/models/benificial_owner.py | 26 + build/lib/unit/models/bill_pay.py | 21 + build/lib/unit/models/card.py | 617 ++++++++++++++++++ build/lib/unit/models/codecs.py | 403 ++++++++++++ build/lib/unit/models/counterparty.py | 172 +++++ build/lib/unit/models/customer.py | 158 +++++ build/lib/unit/models/customerToken.py | 89 +++ build/lib/unit/models/event.py | 463 +++++++++++++ build/lib/unit/models/fee.py | 50 ++ build/lib/unit/models/institution.py | 17 + build/lib/unit/models/payment.py | 450 +++++++++++++ build/lib/unit/models/repayment.py | 114 ++++ build/lib/unit/models/returnAch.py | 29 + build/lib/unit/models/reward.py | 137 ++++ build/lib/unit/models/statement.py | 46 ++ build/lib/unit/models/transaction.py | 466 +++++++++++++ build/lib/unit/models/webhook.py | 92 +++ build/lib/unit/utils/__init__.py | 0 build/lib/unit/utils/date_utils.py | 13 + unit/api/account_resource.py | 4 +- unit/api/repayment_resource.py | 38 ++ unit/models/__init__.py | 23 + unit/models/account.py | 114 +++- unit/models/card.py | 256 ++++++-- unit/models/repayment.py | 114 ++++ unit_python_sdk.egg-info/PKG-INFO | 18 + unit_python_sdk.egg-info/SOURCES.txt | 65 ++ unit_python_sdk.egg-info/dependency_links.txt | 1 + unit_python_sdk.egg-info/requires.txt | 1 + unit_python_sdk.egg-info/top_level.txt | 1 + 67 files changed, 6680 insertions(+), 77 deletions(-) create mode 100644 build/lib/unit/__init__.py create mode 100644 build/lib/unit/api/__init__.py create mode 100644 build/lib/unit/api/account_end_of_day_resource.py create mode 100644 build/lib/unit/api/account_resource.py create mode 100644 build/lib/unit/api/ach_resource.py create mode 100644 build/lib/unit/api/api_token_resource.py create mode 100644 build/lib/unit/api/applicationForm_resource.py create mode 100644 build/lib/unit/api/application_resource.py create mode 100644 build/lib/unit/api/atmLocation_resource.py create mode 100644 build/lib/unit/api/authorization_request_resource.py create mode 100644 build/lib/unit/api/authorization_resource.py create mode 100644 build/lib/unit/api/base_resource.py create mode 100644 build/lib/unit/api/bill_pay_resource.py create mode 100644 build/lib/unit/api/card_resource.py create mode 100644 build/lib/unit/api/counterparty_resource.py create mode 100644 build/lib/unit/api/customerToken_resource.py create mode 100644 build/lib/unit/api/customer_resource.py create mode 100644 build/lib/unit/api/event_resource.py create mode 100644 build/lib/unit/api/fee_resource.py create mode 100644 build/lib/unit/api/institution_resource.py create mode 100644 build/lib/unit/api/payment_resource.py create mode 100644 build/lib/unit/api/received_payment_resource.py create mode 100644 build/lib/unit/api/repayment_resource.py create mode 100644 build/lib/unit/api/returnAch_resource.py create mode 100644 build/lib/unit/api/reward_resource.py create mode 100644 build/lib/unit/api/statement_resource.py create mode 100644 build/lib/unit/api/transaction_resource.py create mode 100644 build/lib/unit/api/webhook_resource.py create mode 100644 build/lib/unit/models/__init__.py create mode 100644 build/lib/unit/models/account.py create mode 100644 build/lib/unit/models/account_end_of_day.py create mode 100644 build/lib/unit/models/api_token.py create mode 100644 build/lib/unit/models/application.py create mode 100644 build/lib/unit/models/applicationForm.py create mode 100644 build/lib/unit/models/atm_location.py create mode 100644 build/lib/unit/models/authorization.py create mode 100644 build/lib/unit/models/authorization_request.py create mode 100644 build/lib/unit/models/benificial_owner.py create mode 100644 build/lib/unit/models/bill_pay.py create mode 100644 build/lib/unit/models/card.py create mode 100644 build/lib/unit/models/codecs.py create mode 100644 build/lib/unit/models/counterparty.py create mode 100644 build/lib/unit/models/customer.py create mode 100644 build/lib/unit/models/customerToken.py create mode 100644 build/lib/unit/models/event.py create mode 100644 build/lib/unit/models/fee.py create mode 100644 build/lib/unit/models/institution.py create mode 100644 build/lib/unit/models/payment.py create mode 100644 build/lib/unit/models/repayment.py create mode 100644 build/lib/unit/models/returnAch.py create mode 100644 build/lib/unit/models/reward.py create mode 100644 build/lib/unit/models/statement.py create mode 100644 build/lib/unit/models/transaction.py create mode 100644 build/lib/unit/models/webhook.py create mode 100644 build/lib/unit/utils/__init__.py create mode 100644 build/lib/unit/utils/date_utils.py create mode 100644 unit/api/repayment_resource.py create mode 100644 unit/models/repayment.py create mode 100644 unit_python_sdk.egg-info/PKG-INFO create mode 100644 unit_python_sdk.egg-info/SOURCES.txt create mode 100644 unit_python_sdk.egg-info/dependency_links.txt create mode 100644 unit_python_sdk.egg-info/requires.txt create mode 100644 unit_python_sdk.egg-info/top_level.txt diff --git a/build/lib/unit/__init__.py b/build/lib/unit/__init__.py new file mode 100644 index 00000000..99bda338 --- /dev/null +++ b/build/lib/unit/__init__.py @@ -0,0 +1,52 @@ +from unit.api.application_resource import ApplicationResource +from unit.api.customer_resource import CustomerResource +from unit.api.account_resource import AccountResource +from unit.api.card_resource import CardResource +from unit.api.transaction_resource import TransactionResource +from unit.api.payment_resource import PaymentResource +from unit.api.ach_resource import AchResource +from unit.api.statement_resource import StatementResource +from unit.api.customerToken_resource import CustomerTokenResource +from unit.api.counterparty_resource import CounterpartyResource +from unit.api.returnAch_resource import ReturnAchResource +from unit.api.applicationForm_resource import ApplicationFormResource +from unit.api.fee_resource import FeeResource +from unit.api.event_resource import EventResource +from unit.api.webhook_resource import WebhookResource +from unit.api.institution_resource import InstitutionResource +from unit.api.atmLocation_resource import AtmLocationResource +from unit.api.bill_pay_resource import BillPayResource +from unit.api.api_token_resource import APITokenResource +from unit.api.authorization_resource import AuthorizationResource +from unit.api.authorization_request_resource import AuthorizationRequestResource +from unit.api.account_end_of_day_resource import AccountEndOfDayResource +from unit.api.reward_resource import RewardResource + +__all__ = ["api", "models", "utils"] + + +class Unit(object): + def __init__(self, api_url, token): + self.applications = ApplicationResource(api_url, token) + self.customers = CustomerResource(api_url, token) + self.accounts = AccountResource(api_url, token) + self.cards = CardResource(api_url, token) + self.transactions = TransactionResource(api_url, token) + self.payments = PaymentResource(api_url, token) + self.ach = AchResource(api_url, token) + self.statements = StatementResource(api_url, token) + self.customerTokens = CustomerTokenResource(api_url, token) + self.counterparty = CounterpartyResource(api_url, token) + self.returnAch = ReturnAchResource(api_url, token) + self.applicationForms = ApplicationFormResource(api_url, token) + self.fees = FeeResource(api_url, token) + self.events = EventResource(api_url, token) + self.webhooks = WebhookResource(api_url, token) + self.institutions = InstitutionResource(api_url, token) + self.atmLocations = AtmLocationResource(api_url, token) + self.billPays = BillPayResource(api_url, token) + self.api_tokens = APITokenResource(api_url, token) + self.authorizations = AuthorizationResource(api_url, token) + self.authorization_requests = AuthorizationRequestResource(api_url, token) + self.account_end_of_day = AccountEndOfDayResource(api_url, token) + self.rewards = RewardResource(api_url, token) diff --git a/build/lib/unit/api/__init__.py b/build/lib/unit/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/build/lib/unit/api/account_end_of_day_resource.py b/build/lib/unit/api/account_end_of_day_resource.py new file mode 100644 index 00000000..71490714 --- /dev/null +++ b/build/lib/unit/api/account_end_of_day_resource.py @@ -0,0 +1,19 @@ +from unit.api.base_resource import BaseResource +from unit.models.account_end_of_day import * +from unit.models.codecs import DtoDecoder + + +class AccountEndOfDayResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "account-end-of-day" + + def list(self, params: ListAccountEndOfDayParams = None) -> Union[UnitResponse[List[AccountEndOfDayDTO]], UnitError]: + params = params or ListAccountEndOfDayParams() + response = super().get(self.resource, params.to_dict()) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[AccountEndOfDayDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + diff --git a/build/lib/unit/api/account_resource.py b/build/lib/unit/api/account_resource.py new file mode 100644 index 00000000..a034469d --- /dev/null +++ b/build/lib/unit/api/account_resource.py @@ -0,0 +1,71 @@ +from unit.api.base_resource import BaseResource +from unit.models.account import * +from unit.models.codecs import DtoDecoder + +class AccountResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "accounts" + + def create(self, request: CreateAccountRequest) -> Union[UnitResponse[AccountDTO], UnitError]: + payload = request.to_json_api() + response = super().post(self.resource, payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[AccountDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def close_account(self, request: CloseAccountRequest) -> Union[UnitResponse[AccountDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"{self.resource}/{request.account_id}/close", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[AccountDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def reopen_account(self, account_id: str, reason: str = "ByCustomer") -> Union[UnitResponse[AccountDTO], UnitError]: + response = super().post(f"{self.resource}/{account_id}/reopen", {'reason': reason}) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[AccountDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def get(self, account_id: str, include: Optional[str] = "") -> Union[UnitResponse[AccountDTO], UnitError]: + response = super().get(f"{self.resource}/{account_id}", {"include": include}) + if super().is_20x(response.status_code): + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[AccountDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListAccountParams = None) -> Union[UnitResponse[List[AccountDTO]], UnitError]: + params = params or ListAccountParams() + response = super().get(self.resource, params.to_dict()) + if super().is_20x(response.status_code): + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[AccountDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) + + def update(self, request: PatchAccountRequest) -> Union[UnitResponse[AccountDTO], UnitError]: + payload = request.to_json_api() + response = super().patch(f"{self.resource}/{request.account_id}", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[AccountDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def limits(self, account_id: str) -> Union[UnitResponse[AccountLimitsDTO], UnitError]: + response = super().get(f"{self.resource}/{account_id}/limits", None) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[AccountLimitsDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + diff --git a/build/lib/unit/api/ach_resource.py b/build/lib/unit/api/ach_resource.py new file mode 100644 index 00000000..f19835db --- /dev/null +++ b/build/lib/unit/api/ach_resource.py @@ -0,0 +1,34 @@ +from unit.api.base_resource import BaseResource +from unit.models.payment import * +from unit.models.codecs import DtoDecoder, split_json_api_single_response + + +class AchResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "ach" + + def simulate_transmit(self, request: SimulateTransmitAchRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"sandbox/{self.resource}/transmit", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + # TODO Fix dto + _id, _type, attributes, relationships = split_json_api_single_response(data) + print("simulate_transmit") + print("data", data) + print("_id, _type, attributes, relationships", _id, _type, attributes, relationships) + return UnitResponse[SimulateAchPaymentDTO](SimulateAchPaymentDTO.from_json_api(_id, _type, attributes, relationships), None) + else: + return UnitError.from_json_api(response.json()) + + def simulate_clear(self, request: SimulateClearAchRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"sandbox/{self.resource}/clear", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + # TODO Fix dto + _id, _type, attributes, relationships = split_json_api_single_response(data) + return UnitResponse[SimulateAchPaymentDTO](SimulateAchPaymentDTO.from_json_api(_id, _type, attributes, relationships), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/api_token_resource.py b/build/lib/unit/api/api_token_resource.py new file mode 100644 index 00000000..e44206d5 --- /dev/null +++ b/build/lib/unit/api/api_token_resource.py @@ -0,0 +1,34 @@ +from unit.api.base_resource import BaseResource +from unit.models.api_token import * +from unit.models.codecs import DtoDecoder + + +class APITokenResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "users" + + def create(self, request: CreateAPITokenRequest) -> Union[UnitResponse[APITokenDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"{self.resource}/{request.user_id}/api-tokens", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[APITokenDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def list(self, user_id: str) -> Union[UnitResponse[List[APITokenDTO]], UnitError]: + response = super().get(f"{self.resource}/{user_id}/api-tokens") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[APITokenDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def revoke(self, user_id: str, token_id: str) -> Union[UnitResponse, UnitError]: + response = super().delete(f"{self.resource}/{user_id}/api-tokens/{token_id}") + if super().is_20x(response.status_code): + return UnitResponse([], None) + else: + return UnitError.from_json_api(response.json()) + diff --git a/build/lib/unit/api/applicationForm_resource.py b/build/lib/unit/api/applicationForm_resource.py new file mode 100644 index 00000000..3ef83f3e --- /dev/null +++ b/build/lib/unit/api/applicationForm_resource.py @@ -0,0 +1,37 @@ +from unit.api.base_resource import BaseResource +from unit.models.applicationForm import * +from unit.models.codecs import DtoDecoder + + +class ApplicationFormResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "application-forms" + + def create(self, request: CreateApplicationFormRequest) -> Union[UnitResponse[ApplicationFormDTO], UnitError]: + payload = request.to_json_api() + response = super().post(self.resource, payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[ApplicationFormDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def get(self, application_form_id: str, include: Optional[str] = "") -> Union[UnitResponse[ApplicationFormDTO], UnitError]: + response = super().get(f"{self.resource}/{application_form_id}", {"include": include}) + if super().is_20x(response.status_code): + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[ApplicationFormDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListApplicationFormParams = None) -> Union[UnitResponse[List[ApplicationFormDTO]], UnitError]: + params = params or ListApplicationFormParams() + response = super().get(self.resource, params.to_dict()) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[ApplicationFormDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + diff --git a/build/lib/unit/api/application_resource.py b/build/lib/unit/api/application_resource.py new file mode 100644 index 00000000..82d041e7 --- /dev/null +++ b/build/lib/unit/api/application_resource.py @@ -0,0 +1,90 @@ +from unit.api.base_resource import BaseResource +from unit.models.application import * +from unit.models.codecs import DtoDecoder + + +class ApplicationResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "applications" + + def create(self, request: Union[CreateIndividualApplicationRequest, CreateBusinessApplicationRequest]) -> Union[UnitResponse[ApplicationDTO], UnitError]: + payload = request.to_json_api() + response = super().post(self.resource, payload) + + if response.ok: + data = response.json().get("data") + included = response.json().get("included") + if data["type"] == "individualApplication": + return UnitResponse[IndividualApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitResponse[BusinessApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListApplicationParams = None) -> Union[UnitResponse[List[ApplicationDTO]], UnitError]: + params = params or ListApplicationParams() + response = super().get(self.resource, params.to_dict()) + if response.status_code == 200: + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[ApplicationDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def get(self, application_id: str) -> Union[UnitResponse[ApplicationDTO], UnitError]: + response = super().get(f"{self.resource}/{application_id}") + if response.status_code == 200: + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[ApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) + + def upload(self, request: UploadDocumentRequest): + url = f"{self.resource}/{request.application_id}/documents/{request.document_id}" + if request.is_back_side: + url += "/back" + + headers = {} + + if request.file_type == "jpeg": + headers = {"Content-Type": "image/jpeg"} + if request.file_type == "png": + headers = {"Content-Type": "image/png"} + if request.file_type == "pdf": + headers = {"Content-Type": "application/pdf"} + + response = super().put(url, request.file, headers) + if response.status_code == 200: + data = response.json().get("data") + return UnitResponse[ApplicationDocumentDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def update(self, request: PatchApplicationRequest) -> Union[UnitResponse[ApplicationDTO], UnitError]: + payload = request.to_json_api() + response = super().patch(f"{self.resource}/{request.application_id}", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[ApplicationDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def approve_sb(self, request: ApproveApplicationSBRequest): + url = f"sandbox/{self.resource}/{request.application_id}/approve" + + payload = request.to_json_api() + response = super().post(url, payload) + + if response.ok: + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse(data, included) + # TODO need DTOs for this response + # if data["type"] == "individualApplication": + # return UnitResponse[IndividualApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + # else: + # return UnitResponse[BusinessApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/atmLocation_resource.py b/build/lib/unit/api/atmLocation_resource.py new file mode 100644 index 00000000..5e8b8bab --- /dev/null +++ b/build/lib/unit/api/atmLocation_resource.py @@ -0,0 +1,34 @@ +from unit.api.base_resource import BaseResource +from unit.models.atm_location import * +from unit.models.codecs import DtoDecoder, UnitEncoder + +class AtmLocationResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "atm-locations" + + """ + UnitEncoder must be imported here and not in the model class to no cause circular importing. + """ + def get(self, request: GetAtmLocationParams) -> Union[UnitResponse[List[AtmLocationDTO]], UnitError]: + params = {} + + if request.coordinates: + params["filter[coordinates]"] = json.dumps(request.coordinates, cls=UnitEncoder) + + if request.address: + params["filter[address]"] = json.dumps(request.address, cls=UnitEncoder) + + if request.postal_code: + params["filter[postalCode]"] = json.dumps(request.postal_code, cls=UnitEncoder) + + if request.search_radius: + params["filter[searchRadius]"] = request.search_radius + + response = super().get(self.resource, params) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[AtmLocationDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + diff --git a/build/lib/unit/api/authorization_request_resource.py b/build/lib/unit/api/authorization_request_resource.py new file mode 100644 index 00000000..56815ee8 --- /dev/null +++ b/build/lib/unit/api/authorization_request_resource.py @@ -0,0 +1,46 @@ +from unit.api.base_resource import BaseResource +from unit.models.authorization_request import * +from unit.models.codecs import DtoDecoder + + +class AuthorizationRequestResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "authorization-requests" + + def get(self, authorization_id: str) -> Union[UnitResponse[PurchaseAuthorizationRequestDTO], UnitError]: + response = super().get(f"{self.resource}/{authorization_id}") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[PurchaseAuthorizationRequestDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListPurchaseAuthorizationRequestParams = None) \ + -> Union[UnitResponse[List[PurchaseAuthorizationRequestDTO]], UnitError]: + params = params or ListPurchaseAuthorizationRequestParams() + response = super().get(self.resource, params.to_dict()) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[PurchaseAuthorizationRequestDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def approve(self, request: ApproveAuthorizationRequest) -> Union[UnitResponse[PurchaseAuthorizationRequestDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"{self.resource}/{request.authorization_id}/approve", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[PurchaseAuthorizationRequestDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def decline(self, request: DeclineAuthorizationRequest) -> Union[UnitResponse[PurchaseAuthorizationRequestDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"{self.resource}/{request.authorization_id}/decline", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[PurchaseAuthorizationRequestDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + diff --git a/build/lib/unit/api/authorization_resource.py b/build/lib/unit/api/authorization_resource.py new file mode 100644 index 00000000..28a3fec7 --- /dev/null +++ b/build/lib/unit/api/authorization_resource.py @@ -0,0 +1,28 @@ +from unit.api.base_resource import BaseResource +from unit.models.authorization import * +from unit.models.codecs import DtoDecoder + + +class AuthorizationResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "authorizations" + + def get(self, authorization_id: str, include_non_authorized: Optional[bool] = False) -> Union[UnitResponse[AuthorizationDTO], UnitError]: + params = {"filter[includeNonAuthorized]": include_non_authorized} + + response = super().get(f"{self.resource}/{authorization_id}", params) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[AuthorizationDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListAuthorizationParams = None) -> Union[UnitResponse[List[AuthorizationDTO]], UnitError]: + params = params or ListAuthorizationParams() + response = super().get(self.resource, params.to_dict()) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[AuthorizationDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/base_resource.py b/build/lib/unit/api/base_resource.py new file mode 100644 index 00000000..b8b06547 --- /dev/null +++ b/build/lib/unit/api/base_resource.py @@ -0,0 +1,44 @@ +import json +from typing import Optional, Dict +import requests +from unit.models.codecs import UnitEncoder + + +class BaseResource(object): + def __init__(self, api_url, token): + self.api_url = api_url + self.token = token + self.headers = { + "content-type": "application/vnd.api+json", + "authorization": f"Bearer {self.token}", + "user-agent": "unit-python-sdk" + } + + def get(self, resource: str, params: Dict = None, headers: Optional[Dict[str, str]] = None): + return requests.get(f"{self.api_url}/{resource}", params=params, headers=self.__merge_headers(headers)) + + def post(self, resource: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None): + data = json.dumps(data, cls=UnitEncoder) if data is not None else None + return requests.post(f"{self.api_url}/{resource}", data=data, headers=self.__merge_headers(headers)) + + def patch(self, resource: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None): + data = json.dumps(data, cls=UnitEncoder) if data is not None else None + return requests.patch(f"{self.api_url}/{resource}", data=data, headers=self.__merge_headers(headers)) + + def delete(self, resource: str, params: Dict = None, headers: Optional[Dict[str, str]] = None): + return requests.delete(f"{self.api_url}/{resource}", params=params, headers=self.__merge_headers(headers)) + + def put(self, resource: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None): + return requests.put(f"{self.api_url}/{resource}", data=data, headers=self.__merge_headers(headers)) + + def __merge_headers(self, headers: Optional[Dict[str, str]] = None): + if not headers: + return self.headers + else: + merged = self.headers.copy() + merged.update(**headers) + return merged + + def is_20x(self, status: int): + return status == 200 or status == 201 or status == 204 + diff --git a/build/lib/unit/api/bill_pay_resource.py b/build/lib/unit/api/bill_pay_resource.py new file mode 100644 index 00000000..ee4c7342 --- /dev/null +++ b/build/lib/unit/api/bill_pay_resource.py @@ -0,0 +1,22 @@ +from unit.api.base_resource import BaseResource +from unit.models.bill_pay import * +from unit.models.codecs import DtoDecoder + + +class BillPayResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "payments/billpay/billers" + + def get(self, params: GetBillersParams) -> Union[UnitResponse[List[BillerDTO]], UnitError]: + parameters = {"name": params.name} + if params.page: + parameters["page"] = params.page + + response = super().get(self.resource, parameters) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[BillerDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + diff --git a/build/lib/unit/api/card_resource.py b/build/lib/unit/api/card_resource.py new file mode 100644 index 00000000..e51dea54 --- /dev/null +++ b/build/lib/unit/api/card_resource.py @@ -0,0 +1,113 @@ +from unit.api.base_resource import BaseResource +from unit.models.card import * +from unit.models.codecs import DtoDecoder + + +class CardResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "cards" + + def create(self, request: CreateCardRequest) -> Union[UnitResponse[Card], UnitError]: + payload = request.to_json_api() + response = super().post(self.resource, payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[Card](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def report_stolen(self, card_id: str) -> Union[UnitResponse[Card], UnitError]: + response = super().post(f"{self.resource}/{card_id}/report-stolen") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[Card](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def report_lost(self, card_id: str) -> Union[UnitResponse[Card], UnitError]: + response = super().post(f"{self.resource}/{card_id}/report-lost") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[Card](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def close(self, card_id: str) -> Union[UnitResponse[Card], UnitError]: + response = super().post(f"{self.resource}/{card_id}/close") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[Card](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def freeze(self, card_id: str) -> Union[UnitResponse[Card], UnitError]: + response = super().post(f"{self.resource}/{card_id}/freeze") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[Card](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def unfreeze(self, card_id: str) -> Union[UnitResponse[Card], UnitError]: + response = super().post(f"{self.resource}/{card_id}/unfreeze") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[Card](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def replace(self, card_id: str, shipping_address: Optional[Address]) -> Union[UnitResponse[Union[IndividualDebitCardDTO, BusinessDebitCardDTO]], UnitError]: + request = ReplaceCardRequest(shipping_address) + payload = request.to_json_api() + response = super().post(f"{self.resource}/{card_id}/replace", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[Union[IndividualDebitCardDTO, BusinessDebitCardDTO]](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def update(self, request: PatchCardRequest) -> Union[UnitResponse[Card], UnitError]: + payload = request.to_json_api() + response = super().patch(f"{self.resource}/{request.card_id}", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[Card](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def get(self, card_id: str, include: Optional[str] = "") -> Union[UnitResponse[Card], UnitError]: + response = super().get(f"{self.resource}/{card_id}", {"include": include}) + if super().is_20x(response.status_code): + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[Card](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListCardParams = None) -> Union[UnitResponse[List[Card]], UnitError]: + params = params or ListCardParams() + response = super().get(self.resource, params.to_dict()) + if super().is_20x(response.status_code): + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[Card](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) + + def get_pin_status(self, card_id: str) -> Union[UnitResponse[PinStatusDTO], UnitError]: + response = super().get(f"{self.resource}/{card_id}/secure-data/pin/status") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[PinStatusDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + + def limits(self, card_id: str) -> Union[UnitResponse[CardLimitsDTO], UnitError]: + response = super().get(f"{self.resource}/{card_id}/limits") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[CardLimitsDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/counterparty_resource.py b/build/lib/unit/api/counterparty_resource.py new file mode 100644 index 00000000..3cf1518e --- /dev/null +++ b/build/lib/unit/api/counterparty_resource.py @@ -0,0 +1,59 @@ +from unit.api.base_resource import BaseResource +from unit.models.counterparty import * +from unit.models.codecs import DtoDecoder + + +class CounterpartyResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "counterparties" + + def create(self, request: Union[CreateCounterpartyRequest, CreateCounterpartyWithTokenRequest]) -> Union[UnitResponse[CounterpartyDTO], UnitError]: + payload = request.to_json_api() + response = super().post(self.resource, payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[CounterpartyDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def update(self, request: PatchCounterpartyRequest) -> Union[UnitResponse[CounterpartyDTO], UnitError]: + payload = request.to_json_api() + response = super().patch(f"{self.resource}/{request.counterparty_id}", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[CounterpartyDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def delete(self, counterparty_id: str) -> Union[UnitResponse, UnitError]: + response = super().delete(f"{self.resource}/{counterparty_id}") + if super().is_20x(response.status_code): + return UnitResponse([], None) + else: + return UnitError.from_json_api(response.json()) + + def get(self, counterparty_id: str) -> Union[UnitResponse[CounterpartyDTO], UnitError]: + response = super().get(f"{self.resource}/{counterparty_id}") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[CounterpartyDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListCounterpartyParams = None) -> Union[UnitResponse[List[CounterpartyDTO]], UnitError]: + params = params or ListCounterpartyParams() + response = super().get(self.resource, params.to_dict()) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[CounterpartyDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def get_balance(self, counterparty_id: str) -> Union[UnitResponse[CounterpartyBalanceDTO], UnitError]: + response = super().get(f"{self.resource}/{counterparty_id}/balance") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[CounterpartyBalanceDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/customerToken_resource.py b/build/lib/unit/api/customerToken_resource.py new file mode 100644 index 00000000..04e2dfd7 --- /dev/null +++ b/build/lib/unit/api/customerToken_resource.py @@ -0,0 +1,29 @@ +from unit.api.base_resource import BaseResource +from unit.models.customerToken import * +from unit.models.codecs import DtoDecoder + + +class CustomerTokenResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "customers" + + def create_token(self, request: CreateCustomerToken) -> Union[UnitResponse[CustomerTokenDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"{self.resource}/{request.customer_id}/token", payload) + + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[CustomerTokenDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def create_token_verification(self, request: CreateCustomerTokenVerification) -> Union[UnitResponse[CustomerVerificationTokenDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"{self.resource}/{request.customer_id}/token/verification", payload) + + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[CustomerVerificationTokenDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/customer_resource.py b/build/lib/unit/api/customer_resource.py new file mode 100644 index 00000000..d164452c --- /dev/null +++ b/build/lib/unit/api/customer_resource.py @@ -0,0 +1,40 @@ +from unit.api.base_resource import BaseResource +from unit.models.customer import * +from unit.models.codecs import DtoDecoder + + +class CustomerResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "customers" + + def update(self, request: Union[PatchIndividualCustomerRequest, PatchBusinessCustomerRequest]) -> Union[UnitResponse[CustomerDTO], UnitError]: + payload = request.to_json_api() + response = super().patch(f"{self.resource}/{request.customer_id}", payload) + + if response.ok: + data = response.json().get("data") + if data["type"] == "individualCustomer": + return UnitResponse[IndividualCustomerDTO](DtoDecoder.decode(data), None) + else: + return UnitResponse[BusinessCustomerDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + + def get(self, customer_id: str) -> Union[UnitResponse[CustomerDTO], UnitError]: + response = super().get(f"{self.resource}/{customer_id}") + if response.status_code == 200: + data = response.json().get("data") + return UnitResponse[CustomerDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListCustomerParams = None) -> Union[UnitResponse[List[CustomerDTO]], UnitError]: + params = params or ListCustomerParams() + response = super().get(self.resource, params.to_dict()) + if response.status_code == 200: + data = response.json().get("data") + return UnitResponse[CustomerDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/event_resource.py b/build/lib/unit/api/event_resource.py new file mode 100644 index 00000000..aaec2f97 --- /dev/null +++ b/build/lib/unit/api/event_resource.py @@ -0,0 +1,33 @@ +from unit.api.base_resource import BaseResource +from unit.models.event import * +from unit.models.codecs import DtoDecoder + + +class EventResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "events" + + def get(self, event_id: str) -> Union[UnitResponse[EventDTO], UnitError]: + response = super().get(f"{self.resource}/{event_id}") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[EventDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListEventParams = None) -> Union[UnitResponse[List[EventDTO]], UnitError]: + params = params or ListEventParams() + response = super().get(self.resource, params.to_dict()) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[EventDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def fire(self, event_id: str) -> Union[UnitResponse, UnitError]: + response = super().post(f"{self.resource}/{event_id}") + if super().is_20x(response.status_code): + return UnitResponse([], None) + else: + return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/fee_resource.py b/build/lib/unit/api/fee_resource.py new file mode 100644 index 00000000..68bb2756 --- /dev/null +++ b/build/lib/unit/api/fee_resource.py @@ -0,0 +1,19 @@ +from unit.api.base_resource import BaseResource +from unit.models.fee import * +from unit.models.codecs import DtoDecoder + + +class FeeResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "fees" + + def create(self, request: CreateFeeRequest) -> Union[UnitResponse[FeeDTO], UnitError]: + payload = request.to_json_api() + response = super().post(self.resource, payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[FeeDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + diff --git a/build/lib/unit/api/institution_resource.py b/build/lib/unit/api/institution_resource.py new file mode 100644 index 00000000..d91ce96d --- /dev/null +++ b/build/lib/unit/api/institution_resource.py @@ -0,0 +1,17 @@ +from unit.api.base_resource import BaseResource +from unit.models.institution import * +from unit.models.codecs import DtoDecoder + + +class InstitutionResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "institutions" + + def get(self, routing_number: str) -> Union[UnitResponse[InstitutionDTO], UnitError]: + response = super().get(f"{self.resource}/{routing_number}", None) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[InstitutionDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/payment_resource.py b/build/lib/unit/api/payment_resource.py new file mode 100644 index 00000000..33c13fda --- /dev/null +++ b/build/lib/unit/api/payment_resource.py @@ -0,0 +1,57 @@ +from unit.api.base_resource import BaseResource +from unit.models.payment import * +from unit.models.codecs import DtoDecoder, split_json_api_single_response + + +class PaymentResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "payments" + + def create(self, request: CreatePaymentRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().post(self.resource, payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[PaymentDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def update(self, request: PatchPaymentRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().patch(f"{self.resource}/{request.payment_id}", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[PaymentDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def get(self, payment_id: str, include: Optional[str] = "") -> Union[UnitResponse[PaymentDTO], UnitError]: + response = super().get(f"{self.resource}/{payment_id}", {"include": include}) + if response.status_code == 200: + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[PaymentDTO](DtoDecoder.decode(data), DtoDecoder.decode(data)) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListPaymentParams = None) -> Union[UnitResponse[List[PaymentDTO]], UnitError]: + params = params or ListPaymentParams() + response = super().get(self.resource, params.to_dict()) + if response.status_code == 200: + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[PaymentDTO](DtoDecoder.decode(data), DtoDecoder.decode(data)) + else: + return UnitError.from_json_api(response.json()) + + def simulate_incoming_ach(self, request: SimulateIncomingAchRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"sandbox/{self.resource}", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + # TODO Fix dto + _id, _type, attributes, relationships = split_json_api_single_response(data) + return UnitResponse[SimulateIncomingAchPaymentDTO](SimulateIncomingAchPaymentDTO.from_json_api(_id, _type, attributes, relationships), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/received_payment_resource.py b/build/lib/unit/api/received_payment_resource.py new file mode 100644 index 00000000..b453d6ac --- /dev/null +++ b/build/lib/unit/api/received_payment_resource.py @@ -0,0 +1,45 @@ +from unit.api.base_resource import BaseResource +from unit.models.payment import * +from unit.models.codecs import DtoDecoder + + +class ReceivedPaymentResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "received-payments" + + def update(self, request: PatchPaymentRequest) -> Union[UnitResponse[AchReceivedPaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().patch(f"{self.resource}/{request.payment_id}", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[AchReceivedPaymentDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def get(self, payment_id: str, include: Optional[str] = "") -> Union[UnitResponse[AchReceivedPaymentDTO], UnitError]: + response = super().get(f"{self.resource}/{payment_id}", {"include": include}) + if response.status_code == 200: + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[AchReceivedPaymentDTO](DtoDecoder.decode(data), DtoDecoder.decode(data)) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListReceivedPaymentParams = None) -> Union[UnitResponse[List[AchReceivedPaymentDTO]], UnitError]: + params = params or ListReceivedPaymentParams() + response = super().get(self.resource, params.to_dict()) + if response.status_code == 200: + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[AchReceivedPaymentDTO](DtoDecoder.decode(data), DtoDecoder.decode(data)) + else: + return UnitError.from_json_api(response.json()) + + def advance(self, payment_id: str) -> Union[UnitResponse[AchReceivedPaymentDTO], UnitError]: + response = super().post(f"{self.resource}/{payment_id}/advance") + if response.status_code == 200: + data = response.json().get("data") + return UnitResponse[AchReceivedPaymentDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) \ No newline at end of file diff --git a/build/lib/unit/api/repayment_resource.py b/build/lib/unit/api/repayment_resource.py new file mode 100644 index 00000000..d55a69d0 --- /dev/null +++ b/build/lib/unit/api/repayment_resource.py @@ -0,0 +1,38 @@ +from typing import Union, List, Optional + +from unit.api.base_resource import BaseResource +from unit.models import UnitResponse, UnitError +from unit.models.codecs import DtoDecoder +from unit.models.repayment import RepaymentDTO, CreateRepaymentRequest, ListRepaymentParams + + +class RepaymentResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "repayments" + + def create(self, request: CreateRepaymentRequest) -> Union[UnitResponse[RepaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().post_create(self.resource, payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[RepaymentDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def get(self, repayment_id: str) -> Union[UnitResponse[RepaymentDTO], UnitError]: + response = super().get(f"{self.resource}/{repayment_id}") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[RepaymentDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: Optional[ListRepaymentParams] = None) -> Union[UnitResponse[List[RepaymentDTO]], UnitError]: + params = params or ListRepaymentParams() + response = super().get(self.resource, params.to_dict()) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[List[RepaymentDTO]](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/returnAch_resource.py b/build/lib/unit/api/returnAch_resource.py new file mode 100644 index 00000000..948479de --- /dev/null +++ b/build/lib/unit/api/returnAch_resource.py @@ -0,0 +1,20 @@ +from unit.api.base_resource import BaseResource +from unit.models.transaction import ReturnedReceivedAchTransactionDTO +from unit.models.returnAch import * +from unit.models.codecs import DtoDecoder + + +class ReturnAchResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "returns" + + def return_ach(self, request: ReturnReceivedAchTransactionRequest) -> Union[UnitResponse[ReturnedReceivedAchTransactionDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"{self.resource}/{request.transaction_id}", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[ReturnedReceivedAchTransactionDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + diff --git a/build/lib/unit/api/reward_resource.py b/build/lib/unit/api/reward_resource.py new file mode 100644 index 00000000..8cafa463 --- /dev/null +++ b/build/lib/unit/api/reward_resource.py @@ -0,0 +1,39 @@ +from typing import Union, List + +from unit.api.base_resource import BaseResource +from unit.models import UnitResponse, UnitError +from unit.models.reward import RewardDTO, ListRewardsParams, CreateRewardRequest + +from unit.models.codecs import DtoDecoder + + +class RewardResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "rewards" + + def create(self, request: CreateRewardRequest) -> Union[UnitResponse[RewardDTO], UnitError]: + payload = request.to_json_api() + response = super().post(self.resource, payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[RewardDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def get(self, reward_id: str) -> Union[UnitResponse[RewardDTO], UnitError]: + response = super().get(f"{self.resource}/{reward_id}") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[RewardDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListRewardsParams = None) -> Union[UnitResponse[List[RewardDTO]], UnitError]: + params = params or ListRewardsParams() + response = super().get(self.resource, params.to_dict()) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[List[RewardDTO]](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/statement_resource.py b/build/lib/unit/api/statement_resource.py new file mode 100644 index 00000000..be711e1f --- /dev/null +++ b/build/lib/unit/api/statement_resource.py @@ -0,0 +1,38 @@ +from unit.api.base_resource import BaseResource +from unit.models.statement import * +from unit.models.codecs import DtoDecoder + + +class StatementResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "statements" + + def get(self, params: GetStatementParams) -> Union[UnitResponse[str], UnitError]: + parameters = {"language": params.language} + if params.customer_id: + parameters["filter[customerId]"] = params.customer_id + + response = super().get(f"{self.resource}/{params.statement_id}/{params.output_type}", parameters) + if response.status_code == 200: + return UnitResponse[bytes](response.content, None) + else: + return UnitError.from_json_api(response.json()) + + def get_bank_verification(self, account_id: str, include_proof_of_funds: Optional[bool] = False) -> Union[UnitResponse[str], UnitError]: + response = super().get(f"{self.resource}/{account_id}/bank/pdf", + {"includeProofOfFunds": include_proof_of_funds}) + if response.status_code == 200: + return UnitResponse[str](response.text, None) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListStatementParams = None) -> Union[UnitResponse[List[StatementDTO]], UnitError]: + params = params or ListStatementParams() + response = super().get(self.resource, params.to_dict()) + if response.status_code == 200: + data = response.json().get("data") + return UnitResponse[StatementDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + diff --git a/build/lib/unit/api/transaction_resource.py b/build/lib/unit/api/transaction_resource.py new file mode 100644 index 00000000..abcb8e38 --- /dev/null +++ b/build/lib/unit/api/transaction_resource.py @@ -0,0 +1,41 @@ +from unit.api.base_resource import BaseResource +from unit.models.transaction import * +from unit.models.codecs import DtoDecoder +from unit.models.transaction import * + + +class TransactionResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "transactions" + + def get(self, transaction_id: str, account_id: str, include: Optional[str] = "") -> Union[UnitResponse[TransactionDTO], UnitError]: + params = {"filter[accountId]": account_id, "include": include} + response = super().get(f"{self.resource}/{transaction_id}", params) + if response.status_code == 200: + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[TransactionDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListTransactionParams = None) -> Union[UnitResponse[List[TransactionDTO]], UnitError]: + params = params or ListTransactionParams() + response = super().get(self.resource, params.to_dict()) + if response.status_code == 200: + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[TransactionDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) + + def update(self, request: PatchTransactionRequest) -> Union[UnitResponse[TransactionDTO], UnitError]: + payload = request.to_json_api() + response = super().patch(f"accounts/{request.account_id}/{self.resource}/{request.transaction_id}", payload) + if response.status_code == 200: + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[TransactionDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) + diff --git a/build/lib/unit/api/webhook_resource.py b/build/lib/unit/api/webhook_resource.py new file mode 100644 index 00000000..78349feb --- /dev/null +++ b/build/lib/unit/api/webhook_resource.py @@ -0,0 +1,72 @@ +from unit.api.base_resource import BaseResource +from unit.models.webhook import * +from unit.models.codecs import DtoDecoder +import hmac +from hashlib import sha1 +import base64 + + +class WebhookResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "webhooks" + + def create(self, request: CreateWebhookRequest) -> Union[UnitResponse[WebhookDTO], UnitError]: + payload = request.to_json_api() + response = super().post(self.resource, payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[WebhookDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def get(self, webhook_id: str) -> Union[UnitResponse[WebhookDTO], UnitError]: + response = super().get(f"{self.resource}/{webhook_id}") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[WebhookDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListWebhookParams = None) -> Union[UnitResponse[List[WebhookDTO]], UnitError]: + params = params or ListWebhookParams() + response = super().get(self.resource, params.to_dict()) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[WebhookDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def update(self, request: PatchWebhookRequest) -> Union[UnitResponse[WebhookDTO], UnitError]: + payload = request.to_json_api() + response = super().patch(f"{self.resource}/{request.webhook_id}", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[WebhookDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def enable(self, webhook_id: str) -> Union[UnitResponse[WebhookDTO], UnitError]: + response = super().post(f"{self.resource}/{webhook_id}/enable") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[WebhookDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def disable(self, webhook_id: str) -> Union[UnitResponse[WebhookDTO], UnitError]: + response = super().post(f"{self.resource}/{webhook_id}/disable") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[WebhookDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def verify(self, signature: str, secret: str, payload): + mac = hmac.new( + secret.encode(), + msg=json.dumps(payload, separators=(',', ':'), ensure_ascii=False).encode('utf-8'), + digestmod=sha1, + ) + res = base64.encodebytes(mac.digest()).decode().rstrip('\n') + return res == signature diff --git a/build/lib/unit/models/__init__.py b/build/lib/unit/models/__init__.py new file mode 100644 index 00000000..a25d06dc --- /dev/null +++ b/build/lib/unit/models/__init__.py @@ -0,0 +1,301 @@ +import json +from typing import TypeVar, Generic, Union, Optional, Literal, List, Dict +from datetime import datetime, date + +def to_camel_case(snake_str): + components = snake_str.lstrip('_').split('_') + # We capitalize the first letter of each component except the first one + # with the 'title' method and join them together. + return components[0] + ''.join(x.title() for x in components[1:]) + + +def extract_attributes(list_of_attributes, attributes): + extracted_attributes = {} + for a in list_of_attributes: + if a in attributes: + extracted_attributes[a] = attributes[a] + + return extracted_attributes + + +class UnitDTO(object): + def to_dict(self): + if type(self) is dict: + return self + else: + v = vars(self) + return dict((to_camel_case(k), val) for k, val in v.items() if val is not None) + +class Relationship(object): + def __init__(self, _type: str, _id: str): + self.type = _type + self.id = _id + + def to_dict(self): + return {"type": self.type, "id": self.id} + + +T = TypeVar('T') + +class RelationshipArray(Generic[T]): + def __init__(self, l: List[T]): + self.relationships = l + + +class UnitResponse(Generic[T]): + def __init__(self, data: Union[T, List[T]], included): + self.data = data + self.included = included + + @staticmethod + def from_json_api(data: str): + pass + + +class UnitRequest(object): + def to_json_api(self) -> Dict: + pass + +class UnitParams(object): + def to_dict(self) -> Dict: + pass + +class RawUnitObject(object): + def __init__(self, _id, _type, attributes, relationships): + self.id = _id + self.type = _type + self.attributes = attributes + self.relationships = relationships + +class UnitErrorPayload(object): + def __init__(self, title: str, status: str, detail: Optional[str] = None, details: Optional[str] = None, + source: Optional[Dict] = None): + self.title = title + self.status = status + self.detail = detail + self.details = details + self.source = source + + def __str__(self): + return self.detail + + +class UnitError(object): + def __init__(self, errors: List[UnitErrorPayload]): + self.errors = errors + + @staticmethod + def from_json_api(data: Dict): + errors = [] + for err in data["errors"]: + errors.append( + UnitErrorPayload(err.get("title"), err.get("status"), err.get("detail", None), + err.get("details", None), err.get("source", None)) + ) + + return UnitError(errors) + + def __str__(self): + return json.dumps({"errors": [{"title": err.title, "status": err.status, "detail": err.detail, + "details": err.details, "source": err.source} for err in self.errors]}) + + +Status = Literal["Approved", "Denied", "PendingReview"] +Title = Literal["CEO", "COO", "CFO", "President"] +EntityType = Literal["Corporation", "LLC", "Partnership"] + +class FullName(object): + def __init__(self, first: str, last: str): + self.first = first + self.last = last + + def __str__(self): + return f"{self.first} {self.last}" + + @staticmethod + def from_json_api(data: Dict): + return FullName(data.get("first"), data.get("last")) + + +# todo: Alex - use typing.Literal for multi accepted values (e.g country) +class Address(object): + def __init__(self, street: str, city: str, state: str, postal_code: str, country: str, + street2: Optional[str] = None): + self.street = street + self.street2 = street2 + self.city = city + self.state = state + self.postal_code = postal_code + self.country = country + + @staticmethod + def from_json_api(data: Dict): + return Address(data.get("street"), data.get("city"), data.get("state"), + data.get("postalCode"), data.get("country"), data.get("street2", None)) + + +class Phone(object): + def __init__(self, country_code: str, number: str): + self.country_code = country_code + self.number = number + + @staticmethod + def from_json_api(data: Dict): + return Phone(data.get("countryCode"), data.get("number")) + + +class BusinessContact(object): + def __init__(self, full_name: FullName, email: str, phone: Phone): + self.full_name = full_name + self.email = email + self.phone = phone + + @staticmethod + def from_json_api(data: Dict): + return BusinessContact(FullName.from_json_api(data.get("fullName")), data.get("email"), Phone.from_json_api(data.get("phone"))) + + +class Officer(object): + def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, + status: Optional[Status] = None, title: Optional[Title] = None, ssn: Optional[str] = None, + passport: Optional[str] = None, nationality: Optional[str] = None): + self.full_name = full_name + self.date_of_birth = date_of_birth + self.address = address + self.phone = phone + self.email = email + self.status = status + self.title = title + self.ssn = ssn + self.passport = passport + self.nationality = nationality + + @staticmethod + def from_json_api(data: Dict): + return Officer(data.get("fullName"), data.get("dateOfBirth"), data.get("address"), data.get("phone"), + data.get("email"), data.get("status"), data.get("title"), data.get("ssn"), data.get("passport"), + data.get("nationality")) + + +class BeneficialOwner(object): + def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, + status: Optional[Status] = None, ssn: Optional[str] = None, passport: Optional[str] = None, + nationality: Optional[str] = None, percentage: Optional[int] = None): + self.full_name = full_name + self.date_of_birth = date_of_birth + self.address = address + self.phone = phone + self.email = email + self.status = status + self.ssn = ssn + self.passport = passport + self.nationality = nationality + self.percentage = percentage + + @staticmethod + def from_json_api(l: List): + beneficial_owners = [] + for data in l: + beneficial_owners.append(BeneficialOwner(data.get("fullName"), data.get("dateOfBirth"), data.get("address"), + data.get("phone"), data.get("email"), data.get("status"), data.get("ssn"), + data.get("passport"), data.get("nationality"), data.get("percentage"))) + return beneficial_owners + + +class AuthorizedUser(object): + def __init__(self, full_name: FullName, email: str, phone: Phone): + self.full_name = full_name + self.email = email + self.phone = phone + + @staticmethod + def from_json_api(l: List) -> List: + authorized_users = [] + for data in l: + authorized_users.append(AuthorizedUser(data.get("fullName"), data.get("email"), data.get("phone"))) + return authorized_users + +class WireCounterparty(object): + def __init__(self, routing_number: str, account_number: str, name: str, address: Address): + self.routing_number = routing_number + self.account_number = account_number + self.name = name + self.address = address + + @staticmethod + def from_json_api(data: Dict): + return WireCounterparty(data["routingNumber"], data["accountNumber"], data["name"], + Address.from_json_api(data["address"])) + +class Counterparty(object): + def __init__(self, routing_number: str, account_number: str, account_type: str, name: str): + self.routing_number = routing_number + self.account_number = account_number + self.account_type = account_type + self.name = name + + @staticmethod + def from_json_api(data: Dict): + return Counterparty(data["routingNumber"], data["accountNumber"], data["accountType"], data["name"]) + +class Coordinates(object): + def __init__(self, longitude: int, latitude: int): + self.longitude = longitude + self.latitude = latitude + + @staticmethod + def from_json_api(data: Dict): + if data: + return Coordinates(data["longitude"], data["latitude"]) + else: + return None + + +class Merchant(object): + def __init__(self, name: str, type: int, category: Optional[str], location: Optional[str]): + self.name = name + self.type = type + self.category = category + self.location = location + + @staticmethod + def from_json_api(data: Dict): + return Merchant(data["name"], data["type"], data.get("category"), data.get("location")) + +class CardLevelLimits(object): + def __init__(self, daily_withdrawal: int, daily_purchase: int, monthly_withdrawal: int, monthly_purchase: int): + self.daily_withdrawal = daily_withdrawal + self.daily_purchase = daily_purchase + self.monthly_withdrawal = monthly_withdrawal + self.monthly_purchase = monthly_purchase + + @staticmethod + def from_json_api(data: Dict): + return CardLevelLimits(data["dailyWithdrawal"], data["dailyPurchase"], data["monthlyWithdrawal"], + data["monthlyPurchase"]) + +class CardTotals(object): + def __init__(self, withdrawals: int, deposits: int, purchases: int): + self.withdrawals = withdrawals + self.deposits = deposits + self.purchases = purchases + + @staticmethod + def from_json_api(data: Dict): + return CardTotals(data["withdrawals"], data["deposits"], data["purchases"]) + + +class DeviceFingerprint(object): + def __init__(self, value: str, provider: str = "iovation"): + self.value = value + self.provider = provider + + def to_json_api(self): + return { + "value": self.value, + "provider": self.provider, + } + + @classmethod + def from_json_api(cls, data: Dict): + return cls(value=data["value"], provider=data["provider"]) diff --git a/build/lib/unit/models/account.py b/build/lib/unit/models/account.py new file mode 100644 index 00000000..70dcc444 --- /dev/null +++ b/build/lib/unit/models/account.py @@ -0,0 +1,344 @@ +from unit.utils import date_utils + +from unit.models import * + +AccountStatus = Literal["Open", "Frozen", "Closed"] +CloseReason = Literal["ByCustomer", "Fraud"] +FraudReason = Literal["ACHActivity", "CardActivity", "CheckActivity", "ApplicationHistory", "AccountActivity", + "ClientIdentified", "IdentityTheft", "LinkedToFraudulentCustomer"] + +CreditAccountType = "creditAccount" +DepositAccountType = "depositAccount" +AccountTypes = Literal[CreditAccountType, DepositAccountType] + +class DepositAccountDTO(object): + def __init__(self, id: str, created_at: datetime, name: str, deposit_product: str, routing_number: str, + account_number: str, currency: str, balance: int, hold: int, available: int, status: AccountStatus, + tags: Optional[Dict[str, str]], close_reason: Optional[CloseReason], + relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = "depositAccount" + self.attributes = {"name": name, "createdAt": created_at, "depositProduct": deposit_product, + "routingNumber": routing_number, "accountNumber": account_number, "currency": currency, + "balance": balance, "hold": hold, "available": available, "status": status, + "closeReason": close_reason, "tags": tags} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return DepositAccountDTO( + _id, date_utils.to_datetime(attributes["createdAt"]), attributes["name"], attributes["depositProduct"], + attributes["routingNumber"], attributes["accountNumber"], attributes["currency"], attributes["balance"], + attributes["hold"], attributes["available"], attributes["status"], attributes.get("tags"), + attributes.get("closeReason"), relationships + ) + + +class CreditAccountDTO(object): + def __init__(self, _id: str, created_at: datetime, updated_at: Optional[datetime], name: str, credit_terms: str, + currency: str, credit_limit: int, balance: int, hold: int, available: int, + tags: Optional[Dict[str, str]], status: AccountStatus, freeze_reason: Optional[str], + close_reason: Optional[str], close_reason_text: Optional[str], fraud_reason: Optional[FraudReason], + relationships: Optional[Dict[str, Relationship]]): + self.id = _id + self.type = CreditAccountType + self.attributes = {"createdAt": created_at, "updatedAt": updated_at, "name": name, "status": status, + "creditTerms": credit_terms, "currency": currency, "creditLimit": credit_limit, + "balance": balance, "hold": hold, "available": available, "tags": tags, + "freezeReason": freeze_reason, "closeReason": close_reason, + "closeReasonText": close_reason_text, "fraudReason": fraud_reason} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CreditAccountDTO(_id, date_utils.to_datetime(attributes["createdAt"]), + date_utils.to_datetime(attributes.get("updatedAt")), attributes["name"], + attributes["creditTerms"], attributes["currency"], attributes["creditLimit"], + attributes["balance"], attributes["hold"], attributes["available"], + attributes.get("tags"), attributes["status"], attributes.get("freezeReason"), + attributes.get("closeReason"), attributes.get("closeReasonText"), + attributes.get("fraudReason"), relationships) + + +AccountDTO = Union[DepositAccountDTO, CreditAccountDTO] + + +class CreateDepositAccountRequest(UnitRequest): + def __init__(self, deposit_product: str, relationships: Optional[Dict[str, Union[Relationship, RelationshipArray]]], + tags: Optional[Dict[str, str]] = None, idempotency_key: Optional[str] = None): + self.deposit_product = deposit_product + self.tags = tags + self.idempotency_key = idempotency_key + self.relationships = relationships + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "depositAccount", + "attributes": { + "depositProduct": self.deposit_product, + }, + "relationships": self.relationships + } + } + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + +class CreateCreditAccountRequest(UnitRequest): + def __init__(self, credit_terms: str, credit_limit: int, relationships: Dict[str, Relationship], + tags: Optional[Dict[str, str]] = None, idempotency_key: Optional[str] = None): + self.credit_terms = credit_terms + self.credit_limit = credit_limit + self.tags = tags + self.idempotency_key = idempotency_key + self.relationships = relationships + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "creditAccount", + "attributes": { + "creditTerms": self.credit_terms, + "creditLimit": self.credit_limit + }, + "relationships": self.relationships + } + } + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + return payload + + def __repr__(self): + return json.dumps(self.to_json_api()) + +CreateAccountRequest = Union[CreateDepositAccountRequest, CreateCreditAccountRequest] + +class PatchDepositAccountRequest(UnitRequest): + def __init__(self, account_id: str, deposit_product: Optional[str] = None, tags: Optional[Dict[str, str]] = None): + self.account_id = account_id + self.deposit_product = deposit_product + self.tags = tags + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "depositAccount", + "attributes": {} + } + } + + if self.deposit_product: + payload["data"]["attributes"]["depositProduct"] = self.deposit_product + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + +class PatchCreditAccountRequest(UnitRequest): + def __init__(self, account_id: str, tags: Optional[Dict[str, str]] = None, credit_limit: Optional[int] = None): + self.account_id = account_id + self.tags = tags + self.credit_limit = credit_limit + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": CreditAccountType, + "attributes": {} + } + } + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.credit_limit: + payload["data"]["attributes"]["creditLimit"] = self.credit_limit + + return payload + + def __repr__(self): + return json.dumps(self.to_json_api()) + +PatchAccountRequest = Union[PatchDepositAccountRequest, PatchCreditAccountRequest] + + +class AchTotals(object): + def __init__(self, debits: int, credits: int): + self.debits = debits + self.credits = credits + + @staticmethod + def from_json_api(data: Dict): + return AchTotals(data["debits"], data["credits"]) + + +class AchLimits(object): + def __init__(self, daily_debit: int, daily_credit: int, monthly_debit: int, monthly_credit: int, + daily_debit_soft: int, monthly_debit_soft: int): + self.daily_debit = daily_debit + self.daily_credit = daily_credit + self.monthly_debit = monthly_debit + self.monthly_credit = monthly_credit + self.daily_debit_soft = daily_debit_soft + self.monthly_debit_soft = monthly_debit_soft + + @staticmethod + def from_json_api(data: Dict): + return AchLimits(data["dailyDebit"], data["dailyCredit"], data["monthlyDebit"], data["monthlyCredit"], + data["dailyDebitSoft"], data["monthlyDebitSoft"]) + + +class AccountAchLimits(object): + def __init__(self, limits: AchLimits, totals_daily: AchTotals, totals_monthly: AchTotals): + self.limits = limits + self.totals_daily = totals_daily + self.totals_monthly = totals_monthly + + @staticmethod + def from_json_api(data: Dict): + return AccountAchLimits(AchLimits.from_json_api(data["limits"]), AchTotals.from_json_api(data["totalsDaily"]), + AchTotals.from_json_api(data["totalsMonthly"])) + + +class CardLimits(object): + def __init__(self, daily_withdrawal: int, daily_deposit: int, daily_purchase: int, daily_card_transaction: int): + self.daily_withdrawal = daily_withdrawal + self.daily_deposit = daily_deposit + self.daily_purchase = daily_purchase + self.daily_card_transaction = daily_card_transaction + + @staticmethod + def from_json_api(data: Dict): + return CardLimits(data["dailyWithdrawal"], data["dailyDeposit"], + data["dailyPurchase"], data["dailyCardTransaction"]) + +class CardTotals(object): + def __init__(self, withdrawals: int, deposits: int, purchases: int, card_transactions: int): + self.withdrawals = withdrawals + self.deposits = deposits + self.purchases = purchases + self.card_transactions = card_transactions + + @staticmethod + def from_json_api(data: Dict): + return CardTotals(data["withdrawals"], data["deposits"], data["purchases"], data["cardTransactions"]) + +class AccountCardLimits(object): + def __init__(self, limits: CardLimits, totals_daily: CardTotals): + self.limits = limits + self.totals_daily = totals_daily + + @staticmethod + def from_json_api(data: Dict): + return AccountCardLimits(CardLimits.from_json_api(data["limits"]), + CardTotals.from_json_api(data["totalsDaily"])) + + +class CheckDepositLimits(object): + def __init__(self, daily: int, monthly: int, daily_soft: int, monthly_soft: int): + self.daily = daily + self.monthly = monthly + self.daily_soft = daily_soft + self.monthly_soft = monthly_soft + + @staticmethod + def from_json_api(data: Dict): + return CheckDepositLimits(data["daily"], data["monthly"], data["dailySoft"], data["monthlySoft"]) + + +class CheckDepositAccountLimits(object): + def __init__(self, limits: CheckDepositLimits, totals_daily: int, totals_monthly: int): + self.limits = limits + self.totals_daily = totals_daily + self.totals_monthly = totals_monthly + + @staticmethod + def from_json_api(data: Dict): + return CheckDepositAccountLimits(CheckDepositLimits.from_json_api(data["limits"]), data["totalsDaily"], + data["totalsMonthly"]) + + +class AccountLimitsDTO(object): + def __init__(self, ach: AccountAchLimits, card: AccountCardLimits, check_deposit: CheckDepositAccountLimits): + self.type = "limits" + self.attributes = {"ach": ach, "card": card, "checkDeposit": check_deposit} + + @staticmethod + def from_json_api(attributes): + return AccountLimitsDTO(AccountAchLimits.from_json_api(attributes["ach"]), + AccountCardLimits.from_json_api(attributes["card"]), + CheckDepositAccountLimits.from_json_api(attributes["checkDeposit"])) + + +class CloseAccountRequest(UnitRequest): + def __init__(self, account_id: str, reason: Optional[Literal["ByCustomer", "Fraud"]] = "ByCustomer"): + self.account_id = account_id + self.reason = reason + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "accountClose", + "attributes": { + "reason": self.reason, + } + } + } + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class ListAccountParams(UnitParams): + def __init__(self, offset: int = 0, limit: int = 100, customer_id: Optional[str] = None, + tags: Optional[Dict[str, str]] = None, include: Optional[str] = None, + status: Optional[AccountStatus] = None, from_balance: Optional[int] = None, + to_balance: Optional[int] = None, _type: Optional[AccountTypes] = None): + self.offset = offset + self.limit = limit + self.customer_id = customer_id + self.tags = tags + self.include = include + self.status = status + self.from_balance = from_balance + self.to_balance = to_balance + self._type = _type + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + if self.tags: + parameters["filter[tags]"] = json.dumps(self.tags) + if self.include: + parameters["include"] = self.include + if self.status: + for idx, status_filter in enumerate(self.status): + parameters[f"filter[status][{idx}]"] = status_filter + if self._type: + parameters[f"filter[type]"] = self._type + if self.from_balance: + parameters["filter[fromBalance]"] = self.from_balance + if self.to_balance: + parameters["filter[toBalance]"] = self.to_balance + return parameters diff --git a/build/lib/unit/models/account_end_of_day.py b/build/lib/unit/models/account_end_of_day.py new file mode 100644 index 00000000..b22bf22a --- /dev/null +++ b/build/lib/unit/models/account_end_of_day.py @@ -0,0 +1,41 @@ +import json +from typing import Optional +from unit.models import * + + +class AccountEndOfDayDTO(object): + def __init__(self, id: str, date: str, balance: int, hold: int, available: int, + relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = "accountEndOfDay" + self.attributes = {"date": date, "balance": balance, "hold": hold, "available": available} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AccountEndOfDayDTO(_id, attributes["date"], attributes["balance"], attributes["hold"], + attributes["available"], relationships) + + +class ListAccountEndOfDayParams(UnitParams): + def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, + customer_id: Optional[str] = None, since: Optional[str] = None, until: Optional[str] = None): + self.limit = limit + self.offset = offset + self.account_id = account_id + self.customer_id = customer_id + self.since = since + self.until = until + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + if self.account_id: + parameters["filter[accountId]"] = self.account_id + if self.since: + parameters["filter[since]"] = self.since + if self.until: + parameters["filter[until]"] = self.until + return parameters + diff --git a/build/lib/unit/models/api_token.py b/build/lib/unit/models/api_token.py new file mode 100644 index 00000000..a08e3550 --- /dev/null +++ b/build/lib/unit/models/api_token.py @@ -0,0 +1,48 @@ +from unit.models import * +from unit.utils import date_utils + + +class APITokenDTO(object): + def __init__(self, id: str, created_at: datetime, description: str, expiration: datetime, token: Optional[str], + source_ip: Optional[str]): + self.id = id + self.type = "apiToken" + self.attributes = {"createdAt": created_at, "description": description, "expiration": expiration, + "token": token, "sourceIp": source_ip} + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return APITokenDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["description"], + date_utils.to_datetime(attributes["expiration"]), attributes.get("token"), + attributes.get("sourceIp")) + + +class CreateAPITokenRequest(object): + def __init__(self, user_id: str, description: str, scope: str, expiration: datetime, + source_ip: Optional[str] = None): + self.user_id = user_id + self.description = description + self.scope = scope + self.expiration = expiration + self.source_ip = source_ip + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "apiToken", + "attributes": { + "description": self.description, + "scope": self.scope, + "expiration": self.expiration + } + } + } + + if self.source_ip: + payload["data"]["attributes"]["sourceIp"] = self.source_ip + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + diff --git a/build/lib/unit/models/application.py b/build/lib/unit/models/application.py new file mode 100644 index 00000000..bfb450b7 --- /dev/null +++ b/build/lib/unit/models/application.py @@ -0,0 +1,564 @@ +from unit.utils import date_utils +from unit.models import * +from typing import IO + +ApplicationStatus = Literal["Approved", "Denied", "Pending", "PendingReview"] + +DocumentType = Literal[ + "IdDocument", + "Passport", + "AddressVerification", + "CertificateOfIncorporation", + "EmployerIdentificationNumberConfirmation", +] + +ReasonCode = Literal[ + "PoorQuality", + "NameMismatch", + "SSNMismatch", + "AddressMismatch", + "DOBMismatch", + "ExpiredId", + "EINMismatch", + "StateMismatch", + "Other", +] + +ApplicationTypes = Literal[ + "individualApplication", "businessApplication", "trustApplication" +] + + +Industry = Literal[ + "Retail", + "Wholesale", + "Restaurants", + "Hospitals", + "Construction", + "Insurance", + "Unions", + "RealEstate", + "FreelanceProfessional", + "OtherProfessionalServices", + "OnlineRetailer", + "OtherEducationServices", +] + +AnnualRevenue = Literal[ + "UpTo250k", + "Between250kAnd500k", + "Between500kAnd1m", + "Between1mAnd5m", + "Over5m", + "UpTo50k", + "Between50kAnd100k", + "Between100kAnd200k", + "Between200kAnd500k", + "Over500k", +] + +NumberOfEmployees = Literal[ + "One", + "Between2And5", + "Between5And10", + "Over10", + "UpTo10", + "Between10And50", + "Between50And100", + "Between100And500", + "Over500", +] + +CashFlow = Literal["Unpredictable", "Predictable"] + +BusinessVertical = Literal[ + "AdultEntertainmentDatingOrEscortServices", + "AgricultureForestryFishingOrHunting", + "ArtsEntertainmentAndRecreation", + "BusinessSupportOrBuildingServices", + "Cannabis", + "Construction", + "DirectMarketingOrTelemarketing", + "EducationalServices", + "FinancialServicesCryptocurrency", + "FinancialServicesDebitCollectionOrConsolidation", + "FinancialServicesMoneyServicesBusinessOrCurrencyExchange", + "FinancialServicesOther", + "FinancialServicesPaydayLending", + "GamingOrGambling", + "HealthCareAndSocialAssistance", + "HospitalityAccommodationOrFoodServices", + "LegalAccountingConsultingOrComputerProgramming", + "Manufacturing", + "Mining", + "Nutraceuticals", + "PersonalCareServices", + "PublicAdministration", + "RealEstate", + "ReligiousCivicAndSocialOrganizations", + "RepairAndMaintenance", + "RetailTrade", + "TechnologyMediaOrTelecom", + "TransportationOrWarehousing", + "Utilities", + "WholesaleTrade", +] + +Revocability = Literal["Revocable", "Irrevocable"] +SourceOfFunds = Literal[ + "Inheritance", "Salary", "Savings", "InvestmentReturns", "Gifts" +] + + +class IndividualApplicationDTO(object): + def __init__( + self, + id: str, + created_at: datetime, + full_name: FullName, + address: Address, + date_of_birth: date, + email: str, + phone: Phone, + status: ApplicationStatus, + ssn: Optional[str], + message: Optional[str], + ip: Optional[str], + ein: Optional[str], + dba: Optional[str], + sole_proprietorship: Optional[bool], + tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]], + ): + self.id = id + self.type = "individualApplication" + self.attributes = { + "createdAt": created_at, + "fullName": full_name, + "address": address, + "dateOfBirth": date_of_birth, + "email": email, + "phone": phone, + "status": status, + "ssn": ssn, + "message": message, + "ip": ip, + "ein": ein, + "dba": dba, + "soleProprietorship": sole_proprietorship, + "tags": tags, + } + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return IndividualApplicationDTO( + _id, + date_utils.to_datetime(attributes["createdAt"]), + FullName.from_json_api(attributes["fullName"]), + Address.from_json_api(attributes["address"]), + date_utils.to_date(attributes["dateOfBirth"]), + attributes["email"], + Phone.from_json_api(attributes["phone"]), + attributes["status"], + attributes.get("ssn"), + attributes.get("message"), + attributes.get("ip"), + attributes.get("ein"), + attributes.get("dba"), + attributes.get("soleProprietorship"), + attributes.get("tags"), + relationships, + ) + + +class BusinessApplicationDTO(object): + def __init__( + self, + id: str, + created_at: datetime, + name: str, + address: Address, + phone: Phone, + status: ApplicationStatus, + state_of_incorporation: str, + entity_type: EntityType, + contact: BusinessContact, + officer: Officer, + beneficial_owners: [BeneficialOwner], + ssn: Optional[str], + message: Optional[str], + ip: Optional[str], + ein: Optional[str], + dba: Optional[str], + tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]], + ): + self.id = id + self.type = "businessApplication" + self.attributes = { + "createdAt": created_at, + "name": name, + "address": address, + "phone": phone, + "status": status, + "ssn": ssn, + "stateOfIncorporation": state_of_incorporation, + "ssn": ssn, + "message": message, + "ip": ip, + "ein": ein, + "entityType": entity_type, + "dba": dba, + "contact": contact, + "officer": officer, + "beneficialOwners": beneficial_owners, + "tags": tags, + } + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BusinessApplicationDTO( + _id, + date_utils.to_datetime(attributes["createdAt"]), + attributes.get("name"), + Address.from_json_api(attributes["address"]), + Phone.from_json_api(attributes["phone"]), + attributes["status"], + attributes.get("stateOfIncorporation"), + attributes.get("entityType"), + BusinessContact.from_json_api(attributes["contact"]), + Officer.from_json_api(attributes["officer"]), + BeneficialOwner.from_json_api(attributes["beneficialOwners"]), + attributes.get("ssn"), + attributes.get("message"), + attributes.get("ip"), + attributes.get("ein"), + attributes.get("dba"), + attributes.get("tags"), + relationships, + ) + + +ApplicationDTO = Union[IndividualApplicationDTO, BusinessApplicationDTO] + + +class CreateIndividualApplicationRequest(UnitRequest): + def __init__( + self, + full_name: FullName, + date_of_birth: date, + address: Address, + email: str, + phone: Phone, + ip: str = None, + ein: str = None, + dba: str = None, + sole_proprietorship: bool = None, + passport: str = None, + nationality: str = None, + ssn=None, + device_fingerprints: Optional[List[DeviceFingerprint]] = None, + idempotency_key: str = None, + tags: Optional[Dict[str, str]] = None, + ): + self.full_name = full_name + self.date_of_birth = date_of_birth + self.address = address + self.email = email + self.phone = phone + self.ip = ip + self.ein = ein + self.dba = dba + self.sole_proprietorship = sole_proprietorship + self.ssn = ssn + self.passport = passport + self.nationality = nationality + self.device_fingerprints = device_fingerprints + self.idempotency_key = idempotency_key + self.tags = tags + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "individualApplication", + "attributes": { + "fullName": self.full_name, + "dateOfBirth": date_utils.to_date_str(self.date_of_birth), + "address": self.address, + "email": self.email, + "phone": self.phone, + }, + } + } + + if self.ip: + payload["data"]["attributes"]["ip"] = self.ip + + if self.ein: + payload["data"]["attributes"]["ein"] = self.ein + + if self.dba: + payload["data"]["attributes"]["dba"] = self.dba + + if self.sole_proprietorship: + payload["data"]["attributes"][ + "soleProprietorship" + ] = self.sole_proprietorship + + if self.ssn: + payload["data"]["attributes"]["ssn"] = self.ssn + + if self.passport: + payload["data"]["attributes"]["passport"] = self.passport + + if self.nationality: + payload["data"]["attributes"]["nationality"] = self.nationality + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + if self.device_fingerprints: + payload["data"]["attributes"]["deviceFingerprints"] = [ + e.to_json_api() for e in self.device_fingerprints + ] + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class CreateBusinessApplicationRequest(UnitRequest): + def __init__( + self, + name: str, + address: Address, + phone: Phone, + state_of_incorporation: str, + ein: str, + contact: BusinessContact, + officer: Officer, + beneficial_owners: [BeneficialOwner], + entity_type: EntityType, + tags: Optional[Dict[str, str]] = None, + dba: str = None, + ip: str = None, + website: str = None, + industry: Optional[Industry] = None, + annual_revenue: Optional[AnnualRevenue] = None, + number_of_employees: Optional[NumberOfEmployees] = None, + cash_flow: Optional[CashFlow] = None, + year_of_incorporation: Optional[str] = None, + countries_of_operation: Optional[List[str]] = None, + stock_symbol: Optional[str] = None, + business_vertical: Optional[BusinessVertical] = None, + device_fingerprints: Optional[List[DeviceFingerprint]] = None + ): + self.name = name + self.address = address + self.phone = phone + self.state_of_incorporation = state_of_incorporation + self.ein = ein + self.contact = contact + self.officer = officer + self.beneficial_owners = beneficial_owners + self.entity_type = entity_type + self.dba = dba + self.ip = ip + self.website = website + self.tags = tags + self.industry = industry + self.annual_revenue = annual_revenue + self.number_of_employees = number_of_employees + self.cash_flow = cash_flow + self.year_of_incorporation = year_of_incorporation + self.countries_of_operation = countries_of_operation + self.stock_symbol = stock_symbol + self.business_vertical = business_vertical + self.device_fingerprints = device_fingerprints + + def to_json_api(self) -> dict: + payload = { + "data": { + "type": "businessApplication", + "attributes": { + "name": self.name, + "address": self.address, + "phone": self.phone, + "stateOfIncorporation": self.state_of_incorporation, + "ein": self.ein, + "contact": self.contact, + "officer": self.officer, + "beneficialOwners": self.beneficial_owners, + "entityType": self.entity_type, + "industry": self.industry, + "annualRevenue": self.annual_revenue, + "numberOfEmployees": self.number_of_employees, + "cashFlow": self.cash_flow, + "yearOfIncorporation": self.year_of_incorporation, + "countriesOfOperation": self.countries_of_operation, + "stockSymbol": self.stock_symbol, + "businessVertical": self.business_vertical, + }, + } + } + + if self.dba: + payload["data"]["attributes"]["dba"] = self.dba + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.ip: + payload["data"]["attributes"]["ip"] = self.ip + + if self.website: + payload["data"]["attributes"]["website"] = self.website + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class ApplicationDocumentDTO(object): + def __init__( + self, + id: str, + status: ApplicationStatus, + document_type: DocumentType, + description: str, + name: str, + address: Optional[Address], + date_of_birth: Optional[date], + passport: Optional[str], + ein: Optional[str], + reason_code: Optional[ReasonCode], + reason: Optional[str], + ): + self.id = id + self.type = "document" + self.attributes = { + "status": status, + "documentType": document_type, + "description": description, + "name": name, + "address": address, + "dateOfBirth": date_of_birth, + "passport": passport, + "ein": ein, + "reasonCode": reason_code, + "reason": reason, + } + + @staticmethod + def from_json_api(_id, _type, attributes): + address = ( + Address.from_json_api(attributes.get("address")) + if attributes.get("address") + else None + ) + return ApplicationDocumentDTO( + _id, + attributes["status"], + attributes["documentType"], + attributes["description"], + attributes["name"], + address, + attributes.get("dateOfBirth"), + attributes.get("passport"), + attributes.get("ein"), + attributes.get("reasonCode"), + attributes.get("reason"), + ) + + +FileType = Literal["jpeg", "png", "pdf"] + + +class UploadDocumentRequest(object): + def __init__( + self, + application_id: str, + document_id: str, + file: IO, + file_type: FileType, + is_back_side: Optional[bool] = False, + ): + self.application_id = application_id + self.document_id = document_id + self.file = file + self.file_type = file_type + self.is_back_side = is_back_side + + +class ListApplicationParams(UnitParams): + def __init__( + self, + offset: int = 0, + limit: int = 100, + email: Optional[str] = None, + tags: Optional[object] = None, + query: Optional[str] = None, + sort: Optional[Literal["createdAt", "-createdAt"]] = None, + ): + self.offset = offset + self.limit = limit + self.email = email + self.query = query + self.sort = sort + self.tags = tags + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.email: + parameters["filter[email]"] = self.email + if self.query: + parameters["filter[query]"] = self.query + if self.tags: + parameters["filter[tags]"] = self.tags + if self.sort: + parameters["sort"] = self.sort + return parameters + + +class PatchApplicationRequest(UnitRequest): + def __init__( + self, + application_id: str, + type: ApplicationTypes = "individualApplication", + tags: Optional[Dict[str, str]] = None, + ): + self.application_id = application_id + self.type = type + self.tags = tags + + def to_json_api(self) -> Dict: + payload = {"data": {"type": self.type, "attributes": {}}} + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class ApproveApplicationSBRequest(UnitRequest): + def __init__(self, application_id: str): + self.application_id = application_id + + def to_json_api(self) -> Dict: + payload = { + "data": {"type": "applicationApprove", "attributes": {"reason": "sandbox"}} + } + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) diff --git a/build/lib/unit/models/applicationForm.py b/build/lib/unit/models/applicationForm.py new file mode 100644 index 00000000..99708e7a --- /dev/null +++ b/build/lib/unit/models/applicationForm.py @@ -0,0 +1,103 @@ +import json +from datetime import datetime, date +from typing import Literal, Optional +from unit.utils import date_utils +from unit.models import * + +ApplicationFormStage = Literal["ChooseBusinessOrIndividual", "EnterIndividualInformation", + "IndividualApplicationCreated", "EnterBusinessInformation", "EnterOfficerInformation", + "EnterBeneficialOwnersInformation", "BusinessApplicationCreated", + "EnterSoleProprietorshipInformation", "SoleProprietorshipApplicationCreated"] + + +class ApplicationFormPrefill(object): + def __init__(self, application_type: Optional[str], full_name: Optional[FullName], ssn: Optional[str], + passport: Optional[str], nationality: Optional[str], date_of_birth: Optional[date], + email: Optional[str], name: Optional[str], state_of_incorporation: Optional[str], + entity_type: Optional[str], contact: Optional[BusinessContact], officer: Optional[Officer], + beneficial_owners: [BeneficialOwner], website: Optional[str], dba: Optional[str], + ein: Optional[str], address: Optional[Address], phone: Optional[Phone]): + self.application_type = application_type + self.full_name = full_name + self.ssn = ssn + self.passport = passport + self.nationality = nationality + self.date_of_birth = date_of_birth + self.email = email + self.name = name + self.state_of_incorporation = state_of_incorporation + self.entity_type = entity_type + self.contact = contact + self.officer = officer + self.beneficial_owners = beneficial_owners + self.website = website + self.dba = dba + self.ein = ein + self.address = address + self.phone = phone + + +class ApplicationFormDTO(object): + def __init__(self, id: str, url: str, stage: ApplicationFormStage, applicant_details: ApplicationFormPrefill, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = "applicationForm" + self.attributes = {"url": url, "stage": stage, "applicantDetails": applicant_details, "tags": tags} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return ApplicationFormDTO(_id, attributes["url"], attributes["stage"], attributes.get("applicantDetails"), + attributes.get("tags"), relationships) + + +AllowedApplicationTypes = Union["Individual", "Business", "SoleProprietorship"] + + +class CreateApplicationFormRequest(UnitRequest): + def __init__(self, tags: Optional[Dict[str, str]] = None, + application_details: Optional[ApplicationFormPrefill] = None, + allowed_application_types: [AllowedApplicationTypes] = None): + self.tags = tags + self.application_details = application_details + self.allowed_application_types = allowed_application_types + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "applicationForm", + "attributes": {} + } + } + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.application_details: + payload["data"]["attributes"]["applicantDetails"] = self.application_details + + if self.allowed_application_types: + payload["data"]["attributes"]["allowedApplicationTypes"] = self.allowed_application_types + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class ListApplicationFormParams(UnitParams): + def __init__(self, offset: int = 0, limit: int = 100, tags: Optional[object] = None, + sort: Optional[Literal["createdAt", "-createdAt"]] = None): + self.offset = offset + self.limit = limit + self.tags = tags + self.sort = sort + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.tags: + parameters["filter[tags]"] = self.tags + if self.sort: + parameters["sort"] = self.sort + return parameters + diff --git a/build/lib/unit/models/atm_location.py b/build/lib/unit/models/atm_location.py new file mode 100644 index 00000000..95d2a545 --- /dev/null +++ b/build/lib/unit/models/atm_location.py @@ -0,0 +1,28 @@ +import json +from unit.models import * + + +class AtmLocationDTO(object): + def __init__(self, network: int, location_name: str, coordinates: Coordinates, address: Address, distance: int, + surcharge_free: bool, accept_deposits: bool): + self.type = "atmLocation" + self.attributes = {"network": network, "locationName": location_name, "coordinates": coordinates, + "address": address, "distance": distance, "surchargeFree": surcharge_free, + "acceptDeposits": accept_deposits} + + @staticmethod + def from_json_api(_type, attributes): + return AtmLocationDTO(attributes["network"], attributes["locationName"], + Coordinates.from_json_api(attributes["coordinates"]), + Address.from_json_api(attributes["address"]), attributes["distance"], + attributes["surchargeFree"], attributes["acceptDeposits"]) + + +class GetAtmLocationParams(object): + def __init__(self, search_radius: Optional[int] = None, coordinates: Optional[Coordinates] = None, + postal_code: Optional[str] = None, address: Optional[Address] = None): + self.search_radius = search_radius + self.coordinates = coordinates + self.postal_code = postal_code + self.address = address + diff --git a/build/lib/unit/models/authorization.py b/build/lib/unit/models/authorization.py new file mode 100644 index 00000000..9f6de45b --- /dev/null +++ b/build/lib/unit/models/authorization.py @@ -0,0 +1,61 @@ +import json +from typing import Optional +from unit.models import * +from unit.utils import date_utils + +AuthorizationStatus = Literal["Authorized", "Completed", "Canceled", "Declined"] + +class AuthorizationDTO(object): + def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, status: AuthorizationStatus, + merchant: Merchant, recurring: bool, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = "authorization" + self.attributes = {"createdAt": created_at, "amount": amount, "cardLast4Digits": card_last_4_digits, + "status": status, "merchant": merchant, + "recurring": recurring, "tags": tags} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AuthorizationDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["amount"], + attributes["cardLast4Digits"], attributes["status"], + Merchant.from_json_api(attributes["merchant"]), attributes["recurring"], + attributes.get("tags"), relationships) + + +class ListAuthorizationParams(UnitParams): + def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, + customer_id: Optional[str] = None, card_id: Optional[str] = None, since: Optional[str] = None, + until: Optional[str] = None, include_non_authorized: Optional[bool] = False, + status: Optional[str] = None, sort: Optional[Literal["createdAt", "-createdAt"]] = None): + self.limit = limit + self.offset = offset + self.account_id = account_id + self.customer_id = customer_id + self.card_id = card_id + self.since = since + self.until = until + self.include_non_authorized = include_non_authorized + self.status = status + self.sort = sort + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + if self.account_id: + parameters["filter[accountId]"] = self.account_id + if self.card_id: + parameters["filter[cardId]"] = self.card_id + if self.include_non_authorized: + parameters["filter[includeNonAuthorized]"] = self.include_non_authorized + if self.status: + parameters["filter[status]"] = self.status + if self.since: + parameters["filter[since]"] = self.since + if self.until: + parameters["filter[until]"] = self.until + if self.sort: + parameters["sort"] = self.sort + return parameters diff --git a/build/lib/unit/models/authorization_request.py b/build/lib/unit/models/authorization_request.py new file mode 100644 index 00000000..f654701d --- /dev/null +++ b/build/lib/unit/models/authorization_request.py @@ -0,0 +1,98 @@ +import json +from typing import Optional, Literal +from unit.models import * +from unit.utils import date_utils + +PurchaseAuthorizationRequestStatus = Literal["Pending", "Approved", "Declined"] +DeclineReason = Literal["AccountClosed", "CardExceedsAmountLimit", "DoNotHonor", "InsufficientFunds", "InvalidMerchant", + "ReferToCardIssuer", "RestrictedCard", "Timeout", "TransactionNotPermittedToCardholder"] + +class PurchaseAuthorizationRequestDTO(object): + def __init__(self, id: str, created_at: datetime, amount: int, status: PurchaseAuthorizationRequestStatus, + partial_approval_allowed: str, approved_amount: Optional[int], decline_reason: Optional[DeclineReason], + merchant_name: str, merchant_type: int, merchant_category: str, merchant_location: Optional[str], + recurring: bool, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = "purchaseAuthorizationRequest" + self.attributes = {"createdAt": created_at, "amount": amount, "status": status, + "partialApprovalAllowed": partial_approval_allowed, "approvedAmount": approved_amount, + "declineReason": decline_reason, "merchant": { "name": merchant_name, "type": merchant_type, + "category": merchant_category, + "location": merchant_location}, + "recurring": recurring, "tags": tags} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PurchaseAuthorizationRequestDTO(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["amount"], attributes["status"], + attributes.get("partialApprovalAllowed"), + attributes.get("approvedAmount"), attributes.get("declineReason"), + attributes["merchant"]["name"], attributes["merchant"]["type"], + attributes["merchant"]["category"], + attributes["merchant"].get("location"), attributes["recurring"], + attributes.get("tags"), relationships) + + +class ListPurchaseAuthorizationRequestParams(UnitParams): + def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, + customer_id: Optional[str] = None): + self.limit = limit + self.offset = offset + self.account_id = account_id + self.customer_id = customer_id + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + if self.account_id: + parameters["filter[accountId]"] = self.account_id + return parameters + + +class ApproveAuthorizationRequest(UnitRequest): + def __init__(self, authorization_id: str, amount: Optional[int] = None, tags: Optional[Dict[str, str]] = None): + self.authorization_id = authorization_id + self.amount = amount + self.tags = tags + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "approveAuthorizationRequest", + "attributes": {} + } + } + + if self.amount: + payload["data"]["attributes"]["amount"] = self.amount + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class DeclineAuthorizationRequest(UnitRequest): + def __init__(self, authorization_id: str, reason: DeclineReason): + self.authorization_id = authorization_id + self.reason = reason + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "declineAuthorizationRequest", + "attributes": { + "reason": self.reason + } + } + } + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) diff --git a/build/lib/unit/models/benificial_owner.py b/build/lib/unit/models/benificial_owner.py new file mode 100644 index 00000000..3b3fcd12 --- /dev/null +++ b/build/lib/unit/models/benificial_owner.py @@ -0,0 +1,26 @@ +import json +from typing import Optional +from unit.models import * +from unit.utils import date_utils + + +class BenificialOwnerDTO(object): + def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, + status: Optional[Status] = None, ssn: Optional[str] = None, passport: Optional[str] = None, + nationality: Optional[str] = None, percentage: Optional[int] = None): + self.full_name = full_name + self.date_of_birth = date_of_birth + self.address = address + self.phone = phone + self.email = email + self.status = status + self.ssn = ssn + self.passport = passport + self.nationality = nationality + self.percentage = percentage + + @staticmethod + def from_json_api(attributes): + return BenificialOwnerDTO(attributes.get("fullName"), attributes.get("dateOfBirth"), attributes.get("address"), + attributes.get("phone"), attributes.get("email"), attributes.get("status"), attributes.get("ssn"), + attributes.get("passport"), attributes.get("nationality"), attributes.get("percentage")) diff --git a/build/lib/unit/models/bill_pay.py b/build/lib/unit/models/bill_pay.py new file mode 100644 index 00000000..23e25713 --- /dev/null +++ b/build/lib/unit/models/bill_pay.py @@ -0,0 +1,21 @@ +import json +from typing import Optional +from unit.models import * + + +class BillerDTO(object): + def __init__(self, id: str, name: int, category: str): + self.id = id + self.type = "biller" + self.attributes = {"name": name, "category": category} + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BillerDTO(_id, attributes["name"], attributes["category"]) + + +class GetBillersParams(object): + def __init__(self, name: str, page: Optional[int] = None): + self.name = name + self.page = page + diff --git a/build/lib/unit/models/card.py b/build/lib/unit/models/card.py new file mode 100644 index 00000000..bf0c4ab0 --- /dev/null +++ b/build/lib/unit/models/card.py @@ -0,0 +1,617 @@ +from unit.utils import date_utils +from unit.models import * + +CardStatus = Literal["Inactive", "Active", "Stolen", "Lost", "Frozen", "ClosedByCustomer", "SuspectedFraud"] + + +class IndividualDebitCardDTO(object): + def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, status: CardStatus, + shipping_address: Optional[Address], design: Optional[str], + relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = "individualDebitCard" + self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, + "status": status, "shippingAddress": shipping_address, "design": design} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + shipping_address = Address.from_json_api(attributes.get("shippingAddress")) if attributes.get("shippingAddress") else None + return IndividualDebitCardDTO( + _id, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], + attributes["expirationDate"], attributes["status"], + shipping_address, attributes.get("design"), relationships + ) + + +class BusinessCardDTO(object): + def __init__(self, _id: str, _type: str, created_at: datetime, last_4_digits: str, expiration_date: str, + ssn: Optional[str], full_name: FullName, date_of_birth: date, address: Address, phone: Phone, + email: str, status: CardStatus, passport: Optional[str], nationality: Optional[str], + shipping_address: Optional[Address], design: Optional[str], + relationships: Optional[Dict[str, Relationship]], tags: Optional[Dict[str, str]]): + self.id = _id + self.type = _type + self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, + "ssn": ssn, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, + "phone": phone, "email": email, "status": status, "passport": passport, + "nationality": nationality, "shippingAddress": shipping_address, "design": design, + "tags": tags} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BusinessCardDTO( + _id, _type, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], + attributes["expirationDate"], attributes.get("ssn"), FullName.from_json_api(attributes["fullName"]), + attributes["dateOfBirth"], Address.from_json_api(attributes["address"]), + Phone.from_json_api(attributes["phone"]), attributes["email"], attributes["status"], + attributes.get("passport"), attributes.get("nationality"), + Address.from_json_api(attributes.get("shippingAddress")), attributes.get("design"), relationships, + attributes.get("tags") + ) + +class BusinessDebitCardDTO(BusinessCardDTO): + def __init__(self, card: BusinessCardDTO): + self.id = card.id + self.type = card.type + self.attributes = card.attributes + self.relationships = card.relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BusinessDebitCardDTO(BusinessCardDTO.from_json_api(_id, _type, attributes, relationships)) + + +class BusinessCreditCardDTO(BusinessCardDTO): + def __init__(self, card: BusinessCardDTO): + self.id = card.id + self.type = card.type + self.attributes = card.attributes + self.relationships = card.relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BusinessCreditCardDTO(BusinessCardDTO.from_json_api(_id, _type, attributes, relationships)) + +class IndividualVirtualDebitCardDTO(object): + def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, status: CardStatus, + relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = "individualVirtualDebitCard" + self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, + "status": status} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return IndividualVirtualDebitCardDTO( + _id, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], + attributes["expirationDate"], attributes["status"], relationships + ) + + +class BusinessVirtualDebitCardDTO(object): + def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, + full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, + status: CardStatus, passport: Optional[str] = None, nationality: Optional[str] = None, + relationships: Optional[Dict[str, Relationship]] = None): + self.id = id + self.type = "businessVirtualDebitCard" + self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, + "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, + "phone": phone, "email": email, "status": status, "passport": passport, + "nationality": nationality} + self.relationships = relationships or {} + + def from_json_api(_id, _type, attributes, relationships): + return BusinessVirtualDebitCardDTO( + _id, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], + attributes["expirationDate"], FullName.from_json_api(attributes["fullName"]), + attributes["dateOfBirth"], Address.from_json_api(attributes["address"]), + Phone.from_json_api(attributes["phone"]), attributes["email"], attributes["status"], + attributes.get("passport"), attributes.get("nationality"), relationships + ) + +class BusinessVirtualCardDTO(object): + def __init__(self, _id: str, _type: str, created_at: datetime, last_4_digits: str, expiration_date: str, + ssn: Optional[str], full_name: FullName, date_of_birth: date, address: Address, phone: Phone, + email: str, status: CardStatus, passport: Optional[str], nationality: Optional[str], + relationships: Optional[Dict[str, Relationship]], tags: Optional[Dict[str, str]]): + self.id = _id + self.type = _type + self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, + "ssn": ssn, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, + "phone": phone, "email": email, "status": status, "passport": passport, + "nationality": nationality, "tags": tags} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BusinessVirtualCardDTO( + _id, _type, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], + attributes["expirationDate"], attributes.get("ssn"), FullName.from_json_api(attributes["fullName"]), + attributes["dateOfBirth"], Address.from_json_api(attributes["address"]), + Phone.from_json_api(attributes["phone"]), attributes["email"], attributes["status"], + attributes.get("passport"), attributes.get("nationality"), relationships, attributes.get("tags")) + +class BusinessVirtualCreditCardDTO(BusinessVirtualCardDTO): + def __init__(self, card: BusinessVirtualCardDTO): + self.id = card.id + self.type = card.type + self.attributes = card.attributes + self.relationships = card.relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BusinessVirtualCreditCardDTO(BusinessVirtualCardDTO.from_json_api(_id, _type, attributes, relationships)) + +Card = Union[IndividualDebitCardDTO, BusinessDebitCardDTO, IndividualVirtualDebitCardDTO, BusinessVirtualDebitCardDTO, + BusinessVirtualCreditCardDTO, BusinessCreditCardDTO] + + +class CreateIndividualDebitCard(UnitRequest): + def __init__(self, relationships: Dict[str, Relationship], limits: Optional[CardLevelLimits] = None, + shipping_address: Optional[Address] = None, design: Optional[str] = None, + idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None): + self.shipping_address = shipping_address + self.design = design + self.limits = limits + self.idempotency_key = idempotency_key + self.tags = tags + self.relationships = relationships + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "individualDebitCard", + "attributes": {}, + "relationships": self.relationships + } + } + + if self.shipping_address: + payload["data"]["attributes"]["shippingAddress"] = self.shipping_address + + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + + if self.design: + payload["data"]["attributes"]["design"] = self.design + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + +class CreateBusinessCard(object): + def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, + relationships: Dict[str, Relationship], shipping_address: Optional[Address] = None, + ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, + design: Optional[str] = None, idempotency_key: Optional[str] = None, + tags: Optional[Dict[str, str]] = None, limits: Optional[CardLevelLimits] = None, + additional_embossed_text: Optional[str] = None, print_only_business_name: Optional[bool] = None): + self.full_name = full_name + self.date_of_birth = date_of_birth + self.address = address + self.phone = phone + self.email = email + self.shipping_address = shipping_address + self.ssn = ssn + self.passport = passport + self.nationality = nationality + self.design = design + self.idempotency_key = idempotency_key + self.tags = tags + self.relationships = relationships + self.limits = limits + self.additional_embossed_text = additional_embossed_text + self.print_only_business_name = print_only_business_name + + def to_json_api(self, _type: str) -> Dict: + payload = { + "data": { + "type": _type, + "attributes": { + "fullName": self.full_name, + "dateOfBirth": self.date_of_birth, + "address": self.address, + "phone": self.phone, + "email": self.email, + }, + "relationships": self.relationships + } + } + + if self.shipping_address: + payload["data"]["attributes"]["shippingAddress"] = self.shipping_address + + if self.ssn: + payload["data"]["attributes"]["ssn"] = self.ssn + + if self.passport: + payload["data"]["attributes"]["passport"] = self.passport + + if self.nationality: + payload["data"]["attributes"]["nationality"] = self.nationality + + if self.design: + payload["data"]["attributes"]["design"] = self.design + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + + if self.additional_embossed_text: + payload["data"]["attributes"]["additionalEmbossedText"] = self.additional_embossed_text + + if self.print_only_business_name is not None: + payload["data"]["attributes"]["printOnlyBusinessName"] = self.print_only_business_name + + return payload + + def __repr__(self): + return json.dumps(self.to_json_api()) + + +class CreateBusinessDebitCard(CreateBusinessCard): + def to_json_api(self): + return super().to_json_api("businessDebitCard") + + +class CreateBusinessCreditCard(CreateBusinessCard): + def to_json_api(self): + return super().to_json_api("businessCreditCard") + +class CreateIndividualVirtualDebitCard(UnitRequest): + def __init__(self, relationships: Dict[str, Relationship], idempotency_key: Optional[str] = None, + limits: Optional[CardLevelLimits] = None, tags: Optional[Dict[str, str]] = None): + self.idempotency_key = idempotency_key + self.limits = limits + self.tags = tags + self.relationships = relationships + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "individualVirtualDebitCard", + "attributes": {}, + "relationships": self.relationships + } + } + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class CreateBusinessVirtualCard(object): + def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, + relationships: Dict[str, Relationship], ssn: Optional[str] = None, passport: Optional[str] = None, + nationality: Optional[str] = None, idempotency_key: Optional[str] = None, + tags: Optional[Dict[str, str]] = None, limits: Optional[CardLevelLimits] = None): + self.full_name = full_name + self.date_of_birth = date_of_birth + self.address = address + self.phone = phone + self.email = email + self.ssn = ssn + self.passport = passport + self.nationality = nationality + self.idempotency_key = idempotency_key + self.tags = tags + self.relationships = relationships + self.limits = limits + + def to_json_api(self, _type: str) -> Dict: + payload = { + "data": { + "type": _type, + "attributes": { + "fullName": self.full_name, + "dateOfBirth": self.date_of_birth, + "address": self.address, + "phone": self.phone, + "email": self.email, + }, + "relationships": self.relationships + } + } + + if self.ssn: + payload["data"]["attributes"]["ssn"] = self.ssn + + if self.passport: + payload["data"]["attributes"]["passport"] = self.passport + + if self.nationality: + payload["data"]["attributes"]["nationality"] = self.nationality + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + + return payload + + def __repr__(self): + return json.dumps(self.to_json_api()) + + +class CreateBusinessVirtualDebitCard(CreateBusinessVirtualCard): + def to_json_api(self): + return super().to_json_api("businessVirtualDebitCard") + + +class CreateBusinessVirtualCreditCard(CreateBusinessVirtualCard): + def to_json_api(self): + return super().to_json_api("businessVirtualCreditCard") + + +CreateCardRequest = Union[CreateIndividualDebitCard, CreateBusinessDebitCard, CreateIndividualVirtualDebitCard, + CreateBusinessVirtualDebitCard, CreateBusinessVirtualCreditCard, CreateBusinessCreditCard] + +class PatchIndividualDebitCard(UnitRequest): + def __init__(self,card_id: str, shipping_address: Optional[Address] = None, design: Optional[str] = None, + limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): + self.card_id = card_id + self.shipping_address = shipping_address + self.design = design + self.limits = limits + self.tags = tags + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "individualDebitCard", + "attributes": {}, + } + } + + if self.shipping_address: + payload["data"]["attributes"]["shippingAddress"] = self.shipping_address + + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + + if self.design: + payload["data"]["attributes"]["design"] = self.design + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class PatchBusinessCard(object): + def __init__(self, card_id: str, shipping_address: Optional[Address] = None, address: Optional[Address] = None, + phone: Optional[Phone] = None, email: Optional[str] = None, design: Optional[str] = None, + tags: Optional[Dict[str, str]] = None, limits: Optional[CardLevelLimits] = None): + self.card_id = card_id + self.shipping_address = shipping_address + self.address = address + self.phone = phone + self.email = email + self.design = design + self.tags = tags + self.limits = limits + + def to_json_api(self, _type: str = "businessDebitCard") -> Dict: + payload = { + "data": { + "type": _type, + "attributes": {}, + } + } + + if self.shipping_address: + payload["data"]["attributes"]["shippingAddress"] = self.shipping_address + + if self.address: + payload["data"]["attributes"]["address"] = self.address + + if self.phone: + payload["data"]["attributes"]["phone"] = self.phone + + if self.email: + payload["data"]["attributes"]["email"] = self.email + + if self.design: + payload["data"]["attributes"]["design"] = self.design + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + + return payload + + def __repr__(self): + return json.dumps(self.to_json_api()) + + +class PatchBusinessDebitCard(PatchBusinessCard): + pass + + +class PatchBusinessCreditCard(PatchBusinessCard): + def to_json_api(self) -> Dict: + return super().to_json_api("businessCreditCard") + +class PatchIndividualVirtualDebitCard(UnitRequest): + def __init__(self, card_id: str, limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): + self.card_id = card_id + self.limits = limits + self.tags = tags + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "individualVirtualDebitCard", + "attributes": {}, + } + } + + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + + payload["data"]["attributes"]["tags"] = self.tags or {} + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + +class PatchBusinessVirtualCard(object): + def __init__(self, card_id: str, address: Optional[Address] = None, phone: Optional[Phone] = None, + email: Optional[str] = None, tags: Optional[Dict[str, str]] = None, + _type: str = "businessVirtualDebitCard", limits: Optional[CardLevelLimits] = None): + self.card_id = card_id + self.address = address + self.phone = phone + self.email = email + self.tags = tags + self.limits = limits + + def to_json_api(self, _type: str = "businessVirtualDebitCard") -> Dict: + payload = { + "data": { + "type": _type, + "attributes": {}, + } + } + + if self.address: + payload["data"]["attributes"]["address"] = self.address + + if self.phone: + payload["data"]["attributes"]["phone"] = self.phone + + if self.email: + payload["data"]["attributes"]["email"] = self.email + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + + return payload + + +class PatchBusinessVirtualDebitCard(PatchBusinessVirtualCard): + pass + + +class PatchBusinessVirtualCreditCard(PatchBusinessVirtualCard): + def to_json_api(self) -> Dict: + return super().to_json_api("businessVirtualCreditCard") + + +PatchCardRequest = Union[PatchIndividualDebitCard, PatchBusinessDebitCard, PatchIndividualVirtualDebitCard, + PatchBusinessVirtualDebitCard, PatchBusinessCreditCard, PatchBusinessVirtualCreditCard] + +class ReplaceCardRequest(object): + def __init__(self, shipping_address: Optional[Address] = None): + self.shipping_address = shipping_address + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "replaceCard", + "attributes": {}, + } + } + + if self.shipping_address: + payload["data"]["attributes"]["shippingAddress"] = self.shipping_address + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + +PinStatus = Literal["Set", "NotSet"] + +class PinStatusDTO(object): + def __init__(self, status: PinStatus): + self.type = "pinStatus" + self.attributes = {"status": status} + + @staticmethod + def from_json_api(attributes): + return PinStatusDTO(attributes["status"]) + + +class CardLimitsDTO(object): + def __init__(self, limits: CardLevelLimits, daily_totals: CardTotals, monthly_totals: CardTotals): + self.type = "limits" + self.attributes = {"limits": limits, "dailyTotals": daily_totals, "monthlyTotals": monthly_totals} + + @staticmethod + def from_json_api(attributes): + limits = CardLevelLimits.from_json_api(attributes.get("limits")) if attributes.get("limits") else None + return CardLimitsDTO(limits, CardTotals.from_json_api(attributes.get("dailyTotals")), + CardTotals.from_json_api(attributes.get("monthlyTotals"))) + + +class ListCardParams(UnitParams): + def __init__(self, offset: int = 0, limit: int = 100, account_id: Optional[str] = None, + customer_id: Optional[str] = None, tags: Optional[Dict[str, str]] = None, include: Optional[str] = None, + sort: Optional[Literal["createdAt", "-createdAt"]] = None, + status: Optional[List[CardStatus]] = None): + self.offset = offset + self.limit = limit + self.account_id = account_id + self.customer_id = customer_id + self.tags = tags + self.include = include + self.sort = sort + self.status = status + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + if self.account_id: + parameters["filter[accountId]"] = self.account_id + if self.tags: + parameters["filter[tags]"] = json.dumps(self.tags) + if self.include: + parameters["include"] = self.include + if self.sort: + parameters["sort"] = self.sort + if self.status: + for idx, status_filter in enumerate(self.status): + parameters[f"filter[status][{idx}]"] = status_filter + return parameters + diff --git a/build/lib/unit/models/codecs.py b/build/lib/unit/models/codecs.py new file mode 100644 index 00000000..ed7d296e --- /dev/null +++ b/build/lib/unit/models/codecs.py @@ -0,0 +1,403 @@ +import json +from unit.models import * +from datetime import datetime, date + +from unit.models.reward import RewardDTO +from unit.utils import date_utils +from unit.models.applicationForm import ApplicationFormDTO +from unit.models.application import IndividualApplicationDTO, BusinessApplicationDTO, ApplicationDocumentDTO +from unit.models.account import DepositAccountDTO, AccountLimitsDTO +from unit.models.customer import IndividualCustomerDTO, BusinessCustomerDTO +from unit.models.card import IndividualDebitCardDTO, BusinessDebitCardDTO, IndividualVirtualDebitCardDTO,\ + BusinessVirtualDebitCardDTO, PinStatusDTO, CardLimitsDTO +from unit.models.transaction import * +from unit.models.payment import AchPaymentDTO, BookPaymentDTO, WirePaymentDTO, AchReceivedPaymentDTO, BillPaymentDTO, \ + SimulateIncomingAchPaymentDTO +from unit.models.customerToken import CustomerTokenDTO, CustomerVerificationTokenDTO +from unit.models.fee import FeeDTO +from unit.models.event import * +from unit.models.counterparty import CounterpartyDTO, CounterpartyBalanceDTO +from unit.models.webhook import WebhookDTO +from unit.models.institution import InstitutionDTO +from unit.models.statement import StatementDTO +from unit.models.atm_location import AtmLocationDTO +from unit.models.bill_pay import BillerDTO +from unit.models.api_token import APITokenDTO +from unit.models.authorization import AuthorizationDTO +from unit.models.authorization_request import PurchaseAuthorizationRequestDTO +from unit.models.account_end_of_day import AccountEndOfDayDTO +from unit.models.benificial_owner import BenificialOwnerDTO + +mappings = { + "individualApplication": lambda _id, _type, attributes, relationships: + IndividualApplicationDTO.from_json_api(_id, _type, attributes, relationships), + + "businessApplication": lambda _id, _type, attributes, relationships: + BusinessApplicationDTO.from_json_api(_id, _type, attributes, relationships), + + "document": lambda _id, _type, attributes, relationships: + ApplicationDocumentDTO.from_json_api(_id, _type, attributes), + + "individualCustomer": lambda _id, _type, attributes, relationships: + IndividualCustomerDTO.from_json_api(_id, _type, attributes, relationships), + + "businessCustomer": lambda _id, _type, attributes, relationships: + BusinessCustomerDTO.from_json_api(_id, _type, attributes, relationships), + + "depositAccount": lambda _id, _type, attributes, relationships: + DepositAccountDTO.from_json_api(_id, _type, attributes, relationships), + + "limits": lambda _id, _type, attributes, relationships: + decode_limits(attributes), + + "individualDebitCard": lambda _id, _type, attributes, relationships: + IndividualDebitCardDTO.from_json_api(_id, _type, attributes, relationships), + + "businessDebitCard": lambda _id, _type, attributes, relationships: + BusinessDebitCardDTO.from_json_api(_id, _type, attributes, relationships), + + "individualVirtualDebitCard": lambda _id, _type, attributes, relationships: + IndividualVirtualDebitCardDTO.from_json_api(_id, _type, attributes, relationships), + + "businessVirtualDebitCard": lambda _id, _type, attributes, relationships: + BusinessVirtualDebitCardDTO.from_json_api(_id, _type, attributes, relationships), + + "originatedAchTransaction": lambda _id, _type, attributes, relationships: + OriginatedAchTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "receivedAchTransaction": lambda _id, _type, attributes, relationships: + ReceivedAchTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "returnedAchTransaction": lambda _id, _type, attributes, relationships: + ReturnedAchTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "returnedReceivedAchTransaction": lambda _id, _type, attributes, relationships: + ReturnedReceivedAchTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "dishonoredAchTransaction": lambda _id, _type, attributes, relationships: + DishonoredAchTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "bookTransaction": lambda _id, _type, attributes, relationships: + BookTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "purchaseTransaction": lambda _id, _type, attributes, relationships: + PurchaseTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "atmTransaction": lambda _id, _type, attributes, relationships: + AtmTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "feeTransaction": lambda _id, _type, attributes, relationships: + FeeTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "cardTransaction": lambda _id, _type, attributes, relationships: + CardTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "wireTransaction": lambda _id, _type, attributes, relationships: + WireTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "releaseTransaction": lambda _id, _type, attributes, relationships: + ReleaseTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "adjustmentTransaction": lambda _id, _type, attributes, relationships: + AdjustmentTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "interestTransaction": lambda _id, _type, attributes, relationships: + InterestTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "disputeTransaction": lambda _id, _type, attributes, relationships: + DisputeTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "checkDepositTransaction": lambda _id, _type, attributes, relationships: + CheckDepositTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "returnedCheckDepositTransaction": lambda _id, _type, attributes, relationships: + ReturnedCheckDepositTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + #"paymentAdvanceTransaction": lambda _id, _type, attributes, relationships: + #PaymentAdvanceTransactionTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "repaidPaymentAdvanceTransaction": lambda _id, _type, attributes, relationships: + RepaidPaymentAdvanceTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "achPayment": lambda _id, _type, attributes, relationships: + AchPaymentDTO.from_json_api(_id, _type, attributes, relationships), + + "bookPayment": lambda _id, _type, attributes, relationships: + BookPaymentDTO.from_json_api(_id, _type, attributes, relationships), + + "wirePayment": lambda _id, _type, attributes, relationships: + WirePaymentDTO.from_json_api(_id, _type, attributes, relationships), + + "billPayment": lambda _id, _type, attributes, relationships: + BillPaymentDTO.from_json_api(_id, _type, attributes, relationships), + + "achReceivedPayment": lambda _id, _type, attributes, relationships: + AchReceivedPaymentDTO.from_json_api(_id, _type, attributes, relationships), + + "accountStatementDTO": lambda _id, _type, attributes, relationships: + StatementDTO.from_json_api(_id, _type, attributes, relationships), + + "sandboxAccountStatement": lambda _id, _type, attributes, relationships: + StatementDTO.from_json_api(_id, _type, attributes, relationships), + + "customerBearerToken": lambda _id, _type, attributes, relationships: + CustomerTokenDTO.from_json_api(_id, _type, attributes, relationships), + + "customerTokenVerification": lambda _id, _type, attributes, relationships: + CustomerVerificationTokenDTO.from_json_api(_id, _type, attributes, relationships), + + "achCounterparty": lambda _id, _type, attributes, relationships: + CounterpartyDTO.from_json_api(_id, _type, attributes, relationships), + + "applicationForm": lambda _id, _type, attributes, relationships: + ApplicationFormDTO.from_json_api(_id, _type, attributes, relationships), + + "fee": lambda _id, _type, attributes, relationships: + FeeDTO.from_json_api(_id, _type, attributes, relationships), + + "account.closed": lambda _id, _type, attributes, relationships: + AccountClosedEvent.from_json_api(_id, _type, attributes, relationships), + + "account.frozen": lambda _id, _type, attributes, relationships: + AccountFrozenEvent.from_json_api(_id, _type, attributes, relationships), + + "application.awaitingDocuments": lambda _id, _type, attributes, relationships: + ApplicationAwaitingDocumentsEvent.from_json_api(_id, _type, attributes, relationships), + + "application.denied": lambda _id, _type, attributes, relationships: + ApplicationDeniedEvent.from_json_api(_id, _type, attributes, relationships), + + "application.pendingReview": lambda _id, _type, attributes, relationships: + ApplicationPendingReviewEvent.from_json_api(_id, _type, attributes, relationships), + + "card.activated": lambda _id, _type, attributes, relationships: + CardActivatedEvent.from_json_api(_id, _type, attributes, relationships), + + "card.statusChanged": lambda _id, _type, attributes, relationships: + CardStatusChangedEvent.from_json_api(_id, _type, attributes, relationships), + + "authorization.created": lambda _id, _type, attributes, relationships: + AuthorizationCreatedEvent.from_json_api(_id, _type, attributes, relationships), + + "authorization.canceled": lambda _id, _type, attributes, relationships: + AuthorizationCanceledEvent.from_json_api(_id, _type, attributes, relationships), + + "authorization.declined": lambda _id, _type, attributes, relationships: + AuthorizationDeclinedEvent.from_json_api(_id, _type, attributes, relationships), + + "authorizationRequest.declined": lambda _id, _type, attributes, relationships: + AuthorizationRequestDeclinedEvent.from_json_api(_id, _type, attributes, relationships), + + "authorizationRequest.pending": lambda _id, _type, attributes, relationships: + AuthorizationRequestPendingEvent.from_json_api(_id, _type, attributes, relationships), + + "authorizationRequest.approved": lambda _id, _type, attributes, relationships: + AuthorizationRequestApprovedEvent.from_json_api(_id, _type, attributes, relationships), + + "document.rejected": lambda _id, _type, attributes, relationships: + DocumentRejectedEvent.from_json_api(_id, _type, attributes, relationships), + + "document.approved": lambda _id, _type, attributes, relationships: + DocumentApprovedEvent.from_json_api(_id, _type, attributes, relationships), + + "checkDeposit.created": lambda _id, _type, attributes, relationships: + CheckDepositCreatedEvent.from_json_api(_id, _type, attributes, relationships), + + "checkDeposit.clearing": lambda _id, _type, attributes, relationships: + CheckDepositClearingEvent.from_json_api(_id, _type, attributes, relationships), + + "checkDeposit.sent": lambda _id, _type, attributes, relationships: + CheckDepositSentEvent.from_json_api(_id, _type, attributes, relationships), + + "payment.clearing": lambda _id, _type, attributes, relationships: + PaymentClearingEvent.from_json_api(_id, _type, attributes, relationships), + + "payment.created": lambda _id, _type, attributes, relationships: + PaymentCreatedEvent.from_json_api(_id, _type, attributes, relationships), + + "payment.sent": lambda _id, _type, attributes, relationships: + PaymentSentEvent.from_json_api(_id, _type, attributes, relationships), + + "payment.returned": lambda _id, _type, attributes, relationships: + PaymentReturnedEvent.from_json_api(_id, _type, attributes, relationships), + + "payment.rejected": lambda _id, _type, attributes, relationships: + PaymentRejectedEvent.from_json_api(_id, _type, attributes, relationships), + + "statements.created": lambda _id, _type, attributes, relationships: + StatementsCreatedEvent.from_json_api(_id, _type, attributes, relationships), + + "transaction.created": lambda _id, _type, attributes, relationships: + TransactionCreatedEvent.from_json_api(_id, _type, attributes, relationships), + + "customer.created": lambda _id, _type, attributes, relationships: + CustomerCreatedEvent.from_json_api(_id, _type, attributes, relationships), + + "customer.updated": lambda _id, _type, attributes, relationships: + CustomerUpdatedEvent.from_json_api(_id, _type, attributes, relationships), + + "account.reopened": lambda _id, _type, attributes, relationships: + AccountReopenedEvent.from_json_api(_id, _type, attributes, relationships), + + "webhook": lambda _id, _type, attributes, relationships: + WebhookDTO.from_json_api(_id, _type, attributes, relationships), + + "institution": lambda _id, _type, attributes, relationships: + InstitutionDTO.from_json_api(_id, _type, attributes, relationships), + + "atmLocation": lambda _id, _type, attributes, relationships: + AtmLocationDTO.from_json_api(_type, attributes), + + "biller": lambda _id, _type, attributes, relationships: + BillerDTO.from_json_api(_id, _type, attributes, relationships), + + "apiToken": lambda _id, _type, attributes, relationships: + APITokenDTO.from_json_api(_id, _type, attributes, relationships), + + "authorization": lambda _id, _type, attributes, relationships: + AuthorizationDTO.from_json_api(_id, _type, attributes, relationships), + + "purchaseAuthorizationRequest": lambda _id, _type, attributes, relationships: + PurchaseAuthorizationRequestDTO.from_json_api(_id, _type, attributes, relationships), + + "accountEndOfDay": lambda _id, _type, attributes, relationships: + AccountEndOfDayDTO.from_json_api(_id, _type, attributes, relationships), + + "counterpartyBalance": lambda _id, _type, attributes, relationships: + CounterpartyBalanceDTO.from_json_api(_id, _type, attributes, relationships), + + "pinStatus": lambda _id, _type, attributes, relationships: + PinStatusDTO.from_json_api(attributes), + + "beneficialOwner": lambda _id, _type, attributes, relationships: + BenificialOwnerDTO.from_json_api(attributes), + + "reward": lambda _id, _type, attributes, relationships: + RewardDTO.from_json_api(_id, attributes, relationships), + +} + + +def split_json_api_single_response(payload: Dict): + _id, _type, attributes = payload.get("id"), payload["type"], payload["attributes"] + relationships = None + + if payload.get("relationships"): + relationships = dict() + for k, v in payload.get("relationships").items(): + if isinstance(v["data"], list): + # todo: alex handle cases when relationships are in a form of array (e.g. jointAccount or documents) + continue + else: + relationships[k] = Relationship(v["data"]["type"], v["data"]["id"]) + + return _id, _type, attributes, relationships + + +def split_json_api_array_response(payload): + if not isinstance(payload, list): + raise Exception("split_json_api_array_response - couldn't parse response.") + + dtos = [] + for single_obj in payload: + dtos.append(split_json_api_single_response(single_obj)) + + return dtos + + +def decode_limits(attributes: Dict): + if "ach" in attributes.keys(): + return AccountLimitsDTO.from_json_api(attributes) + else: + return CardLimitsDTO.from_json_api(attributes) + +def mapping_wraper(_id, _type, attributes, relationships): + if _type in mappings: + return mappings[_type](_id, _type, attributes, relationships) + else: + return RawUnitObject(_id, _type, attributes, relationships) + +class DtoDecoder(object): + @staticmethod + def decode(payload): + if payload is None: + return None + # if response contains a list of dtos + if isinstance(payload, list): + dtos = split_json_api_array_response(payload) + response = [] + for _id, _type, attributes, relationships in dtos: + response.append(mapping_wraper(_id, _type, attributes, relationships)) + + return response + else: + _id, _type, attributes, relationships = split_json_api_single_response(payload) + return mapping_wraper(_id, _type, attributes, relationships) + +class UnitEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, CardLevelLimits): + return { + "dailyWithdrawal": obj.daily_withdrawal, + "dailyPurchase": obj.daily_purchase, + "monthlyWithdrawal": obj.monthly_withdrawal, + "monthlyPurchase": obj.monthly_purchase + } + if isinstance(obj, FullName): + return {"first": obj.first, "last": obj.last} + if isinstance(obj, Phone): + return {"countryCode": obj.country_code, "number": obj.number} + if isinstance(obj, Address): + addr = { + "street": obj.street, + "city": obj.city, + "state": obj.state, + "postalCode": obj.postal_code, + "country": obj.country + } + + if obj.street2 is not None: + addr["street2"] = obj.street2 + return addr + if isinstance(obj, BusinessContact): + return {"fullName": obj.full_name, "email": obj.email, "phone": obj.phone} + if isinstance(obj, AuthorizedUser): + return {"fullName": obj.full_name, "email": obj.email, "phone": obj.phone} + if isinstance(obj, Officer): + officer = {"fullName": obj.full_name, "dateOfBirth": date_utils.to_date_str(obj.date_of_birth), + "address": obj.address, "phone": obj.phone, "email": obj.email} + if obj.status is not None: + officer["status"] = obj.status + if obj.title is not None: + officer["title"] = obj.title + if obj.ssn is not None: + officer["ssn"] = obj.ssn + if obj.passport is not None: + officer["passport"] = obj.passport + if obj.nationality is not None: + officer["nationality"] = obj.nationality + return officer + if isinstance(obj, BeneficialOwner): + beneficial_owner = {"fullName": obj.full_name, "dateOfBirth": date_utils.to_date_str(obj.date_of_birth), + "address": obj.address, "phone": obj.phone, "email": obj.email} + if obj.status is not None: + beneficial_owner["status"] = obj.status + if obj.ssn is not None: + beneficial_owner["ssn"] = obj.ssn + if obj.passport is not None: + beneficial_owner["passport"] = obj.passport + if obj.nationality is not None: + beneficial_owner["nationality"] = obj.nationality + if obj.percentage is not None: + beneficial_owner["percentage"] = obj.percentage + return beneficial_owner + if isinstance(obj, RelationshipArray): + return {"data": list(map(lambda r: r.to_dict(), obj.relationships))} + if isinstance(obj, Relationship): + return {"data": obj.to_dict()} + if isinstance(obj, Counterparty): + return {"routingNumber": obj.routing_number, "accountNumber": obj.account_number, + "accountType": obj.account_type, "name": obj.name} + if isinstance(obj, Coordinates): + return {"longitude": obj.longitude, "latitude": obj.latitude} + return json.JSONEncoder.default(self, obj) diff --git a/build/lib/unit/models/counterparty.py b/build/lib/unit/models/counterparty.py new file mode 100644 index 00000000..e043b414 --- /dev/null +++ b/build/lib/unit/models/counterparty.py @@ -0,0 +1,172 @@ +import json +from datetime import datetime, date +from typing import Optional +from unit.utils import date_utils +from unit.models import * + + +class CounterpartyDTO(object): + def __init__(self, id: str, created_at: datetime, name: str, routing_number: str, bank: Optional[str], + account_number: str, account_type: str, type: str, permissions: str, + relationships: [Dict[str, Relationship]]): + self.id = id + self.type = "achCounterparty" + self.attributes = {"createdAt": created_at, "name": name, "routingNumber": routing_number, "bank": bank, + "accountNumber": account_number, "accountType": account_type, "type": type, + "permissions": permissions} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CounterpartyDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["name"], + attributes["routingNumber"], attributes.get("bank"), attributes["accountNumber"], + attributes["accountType"], attributes["type"], attributes["permissions"], relationships) + + +class CreateCounterpartyRequest(UnitRequest): + def __init__(self, name: str, routing_number: str, account_number: str, account_type: str, type: str, + relationships: [Dict[str, Relationship]], tags: Optional[object] = None, + idempotency_key: Optional[str] = None): + self.name = name + self.routing_number = routing_number + self.account_number = account_number + self.account_type = account_type + self.type = type + self.relationships = relationships + self.tags = tags + self.idempotency_key = idempotency_key + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "achCounterparty", + "attributes": { + "name": self.name, + "routingNumber": self.routing_number, + "accountNumber": self.account_number, + "accountType": self.account_type, + "type": self.type + }, + "relationships": self.relationships + } + } + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class CreateCounterpartyWithTokenRequest(UnitRequest): + def __init__(self, name: str, type: str, plaid_processor_token: str, relationships: [Dict[str, Relationship]], + verify_name: Optional[bool] = None, permissions: Optional[str] = None, tags: Optional[object] = None, + idempotency_key: Optional[str] = None): + self.name = name + self.type = type + self.plaid_processor_token = plaid_processor_token + self.verify_name = verify_name + self.permissions = permissions + self.relationships = relationships + self.tags = tags + self.idempotency_key = idempotency_key + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "achCounterparty", + "attributes": { + "name": self.name, + "type": self.type, + "plaidProcessorToken": self.plaid_processor_token + }, + "relationships": self.relationships + } + } + + if self.verify_name: + payload["data"]["attributes"]["verifyName"] = self.verify_name + + if self.permissions: + payload["data"]["attributes"]["permissions"] = self.permissions + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class PatchCounterpartyRequest(object): + def __init__(self, counterparty_id: str, plaid_processor_token: str, verify_name: Optional[bool] = None, + permissions: Optional[str] = None, tags: Optional[object] = None): + self.counterparty_id = counterparty_id + self.plaid_processor_token = plaid_processor_token + self.verify_name = verify_name + self.permissions = permissions + self.tags = tags + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "counterparty", + "attributes": { + "plaidProcessorToken": self.plaid_processor_token + } + } + } + + if self.verify_name: + payload["data"]["attributes"]["verifyName"] = self.verify_name + + if self.permissions: + payload["data"]["attributes"]["permissions"] = self.permissions + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class CounterpartyBalanceDTO(object): + def __init__(self, id: str, balance: int, available: int, relationships: [Dict[str, Relationship]]): + self.id = id + self.type = "counterpartyBalance" + self.attributes = {"balance": balance, "available": available} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CounterpartyBalanceDTO(_id, attributes["balance"], attributes["available"], relationships) + + +class ListCounterpartyParams(UnitParams): + def __init__(self, offset: int = 0, limit: int = 100, customer_id: Optional[str] = None, + tags: Optional[object] = None): + self.offset = offset + self.limit = limit + self.customer_id = customer_id + self.tags = tags + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + if self.tags: + parameters["filter[tags]"] = self.tags + return parameters + diff --git a/build/lib/unit/models/customer.py b/build/lib/unit/models/customer.py new file mode 100644 index 00000000..83ebb29b --- /dev/null +++ b/build/lib/unit/models/customer.py @@ -0,0 +1,158 @@ +from unit.utils import date_utils +from unit.models import * + + +class IndividualCustomerDTO(object): + def __init__(self, id: str, created_at: datetime, full_name: FullName, date_of_birth: date, address: Address, + phone: Phone, email: str, ssn: Optional[str], passport: Optional[str], nationality: Optional[str], + authorized_users: [AuthorizedUser], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = 'individualCustomer' + self.attributes = {"createdAt": created_at, "fullName": full_name, "dateOfBirth": date_of_birth, + "address": address, "phone": phone, "email": email, "ssn": ssn, "passport": passport, + "nationality": nationality, "authorizedUsers": authorized_users, "tags": tags} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return IndividualCustomerDTO( + _id, date_utils.to_datetime(attributes["createdAt"]), + FullName.from_json_api(attributes["fullName"]), date_utils.to_date(attributes["dateOfBirth"]), + Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), + attributes["email"], attributes.get("ssn"), attributes.get("passport"), attributes.get("nationality"), + AuthorizedUser.from_json_api(attributes["authorizedUsers"]), attributes.get("tags"), relationships + ) + + +class BusinessCustomerDTO(object): + def __init__(self, id: str, created_at: datetime, name: str, address: Address, phone: Phone, + state_of_incorporation: str, ein: str, entity_type: EntityType, contact: BusinessContact, + authorized_users: [AuthorizedUser], dba: Optional[str], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = 'businessCustomer' + self.attributes = {"createdAt": created_at, "name": name, "address": address, "phone": phone, + "stateOfIncorporation": state_of_incorporation, "ein": ein, "entityType": entity_type, + "contact": contact, "authorizedUsers": authorized_users, "dba": dba, "tags": tags} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BusinessCustomerDTO( + _id, date_utils.to_datetime(attributes["createdAt"]), attributes["name"], + Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), + attributes["stateOfIncorporation"], attributes["ein"], attributes["entityType"], + BusinessContact.from_json_api(attributes["contact"]), + AuthorizedUser.from_json_api(attributes["authorizedUsers"]), + attributes.get("dba"), attributes.get("tags"), relationships) + +CustomerDTO = Union[IndividualCustomerDTO, BusinessCustomerDTO] + + +class PatchIndividualCustomerRequest(UnitRequest): + def __init__(self, customer_id: str, address: Optional[Address] = None, phone: Optional[Phone] = None, + email: Optional[str] = None, dba: Optional[str] = None, + authorized_users: Optional[List[AuthorizedUser]] = None, tags: Optional[Dict[str, str]] = None): + self.customer_id = customer_id + self.address = address + self.phone = phone + self.email = email + self.dba = dba + self.authorized_users = authorized_users + self.tags = tags + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "individualCustomer", + "attributes": {} + } + } + + if self.address: + payload["data"]["attributes"]["address"] = self.address + + if self.phone: + payload["data"]["attributes"]["phone"] = self.phone + + if self.email: + payload["data"]["attributes"]["email"] = self.email + + if self.dba: + payload["data"]["attributes"]["dba"] = self.dba + + if self.authorized_users: + payload["data"]["attributes"]["authorizedUsers"] = self.authorized_users + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class PatchBusinessCustomerRequest(UnitRequest): + def __init__(self, customer_id: str, address: Optional[Address] = None, phone: Optional[Phone] = None, + contact: Optional[BusinessContact] = None, authorized_users: Optional[List[AuthorizedUser]] = None, + tags: Optional[Dict[str, str]] = None): + self.customer_id = customer_id + self.address = address + self.phone = phone + self.contact = contact + self.authorized_users = authorized_users + self.tags = tags + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "businessCustomer", + "attributes": {} + } + } + + if self.address: + payload["data"]["attributes"]["address"] = self.address + + if self.phone: + payload["data"]["attributes"]["phone"] = self.phone + + if self.contact: + payload["data"]["attributes"]["contact"] = self.contact + + if self.authorized_users: + payload["data"]["attributes"]["authorizedUsers"] = self.authorized_users + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class ListCustomerParams(UnitParams): + def __init__(self, offset: int = 0, limit: int = 100, query: Optional[str] = None, email: Optional[str] = None, + tags: Optional[object] = None, sort: Optional[Literal["createdAt", "-createdAt"]] = None): + self.offset = offset + self.limit = limit + self.query = query + self.email = email + self.tags = tags + self.sort = sort + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.query: + parameters["filter[query]"] = self.query + if self.email: + parameters["filter[email]"] = self.email + if self.tags: + parameters["filter[tags]"] = self.tags + if self.sort: + parameters["sort"] = self.sort + return parameters + diff --git a/build/lib/unit/models/customerToken.py b/build/lib/unit/models/customerToken.py new file mode 100644 index 00000000..856ab64a --- /dev/null +++ b/build/lib/unit/models/customerToken.py @@ -0,0 +1,89 @@ +from unit.models import * + +class CustomerTokenDTO(object): + def __init__(self, token: str, expires_in: int): + self.type = "customerBearerToken" + self.attributes = {"token": token, "expiresIn": expires_in} + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CustomerTokenDTO(attributes["token"], attributes["expiresIn"]) + +class CustomerVerificationTokenDTO(object): + def __init__(self, verification_token: str): + self.type = "customerTokenVerification" + self.attributes = {"verificationToken": verification_token} + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CustomerVerificationTokenDTO(attributes["verificationToken"]) + + +class CreateCustomerToken(UnitRequest): + def __init__(self, customer_id: str, scope: str, verification_token: Optional[str] = None, + verification_code: Optional[str] = None, expires_in: Optional[int] = None): + self.customer_id = customer_id + self.scope = scope + self.verification_token = verification_token + self.verification_code = verification_code + self.expires_in = expires_in + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "customerToken", + "attributes": { + "scope": self.scope + } + } + } + + if self.expires_in: + payload["data"]["attributes"]["expiresIn"] = self.expires_in + + if self.verification_token: + payload["data"]["attributes"]["verificationToken"] = self.verification_token + + if self.verification_code: + payload["data"]["attributes"]["verificationCode"] = self.verification_code + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class CreateCustomerTokenVerification(UnitRequest): + + def __init__(self, customer_id: str, channel: str, phone: Optional[Phone] = None, app_hash: Optional[str] = None, + language: Optional[str] = None): + self.customer_id = customer_id + self.channel = channel + self.phone = phone + self.app_hash = app_hash + self.language = language + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "customerTokenVerification", + "attributes": { + "channel": self.channel + } + } + } + + if self.phone: + payload["data"]["attributes"]["phone"] = self.phone + + if self.app_hash: + payload["data"]["attributes"]["appHash"] = self.app_hash + + if self.language: + payload["data"]["attributes"]["language"] = self.language + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + diff --git a/build/lib/unit/models/event.py b/build/lib/unit/models/event.py new file mode 100644 index 00000000..e6fcfa72 --- /dev/null +++ b/build/lib/unit/models/event.py @@ -0,0 +1,463 @@ +import json +from datetime import datetime, date +from typing import Literal, Optional +from unit.utils import date_utils +from unit.models import * + +class BaseEvent(object): + def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.attributes = {"createdAt": created_at, "tags": tags} + self.relationships = relationships + + +class AccountClosedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, close_reason: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'account.closed' + self.attributes["closeReason"] = close_reason + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AccountClosedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["closeReason"], + attributes.get("tags"), relationships) + + +class AccountFrozenEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, freeze_reason: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'account.frozen' + self.attributes["freezeReason"] = freeze_reason + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AccountFrozenEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["freezeReason"], + attributes.get("tags"), relationships) + + +class ApplicationDeniedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'application.denied' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return ApplicationDeniedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), + relationships) + + +class ApplicationPendingReviewEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'application.pendingReview' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return ApplicationPendingReviewEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes.get("tags"), relationships) + + +class ApplicationAwaitingDocumentsEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'application.awaitingDocuments' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return ApplicationAwaitingDocumentsEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes.get("tags"), relationships) + +class AuthorizationCanceledEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, + recurring: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'authorization.canceled' + self.attributes["cardLast4Digits"] = card_last_4_digits + self.attributes["amount"] = amount + self.attributes["recurring"] = recurring + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AuthorizationCanceledEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["amount"], attributes["cardLast4Digits"], + attributes["recurring"], attributes.get("tags"), + relationships) + +class AuthorizationDeclinedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, merchant: Merchant, + reason: str, recurring: str, tags: Optional[Dict[str, str]] = None, + relationships: Optional[Dict[str, Relationship]] = None): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'authorization.declined' + self.attributes["cardLast4Digits"] = card_last_4_digits + self.attributes["amount"] = amount + self.attributes["reason"] = reason + self.attributes["merchant"] = merchant + self.attributes["recurring"] = recurring + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AuthorizationDeclinedEvent( + id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), + amount=attributes["amount"], card_last_4_digits=attributes["cardLast4Digits"], + merchant=Merchant.from_json_api(attributes["merchant"]), reason=attributes.get("reason"), recurring=attributes["recurring"], + tags=attributes.get("tags"), relationships=relationships) + +class AuthorizationCreatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, merchant: Merchant, + recurring: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'authorization.created' + self.attributes["cardLast4Digits"] = card_last_4_digits + self.attributes["amount"] = amount + self.attributes["merchant"] = merchant + self.attributes["recurring"] = recurring + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AuthorizationCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["amount"], attributes["cardLast4Digits"], Merchant.from_json_api(attributes["merchant"]), + attributes["recurring"], attributes.get("tags"), relationships) + +class AuthorizationRequestApprovedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, amount: str, status: str, approved_amount: str, + partial_approval_allowed: str, merchant: Dict[str, str], recurring: str, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'authorizationRequest.approved' + self.attributes["amount"] = amount + self.attributes["status"] = status + self.attributes["approvedAmount"] = approved_amount + self.attributes["partialApprovalAllowed"] = partial_approval_allowed + self.attributes["merchant"] = merchant + self.attributes["recurring"] = recurring + + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AuthorizationRequestApprovedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["amount"], attributes["status"], + attributes["approvedAmount"], attributes["partialApprovalAllowed"], + attributes["merchant"], attributes["recurring"], + attributes.get("tags"), relationships) + + +class AuthorizationRequestDeclinedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, amount: str, status: str, decline_reason: str, + partial_approval_allowed: str, merchant: Dict[str, str], recurring: str, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'authorizationRequest.declined' + self.attributes["amount"] = amount + self.attributes["status"] = status + self.attributes["declineReason"] = decline_reason + self.attributes["partialApprovalAllowed"] = partial_approval_allowed + self.attributes["merchant"] = merchant + self.attributes["recurring"] = recurring + + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AuthorizationRequestDeclinedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["amount"], attributes["status"], + attributes["declineReason"], attributes["partialApprovalAllowed"], + attributes["merchant"], attributes["recurring"], + attributes.get("tags"), relationships) + + +class AuthorizationRequestPendingEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, amount: int, status: str, partial_approval_allowed: str, + card_present: bool, digital_wallet: str, ecommerce: bool, merchant: Merchant, recurring: str, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'authorizationRequest.pending' + self.attributes["amount"] = amount + self.attributes["status"] = status + self.attributes["cardPresent"] = card_present + self.attributes["digitalWallet"] = digital_wallet + self.attributes["ecommerce"] = ecommerce + self.attributes["partialApprovalAllowed"] = partial_approval_allowed + self.attributes["merchant"] = merchant + self.attributes["recurring"] = recurring + + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AuthorizationRequestPendingEvent( + id=_id, + created_at=date_utils.to_datetime(attributes["createdAt"]), + amount=attributes["amount"], + status=attributes["status"], + ecommerce=attributes.get("ecommerce"), + card_present=attributes.get("cardPresent"), + digital_wallet=attributes.get("digitalWallet"), + partial_approval_allowed=attributes["partialApprovalAllowed"], + merchant=Merchant.from_json_api(attributes["merchant"]), + recurring=attributes["recurring"], + tags=attributes.get("tags"), + relationships=relationships + ) + +class CardActivatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'card.activated' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CardActivatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), + relationships) + + +class CardStatusChangedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, new_status: str, previous_status: str, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'card.statusChanged' + self.attributes["newStatus"] = new_status + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CardStatusChangedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["newStatus"], + attributes["previousStatus"], attributes.get("tags"), relationships) + + +class CheckDepositCreatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkDeposit.created' + self.attributes["status"] = status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckDepositCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["status"], attributes.get("tags"), relationships) + +class CheckDepositClearingEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkDeposit.clearing' + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckDepositClearingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["previousStatus"], attributes.get("tags"), relationships) + + +class CheckDepositSentEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkDeposit.sent' + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckDepositSentEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["previousStatus"], attributes.get("tags"), relationships) + + +class CheckDepositReturnedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkDeposit.returned' + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckDepositReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["previousStatus"], attributes.get("tags"), relationships) + + +class CustomerCreatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'customer.created' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CustomerCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes.get("tags"), relationships) + + +class CustomerUpdatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'customer.updated' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CustomerUpdatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes.get("tags"), relationships) + + +class DocumentApprovedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'document.approved' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return DocumentApprovedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes.get("tags"), relationships) + +class DocumentRejectedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, reason: str, reason_code: str, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'document.rejected' + self.attributes["reason"] = reason + self.attributes["reasonCode"] = reason_code + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return DocumentRejectedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["reason"], attributes["reasonCode"], attributes.get("tags"), + relationships) + +class PaymentCreatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'payment.created' + self.attributes["status"] = status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes.get("tags"), relationships) + +class PaymentRejectedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'payment.rejected' + self.attributes["status"] = status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentClearingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes.get("tags"), relationships) + +class PaymentClearingEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'payment.clearing' + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentClearingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], + attributes.get("tags"), relationships) + +class PaymentSentEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'payment.sent' + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentSentEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], + attributes.get("tags"), relationships) + +class PaymentReturnedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'payment.returned' + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], + attributes.get("tags"), relationships) + +class PaymentRejectedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, reason: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'payment.rejected' + self.attributes["reason"] = reason + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentRejectedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["reason"], + attributes.get("tags"), relationships) + +class StatementsCreatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, period: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'statements.created' + self.attributes["period"] = period + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return StatementsCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["period"], + attributes.get("tags"), relationships) + +class TransactionCreatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, summary: str, direction: str, amount: int, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'transaction.created' + self.attributes["summary"] = summary + self.attributes["direction"] = direction + self.attributes["amount"] = amount + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return TransactionCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["summary"], + attributes["direction"], attributes["amount"], attributes.get("tags"), + relationships) + +class AccountReopenedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime,tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'account.reopened' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AccountReopenedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), + relationships) + +EventDTO = Union[AccountClosedEvent, AccountFrozenEvent, ApplicationDeniedEvent, ApplicationAwaitingDocumentsEvent, + ApplicationPendingReviewEvent, CardActivatedEvent, CardStatusChangedEvent, + AuthorizationCreatedEvent, AuthorizationCanceledEvent, AuthorizationDeclinedEvent, + AuthorizationRequestDeclinedEvent, AuthorizationRequestPendingEvent, + AuthorizationRequestApprovedEvent, DocumentApprovedEvent, DocumentRejectedEvent, + CheckDepositCreatedEvent, CheckDepositClearingEvent, CheckDepositSentEvent, + CheckDepositReturnedEvent, CustomerCreatedEvent, PaymentClearingEvent, PaymentSentEvent, + PaymentReturnedEvent, StatementsCreatedEvent, TransactionCreatedEvent, AccountReopenedEvent, RawUnitObject] + + +class ListEventParams(UnitParams): + def __init__(self, limit: int = 100, offset: int = 0, type: Optional[List[str]] = None): + self.limit = limit + self.offset = offset + self.type = type + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.type: + parameters["filter[type][]"] = self.type + return parameters diff --git a/build/lib/unit/models/fee.py b/build/lib/unit/models/fee.py new file mode 100644 index 00000000..296ce187 --- /dev/null +++ b/build/lib/unit/models/fee.py @@ -0,0 +1,50 @@ +import json +from typing import Optional +from unit.models import * + + +class FeeDTO(object): + def __init__(self, id: str, amount: int, description: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = "fee" + self.attributes = {"amount": amount, "description": description, "tags": tags} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return FeeDTO(_id, attributes["amount"], attributes["description"], attributes.get("tags"), relationships) + + +class CreateFeeRequest(object): + def __init__(self, amount: int, description: str, relationships: Optional[Dict[str, Relationship]], + tags: Optional[Dict[str, str]] = None, idempotency_key: Optional[str] = None): + self.amount = amount + self.description = description + self.tags = tags + self.idempotency_key = idempotency_key + self.relationships = relationships + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "fee", + "attributes": { + "amount": self.amount, + "description": self.description + }, + "relationships": self.relationships + } + } + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.tags + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + diff --git a/build/lib/unit/models/institution.py b/build/lib/unit/models/institution.py new file mode 100644 index 00000000..25599e68 --- /dev/null +++ b/build/lib/unit/models/institution.py @@ -0,0 +1,17 @@ +import json +from typing import Optional +from unit.models import * + + +class InstitutionDTO(object): + def __init__(self, routing_number: str, name: str, is_ach_supported: bool, is_wire_supported: bool, + address: Optional[Address] = None): + self.type = "institution" + self.attributes = {"routingNumber": routing_number, "name": name, "address": address, + "isACHSupported": is_ach_supported, "isWireSupported": is_wire_supported} + + def from_json_api(_id, _type, attributes, relationships): + return InstitutionDTO( + attributes["routingNumber"], attributes["name"], attributes["isACHSupported"], + attributes["isWireSupported"], attributes.get("address")) + diff --git a/build/lib/unit/models/payment.py b/build/lib/unit/models/payment.py new file mode 100644 index 00000000..502fb3f0 --- /dev/null +++ b/build/lib/unit/models/payment.py @@ -0,0 +1,450 @@ +from unit.utils import date_utils +from unit.models import * + +PaymentTypes = Literal["AchPayment", "BookPayment", "WirePayment", "BillPayment"] +PaymentDirections = Literal["Debit", "Credit"] +PaymentStatus = Literal["Pending", "Rejected", "Clearing", "Sent", "Canceled", "Returned"] + +class BasePayment(object): + def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: PaymentDirections, description: str, + amount: int, reason: Optional[str], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.attributes = {"createdAt": created_at, "status": status, "direction": direction, + "description": description, "amount": amount, "reason": reason, "tags": tags} + self.relationships = relationships + +class AchPaymentDTO(BasePayment): + def __init__(self, id: str, created_at: datetime, status: PaymentStatus, counterparty: Counterparty, direction: str, + description: str, amount: int, addenda: Optional[str], reason: Optional[str], + settlement_date: Optional[datetime], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BasePayment.__init__(self, id, created_at, status, direction, description, amount, reason, tags, relationships) + self.type = 'achPayment' + self.attributes["counterparty"] = counterparty + self.attributes["addenda"] = addenda + self.settlement_date = settlement_date + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + settlement_date = date_utils.to_date(attributes.get("settlementDate")) if attributes.get("settlementDate") else None + return AchPaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes["counterparty"], attributes["direction"], attributes["description"], + attributes["amount"], attributes.get("addenda"), attributes.get("reason"), settlement_date, + attributes.get("tags"), relationships) + +class SimulateIncomingAchPaymentDTO(BasePayment): + def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: str, + description: str, amount: int, reason: Optional[str], + settlement_date: Optional[datetime], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BasePayment.__init__(self, id, created_at, direction, description, amount, reason, tags, relationships) + self.type = 'achPayment' + self.attributes["status"] = status + self.settlement_date = settlement_date + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BasePayment( + _id, + date_utils.to_datetime(attributes["createdAt"]), + attributes.get("direction"), + attributes["description"], + attributes["amount"], + attributes.get("reason"), + attributes.get("tags"), + relationships + ) + +class SimulateAchPaymentDTO(object): + def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: str, + description: str, amount: int, reason: Optional[str], + settlement_date: Optional[datetime], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BasePayment.__init__(self, id, created_at, direction, description, amount, reason, tags, relationships) + self.type = 'achPayment' + self.attributes["status"] = status + self.settlement_date = settlement_date + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BasePayment( + _id, + date_utils.to_datetime(attributes["createdAt"]), + attributes.get("status"), + attributes.get("direction"), + attributes["description"], + attributes["amount"], + attributes.get("reason"), + attributes.get("tags"), + relationships + ) + +class BookPaymentDTO(BasePayment): + def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: Optional[str], description: str, + amount: int, reason: Optional[str], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BasePayment.__init__(self, id, created_at, status, direction, description, amount, reason, tags, relationships) + self.type = 'bookPayment' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BookPaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes.get("direction"), attributes["description"], attributes["amount"], + attributes.get("reason"), attributes.get("tags"), relationships) + +class WirePaymentDTO(BasePayment): + def __init__(self, id: str, created_at: datetime, status: PaymentStatus, counterparty: WireCounterparty, + direction: str, description: str, amount: int, reason: Optional[str], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BasePayment.__init__(self, id, created_at, direction, description, amount, reason, tags, relationships) + self.type = "wirePayment" + self.attributes["counterparty"] = counterparty + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return WirePaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + WireCounterparty.from_json_api(attributes["counterparty"]), attributes["direction"], + attributes["description"], attributes["amount"], attributes.get("reason"), + attributes.get("tags"), relationships) + +class BillPaymentDTO(BasePayment): + def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: str, description: str, + amount: int, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BasePayment.__init__(self, id, created_at, status, direction, description, amount, tags, relationships) + self.type = 'billPayment' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BillPaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes["direction"], attributes["description"], attributes["amount"], + attributes.get("reason"), attributes.get("tags"), relationships) + +PaymentDTO = Union[AchPaymentDTO, BookPaymentDTO, WirePaymentDTO, BillPaymentDTO] + +AchReceivedPaymentStatus = Literal["Pending", "Advanced", "Completed", "Returned"] + +class AchReceivedPaymentDTO(object): + def __init__(self, id: str, created_at: datetime, status: AchReceivedPaymentStatus, was_advanced: bool, + completion_date: datetime, return_reason: Optional[str], amount: int, description: str, + addenda: Optional[str], company_name: str, counterparty_routing_number: str, trace_number: str, + sec_code: Optional[str], tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + self.type = "achReceivedPayment" + self.attributes = {"createdAt": created_at, "status": status, "wasAdvanced": was_advanced, + "completionDate": completion_date, "returnReason": return_reason, "description": description, + "amount": amount, "addenda": addenda, "companyName": company_name, + "counterpartyRoutingNumber": counterparty_routing_number, "traceNumber": trace_number, + "secCode": sec_code, "tags": tags} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AchReceivedPaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes["wasAdvanced"], attributes["completionDate"], + attributes.get("returnReason"),attributes["amount"], attributes["description"], + attributes.get("addenda"), attributes.get("companyName"), + attributes.get("counterpartyRoutingNumber"), attributes.get("traceNumber"), + attributes.get("secCode"), attributes.get("tags"), relationships) + +class CreatePaymentBaseRequest(UnitRequest): + def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], + idempotency_key: Optional[str], tags: Optional[Dict[str, str]], direction: str = "Credit", + type: str = "achPayment"): + self.type = type + self.amount = amount + self.description = description + self.direction = direction + self.idempotency_key = idempotency_key + self.tags = tags + self.relationships = relationships + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": self.type, + "attributes": { + "amount": self.amount, + "direction": self.direction, + "description": self.description + }, + "relationships": self.relationships + } + } + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + +class CreateInlinePaymentRequest(CreatePaymentBaseRequest): + def __init__(self, amount: int, description: str, counterparty: Counterparty, relationships: Dict[str, Relationship], + addenda: Optional[str], idempotency_key: Optional[str], tags: Optional[Dict[str, str]], + direction: str = "Credit"): + CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction) + self.counterparty = counterparty + self.addenda = addenda + + def to_json_api(self) -> Dict: + payload = CreatePaymentBaseRequest.to_json_api(self) + + payload["data"]["attributes"]["counterparty"] = self.counterparty + + if self.addenda: + payload["data"]["attributes"]["addenda"] = self.addenda + + return payload + +class CreateLinkedPaymentRequest(CreatePaymentBaseRequest): + def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], addenda: Optional[str], + verify_counterparty_balance: Optional[bool], idempotency_key: Optional[str], + tags: Optional[Dict[str, str]], direction: str = "Credit"): + CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction) + self.addenda = addenda + self.verify_counterparty_balance = verify_counterparty_balance + + def to_json_api(self) -> Dict: + payload = CreatePaymentBaseRequest.to_json_api(self) + + if self.addenda: + payload["data"]["attributes"]["addenda"] = self.addenda + + if self.verify_counterparty_balance: + payload["data"]["attributes"]["verifyCounterpartyBalance"] = self.verify_counterparty_balance + + return payload + +class SimulateIncomingAchRequest(CreatePaymentBaseRequest): + def __init__( + self, amount: int, description: str, + relationships: Dict[str, Relationship], + direction: str = "Credit" + ): + CreatePaymentBaseRequest.__init__(self, amount, description, relationships, None, None, direction) + self.verify_counterparty_balance = False + + def to_json_api(self) -> Dict: + payload = CreatePaymentBaseRequest.to_json_api(self) + + return payload + +class SimulateTransmitAchRequest(UnitRequest): + def __init__( + self, payment_id: int + ): + self.type = "transmitAchPayment" + self.id = payment_id + self.relationships = { + "payment": { + "data": { + "type": "achPayment", + "id": self.id + } + } + } + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": self.type, + "relationships": self.relationships + } + } + return payload + +class SimulateClearAchRequest(UnitRequest): + def __init__( + self, payment_id: int + ): + self.type = "clearAchPayment" + self.id = payment_id + self.relationships = { + "payment": { + "data": { + "type": "achPayment", + "id": self.id + } + } + } + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": self.type, + "relationships": self.relationships + } + } + return payload + +class CreateVerifiedPaymentRequest(CreatePaymentBaseRequest): + def __init__(self, amount: int, description: str, plaid_processor_token: str, relationships: Dict[str, Relationship], + counterparty_name: Optional[str], verify_counterparty_balance: Optional[bool], + idempotency_key: Optional[str], tags: Optional[Dict[str, str]], direction: str = "Credit"): + CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags) + self.plaid_Processor_token = plaid_Processor_token + self.counterparty_name = counterparty_name + self.verify_counterparty_balance = verify_counterparty_balance + + def to_json_api(self) -> Dict: + payload = CreatePaymentBaseRequest.to_json_api(self) + payload["data"]["attributes"]["counterparty"] = self.counterparty + payload["data"]["attributes"]["plaidProcessorToken"] = self.plaid_processor_token + + if counterparty_name: + payload["data"]["attributes"]["counterpartyName"] = self.counterparty_name + + if verify_counterparty_balance: + payload["data"]["attributes"]["verifyCounterpartyBalance"] = self.verify_counterparty_balance + + return payload + +class CreateBookPaymentRequest(CreatePaymentBaseRequest): + def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], + idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, + direction: str = "Credit"): + super().__init__(amount, description, relationships, idempotency_key, tags, direction, "bookPayment") + +class CreateWirePaymentRequest(CreatePaymentBaseRequest): + def __init__(self, amount: int, description: str, counterparty: WireCounterparty, + relationships: Dict[str, Relationship], idempotency_key: Optional[str], tags: Optional[Dict[str, str]], + direction: str = "Credit"): + CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction, + "wirePayment") + self.counterparty = counterparty + + def to_json_api(self) -> Dict: + payload = CreatePaymentBaseRequest.to_json_api(self) + payload["data"]["attributes"]["counterparty"] = self.counterparty + return payload + +CreatePaymentRequest = Union[CreateInlinePaymentRequest, CreateLinkedPaymentRequest, CreateVerifiedPaymentRequest, + CreateBookPaymentRequest, CreateWirePaymentRequest] + +class PatchAchPaymentRequest(object): + def __init__(self, payment_id: str, tags: Dict[str, str]): + self.payment_id = payment_id + self.tags = tags + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "achPayment", + "attributes": { + "tags": self.tags + } + } + } + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + +class PatchBookPaymentRequest(object): + def __init__(self, payment_id: str, tags: Dict[str, str]): + self.payment_id = payment_id + self.tags = tags + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "bookPayment", + "attributes": { + "tags": self.tags + } + } + } + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + +PatchPaymentRequest = Union[PatchAchPaymentRequest, PatchBookPaymentRequest] + +class ListPaymentParams(UnitParams): + def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, + customer_id: Optional[str] = None, tags: Optional[object] = None, + status: Optional[List[PaymentStatus]] = None, type: Optional[List[PaymentTypes]] = None, + direction: Optional[List[PaymentDirections]] = None, since: Optional[str] = None, + until: Optional[str] = None, sort: Optional[Literal["createdAt", "-createdAt"]] = None, + include: Optional[str] = None): + self.limit = limit + self.offset = offset + self.account_id = account_id + self.customer_id = customer_id + self.tags = tags + self.status = status + self.type = type + self.direction = direction + self.since = since + self.until = until + self.sort = sort + self.include = include + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + if self.account_id: + parameters["filter[accountId]"] = self.account_id + if self.tags: + parameters["filter[tags]"] = self.tags + if self.status: + for idx, status_filter in enumerate(self.status): + parameters[f"filter[status][{idx}]"] = status_filter + if self.type: + for idx, type_filter in enumerate(self.type): + parameters[f"filter[type][{idx}]"] = type_filter + if self.direction: + for idx, direction_filter in enumerate(self.direction): + parameters[f"filter[direction][{idx}]"] = direction_filter + if self.since: + parameters["filter[since]"] = self.since + if self.until: + parameters["filter[until]"] = self.until + if self.sort: + parameters["sort"] = self.sort + if self.include: + parameters["include"] = self.include + return parameters + +class ListReceivedPaymentParams(UnitParams): + def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, + customer_id: Optional[str] = None, tags: Optional[object] = None, + status: Optional[List[AchReceivedPaymentStatus]] = None, + direction: Optional[List[PaymentDirections]] = None, include_completed: Optional[bool] = None, + sort: Optional[Literal["createdAt", "-createdAt"]] = None, include: Optional[str] = None): + self.limit = limit + self.offset = offset + self.account_id = account_id + self.customer_id = customer_id + self.tags = tags + self.status = status + self.include_completed = include_completed + self.sort = sort + self.include = include + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + if self.account_id: + parameters["filter[accountId]"] = self.account_id + if self.tags: + parameters["filter[tags]"] = self.tags + if self.include_completed: + parameters["filter[includeCompleted]"] = self.include_completed + if self.status: + for idx, status_filter in enumerate(self.status): + parameters[f"filter[status][{idx}]"] = status_filter + if self.sort: + parameters["sort"] = self.sort + if self.include: + parameters["include"] = self.include + return parameters diff --git a/build/lib/unit/models/repayment.py b/build/lib/unit/models/repayment.py new file mode 100644 index 00000000..3554b94b --- /dev/null +++ b/build/lib/unit/models/repayment.py @@ -0,0 +1,114 @@ +try: + from typing import Optional, Dict, Union, List, Literal +except ImportError: + from typing import Optional, Dict, Union, List + from typing_extensions import Literal + +from unit.models import UnitDTO, extract_attributes, UnitRequest, Relationship, UnitParams +from unit.utils import date_utils + + +class BaseRepayment(UnitDTO): + def __init__(self, _id, _type, attributes, relationships): + self.id = _id + self.type = _type + self.attributes = extract_attributes(["amount", "status", "tags"], attributes) + attrs = {"createdAt": date_utils.to_datetime(attributes["createdAt"]), + "updatedAt": date_utils.to_datetime(attributes["updatedAt"])} + self.attributes.update(attrs) + self.relationships = relationships + + +class BookRepaymentDTO(BaseRepayment): + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BookRepaymentDTO(_id, _type, attributes, relationships) + + +class AchRepaymentDTO(BaseRepayment): + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AchRepaymentDTO(_id, _type, attributes, relationships) + + +RepaymentDTO = Union[BookRepaymentDTO, AchRepaymentDTO] + + +class CreateBookRepaymentRequest(UnitRequest): + def __init__(self, description: str, amount: int, relationships: Dict[str, Relationship], + transaction_summary_override: Optional[str] = None, tags: Optional[Dict[str, str]] = None, + idempotency_key: Optional[str] = None): + self.description = description + self.amount = amount + self.transaction_summary_override = transaction_summary_override + self.tags = tags + self.idempotency_key = idempotency_key + self.relationships = relationships + + def to_json_api(self) -> Dict: + return super().to_payload("bookRepayment", self.relationships) + + +class CreateAchRepaymentRequest(UnitRequest): + def __init__(self, description: str, amount: int, relationships: Dict[str, Relationship], + addenda: Optional[str] = None, tags: Optional[Dict[str, str]] = None, same_day: Optional[bool] = None, + idempotency_key: Optional[str] = None): + self.description = description + self.amount = amount + self.addenda = addenda + self.tags = tags + self.same_day = same_day + self.idempotency_key = idempotency_key + self.relationships = relationships + + def to_json_api(self) -> Dict: + return super().to_payload("achRepayment", self.relationships) + + +CreateRepaymentRequest = Union[CreateBookRepaymentRequest, CreateAchRepaymentRequest] + +RepaymentStatus = Literal["Pending", "PendingReview", "Returned", "Sent", "Rejected"] +RepaymentType = Literal["bookRepayment", "achRepayment"] + + +class ListRepaymentParams(UnitParams): + def __init__( + self, + limit: int = 100, + offset: int = 0, + account_id: Optional[str] = None, + credit_account_id: Optional[str] = None, + customer_id: Optional[str] = None, + status: Optional[List[RepaymentStatus]] = None, + _type: Optional[List[str]] = None, + ): + self.limit = limit + self.offset = offset + self.account_id = account_id + self.credit_account_id = credit_account_id + self.customer_id = customer_id + self.status = status + self.type = _type + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + + if self.account_id: + parameters["filter[accountId]"] = self.account_id + + if self.credit_account_id: + parameters["filter[creditAccountId]"] = self.credit_account_id + + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + + if self.status: + for idx, status_filter in enumerate(self.status): + parameters[f"filter[status][{idx}]"] = status_filter + + if self.type: + for idx, type_filter in enumerate(self.type): + parameters[f"filter[type][{idx}]"] = type_filter + + return parameters + diff --git a/build/lib/unit/models/returnAch.py b/build/lib/unit/models/returnAch.py new file mode 100644 index 00000000..6c972246 --- /dev/null +++ b/build/lib/unit/models/returnAch.py @@ -0,0 +1,29 @@ +import json +from typing import Literal +from unit.models import * + +AchReturnReason = Literal["InsufficientFunds", "Unauthorized", "UncollectedFunds"] + + +class ReturnReceivedAchTransactionRequest(UnitRequest): + def __init__(self, transaction_id: str, reason: AchReturnReason, relationships: [Dict[str, Relationship]]): + self.transaction_id = transaction_id + self.reason = reason + self.relationships = relationships + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "returnAch", + "attributes": { + "reason": self.reason + }, + "relationships": self.relationships + } + } + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + diff --git a/build/lib/unit/models/reward.py b/build/lib/unit/models/reward.py new file mode 100644 index 00000000..07f1535b --- /dev/null +++ b/build/lib/unit/models/reward.py @@ -0,0 +1,137 @@ +import json +from typing import Optional, Literal, Dict, List +from datetime import datetime + +from unit.models import Relationship, UnitRequest, UnitParams + + +SORT_ORDERS = Literal["created_at", "-created_at"] +RELATED_RESOURCES = Literal["customer", "account", "transaction"] + + +class RewardDTO(object): + def __init__(self, id: str, amount: int, description: str, status: str, tags: Optional[Dict[str, str]] = None, + relationships: Optional[Dict[str, Relationship]] = None): + self.id = id + self.type = "reward" + self.attributes = {"amount": amount, "description": description, "status": status, "tags": tags} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, attributes, relationships): + return RewardDTO(_id, attributes["amount"], attributes["description"], attributes["status"], attributes.get("tags"), relationships) + + +class CreateRewardRequest(UnitRequest): + def __init__( + self, + amount: int, + description: str, + receiving_account_id: str, + rewarded_transaction_id: Optional[str] = None, + funding_account_id: Optional[str] = None, + idempotency_key: Optional[str] = None, + tags: Optional[Dict[str, str]] = None + ): + self.type = "reward" + self.amount = amount + self.description = description + self.rewarded_transaction_id = rewarded_transaction_id + self.receiving_account_id = receiving_account_id + self.funding_account_id = funding_account_id + self.idempotency_key = idempotency_key + self.tags = tags + + self.relationships = { + "receivingAccount": Relationship(_type="depositAccount", _id=self.receiving_account_id) + } + if self.rewarded_transaction_id: + self.relationships["rewardedTransaction"] = Relationship(_type="transaction", _id=self.rewarded_transaction_id) + + if self.funding_account_id: + self.relationships["fundingAccount"] = Relationship(_type="depositAccount", _id=self.funding_account_id) + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": self.type, + "attributes": { + "amount": self.amount, + "description": self.description + }, + "relationships": self.relationships + } + } + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class ListRewardsParams(UnitParams): + def __init__( + self, + limit: int = 100, + offset: int = 0, + transaction_id: Optional[str] = None, + rewarded_transaction_id: Optional[str] = None, + receiving_account_id: Optional[str] = None, + customer_id: Optional[str] = None, + card_id: Optional[str] = None, + status: Optional[str] = None, + since: Optional[datetime] = None, + until: Optional[datetime] = None, + sort: Optional[SORT_ORDERS] = None, + include: Optional[List[RELATED_RESOURCES]] = None, + ): + self.limit = limit + self.offset = offset + self.transaction_id = transaction_id + self.rewarded_transaction_id = rewarded_transaction_id + self.receiving_account_id = receiving_account_id + self.customer_id = customer_id + self.card_id = card_id + self.status = status + self.since = since + self.until = until + self.sort = sort + self.include = include + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + + if self.transaction_id: + parameters["filter[transactionId]"] = self.transaction_id + + if self.rewarded_transaction_id: + parameters["filter[rewardedTransactionId]"] = self.rewarded_transaction_id + + if self.receiving_account_id: + parameters["filter[receivingAccountId]"] = self.receiving_account_id + + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + + if self.card_id: + parameters["filter[cardId]"] = self.card_id + + if self.status: + parameters["filter[status]"] = self.status + + if self.since: + parameters["filter[since]"] = self.since + + if self.unitl: + parameters["filter[until]"] = self.until + + if self.sort: + parameters["sort"] = self.sort + + return parameters diff --git a/build/lib/unit/models/statement.py b/build/lib/unit/models/statement.py new file mode 100644 index 00000000..590e2604 --- /dev/null +++ b/build/lib/unit/models/statement.py @@ -0,0 +1,46 @@ +import json +from unit.models import * + + +class StatementDTO(object): + def __init__(self, id: str, _type: str, period: str, relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = _type + self.attributes = {"period": period} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return StatementDTO(_id, _type, attributes["period"], relationships) + + +OutputType = Literal["html", "pdf"] + +class GetStatementParams(UnitRequest): + def __init__(self, statement_id: str, output_type: Optional[OutputType] = "html", language: Optional[str] = "en", + customer_id: Optional[str] = None): + self.statement_id = statement_id + self.output_type = output_type + self.language = language + self.customer_id = customer_id + + +class ListStatementParams(UnitParams): + def __init__(self, limit: int = 100, offset: int = 0, customer_id: Optional[str] = None, + account_id: Optional[str] = None, sort: Optional[Literal["period", "-period"]] = None): + self.limit = limit + self.offset = offset + self.customer_id = customer_id + self.account_id = account_id + self.sort = sort + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + if self.account_id: + parameters["filter[accountId]"] = self.account_id + if self.sort: + parameters["sort"] = self.sort + return parameters + diff --git a/build/lib/unit/models/transaction.py b/build/lib/unit/models/transaction.py new file mode 100644 index 00000000..58aec0fa --- /dev/null +++ b/build/lib/unit/models/transaction.py @@ -0,0 +1,466 @@ +from unit.utils import date_utils +from unit.models import * + + +class BaseTransactionDTO(object): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, + summary: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.attributes = {"createdAt": created_at, "direction": direction, "amount": amount, "balance": balance, + "summary": summary, "tags": tags} + self.relationships = relationships + + +class OriginatedAchTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, + summary: str, description: str, addenda: Optional[str], counterparty: Counterparty, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'originatedAchTransaction' + self.attributes["description"] = description + self.attributes["addenda"] = addenda + self.attributes["counterparty"] = counterparty + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return OriginatedAchTransactionDTO( + _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], attributes["description"], + attributes.get("addenda"), Counterparty.from_json_api(attributes["counterparty"]), + attributes.get("tags"), relationships) + + +class ReceivedAchTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, + summary: str, description: str, addenda: Optional[str], company_name: str, + counterparty_routing_number: str, trace_number: Optional[str], sec_code: Optional[str], + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'receivedAchTransaction' + self.attributes["description"] = description + self.attributes["addenda"] = addenda + self.attributes["companyName"] = company_name + self.attributes["counterpartyRoutingNumber"] = counterparty_routing_number + self.attributes["traceNumber"] = trace_number + self.attributes["secCode"] = sec_code + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return ReceivedAchTransactionDTO( + _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], attributes["description"], + attributes.get("addenda"), attributes["companyName"], attributes["counterpartyRoutingNumber"], + attributes.get("traceNumber"), attributes.get("secCode"), attributes.get("tags"), relationships) + + +class ReturnedAchTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, + summary: str, company_name: str, counterparty_name: str, counterparty_routing_number: str, reason: str, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'returnedAchTransaction' + self.attributes["companyName"] = company_name + self.attributes["counterpartyName"] = counterparty_name + self.attributes["counterpartyRoutingNumber"] = counterparty_routing_number + self.attributes["reason"] = reason + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return ReturnedAchTransactionDTO( + _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], attributes["amount"], + attributes["balance"], attributes["summary"], attributes["companyName"], attributes["counterpartyName"], + attributes["counterpartyRoutingNumber"], attributes["reason"], attributes.get("tags"), relationships) + + +class ReturnedReceivedAchTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, + company_name: str, reason: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'returnedReceivedAchTransaction' + self.attributes["companyName"] = company_name + self.attributes["reason"] = reason + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return ReturnedReceivedAchTransactionDTO( + _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], attributes["companyName"], + attributes["reason"], attributes.get("tags"), relationships) + + +class DishonoredAchTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, + company_name: str, counterparty_routing_number: str, reason: str, trace_number: Optional[str], + sec_code: Optional[str], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'dishonoredAchTransaction' + self.attributes["companyName"] = company_name + self.attributes["counterpartyRoutingNumber"] = counterparty_routing_number + self.attributes["traceNumber"] = trace_number + self.attributes["reason"] = reason + self.attributes["secCode"] = sec_code + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return DishonoredAchTransactionDTO( + _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], attributes["companyName"], + attributes["counterpartyRoutingNumber"], attributes["reason"], attributes.get("traceNumber"), + attributes.get("secCode"), attributes.get("tags"), relationships) + + +class BookTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, + summary: str, counterparty: Counterparty, tags: Optional[Dict[str, str]] = None, + relationships: Optional[Dict[str, Relationship]] = None): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'bookTransaction' + self.attributes["counterparty"] = counterparty + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BookTransactionDTO( + id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), direction=attributes["direction"], + amount=attributes["amount"], balance=attributes["balance"], summary=attributes["summary"], + counterparty=Counterparty.from_json_api(attributes["counterparty"]), + tags=attributes.get("tags"), relationships=relationships + ) + + +class PurchaseTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, + summary: str, card_last_4_digits: str, merchant: Merchant, coordinates: Coordinates, recurring: bool, + interchange: Optional[int], ecommerce: bool, card_present: bool, payment_method: Optional[str], + digital_wallet: Optional[str], card_verification_data, card_network: Optional[str], + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'purchaseTransaction' + self.attributes["cardLast4Digits"] = card_last_4_digits + self.attributes["merchant"] = merchant + self.attributes["coordinates"] = coordinates + self.attributes["recurring"] = recurring + self.attributes["interchange"] = interchange + self.attributes["ecommerce"] = ecommerce + self.attributes["cardPresent"] = card_present + self.attributes["paymentMethod"] = payment_method + self.attributes["digitalWallet"] = digital_wallet + self.attributes["cardVerificationData"] = card_verification_data + self.attributes["cardNetwork"] = card_network + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PurchaseTransactionDTO( + _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], attributes["cardLast4Digits"], + Merchant.from_json_api(attributes["merchant"]), Coordinates.from_json_api(attributes.get("coordinates")), + attributes["recurring"], attributes.get("interchange"), attributes.get("ecommerce"), + attributes.get("cardPresent"), attributes.get("paymentMethod"), attributes.get("digitalWallet"), + attributes.get("cardVerificationData"), attributes.get("cardNetwork"), attributes.get("tags"), + relationships) + + +class AtmTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, + summary: str, card_last_4_digits: str, atm_name: str, atm_location: Optional[str], surcharge: int, + interchange: Optional[int], card_network: Optional[str], + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'atmTransaction' + self.attributes["cardLast4Digits"] = card_last_4_digits + self.attributes["atmName"] = atm_name + self.attributes["atmLocation"] = atm_location + self.attributes["surcharge"] = surcharge + self.attributes["interchange"] = interchange + self.attributes["cardNetwork"] = card_network + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AtmTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], + attributes["cardLast4Digits"], attributes["atmName"], attributes.get("atmLocation"), + attributes["surcharge"], attributes.get("interchange"), attributes.get("cardNetwork"), + attributes.get("tags"), relationships) + + +class FeeTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, + summary: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'feeTransaction' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return FeeTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], + attributes.get("tags"), relationships) + + +class CardTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, + summary: str, card_last_4_digits: str, merchant: Merchant, recurring: Optional[bool], + interchange: Optional[int], payment_method: Optional[str], digital_wallet: Optional[str], + card_verification_data: Optional[Dict], card_network: Optional[str], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'cardTransaction' + self.attributes["cardLast4Digits"] = card_last_4_digits + self.attributes["merchant"] = merchant + self.attributes["recurring"] = recurring + self.attributes["interchange"] = interchange + self.attributes["paymentMethod"] = payment_method + self.attributes["digitalWallet"] = digital_wallet + self.attributes["cardVerificationData"] = card_verification_data + self.attributes["cardNetwork"] = card_network + + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CardTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], + attributes["cardLast4Digits"], Merchant.from_json_api(attributes["merchant"]), + attributes.get("recurring"), attributes.get("interchange"), + attributes.get("paymentMethod"), attributes.get("digitalWallet"), + attributes.get("cardVerificationData"), attributes.get("cardNetwork"), + attributes.get("tags"), relationships) + + +class CardReversalTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, + summary: str, card_last_4_digits: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'cardReversalTransaction' + self.attributes["cardLast4Digits"] = card_last_4_digits + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CardReversalTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], + attributes["cardLast4Digits"], attributes.get("tags"), relationships) + + +class WireTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, + summary: str, counterparty: Counterparty, description: str, + originator_to_beneficiary_information: str, sender_reference: str, + reference_for_beneficiary: str, beneficiary_information: str, + beneficiary_advice_information: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'wireTransaction' + self.attributes["description"] = description + self.attributes["counterparty"] = counterparty + self.attributes["originatorToBeneficiaryInformation"] = originator_to_beneficiary_information + self.attributes["senderReference"] = sender_reference + self.attributes["referenceForBeneficiary"] = reference_for_beneficiary + self.attributes["beneficiaryInformation"] = beneficiary_information + self.attributes["beneficiaryAdviceInformation"] = beneficiary_advice_information + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return WireTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], + Counterparty.from_json_api(attributes["counterparty"]), attributes["description"], + attributes.get("originatorToBeneficiaryInformation"), attributes.get("senderReference"), + attributes.get("referenceForBeneficiary"), attributes.get("beneficiaryInformation"), + attributes.get("beneficiaryAdviceInformation"), attributes.get("tags"), relationships) + + +class ReleaseTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, sender_name: str, sender_address: Address, + sender_account_number: str, counterparty: Counterparty, amount: int, direction: str, + description: str, balance: int, summary: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'releaseTransaction' + self.attributes["description"] = description + self.attributes["senderName"] = sender_name + self.attributes["senderAddress"] = sender_address + self.attributes["senderAccountNumber"] = sender_account_number + self.attributes["counterparty"] = counterparty + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return ReleaseTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["senderName"], + Address.from_json_api(attributes["senderAddress"]), + attributes["senderAccountNumber"], + Counterparty.from_json_api(attributes["counterparty"]), attributes["amount"], + attributes["direction"], attributes["description"], attributes["balance"], + attributes["summary"], attributes.get("tags"), relationships) + + +class AdjustmentTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, + description: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'adjustmentTransaction' + self.attributes["description"] = description + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AdjustmentTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], + attributes["summary"], attributes["description"], attributes.get("tags"), + relationships) + + +class InterestTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'interestTransaction' + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return InterestTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], + attributes.get("tags"), relationships) + + +class DisputeTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, dispute_id: str, + summary: str, reason: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'disputeTransaction' + self.attributes["disputeId"] = dispute_id + self.attributes["reason"] = reason + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return DisputeTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["disputeId"], + attributes["summary"], attributes["reason"], attributes.get("tags"), relationships) + + +class CheckDepositTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'checkDepositTransaction' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckDepositTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], + attributes.get("tags"), relationships) + + +class ReturnedCheckDepositTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, + reason: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'returnedCheckDepositTransaction' + self.attributes["reason"] = reason + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return ReturnedCheckDepositTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], + attributes["reason"], attributes.get("tags"), relationships) + +class PaymentAdvanceTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, + reason: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'paymentAdvanceTransaction' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PaymentAdvanceTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], + attributes.get("tags"), relationships) + +class RepaidPaymentAdvanceTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, + reason: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'repaidPaymentAdvanceTransaction' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return RepaidPaymentAdvanceTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], + attributes.get("tags"), relationships) + +TransactionDTO = Union[OriginatedAchTransactionDTO, ReceivedAchTransactionDTO, ReturnedAchTransactionDTO, + ReturnedReceivedAchTransactionDTO, DishonoredAchTransactionDTO, BookTransactionDTO, + PurchaseTransactionDTO, AtmTransactionDTO, FeeTransactionDTO, CardTransactionDTO, + CardReversalTransactionDTO, WireTransactionDTO, ReleaseTransactionDTO, AdjustmentTransactionDTO, + InterestTransactionDTO, DisputeTransactionDTO, CheckDepositTransactionDTO, + ReturnedCheckDepositTransactionDTO, PaymentAdvanceTransactionDTO, + RepaidPaymentAdvanceTransactionDTO] + + +class PatchTransactionRequest(BaseTransactionDTO, UnitRequest): + def __init__(self, account_id: str, transaction_id: str, tags: Optional[Dict[str, str]] = None): + self.account_id = account_id + self.transaction_id = transaction_id + self.tags = tags + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "transaction", + "attributes": {} + } + } + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload + + +class ListTransactionParams(UnitParams): + def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, + customer_id: Optional[str] = None, query: Optional[str] = None, tags: Optional[object] = None, + since: Optional[str] = None, until: Optional[str] = None, card_id: Optional[str] = None, + type: Optional[List[str]] = None, exclude_fees: Optional[bool] = None, + sort: Optional[Literal["createdAt", "-createdAt"]] = None, include: Optional[str] = None): + self.limit = limit + self.offset = offset + self.account_id = account_id + self.customer_id = customer_id + self.query = query + self.tags = tags + self.since = since + self.until = until + self.card_id = card_id + self.type = type + self.exclude_fees = exclude_fees + self.sort = sort + self.include = include + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + if self.account_id: + parameters["filter[accountId]"] = self.account_id + if self.query: + parameters["filter[query]"] = self.query + if self.tags: + parameters["filter[tags]"] = self.tags + if self.since: + parameters["filter[since]"] = self.since + if self.until: + parameters["filter[until]"] = self.until + if self.card_id: + parameters["filter[cardId]"] = self.card_id + if self.type: + for idx, type_filter in enumerate(self.type): + parameters[f"filter[type][{idx}]"] = type_filter + if self.exclude_fees: + parameters["filter[excludeFees]"] = self.exclude_fees + if self.sort: + parameters["sort"] = self.sort + if self.include: + parameters["include"] = self.include + return parameters \ No newline at end of file diff --git a/build/lib/unit/models/webhook.py b/build/lib/unit/models/webhook.py new file mode 100644 index 00000000..c19d82c1 --- /dev/null +++ b/build/lib/unit/models/webhook.py @@ -0,0 +1,92 @@ +import json +from datetime import datetime, date +from unit.utils import date_utils +from unit.models import * +from typing import Literal + +ContentType = Literal["Json", "JsonAPI"] +WebhookStatus = Literal["Enabled", "Disabled"] + + +class WebhookDTO(object): + def __init__(self, id: str, created_at: datetime, label: str, url: str, status: WebhookStatus, + content_type: ContentType, token: str): + self.id = id + self.type = 'webhook' + self.attributes = {"createdAt": created_at, "label": label, "url": url, "status": status, + "contentType": content_type, "token": token} + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return WebhookDTO( + _id, date_utils.to_datetime(attributes["createdAt"]), attributes["label"], attributes["url"], + attributes["status"], attributes["contentType"], attributes["token"]) + + +class CreateWebhookRequest(object): + def __init__(self, label: str, url: str, token: str, content_type: ContentType): + self.label = label + self.url = url + self.token = token + self.content_type = content_type + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "webhook", + "attributes": { + "label": self.label, + "url": self.url, + "token": self.token, + "contentType": self.content_type + } + } + } + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class PatchWebhookRequest(object): + def __init__(self, webhook_id: str, label: Optional[str] = None, url: Optional[str] = None, + content_type: Optional[ContentType] = None, token: Optional[str] = None): + self.webhook_id = webhook_id + self.label = label + self.url = url + self.content_type = content_type + self.token = token + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "webhook", + "attributes": {} + } + } + + if self.label: + payload["data"]["attributes"]["label"] = self.label + + if self.url: + payload["data"]["attributes"]["url"] = self.url + + if self.content_type: + payload["data"]["attributes"]["contentType"] = self.content_type + + if self.token: + payload["data"]["attributes"]["token"] = self.token + + return payload + + +class ListWebhookParams(UnitParams): + def __init__(self, limit: int = 100, offset: int = 0): + self.limit = limit + self.offset = offset + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + return parameters + diff --git a/build/lib/unit/utils/__init__.py b/build/lib/unit/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/build/lib/unit/utils/date_utils.py b/build/lib/unit/utils/date_utils.py new file mode 100644 index 00000000..6dbdbec6 --- /dev/null +++ b/build/lib/unit/utils/date_utils.py @@ -0,0 +1,13 @@ +from datetime import date, datetime + + +def to_datetime(dt: str): + return datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S.%f%z") + + +def to_date(d: str): + return date.fromisoformat(d) + + +def to_date_str(d: date): + return d.strftime("%Y-%m-%d") diff --git a/unit/api/account_resource.py b/unit/api/account_resource.py index 06fb60bd..a034469d 100644 --- a/unit/api/account_resource.py +++ b/unit/api/account_resource.py @@ -7,7 +7,7 @@ def __init__(self, api_url, token): super().__init__(api_url, token) self.resource = "accounts" - def create(self, request: CreateDepositAccountRequest) -> Union[UnitResponse[AccountDTO], UnitError]: + def create(self, request: CreateAccountRequest) -> Union[UnitResponse[AccountDTO], UnitError]: payload = request.to_json_api() response = super().post(self.resource, payload) if super().is_20x(response.status_code): @@ -52,7 +52,7 @@ def list(self, params: ListAccountParams = None) -> Union[UnitResponse[List[Acco else: return UnitError.from_json_api(response.json()) - def update(self, request: PatchDepositAccountRequest) -> Union[UnitResponse[AccountDTO], UnitError]: + def update(self, request: PatchAccountRequest) -> Union[UnitResponse[AccountDTO], UnitError]: payload = request.to_json_api() response = super().patch(f"{self.resource}/{request.account_id}", payload) if super().is_20x(response.status_code): diff --git a/unit/api/repayment_resource.py b/unit/api/repayment_resource.py new file mode 100644 index 00000000..d55a69d0 --- /dev/null +++ b/unit/api/repayment_resource.py @@ -0,0 +1,38 @@ +from typing import Union, List, Optional + +from unit.api.base_resource import BaseResource +from unit.models import UnitResponse, UnitError +from unit.models.codecs import DtoDecoder +from unit.models.repayment import RepaymentDTO, CreateRepaymentRequest, ListRepaymentParams + + +class RepaymentResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "repayments" + + def create(self, request: CreateRepaymentRequest) -> Union[UnitResponse[RepaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().post_create(self.resource, payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[RepaymentDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def get(self, repayment_id: str) -> Union[UnitResponse[RepaymentDTO], UnitError]: + response = super().get(f"{self.resource}/{repayment_id}") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[RepaymentDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: Optional[ListRepaymentParams] = None) -> Union[UnitResponse[List[RepaymentDTO]], UnitError]: + params = params or ListRepaymentParams() + response = super().get(self.resource, params.to_dict()) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[List[RepaymentDTO]](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index fb288cbe..a25d06dc 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -2,6 +2,29 @@ from typing import TypeVar, Generic, Union, Optional, Literal, List, Dict from datetime import datetime, date +def to_camel_case(snake_str): + components = snake_str.lstrip('_').split('_') + # We capitalize the first letter of each component except the first one + # with the 'title' method and join them together. + return components[0] + ''.join(x.title() for x in components[1:]) + + +def extract_attributes(list_of_attributes, attributes): + extracted_attributes = {} + for a in list_of_attributes: + if a in attributes: + extracted_attributes[a] = attributes[a] + + return extracted_attributes + + +class UnitDTO(object): + def to_dict(self): + if type(self) is dict: + return self + else: + v = vars(self) + return dict((to_camel_case(k), val) for k, val in v.items() if val is not None) class Relationship(object): def __init__(self, _type: str, _id: str): diff --git a/unit/models/account.py b/unit/models/account.py index 9768faf9..70dcc444 100644 --- a/unit/models/account.py +++ b/unit/models/account.py @@ -2,9 +2,14 @@ from unit.models import * -AccountStatus = Literal["Open", "Closed"] +AccountStatus = Literal["Open", "Frozen", "Closed"] CloseReason = Literal["ByCustomer", "Fraud"] +FraudReason = Literal["ACHActivity", "CardActivity", "CheckActivity", "ApplicationHistory", "AccountActivity", + "ClientIdentified", "IdentityTheft", "LinkedToFraudulentCustomer"] +CreditAccountType = "creditAccount" +DepositAccountType = "depositAccount" +AccountTypes = Literal[CreditAccountType, DepositAccountType] class DepositAccountDTO(object): def __init__(self, id: str, created_at: datetime, name: str, deposit_product: str, routing_number: str, @@ -29,7 +34,33 @@ def from_json_api(_id, _type, attributes, relationships): ) -AccountDTO = Union[DepositAccountDTO] +class CreditAccountDTO(object): + def __init__(self, _id: str, created_at: datetime, updated_at: Optional[datetime], name: str, credit_terms: str, + currency: str, credit_limit: int, balance: int, hold: int, available: int, + tags: Optional[Dict[str, str]], status: AccountStatus, freeze_reason: Optional[str], + close_reason: Optional[str], close_reason_text: Optional[str], fraud_reason: Optional[FraudReason], + relationships: Optional[Dict[str, Relationship]]): + self.id = _id + self.type = CreditAccountType + self.attributes = {"createdAt": created_at, "updatedAt": updated_at, "name": name, "status": status, + "creditTerms": credit_terms, "currency": currency, "creditLimit": credit_limit, + "balance": balance, "hold": hold, "available": available, "tags": tags, + "freezeReason": freeze_reason, "closeReason": close_reason, + "closeReasonText": close_reason_text, "fraudReason": fraud_reason} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CreditAccountDTO(_id, date_utils.to_datetime(attributes["createdAt"]), + date_utils.to_datetime(attributes.get("updatedAt")), attributes["name"], + attributes["creditTerms"], attributes["currency"], attributes["creditLimit"], + attributes["balance"], attributes["hold"], attributes["available"], + attributes.get("tags"), attributes["status"], attributes.get("freezeReason"), + attributes.get("closeReason"), attributes.get("closeReasonText"), + attributes.get("fraudReason"), relationships) + + +AccountDTO = Union[DepositAccountDTO, CreditAccountDTO] class CreateDepositAccountRequest(UnitRequest): @@ -62,6 +93,39 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) +class CreateCreditAccountRequest(UnitRequest): + def __init__(self, credit_terms: str, credit_limit: int, relationships: Dict[str, Relationship], + tags: Optional[Dict[str, str]] = None, idempotency_key: Optional[str] = None): + self.credit_terms = credit_terms + self.credit_limit = credit_limit + self.tags = tags + self.idempotency_key = idempotency_key + self.relationships = relationships + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "creditAccount", + "attributes": { + "creditTerms": self.credit_terms, + "creditLimit": self.credit_limit + }, + "relationships": self.relationships + } + } + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + return payload + + def __repr__(self): + return json.dumps(self.to_json_api()) + +CreateAccountRequest = Union[CreateDepositAccountRequest, CreateCreditAccountRequest] class PatchDepositAccountRequest(UnitRequest): def __init__(self, account_id: str, deposit_product: Optional[str] = None, tags: Optional[Dict[str, str]] = None): @@ -88,6 +152,33 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) +class PatchCreditAccountRequest(UnitRequest): + def __init__(self, account_id: str, tags: Optional[Dict[str, str]] = None, credit_limit: Optional[int] = None): + self.account_id = account_id + self.tags = tags + self.credit_limit = credit_limit + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": CreditAccountType, + "attributes": {} + } + } + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.credit_limit: + payload["data"]["attributes"]["creditLimit"] = self.credit_limit + + return payload + + def __repr__(self): + return json.dumps(self.to_json_api()) + +PatchAccountRequest = Union[PatchDepositAccountRequest, PatchCreditAccountRequest] + class AchTotals(object): def __init__(self, debits: int, credits: int): @@ -220,19 +311,34 @@ def __repr__(self): class ListAccountParams(UnitParams): def __init__(self, offset: int = 0, limit: int = 100, customer_id: Optional[str] = None, - tags: Optional[object] = None, include: Optional[str] = None): + tags: Optional[Dict[str, str]] = None, include: Optional[str] = None, + status: Optional[AccountStatus] = None, from_balance: Optional[int] = None, + to_balance: Optional[int] = None, _type: Optional[AccountTypes] = None): self.offset = offset self.limit = limit self.customer_id = customer_id self.tags = tags self.include = include + self.status = status + self.from_balance = from_balance + self.to_balance = to_balance + self._type = _type def to_dict(self) -> Dict: parameters = {"page[limit]": self.limit, "page[offset]": self.offset} if self.customer_id: parameters["filter[customerId]"] = self.customer_id if self.tags: - parameters["filter[tags]"] = self.tags + parameters["filter[tags]"] = json.dumps(self.tags) if self.include: parameters["include"] = self.include + if self.status: + for idx, status_filter in enumerate(self.status): + parameters[f"filter[status][{idx}]"] = status_filter + if self._type: + parameters[f"filter[type]"] = self._type + if self.from_balance: + parameters["filter[fromBalance]"] = self.from_balance + if self.to_balance: + parameters["filter[toBalance]"] = self.to_balance return parameters diff --git a/unit/models/card.py b/unit/models/card.py index 69d5e5f4..bf0c4ab0 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -24,31 +24,55 @@ def from_json_api(_id, _type, attributes, relationships): ) -class BusinessDebitCardDTO(object): - def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, - full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, passport: Optional[str], nationality: Optional[str], +class BusinessCardDTO(object): + def __init__(self, _id: str, _type: str, created_at: datetime, last_4_digits: str, expiration_date: str, + ssn: Optional[str], full_name: FullName, date_of_birth: date, address: Address, phone: Phone, + email: str, status: CardStatus, passport: Optional[str], nationality: Optional[str], shipping_address: Optional[Address], design: Optional[str], - relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.type = "businessDebitCard" + relationships: Optional[Dict[str, Relationship]], tags: Optional[Dict[str, str]]): + self.id = _id + self.type = _type self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, - "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, + "ssn": ssn, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, "phone": phone, "email": email, "status": status, "passport": passport, - "nationality": nationality, "shippingAddress": shipping_address, "design": design} + "nationality": nationality, "shippingAddress": shipping_address, "design": design, + "tags": tags} self.relationships = relationships + @staticmethod def from_json_api(_id, _type, attributes, relationships): - shipping_address = Address.from_json_api(attributes.get("shippingAddress")) if attributes.get("shippingAddress") else None - return BusinessDebitCardDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], - attributes["expirationDate"], FullName.from_json_api(attributes["fullName"]), + return BusinessCardDTO( + _id, _type, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], + attributes["expirationDate"], attributes.get("ssn"), FullName.from_json_api(attributes["fullName"]), attributes["dateOfBirth"], Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), attributes["email"], attributes["status"], attributes.get("passport"), attributes.get("nationality"), - shipping_address, attributes.get("design"), relationships + Address.from_json_api(attributes.get("shippingAddress")), attributes.get("design"), relationships, + attributes.get("tags") ) +class BusinessDebitCardDTO(BusinessCardDTO): + def __init__(self, card: BusinessCardDTO): + self.id = card.id + self.type = card.type + self.attributes = card.attributes + self.relationships = card.relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BusinessDebitCardDTO(BusinessCardDTO.from_json_api(_id, _type, attributes, relationships)) + + +class BusinessCreditCardDTO(BusinessCardDTO): + def __init__(self, card: BusinessCardDTO): + self.id = card.id + self.type = card.type + self.attributes = card.attributes + self.relationships = card.relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BusinessCreditCardDTO(BusinessCardDTO.from_json_api(_id, _type, attributes, relationships)) class IndividualVirtualDebitCardDTO(object): def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, status: CardStatus, @@ -89,8 +113,41 @@ def from_json_api(_id, _type, attributes, relationships): attributes.get("passport"), attributes.get("nationality"), relationships ) +class BusinessVirtualCardDTO(object): + def __init__(self, _id: str, _type: str, created_at: datetime, last_4_digits: str, expiration_date: str, + ssn: Optional[str], full_name: FullName, date_of_birth: date, address: Address, phone: Phone, + email: str, status: CardStatus, passport: Optional[str], nationality: Optional[str], + relationships: Optional[Dict[str, Relationship]], tags: Optional[Dict[str, str]]): + self.id = _id + self.type = _type + self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, + "ssn": ssn, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, + "phone": phone, "email": email, "status": status, "passport": passport, + "nationality": nationality, "tags": tags} + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BusinessVirtualCardDTO( + _id, _type, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], + attributes["expirationDate"], attributes.get("ssn"), FullName.from_json_api(attributes["fullName"]), + attributes["dateOfBirth"], Address.from_json_api(attributes["address"]), + Phone.from_json_api(attributes["phone"]), attributes["email"], attributes["status"], + attributes.get("passport"), attributes.get("nationality"), relationships, attributes.get("tags")) + +class BusinessVirtualCreditCardDTO(BusinessVirtualCardDTO): + def __init__(self, card: BusinessVirtualCardDTO): + self.id = card.id + self.type = card.type + self.attributes = card.attributes + self.relationships = card.relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BusinessVirtualCreditCardDTO(BusinessVirtualCardDTO.from_json_api(_id, _type, attributes, relationships)) -Card = Union[IndividualDebitCardDTO, BusinessDebitCardDTO, IndividualVirtualDebitCardDTO, BusinessVirtualDebitCardDTO] +Card = Union[IndividualDebitCardDTO, BusinessDebitCardDTO, IndividualVirtualDebitCardDTO, BusinessVirtualDebitCardDTO, + BusinessVirtualCreditCardDTO, BusinessCreditCardDTO] class CreateIndividualDebitCard(UnitRequest): @@ -133,32 +190,34 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) -class CreateBusinessDebitCard(UnitRequest): +class CreateBusinessCard(object): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, limits: Optional[CardLevelLimits] = None, shipping_address: Optional[Address] = None, + relationships: Dict[str, Relationship], shipping_address: Optional[Address] = None, ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, design: Optional[str] = None, idempotency_key: Optional[str] = None, - tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None): + tags: Optional[Dict[str, str]] = None, limits: Optional[CardLevelLimits] = None, + additional_embossed_text: Optional[str] = None, print_only_business_name: Optional[bool] = None): self.full_name = full_name self.date_of_birth = date_of_birth self.address = address self.phone = phone self.email = email - self.status = status - self.limits = limits self.shipping_address = shipping_address self.ssn = ssn self.passport = passport self.nationality = nationality self.design = design self.idempotency_key = idempotency_key - self.tags = tags or {} - self.relationships = relationships or {} + self.tags = tags + self.relationships = relationships + self.limits = limits + self.additional_embossed_text = additional_embossed_text + self.print_only_business_name = print_only_business_name - def to_json_api(self) -> Dict: + def to_json_api(self, _type: str) -> Dict: payload = { "data": { - "type": "businessDebitCard", + "type": _type, "attributes": { "fullName": self.full_name, "dateOfBirth": self.date_of_birth, @@ -173,9 +232,6 @@ def to_json_api(self) -> Dict: if self.shipping_address: payload["data"]["attributes"]["shippingAddress"] = self.shipping_address - if self.limits: - payload["data"]["attributes"]["limits"] = self.limits - if self.ssn: payload["data"]["attributes"]["ssn"] = self.ssn @@ -194,10 +250,29 @@ def to_json_api(self) -> Dict: if self.tags: payload["data"]["attributes"]["tags"] = self.tags + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + + if self.additional_embossed_text: + payload["data"]["attributes"]["additionalEmbossedText"] = self.additional_embossed_text + + if self.print_only_business_name is not None: + payload["data"]["attributes"]["printOnlyBusinessName"] = self.print_only_business_name + return payload def __repr__(self): - json.dumps(self.to_json_api()) + return json.dumps(self.to_json_api()) + + +class CreateBusinessDebitCard(CreateBusinessCard): + def to_json_api(self): + return super().to_json_api("businessDebitCard") + + +class CreateBusinessCreditCard(CreateBusinessCard): + def to_json_api(self): + return super().to_json_api("businessCreditCard") class CreateIndividualVirtualDebitCard(UnitRequest): def __init__(self, relationships: Dict[str, Relationship], idempotency_key: Optional[str] = None, @@ -231,29 +306,28 @@ def __repr__(self): json.dumps(self.to_json_api()) -class CreateBusinessVirtualDebitCard(UnitRequest): +class CreateBusinessVirtualCard(object): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, limits: Optional[CardLevelLimits] = None, - passport: Optional[str] = None, nationality: Optional[str] = None, - idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, - relationships: Optional[Dict[str, Relationship]] = None): + relationships: Dict[str, Relationship], ssn: Optional[str] = None, passport: Optional[str] = None, + nationality: Optional[str] = None, idempotency_key: Optional[str] = None, + tags: Optional[Dict[str, str]] = None, limits: Optional[CardLevelLimits] = None): self.full_name = full_name self.date_of_birth = date_of_birth self.address = address self.phone = phone self.email = email - self.status = status + self.ssn = ssn self.passport = passport - self.limits = limits self.nationality = nationality self.idempotency_key = idempotency_key - self.tags = tags or {} - self.relationships = relationships or {} + self.tags = tags + self.relationships = relationships + self.limits = limits - def to_json_api(self) -> Dict: + def to_json_api(self, _type: str) -> Dict: payload = { "data": { - "type": "businessVirtualDebitCard", + "type": _type, "attributes": { "fullName": self.full_name, "dateOfBirth": self.date_of_birth, @@ -265,11 +339,8 @@ def to_json_api(self) -> Dict: } } - if self.limits: - payload["data"]["attributes"]["limits"] = self.limits - - if self.limits: - payload["data"]["attributes"]["limits"] = self.limits + if self.ssn: + payload["data"]["attributes"]["ssn"] = self.ssn if self.passport: payload["data"]["attributes"]["passport"] = self.passport @@ -283,14 +354,27 @@ def to_json_api(self) -> Dict: if self.tags: payload["data"]["attributes"]["tags"] = self.tags + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + return payload def __repr__(self): - json.dumps(self.to_json_api()) + return json.dumps(self.to_json_api()) + + +class CreateBusinessVirtualDebitCard(CreateBusinessVirtualCard): + def to_json_api(self): + return super().to_json_api("businessVirtualDebitCard") + + +class CreateBusinessVirtualCreditCard(CreateBusinessVirtualCard): + def to_json_api(self): + return super().to_json_api("businessVirtualCreditCard") CreateCardRequest = Union[CreateIndividualDebitCard, CreateBusinessDebitCard, CreateIndividualVirtualDebitCard, - CreateBusinessVirtualDebitCard] + CreateBusinessVirtualDebitCard, CreateBusinessVirtualCreditCard, CreateBusinessCreditCard] class PatchIndividualDebitCard(UnitRequest): def __init__(self,card_id: str, shipping_address: Optional[Address] = None, design: Optional[str] = None, @@ -327,20 +411,23 @@ def __repr__(self): json.dumps(self.to_json_api()) -class PatchBusinessDebitCard(UnitRequest): +class PatchBusinessCard(object): def __init__(self, card_id: str, shipping_address: Optional[Address] = None, address: Optional[Address] = None, phone: Optional[Phone] = None, email: Optional[str] = None, design: Optional[str] = None, - limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): + tags: Optional[Dict[str, str]] = None, limits: Optional[CardLevelLimits] = None): self.card_id = card_id self.shipping_address = shipping_address - self.limits = limits + self.address = address + self.phone = phone + self.email = email self.design = design self.tags = tags + self.limits = limits - def to_json_api(self) -> Dict: + def to_json_api(self, _type: str = "businessDebitCard") -> Dict: payload = { "data": { - "type": "businessDebitCard", + "type": _type, "attributes": {}, } } @@ -348,9 +435,6 @@ def to_json_api(self) -> Dict: if self.shipping_address: payload["data"]["attributes"]["shippingAddress"] = self.shipping_address - if self.limits: - payload["data"]["attributes"]["limits"] = self.limits - if self.address: payload["data"]["attributes"]["address"] = self.address @@ -366,10 +450,22 @@ def to_json_api(self) -> Dict: if self.tags: payload["data"]["attributes"]["tags"] = self.tags + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits + return payload def __repr__(self): - json.dumps(self.to_json_api()) + return json.dumps(self.to_json_api()) + + +class PatchBusinessDebitCard(PatchBusinessCard): + pass + + +class PatchBusinessCreditCard(PatchBusinessCard): + def to_json_api(self) -> Dict: + return super().to_json_api("businessCreditCard") class PatchIndividualVirtualDebitCard(UnitRequest): def __init__(self, card_id: str, limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): @@ -395,27 +491,25 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) -class PatchBusinessVirtualDebitCard(UnitRequest): +class PatchBusinessVirtualCard(object): def __init__(self, card_id: str, address: Optional[Address] = None, phone: Optional[Phone] = None, - email: Optional[str] = None, limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): + email: Optional[str] = None, tags: Optional[Dict[str, str]] = None, + _type: str = "businessVirtualDebitCard", limits: Optional[CardLevelLimits] = None): self.card_id = card_id self.address = address - self.limits = limits self.phone = phone self.email = email self.tags = tags + self.limits = limits - def to_json_api(self) -> Dict: + def to_json_api(self, _type: str = "businessVirtualDebitCard") -> Dict: payload = { "data": { - "type": "businessVirtualDebitCard", + "type": _type, "attributes": {}, } } - if self.limits: - payload["data"]["attributes"]["limits"] = self.limits - if self.address: payload["data"]["attributes"]["address"] = self.address @@ -425,15 +519,26 @@ def to_json_api(self) -> Dict: if self.email: payload["data"]["attributes"]["email"] = self.email - payload["data"]["attributes"]["tags"] = self.tags or {} + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + if self.limits: + payload["data"]["attributes"]["limits"] = self.limits return payload - def __repr__(self): - json.dumps(self.to_json_api()) + +class PatchBusinessVirtualDebitCard(PatchBusinessVirtualCard): + pass + + +class PatchBusinessVirtualCreditCard(PatchBusinessVirtualCard): + def to_json_api(self) -> Dict: + return super().to_json_api("businessVirtualCreditCard") + PatchCardRequest = Union[PatchIndividualDebitCard, PatchBusinessDebitCard, PatchIndividualVirtualDebitCard, - PatchBusinessVirtualDebitCard] + PatchBusinessVirtualDebitCard, PatchBusinessCreditCard, PatchBusinessVirtualCreditCard] class ReplaceCardRequest(object): def __init__(self, shipping_address: Optional[Address] = None): @@ -452,8 +557,8 @@ def to_json_api(self) -> Dict: return payload - def __repr__(self): - json.dumps(self.to_json_api()) + def __repr__(self): + json.dumps(self.to_json_api()) PinStatus = Literal["Set", "NotSet"] @@ -481,13 +586,17 @@ def from_json_api(attributes): class ListCardParams(UnitParams): def __init__(self, offset: int = 0, limit: int = 100, account_id: Optional[str] = None, - customer_id: Optional[str] = None, tags: Optional[object] = None, include: Optional[str] = None): + customer_id: Optional[str] = None, tags: Optional[Dict[str, str]] = None, include: Optional[str] = None, + sort: Optional[Literal["createdAt", "-createdAt"]] = None, + status: Optional[List[CardStatus]] = None): self.offset = offset self.limit = limit self.account_id = account_id self.customer_id = customer_id self.tags = tags self.include = include + self.sort = sort + self.status = status def to_dict(self) -> Dict: parameters = {"page[limit]": self.limit, "page[offset]": self.offset} @@ -496,8 +605,13 @@ def to_dict(self) -> Dict: if self.account_id: parameters["filter[accountId]"] = self.account_id if self.tags: - parameters["filter[tags]"] = self.tags + parameters["filter[tags]"] = json.dumps(self.tags) if self.include: parameters["include"] = self.include + if self.sort: + parameters["sort"] = self.sort + if self.status: + for idx, status_filter in enumerate(self.status): + parameters[f"filter[status][{idx}]"] = status_filter return parameters diff --git a/unit/models/repayment.py b/unit/models/repayment.py new file mode 100644 index 00000000..3554b94b --- /dev/null +++ b/unit/models/repayment.py @@ -0,0 +1,114 @@ +try: + from typing import Optional, Dict, Union, List, Literal +except ImportError: + from typing import Optional, Dict, Union, List + from typing_extensions import Literal + +from unit.models import UnitDTO, extract_attributes, UnitRequest, Relationship, UnitParams +from unit.utils import date_utils + + +class BaseRepayment(UnitDTO): + def __init__(self, _id, _type, attributes, relationships): + self.id = _id + self.type = _type + self.attributes = extract_attributes(["amount", "status", "tags"], attributes) + attrs = {"createdAt": date_utils.to_datetime(attributes["createdAt"]), + "updatedAt": date_utils.to_datetime(attributes["updatedAt"])} + self.attributes.update(attrs) + self.relationships = relationships + + +class BookRepaymentDTO(BaseRepayment): + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BookRepaymentDTO(_id, _type, attributes, relationships) + + +class AchRepaymentDTO(BaseRepayment): + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return AchRepaymentDTO(_id, _type, attributes, relationships) + + +RepaymentDTO = Union[BookRepaymentDTO, AchRepaymentDTO] + + +class CreateBookRepaymentRequest(UnitRequest): + def __init__(self, description: str, amount: int, relationships: Dict[str, Relationship], + transaction_summary_override: Optional[str] = None, tags: Optional[Dict[str, str]] = None, + idempotency_key: Optional[str] = None): + self.description = description + self.amount = amount + self.transaction_summary_override = transaction_summary_override + self.tags = tags + self.idempotency_key = idempotency_key + self.relationships = relationships + + def to_json_api(self) -> Dict: + return super().to_payload("bookRepayment", self.relationships) + + +class CreateAchRepaymentRequest(UnitRequest): + def __init__(self, description: str, amount: int, relationships: Dict[str, Relationship], + addenda: Optional[str] = None, tags: Optional[Dict[str, str]] = None, same_day: Optional[bool] = None, + idempotency_key: Optional[str] = None): + self.description = description + self.amount = amount + self.addenda = addenda + self.tags = tags + self.same_day = same_day + self.idempotency_key = idempotency_key + self.relationships = relationships + + def to_json_api(self) -> Dict: + return super().to_payload("achRepayment", self.relationships) + + +CreateRepaymentRequest = Union[CreateBookRepaymentRequest, CreateAchRepaymentRequest] + +RepaymentStatus = Literal["Pending", "PendingReview", "Returned", "Sent", "Rejected"] +RepaymentType = Literal["bookRepayment", "achRepayment"] + + +class ListRepaymentParams(UnitParams): + def __init__( + self, + limit: int = 100, + offset: int = 0, + account_id: Optional[str] = None, + credit_account_id: Optional[str] = None, + customer_id: Optional[str] = None, + status: Optional[List[RepaymentStatus]] = None, + _type: Optional[List[str]] = None, + ): + self.limit = limit + self.offset = offset + self.account_id = account_id + self.credit_account_id = credit_account_id + self.customer_id = customer_id + self.status = status + self.type = _type + + def to_dict(self) -> Dict: + parameters = {"page[limit]": self.limit, "page[offset]": self.offset} + + if self.account_id: + parameters["filter[accountId]"] = self.account_id + + if self.credit_account_id: + parameters["filter[creditAccountId]"] = self.credit_account_id + + if self.customer_id: + parameters["filter[customerId]"] = self.customer_id + + if self.status: + for idx, status_filter in enumerate(self.status): + parameters[f"filter[status][{idx}]"] = status_filter + + if self.type: + for idx, type_filter in enumerate(self.type): + parameters[f"filter[type][{idx}]"] = type_filter + + return parameters + diff --git a/unit_python_sdk.egg-info/PKG-INFO b/unit_python_sdk.egg-info/PKG-INFO new file mode 100644 index 00000000..c3be286a --- /dev/null +++ b/unit_python_sdk.egg-info/PKG-INFO @@ -0,0 +1,18 @@ +Metadata-Version: 2.1 +Name: unit-python-sdk +Version: 0.10.5 +Summary: This library provides a python wrapper to http://unit.co API. See https://docs.unit.co/ +Home-page: https://github.com/unit-finance/unit-python-sdk +Download-URL: https://github.com/unit-finance/unit-python-sdk.git +Author: unit.co +Author-email: dev@unit.co +License: Mozilla Public License 2.0 +Keywords: unit,finance,banking,banking-as-a-service,API,SDK +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Topic :: Software Development :: Build Tools +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +License-File: LICENSE diff --git a/unit_python_sdk.egg-info/SOURCES.txt b/unit_python_sdk.egg-info/SOURCES.txt new file mode 100644 index 00000000..da93de6f --- /dev/null +++ b/unit_python_sdk.egg-info/SOURCES.txt @@ -0,0 +1,65 @@ +LICENSE +README.md +setup.cfg +setup.py +unit/__init__.py +unit/api/__init__.py +unit/api/account_end_of_day_resource.py +unit/api/account_resource.py +unit/api/ach_resource.py +unit/api/api_token_resource.py +unit/api/applicationForm_resource.py +unit/api/application_resource.py +unit/api/atmLocation_resource.py +unit/api/authorization_request_resource.py +unit/api/authorization_resource.py +unit/api/base_resource.py +unit/api/bill_pay_resource.py +unit/api/card_resource.py +unit/api/counterparty_resource.py +unit/api/customerToken_resource.py +unit/api/customer_resource.py +unit/api/event_resource.py +unit/api/fee_resource.py +unit/api/institution_resource.py +unit/api/payment_resource.py +unit/api/received_payment_resource.py +unit/api/repayment_resource.py +unit/api/returnAch_resource.py +unit/api/reward_resource.py +unit/api/statement_resource.py +unit/api/transaction_resource.py +unit/api/webhook_resource.py +unit/models/__init__.py +unit/models/account.py +unit/models/account_end_of_day.py +unit/models/api_token.py +unit/models/application.py +unit/models/applicationForm.py +unit/models/atm_location.py +unit/models/authorization.py +unit/models/authorization_request.py +unit/models/benificial_owner.py +unit/models/bill_pay.py +unit/models/card.py +unit/models/codecs.py +unit/models/counterparty.py +unit/models/customer.py +unit/models/customerToken.py +unit/models/event.py +unit/models/fee.py +unit/models/institution.py +unit/models/payment.py +unit/models/repayment.py +unit/models/returnAch.py +unit/models/reward.py +unit/models/statement.py +unit/models/transaction.py +unit/models/webhook.py +unit/utils/__init__.py +unit/utils/date_utils.py +unit_python_sdk.egg-info/PKG-INFO +unit_python_sdk.egg-info/SOURCES.txt +unit_python_sdk.egg-info/dependency_links.txt +unit_python_sdk.egg-info/requires.txt +unit_python_sdk.egg-info/top_level.txt \ No newline at end of file diff --git a/unit_python_sdk.egg-info/dependency_links.txt b/unit_python_sdk.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/unit_python_sdk.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/unit_python_sdk.egg-info/requires.txt b/unit_python_sdk.egg-info/requires.txt new file mode 100644 index 00000000..f2293605 --- /dev/null +++ b/unit_python_sdk.egg-info/requires.txt @@ -0,0 +1 @@ +requests diff --git a/unit_python_sdk.egg-info/top_level.txt b/unit_python_sdk.egg-info/top_level.txt new file mode 100644 index 00000000..47b23a20 --- /dev/null +++ b/unit_python_sdk.egg-info/top_level.txt @@ -0,0 +1 @@ +unit From dfbe4f725940b9c648a71be312a587153f477150 Mon Sep 17 00:00:00 2001 From: Eric Ghildyal Date: Tue, 11 Jul 2023 13:21:55 -0400 Subject: [PATCH 076/137] Remove build/ folder and add to gitignore --- .gitignore | 2 + build/lib/unit/__init__.py | 52 -- build/lib/unit/api/__init__.py | 0 .../unit/api/account_end_of_day_resource.py | 19 - build/lib/unit/api/account_resource.py | 71 -- build/lib/unit/api/ach_resource.py | 34 - build/lib/unit/api/api_token_resource.py | 34 - .../lib/unit/api/applicationForm_resource.py | 37 -- build/lib/unit/api/application_resource.py | 90 --- build/lib/unit/api/atmLocation_resource.py | 34 - .../api/authorization_request_resource.py | 46 -- build/lib/unit/api/authorization_resource.py | 28 - build/lib/unit/api/base_resource.py | 44 -- build/lib/unit/api/bill_pay_resource.py | 22 - build/lib/unit/api/card_resource.py | 113 ---- build/lib/unit/api/counterparty_resource.py | 59 -- build/lib/unit/api/customerToken_resource.py | 29 - build/lib/unit/api/customer_resource.py | 40 -- build/lib/unit/api/event_resource.py | 33 - build/lib/unit/api/fee_resource.py | 19 - build/lib/unit/api/institution_resource.py | 17 - build/lib/unit/api/payment_resource.py | 57 -- .../lib/unit/api/received_payment_resource.py | 45 -- build/lib/unit/api/repayment_resource.py | 38 -- build/lib/unit/api/returnAch_resource.py | 20 - build/lib/unit/api/reward_resource.py | 39 -- build/lib/unit/api/statement_resource.py | 38 -- build/lib/unit/api/transaction_resource.py | 41 -- build/lib/unit/api/webhook_resource.py | 72 -- build/lib/unit/models/__init__.py | 301 --------- build/lib/unit/models/account.py | 344 ---------- build/lib/unit/models/account_end_of_day.py | 41 -- build/lib/unit/models/api_token.py | 48 -- build/lib/unit/models/application.py | 564 ---------------- build/lib/unit/models/applicationForm.py | 103 --- build/lib/unit/models/atm_location.py | 28 - build/lib/unit/models/authorization.py | 61 -- .../lib/unit/models/authorization_request.py | 98 --- build/lib/unit/models/benificial_owner.py | 26 - build/lib/unit/models/bill_pay.py | 21 - build/lib/unit/models/card.py | 617 ------------------ build/lib/unit/models/codecs.py | 403 ------------ build/lib/unit/models/counterparty.py | 172 ----- build/lib/unit/models/customer.py | 158 ----- build/lib/unit/models/customerToken.py | 89 --- build/lib/unit/models/event.py | 463 ------------- build/lib/unit/models/fee.py | 50 -- build/lib/unit/models/institution.py | 17 - build/lib/unit/models/payment.py | 450 ------------- build/lib/unit/models/repayment.py | 114 ---- build/lib/unit/models/returnAch.py | 29 - build/lib/unit/models/reward.py | 137 ---- build/lib/unit/models/statement.py | 46 -- build/lib/unit/models/transaction.py | 466 ------------- build/lib/unit/models/webhook.py | 92 --- build/lib/unit/utils/__init__.py | 0 build/lib/unit/utils/date_utils.py | 13 - 57 files changed, 2 insertions(+), 6122 deletions(-) delete mode 100644 build/lib/unit/__init__.py delete mode 100644 build/lib/unit/api/__init__.py delete mode 100644 build/lib/unit/api/account_end_of_day_resource.py delete mode 100644 build/lib/unit/api/account_resource.py delete mode 100644 build/lib/unit/api/ach_resource.py delete mode 100644 build/lib/unit/api/api_token_resource.py delete mode 100644 build/lib/unit/api/applicationForm_resource.py delete mode 100644 build/lib/unit/api/application_resource.py delete mode 100644 build/lib/unit/api/atmLocation_resource.py delete mode 100644 build/lib/unit/api/authorization_request_resource.py delete mode 100644 build/lib/unit/api/authorization_resource.py delete mode 100644 build/lib/unit/api/base_resource.py delete mode 100644 build/lib/unit/api/bill_pay_resource.py delete mode 100644 build/lib/unit/api/card_resource.py delete mode 100644 build/lib/unit/api/counterparty_resource.py delete mode 100644 build/lib/unit/api/customerToken_resource.py delete mode 100644 build/lib/unit/api/customer_resource.py delete mode 100644 build/lib/unit/api/event_resource.py delete mode 100644 build/lib/unit/api/fee_resource.py delete mode 100644 build/lib/unit/api/institution_resource.py delete mode 100644 build/lib/unit/api/payment_resource.py delete mode 100644 build/lib/unit/api/received_payment_resource.py delete mode 100644 build/lib/unit/api/repayment_resource.py delete mode 100644 build/lib/unit/api/returnAch_resource.py delete mode 100644 build/lib/unit/api/reward_resource.py delete mode 100644 build/lib/unit/api/statement_resource.py delete mode 100644 build/lib/unit/api/transaction_resource.py delete mode 100644 build/lib/unit/api/webhook_resource.py delete mode 100644 build/lib/unit/models/__init__.py delete mode 100644 build/lib/unit/models/account.py delete mode 100644 build/lib/unit/models/account_end_of_day.py delete mode 100644 build/lib/unit/models/api_token.py delete mode 100644 build/lib/unit/models/application.py delete mode 100644 build/lib/unit/models/applicationForm.py delete mode 100644 build/lib/unit/models/atm_location.py delete mode 100644 build/lib/unit/models/authorization.py delete mode 100644 build/lib/unit/models/authorization_request.py delete mode 100644 build/lib/unit/models/benificial_owner.py delete mode 100644 build/lib/unit/models/bill_pay.py delete mode 100644 build/lib/unit/models/card.py delete mode 100644 build/lib/unit/models/codecs.py delete mode 100644 build/lib/unit/models/counterparty.py delete mode 100644 build/lib/unit/models/customer.py delete mode 100644 build/lib/unit/models/customerToken.py delete mode 100644 build/lib/unit/models/event.py delete mode 100644 build/lib/unit/models/fee.py delete mode 100644 build/lib/unit/models/institution.py delete mode 100644 build/lib/unit/models/payment.py delete mode 100644 build/lib/unit/models/repayment.py delete mode 100644 build/lib/unit/models/returnAch.py delete mode 100644 build/lib/unit/models/reward.py delete mode 100644 build/lib/unit/models/statement.py delete mode 100644 build/lib/unit/models/transaction.py delete mode 100644 build/lib/unit/models/webhook.py delete mode 100644 build/lib/unit/utils/__init__.py delete mode 100644 build/lib/unit/utils/date_utils.py diff --git a/.gitignore b/.gitignore index 813da845..09a47dab 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ venv/* dist/ .DS_Store + +build/ \ No newline at end of file diff --git a/build/lib/unit/__init__.py b/build/lib/unit/__init__.py deleted file mode 100644 index 99bda338..00000000 --- a/build/lib/unit/__init__.py +++ /dev/null @@ -1,52 +0,0 @@ -from unit.api.application_resource import ApplicationResource -from unit.api.customer_resource import CustomerResource -from unit.api.account_resource import AccountResource -from unit.api.card_resource import CardResource -from unit.api.transaction_resource import TransactionResource -from unit.api.payment_resource import PaymentResource -from unit.api.ach_resource import AchResource -from unit.api.statement_resource import StatementResource -from unit.api.customerToken_resource import CustomerTokenResource -from unit.api.counterparty_resource import CounterpartyResource -from unit.api.returnAch_resource import ReturnAchResource -from unit.api.applicationForm_resource import ApplicationFormResource -from unit.api.fee_resource import FeeResource -from unit.api.event_resource import EventResource -from unit.api.webhook_resource import WebhookResource -from unit.api.institution_resource import InstitutionResource -from unit.api.atmLocation_resource import AtmLocationResource -from unit.api.bill_pay_resource import BillPayResource -from unit.api.api_token_resource import APITokenResource -from unit.api.authorization_resource import AuthorizationResource -from unit.api.authorization_request_resource import AuthorizationRequestResource -from unit.api.account_end_of_day_resource import AccountEndOfDayResource -from unit.api.reward_resource import RewardResource - -__all__ = ["api", "models", "utils"] - - -class Unit(object): - def __init__(self, api_url, token): - self.applications = ApplicationResource(api_url, token) - self.customers = CustomerResource(api_url, token) - self.accounts = AccountResource(api_url, token) - self.cards = CardResource(api_url, token) - self.transactions = TransactionResource(api_url, token) - self.payments = PaymentResource(api_url, token) - self.ach = AchResource(api_url, token) - self.statements = StatementResource(api_url, token) - self.customerTokens = CustomerTokenResource(api_url, token) - self.counterparty = CounterpartyResource(api_url, token) - self.returnAch = ReturnAchResource(api_url, token) - self.applicationForms = ApplicationFormResource(api_url, token) - self.fees = FeeResource(api_url, token) - self.events = EventResource(api_url, token) - self.webhooks = WebhookResource(api_url, token) - self.institutions = InstitutionResource(api_url, token) - self.atmLocations = AtmLocationResource(api_url, token) - self.billPays = BillPayResource(api_url, token) - self.api_tokens = APITokenResource(api_url, token) - self.authorizations = AuthorizationResource(api_url, token) - self.authorization_requests = AuthorizationRequestResource(api_url, token) - self.account_end_of_day = AccountEndOfDayResource(api_url, token) - self.rewards = RewardResource(api_url, token) diff --git a/build/lib/unit/api/__init__.py b/build/lib/unit/api/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/build/lib/unit/api/account_end_of_day_resource.py b/build/lib/unit/api/account_end_of_day_resource.py deleted file mode 100644 index 71490714..00000000 --- a/build/lib/unit/api/account_end_of_day_resource.py +++ /dev/null @@ -1,19 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.account_end_of_day import * -from unit.models.codecs import DtoDecoder - - -class AccountEndOfDayResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "account-end-of-day" - - def list(self, params: ListAccountEndOfDayParams = None) -> Union[UnitResponse[List[AccountEndOfDayDTO]], UnitError]: - params = params or ListAccountEndOfDayParams() - response = super().get(self.resource, params.to_dict()) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[AccountEndOfDayDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - diff --git a/build/lib/unit/api/account_resource.py b/build/lib/unit/api/account_resource.py deleted file mode 100644 index a034469d..00000000 --- a/build/lib/unit/api/account_resource.py +++ /dev/null @@ -1,71 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.account import * -from unit.models.codecs import DtoDecoder - -class AccountResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "accounts" - - def create(self, request: CreateAccountRequest) -> Union[UnitResponse[AccountDTO], UnitError]: - payload = request.to_json_api() - response = super().post(self.resource, payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[AccountDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def close_account(self, request: CloseAccountRequest) -> Union[UnitResponse[AccountDTO], UnitError]: - payload = request.to_json_api() - response = super().post(f"{self.resource}/{request.account_id}/close", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[AccountDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def reopen_account(self, account_id: str, reason: str = "ByCustomer") -> Union[UnitResponse[AccountDTO], UnitError]: - response = super().post(f"{self.resource}/{account_id}/reopen", {'reason': reason}) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[AccountDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def get(self, account_id: str, include: Optional[str] = "") -> Union[UnitResponse[AccountDTO], UnitError]: - response = super().get(f"{self.resource}/{account_id}", {"include": include}) - if super().is_20x(response.status_code): - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[AccountDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListAccountParams = None) -> Union[UnitResponse[List[AccountDTO]], UnitError]: - params = params or ListAccountParams() - response = super().get(self.resource, params.to_dict()) - if super().is_20x(response.status_code): - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[AccountDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) - else: - return UnitError.from_json_api(response.json()) - - def update(self, request: PatchAccountRequest) -> Union[UnitResponse[AccountDTO], UnitError]: - payload = request.to_json_api() - response = super().patch(f"{self.resource}/{request.account_id}", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[AccountDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def limits(self, account_id: str) -> Union[UnitResponse[AccountLimitsDTO], UnitError]: - response = super().get(f"{self.resource}/{account_id}/limits", None) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[AccountLimitsDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - diff --git a/build/lib/unit/api/ach_resource.py b/build/lib/unit/api/ach_resource.py deleted file mode 100644 index f19835db..00000000 --- a/build/lib/unit/api/ach_resource.py +++ /dev/null @@ -1,34 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.payment import * -from unit.models.codecs import DtoDecoder, split_json_api_single_response - - -class AchResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "ach" - - def simulate_transmit(self, request: SimulateTransmitAchRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: - payload = request.to_json_api() - response = super().post(f"sandbox/{self.resource}/transmit", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - # TODO Fix dto - _id, _type, attributes, relationships = split_json_api_single_response(data) - print("simulate_transmit") - print("data", data) - print("_id, _type, attributes, relationships", _id, _type, attributes, relationships) - return UnitResponse[SimulateAchPaymentDTO](SimulateAchPaymentDTO.from_json_api(_id, _type, attributes, relationships), None) - else: - return UnitError.from_json_api(response.json()) - - def simulate_clear(self, request: SimulateClearAchRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: - payload = request.to_json_api() - response = super().post(f"sandbox/{self.resource}/clear", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - # TODO Fix dto - _id, _type, attributes, relationships = split_json_api_single_response(data) - return UnitResponse[SimulateAchPaymentDTO](SimulateAchPaymentDTO.from_json_api(_id, _type, attributes, relationships), None) - else: - return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/api_token_resource.py b/build/lib/unit/api/api_token_resource.py deleted file mode 100644 index e44206d5..00000000 --- a/build/lib/unit/api/api_token_resource.py +++ /dev/null @@ -1,34 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.api_token import * -from unit.models.codecs import DtoDecoder - - -class APITokenResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "users" - - def create(self, request: CreateAPITokenRequest) -> Union[UnitResponse[APITokenDTO], UnitError]: - payload = request.to_json_api() - response = super().post(f"{self.resource}/{request.user_id}/api-tokens", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[APITokenDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def list(self, user_id: str) -> Union[UnitResponse[List[APITokenDTO]], UnitError]: - response = super().get(f"{self.resource}/{user_id}/api-tokens") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[APITokenDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def revoke(self, user_id: str, token_id: str) -> Union[UnitResponse, UnitError]: - response = super().delete(f"{self.resource}/{user_id}/api-tokens/{token_id}") - if super().is_20x(response.status_code): - return UnitResponse([], None) - else: - return UnitError.from_json_api(response.json()) - diff --git a/build/lib/unit/api/applicationForm_resource.py b/build/lib/unit/api/applicationForm_resource.py deleted file mode 100644 index 3ef83f3e..00000000 --- a/build/lib/unit/api/applicationForm_resource.py +++ /dev/null @@ -1,37 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.applicationForm import * -from unit.models.codecs import DtoDecoder - - -class ApplicationFormResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "application-forms" - - def create(self, request: CreateApplicationFormRequest) -> Union[UnitResponse[ApplicationFormDTO], UnitError]: - payload = request.to_json_api() - response = super().post(self.resource, payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[ApplicationFormDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def get(self, application_form_id: str, include: Optional[str] = "") -> Union[UnitResponse[ApplicationFormDTO], UnitError]: - response = super().get(f"{self.resource}/{application_form_id}", {"include": include}) - if super().is_20x(response.status_code): - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[ApplicationFormDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListApplicationFormParams = None) -> Union[UnitResponse[List[ApplicationFormDTO]], UnitError]: - params = params or ListApplicationFormParams() - response = super().get(self.resource, params.to_dict()) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[ApplicationFormDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - diff --git a/build/lib/unit/api/application_resource.py b/build/lib/unit/api/application_resource.py deleted file mode 100644 index 82d041e7..00000000 --- a/build/lib/unit/api/application_resource.py +++ /dev/null @@ -1,90 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.application import * -from unit.models.codecs import DtoDecoder - - -class ApplicationResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "applications" - - def create(self, request: Union[CreateIndividualApplicationRequest, CreateBusinessApplicationRequest]) -> Union[UnitResponse[ApplicationDTO], UnitError]: - payload = request.to_json_api() - response = super().post(self.resource, payload) - - if response.ok: - data = response.json().get("data") - included = response.json().get("included") - if data["type"] == "individualApplication": - return UnitResponse[IndividualApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) - else: - return UnitResponse[BusinessApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListApplicationParams = None) -> Union[UnitResponse[List[ApplicationDTO]], UnitError]: - params = params or ListApplicationParams() - response = super().get(self.resource, params.to_dict()) - if response.status_code == 200: - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[ApplicationDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def get(self, application_id: str) -> Union[UnitResponse[ApplicationDTO], UnitError]: - response = super().get(f"{self.resource}/{application_id}") - if response.status_code == 200: - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[ApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) - else: - return UnitError.from_json_api(response.json()) - - def upload(self, request: UploadDocumentRequest): - url = f"{self.resource}/{request.application_id}/documents/{request.document_id}" - if request.is_back_side: - url += "/back" - - headers = {} - - if request.file_type == "jpeg": - headers = {"Content-Type": "image/jpeg"} - if request.file_type == "png": - headers = {"Content-Type": "image/png"} - if request.file_type == "pdf": - headers = {"Content-Type": "application/pdf"} - - response = super().put(url, request.file, headers) - if response.status_code == 200: - data = response.json().get("data") - return UnitResponse[ApplicationDocumentDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def update(self, request: PatchApplicationRequest) -> Union[UnitResponse[ApplicationDTO], UnitError]: - payload = request.to_json_api() - response = super().patch(f"{self.resource}/{request.application_id}", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[ApplicationDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def approve_sb(self, request: ApproveApplicationSBRequest): - url = f"sandbox/{self.resource}/{request.application_id}/approve" - - payload = request.to_json_api() - response = super().post(url, payload) - - if response.ok: - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse(data, included) - # TODO need DTOs for this response - # if data["type"] == "individualApplication": - # return UnitResponse[IndividualApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) - # else: - # return UnitResponse[BusinessApplicationDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) - else: - return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/atmLocation_resource.py b/build/lib/unit/api/atmLocation_resource.py deleted file mode 100644 index 5e8b8bab..00000000 --- a/build/lib/unit/api/atmLocation_resource.py +++ /dev/null @@ -1,34 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.atm_location import * -from unit.models.codecs import DtoDecoder, UnitEncoder - -class AtmLocationResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "atm-locations" - - """ - UnitEncoder must be imported here and not in the model class to no cause circular importing. - """ - def get(self, request: GetAtmLocationParams) -> Union[UnitResponse[List[AtmLocationDTO]], UnitError]: - params = {} - - if request.coordinates: - params["filter[coordinates]"] = json.dumps(request.coordinates, cls=UnitEncoder) - - if request.address: - params["filter[address]"] = json.dumps(request.address, cls=UnitEncoder) - - if request.postal_code: - params["filter[postalCode]"] = json.dumps(request.postal_code, cls=UnitEncoder) - - if request.search_radius: - params["filter[searchRadius]"] = request.search_radius - - response = super().get(self.resource, params) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[AtmLocationDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - diff --git a/build/lib/unit/api/authorization_request_resource.py b/build/lib/unit/api/authorization_request_resource.py deleted file mode 100644 index 56815ee8..00000000 --- a/build/lib/unit/api/authorization_request_resource.py +++ /dev/null @@ -1,46 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.authorization_request import * -from unit.models.codecs import DtoDecoder - - -class AuthorizationRequestResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "authorization-requests" - - def get(self, authorization_id: str) -> Union[UnitResponse[PurchaseAuthorizationRequestDTO], UnitError]: - response = super().get(f"{self.resource}/{authorization_id}") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[PurchaseAuthorizationRequestDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListPurchaseAuthorizationRequestParams = None) \ - -> Union[UnitResponse[List[PurchaseAuthorizationRequestDTO]], UnitError]: - params = params or ListPurchaseAuthorizationRequestParams() - response = super().get(self.resource, params.to_dict()) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[PurchaseAuthorizationRequestDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def approve(self, request: ApproveAuthorizationRequest) -> Union[UnitResponse[PurchaseAuthorizationRequestDTO], UnitError]: - payload = request.to_json_api() - response = super().post(f"{self.resource}/{request.authorization_id}/approve", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[PurchaseAuthorizationRequestDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def decline(self, request: DeclineAuthorizationRequest) -> Union[UnitResponse[PurchaseAuthorizationRequestDTO], UnitError]: - payload = request.to_json_api() - response = super().post(f"{self.resource}/{request.authorization_id}/decline", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[PurchaseAuthorizationRequestDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - diff --git a/build/lib/unit/api/authorization_resource.py b/build/lib/unit/api/authorization_resource.py deleted file mode 100644 index 28a3fec7..00000000 --- a/build/lib/unit/api/authorization_resource.py +++ /dev/null @@ -1,28 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.authorization import * -from unit.models.codecs import DtoDecoder - - -class AuthorizationResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "authorizations" - - def get(self, authorization_id: str, include_non_authorized: Optional[bool] = False) -> Union[UnitResponse[AuthorizationDTO], UnitError]: - params = {"filter[includeNonAuthorized]": include_non_authorized} - - response = super().get(f"{self.resource}/{authorization_id}", params) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[AuthorizationDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListAuthorizationParams = None) -> Union[UnitResponse[List[AuthorizationDTO]], UnitError]: - params = params or ListAuthorizationParams() - response = super().get(self.resource, params.to_dict()) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[AuthorizationDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/base_resource.py b/build/lib/unit/api/base_resource.py deleted file mode 100644 index b8b06547..00000000 --- a/build/lib/unit/api/base_resource.py +++ /dev/null @@ -1,44 +0,0 @@ -import json -from typing import Optional, Dict -import requests -from unit.models.codecs import UnitEncoder - - -class BaseResource(object): - def __init__(self, api_url, token): - self.api_url = api_url - self.token = token - self.headers = { - "content-type": "application/vnd.api+json", - "authorization": f"Bearer {self.token}", - "user-agent": "unit-python-sdk" - } - - def get(self, resource: str, params: Dict = None, headers: Optional[Dict[str, str]] = None): - return requests.get(f"{self.api_url}/{resource}", params=params, headers=self.__merge_headers(headers)) - - def post(self, resource: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None): - data = json.dumps(data, cls=UnitEncoder) if data is not None else None - return requests.post(f"{self.api_url}/{resource}", data=data, headers=self.__merge_headers(headers)) - - def patch(self, resource: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None): - data = json.dumps(data, cls=UnitEncoder) if data is not None else None - return requests.patch(f"{self.api_url}/{resource}", data=data, headers=self.__merge_headers(headers)) - - def delete(self, resource: str, params: Dict = None, headers: Optional[Dict[str, str]] = None): - return requests.delete(f"{self.api_url}/{resource}", params=params, headers=self.__merge_headers(headers)) - - def put(self, resource: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None): - return requests.put(f"{self.api_url}/{resource}", data=data, headers=self.__merge_headers(headers)) - - def __merge_headers(self, headers: Optional[Dict[str, str]] = None): - if not headers: - return self.headers - else: - merged = self.headers.copy() - merged.update(**headers) - return merged - - def is_20x(self, status: int): - return status == 200 or status == 201 or status == 204 - diff --git a/build/lib/unit/api/bill_pay_resource.py b/build/lib/unit/api/bill_pay_resource.py deleted file mode 100644 index ee4c7342..00000000 --- a/build/lib/unit/api/bill_pay_resource.py +++ /dev/null @@ -1,22 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.bill_pay import * -from unit.models.codecs import DtoDecoder - - -class BillPayResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "payments/billpay/billers" - - def get(self, params: GetBillersParams) -> Union[UnitResponse[List[BillerDTO]], UnitError]: - parameters = {"name": params.name} - if params.page: - parameters["page"] = params.page - - response = super().get(self.resource, parameters) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[BillerDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - diff --git a/build/lib/unit/api/card_resource.py b/build/lib/unit/api/card_resource.py deleted file mode 100644 index e51dea54..00000000 --- a/build/lib/unit/api/card_resource.py +++ /dev/null @@ -1,113 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.card import * -from unit.models.codecs import DtoDecoder - - -class CardResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "cards" - - def create(self, request: CreateCardRequest) -> Union[UnitResponse[Card], UnitError]: - payload = request.to_json_api() - response = super().post(self.resource, payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[Card](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def report_stolen(self, card_id: str) -> Union[UnitResponse[Card], UnitError]: - response = super().post(f"{self.resource}/{card_id}/report-stolen") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[Card](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def report_lost(self, card_id: str) -> Union[UnitResponse[Card], UnitError]: - response = super().post(f"{self.resource}/{card_id}/report-lost") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[Card](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def close(self, card_id: str) -> Union[UnitResponse[Card], UnitError]: - response = super().post(f"{self.resource}/{card_id}/close") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[Card](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def freeze(self, card_id: str) -> Union[UnitResponse[Card], UnitError]: - response = super().post(f"{self.resource}/{card_id}/freeze") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[Card](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def unfreeze(self, card_id: str) -> Union[UnitResponse[Card], UnitError]: - response = super().post(f"{self.resource}/{card_id}/unfreeze") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[Card](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def replace(self, card_id: str, shipping_address: Optional[Address]) -> Union[UnitResponse[Union[IndividualDebitCardDTO, BusinessDebitCardDTO]], UnitError]: - request = ReplaceCardRequest(shipping_address) - payload = request.to_json_api() - response = super().post(f"{self.resource}/{card_id}/replace", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[Union[IndividualDebitCardDTO, BusinessDebitCardDTO]](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def update(self, request: PatchCardRequest) -> Union[UnitResponse[Card], UnitError]: - payload = request.to_json_api() - response = super().patch(f"{self.resource}/{request.card_id}", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[Card](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def get(self, card_id: str, include: Optional[str] = "") -> Union[UnitResponse[Card], UnitError]: - response = super().get(f"{self.resource}/{card_id}", {"include": include}) - if super().is_20x(response.status_code): - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[Card](DtoDecoder.decode(data), DtoDecoder.decode(included)) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListCardParams = None) -> Union[UnitResponse[List[Card]], UnitError]: - params = params or ListCardParams() - response = super().get(self.resource, params.to_dict()) - if super().is_20x(response.status_code): - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[Card](DtoDecoder.decode(data), DtoDecoder.decode(included)) - else: - return UnitError.from_json_api(response.json()) - - def get_pin_status(self, card_id: str) -> Union[UnitResponse[PinStatusDTO], UnitError]: - response = super().get(f"{self.resource}/{card_id}/secure-data/pin/status") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[PinStatusDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - - def limits(self, card_id: str) -> Union[UnitResponse[CardLimitsDTO], UnitError]: - response = super().get(f"{self.resource}/{card_id}/limits") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[CardLimitsDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/counterparty_resource.py b/build/lib/unit/api/counterparty_resource.py deleted file mode 100644 index 3cf1518e..00000000 --- a/build/lib/unit/api/counterparty_resource.py +++ /dev/null @@ -1,59 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.counterparty import * -from unit.models.codecs import DtoDecoder - - -class CounterpartyResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "counterparties" - - def create(self, request: Union[CreateCounterpartyRequest, CreateCounterpartyWithTokenRequest]) -> Union[UnitResponse[CounterpartyDTO], UnitError]: - payload = request.to_json_api() - response = super().post(self.resource, payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[CounterpartyDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def update(self, request: PatchCounterpartyRequest) -> Union[UnitResponse[CounterpartyDTO], UnitError]: - payload = request.to_json_api() - response = super().patch(f"{self.resource}/{request.counterparty_id}", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[CounterpartyDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def delete(self, counterparty_id: str) -> Union[UnitResponse, UnitError]: - response = super().delete(f"{self.resource}/{counterparty_id}") - if super().is_20x(response.status_code): - return UnitResponse([], None) - else: - return UnitError.from_json_api(response.json()) - - def get(self, counterparty_id: str) -> Union[UnitResponse[CounterpartyDTO], UnitError]: - response = super().get(f"{self.resource}/{counterparty_id}") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[CounterpartyDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListCounterpartyParams = None) -> Union[UnitResponse[List[CounterpartyDTO]], UnitError]: - params = params or ListCounterpartyParams() - response = super().get(self.resource, params.to_dict()) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[CounterpartyDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def get_balance(self, counterparty_id: str) -> Union[UnitResponse[CounterpartyBalanceDTO], UnitError]: - response = super().get(f"{self.resource}/{counterparty_id}/balance") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[CounterpartyBalanceDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/customerToken_resource.py b/build/lib/unit/api/customerToken_resource.py deleted file mode 100644 index 04e2dfd7..00000000 --- a/build/lib/unit/api/customerToken_resource.py +++ /dev/null @@ -1,29 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.customerToken import * -from unit.models.codecs import DtoDecoder - - -class CustomerTokenResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "customers" - - def create_token(self, request: CreateCustomerToken) -> Union[UnitResponse[CustomerTokenDTO], UnitError]: - payload = request.to_json_api() - response = super().post(f"{self.resource}/{request.customer_id}/token", payload) - - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[CustomerTokenDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def create_token_verification(self, request: CreateCustomerTokenVerification) -> Union[UnitResponse[CustomerVerificationTokenDTO], UnitError]: - payload = request.to_json_api() - response = super().post(f"{self.resource}/{request.customer_id}/token/verification", payload) - - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[CustomerVerificationTokenDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/customer_resource.py b/build/lib/unit/api/customer_resource.py deleted file mode 100644 index d164452c..00000000 --- a/build/lib/unit/api/customer_resource.py +++ /dev/null @@ -1,40 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.customer import * -from unit.models.codecs import DtoDecoder - - -class CustomerResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "customers" - - def update(self, request: Union[PatchIndividualCustomerRequest, PatchBusinessCustomerRequest]) -> Union[UnitResponse[CustomerDTO], UnitError]: - payload = request.to_json_api() - response = super().patch(f"{self.resource}/{request.customer_id}", payload) - - if response.ok: - data = response.json().get("data") - if data["type"] == "individualCustomer": - return UnitResponse[IndividualCustomerDTO](DtoDecoder.decode(data), None) - else: - return UnitResponse[BusinessCustomerDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - - def get(self, customer_id: str) -> Union[UnitResponse[CustomerDTO], UnitError]: - response = super().get(f"{self.resource}/{customer_id}") - if response.status_code == 200: - data = response.json().get("data") - return UnitResponse[CustomerDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListCustomerParams = None) -> Union[UnitResponse[List[CustomerDTO]], UnitError]: - params = params or ListCustomerParams() - response = super().get(self.resource, params.to_dict()) - if response.status_code == 200: - data = response.json().get("data") - return UnitResponse[CustomerDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/event_resource.py b/build/lib/unit/api/event_resource.py deleted file mode 100644 index aaec2f97..00000000 --- a/build/lib/unit/api/event_resource.py +++ /dev/null @@ -1,33 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.event import * -from unit.models.codecs import DtoDecoder - - -class EventResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "events" - - def get(self, event_id: str) -> Union[UnitResponse[EventDTO], UnitError]: - response = super().get(f"{self.resource}/{event_id}") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[EventDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListEventParams = None) -> Union[UnitResponse[List[EventDTO]], UnitError]: - params = params or ListEventParams() - response = super().get(self.resource, params.to_dict()) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[EventDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def fire(self, event_id: str) -> Union[UnitResponse, UnitError]: - response = super().post(f"{self.resource}/{event_id}") - if super().is_20x(response.status_code): - return UnitResponse([], None) - else: - return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/fee_resource.py b/build/lib/unit/api/fee_resource.py deleted file mode 100644 index 68bb2756..00000000 --- a/build/lib/unit/api/fee_resource.py +++ /dev/null @@ -1,19 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.fee import * -from unit.models.codecs import DtoDecoder - - -class FeeResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "fees" - - def create(self, request: CreateFeeRequest) -> Union[UnitResponse[FeeDTO], UnitError]: - payload = request.to_json_api() - response = super().post(self.resource, payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[FeeDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - diff --git a/build/lib/unit/api/institution_resource.py b/build/lib/unit/api/institution_resource.py deleted file mode 100644 index d91ce96d..00000000 --- a/build/lib/unit/api/institution_resource.py +++ /dev/null @@ -1,17 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.institution import * -from unit.models.codecs import DtoDecoder - - -class InstitutionResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "institutions" - - def get(self, routing_number: str) -> Union[UnitResponse[InstitutionDTO], UnitError]: - response = super().get(f"{self.resource}/{routing_number}", None) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[InstitutionDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/payment_resource.py b/build/lib/unit/api/payment_resource.py deleted file mode 100644 index 33c13fda..00000000 --- a/build/lib/unit/api/payment_resource.py +++ /dev/null @@ -1,57 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.payment import * -from unit.models.codecs import DtoDecoder, split_json_api_single_response - - -class PaymentResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "payments" - - def create(self, request: CreatePaymentRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: - payload = request.to_json_api() - response = super().post(self.resource, payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[PaymentDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def update(self, request: PatchPaymentRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: - payload = request.to_json_api() - response = super().patch(f"{self.resource}/{request.payment_id}", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[PaymentDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def get(self, payment_id: str, include: Optional[str] = "") -> Union[UnitResponse[PaymentDTO], UnitError]: - response = super().get(f"{self.resource}/{payment_id}", {"include": include}) - if response.status_code == 200: - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[PaymentDTO](DtoDecoder.decode(data), DtoDecoder.decode(data)) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListPaymentParams = None) -> Union[UnitResponse[List[PaymentDTO]], UnitError]: - params = params or ListPaymentParams() - response = super().get(self.resource, params.to_dict()) - if response.status_code == 200: - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[PaymentDTO](DtoDecoder.decode(data), DtoDecoder.decode(data)) - else: - return UnitError.from_json_api(response.json()) - - def simulate_incoming_ach(self, request: SimulateIncomingAchRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: - payload = request.to_json_api() - response = super().post(f"sandbox/{self.resource}", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - # TODO Fix dto - _id, _type, attributes, relationships = split_json_api_single_response(data) - return UnitResponse[SimulateIncomingAchPaymentDTO](SimulateIncomingAchPaymentDTO.from_json_api(_id, _type, attributes, relationships), None) - else: - return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/received_payment_resource.py b/build/lib/unit/api/received_payment_resource.py deleted file mode 100644 index b453d6ac..00000000 --- a/build/lib/unit/api/received_payment_resource.py +++ /dev/null @@ -1,45 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.payment import * -from unit.models.codecs import DtoDecoder - - -class ReceivedPaymentResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "received-payments" - - def update(self, request: PatchPaymentRequest) -> Union[UnitResponse[AchReceivedPaymentDTO], UnitError]: - payload = request.to_json_api() - response = super().patch(f"{self.resource}/{request.payment_id}", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[AchReceivedPaymentDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def get(self, payment_id: str, include: Optional[str] = "") -> Union[UnitResponse[AchReceivedPaymentDTO], UnitError]: - response = super().get(f"{self.resource}/{payment_id}", {"include": include}) - if response.status_code == 200: - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[AchReceivedPaymentDTO](DtoDecoder.decode(data), DtoDecoder.decode(data)) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListReceivedPaymentParams = None) -> Union[UnitResponse[List[AchReceivedPaymentDTO]], UnitError]: - params = params or ListReceivedPaymentParams() - response = super().get(self.resource, params.to_dict()) - if response.status_code == 200: - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[AchReceivedPaymentDTO](DtoDecoder.decode(data), DtoDecoder.decode(data)) - else: - return UnitError.from_json_api(response.json()) - - def advance(self, payment_id: str) -> Union[UnitResponse[AchReceivedPaymentDTO], UnitError]: - response = super().post(f"{self.resource}/{payment_id}/advance") - if response.status_code == 200: - data = response.json().get("data") - return UnitResponse[AchReceivedPaymentDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) \ No newline at end of file diff --git a/build/lib/unit/api/repayment_resource.py b/build/lib/unit/api/repayment_resource.py deleted file mode 100644 index d55a69d0..00000000 --- a/build/lib/unit/api/repayment_resource.py +++ /dev/null @@ -1,38 +0,0 @@ -from typing import Union, List, Optional - -from unit.api.base_resource import BaseResource -from unit.models import UnitResponse, UnitError -from unit.models.codecs import DtoDecoder -from unit.models.repayment import RepaymentDTO, CreateRepaymentRequest, ListRepaymentParams - - -class RepaymentResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "repayments" - - def create(self, request: CreateRepaymentRequest) -> Union[UnitResponse[RepaymentDTO], UnitError]: - payload = request.to_json_api() - response = super().post_create(self.resource, payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[RepaymentDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def get(self, repayment_id: str) -> Union[UnitResponse[RepaymentDTO], UnitError]: - response = super().get(f"{self.resource}/{repayment_id}") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[RepaymentDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: Optional[ListRepaymentParams] = None) -> Union[UnitResponse[List[RepaymentDTO]], UnitError]: - params = params or ListRepaymentParams() - response = super().get(self.resource, params.to_dict()) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[List[RepaymentDTO]](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/returnAch_resource.py b/build/lib/unit/api/returnAch_resource.py deleted file mode 100644 index 948479de..00000000 --- a/build/lib/unit/api/returnAch_resource.py +++ /dev/null @@ -1,20 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.transaction import ReturnedReceivedAchTransactionDTO -from unit.models.returnAch import * -from unit.models.codecs import DtoDecoder - - -class ReturnAchResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "returns" - - def return_ach(self, request: ReturnReceivedAchTransactionRequest) -> Union[UnitResponse[ReturnedReceivedAchTransactionDTO], UnitError]: - payload = request.to_json_api() - response = super().post(f"{self.resource}/{request.transaction_id}", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[ReturnedReceivedAchTransactionDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - diff --git a/build/lib/unit/api/reward_resource.py b/build/lib/unit/api/reward_resource.py deleted file mode 100644 index 8cafa463..00000000 --- a/build/lib/unit/api/reward_resource.py +++ /dev/null @@ -1,39 +0,0 @@ -from typing import Union, List - -from unit.api.base_resource import BaseResource -from unit.models import UnitResponse, UnitError -from unit.models.reward import RewardDTO, ListRewardsParams, CreateRewardRequest - -from unit.models.codecs import DtoDecoder - - -class RewardResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "rewards" - - def create(self, request: CreateRewardRequest) -> Union[UnitResponse[RewardDTO], UnitError]: - payload = request.to_json_api() - response = super().post(self.resource, payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[RewardDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def get(self, reward_id: str) -> Union[UnitResponse[RewardDTO], UnitError]: - response = super().get(f"{self.resource}/{reward_id}") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[RewardDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListRewardsParams = None) -> Union[UnitResponse[List[RewardDTO]], UnitError]: - params = params or ListRewardsParams() - response = super().get(self.resource, params.to_dict()) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[List[RewardDTO]](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) diff --git a/build/lib/unit/api/statement_resource.py b/build/lib/unit/api/statement_resource.py deleted file mode 100644 index be711e1f..00000000 --- a/build/lib/unit/api/statement_resource.py +++ /dev/null @@ -1,38 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.statement import * -from unit.models.codecs import DtoDecoder - - -class StatementResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "statements" - - def get(self, params: GetStatementParams) -> Union[UnitResponse[str], UnitError]: - parameters = {"language": params.language} - if params.customer_id: - parameters["filter[customerId]"] = params.customer_id - - response = super().get(f"{self.resource}/{params.statement_id}/{params.output_type}", parameters) - if response.status_code == 200: - return UnitResponse[bytes](response.content, None) - else: - return UnitError.from_json_api(response.json()) - - def get_bank_verification(self, account_id: str, include_proof_of_funds: Optional[bool] = False) -> Union[UnitResponse[str], UnitError]: - response = super().get(f"{self.resource}/{account_id}/bank/pdf", - {"includeProofOfFunds": include_proof_of_funds}) - if response.status_code == 200: - return UnitResponse[str](response.text, None) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListStatementParams = None) -> Union[UnitResponse[List[StatementDTO]], UnitError]: - params = params or ListStatementParams() - response = super().get(self.resource, params.to_dict()) - if response.status_code == 200: - data = response.json().get("data") - return UnitResponse[StatementDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - diff --git a/build/lib/unit/api/transaction_resource.py b/build/lib/unit/api/transaction_resource.py deleted file mode 100644 index abcb8e38..00000000 --- a/build/lib/unit/api/transaction_resource.py +++ /dev/null @@ -1,41 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.transaction import * -from unit.models.codecs import DtoDecoder -from unit.models.transaction import * - - -class TransactionResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "transactions" - - def get(self, transaction_id: str, account_id: str, include: Optional[str] = "") -> Union[UnitResponse[TransactionDTO], UnitError]: - params = {"filter[accountId]": account_id, "include": include} - response = super().get(f"{self.resource}/{transaction_id}", params) - if response.status_code == 200: - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[TransactionDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListTransactionParams = None) -> Union[UnitResponse[List[TransactionDTO]], UnitError]: - params = params or ListTransactionParams() - response = super().get(self.resource, params.to_dict()) - if response.status_code == 200: - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[TransactionDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) - else: - return UnitError.from_json_api(response.json()) - - def update(self, request: PatchTransactionRequest) -> Union[UnitResponse[TransactionDTO], UnitError]: - payload = request.to_json_api() - response = super().patch(f"accounts/{request.account_id}/{self.resource}/{request.transaction_id}", payload) - if response.status_code == 200: - data = response.json().get("data") - included = response.json().get("included") - return UnitResponse[TransactionDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) - else: - return UnitError.from_json_api(response.json()) - diff --git a/build/lib/unit/api/webhook_resource.py b/build/lib/unit/api/webhook_resource.py deleted file mode 100644 index 78349feb..00000000 --- a/build/lib/unit/api/webhook_resource.py +++ /dev/null @@ -1,72 +0,0 @@ -from unit.api.base_resource import BaseResource -from unit.models.webhook import * -from unit.models.codecs import DtoDecoder -import hmac -from hashlib import sha1 -import base64 - - -class WebhookResource(BaseResource): - def __init__(self, api_url, token): - super().__init__(api_url, token) - self.resource = "webhooks" - - def create(self, request: CreateWebhookRequest) -> Union[UnitResponse[WebhookDTO], UnitError]: - payload = request.to_json_api() - response = super().post(self.resource, payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[WebhookDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def get(self, webhook_id: str) -> Union[UnitResponse[WebhookDTO], UnitError]: - response = super().get(f"{self.resource}/{webhook_id}") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[WebhookDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def list(self, params: ListWebhookParams = None) -> Union[UnitResponse[List[WebhookDTO]], UnitError]: - params = params or ListWebhookParams() - response = super().get(self.resource, params.to_dict()) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[WebhookDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def update(self, request: PatchWebhookRequest) -> Union[UnitResponse[WebhookDTO], UnitError]: - payload = request.to_json_api() - response = super().patch(f"{self.resource}/{request.webhook_id}", payload) - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[WebhookDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def enable(self, webhook_id: str) -> Union[UnitResponse[WebhookDTO], UnitError]: - response = super().post(f"{self.resource}/{webhook_id}/enable") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[WebhookDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def disable(self, webhook_id: str) -> Union[UnitResponse[WebhookDTO], UnitError]: - response = super().post(f"{self.resource}/{webhook_id}/disable") - if super().is_20x(response.status_code): - data = response.json().get("data") - return UnitResponse[WebhookDTO](DtoDecoder.decode(data), None) - else: - return UnitError.from_json_api(response.json()) - - def verify(self, signature: str, secret: str, payload): - mac = hmac.new( - secret.encode(), - msg=json.dumps(payload, separators=(',', ':'), ensure_ascii=False).encode('utf-8'), - digestmod=sha1, - ) - res = base64.encodebytes(mac.digest()).decode().rstrip('\n') - return res == signature diff --git a/build/lib/unit/models/__init__.py b/build/lib/unit/models/__init__.py deleted file mode 100644 index a25d06dc..00000000 --- a/build/lib/unit/models/__init__.py +++ /dev/null @@ -1,301 +0,0 @@ -import json -from typing import TypeVar, Generic, Union, Optional, Literal, List, Dict -from datetime import datetime, date - -def to_camel_case(snake_str): - components = snake_str.lstrip('_').split('_') - # We capitalize the first letter of each component except the first one - # with the 'title' method and join them together. - return components[0] + ''.join(x.title() for x in components[1:]) - - -def extract_attributes(list_of_attributes, attributes): - extracted_attributes = {} - for a in list_of_attributes: - if a in attributes: - extracted_attributes[a] = attributes[a] - - return extracted_attributes - - -class UnitDTO(object): - def to_dict(self): - if type(self) is dict: - return self - else: - v = vars(self) - return dict((to_camel_case(k), val) for k, val in v.items() if val is not None) - -class Relationship(object): - def __init__(self, _type: str, _id: str): - self.type = _type - self.id = _id - - def to_dict(self): - return {"type": self.type, "id": self.id} - - -T = TypeVar('T') - -class RelationshipArray(Generic[T]): - def __init__(self, l: List[T]): - self.relationships = l - - -class UnitResponse(Generic[T]): - def __init__(self, data: Union[T, List[T]], included): - self.data = data - self.included = included - - @staticmethod - def from_json_api(data: str): - pass - - -class UnitRequest(object): - def to_json_api(self) -> Dict: - pass - -class UnitParams(object): - def to_dict(self) -> Dict: - pass - -class RawUnitObject(object): - def __init__(self, _id, _type, attributes, relationships): - self.id = _id - self.type = _type - self.attributes = attributes - self.relationships = relationships - -class UnitErrorPayload(object): - def __init__(self, title: str, status: str, detail: Optional[str] = None, details: Optional[str] = None, - source: Optional[Dict] = None): - self.title = title - self.status = status - self.detail = detail - self.details = details - self.source = source - - def __str__(self): - return self.detail - - -class UnitError(object): - def __init__(self, errors: List[UnitErrorPayload]): - self.errors = errors - - @staticmethod - def from_json_api(data: Dict): - errors = [] - for err in data["errors"]: - errors.append( - UnitErrorPayload(err.get("title"), err.get("status"), err.get("detail", None), - err.get("details", None), err.get("source", None)) - ) - - return UnitError(errors) - - def __str__(self): - return json.dumps({"errors": [{"title": err.title, "status": err.status, "detail": err.detail, - "details": err.details, "source": err.source} for err in self.errors]}) - - -Status = Literal["Approved", "Denied", "PendingReview"] -Title = Literal["CEO", "COO", "CFO", "President"] -EntityType = Literal["Corporation", "LLC", "Partnership"] - -class FullName(object): - def __init__(self, first: str, last: str): - self.first = first - self.last = last - - def __str__(self): - return f"{self.first} {self.last}" - - @staticmethod - def from_json_api(data: Dict): - return FullName(data.get("first"), data.get("last")) - - -# todo: Alex - use typing.Literal for multi accepted values (e.g country) -class Address(object): - def __init__(self, street: str, city: str, state: str, postal_code: str, country: str, - street2: Optional[str] = None): - self.street = street - self.street2 = street2 - self.city = city - self.state = state - self.postal_code = postal_code - self.country = country - - @staticmethod - def from_json_api(data: Dict): - return Address(data.get("street"), data.get("city"), data.get("state"), - data.get("postalCode"), data.get("country"), data.get("street2", None)) - - -class Phone(object): - def __init__(self, country_code: str, number: str): - self.country_code = country_code - self.number = number - - @staticmethod - def from_json_api(data: Dict): - return Phone(data.get("countryCode"), data.get("number")) - - -class BusinessContact(object): - def __init__(self, full_name: FullName, email: str, phone: Phone): - self.full_name = full_name - self.email = email - self.phone = phone - - @staticmethod - def from_json_api(data: Dict): - return BusinessContact(FullName.from_json_api(data.get("fullName")), data.get("email"), Phone.from_json_api(data.get("phone"))) - - -class Officer(object): - def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: Optional[Status] = None, title: Optional[Title] = None, ssn: Optional[str] = None, - passport: Optional[str] = None, nationality: Optional[str] = None): - self.full_name = full_name - self.date_of_birth = date_of_birth - self.address = address - self.phone = phone - self.email = email - self.status = status - self.title = title - self.ssn = ssn - self.passport = passport - self.nationality = nationality - - @staticmethod - def from_json_api(data: Dict): - return Officer(data.get("fullName"), data.get("dateOfBirth"), data.get("address"), data.get("phone"), - data.get("email"), data.get("status"), data.get("title"), data.get("ssn"), data.get("passport"), - data.get("nationality")) - - -class BeneficialOwner(object): - def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: Optional[Status] = None, ssn: Optional[str] = None, passport: Optional[str] = None, - nationality: Optional[str] = None, percentage: Optional[int] = None): - self.full_name = full_name - self.date_of_birth = date_of_birth - self.address = address - self.phone = phone - self.email = email - self.status = status - self.ssn = ssn - self.passport = passport - self.nationality = nationality - self.percentage = percentage - - @staticmethod - def from_json_api(l: List): - beneficial_owners = [] - for data in l: - beneficial_owners.append(BeneficialOwner(data.get("fullName"), data.get("dateOfBirth"), data.get("address"), - data.get("phone"), data.get("email"), data.get("status"), data.get("ssn"), - data.get("passport"), data.get("nationality"), data.get("percentage"))) - return beneficial_owners - - -class AuthorizedUser(object): - def __init__(self, full_name: FullName, email: str, phone: Phone): - self.full_name = full_name - self.email = email - self.phone = phone - - @staticmethod - def from_json_api(l: List) -> List: - authorized_users = [] - for data in l: - authorized_users.append(AuthorizedUser(data.get("fullName"), data.get("email"), data.get("phone"))) - return authorized_users - -class WireCounterparty(object): - def __init__(self, routing_number: str, account_number: str, name: str, address: Address): - self.routing_number = routing_number - self.account_number = account_number - self.name = name - self.address = address - - @staticmethod - def from_json_api(data: Dict): - return WireCounterparty(data["routingNumber"], data["accountNumber"], data["name"], - Address.from_json_api(data["address"])) - -class Counterparty(object): - def __init__(self, routing_number: str, account_number: str, account_type: str, name: str): - self.routing_number = routing_number - self.account_number = account_number - self.account_type = account_type - self.name = name - - @staticmethod - def from_json_api(data: Dict): - return Counterparty(data["routingNumber"], data["accountNumber"], data["accountType"], data["name"]) - -class Coordinates(object): - def __init__(self, longitude: int, latitude: int): - self.longitude = longitude - self.latitude = latitude - - @staticmethod - def from_json_api(data: Dict): - if data: - return Coordinates(data["longitude"], data["latitude"]) - else: - return None - - -class Merchant(object): - def __init__(self, name: str, type: int, category: Optional[str], location: Optional[str]): - self.name = name - self.type = type - self.category = category - self.location = location - - @staticmethod - def from_json_api(data: Dict): - return Merchant(data["name"], data["type"], data.get("category"), data.get("location")) - -class CardLevelLimits(object): - def __init__(self, daily_withdrawal: int, daily_purchase: int, monthly_withdrawal: int, monthly_purchase: int): - self.daily_withdrawal = daily_withdrawal - self.daily_purchase = daily_purchase - self.monthly_withdrawal = monthly_withdrawal - self.monthly_purchase = monthly_purchase - - @staticmethod - def from_json_api(data: Dict): - return CardLevelLimits(data["dailyWithdrawal"], data["dailyPurchase"], data["monthlyWithdrawal"], - data["monthlyPurchase"]) - -class CardTotals(object): - def __init__(self, withdrawals: int, deposits: int, purchases: int): - self.withdrawals = withdrawals - self.deposits = deposits - self.purchases = purchases - - @staticmethod - def from_json_api(data: Dict): - return CardTotals(data["withdrawals"], data["deposits"], data["purchases"]) - - -class DeviceFingerprint(object): - def __init__(self, value: str, provider: str = "iovation"): - self.value = value - self.provider = provider - - def to_json_api(self): - return { - "value": self.value, - "provider": self.provider, - } - - @classmethod - def from_json_api(cls, data: Dict): - return cls(value=data["value"], provider=data["provider"]) diff --git a/build/lib/unit/models/account.py b/build/lib/unit/models/account.py deleted file mode 100644 index 70dcc444..00000000 --- a/build/lib/unit/models/account.py +++ /dev/null @@ -1,344 +0,0 @@ -from unit.utils import date_utils - -from unit.models import * - -AccountStatus = Literal["Open", "Frozen", "Closed"] -CloseReason = Literal["ByCustomer", "Fraud"] -FraudReason = Literal["ACHActivity", "CardActivity", "CheckActivity", "ApplicationHistory", "AccountActivity", - "ClientIdentified", "IdentityTheft", "LinkedToFraudulentCustomer"] - -CreditAccountType = "creditAccount" -DepositAccountType = "depositAccount" -AccountTypes = Literal[CreditAccountType, DepositAccountType] - -class DepositAccountDTO(object): - def __init__(self, id: str, created_at: datetime, name: str, deposit_product: str, routing_number: str, - account_number: str, currency: str, balance: int, hold: int, available: int, status: AccountStatus, - tags: Optional[Dict[str, str]], close_reason: Optional[CloseReason], - relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.type = "depositAccount" - self.attributes = {"name": name, "createdAt": created_at, "depositProduct": deposit_product, - "routingNumber": routing_number, "accountNumber": account_number, "currency": currency, - "balance": balance, "hold": hold, "available": available, "status": status, - "closeReason": close_reason, "tags": tags} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return DepositAccountDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["name"], attributes["depositProduct"], - attributes["routingNumber"], attributes["accountNumber"], attributes["currency"], attributes["balance"], - attributes["hold"], attributes["available"], attributes["status"], attributes.get("tags"), - attributes.get("closeReason"), relationships - ) - - -class CreditAccountDTO(object): - def __init__(self, _id: str, created_at: datetime, updated_at: Optional[datetime], name: str, credit_terms: str, - currency: str, credit_limit: int, balance: int, hold: int, available: int, - tags: Optional[Dict[str, str]], status: AccountStatus, freeze_reason: Optional[str], - close_reason: Optional[str], close_reason_text: Optional[str], fraud_reason: Optional[FraudReason], - relationships: Optional[Dict[str, Relationship]]): - self.id = _id - self.type = CreditAccountType - self.attributes = {"createdAt": created_at, "updatedAt": updated_at, "name": name, "status": status, - "creditTerms": credit_terms, "currency": currency, "creditLimit": credit_limit, - "balance": balance, "hold": hold, "available": available, "tags": tags, - "freezeReason": freeze_reason, "closeReason": close_reason, - "closeReasonText": close_reason_text, "fraudReason": fraud_reason} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CreditAccountDTO(_id, date_utils.to_datetime(attributes["createdAt"]), - date_utils.to_datetime(attributes.get("updatedAt")), attributes["name"], - attributes["creditTerms"], attributes["currency"], attributes["creditLimit"], - attributes["balance"], attributes["hold"], attributes["available"], - attributes.get("tags"), attributes["status"], attributes.get("freezeReason"), - attributes.get("closeReason"), attributes.get("closeReasonText"), - attributes.get("fraudReason"), relationships) - - -AccountDTO = Union[DepositAccountDTO, CreditAccountDTO] - - -class CreateDepositAccountRequest(UnitRequest): - def __init__(self, deposit_product: str, relationships: Optional[Dict[str, Union[Relationship, RelationshipArray]]], - tags: Optional[Dict[str, str]] = None, idempotency_key: Optional[str] = None): - self.deposit_product = deposit_product - self.tags = tags - self.idempotency_key = idempotency_key - self.relationships = relationships - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "depositAccount", - "attributes": { - "depositProduct": self.deposit_product, - }, - "relationships": self.relationships - } - } - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - -class CreateCreditAccountRequest(UnitRequest): - def __init__(self, credit_terms: str, credit_limit: int, relationships: Dict[str, Relationship], - tags: Optional[Dict[str, str]] = None, idempotency_key: Optional[str] = None): - self.credit_terms = credit_terms - self.credit_limit = credit_limit - self.tags = tags - self.idempotency_key = idempotency_key - self.relationships = relationships - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "creditAccount", - "attributes": { - "creditTerms": self.credit_terms, - "creditLimit": self.credit_limit - }, - "relationships": self.relationships - } - } - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key - - return payload - - def __repr__(self): - return json.dumps(self.to_json_api()) - -CreateAccountRequest = Union[CreateDepositAccountRequest, CreateCreditAccountRequest] - -class PatchDepositAccountRequest(UnitRequest): - def __init__(self, account_id: str, deposit_product: Optional[str] = None, tags: Optional[Dict[str, str]] = None): - self.account_id = account_id - self.deposit_product = deposit_product - self.tags = tags - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "depositAccount", - "attributes": {} - } - } - - if self.deposit_product: - payload["data"]["attributes"]["depositProduct"] = self.deposit_product - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - -class PatchCreditAccountRequest(UnitRequest): - def __init__(self, account_id: str, tags: Optional[Dict[str, str]] = None, credit_limit: Optional[int] = None): - self.account_id = account_id - self.tags = tags - self.credit_limit = credit_limit - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": CreditAccountType, - "attributes": {} - } - } - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - if self.credit_limit: - payload["data"]["attributes"]["creditLimit"] = self.credit_limit - - return payload - - def __repr__(self): - return json.dumps(self.to_json_api()) - -PatchAccountRequest = Union[PatchDepositAccountRequest, PatchCreditAccountRequest] - - -class AchTotals(object): - def __init__(self, debits: int, credits: int): - self.debits = debits - self.credits = credits - - @staticmethod - def from_json_api(data: Dict): - return AchTotals(data["debits"], data["credits"]) - - -class AchLimits(object): - def __init__(self, daily_debit: int, daily_credit: int, monthly_debit: int, monthly_credit: int, - daily_debit_soft: int, monthly_debit_soft: int): - self.daily_debit = daily_debit - self.daily_credit = daily_credit - self.monthly_debit = monthly_debit - self.monthly_credit = monthly_credit - self.daily_debit_soft = daily_debit_soft - self.monthly_debit_soft = monthly_debit_soft - - @staticmethod - def from_json_api(data: Dict): - return AchLimits(data["dailyDebit"], data["dailyCredit"], data["monthlyDebit"], data["monthlyCredit"], - data["dailyDebitSoft"], data["monthlyDebitSoft"]) - - -class AccountAchLimits(object): - def __init__(self, limits: AchLimits, totals_daily: AchTotals, totals_monthly: AchTotals): - self.limits = limits - self.totals_daily = totals_daily - self.totals_monthly = totals_monthly - - @staticmethod - def from_json_api(data: Dict): - return AccountAchLimits(AchLimits.from_json_api(data["limits"]), AchTotals.from_json_api(data["totalsDaily"]), - AchTotals.from_json_api(data["totalsMonthly"])) - - -class CardLimits(object): - def __init__(self, daily_withdrawal: int, daily_deposit: int, daily_purchase: int, daily_card_transaction: int): - self.daily_withdrawal = daily_withdrawal - self.daily_deposit = daily_deposit - self.daily_purchase = daily_purchase - self.daily_card_transaction = daily_card_transaction - - @staticmethod - def from_json_api(data: Dict): - return CardLimits(data["dailyWithdrawal"], data["dailyDeposit"], - data["dailyPurchase"], data["dailyCardTransaction"]) - -class CardTotals(object): - def __init__(self, withdrawals: int, deposits: int, purchases: int, card_transactions: int): - self.withdrawals = withdrawals - self.deposits = deposits - self.purchases = purchases - self.card_transactions = card_transactions - - @staticmethod - def from_json_api(data: Dict): - return CardTotals(data["withdrawals"], data["deposits"], data["purchases"], data["cardTransactions"]) - -class AccountCardLimits(object): - def __init__(self, limits: CardLimits, totals_daily: CardTotals): - self.limits = limits - self.totals_daily = totals_daily - - @staticmethod - def from_json_api(data: Dict): - return AccountCardLimits(CardLimits.from_json_api(data["limits"]), - CardTotals.from_json_api(data["totalsDaily"])) - - -class CheckDepositLimits(object): - def __init__(self, daily: int, monthly: int, daily_soft: int, monthly_soft: int): - self.daily = daily - self.monthly = monthly - self.daily_soft = daily_soft - self.monthly_soft = monthly_soft - - @staticmethod - def from_json_api(data: Dict): - return CheckDepositLimits(data["daily"], data["monthly"], data["dailySoft"], data["monthlySoft"]) - - -class CheckDepositAccountLimits(object): - def __init__(self, limits: CheckDepositLimits, totals_daily: int, totals_monthly: int): - self.limits = limits - self.totals_daily = totals_daily - self.totals_monthly = totals_monthly - - @staticmethod - def from_json_api(data: Dict): - return CheckDepositAccountLimits(CheckDepositLimits.from_json_api(data["limits"]), data["totalsDaily"], - data["totalsMonthly"]) - - -class AccountLimitsDTO(object): - def __init__(self, ach: AccountAchLimits, card: AccountCardLimits, check_deposit: CheckDepositAccountLimits): - self.type = "limits" - self.attributes = {"ach": ach, "card": card, "checkDeposit": check_deposit} - - @staticmethod - def from_json_api(attributes): - return AccountLimitsDTO(AccountAchLimits.from_json_api(attributes["ach"]), - AccountCardLimits.from_json_api(attributes["card"]), - CheckDepositAccountLimits.from_json_api(attributes["checkDeposit"])) - - -class CloseAccountRequest(UnitRequest): - def __init__(self, account_id: str, reason: Optional[Literal["ByCustomer", "Fraud"]] = "ByCustomer"): - self.account_id = account_id - self.reason = reason - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "accountClose", - "attributes": { - "reason": self.reason, - } - } - } - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class ListAccountParams(UnitParams): - def __init__(self, offset: int = 0, limit: int = 100, customer_id: Optional[str] = None, - tags: Optional[Dict[str, str]] = None, include: Optional[str] = None, - status: Optional[AccountStatus] = None, from_balance: Optional[int] = None, - to_balance: Optional[int] = None, _type: Optional[AccountTypes] = None): - self.offset = offset - self.limit = limit - self.customer_id = customer_id - self.tags = tags - self.include = include - self.status = status - self.from_balance = from_balance - self.to_balance = to_balance - self._type = _type - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.customer_id: - parameters["filter[customerId]"] = self.customer_id - if self.tags: - parameters["filter[tags]"] = json.dumps(self.tags) - if self.include: - parameters["include"] = self.include - if self.status: - for idx, status_filter in enumerate(self.status): - parameters[f"filter[status][{idx}]"] = status_filter - if self._type: - parameters[f"filter[type]"] = self._type - if self.from_balance: - parameters["filter[fromBalance]"] = self.from_balance - if self.to_balance: - parameters["filter[toBalance]"] = self.to_balance - return parameters diff --git a/build/lib/unit/models/account_end_of_day.py b/build/lib/unit/models/account_end_of_day.py deleted file mode 100644 index b22bf22a..00000000 --- a/build/lib/unit/models/account_end_of_day.py +++ /dev/null @@ -1,41 +0,0 @@ -import json -from typing import Optional -from unit.models import * - - -class AccountEndOfDayDTO(object): - def __init__(self, id: str, date: str, balance: int, hold: int, available: int, - relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.type = "accountEndOfDay" - self.attributes = {"date": date, "balance": balance, "hold": hold, "available": available} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AccountEndOfDayDTO(_id, attributes["date"], attributes["balance"], attributes["hold"], - attributes["available"], relationships) - - -class ListAccountEndOfDayParams(UnitParams): - def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, - customer_id: Optional[str] = None, since: Optional[str] = None, until: Optional[str] = None): - self.limit = limit - self.offset = offset - self.account_id = account_id - self.customer_id = customer_id - self.since = since - self.until = until - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.customer_id: - parameters["filter[customerId]"] = self.customer_id - if self.account_id: - parameters["filter[accountId]"] = self.account_id - if self.since: - parameters["filter[since]"] = self.since - if self.until: - parameters["filter[until]"] = self.until - return parameters - diff --git a/build/lib/unit/models/api_token.py b/build/lib/unit/models/api_token.py deleted file mode 100644 index a08e3550..00000000 --- a/build/lib/unit/models/api_token.py +++ /dev/null @@ -1,48 +0,0 @@ -from unit.models import * -from unit.utils import date_utils - - -class APITokenDTO(object): - def __init__(self, id: str, created_at: datetime, description: str, expiration: datetime, token: Optional[str], - source_ip: Optional[str]): - self.id = id - self.type = "apiToken" - self.attributes = {"createdAt": created_at, "description": description, "expiration": expiration, - "token": token, "sourceIp": source_ip} - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return APITokenDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["description"], - date_utils.to_datetime(attributes["expiration"]), attributes.get("token"), - attributes.get("sourceIp")) - - -class CreateAPITokenRequest(object): - def __init__(self, user_id: str, description: str, scope: str, expiration: datetime, - source_ip: Optional[str] = None): - self.user_id = user_id - self.description = description - self.scope = scope - self.expiration = expiration - self.source_ip = source_ip - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "apiToken", - "attributes": { - "description": self.description, - "scope": self.scope, - "expiration": self.expiration - } - } - } - - if self.source_ip: - payload["data"]["attributes"]["sourceIp"] = self.source_ip - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - diff --git a/build/lib/unit/models/application.py b/build/lib/unit/models/application.py deleted file mode 100644 index bfb450b7..00000000 --- a/build/lib/unit/models/application.py +++ /dev/null @@ -1,564 +0,0 @@ -from unit.utils import date_utils -from unit.models import * -from typing import IO - -ApplicationStatus = Literal["Approved", "Denied", "Pending", "PendingReview"] - -DocumentType = Literal[ - "IdDocument", - "Passport", - "AddressVerification", - "CertificateOfIncorporation", - "EmployerIdentificationNumberConfirmation", -] - -ReasonCode = Literal[ - "PoorQuality", - "NameMismatch", - "SSNMismatch", - "AddressMismatch", - "DOBMismatch", - "ExpiredId", - "EINMismatch", - "StateMismatch", - "Other", -] - -ApplicationTypes = Literal[ - "individualApplication", "businessApplication", "trustApplication" -] - - -Industry = Literal[ - "Retail", - "Wholesale", - "Restaurants", - "Hospitals", - "Construction", - "Insurance", - "Unions", - "RealEstate", - "FreelanceProfessional", - "OtherProfessionalServices", - "OnlineRetailer", - "OtherEducationServices", -] - -AnnualRevenue = Literal[ - "UpTo250k", - "Between250kAnd500k", - "Between500kAnd1m", - "Between1mAnd5m", - "Over5m", - "UpTo50k", - "Between50kAnd100k", - "Between100kAnd200k", - "Between200kAnd500k", - "Over500k", -] - -NumberOfEmployees = Literal[ - "One", - "Between2And5", - "Between5And10", - "Over10", - "UpTo10", - "Between10And50", - "Between50And100", - "Between100And500", - "Over500", -] - -CashFlow = Literal["Unpredictable", "Predictable"] - -BusinessVertical = Literal[ - "AdultEntertainmentDatingOrEscortServices", - "AgricultureForestryFishingOrHunting", - "ArtsEntertainmentAndRecreation", - "BusinessSupportOrBuildingServices", - "Cannabis", - "Construction", - "DirectMarketingOrTelemarketing", - "EducationalServices", - "FinancialServicesCryptocurrency", - "FinancialServicesDebitCollectionOrConsolidation", - "FinancialServicesMoneyServicesBusinessOrCurrencyExchange", - "FinancialServicesOther", - "FinancialServicesPaydayLending", - "GamingOrGambling", - "HealthCareAndSocialAssistance", - "HospitalityAccommodationOrFoodServices", - "LegalAccountingConsultingOrComputerProgramming", - "Manufacturing", - "Mining", - "Nutraceuticals", - "PersonalCareServices", - "PublicAdministration", - "RealEstate", - "ReligiousCivicAndSocialOrganizations", - "RepairAndMaintenance", - "RetailTrade", - "TechnologyMediaOrTelecom", - "TransportationOrWarehousing", - "Utilities", - "WholesaleTrade", -] - -Revocability = Literal["Revocable", "Irrevocable"] -SourceOfFunds = Literal[ - "Inheritance", "Salary", "Savings", "InvestmentReturns", "Gifts" -] - - -class IndividualApplicationDTO(object): - def __init__( - self, - id: str, - created_at: datetime, - full_name: FullName, - address: Address, - date_of_birth: date, - email: str, - phone: Phone, - status: ApplicationStatus, - ssn: Optional[str], - message: Optional[str], - ip: Optional[str], - ein: Optional[str], - dba: Optional[str], - sole_proprietorship: Optional[bool], - tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]], - ): - self.id = id - self.type = "individualApplication" - self.attributes = { - "createdAt": created_at, - "fullName": full_name, - "address": address, - "dateOfBirth": date_of_birth, - "email": email, - "phone": phone, - "status": status, - "ssn": ssn, - "message": message, - "ip": ip, - "ein": ein, - "dba": dba, - "soleProprietorship": sole_proprietorship, - "tags": tags, - } - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return IndividualApplicationDTO( - _id, - date_utils.to_datetime(attributes["createdAt"]), - FullName.from_json_api(attributes["fullName"]), - Address.from_json_api(attributes["address"]), - date_utils.to_date(attributes["dateOfBirth"]), - attributes["email"], - Phone.from_json_api(attributes["phone"]), - attributes["status"], - attributes.get("ssn"), - attributes.get("message"), - attributes.get("ip"), - attributes.get("ein"), - attributes.get("dba"), - attributes.get("soleProprietorship"), - attributes.get("tags"), - relationships, - ) - - -class BusinessApplicationDTO(object): - def __init__( - self, - id: str, - created_at: datetime, - name: str, - address: Address, - phone: Phone, - status: ApplicationStatus, - state_of_incorporation: str, - entity_type: EntityType, - contact: BusinessContact, - officer: Officer, - beneficial_owners: [BeneficialOwner], - ssn: Optional[str], - message: Optional[str], - ip: Optional[str], - ein: Optional[str], - dba: Optional[str], - tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]], - ): - self.id = id - self.type = "businessApplication" - self.attributes = { - "createdAt": created_at, - "name": name, - "address": address, - "phone": phone, - "status": status, - "ssn": ssn, - "stateOfIncorporation": state_of_incorporation, - "ssn": ssn, - "message": message, - "ip": ip, - "ein": ein, - "entityType": entity_type, - "dba": dba, - "contact": contact, - "officer": officer, - "beneficialOwners": beneficial_owners, - "tags": tags, - } - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BusinessApplicationDTO( - _id, - date_utils.to_datetime(attributes["createdAt"]), - attributes.get("name"), - Address.from_json_api(attributes["address"]), - Phone.from_json_api(attributes["phone"]), - attributes["status"], - attributes.get("stateOfIncorporation"), - attributes.get("entityType"), - BusinessContact.from_json_api(attributes["contact"]), - Officer.from_json_api(attributes["officer"]), - BeneficialOwner.from_json_api(attributes["beneficialOwners"]), - attributes.get("ssn"), - attributes.get("message"), - attributes.get("ip"), - attributes.get("ein"), - attributes.get("dba"), - attributes.get("tags"), - relationships, - ) - - -ApplicationDTO = Union[IndividualApplicationDTO, BusinessApplicationDTO] - - -class CreateIndividualApplicationRequest(UnitRequest): - def __init__( - self, - full_name: FullName, - date_of_birth: date, - address: Address, - email: str, - phone: Phone, - ip: str = None, - ein: str = None, - dba: str = None, - sole_proprietorship: bool = None, - passport: str = None, - nationality: str = None, - ssn=None, - device_fingerprints: Optional[List[DeviceFingerprint]] = None, - idempotency_key: str = None, - tags: Optional[Dict[str, str]] = None, - ): - self.full_name = full_name - self.date_of_birth = date_of_birth - self.address = address - self.email = email - self.phone = phone - self.ip = ip - self.ein = ein - self.dba = dba - self.sole_proprietorship = sole_proprietorship - self.ssn = ssn - self.passport = passport - self.nationality = nationality - self.device_fingerprints = device_fingerprints - self.idempotency_key = idempotency_key - self.tags = tags - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "individualApplication", - "attributes": { - "fullName": self.full_name, - "dateOfBirth": date_utils.to_date_str(self.date_of_birth), - "address": self.address, - "email": self.email, - "phone": self.phone, - }, - } - } - - if self.ip: - payload["data"]["attributes"]["ip"] = self.ip - - if self.ein: - payload["data"]["attributes"]["ein"] = self.ein - - if self.dba: - payload["data"]["attributes"]["dba"] = self.dba - - if self.sole_proprietorship: - payload["data"]["attributes"][ - "soleProprietorship" - ] = self.sole_proprietorship - - if self.ssn: - payload["data"]["attributes"]["ssn"] = self.ssn - - if self.passport: - payload["data"]["attributes"]["passport"] = self.passport - - if self.nationality: - payload["data"]["attributes"]["nationality"] = self.nationality - - if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key - - if self.device_fingerprints: - payload["data"]["attributes"]["deviceFingerprints"] = [ - e.to_json_api() for e in self.device_fingerprints - ] - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class CreateBusinessApplicationRequest(UnitRequest): - def __init__( - self, - name: str, - address: Address, - phone: Phone, - state_of_incorporation: str, - ein: str, - contact: BusinessContact, - officer: Officer, - beneficial_owners: [BeneficialOwner], - entity_type: EntityType, - tags: Optional[Dict[str, str]] = None, - dba: str = None, - ip: str = None, - website: str = None, - industry: Optional[Industry] = None, - annual_revenue: Optional[AnnualRevenue] = None, - number_of_employees: Optional[NumberOfEmployees] = None, - cash_flow: Optional[CashFlow] = None, - year_of_incorporation: Optional[str] = None, - countries_of_operation: Optional[List[str]] = None, - stock_symbol: Optional[str] = None, - business_vertical: Optional[BusinessVertical] = None, - device_fingerprints: Optional[List[DeviceFingerprint]] = None - ): - self.name = name - self.address = address - self.phone = phone - self.state_of_incorporation = state_of_incorporation - self.ein = ein - self.contact = contact - self.officer = officer - self.beneficial_owners = beneficial_owners - self.entity_type = entity_type - self.dba = dba - self.ip = ip - self.website = website - self.tags = tags - self.industry = industry - self.annual_revenue = annual_revenue - self.number_of_employees = number_of_employees - self.cash_flow = cash_flow - self.year_of_incorporation = year_of_incorporation - self.countries_of_operation = countries_of_operation - self.stock_symbol = stock_symbol - self.business_vertical = business_vertical - self.device_fingerprints = device_fingerprints - - def to_json_api(self) -> dict: - payload = { - "data": { - "type": "businessApplication", - "attributes": { - "name": self.name, - "address": self.address, - "phone": self.phone, - "stateOfIncorporation": self.state_of_incorporation, - "ein": self.ein, - "contact": self.contact, - "officer": self.officer, - "beneficialOwners": self.beneficial_owners, - "entityType": self.entity_type, - "industry": self.industry, - "annualRevenue": self.annual_revenue, - "numberOfEmployees": self.number_of_employees, - "cashFlow": self.cash_flow, - "yearOfIncorporation": self.year_of_incorporation, - "countriesOfOperation": self.countries_of_operation, - "stockSymbol": self.stock_symbol, - "businessVertical": self.business_vertical, - }, - } - } - - if self.dba: - payload["data"]["attributes"]["dba"] = self.dba - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - if self.ip: - payload["data"]["attributes"]["ip"] = self.ip - - if self.website: - payload["data"]["attributes"]["website"] = self.website - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class ApplicationDocumentDTO(object): - def __init__( - self, - id: str, - status: ApplicationStatus, - document_type: DocumentType, - description: str, - name: str, - address: Optional[Address], - date_of_birth: Optional[date], - passport: Optional[str], - ein: Optional[str], - reason_code: Optional[ReasonCode], - reason: Optional[str], - ): - self.id = id - self.type = "document" - self.attributes = { - "status": status, - "documentType": document_type, - "description": description, - "name": name, - "address": address, - "dateOfBirth": date_of_birth, - "passport": passport, - "ein": ein, - "reasonCode": reason_code, - "reason": reason, - } - - @staticmethod - def from_json_api(_id, _type, attributes): - address = ( - Address.from_json_api(attributes.get("address")) - if attributes.get("address") - else None - ) - return ApplicationDocumentDTO( - _id, - attributes["status"], - attributes["documentType"], - attributes["description"], - attributes["name"], - address, - attributes.get("dateOfBirth"), - attributes.get("passport"), - attributes.get("ein"), - attributes.get("reasonCode"), - attributes.get("reason"), - ) - - -FileType = Literal["jpeg", "png", "pdf"] - - -class UploadDocumentRequest(object): - def __init__( - self, - application_id: str, - document_id: str, - file: IO, - file_type: FileType, - is_back_side: Optional[bool] = False, - ): - self.application_id = application_id - self.document_id = document_id - self.file = file - self.file_type = file_type - self.is_back_side = is_back_side - - -class ListApplicationParams(UnitParams): - def __init__( - self, - offset: int = 0, - limit: int = 100, - email: Optional[str] = None, - tags: Optional[object] = None, - query: Optional[str] = None, - sort: Optional[Literal["createdAt", "-createdAt"]] = None, - ): - self.offset = offset - self.limit = limit - self.email = email - self.query = query - self.sort = sort - self.tags = tags - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.email: - parameters["filter[email]"] = self.email - if self.query: - parameters["filter[query]"] = self.query - if self.tags: - parameters["filter[tags]"] = self.tags - if self.sort: - parameters["sort"] = self.sort - return parameters - - -class PatchApplicationRequest(UnitRequest): - def __init__( - self, - application_id: str, - type: ApplicationTypes = "individualApplication", - tags: Optional[Dict[str, str]] = None, - ): - self.application_id = application_id - self.type = type - self.tags = tags - - def to_json_api(self) -> Dict: - payload = {"data": {"type": self.type, "attributes": {}}} - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class ApproveApplicationSBRequest(UnitRequest): - def __init__(self, application_id: str): - self.application_id = application_id - - def to_json_api(self) -> Dict: - payload = { - "data": {"type": "applicationApprove", "attributes": {"reason": "sandbox"}} - } - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) diff --git a/build/lib/unit/models/applicationForm.py b/build/lib/unit/models/applicationForm.py deleted file mode 100644 index 99708e7a..00000000 --- a/build/lib/unit/models/applicationForm.py +++ /dev/null @@ -1,103 +0,0 @@ -import json -from datetime import datetime, date -from typing import Literal, Optional -from unit.utils import date_utils -from unit.models import * - -ApplicationFormStage = Literal["ChooseBusinessOrIndividual", "EnterIndividualInformation", - "IndividualApplicationCreated", "EnterBusinessInformation", "EnterOfficerInformation", - "EnterBeneficialOwnersInformation", "BusinessApplicationCreated", - "EnterSoleProprietorshipInformation", "SoleProprietorshipApplicationCreated"] - - -class ApplicationFormPrefill(object): - def __init__(self, application_type: Optional[str], full_name: Optional[FullName], ssn: Optional[str], - passport: Optional[str], nationality: Optional[str], date_of_birth: Optional[date], - email: Optional[str], name: Optional[str], state_of_incorporation: Optional[str], - entity_type: Optional[str], contact: Optional[BusinessContact], officer: Optional[Officer], - beneficial_owners: [BeneficialOwner], website: Optional[str], dba: Optional[str], - ein: Optional[str], address: Optional[Address], phone: Optional[Phone]): - self.application_type = application_type - self.full_name = full_name - self.ssn = ssn - self.passport = passport - self.nationality = nationality - self.date_of_birth = date_of_birth - self.email = email - self.name = name - self.state_of_incorporation = state_of_incorporation - self.entity_type = entity_type - self.contact = contact - self.officer = officer - self.beneficial_owners = beneficial_owners - self.website = website - self.dba = dba - self.ein = ein - self.address = address - self.phone = phone - - -class ApplicationFormDTO(object): - def __init__(self, id: str, url: str, stage: ApplicationFormStage, applicant_details: ApplicationFormPrefill, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.type = "applicationForm" - self.attributes = {"url": url, "stage": stage, "applicantDetails": applicant_details, "tags": tags} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return ApplicationFormDTO(_id, attributes["url"], attributes["stage"], attributes.get("applicantDetails"), - attributes.get("tags"), relationships) - - -AllowedApplicationTypes = Union["Individual", "Business", "SoleProprietorship"] - - -class CreateApplicationFormRequest(UnitRequest): - def __init__(self, tags: Optional[Dict[str, str]] = None, - application_details: Optional[ApplicationFormPrefill] = None, - allowed_application_types: [AllowedApplicationTypes] = None): - self.tags = tags - self.application_details = application_details - self.allowed_application_types = allowed_application_types - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "applicationForm", - "attributes": {} - } - } - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - if self.application_details: - payload["data"]["attributes"]["applicantDetails"] = self.application_details - - if self.allowed_application_types: - payload["data"]["attributes"]["allowedApplicationTypes"] = self.allowed_application_types - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class ListApplicationFormParams(UnitParams): - def __init__(self, offset: int = 0, limit: int = 100, tags: Optional[object] = None, - sort: Optional[Literal["createdAt", "-createdAt"]] = None): - self.offset = offset - self.limit = limit - self.tags = tags - self.sort = sort - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.tags: - parameters["filter[tags]"] = self.tags - if self.sort: - parameters["sort"] = self.sort - return parameters - diff --git a/build/lib/unit/models/atm_location.py b/build/lib/unit/models/atm_location.py deleted file mode 100644 index 95d2a545..00000000 --- a/build/lib/unit/models/atm_location.py +++ /dev/null @@ -1,28 +0,0 @@ -import json -from unit.models import * - - -class AtmLocationDTO(object): - def __init__(self, network: int, location_name: str, coordinates: Coordinates, address: Address, distance: int, - surcharge_free: bool, accept_deposits: bool): - self.type = "atmLocation" - self.attributes = {"network": network, "locationName": location_name, "coordinates": coordinates, - "address": address, "distance": distance, "surchargeFree": surcharge_free, - "acceptDeposits": accept_deposits} - - @staticmethod - def from_json_api(_type, attributes): - return AtmLocationDTO(attributes["network"], attributes["locationName"], - Coordinates.from_json_api(attributes["coordinates"]), - Address.from_json_api(attributes["address"]), attributes["distance"], - attributes["surchargeFree"], attributes["acceptDeposits"]) - - -class GetAtmLocationParams(object): - def __init__(self, search_radius: Optional[int] = None, coordinates: Optional[Coordinates] = None, - postal_code: Optional[str] = None, address: Optional[Address] = None): - self.search_radius = search_radius - self.coordinates = coordinates - self.postal_code = postal_code - self.address = address - diff --git a/build/lib/unit/models/authorization.py b/build/lib/unit/models/authorization.py deleted file mode 100644 index 9f6de45b..00000000 --- a/build/lib/unit/models/authorization.py +++ /dev/null @@ -1,61 +0,0 @@ -import json -from typing import Optional -from unit.models import * -from unit.utils import date_utils - -AuthorizationStatus = Literal["Authorized", "Completed", "Canceled", "Declined"] - -class AuthorizationDTO(object): - def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, status: AuthorizationStatus, - merchant: Merchant, recurring: bool, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.type = "authorization" - self.attributes = {"createdAt": created_at, "amount": amount, "cardLast4Digits": card_last_4_digits, - "status": status, "merchant": merchant, - "recurring": recurring, "tags": tags} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AuthorizationDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["amount"], - attributes["cardLast4Digits"], attributes["status"], - Merchant.from_json_api(attributes["merchant"]), attributes["recurring"], - attributes.get("tags"), relationships) - - -class ListAuthorizationParams(UnitParams): - def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, - customer_id: Optional[str] = None, card_id: Optional[str] = None, since: Optional[str] = None, - until: Optional[str] = None, include_non_authorized: Optional[bool] = False, - status: Optional[str] = None, sort: Optional[Literal["createdAt", "-createdAt"]] = None): - self.limit = limit - self.offset = offset - self.account_id = account_id - self.customer_id = customer_id - self.card_id = card_id - self.since = since - self.until = until - self.include_non_authorized = include_non_authorized - self.status = status - self.sort = sort - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.customer_id: - parameters["filter[customerId]"] = self.customer_id - if self.account_id: - parameters["filter[accountId]"] = self.account_id - if self.card_id: - parameters["filter[cardId]"] = self.card_id - if self.include_non_authorized: - parameters["filter[includeNonAuthorized]"] = self.include_non_authorized - if self.status: - parameters["filter[status]"] = self.status - if self.since: - parameters["filter[since]"] = self.since - if self.until: - parameters["filter[until]"] = self.until - if self.sort: - parameters["sort"] = self.sort - return parameters diff --git a/build/lib/unit/models/authorization_request.py b/build/lib/unit/models/authorization_request.py deleted file mode 100644 index f654701d..00000000 --- a/build/lib/unit/models/authorization_request.py +++ /dev/null @@ -1,98 +0,0 @@ -import json -from typing import Optional, Literal -from unit.models import * -from unit.utils import date_utils - -PurchaseAuthorizationRequestStatus = Literal["Pending", "Approved", "Declined"] -DeclineReason = Literal["AccountClosed", "CardExceedsAmountLimit", "DoNotHonor", "InsufficientFunds", "InvalidMerchant", - "ReferToCardIssuer", "RestrictedCard", "Timeout", "TransactionNotPermittedToCardholder"] - -class PurchaseAuthorizationRequestDTO(object): - def __init__(self, id: str, created_at: datetime, amount: int, status: PurchaseAuthorizationRequestStatus, - partial_approval_allowed: str, approved_amount: Optional[int], decline_reason: Optional[DeclineReason], - merchant_name: str, merchant_type: int, merchant_category: str, merchant_location: Optional[str], - recurring: bool, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.type = "purchaseAuthorizationRequest" - self.attributes = {"createdAt": created_at, "amount": amount, "status": status, - "partialApprovalAllowed": partial_approval_allowed, "approvedAmount": approved_amount, - "declineReason": decline_reason, "merchant": { "name": merchant_name, "type": merchant_type, - "category": merchant_category, - "location": merchant_location}, - "recurring": recurring, "tags": tags} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return PurchaseAuthorizationRequestDTO(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["amount"], attributes["status"], - attributes.get("partialApprovalAllowed"), - attributes.get("approvedAmount"), attributes.get("declineReason"), - attributes["merchant"]["name"], attributes["merchant"]["type"], - attributes["merchant"]["category"], - attributes["merchant"].get("location"), attributes["recurring"], - attributes.get("tags"), relationships) - - -class ListPurchaseAuthorizationRequestParams(UnitParams): - def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, - customer_id: Optional[str] = None): - self.limit = limit - self.offset = offset - self.account_id = account_id - self.customer_id = customer_id - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.customer_id: - parameters["filter[customerId]"] = self.customer_id - if self.account_id: - parameters["filter[accountId]"] = self.account_id - return parameters - - -class ApproveAuthorizationRequest(UnitRequest): - def __init__(self, authorization_id: str, amount: Optional[int] = None, tags: Optional[Dict[str, str]] = None): - self.authorization_id = authorization_id - self.amount = amount - self.tags = tags - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "approveAuthorizationRequest", - "attributes": {} - } - } - - if self.amount: - payload["data"]["attributes"]["amount"] = self.amount - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class DeclineAuthorizationRequest(UnitRequest): - def __init__(self, authorization_id: str, reason: DeclineReason): - self.authorization_id = authorization_id - self.reason = reason - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "declineAuthorizationRequest", - "attributes": { - "reason": self.reason - } - } - } - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) diff --git a/build/lib/unit/models/benificial_owner.py b/build/lib/unit/models/benificial_owner.py deleted file mode 100644 index 3b3fcd12..00000000 --- a/build/lib/unit/models/benificial_owner.py +++ /dev/null @@ -1,26 +0,0 @@ -import json -from typing import Optional -from unit.models import * -from unit.utils import date_utils - - -class BenificialOwnerDTO(object): - def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: Optional[Status] = None, ssn: Optional[str] = None, passport: Optional[str] = None, - nationality: Optional[str] = None, percentage: Optional[int] = None): - self.full_name = full_name - self.date_of_birth = date_of_birth - self.address = address - self.phone = phone - self.email = email - self.status = status - self.ssn = ssn - self.passport = passport - self.nationality = nationality - self.percentage = percentage - - @staticmethod - def from_json_api(attributes): - return BenificialOwnerDTO(attributes.get("fullName"), attributes.get("dateOfBirth"), attributes.get("address"), - attributes.get("phone"), attributes.get("email"), attributes.get("status"), attributes.get("ssn"), - attributes.get("passport"), attributes.get("nationality"), attributes.get("percentage")) diff --git a/build/lib/unit/models/bill_pay.py b/build/lib/unit/models/bill_pay.py deleted file mode 100644 index 23e25713..00000000 --- a/build/lib/unit/models/bill_pay.py +++ /dev/null @@ -1,21 +0,0 @@ -import json -from typing import Optional -from unit.models import * - - -class BillerDTO(object): - def __init__(self, id: str, name: int, category: str): - self.id = id - self.type = "biller" - self.attributes = {"name": name, "category": category} - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BillerDTO(_id, attributes["name"], attributes["category"]) - - -class GetBillersParams(object): - def __init__(self, name: str, page: Optional[int] = None): - self.name = name - self.page = page - diff --git a/build/lib/unit/models/card.py b/build/lib/unit/models/card.py deleted file mode 100644 index bf0c4ab0..00000000 --- a/build/lib/unit/models/card.py +++ /dev/null @@ -1,617 +0,0 @@ -from unit.utils import date_utils -from unit.models import * - -CardStatus = Literal["Inactive", "Active", "Stolen", "Lost", "Frozen", "ClosedByCustomer", "SuspectedFraud"] - - -class IndividualDebitCardDTO(object): - def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, status: CardStatus, - shipping_address: Optional[Address], design: Optional[str], - relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.type = "individualDebitCard" - self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, - "status": status, "shippingAddress": shipping_address, "design": design} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - shipping_address = Address.from_json_api(attributes.get("shippingAddress")) if attributes.get("shippingAddress") else None - return IndividualDebitCardDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], - attributes["expirationDate"], attributes["status"], - shipping_address, attributes.get("design"), relationships - ) - - -class BusinessCardDTO(object): - def __init__(self, _id: str, _type: str, created_at: datetime, last_4_digits: str, expiration_date: str, - ssn: Optional[str], full_name: FullName, date_of_birth: date, address: Address, phone: Phone, - email: str, status: CardStatus, passport: Optional[str], nationality: Optional[str], - shipping_address: Optional[Address], design: Optional[str], - relationships: Optional[Dict[str, Relationship]], tags: Optional[Dict[str, str]]): - self.id = _id - self.type = _type - self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, - "ssn": ssn, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, - "phone": phone, "email": email, "status": status, "passport": passport, - "nationality": nationality, "shippingAddress": shipping_address, "design": design, - "tags": tags} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BusinessCardDTO( - _id, _type, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], - attributes["expirationDate"], attributes.get("ssn"), FullName.from_json_api(attributes["fullName"]), - attributes["dateOfBirth"], Address.from_json_api(attributes["address"]), - Phone.from_json_api(attributes["phone"]), attributes["email"], attributes["status"], - attributes.get("passport"), attributes.get("nationality"), - Address.from_json_api(attributes.get("shippingAddress")), attributes.get("design"), relationships, - attributes.get("tags") - ) - -class BusinessDebitCardDTO(BusinessCardDTO): - def __init__(self, card: BusinessCardDTO): - self.id = card.id - self.type = card.type - self.attributes = card.attributes - self.relationships = card.relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BusinessDebitCardDTO(BusinessCardDTO.from_json_api(_id, _type, attributes, relationships)) - - -class BusinessCreditCardDTO(BusinessCardDTO): - def __init__(self, card: BusinessCardDTO): - self.id = card.id - self.type = card.type - self.attributes = card.attributes - self.relationships = card.relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BusinessCreditCardDTO(BusinessCardDTO.from_json_api(_id, _type, attributes, relationships)) - -class IndividualVirtualDebitCardDTO(object): - def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, status: CardStatus, - relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.type = "individualVirtualDebitCard" - self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, - "status": status} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return IndividualVirtualDebitCardDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], - attributes["expirationDate"], attributes["status"], relationships - ) - - -class BusinessVirtualDebitCardDTO(object): - def __init__(self, id: str, created_at: datetime, last_4_digits: str, expiration_date: str, - full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: CardStatus, passport: Optional[str] = None, nationality: Optional[str] = None, - relationships: Optional[Dict[str, Relationship]] = None): - self.id = id - self.type = "businessVirtualDebitCard" - self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, - "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, - "phone": phone, "email": email, "status": status, "passport": passport, - "nationality": nationality} - self.relationships = relationships or {} - - def from_json_api(_id, _type, attributes, relationships): - return BusinessVirtualDebitCardDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], - attributes["expirationDate"], FullName.from_json_api(attributes["fullName"]), - attributes["dateOfBirth"], Address.from_json_api(attributes["address"]), - Phone.from_json_api(attributes["phone"]), attributes["email"], attributes["status"], - attributes.get("passport"), attributes.get("nationality"), relationships - ) - -class BusinessVirtualCardDTO(object): - def __init__(self, _id: str, _type: str, created_at: datetime, last_4_digits: str, expiration_date: str, - ssn: Optional[str], full_name: FullName, date_of_birth: date, address: Address, phone: Phone, - email: str, status: CardStatus, passport: Optional[str], nationality: Optional[str], - relationships: Optional[Dict[str, Relationship]], tags: Optional[Dict[str, str]]): - self.id = _id - self.type = _type - self.attributes = {"createdAt": created_at, "last4Digits": last_4_digits, "expirationDate": expiration_date, - "ssn": ssn, "fullName": full_name, "dateOfBirth": date_of_birth, "address": address, - "phone": phone, "email": email, "status": status, "passport": passport, - "nationality": nationality, "tags": tags} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BusinessVirtualCardDTO( - _id, _type, date_utils.to_datetime(attributes["createdAt"]), attributes["last4Digits"], - attributes["expirationDate"], attributes.get("ssn"), FullName.from_json_api(attributes["fullName"]), - attributes["dateOfBirth"], Address.from_json_api(attributes["address"]), - Phone.from_json_api(attributes["phone"]), attributes["email"], attributes["status"], - attributes.get("passport"), attributes.get("nationality"), relationships, attributes.get("tags")) - -class BusinessVirtualCreditCardDTO(BusinessVirtualCardDTO): - def __init__(self, card: BusinessVirtualCardDTO): - self.id = card.id - self.type = card.type - self.attributes = card.attributes - self.relationships = card.relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BusinessVirtualCreditCardDTO(BusinessVirtualCardDTO.from_json_api(_id, _type, attributes, relationships)) - -Card = Union[IndividualDebitCardDTO, BusinessDebitCardDTO, IndividualVirtualDebitCardDTO, BusinessVirtualDebitCardDTO, - BusinessVirtualCreditCardDTO, BusinessCreditCardDTO] - - -class CreateIndividualDebitCard(UnitRequest): - def __init__(self, relationships: Dict[str, Relationship], limits: Optional[CardLevelLimits] = None, - shipping_address: Optional[Address] = None, design: Optional[str] = None, - idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None): - self.shipping_address = shipping_address - self.design = design - self.limits = limits - self.idempotency_key = idempotency_key - self.tags = tags - self.relationships = relationships - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "individualDebitCard", - "attributes": {}, - "relationships": self.relationships - } - } - - if self.shipping_address: - payload["data"]["attributes"]["shippingAddress"] = self.shipping_address - - if self.limits: - payload["data"]["attributes"]["limits"] = self.limits - - if self.design: - payload["data"]["attributes"]["design"] = self.design - - if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - -class CreateBusinessCard(object): - def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - relationships: Dict[str, Relationship], shipping_address: Optional[Address] = None, - ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, - design: Optional[str] = None, idempotency_key: Optional[str] = None, - tags: Optional[Dict[str, str]] = None, limits: Optional[CardLevelLimits] = None, - additional_embossed_text: Optional[str] = None, print_only_business_name: Optional[bool] = None): - self.full_name = full_name - self.date_of_birth = date_of_birth - self.address = address - self.phone = phone - self.email = email - self.shipping_address = shipping_address - self.ssn = ssn - self.passport = passport - self.nationality = nationality - self.design = design - self.idempotency_key = idempotency_key - self.tags = tags - self.relationships = relationships - self.limits = limits - self.additional_embossed_text = additional_embossed_text - self.print_only_business_name = print_only_business_name - - def to_json_api(self, _type: str) -> Dict: - payload = { - "data": { - "type": _type, - "attributes": { - "fullName": self.full_name, - "dateOfBirth": self.date_of_birth, - "address": self.address, - "phone": self.phone, - "email": self.email, - }, - "relationships": self.relationships - } - } - - if self.shipping_address: - payload["data"]["attributes"]["shippingAddress"] = self.shipping_address - - if self.ssn: - payload["data"]["attributes"]["ssn"] = self.ssn - - if self.passport: - payload["data"]["attributes"]["passport"] = self.passport - - if self.nationality: - payload["data"]["attributes"]["nationality"] = self.nationality - - if self.design: - payload["data"]["attributes"]["design"] = self.design - - if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - if self.limits: - payload["data"]["attributes"]["limits"] = self.limits - - if self.additional_embossed_text: - payload["data"]["attributes"]["additionalEmbossedText"] = self.additional_embossed_text - - if self.print_only_business_name is not None: - payload["data"]["attributes"]["printOnlyBusinessName"] = self.print_only_business_name - - return payload - - def __repr__(self): - return json.dumps(self.to_json_api()) - - -class CreateBusinessDebitCard(CreateBusinessCard): - def to_json_api(self): - return super().to_json_api("businessDebitCard") - - -class CreateBusinessCreditCard(CreateBusinessCard): - def to_json_api(self): - return super().to_json_api("businessCreditCard") - -class CreateIndividualVirtualDebitCard(UnitRequest): - def __init__(self, relationships: Dict[str, Relationship], idempotency_key: Optional[str] = None, - limits: Optional[CardLevelLimits] = None, tags: Optional[Dict[str, str]] = None): - self.idempotency_key = idempotency_key - self.limits = limits - self.tags = tags - self.relationships = relationships - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "individualVirtualDebitCard", - "attributes": {}, - "relationships": self.relationships - } - } - - if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key - - if self.limits: - payload["data"]["attributes"]["limits"] = self.limits - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class CreateBusinessVirtualCard(object): - def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - relationships: Dict[str, Relationship], ssn: Optional[str] = None, passport: Optional[str] = None, - nationality: Optional[str] = None, idempotency_key: Optional[str] = None, - tags: Optional[Dict[str, str]] = None, limits: Optional[CardLevelLimits] = None): - self.full_name = full_name - self.date_of_birth = date_of_birth - self.address = address - self.phone = phone - self.email = email - self.ssn = ssn - self.passport = passport - self.nationality = nationality - self.idempotency_key = idempotency_key - self.tags = tags - self.relationships = relationships - self.limits = limits - - def to_json_api(self, _type: str) -> Dict: - payload = { - "data": { - "type": _type, - "attributes": { - "fullName": self.full_name, - "dateOfBirth": self.date_of_birth, - "address": self.address, - "phone": self.phone, - "email": self.email, - }, - "relationships": self.relationships - } - } - - if self.ssn: - payload["data"]["attributes"]["ssn"] = self.ssn - - if self.passport: - payload["data"]["attributes"]["passport"] = self.passport - - if self.nationality: - payload["data"]["attributes"]["nationality"] = self.nationality - - if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - if self.limits: - payload["data"]["attributes"]["limits"] = self.limits - - return payload - - def __repr__(self): - return json.dumps(self.to_json_api()) - - -class CreateBusinessVirtualDebitCard(CreateBusinessVirtualCard): - def to_json_api(self): - return super().to_json_api("businessVirtualDebitCard") - - -class CreateBusinessVirtualCreditCard(CreateBusinessVirtualCard): - def to_json_api(self): - return super().to_json_api("businessVirtualCreditCard") - - -CreateCardRequest = Union[CreateIndividualDebitCard, CreateBusinessDebitCard, CreateIndividualVirtualDebitCard, - CreateBusinessVirtualDebitCard, CreateBusinessVirtualCreditCard, CreateBusinessCreditCard] - -class PatchIndividualDebitCard(UnitRequest): - def __init__(self,card_id: str, shipping_address: Optional[Address] = None, design: Optional[str] = None, - limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): - self.card_id = card_id - self.shipping_address = shipping_address - self.design = design - self.limits = limits - self.tags = tags - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "individualDebitCard", - "attributes": {}, - } - } - - if self.shipping_address: - payload["data"]["attributes"]["shippingAddress"] = self.shipping_address - - if self.limits: - payload["data"]["attributes"]["limits"] = self.limits - - if self.design: - payload["data"]["attributes"]["design"] = self.design - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class PatchBusinessCard(object): - def __init__(self, card_id: str, shipping_address: Optional[Address] = None, address: Optional[Address] = None, - phone: Optional[Phone] = None, email: Optional[str] = None, design: Optional[str] = None, - tags: Optional[Dict[str, str]] = None, limits: Optional[CardLevelLimits] = None): - self.card_id = card_id - self.shipping_address = shipping_address - self.address = address - self.phone = phone - self.email = email - self.design = design - self.tags = tags - self.limits = limits - - def to_json_api(self, _type: str = "businessDebitCard") -> Dict: - payload = { - "data": { - "type": _type, - "attributes": {}, - } - } - - if self.shipping_address: - payload["data"]["attributes"]["shippingAddress"] = self.shipping_address - - if self.address: - payload["data"]["attributes"]["address"] = self.address - - if self.phone: - payload["data"]["attributes"]["phone"] = self.phone - - if self.email: - payload["data"]["attributes"]["email"] = self.email - - if self.design: - payload["data"]["attributes"]["design"] = self.design - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - if self.limits: - payload["data"]["attributes"]["limits"] = self.limits - - return payload - - def __repr__(self): - return json.dumps(self.to_json_api()) - - -class PatchBusinessDebitCard(PatchBusinessCard): - pass - - -class PatchBusinessCreditCard(PatchBusinessCard): - def to_json_api(self) -> Dict: - return super().to_json_api("businessCreditCard") - -class PatchIndividualVirtualDebitCard(UnitRequest): - def __init__(self, card_id: str, limits: CardLevelLimits = None, tags: Optional[Dict[str, str]] = None): - self.card_id = card_id - self.limits = limits - self.tags = tags - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "individualVirtualDebitCard", - "attributes": {}, - } - } - - if self.limits: - payload["data"]["attributes"]["limits"] = self.limits - - payload["data"]["attributes"]["tags"] = self.tags or {} - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - -class PatchBusinessVirtualCard(object): - def __init__(self, card_id: str, address: Optional[Address] = None, phone: Optional[Phone] = None, - email: Optional[str] = None, tags: Optional[Dict[str, str]] = None, - _type: str = "businessVirtualDebitCard", limits: Optional[CardLevelLimits] = None): - self.card_id = card_id - self.address = address - self.phone = phone - self.email = email - self.tags = tags - self.limits = limits - - def to_json_api(self, _type: str = "businessVirtualDebitCard") -> Dict: - payload = { - "data": { - "type": _type, - "attributes": {}, - } - } - - if self.address: - payload["data"]["attributes"]["address"] = self.address - - if self.phone: - payload["data"]["attributes"]["phone"] = self.phone - - if self.email: - payload["data"]["attributes"]["email"] = self.email - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - if self.limits: - payload["data"]["attributes"]["limits"] = self.limits - - return payload - - -class PatchBusinessVirtualDebitCard(PatchBusinessVirtualCard): - pass - - -class PatchBusinessVirtualCreditCard(PatchBusinessVirtualCard): - def to_json_api(self) -> Dict: - return super().to_json_api("businessVirtualCreditCard") - - -PatchCardRequest = Union[PatchIndividualDebitCard, PatchBusinessDebitCard, PatchIndividualVirtualDebitCard, - PatchBusinessVirtualDebitCard, PatchBusinessCreditCard, PatchBusinessVirtualCreditCard] - -class ReplaceCardRequest(object): - def __init__(self, shipping_address: Optional[Address] = None): - self.shipping_address = shipping_address - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "replaceCard", - "attributes": {}, - } - } - - if self.shipping_address: - payload["data"]["attributes"]["shippingAddress"] = self.shipping_address - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - -PinStatus = Literal["Set", "NotSet"] - -class PinStatusDTO(object): - def __init__(self, status: PinStatus): - self.type = "pinStatus" - self.attributes = {"status": status} - - @staticmethod - def from_json_api(attributes): - return PinStatusDTO(attributes["status"]) - - -class CardLimitsDTO(object): - def __init__(self, limits: CardLevelLimits, daily_totals: CardTotals, monthly_totals: CardTotals): - self.type = "limits" - self.attributes = {"limits": limits, "dailyTotals": daily_totals, "monthlyTotals": monthly_totals} - - @staticmethod - def from_json_api(attributes): - limits = CardLevelLimits.from_json_api(attributes.get("limits")) if attributes.get("limits") else None - return CardLimitsDTO(limits, CardTotals.from_json_api(attributes.get("dailyTotals")), - CardTotals.from_json_api(attributes.get("monthlyTotals"))) - - -class ListCardParams(UnitParams): - def __init__(self, offset: int = 0, limit: int = 100, account_id: Optional[str] = None, - customer_id: Optional[str] = None, tags: Optional[Dict[str, str]] = None, include: Optional[str] = None, - sort: Optional[Literal["createdAt", "-createdAt"]] = None, - status: Optional[List[CardStatus]] = None): - self.offset = offset - self.limit = limit - self.account_id = account_id - self.customer_id = customer_id - self.tags = tags - self.include = include - self.sort = sort - self.status = status - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.customer_id: - parameters["filter[customerId]"] = self.customer_id - if self.account_id: - parameters["filter[accountId]"] = self.account_id - if self.tags: - parameters["filter[tags]"] = json.dumps(self.tags) - if self.include: - parameters["include"] = self.include - if self.sort: - parameters["sort"] = self.sort - if self.status: - for idx, status_filter in enumerate(self.status): - parameters[f"filter[status][{idx}]"] = status_filter - return parameters - diff --git a/build/lib/unit/models/codecs.py b/build/lib/unit/models/codecs.py deleted file mode 100644 index ed7d296e..00000000 --- a/build/lib/unit/models/codecs.py +++ /dev/null @@ -1,403 +0,0 @@ -import json -from unit.models import * -from datetime import datetime, date - -from unit.models.reward import RewardDTO -from unit.utils import date_utils -from unit.models.applicationForm import ApplicationFormDTO -from unit.models.application import IndividualApplicationDTO, BusinessApplicationDTO, ApplicationDocumentDTO -from unit.models.account import DepositAccountDTO, AccountLimitsDTO -from unit.models.customer import IndividualCustomerDTO, BusinessCustomerDTO -from unit.models.card import IndividualDebitCardDTO, BusinessDebitCardDTO, IndividualVirtualDebitCardDTO,\ - BusinessVirtualDebitCardDTO, PinStatusDTO, CardLimitsDTO -from unit.models.transaction import * -from unit.models.payment import AchPaymentDTO, BookPaymentDTO, WirePaymentDTO, AchReceivedPaymentDTO, BillPaymentDTO, \ - SimulateIncomingAchPaymentDTO -from unit.models.customerToken import CustomerTokenDTO, CustomerVerificationTokenDTO -from unit.models.fee import FeeDTO -from unit.models.event import * -from unit.models.counterparty import CounterpartyDTO, CounterpartyBalanceDTO -from unit.models.webhook import WebhookDTO -from unit.models.institution import InstitutionDTO -from unit.models.statement import StatementDTO -from unit.models.atm_location import AtmLocationDTO -from unit.models.bill_pay import BillerDTO -from unit.models.api_token import APITokenDTO -from unit.models.authorization import AuthorizationDTO -from unit.models.authorization_request import PurchaseAuthorizationRequestDTO -from unit.models.account_end_of_day import AccountEndOfDayDTO -from unit.models.benificial_owner import BenificialOwnerDTO - -mappings = { - "individualApplication": lambda _id, _type, attributes, relationships: - IndividualApplicationDTO.from_json_api(_id, _type, attributes, relationships), - - "businessApplication": lambda _id, _type, attributes, relationships: - BusinessApplicationDTO.from_json_api(_id, _type, attributes, relationships), - - "document": lambda _id, _type, attributes, relationships: - ApplicationDocumentDTO.from_json_api(_id, _type, attributes), - - "individualCustomer": lambda _id, _type, attributes, relationships: - IndividualCustomerDTO.from_json_api(_id, _type, attributes, relationships), - - "businessCustomer": lambda _id, _type, attributes, relationships: - BusinessCustomerDTO.from_json_api(_id, _type, attributes, relationships), - - "depositAccount": lambda _id, _type, attributes, relationships: - DepositAccountDTO.from_json_api(_id, _type, attributes, relationships), - - "limits": lambda _id, _type, attributes, relationships: - decode_limits(attributes), - - "individualDebitCard": lambda _id, _type, attributes, relationships: - IndividualDebitCardDTO.from_json_api(_id, _type, attributes, relationships), - - "businessDebitCard": lambda _id, _type, attributes, relationships: - BusinessDebitCardDTO.from_json_api(_id, _type, attributes, relationships), - - "individualVirtualDebitCard": lambda _id, _type, attributes, relationships: - IndividualVirtualDebitCardDTO.from_json_api(_id, _type, attributes, relationships), - - "businessVirtualDebitCard": lambda _id, _type, attributes, relationships: - BusinessVirtualDebitCardDTO.from_json_api(_id, _type, attributes, relationships), - - "originatedAchTransaction": lambda _id, _type, attributes, relationships: - OriginatedAchTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "receivedAchTransaction": lambda _id, _type, attributes, relationships: - ReceivedAchTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "returnedAchTransaction": lambda _id, _type, attributes, relationships: - ReturnedAchTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "returnedReceivedAchTransaction": lambda _id, _type, attributes, relationships: - ReturnedReceivedAchTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "dishonoredAchTransaction": lambda _id, _type, attributes, relationships: - DishonoredAchTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "bookTransaction": lambda _id, _type, attributes, relationships: - BookTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "purchaseTransaction": lambda _id, _type, attributes, relationships: - PurchaseTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "atmTransaction": lambda _id, _type, attributes, relationships: - AtmTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "feeTransaction": lambda _id, _type, attributes, relationships: - FeeTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "cardTransaction": lambda _id, _type, attributes, relationships: - CardTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "wireTransaction": lambda _id, _type, attributes, relationships: - WireTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "releaseTransaction": lambda _id, _type, attributes, relationships: - ReleaseTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "adjustmentTransaction": lambda _id, _type, attributes, relationships: - AdjustmentTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "interestTransaction": lambda _id, _type, attributes, relationships: - InterestTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "disputeTransaction": lambda _id, _type, attributes, relationships: - DisputeTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "checkDepositTransaction": lambda _id, _type, attributes, relationships: - CheckDepositTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "returnedCheckDepositTransaction": lambda _id, _type, attributes, relationships: - ReturnedCheckDepositTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - #"paymentAdvanceTransaction": lambda _id, _type, attributes, relationships: - #PaymentAdvanceTransactionTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "repaidPaymentAdvanceTransaction": lambda _id, _type, attributes, relationships: - RepaidPaymentAdvanceTransactionDTO.from_json_api(_id, _type, attributes, relationships), - - "achPayment": lambda _id, _type, attributes, relationships: - AchPaymentDTO.from_json_api(_id, _type, attributes, relationships), - - "bookPayment": lambda _id, _type, attributes, relationships: - BookPaymentDTO.from_json_api(_id, _type, attributes, relationships), - - "wirePayment": lambda _id, _type, attributes, relationships: - WirePaymentDTO.from_json_api(_id, _type, attributes, relationships), - - "billPayment": lambda _id, _type, attributes, relationships: - BillPaymentDTO.from_json_api(_id, _type, attributes, relationships), - - "achReceivedPayment": lambda _id, _type, attributes, relationships: - AchReceivedPaymentDTO.from_json_api(_id, _type, attributes, relationships), - - "accountStatementDTO": lambda _id, _type, attributes, relationships: - StatementDTO.from_json_api(_id, _type, attributes, relationships), - - "sandboxAccountStatement": lambda _id, _type, attributes, relationships: - StatementDTO.from_json_api(_id, _type, attributes, relationships), - - "customerBearerToken": lambda _id, _type, attributes, relationships: - CustomerTokenDTO.from_json_api(_id, _type, attributes, relationships), - - "customerTokenVerification": lambda _id, _type, attributes, relationships: - CustomerVerificationTokenDTO.from_json_api(_id, _type, attributes, relationships), - - "achCounterparty": lambda _id, _type, attributes, relationships: - CounterpartyDTO.from_json_api(_id, _type, attributes, relationships), - - "applicationForm": lambda _id, _type, attributes, relationships: - ApplicationFormDTO.from_json_api(_id, _type, attributes, relationships), - - "fee": lambda _id, _type, attributes, relationships: - FeeDTO.from_json_api(_id, _type, attributes, relationships), - - "account.closed": lambda _id, _type, attributes, relationships: - AccountClosedEvent.from_json_api(_id, _type, attributes, relationships), - - "account.frozen": lambda _id, _type, attributes, relationships: - AccountFrozenEvent.from_json_api(_id, _type, attributes, relationships), - - "application.awaitingDocuments": lambda _id, _type, attributes, relationships: - ApplicationAwaitingDocumentsEvent.from_json_api(_id, _type, attributes, relationships), - - "application.denied": lambda _id, _type, attributes, relationships: - ApplicationDeniedEvent.from_json_api(_id, _type, attributes, relationships), - - "application.pendingReview": lambda _id, _type, attributes, relationships: - ApplicationPendingReviewEvent.from_json_api(_id, _type, attributes, relationships), - - "card.activated": lambda _id, _type, attributes, relationships: - CardActivatedEvent.from_json_api(_id, _type, attributes, relationships), - - "card.statusChanged": lambda _id, _type, attributes, relationships: - CardStatusChangedEvent.from_json_api(_id, _type, attributes, relationships), - - "authorization.created": lambda _id, _type, attributes, relationships: - AuthorizationCreatedEvent.from_json_api(_id, _type, attributes, relationships), - - "authorization.canceled": lambda _id, _type, attributes, relationships: - AuthorizationCanceledEvent.from_json_api(_id, _type, attributes, relationships), - - "authorization.declined": lambda _id, _type, attributes, relationships: - AuthorizationDeclinedEvent.from_json_api(_id, _type, attributes, relationships), - - "authorizationRequest.declined": lambda _id, _type, attributes, relationships: - AuthorizationRequestDeclinedEvent.from_json_api(_id, _type, attributes, relationships), - - "authorizationRequest.pending": lambda _id, _type, attributes, relationships: - AuthorizationRequestPendingEvent.from_json_api(_id, _type, attributes, relationships), - - "authorizationRequest.approved": lambda _id, _type, attributes, relationships: - AuthorizationRequestApprovedEvent.from_json_api(_id, _type, attributes, relationships), - - "document.rejected": lambda _id, _type, attributes, relationships: - DocumentRejectedEvent.from_json_api(_id, _type, attributes, relationships), - - "document.approved": lambda _id, _type, attributes, relationships: - DocumentApprovedEvent.from_json_api(_id, _type, attributes, relationships), - - "checkDeposit.created": lambda _id, _type, attributes, relationships: - CheckDepositCreatedEvent.from_json_api(_id, _type, attributes, relationships), - - "checkDeposit.clearing": lambda _id, _type, attributes, relationships: - CheckDepositClearingEvent.from_json_api(_id, _type, attributes, relationships), - - "checkDeposit.sent": lambda _id, _type, attributes, relationships: - CheckDepositSentEvent.from_json_api(_id, _type, attributes, relationships), - - "payment.clearing": lambda _id, _type, attributes, relationships: - PaymentClearingEvent.from_json_api(_id, _type, attributes, relationships), - - "payment.created": lambda _id, _type, attributes, relationships: - PaymentCreatedEvent.from_json_api(_id, _type, attributes, relationships), - - "payment.sent": lambda _id, _type, attributes, relationships: - PaymentSentEvent.from_json_api(_id, _type, attributes, relationships), - - "payment.returned": lambda _id, _type, attributes, relationships: - PaymentReturnedEvent.from_json_api(_id, _type, attributes, relationships), - - "payment.rejected": lambda _id, _type, attributes, relationships: - PaymentRejectedEvent.from_json_api(_id, _type, attributes, relationships), - - "statements.created": lambda _id, _type, attributes, relationships: - StatementsCreatedEvent.from_json_api(_id, _type, attributes, relationships), - - "transaction.created": lambda _id, _type, attributes, relationships: - TransactionCreatedEvent.from_json_api(_id, _type, attributes, relationships), - - "customer.created": lambda _id, _type, attributes, relationships: - CustomerCreatedEvent.from_json_api(_id, _type, attributes, relationships), - - "customer.updated": lambda _id, _type, attributes, relationships: - CustomerUpdatedEvent.from_json_api(_id, _type, attributes, relationships), - - "account.reopened": lambda _id, _type, attributes, relationships: - AccountReopenedEvent.from_json_api(_id, _type, attributes, relationships), - - "webhook": lambda _id, _type, attributes, relationships: - WebhookDTO.from_json_api(_id, _type, attributes, relationships), - - "institution": lambda _id, _type, attributes, relationships: - InstitutionDTO.from_json_api(_id, _type, attributes, relationships), - - "atmLocation": lambda _id, _type, attributes, relationships: - AtmLocationDTO.from_json_api(_type, attributes), - - "biller": lambda _id, _type, attributes, relationships: - BillerDTO.from_json_api(_id, _type, attributes, relationships), - - "apiToken": lambda _id, _type, attributes, relationships: - APITokenDTO.from_json_api(_id, _type, attributes, relationships), - - "authorization": lambda _id, _type, attributes, relationships: - AuthorizationDTO.from_json_api(_id, _type, attributes, relationships), - - "purchaseAuthorizationRequest": lambda _id, _type, attributes, relationships: - PurchaseAuthorizationRequestDTO.from_json_api(_id, _type, attributes, relationships), - - "accountEndOfDay": lambda _id, _type, attributes, relationships: - AccountEndOfDayDTO.from_json_api(_id, _type, attributes, relationships), - - "counterpartyBalance": lambda _id, _type, attributes, relationships: - CounterpartyBalanceDTO.from_json_api(_id, _type, attributes, relationships), - - "pinStatus": lambda _id, _type, attributes, relationships: - PinStatusDTO.from_json_api(attributes), - - "beneficialOwner": lambda _id, _type, attributes, relationships: - BenificialOwnerDTO.from_json_api(attributes), - - "reward": lambda _id, _type, attributes, relationships: - RewardDTO.from_json_api(_id, attributes, relationships), - -} - - -def split_json_api_single_response(payload: Dict): - _id, _type, attributes = payload.get("id"), payload["type"], payload["attributes"] - relationships = None - - if payload.get("relationships"): - relationships = dict() - for k, v in payload.get("relationships").items(): - if isinstance(v["data"], list): - # todo: alex handle cases when relationships are in a form of array (e.g. jointAccount or documents) - continue - else: - relationships[k] = Relationship(v["data"]["type"], v["data"]["id"]) - - return _id, _type, attributes, relationships - - -def split_json_api_array_response(payload): - if not isinstance(payload, list): - raise Exception("split_json_api_array_response - couldn't parse response.") - - dtos = [] - for single_obj in payload: - dtos.append(split_json_api_single_response(single_obj)) - - return dtos - - -def decode_limits(attributes: Dict): - if "ach" in attributes.keys(): - return AccountLimitsDTO.from_json_api(attributes) - else: - return CardLimitsDTO.from_json_api(attributes) - -def mapping_wraper(_id, _type, attributes, relationships): - if _type in mappings: - return mappings[_type](_id, _type, attributes, relationships) - else: - return RawUnitObject(_id, _type, attributes, relationships) - -class DtoDecoder(object): - @staticmethod - def decode(payload): - if payload is None: - return None - # if response contains a list of dtos - if isinstance(payload, list): - dtos = split_json_api_array_response(payload) - response = [] - for _id, _type, attributes, relationships in dtos: - response.append(mapping_wraper(_id, _type, attributes, relationships)) - - return response - else: - _id, _type, attributes, relationships = split_json_api_single_response(payload) - return mapping_wraper(_id, _type, attributes, relationships) - -class UnitEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, CardLevelLimits): - return { - "dailyWithdrawal": obj.daily_withdrawal, - "dailyPurchase": obj.daily_purchase, - "monthlyWithdrawal": obj.monthly_withdrawal, - "monthlyPurchase": obj.monthly_purchase - } - if isinstance(obj, FullName): - return {"first": obj.first, "last": obj.last} - if isinstance(obj, Phone): - return {"countryCode": obj.country_code, "number": obj.number} - if isinstance(obj, Address): - addr = { - "street": obj.street, - "city": obj.city, - "state": obj.state, - "postalCode": obj.postal_code, - "country": obj.country - } - - if obj.street2 is not None: - addr["street2"] = obj.street2 - return addr - if isinstance(obj, BusinessContact): - return {"fullName": obj.full_name, "email": obj.email, "phone": obj.phone} - if isinstance(obj, AuthorizedUser): - return {"fullName": obj.full_name, "email": obj.email, "phone": obj.phone} - if isinstance(obj, Officer): - officer = {"fullName": obj.full_name, "dateOfBirth": date_utils.to_date_str(obj.date_of_birth), - "address": obj.address, "phone": obj.phone, "email": obj.email} - if obj.status is not None: - officer["status"] = obj.status - if obj.title is not None: - officer["title"] = obj.title - if obj.ssn is not None: - officer["ssn"] = obj.ssn - if obj.passport is not None: - officer["passport"] = obj.passport - if obj.nationality is not None: - officer["nationality"] = obj.nationality - return officer - if isinstance(obj, BeneficialOwner): - beneficial_owner = {"fullName": obj.full_name, "dateOfBirth": date_utils.to_date_str(obj.date_of_birth), - "address": obj.address, "phone": obj.phone, "email": obj.email} - if obj.status is not None: - beneficial_owner["status"] = obj.status - if obj.ssn is not None: - beneficial_owner["ssn"] = obj.ssn - if obj.passport is not None: - beneficial_owner["passport"] = obj.passport - if obj.nationality is not None: - beneficial_owner["nationality"] = obj.nationality - if obj.percentage is not None: - beneficial_owner["percentage"] = obj.percentage - return beneficial_owner - if isinstance(obj, RelationshipArray): - return {"data": list(map(lambda r: r.to_dict(), obj.relationships))} - if isinstance(obj, Relationship): - return {"data": obj.to_dict()} - if isinstance(obj, Counterparty): - return {"routingNumber": obj.routing_number, "accountNumber": obj.account_number, - "accountType": obj.account_type, "name": obj.name} - if isinstance(obj, Coordinates): - return {"longitude": obj.longitude, "latitude": obj.latitude} - return json.JSONEncoder.default(self, obj) diff --git a/build/lib/unit/models/counterparty.py b/build/lib/unit/models/counterparty.py deleted file mode 100644 index e043b414..00000000 --- a/build/lib/unit/models/counterparty.py +++ /dev/null @@ -1,172 +0,0 @@ -import json -from datetime import datetime, date -from typing import Optional -from unit.utils import date_utils -from unit.models import * - - -class CounterpartyDTO(object): - def __init__(self, id: str, created_at: datetime, name: str, routing_number: str, bank: Optional[str], - account_number: str, account_type: str, type: str, permissions: str, - relationships: [Dict[str, Relationship]]): - self.id = id - self.type = "achCounterparty" - self.attributes = {"createdAt": created_at, "name": name, "routingNumber": routing_number, "bank": bank, - "accountNumber": account_number, "accountType": account_type, "type": type, - "permissions": permissions} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CounterpartyDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["name"], - attributes["routingNumber"], attributes.get("bank"), attributes["accountNumber"], - attributes["accountType"], attributes["type"], attributes["permissions"], relationships) - - -class CreateCounterpartyRequest(UnitRequest): - def __init__(self, name: str, routing_number: str, account_number: str, account_type: str, type: str, - relationships: [Dict[str, Relationship]], tags: Optional[object] = None, - idempotency_key: Optional[str] = None): - self.name = name - self.routing_number = routing_number - self.account_number = account_number - self.account_type = account_type - self.type = type - self.relationships = relationships - self.tags = tags - self.idempotency_key = idempotency_key - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "achCounterparty", - "attributes": { - "name": self.name, - "routingNumber": self.routing_number, - "accountNumber": self.account_number, - "accountType": self.account_type, - "type": self.type - }, - "relationships": self.relationships - } - } - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class CreateCounterpartyWithTokenRequest(UnitRequest): - def __init__(self, name: str, type: str, plaid_processor_token: str, relationships: [Dict[str, Relationship]], - verify_name: Optional[bool] = None, permissions: Optional[str] = None, tags: Optional[object] = None, - idempotency_key: Optional[str] = None): - self.name = name - self.type = type - self.plaid_processor_token = plaid_processor_token - self.verify_name = verify_name - self.permissions = permissions - self.relationships = relationships - self.tags = tags - self.idempotency_key = idempotency_key - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "achCounterparty", - "attributes": { - "name": self.name, - "type": self.type, - "plaidProcessorToken": self.plaid_processor_token - }, - "relationships": self.relationships - } - } - - if self.verify_name: - payload["data"]["attributes"]["verifyName"] = self.verify_name - - if self.permissions: - payload["data"]["attributes"]["permissions"] = self.permissions - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key - - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class PatchCounterpartyRequest(object): - def __init__(self, counterparty_id: str, plaid_processor_token: str, verify_name: Optional[bool] = None, - permissions: Optional[str] = None, tags: Optional[object] = None): - self.counterparty_id = counterparty_id - self.plaid_processor_token = plaid_processor_token - self.verify_name = verify_name - self.permissions = permissions - self.tags = tags - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "counterparty", - "attributes": { - "plaidProcessorToken": self.plaid_processor_token - } - } - } - - if self.verify_name: - payload["data"]["attributes"]["verifyName"] = self.verify_name - - if self.permissions: - payload["data"]["attributes"]["permissions"] = self.permissions - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class CounterpartyBalanceDTO(object): - def __init__(self, id: str, balance: int, available: int, relationships: [Dict[str, Relationship]]): - self.id = id - self.type = "counterpartyBalance" - self.attributes = {"balance": balance, "available": available} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CounterpartyBalanceDTO(_id, attributes["balance"], attributes["available"], relationships) - - -class ListCounterpartyParams(UnitParams): - def __init__(self, offset: int = 0, limit: int = 100, customer_id: Optional[str] = None, - tags: Optional[object] = None): - self.offset = offset - self.limit = limit - self.customer_id = customer_id - self.tags = tags - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.customer_id: - parameters["filter[customerId]"] = self.customer_id - if self.tags: - parameters["filter[tags]"] = self.tags - return parameters - diff --git a/build/lib/unit/models/customer.py b/build/lib/unit/models/customer.py deleted file mode 100644 index 83ebb29b..00000000 --- a/build/lib/unit/models/customer.py +++ /dev/null @@ -1,158 +0,0 @@ -from unit.utils import date_utils -from unit.models import * - - -class IndividualCustomerDTO(object): - def __init__(self, id: str, created_at: datetime, full_name: FullName, date_of_birth: date, address: Address, - phone: Phone, email: str, ssn: Optional[str], passport: Optional[str], nationality: Optional[str], - authorized_users: [AuthorizedUser], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.type = 'individualCustomer' - self.attributes = {"createdAt": created_at, "fullName": full_name, "dateOfBirth": date_of_birth, - "address": address, "phone": phone, "email": email, "ssn": ssn, "passport": passport, - "nationality": nationality, "authorizedUsers": authorized_users, "tags": tags} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return IndividualCustomerDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), - FullName.from_json_api(attributes["fullName"]), date_utils.to_date(attributes["dateOfBirth"]), - Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), - attributes["email"], attributes.get("ssn"), attributes.get("passport"), attributes.get("nationality"), - AuthorizedUser.from_json_api(attributes["authorizedUsers"]), attributes.get("tags"), relationships - ) - - -class BusinessCustomerDTO(object): - def __init__(self, id: str, created_at: datetime, name: str, address: Address, phone: Phone, - state_of_incorporation: str, ein: str, entity_type: EntityType, contact: BusinessContact, - authorized_users: [AuthorizedUser], dba: Optional[str], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.type = 'businessCustomer' - self.attributes = {"createdAt": created_at, "name": name, "address": address, "phone": phone, - "stateOfIncorporation": state_of_incorporation, "ein": ein, "entityType": entity_type, - "contact": contact, "authorizedUsers": authorized_users, "dba": dba, "tags": tags} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BusinessCustomerDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["name"], - Address.from_json_api(attributes["address"]), Phone.from_json_api(attributes["phone"]), - attributes["stateOfIncorporation"], attributes["ein"], attributes["entityType"], - BusinessContact.from_json_api(attributes["contact"]), - AuthorizedUser.from_json_api(attributes["authorizedUsers"]), - attributes.get("dba"), attributes.get("tags"), relationships) - -CustomerDTO = Union[IndividualCustomerDTO, BusinessCustomerDTO] - - -class PatchIndividualCustomerRequest(UnitRequest): - def __init__(self, customer_id: str, address: Optional[Address] = None, phone: Optional[Phone] = None, - email: Optional[str] = None, dba: Optional[str] = None, - authorized_users: Optional[List[AuthorizedUser]] = None, tags: Optional[Dict[str, str]] = None): - self.customer_id = customer_id - self.address = address - self.phone = phone - self.email = email - self.dba = dba - self.authorized_users = authorized_users - self.tags = tags - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "individualCustomer", - "attributes": {} - } - } - - if self.address: - payload["data"]["attributes"]["address"] = self.address - - if self.phone: - payload["data"]["attributes"]["phone"] = self.phone - - if self.email: - payload["data"]["attributes"]["email"] = self.email - - if self.dba: - payload["data"]["attributes"]["dba"] = self.dba - - if self.authorized_users: - payload["data"]["attributes"]["authorizedUsers"] = self.authorized_users - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class PatchBusinessCustomerRequest(UnitRequest): - def __init__(self, customer_id: str, address: Optional[Address] = None, phone: Optional[Phone] = None, - contact: Optional[BusinessContact] = None, authorized_users: Optional[List[AuthorizedUser]] = None, - tags: Optional[Dict[str, str]] = None): - self.customer_id = customer_id - self.address = address - self.phone = phone - self.contact = contact - self.authorized_users = authorized_users - self.tags = tags - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "businessCustomer", - "attributes": {} - } - } - - if self.address: - payload["data"]["attributes"]["address"] = self.address - - if self.phone: - payload["data"]["attributes"]["phone"] = self.phone - - if self.contact: - payload["data"]["attributes"]["contact"] = self.contact - - if self.authorized_users: - payload["data"]["attributes"]["authorizedUsers"] = self.authorized_users - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class ListCustomerParams(UnitParams): - def __init__(self, offset: int = 0, limit: int = 100, query: Optional[str] = None, email: Optional[str] = None, - tags: Optional[object] = None, sort: Optional[Literal["createdAt", "-createdAt"]] = None): - self.offset = offset - self.limit = limit - self.query = query - self.email = email - self.tags = tags - self.sort = sort - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.query: - parameters["filter[query]"] = self.query - if self.email: - parameters["filter[email]"] = self.email - if self.tags: - parameters["filter[tags]"] = self.tags - if self.sort: - parameters["sort"] = self.sort - return parameters - diff --git a/build/lib/unit/models/customerToken.py b/build/lib/unit/models/customerToken.py deleted file mode 100644 index 856ab64a..00000000 --- a/build/lib/unit/models/customerToken.py +++ /dev/null @@ -1,89 +0,0 @@ -from unit.models import * - -class CustomerTokenDTO(object): - def __init__(self, token: str, expires_in: int): - self.type = "customerBearerToken" - self.attributes = {"token": token, "expiresIn": expires_in} - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CustomerTokenDTO(attributes["token"], attributes["expiresIn"]) - -class CustomerVerificationTokenDTO(object): - def __init__(self, verification_token: str): - self.type = "customerTokenVerification" - self.attributes = {"verificationToken": verification_token} - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CustomerVerificationTokenDTO(attributes["verificationToken"]) - - -class CreateCustomerToken(UnitRequest): - def __init__(self, customer_id: str, scope: str, verification_token: Optional[str] = None, - verification_code: Optional[str] = None, expires_in: Optional[int] = None): - self.customer_id = customer_id - self.scope = scope - self.verification_token = verification_token - self.verification_code = verification_code - self.expires_in = expires_in - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "customerToken", - "attributes": { - "scope": self.scope - } - } - } - - if self.expires_in: - payload["data"]["attributes"]["expiresIn"] = self.expires_in - - if self.verification_token: - payload["data"]["attributes"]["verificationToken"] = self.verification_token - - if self.verification_code: - payload["data"]["attributes"]["verificationCode"] = self.verification_code - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class CreateCustomerTokenVerification(UnitRequest): - - def __init__(self, customer_id: str, channel: str, phone: Optional[Phone] = None, app_hash: Optional[str] = None, - language: Optional[str] = None): - self.customer_id = customer_id - self.channel = channel - self.phone = phone - self.app_hash = app_hash - self.language = language - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "customerTokenVerification", - "attributes": { - "channel": self.channel - } - } - } - - if self.phone: - payload["data"]["attributes"]["phone"] = self.phone - - if self.app_hash: - payload["data"]["attributes"]["appHash"] = self.app_hash - - if self.language: - payload["data"]["attributes"]["language"] = self.language - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - diff --git a/build/lib/unit/models/event.py b/build/lib/unit/models/event.py deleted file mode 100644 index e6fcfa72..00000000 --- a/build/lib/unit/models/event.py +++ /dev/null @@ -1,463 +0,0 @@ -import json -from datetime import datetime, date -from typing import Literal, Optional -from unit.utils import date_utils -from unit.models import * - -class BaseEvent(object): - def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.attributes = {"createdAt": created_at, "tags": tags} - self.relationships = relationships - - -class AccountClosedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, close_reason: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'account.closed' - self.attributes["closeReason"] = close_reason - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AccountClosedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["closeReason"], - attributes.get("tags"), relationships) - - -class AccountFrozenEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, freeze_reason: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'account.frozen' - self.attributes["freezeReason"] = freeze_reason - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AccountFrozenEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["freezeReason"], - attributes.get("tags"), relationships) - - -class ApplicationDeniedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'application.denied' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return ApplicationDeniedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), - relationships) - - -class ApplicationPendingReviewEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'application.pendingReview' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return ApplicationPendingReviewEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes.get("tags"), relationships) - - -class ApplicationAwaitingDocumentsEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'application.awaitingDocuments' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return ApplicationAwaitingDocumentsEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes.get("tags"), relationships) - -class AuthorizationCanceledEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, - recurring: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'authorization.canceled' - self.attributes["cardLast4Digits"] = card_last_4_digits - self.attributes["amount"] = amount - self.attributes["recurring"] = recurring - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AuthorizationCanceledEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["amount"], attributes["cardLast4Digits"], - attributes["recurring"], attributes.get("tags"), - relationships) - -class AuthorizationDeclinedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, merchant: Merchant, - reason: str, recurring: str, tags: Optional[Dict[str, str]] = None, - relationships: Optional[Dict[str, Relationship]] = None): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'authorization.declined' - self.attributes["cardLast4Digits"] = card_last_4_digits - self.attributes["amount"] = amount - self.attributes["reason"] = reason - self.attributes["merchant"] = merchant - self.attributes["recurring"] = recurring - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AuthorizationDeclinedEvent( - id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), - amount=attributes["amount"], card_last_4_digits=attributes["cardLast4Digits"], - merchant=Merchant.from_json_api(attributes["merchant"]), reason=attributes.get("reason"), recurring=attributes["recurring"], - tags=attributes.get("tags"), relationships=relationships) - -class AuthorizationCreatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, amount: int, card_last_4_digits: str, merchant: Merchant, - recurring: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'authorization.created' - self.attributes["cardLast4Digits"] = card_last_4_digits - self.attributes["amount"] = amount - self.attributes["merchant"] = merchant - self.attributes["recurring"] = recurring - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AuthorizationCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["amount"], attributes["cardLast4Digits"], Merchant.from_json_api(attributes["merchant"]), - attributes["recurring"], attributes.get("tags"), relationships) - -class AuthorizationRequestApprovedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, amount: str, status: str, approved_amount: str, - partial_approval_allowed: str, merchant: Dict[str, str], recurring: str, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'authorizationRequest.approved' - self.attributes["amount"] = amount - self.attributes["status"] = status - self.attributes["approvedAmount"] = approved_amount - self.attributes["partialApprovalAllowed"] = partial_approval_allowed - self.attributes["merchant"] = merchant - self.attributes["recurring"] = recurring - - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AuthorizationRequestApprovedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["amount"], attributes["status"], - attributes["approvedAmount"], attributes["partialApprovalAllowed"], - attributes["merchant"], attributes["recurring"], - attributes.get("tags"), relationships) - - -class AuthorizationRequestDeclinedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, amount: str, status: str, decline_reason: str, - partial_approval_allowed: str, merchant: Dict[str, str], recurring: str, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'authorizationRequest.declined' - self.attributes["amount"] = amount - self.attributes["status"] = status - self.attributes["declineReason"] = decline_reason - self.attributes["partialApprovalAllowed"] = partial_approval_allowed - self.attributes["merchant"] = merchant - self.attributes["recurring"] = recurring - - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AuthorizationRequestDeclinedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["amount"], attributes["status"], - attributes["declineReason"], attributes["partialApprovalAllowed"], - attributes["merchant"], attributes["recurring"], - attributes.get("tags"), relationships) - - -class AuthorizationRequestPendingEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, amount: int, status: str, partial_approval_allowed: str, - card_present: bool, digital_wallet: str, ecommerce: bool, merchant: Merchant, recurring: str, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'authorizationRequest.pending' - self.attributes["amount"] = amount - self.attributes["status"] = status - self.attributes["cardPresent"] = card_present - self.attributes["digitalWallet"] = digital_wallet - self.attributes["ecommerce"] = ecommerce - self.attributes["partialApprovalAllowed"] = partial_approval_allowed - self.attributes["merchant"] = merchant - self.attributes["recurring"] = recurring - - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AuthorizationRequestPendingEvent( - id=_id, - created_at=date_utils.to_datetime(attributes["createdAt"]), - amount=attributes["amount"], - status=attributes["status"], - ecommerce=attributes.get("ecommerce"), - card_present=attributes.get("cardPresent"), - digital_wallet=attributes.get("digitalWallet"), - partial_approval_allowed=attributes["partialApprovalAllowed"], - merchant=Merchant.from_json_api(attributes["merchant"]), - recurring=attributes["recurring"], - tags=attributes.get("tags"), - relationships=relationships - ) - -class CardActivatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'card.activated' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CardActivatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), - relationships) - - -class CardStatusChangedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, new_status: str, previous_status: str, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'card.statusChanged' - self.attributes["newStatus"] = new_status - self.attributes["previousStatus"] = previous_status - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CardStatusChangedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["newStatus"], - attributes["previousStatus"], attributes.get("tags"), relationships) - - -class CheckDepositCreatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'checkDeposit.created' - self.attributes["status"] = status - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CheckDepositCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["status"], attributes.get("tags"), relationships) - -class CheckDepositClearingEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'checkDeposit.clearing' - self.attributes["previousStatus"] = previous_status - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CheckDepositClearingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["previousStatus"], attributes.get("tags"), relationships) - - -class CheckDepositSentEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'checkDeposit.sent' - self.attributes["previousStatus"] = previous_status - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CheckDepositSentEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["previousStatus"], attributes.get("tags"), relationships) - - -class CheckDepositReturnedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'checkDeposit.returned' - self.attributes["previousStatus"] = previous_status - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CheckDepositReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["previousStatus"], attributes.get("tags"), relationships) - - -class CustomerCreatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'customer.created' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CustomerCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes.get("tags"), relationships) - - -class CustomerUpdatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'customer.updated' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CustomerUpdatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes.get("tags"), relationships) - - -class DocumentApprovedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'document.approved' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return DocumentApprovedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes.get("tags"), relationships) - -class DocumentRejectedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, reason: str, reason_code: str, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'document.rejected' - self.attributes["reason"] = reason - self.attributes["reasonCode"] = reason_code - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return DocumentRejectedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["reason"], attributes["reasonCode"], attributes.get("tags"), - relationships) - -class PaymentCreatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'payment.created' - self.attributes["status"] = status - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return PaymentCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], - attributes.get("tags"), relationships) - -class PaymentRejectedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'payment.rejected' - self.attributes["status"] = status - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return PaymentClearingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], - attributes.get("tags"), relationships) - -class PaymentClearingEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'payment.clearing' - self.attributes["previousStatus"] = previous_status - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return PaymentClearingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], - attributes.get("tags"), relationships) - -class PaymentSentEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'payment.sent' - self.attributes["previousStatus"] = previous_status - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return PaymentSentEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], - attributes.get("tags"), relationships) - -class PaymentReturnedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'payment.returned' - self.attributes["previousStatus"] = previous_status - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return PaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousStatus"], - attributes.get("tags"), relationships) - -class PaymentRejectedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, reason: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'payment.rejected' - self.attributes["reason"] = reason - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return PaymentRejectedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["reason"], - attributes.get("tags"), relationships) - -class StatementsCreatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, period: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'statements.created' - self.attributes["period"] = period - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return StatementsCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["period"], - attributes.get("tags"), relationships) - -class TransactionCreatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, summary: str, direction: str, amount: int, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'transaction.created' - self.attributes["summary"] = summary - self.attributes["direction"] = direction - self.attributes["amount"] = amount - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return TransactionCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["summary"], - attributes["direction"], attributes["amount"], attributes.get("tags"), - relationships) - -class AccountReopenedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime,tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseEvent.__init__(self, id, created_at, tags, relationships) - self.type = 'account.reopened' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AccountReopenedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), - relationships) - -EventDTO = Union[AccountClosedEvent, AccountFrozenEvent, ApplicationDeniedEvent, ApplicationAwaitingDocumentsEvent, - ApplicationPendingReviewEvent, CardActivatedEvent, CardStatusChangedEvent, - AuthorizationCreatedEvent, AuthorizationCanceledEvent, AuthorizationDeclinedEvent, - AuthorizationRequestDeclinedEvent, AuthorizationRequestPendingEvent, - AuthorizationRequestApprovedEvent, DocumentApprovedEvent, DocumentRejectedEvent, - CheckDepositCreatedEvent, CheckDepositClearingEvent, CheckDepositSentEvent, - CheckDepositReturnedEvent, CustomerCreatedEvent, PaymentClearingEvent, PaymentSentEvent, - PaymentReturnedEvent, StatementsCreatedEvent, TransactionCreatedEvent, AccountReopenedEvent, RawUnitObject] - - -class ListEventParams(UnitParams): - def __init__(self, limit: int = 100, offset: int = 0, type: Optional[List[str]] = None): - self.limit = limit - self.offset = offset - self.type = type - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.type: - parameters["filter[type][]"] = self.type - return parameters diff --git a/build/lib/unit/models/fee.py b/build/lib/unit/models/fee.py deleted file mode 100644 index 296ce187..00000000 --- a/build/lib/unit/models/fee.py +++ /dev/null @@ -1,50 +0,0 @@ -import json -from typing import Optional -from unit.models import * - - -class FeeDTO(object): - def __init__(self, id: str, amount: int, description: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.type = "fee" - self.attributes = {"amount": amount, "description": description, "tags": tags} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return FeeDTO(_id, attributes["amount"], attributes["description"], attributes.get("tags"), relationships) - - -class CreateFeeRequest(object): - def __init__(self, amount: int, description: str, relationships: Optional[Dict[str, Relationship]], - tags: Optional[Dict[str, str]] = None, idempotency_key: Optional[str] = None): - self.amount = amount - self.description = description - self.tags = tags - self.idempotency_key = idempotency_key - self.relationships = relationships - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "fee", - "attributes": { - "amount": self.amount, - "description": self.description - }, - "relationships": self.relationships - } - } - - if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.tags - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - diff --git a/build/lib/unit/models/institution.py b/build/lib/unit/models/institution.py deleted file mode 100644 index 25599e68..00000000 --- a/build/lib/unit/models/institution.py +++ /dev/null @@ -1,17 +0,0 @@ -import json -from typing import Optional -from unit.models import * - - -class InstitutionDTO(object): - def __init__(self, routing_number: str, name: str, is_ach_supported: bool, is_wire_supported: bool, - address: Optional[Address] = None): - self.type = "institution" - self.attributes = {"routingNumber": routing_number, "name": name, "address": address, - "isACHSupported": is_ach_supported, "isWireSupported": is_wire_supported} - - def from_json_api(_id, _type, attributes, relationships): - return InstitutionDTO( - attributes["routingNumber"], attributes["name"], attributes["isACHSupported"], - attributes["isWireSupported"], attributes.get("address")) - diff --git a/build/lib/unit/models/payment.py b/build/lib/unit/models/payment.py deleted file mode 100644 index 502fb3f0..00000000 --- a/build/lib/unit/models/payment.py +++ /dev/null @@ -1,450 +0,0 @@ -from unit.utils import date_utils -from unit.models import * - -PaymentTypes = Literal["AchPayment", "BookPayment", "WirePayment", "BillPayment"] -PaymentDirections = Literal["Debit", "Credit"] -PaymentStatus = Literal["Pending", "Rejected", "Clearing", "Sent", "Canceled", "Returned"] - -class BasePayment(object): - def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: PaymentDirections, description: str, - amount: int, reason: Optional[str], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.attributes = {"createdAt": created_at, "status": status, "direction": direction, - "description": description, "amount": amount, "reason": reason, "tags": tags} - self.relationships = relationships - -class AchPaymentDTO(BasePayment): - def __init__(self, id: str, created_at: datetime, status: PaymentStatus, counterparty: Counterparty, direction: str, - description: str, amount: int, addenda: Optional[str], reason: Optional[str], - settlement_date: Optional[datetime], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BasePayment.__init__(self, id, created_at, status, direction, description, amount, reason, tags, relationships) - self.type = 'achPayment' - self.attributes["counterparty"] = counterparty - self.attributes["addenda"] = addenda - self.settlement_date = settlement_date - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - settlement_date = date_utils.to_date(attributes.get("settlementDate")) if attributes.get("settlementDate") else None - return AchPaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], - attributes["counterparty"], attributes["direction"], attributes["description"], - attributes["amount"], attributes.get("addenda"), attributes.get("reason"), settlement_date, - attributes.get("tags"), relationships) - -class SimulateIncomingAchPaymentDTO(BasePayment): - def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: str, - description: str, amount: int, reason: Optional[str], - settlement_date: Optional[datetime], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BasePayment.__init__(self, id, created_at, direction, description, amount, reason, tags, relationships) - self.type = 'achPayment' - self.attributes["status"] = status - self.settlement_date = settlement_date - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BasePayment( - _id, - date_utils.to_datetime(attributes["createdAt"]), - attributes.get("direction"), - attributes["description"], - attributes["amount"], - attributes.get("reason"), - attributes.get("tags"), - relationships - ) - -class SimulateAchPaymentDTO(object): - def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: str, - description: str, amount: int, reason: Optional[str], - settlement_date: Optional[datetime], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BasePayment.__init__(self, id, created_at, direction, description, amount, reason, tags, relationships) - self.type = 'achPayment' - self.attributes["status"] = status - self.settlement_date = settlement_date - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BasePayment( - _id, - date_utils.to_datetime(attributes["createdAt"]), - attributes.get("status"), - attributes.get("direction"), - attributes["description"], - attributes["amount"], - attributes.get("reason"), - attributes.get("tags"), - relationships - ) - -class BookPaymentDTO(BasePayment): - def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: Optional[str], description: str, - amount: int, reason: Optional[str], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BasePayment.__init__(self, id, created_at, status, direction, description, amount, reason, tags, relationships) - self.type = 'bookPayment' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BookPaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], - attributes.get("direction"), attributes["description"], attributes["amount"], - attributes.get("reason"), attributes.get("tags"), relationships) - -class WirePaymentDTO(BasePayment): - def __init__(self, id: str, created_at: datetime, status: PaymentStatus, counterparty: WireCounterparty, - direction: str, description: str, amount: int, reason: Optional[str], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BasePayment.__init__(self, id, created_at, direction, description, amount, reason, tags, relationships) - self.type = "wirePayment" - self.attributes["counterparty"] = counterparty - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return WirePaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], - WireCounterparty.from_json_api(attributes["counterparty"]), attributes["direction"], - attributes["description"], attributes["amount"], attributes.get("reason"), - attributes.get("tags"), relationships) - -class BillPaymentDTO(BasePayment): - def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: str, description: str, - amount: int, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BasePayment.__init__(self, id, created_at, status, direction, description, amount, tags, relationships) - self.type = 'billPayment' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BillPaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], - attributes["direction"], attributes["description"], attributes["amount"], - attributes.get("reason"), attributes.get("tags"), relationships) - -PaymentDTO = Union[AchPaymentDTO, BookPaymentDTO, WirePaymentDTO, BillPaymentDTO] - -AchReceivedPaymentStatus = Literal["Pending", "Advanced", "Completed", "Returned"] - -class AchReceivedPaymentDTO(object): - def __init__(self, id: str, created_at: datetime, status: AchReceivedPaymentStatus, was_advanced: bool, - completion_date: datetime, return_reason: Optional[str], amount: int, description: str, - addenda: Optional[str], company_name: str, counterparty_routing_number: str, trace_number: str, - sec_code: Optional[str], tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - self.type = "achReceivedPayment" - self.attributes = {"createdAt": created_at, "status": status, "wasAdvanced": was_advanced, - "completionDate": completion_date, "returnReason": return_reason, "description": description, - "amount": amount, "addenda": addenda, "companyName": company_name, - "counterpartyRoutingNumber": counterparty_routing_number, "traceNumber": trace_number, - "secCode": sec_code, "tags": tags} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AchReceivedPaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], - attributes["wasAdvanced"], attributes["completionDate"], - attributes.get("returnReason"),attributes["amount"], attributes["description"], - attributes.get("addenda"), attributes.get("companyName"), - attributes.get("counterpartyRoutingNumber"), attributes.get("traceNumber"), - attributes.get("secCode"), attributes.get("tags"), relationships) - -class CreatePaymentBaseRequest(UnitRequest): - def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], - idempotency_key: Optional[str], tags: Optional[Dict[str, str]], direction: str = "Credit", - type: str = "achPayment"): - self.type = type - self.amount = amount - self.description = description - self.direction = direction - self.idempotency_key = idempotency_key - self.tags = tags - self.relationships = relationships - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": self.type, - "attributes": { - "amount": self.amount, - "direction": self.direction, - "description": self.description - }, - "relationships": self.relationships - } - } - - if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - -class CreateInlinePaymentRequest(CreatePaymentBaseRequest): - def __init__(self, amount: int, description: str, counterparty: Counterparty, relationships: Dict[str, Relationship], - addenda: Optional[str], idempotency_key: Optional[str], tags: Optional[Dict[str, str]], - direction: str = "Credit"): - CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction) - self.counterparty = counterparty - self.addenda = addenda - - def to_json_api(self) -> Dict: - payload = CreatePaymentBaseRequest.to_json_api(self) - - payload["data"]["attributes"]["counterparty"] = self.counterparty - - if self.addenda: - payload["data"]["attributes"]["addenda"] = self.addenda - - return payload - -class CreateLinkedPaymentRequest(CreatePaymentBaseRequest): - def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], addenda: Optional[str], - verify_counterparty_balance: Optional[bool], idempotency_key: Optional[str], - tags: Optional[Dict[str, str]], direction: str = "Credit"): - CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction) - self.addenda = addenda - self.verify_counterparty_balance = verify_counterparty_balance - - def to_json_api(self) -> Dict: - payload = CreatePaymentBaseRequest.to_json_api(self) - - if self.addenda: - payload["data"]["attributes"]["addenda"] = self.addenda - - if self.verify_counterparty_balance: - payload["data"]["attributes"]["verifyCounterpartyBalance"] = self.verify_counterparty_balance - - return payload - -class SimulateIncomingAchRequest(CreatePaymentBaseRequest): - def __init__( - self, amount: int, description: str, - relationships: Dict[str, Relationship], - direction: str = "Credit" - ): - CreatePaymentBaseRequest.__init__(self, amount, description, relationships, None, None, direction) - self.verify_counterparty_balance = False - - def to_json_api(self) -> Dict: - payload = CreatePaymentBaseRequest.to_json_api(self) - - return payload - -class SimulateTransmitAchRequest(UnitRequest): - def __init__( - self, payment_id: int - ): - self.type = "transmitAchPayment" - self.id = payment_id - self.relationships = { - "payment": { - "data": { - "type": "achPayment", - "id": self.id - } - } - } - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": self.type, - "relationships": self.relationships - } - } - return payload - -class SimulateClearAchRequest(UnitRequest): - def __init__( - self, payment_id: int - ): - self.type = "clearAchPayment" - self.id = payment_id - self.relationships = { - "payment": { - "data": { - "type": "achPayment", - "id": self.id - } - } - } - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": self.type, - "relationships": self.relationships - } - } - return payload - -class CreateVerifiedPaymentRequest(CreatePaymentBaseRequest): - def __init__(self, amount: int, description: str, plaid_processor_token: str, relationships: Dict[str, Relationship], - counterparty_name: Optional[str], verify_counterparty_balance: Optional[bool], - idempotency_key: Optional[str], tags: Optional[Dict[str, str]], direction: str = "Credit"): - CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags) - self.plaid_Processor_token = plaid_Processor_token - self.counterparty_name = counterparty_name - self.verify_counterparty_balance = verify_counterparty_balance - - def to_json_api(self) -> Dict: - payload = CreatePaymentBaseRequest.to_json_api(self) - payload["data"]["attributes"]["counterparty"] = self.counterparty - payload["data"]["attributes"]["plaidProcessorToken"] = self.plaid_processor_token - - if counterparty_name: - payload["data"]["attributes"]["counterpartyName"] = self.counterparty_name - - if verify_counterparty_balance: - payload["data"]["attributes"]["verifyCounterpartyBalance"] = self.verify_counterparty_balance - - return payload - -class CreateBookPaymentRequest(CreatePaymentBaseRequest): - def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], - idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, - direction: str = "Credit"): - super().__init__(amount, description, relationships, idempotency_key, tags, direction, "bookPayment") - -class CreateWirePaymentRequest(CreatePaymentBaseRequest): - def __init__(self, amount: int, description: str, counterparty: WireCounterparty, - relationships: Dict[str, Relationship], idempotency_key: Optional[str], tags: Optional[Dict[str, str]], - direction: str = "Credit"): - CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction, - "wirePayment") - self.counterparty = counterparty - - def to_json_api(self) -> Dict: - payload = CreatePaymentBaseRequest.to_json_api(self) - payload["data"]["attributes"]["counterparty"] = self.counterparty - return payload - -CreatePaymentRequest = Union[CreateInlinePaymentRequest, CreateLinkedPaymentRequest, CreateVerifiedPaymentRequest, - CreateBookPaymentRequest, CreateWirePaymentRequest] - -class PatchAchPaymentRequest(object): - def __init__(self, payment_id: str, tags: Dict[str, str]): - self.payment_id = payment_id - self.tags = tags - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "achPayment", - "attributes": { - "tags": self.tags - } - } - } - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - -class PatchBookPaymentRequest(object): - def __init__(self, payment_id: str, tags: Dict[str, str]): - self.payment_id = payment_id - self.tags = tags - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "bookPayment", - "attributes": { - "tags": self.tags - } - } - } - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - -PatchPaymentRequest = Union[PatchAchPaymentRequest, PatchBookPaymentRequest] - -class ListPaymentParams(UnitParams): - def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, - customer_id: Optional[str] = None, tags: Optional[object] = None, - status: Optional[List[PaymentStatus]] = None, type: Optional[List[PaymentTypes]] = None, - direction: Optional[List[PaymentDirections]] = None, since: Optional[str] = None, - until: Optional[str] = None, sort: Optional[Literal["createdAt", "-createdAt"]] = None, - include: Optional[str] = None): - self.limit = limit - self.offset = offset - self.account_id = account_id - self.customer_id = customer_id - self.tags = tags - self.status = status - self.type = type - self.direction = direction - self.since = since - self.until = until - self.sort = sort - self.include = include - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.customer_id: - parameters["filter[customerId]"] = self.customer_id - if self.account_id: - parameters["filter[accountId]"] = self.account_id - if self.tags: - parameters["filter[tags]"] = self.tags - if self.status: - for idx, status_filter in enumerate(self.status): - parameters[f"filter[status][{idx}]"] = status_filter - if self.type: - for idx, type_filter in enumerate(self.type): - parameters[f"filter[type][{idx}]"] = type_filter - if self.direction: - for idx, direction_filter in enumerate(self.direction): - parameters[f"filter[direction][{idx}]"] = direction_filter - if self.since: - parameters["filter[since]"] = self.since - if self.until: - parameters["filter[until]"] = self.until - if self.sort: - parameters["sort"] = self.sort - if self.include: - parameters["include"] = self.include - return parameters - -class ListReceivedPaymentParams(UnitParams): - def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, - customer_id: Optional[str] = None, tags: Optional[object] = None, - status: Optional[List[AchReceivedPaymentStatus]] = None, - direction: Optional[List[PaymentDirections]] = None, include_completed: Optional[bool] = None, - sort: Optional[Literal["createdAt", "-createdAt"]] = None, include: Optional[str] = None): - self.limit = limit - self.offset = offset - self.account_id = account_id - self.customer_id = customer_id - self.tags = tags - self.status = status - self.include_completed = include_completed - self.sort = sort - self.include = include - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.customer_id: - parameters["filter[customerId]"] = self.customer_id - if self.account_id: - parameters["filter[accountId]"] = self.account_id - if self.tags: - parameters["filter[tags]"] = self.tags - if self.include_completed: - parameters["filter[includeCompleted]"] = self.include_completed - if self.status: - for idx, status_filter in enumerate(self.status): - parameters[f"filter[status][{idx}]"] = status_filter - if self.sort: - parameters["sort"] = self.sort - if self.include: - parameters["include"] = self.include - return parameters diff --git a/build/lib/unit/models/repayment.py b/build/lib/unit/models/repayment.py deleted file mode 100644 index 3554b94b..00000000 --- a/build/lib/unit/models/repayment.py +++ /dev/null @@ -1,114 +0,0 @@ -try: - from typing import Optional, Dict, Union, List, Literal -except ImportError: - from typing import Optional, Dict, Union, List - from typing_extensions import Literal - -from unit.models import UnitDTO, extract_attributes, UnitRequest, Relationship, UnitParams -from unit.utils import date_utils - - -class BaseRepayment(UnitDTO): - def __init__(self, _id, _type, attributes, relationships): - self.id = _id - self.type = _type - self.attributes = extract_attributes(["amount", "status", "tags"], attributes) - attrs = {"createdAt": date_utils.to_datetime(attributes["createdAt"]), - "updatedAt": date_utils.to_datetime(attributes["updatedAt"])} - self.attributes.update(attrs) - self.relationships = relationships - - -class BookRepaymentDTO(BaseRepayment): - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BookRepaymentDTO(_id, _type, attributes, relationships) - - -class AchRepaymentDTO(BaseRepayment): - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AchRepaymentDTO(_id, _type, attributes, relationships) - - -RepaymentDTO = Union[BookRepaymentDTO, AchRepaymentDTO] - - -class CreateBookRepaymentRequest(UnitRequest): - def __init__(self, description: str, amount: int, relationships: Dict[str, Relationship], - transaction_summary_override: Optional[str] = None, tags: Optional[Dict[str, str]] = None, - idempotency_key: Optional[str] = None): - self.description = description - self.amount = amount - self.transaction_summary_override = transaction_summary_override - self.tags = tags - self.idempotency_key = idempotency_key - self.relationships = relationships - - def to_json_api(self) -> Dict: - return super().to_payload("bookRepayment", self.relationships) - - -class CreateAchRepaymentRequest(UnitRequest): - def __init__(self, description: str, amount: int, relationships: Dict[str, Relationship], - addenda: Optional[str] = None, tags: Optional[Dict[str, str]] = None, same_day: Optional[bool] = None, - idempotency_key: Optional[str] = None): - self.description = description - self.amount = amount - self.addenda = addenda - self.tags = tags - self.same_day = same_day - self.idempotency_key = idempotency_key - self.relationships = relationships - - def to_json_api(self) -> Dict: - return super().to_payload("achRepayment", self.relationships) - - -CreateRepaymentRequest = Union[CreateBookRepaymentRequest, CreateAchRepaymentRequest] - -RepaymentStatus = Literal["Pending", "PendingReview", "Returned", "Sent", "Rejected"] -RepaymentType = Literal["bookRepayment", "achRepayment"] - - -class ListRepaymentParams(UnitParams): - def __init__( - self, - limit: int = 100, - offset: int = 0, - account_id: Optional[str] = None, - credit_account_id: Optional[str] = None, - customer_id: Optional[str] = None, - status: Optional[List[RepaymentStatus]] = None, - _type: Optional[List[str]] = None, - ): - self.limit = limit - self.offset = offset - self.account_id = account_id - self.credit_account_id = credit_account_id - self.customer_id = customer_id - self.status = status - self.type = _type - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - - if self.account_id: - parameters["filter[accountId]"] = self.account_id - - if self.credit_account_id: - parameters["filter[creditAccountId]"] = self.credit_account_id - - if self.customer_id: - parameters["filter[customerId]"] = self.customer_id - - if self.status: - for idx, status_filter in enumerate(self.status): - parameters[f"filter[status][{idx}]"] = status_filter - - if self.type: - for idx, type_filter in enumerate(self.type): - parameters[f"filter[type][{idx}]"] = type_filter - - return parameters - diff --git a/build/lib/unit/models/returnAch.py b/build/lib/unit/models/returnAch.py deleted file mode 100644 index 6c972246..00000000 --- a/build/lib/unit/models/returnAch.py +++ /dev/null @@ -1,29 +0,0 @@ -import json -from typing import Literal -from unit.models import * - -AchReturnReason = Literal["InsufficientFunds", "Unauthorized", "UncollectedFunds"] - - -class ReturnReceivedAchTransactionRequest(UnitRequest): - def __init__(self, transaction_id: str, reason: AchReturnReason, relationships: [Dict[str, Relationship]]): - self.transaction_id = transaction_id - self.reason = reason - self.relationships = relationships - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "returnAch", - "attributes": { - "reason": self.reason - }, - "relationships": self.relationships - } - } - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - diff --git a/build/lib/unit/models/reward.py b/build/lib/unit/models/reward.py deleted file mode 100644 index 07f1535b..00000000 --- a/build/lib/unit/models/reward.py +++ /dev/null @@ -1,137 +0,0 @@ -import json -from typing import Optional, Literal, Dict, List -from datetime import datetime - -from unit.models import Relationship, UnitRequest, UnitParams - - -SORT_ORDERS = Literal["created_at", "-created_at"] -RELATED_RESOURCES = Literal["customer", "account", "transaction"] - - -class RewardDTO(object): - def __init__(self, id: str, amount: int, description: str, status: str, tags: Optional[Dict[str, str]] = None, - relationships: Optional[Dict[str, Relationship]] = None): - self.id = id - self.type = "reward" - self.attributes = {"amount": amount, "description": description, "status": status, "tags": tags} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, attributes, relationships): - return RewardDTO(_id, attributes["amount"], attributes["description"], attributes["status"], attributes.get("tags"), relationships) - - -class CreateRewardRequest(UnitRequest): - def __init__( - self, - amount: int, - description: str, - receiving_account_id: str, - rewarded_transaction_id: Optional[str] = None, - funding_account_id: Optional[str] = None, - idempotency_key: Optional[str] = None, - tags: Optional[Dict[str, str]] = None - ): - self.type = "reward" - self.amount = amount - self.description = description - self.rewarded_transaction_id = rewarded_transaction_id - self.receiving_account_id = receiving_account_id - self.funding_account_id = funding_account_id - self.idempotency_key = idempotency_key - self.tags = tags - - self.relationships = { - "receivingAccount": Relationship(_type="depositAccount", _id=self.receiving_account_id) - } - if self.rewarded_transaction_id: - self.relationships["rewardedTransaction"] = Relationship(_type="transaction", _id=self.rewarded_transaction_id) - - if self.funding_account_id: - self.relationships["fundingAccount"] = Relationship(_type="depositAccount", _id=self.funding_account_id) - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": self.type, - "attributes": { - "amount": self.amount, - "description": self.description - }, - "relationships": self.relationships - } - } - - if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class ListRewardsParams(UnitParams): - def __init__( - self, - limit: int = 100, - offset: int = 0, - transaction_id: Optional[str] = None, - rewarded_transaction_id: Optional[str] = None, - receiving_account_id: Optional[str] = None, - customer_id: Optional[str] = None, - card_id: Optional[str] = None, - status: Optional[str] = None, - since: Optional[datetime] = None, - until: Optional[datetime] = None, - sort: Optional[SORT_ORDERS] = None, - include: Optional[List[RELATED_RESOURCES]] = None, - ): - self.limit = limit - self.offset = offset - self.transaction_id = transaction_id - self.rewarded_transaction_id = rewarded_transaction_id - self.receiving_account_id = receiving_account_id - self.customer_id = customer_id - self.card_id = card_id - self.status = status - self.since = since - self.until = until - self.sort = sort - self.include = include - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - - if self.transaction_id: - parameters["filter[transactionId]"] = self.transaction_id - - if self.rewarded_transaction_id: - parameters["filter[rewardedTransactionId]"] = self.rewarded_transaction_id - - if self.receiving_account_id: - parameters["filter[receivingAccountId]"] = self.receiving_account_id - - if self.customer_id: - parameters["filter[customerId]"] = self.customer_id - - if self.card_id: - parameters["filter[cardId]"] = self.card_id - - if self.status: - parameters["filter[status]"] = self.status - - if self.since: - parameters["filter[since]"] = self.since - - if self.unitl: - parameters["filter[until]"] = self.until - - if self.sort: - parameters["sort"] = self.sort - - return parameters diff --git a/build/lib/unit/models/statement.py b/build/lib/unit/models/statement.py deleted file mode 100644 index 590e2604..00000000 --- a/build/lib/unit/models/statement.py +++ /dev/null @@ -1,46 +0,0 @@ -import json -from unit.models import * - - -class StatementDTO(object): - def __init__(self, id: str, _type: str, period: str, relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.type = _type - self.attributes = {"period": period} - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return StatementDTO(_id, _type, attributes["period"], relationships) - - -OutputType = Literal["html", "pdf"] - -class GetStatementParams(UnitRequest): - def __init__(self, statement_id: str, output_type: Optional[OutputType] = "html", language: Optional[str] = "en", - customer_id: Optional[str] = None): - self.statement_id = statement_id - self.output_type = output_type - self.language = language - self.customer_id = customer_id - - -class ListStatementParams(UnitParams): - def __init__(self, limit: int = 100, offset: int = 0, customer_id: Optional[str] = None, - account_id: Optional[str] = None, sort: Optional[Literal["period", "-period"]] = None): - self.limit = limit - self.offset = offset - self.customer_id = customer_id - self.account_id = account_id - self.sort = sort - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.customer_id: - parameters["filter[customerId]"] = self.customer_id - if self.account_id: - parameters["filter[accountId]"] = self.account_id - if self.sort: - parameters["sort"] = self.sort - return parameters - diff --git a/build/lib/unit/models/transaction.py b/build/lib/unit/models/transaction.py deleted file mode 100644 index 58aec0fa..00000000 --- a/build/lib/unit/models/transaction.py +++ /dev/null @@ -1,466 +0,0 @@ -from unit.utils import date_utils -from unit.models import * - - -class BaseTransactionDTO(object): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - self.id = id - self.attributes = {"createdAt": created_at, "direction": direction, "amount": amount, "balance": balance, - "summary": summary, "tags": tags} - self.relationships = relationships - - -class OriginatedAchTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, description: str, addenda: Optional[str], counterparty: Counterparty, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'originatedAchTransaction' - self.attributes["description"] = description - self.attributes["addenda"] = addenda - self.attributes["counterparty"] = counterparty - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return OriginatedAchTransactionDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], attributes["description"], - attributes.get("addenda"), Counterparty.from_json_api(attributes["counterparty"]), - attributes.get("tags"), relationships) - - -class ReceivedAchTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, description: str, addenda: Optional[str], company_name: str, - counterparty_routing_number: str, trace_number: Optional[str], sec_code: Optional[str], - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'receivedAchTransaction' - self.attributes["description"] = description - self.attributes["addenda"] = addenda - self.attributes["companyName"] = company_name - self.attributes["counterpartyRoutingNumber"] = counterparty_routing_number - self.attributes["traceNumber"] = trace_number - self.attributes["secCode"] = sec_code - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return ReceivedAchTransactionDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], attributes["description"], - attributes.get("addenda"), attributes["companyName"], attributes["counterpartyRoutingNumber"], - attributes.get("traceNumber"), attributes.get("secCode"), attributes.get("tags"), relationships) - - -class ReturnedAchTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, company_name: str, counterparty_name: str, counterparty_routing_number: str, reason: str, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'returnedAchTransaction' - self.attributes["companyName"] = company_name - self.attributes["counterpartyName"] = counterparty_name - self.attributes["counterpartyRoutingNumber"] = counterparty_routing_number - self.attributes["reason"] = reason - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return ReturnedAchTransactionDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], attributes["amount"], - attributes["balance"], attributes["summary"], attributes["companyName"], attributes["counterpartyName"], - attributes["counterpartyRoutingNumber"], attributes["reason"], attributes.get("tags"), relationships) - - -class ReturnedReceivedAchTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, - company_name: str, reason: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'returnedReceivedAchTransaction' - self.attributes["companyName"] = company_name - self.attributes["reason"] = reason - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return ReturnedReceivedAchTransactionDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], attributes["companyName"], - attributes["reason"], attributes.get("tags"), relationships) - - -class DishonoredAchTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, - company_name: str, counterparty_routing_number: str, reason: str, trace_number: Optional[str], - sec_code: Optional[str], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'dishonoredAchTransaction' - self.attributes["companyName"] = company_name - self.attributes["counterpartyRoutingNumber"] = counterparty_routing_number - self.attributes["traceNumber"] = trace_number - self.attributes["reason"] = reason - self.attributes["secCode"] = sec_code - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return DishonoredAchTransactionDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], attributes["companyName"], - attributes["counterpartyRoutingNumber"], attributes["reason"], attributes.get("traceNumber"), - attributes.get("secCode"), attributes.get("tags"), relationships) - - -class BookTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, counterparty: Counterparty, tags: Optional[Dict[str, str]] = None, - relationships: Optional[Dict[str, Relationship]] = None): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'bookTransaction' - self.attributes["counterparty"] = counterparty - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return BookTransactionDTO( - id=_id, created_at=date_utils.to_datetime(attributes["createdAt"]), direction=attributes["direction"], - amount=attributes["amount"], balance=attributes["balance"], summary=attributes["summary"], - counterparty=Counterparty.from_json_api(attributes["counterparty"]), - tags=attributes.get("tags"), relationships=relationships - ) - - -class PurchaseTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, card_last_4_digits: str, merchant: Merchant, coordinates: Coordinates, recurring: bool, - interchange: Optional[int], ecommerce: bool, card_present: bool, payment_method: Optional[str], - digital_wallet: Optional[str], card_verification_data, card_network: Optional[str], - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'purchaseTransaction' - self.attributes["cardLast4Digits"] = card_last_4_digits - self.attributes["merchant"] = merchant - self.attributes["coordinates"] = coordinates - self.attributes["recurring"] = recurring - self.attributes["interchange"] = interchange - self.attributes["ecommerce"] = ecommerce - self.attributes["cardPresent"] = card_present - self.attributes["paymentMethod"] = payment_method - self.attributes["digitalWallet"] = digital_wallet - self.attributes["cardVerificationData"] = card_verification_data - self.attributes["cardNetwork"] = card_network - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return PurchaseTransactionDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], attributes["cardLast4Digits"], - Merchant.from_json_api(attributes["merchant"]), Coordinates.from_json_api(attributes.get("coordinates")), - attributes["recurring"], attributes.get("interchange"), attributes.get("ecommerce"), - attributes.get("cardPresent"), attributes.get("paymentMethod"), attributes.get("digitalWallet"), - attributes.get("cardVerificationData"), attributes.get("cardNetwork"), attributes.get("tags"), - relationships) - - -class AtmTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, card_last_4_digits: str, atm_name: str, atm_location: Optional[str], surcharge: int, - interchange: Optional[int], card_network: Optional[str], - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'atmTransaction' - self.attributes["cardLast4Digits"] = card_last_4_digits - self.attributes["atmName"] = atm_name - self.attributes["atmLocation"] = atm_location - self.attributes["surcharge"] = surcharge - self.attributes["interchange"] = interchange - self.attributes["cardNetwork"] = card_network - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AtmTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], - attributes["cardLast4Digits"], attributes["atmName"], attributes.get("atmLocation"), - attributes["surcharge"], attributes.get("interchange"), attributes.get("cardNetwork"), - attributes.get("tags"), relationships) - - -class FeeTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'feeTransaction' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return FeeTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], - attributes.get("tags"), relationships) - - -class CardTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, card_last_4_digits: str, merchant: Merchant, recurring: Optional[bool], - interchange: Optional[int], payment_method: Optional[str], digital_wallet: Optional[str], - card_verification_data: Optional[Dict], card_network: Optional[str], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'cardTransaction' - self.attributes["cardLast4Digits"] = card_last_4_digits - self.attributes["merchant"] = merchant - self.attributes["recurring"] = recurring - self.attributes["interchange"] = interchange - self.attributes["paymentMethod"] = payment_method - self.attributes["digitalWallet"] = digital_wallet - self.attributes["cardVerificationData"] = card_verification_data - self.attributes["cardNetwork"] = card_network - - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CardTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], - attributes["cardLast4Digits"], Merchant.from_json_api(attributes["merchant"]), - attributes.get("recurring"), attributes.get("interchange"), - attributes.get("paymentMethod"), attributes.get("digitalWallet"), - attributes.get("cardVerificationData"), attributes.get("cardNetwork"), - attributes.get("tags"), relationships) - - -class CardReversalTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, card_last_4_digits: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'cardReversalTransaction' - self.attributes["cardLast4Digits"] = card_last_4_digits - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CardReversalTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], - attributes["cardLast4Digits"], attributes.get("tags"), relationships) - - -class WireTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, counterparty: Counterparty, description: str, - originator_to_beneficiary_information: str, sender_reference: str, - reference_for_beneficiary: str, beneficiary_information: str, - beneficiary_advice_information: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'wireTransaction' - self.attributes["description"] = description - self.attributes["counterparty"] = counterparty - self.attributes["originatorToBeneficiaryInformation"] = originator_to_beneficiary_information - self.attributes["senderReference"] = sender_reference - self.attributes["referenceForBeneficiary"] = reference_for_beneficiary - self.attributes["beneficiaryInformation"] = beneficiary_information - self.attributes["beneficiaryAdviceInformation"] = beneficiary_advice_information - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return WireTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], - Counterparty.from_json_api(attributes["counterparty"]), attributes["description"], - attributes.get("originatorToBeneficiaryInformation"), attributes.get("senderReference"), - attributes.get("referenceForBeneficiary"), attributes.get("beneficiaryInformation"), - attributes.get("beneficiaryAdviceInformation"), attributes.get("tags"), relationships) - - -class ReleaseTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, sender_name: str, sender_address: Address, - sender_account_number: str, counterparty: Counterparty, amount: int, direction: str, - description: str, balance: int, summary: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'releaseTransaction' - self.attributes["description"] = description - self.attributes["senderName"] = sender_name - self.attributes["senderAddress"] = sender_address - self.attributes["senderAccountNumber"] = sender_account_number - self.attributes["counterparty"] = counterparty - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return ReleaseTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["senderName"], - Address.from_json_api(attributes["senderAddress"]), - attributes["senderAccountNumber"], - Counterparty.from_json_api(attributes["counterparty"]), attributes["amount"], - attributes["direction"], attributes["description"], attributes["balance"], - attributes["summary"], attributes.get("tags"), relationships) - - -class AdjustmentTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, - description: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'adjustmentTransaction' - self.attributes["description"] = description - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return AdjustmentTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], - attributes["summary"], attributes["description"], attributes.get("tags"), - relationships) - - -class InterestTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'interestTransaction' - self.relationships = relationships - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return InterestTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], - attributes.get("tags"), relationships) - - -class DisputeTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, dispute_id: str, - summary: str, reason: str, tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'disputeTransaction' - self.attributes["disputeId"] = dispute_id - self.attributes["reason"] = reason - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return DisputeTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["disputeId"], - attributes["summary"], attributes["reason"], attributes.get("tags"), relationships) - - -class CheckDepositTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'checkDepositTransaction' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return CheckDepositTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], - attributes.get("tags"), relationships) - - -class ReturnedCheckDepositTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, - reason: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'returnedCheckDepositTransaction' - self.attributes["reason"] = reason - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return ReturnedCheckDepositTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], - attributes["reason"], attributes.get("tags"), relationships) - -class PaymentAdvanceTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, - reason: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'paymentAdvanceTransaction' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return PaymentAdvanceTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], - attributes.get("tags"), relationships) - -class RepaidPaymentAdvanceTransactionDTO(BaseTransactionDTO): - def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, - reason: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): - BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) - self.type = 'repaidPaymentAdvanceTransaction' - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return RepaidPaymentAdvanceTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], - attributes.get("tags"), relationships) - -TransactionDTO = Union[OriginatedAchTransactionDTO, ReceivedAchTransactionDTO, ReturnedAchTransactionDTO, - ReturnedReceivedAchTransactionDTO, DishonoredAchTransactionDTO, BookTransactionDTO, - PurchaseTransactionDTO, AtmTransactionDTO, FeeTransactionDTO, CardTransactionDTO, - CardReversalTransactionDTO, WireTransactionDTO, ReleaseTransactionDTO, AdjustmentTransactionDTO, - InterestTransactionDTO, DisputeTransactionDTO, CheckDepositTransactionDTO, - ReturnedCheckDepositTransactionDTO, PaymentAdvanceTransactionDTO, - RepaidPaymentAdvanceTransactionDTO] - - -class PatchTransactionRequest(BaseTransactionDTO, UnitRequest): - def __init__(self, account_id: str, transaction_id: str, tags: Optional[Dict[str, str]] = None): - self.account_id = account_id - self.transaction_id = transaction_id - self.tags = tags - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "transaction", - "attributes": {} - } - } - - if self.tags: - payload["data"]["attributes"]["tags"] = self.tags - - return payload - - -class ListTransactionParams(UnitParams): - def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, - customer_id: Optional[str] = None, query: Optional[str] = None, tags: Optional[object] = None, - since: Optional[str] = None, until: Optional[str] = None, card_id: Optional[str] = None, - type: Optional[List[str]] = None, exclude_fees: Optional[bool] = None, - sort: Optional[Literal["createdAt", "-createdAt"]] = None, include: Optional[str] = None): - self.limit = limit - self.offset = offset - self.account_id = account_id - self.customer_id = customer_id - self.query = query - self.tags = tags - self.since = since - self.until = until - self.card_id = card_id - self.type = type - self.exclude_fees = exclude_fees - self.sort = sort - self.include = include - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - if self.customer_id: - parameters["filter[customerId]"] = self.customer_id - if self.account_id: - parameters["filter[accountId]"] = self.account_id - if self.query: - parameters["filter[query]"] = self.query - if self.tags: - parameters["filter[tags]"] = self.tags - if self.since: - parameters["filter[since]"] = self.since - if self.until: - parameters["filter[until]"] = self.until - if self.card_id: - parameters["filter[cardId]"] = self.card_id - if self.type: - for idx, type_filter in enumerate(self.type): - parameters[f"filter[type][{idx}]"] = type_filter - if self.exclude_fees: - parameters["filter[excludeFees]"] = self.exclude_fees - if self.sort: - parameters["sort"] = self.sort - if self.include: - parameters["include"] = self.include - return parameters \ No newline at end of file diff --git a/build/lib/unit/models/webhook.py b/build/lib/unit/models/webhook.py deleted file mode 100644 index c19d82c1..00000000 --- a/build/lib/unit/models/webhook.py +++ /dev/null @@ -1,92 +0,0 @@ -import json -from datetime import datetime, date -from unit.utils import date_utils -from unit.models import * -from typing import Literal - -ContentType = Literal["Json", "JsonAPI"] -WebhookStatus = Literal["Enabled", "Disabled"] - - -class WebhookDTO(object): - def __init__(self, id: str, created_at: datetime, label: str, url: str, status: WebhookStatus, - content_type: ContentType, token: str): - self.id = id - self.type = 'webhook' - self.attributes = {"createdAt": created_at, "label": label, "url": url, "status": status, - "contentType": content_type, "token": token} - - @staticmethod - def from_json_api(_id, _type, attributes, relationships): - return WebhookDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["label"], attributes["url"], - attributes["status"], attributes["contentType"], attributes["token"]) - - -class CreateWebhookRequest(object): - def __init__(self, label: str, url: str, token: str, content_type: ContentType): - self.label = label - self.url = url - self.token = token - self.content_type = content_type - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "webhook", - "attributes": { - "label": self.label, - "url": self.url, - "token": self.token, - "contentType": self.content_type - } - } - } - - return payload - - def __repr__(self): - json.dumps(self.to_json_api()) - - -class PatchWebhookRequest(object): - def __init__(self, webhook_id: str, label: Optional[str] = None, url: Optional[str] = None, - content_type: Optional[ContentType] = None, token: Optional[str] = None): - self.webhook_id = webhook_id - self.label = label - self.url = url - self.content_type = content_type - self.token = token - - def to_json_api(self) -> Dict: - payload = { - "data": { - "type": "webhook", - "attributes": {} - } - } - - if self.label: - payload["data"]["attributes"]["label"] = self.label - - if self.url: - payload["data"]["attributes"]["url"] = self.url - - if self.content_type: - payload["data"]["attributes"]["contentType"] = self.content_type - - if self.token: - payload["data"]["attributes"]["token"] = self.token - - return payload - - -class ListWebhookParams(UnitParams): - def __init__(self, limit: int = 100, offset: int = 0): - self.limit = limit - self.offset = offset - - def to_dict(self) -> Dict: - parameters = {"page[limit]": self.limit, "page[offset]": self.offset} - return parameters - diff --git a/build/lib/unit/utils/__init__.py b/build/lib/unit/utils/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/build/lib/unit/utils/date_utils.py b/build/lib/unit/utils/date_utils.py deleted file mode 100644 index 6dbdbec6..00000000 --- a/build/lib/unit/utils/date_utils.py +++ /dev/null @@ -1,13 +0,0 @@ -from datetime import date, datetime - - -def to_datetime(dt: str): - return datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S.%f%z") - - -def to_date(d: str): - return date.fromisoformat(d) - - -def to_date_str(d: date): - return d.strftime("%Y-%m-%d") From a27740c5328fddab92e126161d437204ae6b2a06 Mon Sep 17 00:00:00 2001 From: Eric Ghildyal Date: Thu, 20 Jul 2023 12:33:59 -0400 Subject: [PATCH 077/137] Minor fixes to charge cards --- unit/__init__.py | 2 + unit/api/repayment_resource.py | 16 ++- unit/models/__init__.py | 230 +++++++++++++++++++++++++++------ unit/models/card.py | 8 +- 4 files changed, 210 insertions(+), 46 deletions(-) diff --git a/unit/__init__.py b/unit/__init__.py index 99bda338..bc74f256 100644 --- a/unit/__init__.py +++ b/unit/__init__.py @@ -4,6 +4,7 @@ from unit.api.card_resource import CardResource from unit.api.transaction_resource import TransactionResource from unit.api.payment_resource import PaymentResource +from unit.api.repayment_resource import RepaymentResource from unit.api.ach_resource import AchResource from unit.api.statement_resource import StatementResource from unit.api.customerToken_resource import CustomerTokenResource @@ -33,6 +34,7 @@ def __init__(self, api_url, token): self.cards = CardResource(api_url, token) self.transactions = TransactionResource(api_url, token) self.payments = PaymentResource(api_url, token) + self.repayments = RepaymentResource(api_url, token) self.ach = AchResource(api_url, token) self.statements = StatementResource(api_url, token) self.customerTokens = CustomerTokenResource(api_url, token) diff --git a/unit/api/repayment_resource.py b/unit/api/repayment_resource.py index d55a69d0..74276816 100644 --- a/unit/api/repayment_resource.py +++ b/unit/api/repayment_resource.py @@ -3,7 +3,11 @@ from unit.api.base_resource import BaseResource from unit.models import UnitResponse, UnitError from unit.models.codecs import DtoDecoder -from unit.models.repayment import RepaymentDTO, CreateRepaymentRequest, ListRepaymentParams +from unit.models.repayment import ( + RepaymentDTO, + CreateRepaymentRequest, + ListRepaymentParams, +) class RepaymentResource(BaseResource): @@ -11,9 +15,11 @@ def __init__(self, api_url, token): super().__init__(api_url, token) self.resource = "repayments" - def create(self, request: CreateRepaymentRequest) -> Union[UnitResponse[RepaymentDTO], UnitError]: + def create( + self, request: CreateRepaymentRequest + ) -> Union[UnitResponse[RepaymentDTO], UnitError]: payload = request.to_json_api() - response = super().post_create(self.resource, payload) + response = super().post(self.resource, payload) if super().is_20x(response.status_code): data = response.json().get("data") return UnitResponse[RepaymentDTO](DtoDecoder.decode(data), None) @@ -28,7 +34,9 @@ def get(self, repayment_id: str) -> Union[UnitResponse[RepaymentDTO], UnitError] else: return UnitError.from_json_api(response.json()) - def list(self, params: Optional[ListRepaymentParams] = None) -> Union[UnitResponse[List[RepaymentDTO]], UnitError]: + def list( + self, params: Optional[ListRepaymentParams] = None + ) -> Union[UnitResponse[List[RepaymentDTO]], UnitError]: params = params or ListRepaymentParams() response = super().get(self.resource, params.to_dict()) if super().is_20x(response.status_code): diff --git a/unit/models/__init__.py b/unit/models/__init__.py index a25d06dc..dfcf1f47 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -2,11 +2,12 @@ from typing import TypeVar, Generic, Union, Optional, Literal, List, Dict from datetime import datetime, date + def to_camel_case(snake_str): - components = snake_str.lstrip('_').split('_') + components = snake_str.lstrip("_").split("_") # We capitalize the first letter of each component except the first one # with the 'title' method and join them together. - return components[0] + ''.join(x.title() for x in components[1:]) + return components[0] + "".join(x.title() for x in components[1:]) def extract_attributes(list_of_attributes, attributes): @@ -24,7 +25,10 @@ def to_dict(self): return self else: v = vars(self) - return dict((to_camel_case(k), val) for k, val in v.items() if val is not None) + return dict( + (to_camel_case(k), val) for k, val in v.items() if val is not None + ) + class Relationship(object): def __init__(self, _type: str, _id: str): @@ -35,7 +39,8 @@ def to_dict(self): return {"type": self.type, "id": self.id} -T = TypeVar('T') +T = TypeVar("T") + class RelationshipArray(Generic[T]): def __init__(self, l: List[T]): @@ -56,10 +61,41 @@ class UnitRequest(object): def to_json_api(self) -> Dict: pass + def vars_to_attributes_dict(self, ignore: List[str] = []) -> Dict: + attributes = {} + + for k in self.__dict__: + if k != "relationships" and k not in ignore: + v = getattr(self, k) + if v: + attributes[to_camel_case(k)] = v + + return attributes + + def to_payload( + self, + _type: str, + relationships: Dict[str, Relationship] = None, + ignore: List[str] = [], + ) -> Dict: + payload = { + "data": { + "type": _type, + "attributes": self.vars_to_attributes_dict(ignore), + } + } + + if relationships: + payload["data"]["relationships"] = relationships + + return payload + + class UnitParams(object): def to_dict(self) -> Dict: pass + class RawUnitObject(object): def __init__(self, _id, _type, attributes, relationships): self.id = _id @@ -67,9 +103,16 @@ def __init__(self, _id, _type, attributes, relationships): self.attributes = attributes self.relationships = relationships + class UnitErrorPayload(object): - def __init__(self, title: str, status: str, detail: Optional[str] = None, details: Optional[str] = None, - source: Optional[Dict] = None): + def __init__( + self, + title: str, + status: str, + detail: Optional[str] = None, + details: Optional[str] = None, + source: Optional[Dict] = None, + ): self.title = title self.status = status self.detail = detail @@ -89,21 +132,39 @@ def from_json_api(data: Dict): errors = [] for err in data["errors"]: errors.append( - UnitErrorPayload(err.get("title"), err.get("status"), err.get("detail", None), - err.get("details", None), err.get("source", None)) + UnitErrorPayload( + err.get("title"), + err.get("status"), + err.get("detail", None), + err.get("details", None), + err.get("source", None), + ) ) return UnitError(errors) def __str__(self): - return json.dumps({"errors": [{"title": err.title, "status": err.status, "detail": err.detail, - "details": err.details, "source": err.source} for err in self.errors]}) + return json.dumps( + { + "errors": [ + { + "title": err.title, + "status": err.status, + "detail": err.detail, + "details": err.details, + "source": err.source, + } + for err in self.errors + ] + } + ) Status = Literal["Approved", "Denied", "PendingReview"] Title = Literal["CEO", "COO", "CFO", "President"] EntityType = Literal["Corporation", "LLC", "Partnership"] + class FullName(object): def __init__(self, first: str, last: str): self.first = first @@ -119,8 +180,15 @@ def from_json_api(data: Dict): # todo: Alex - use typing.Literal for multi accepted values (e.g country) class Address(object): - def __init__(self, street: str, city: str, state: str, postal_code: str, country: str, - street2: Optional[str] = None): + def __init__( + self, + street: str, + city: str, + state: str, + postal_code: str, + country: str, + street2: Optional[str] = None, + ): self.street = street self.street2 = street2 self.city = city @@ -130,8 +198,14 @@ def __init__(self, street: str, city: str, state: str, postal_code: str, country @staticmethod def from_json_api(data: Dict): - return Address(data.get("street"), data.get("city"), data.get("state"), - data.get("postalCode"), data.get("country"), data.get("street2", None)) + return Address( + data.get("street"), + data.get("city"), + data.get("state"), + data.get("postalCode"), + data.get("country"), + data.get("street2", None), + ) class Phone(object): @@ -152,13 +226,27 @@ def __init__(self, full_name: FullName, email: str, phone: Phone): @staticmethod def from_json_api(data: Dict): - return BusinessContact(FullName.from_json_api(data.get("fullName")), data.get("email"), Phone.from_json_api(data.get("phone"))) + return BusinessContact( + FullName.from_json_api(data.get("fullName")), + data.get("email"), + Phone.from_json_api(data.get("phone")), + ) class Officer(object): - def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: Optional[Status] = None, title: Optional[Title] = None, ssn: Optional[str] = None, - passport: Optional[str] = None, nationality: Optional[str] = None): + def __init__( + self, + full_name: FullName, + date_of_birth: date, + address: Address, + phone: Phone, + email: str, + status: Optional[Status] = None, + title: Optional[Title] = None, + ssn: Optional[str] = None, + passport: Optional[str] = None, + nationality: Optional[str] = None, + ): self.full_name = full_name self.date_of_birth = date_of_birth self.address = address @@ -172,15 +260,34 @@ def __init__(self, full_name: FullName, date_of_birth: date, address: Address, p @staticmethod def from_json_api(data: Dict): - return Officer(data.get("fullName"), data.get("dateOfBirth"), data.get("address"), data.get("phone"), - data.get("email"), data.get("status"), data.get("title"), data.get("ssn"), data.get("passport"), - data.get("nationality")) + return Officer( + data.get("fullName"), + data.get("dateOfBirth"), + data.get("address"), + data.get("phone"), + data.get("email"), + data.get("status"), + data.get("title"), + data.get("ssn"), + data.get("passport"), + data.get("nationality"), + ) class BeneficialOwner(object): - def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, - status: Optional[Status] = None, ssn: Optional[str] = None, passport: Optional[str] = None, - nationality: Optional[str] = None, percentage: Optional[int] = None): + def __init__( + self, + full_name: FullName, + date_of_birth: date, + address: Address, + phone: Phone, + email: str, + status: Optional[Status] = None, + ssn: Optional[str] = None, + passport: Optional[str] = None, + nationality: Optional[str] = None, + percentage: Optional[int] = None, + ): self.full_name = full_name self.date_of_birth = date_of_birth self.address = address @@ -196,9 +303,20 @@ def __init__(self, full_name: FullName, date_of_birth: date, address: Address, p def from_json_api(l: List): beneficial_owners = [] for data in l: - beneficial_owners.append(BeneficialOwner(data.get("fullName"), data.get("dateOfBirth"), data.get("address"), - data.get("phone"), data.get("email"), data.get("status"), data.get("ssn"), - data.get("passport"), data.get("nationality"), data.get("percentage"))) + beneficial_owners.append( + BeneficialOwner( + data.get("fullName"), + data.get("dateOfBirth"), + data.get("address"), + data.get("phone"), + data.get("email"), + data.get("status"), + data.get("ssn"), + data.get("passport"), + data.get("nationality"), + data.get("percentage"), + ) + ) return beneficial_owners @@ -212,11 +330,18 @@ def __init__(self, full_name: FullName, email: str, phone: Phone): def from_json_api(l: List) -> List: authorized_users = [] for data in l: - authorized_users.append(AuthorizedUser(data.get("fullName"), data.get("email"), data.get("phone"))) + authorized_users.append( + AuthorizedUser( + data.get("fullName"), data.get("email"), data.get("phone") + ) + ) return authorized_users + class WireCounterparty(object): - def __init__(self, routing_number: str, account_number: str, name: str, address: Address): + def __init__( + self, routing_number: str, account_number: str, name: str, address: Address + ): self.routing_number = routing_number self.account_number = account_number self.name = name @@ -224,11 +349,18 @@ def __init__(self, routing_number: str, account_number: str, name: str, address: @staticmethod def from_json_api(data: Dict): - return WireCounterparty(data["routingNumber"], data["accountNumber"], data["name"], - Address.from_json_api(data["address"])) + return WireCounterparty( + data["routingNumber"], + data["accountNumber"], + data["name"], + Address.from_json_api(data["address"]), + ) + class Counterparty(object): - def __init__(self, routing_number: str, account_number: str, account_type: str, name: str): + def __init__( + self, routing_number: str, account_number: str, account_type: str, name: str + ): self.routing_number = routing_number self.account_number = account_number self.account_type = account_type @@ -236,7 +368,13 @@ def __init__(self, routing_number: str, account_number: str, account_type: str, @staticmethod def from_json_api(data: Dict): - return Counterparty(data["routingNumber"], data["accountNumber"], data["accountType"], data["name"]) + return Counterparty( + data["routingNumber"], + data["accountNumber"], + data["accountType"], + data["name"], + ) + class Coordinates(object): def __init__(self, longitude: int, latitude: int): @@ -252,7 +390,9 @@ def from_json_api(data: Dict): class Merchant(object): - def __init__(self, name: str, type: int, category: Optional[str], location: Optional[str]): + def __init__( + self, name: str, type: int, category: Optional[str], location: Optional[str] + ): self.name = name self.type = type self.category = category @@ -260,10 +400,19 @@ def __init__(self, name: str, type: int, category: Optional[str], location: Opti @staticmethod def from_json_api(data: Dict): - return Merchant(data["name"], data["type"], data.get("category"), data.get("location")) + return Merchant( + data["name"], data["type"], data.get("category"), data.get("location") + ) + class CardLevelLimits(object): - def __init__(self, daily_withdrawal: int, daily_purchase: int, monthly_withdrawal: int, monthly_purchase: int): + def __init__( + self, + daily_withdrawal: int, + daily_purchase: int, + monthly_withdrawal: int, + monthly_purchase: int, + ): self.daily_withdrawal = daily_withdrawal self.daily_purchase = daily_purchase self.monthly_withdrawal = monthly_withdrawal @@ -271,8 +420,13 @@ def __init__(self, daily_withdrawal: int, daily_purchase: int, monthly_withdrawa @staticmethod def from_json_api(data: Dict): - return CardLevelLimits(data["dailyWithdrawal"], data["dailyPurchase"], data["monthlyWithdrawal"], - data["monthlyPurchase"]) + return CardLevelLimits( + data["dailyWithdrawal"], + data["dailyPurchase"], + data["monthlyWithdrawal"], + data["monthlyPurchase"], + ) + class CardTotals(object): def __init__(self, withdrawals: int, deposits: int, purchases: int): diff --git a/unit/models/card.py b/unit/models/card.py index bf0c4ab0..c8ed53f8 100644 --- a/unit/models/card.py +++ b/unit/models/card.py @@ -190,7 +190,7 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) -class CreateBusinessCard(object): +class CreateBusinessCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, relationships: Dict[str, Relationship], shipping_address: Optional[Address] = None, ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, @@ -306,7 +306,7 @@ def __repr__(self): json.dumps(self.to_json_api()) -class CreateBusinessVirtualCard(object): +class CreateBusinessVirtualCard(UnitRequest): def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, relationships: Dict[str, Relationship], ssn: Optional[str] = None, passport: Optional[str] = None, nationality: Optional[str] = None, idempotency_key: Optional[str] = None, @@ -411,7 +411,7 @@ def __repr__(self): json.dumps(self.to_json_api()) -class PatchBusinessCard(object): +class PatchBusinessCard(UnitRequest): def __init__(self, card_id: str, shipping_address: Optional[Address] = None, address: Optional[Address] = None, phone: Optional[Phone] = None, email: Optional[str] = None, design: Optional[str] = None, tags: Optional[Dict[str, str]] = None, limits: Optional[CardLevelLimits] = None): @@ -491,7 +491,7 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) -class PatchBusinessVirtualCard(object): +class PatchBusinessVirtualCard(UnitRequest): def __init__(self, card_id: str, address: Optional[Address] = None, phone: Optional[Phone] = None, email: Optional[str] = None, tags: Optional[Dict[str, str]] = None, _type: str = "businessVirtualDebitCard", limits: Optional[CardLevelLimits] = None): From c997b22384fd2c8622b9048196ff0a27e1a6a369 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Thu, 20 Jul 2023 11:11:04 -0700 Subject: [PATCH 078/137] simulate purchases --- unit/api/authorization_request_resource.py | 8 ++ unit/api/authorization_resource.py | 2 +- unit/api/transaction_resource.py | 17 +++ unit/models/authorization_request.py | 37 ++++++ unit/models/transaction.py | 131 +++++++++++++++++++-- 5 files changed, 187 insertions(+), 8 deletions(-) diff --git a/unit/api/authorization_request_resource.py b/unit/api/authorization_request_resource.py index 56815ee8..e61f538e 100644 --- a/unit/api/authorization_request_resource.py +++ b/unit/api/authorization_request_resource.py @@ -44,3 +44,11 @@ def decline(self, request: DeclineAuthorizationRequest) -> Union[UnitResponse[Pu else: return UnitError.from_json_api(response.json()) + def sb_simulate(self, request: SimulateAuthorizationRequest) -> Union[UnitResponse[PurchaseAuthorizationRequestDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"sandbox/{self.resource}/purchase", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[PurchaseAuthorizationRequestDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/unit/api/authorization_resource.py b/unit/api/authorization_resource.py index 28a3fec7..3991bce1 100644 --- a/unit/api/authorization_resource.py +++ b/unit/api/authorization_resource.py @@ -25,4 +25,4 @@ def list(self, params: ListAuthorizationParams = None) -> Union[UnitResponse[Lis data = response.json().get("data") return UnitResponse[AuthorizationDTO](DtoDecoder.decode(data), None) else: - return UnitError.from_json_api(response.json()) + return UnitError.from_json_api(response.json()) \ No newline at end of file diff --git a/unit/api/transaction_resource.py b/unit/api/transaction_resource.py index abcb8e38..126e304f 100644 --- a/unit/api/transaction_resource.py +++ b/unit/api/transaction_resource.py @@ -39,3 +39,20 @@ def update(self, request: PatchTransactionRequest) -> Union[UnitResponse[Transac else: return UnitError.from_json_api(response.json()) + def sb_simulate_purchase_transaction(self, request: SimulatePurchaseTransaction) -> Union[UnitResponse[PurchaseTransactionDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"sandbox/purchases", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[PurchaseTransactionDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + + def sb_simulate_card_transaction(self, request: SimulateCardTransaction) -> Union[UnitResponse[CardTransactionDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"sandbox/card-transactions", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[CardTransactionDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) \ No newline at end of file diff --git a/unit/models/authorization_request.py b/unit/models/authorization_request.py index f654701d..80a521cd 100644 --- a/unit/models/authorization_request.py +++ b/unit/models/authorization_request.py @@ -96,3 +96,40 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) + + +class SimulateAuthorizationRequest(UnitRequest): + def __init__(self, amount: int, card_id: str, merchant_name: str, merchant_type: int, merchant_location: str): + self.amount = amount + self.card_id = card_id + self.merchant_name = merchant_name + self.merchant_type = merchant_type + self.merchant_location = merchant_location + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "purchaseAuthorizationRequest", + "attributes": { + "amount": self.amount, + "merchantName": self.merchant_name, + "merchantType": self.merchant_type, + "merchantLocation": self.merchant_location, + "recurring": False + }, + "relationships": { + "card": { + "data": { + "type": "card", + "id": self.card_id + } + } + } + + } + } + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) diff --git a/unit/models/transaction.py b/unit/models/transaction.py index 58aec0fa..223525d1 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -131,14 +131,16 @@ def from_json_api(_id, _type, attributes, relationships): class PurchaseTransactionDTO(BaseTransactionDTO): def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, card_last_4_digits: str, merchant: Merchant, coordinates: Coordinates, recurring: bool, - interchange: Optional[int], ecommerce: bool, card_present: bool, payment_method: Optional[str], + summary: str, last_4_digits: str, merchantName: str, merchantType:str, merchantLocation: str, + coordinates: Coordinates, recurring: bool,interchange: Optional[int], ecommerce: bool, card_present: bool, payment_method: Optional[str], digital_wallet: Optional[str], card_verification_data, card_network: Optional[str], tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) self.type = 'purchaseTransaction' - self.attributes["cardLast4Digits"] = card_last_4_digits - self.attributes["merchant"] = merchant + self.attributes["last4Digits"] = last_4_digits + self.attributes["merchantName"] = merchantName + self.attributes["merchantType"] = merchantType + self.attributes["merchantLocation"] = merchantLocation self.attributes["coordinates"] = coordinates self.attributes["recurring"] = recurring self.attributes["interchange"] = interchange @@ -153,8 +155,9 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b def from_json_api(_id, _type, attributes, relationships): return PurchaseTransactionDTO( _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], attributes["cardLast4Digits"], - Merchant.from_json_api(attributes["merchant"]), Coordinates.from_json_api(attributes.get("coordinates")), + attributes["amount"], attributes["balance"], attributes.get("summary"), attributes["last4Digits"], + attributes.get("merchantName"), attributes.get("merchantType"), attributes.get("merchantLocation"), + Coordinates.from_json_api(attributes.get("coordinates")), attributes["recurring"], attributes.get("interchange"), attributes.get("ecommerce"), attributes.get("cardPresent"), attributes.get("paymentMethod"), attributes.get("digitalWallet"), attributes.get("cardVerificationData"), attributes.get("cardNetwork"), attributes.get("tags"), @@ -463,4 +466,118 @@ def to_dict(self) -> Dict: parameters["sort"] = self.sort if self.include: parameters["include"] = self.include - return parameters \ No newline at end of file + return parameters + + +class SimulatePurchaseTransaction(UnitRequest): + def __init__( + self, + amount: int, + card_id: str, + last_4_Digits: str, + deposit_account_id: str, + merchantName: str, + merchantType: str, + merchantLocation: str, + direction: str = "Debit", + authorization_id: str = None, + ): + self.authorization_id = authorization_id + self.last_4_Digits = last_4_Digits + self.deposit_account_id = deposit_account_id + self.direction = direction + self.amount = amount + self.card_id = card_id + self.merchantName = merchantName + self.merchantType = merchantType + self.merchantLocation = merchantLocation + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "purchaseTransaction", + "attributes": { + "amount": self.amount, + "direction": self.direction, + "last4Digits": self.last_4_Digits, + "merchantName": "The Home Depot", + "merchantType": self.merchantType, + "merchantLocation": self.merchantLocation, + "recurring": False + }, + "relationships": { + "account": { + "data": { + "type": "depositAccount", + "id": self.deposit_account_id + } + } + } + + } + } + + if self.authorization_id: + payload["data"]["relationships"]["authorization"] = { + "data": { + "type": "authorization", + "id": self.authorization_id + } + } + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + + +class SimulateCardTransaction(UnitRequest): + def __init__( + self, + amount: int, + card_id: str, + card_last_4_Digits: str, + deposit_account: str, + merchantName: str, + merchantType: str, + merchantLocation: str, + direction: str = "Debit", + ): + self.card_last_4_Digits = card_last_4_Digits + self.deposit_account = deposit_account + self.direction = direction + self.amount = amount + self.card_id = card_id + self.merchantName = merchantName + self.merchantType = merchantType + self.merchantLocation = merchantLocation + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "cardTransaction", + "attributes": { + "amount": self.amount, + "direction": self.direction, + "cardLast4Digits": self.card_last_4_Digits, + "merchantName": "The Home Depot", + "merchantType": self.merchantType, + "merchantLocation": self.merchantLocation, + "recurring": False + }, + "relationships": { + "account": { + "data": { + "type": "depositAccount", + "id": self.deposit_account + } + }, + } + + } + } + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) \ No newline at end of file From 28622343c77d2b6af0769ae3df016a6da35daf9a Mon Sep 17 00:00:00 2001 From: Eric Ghildyal Date: Thu, 3 Aug 2023 11:24:52 -0400 Subject: [PATCH 079/137] Update objects with unit dto --- unit/models/__init__.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index dfcf1f47..38ae37c8 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -165,7 +165,7 @@ def __str__(self): EntityType = Literal["Corporation", "LLC", "Partnership"] -class FullName(object): +class FullName(UnitDTO): def __init__(self, first: str, last: str): self.first = first self.last = last @@ -179,7 +179,7 @@ def from_json_api(data: Dict): # todo: Alex - use typing.Literal for multi accepted values (e.g country) -class Address(object): +class Address(UnitDTO): def __init__( self, street: str, @@ -208,7 +208,7 @@ def from_json_api(data: Dict): ) -class Phone(object): +class Phone(UnitDTO): def __init__(self, country_code: str, number: str): self.country_code = country_code self.number = number @@ -218,7 +218,7 @@ def from_json_api(data: Dict): return Phone(data.get("countryCode"), data.get("number")) -class BusinessContact(object): +class BusinessContact(UnitDTO): def __init__(self, full_name: FullName, email: str, phone: Phone): self.full_name = full_name self.email = email @@ -233,7 +233,7 @@ def from_json_api(data: Dict): ) -class Officer(object): +class Officer(UnitDTO): def __init__( self, full_name: FullName, @@ -274,7 +274,7 @@ def from_json_api(data: Dict): ) -class BeneficialOwner(object): +class BeneficialOwner(UnitDTO): def __init__( self, full_name: FullName, @@ -320,7 +320,7 @@ def from_json_api(l: List): return beneficial_owners -class AuthorizedUser(object): +class AuthorizedUser(UnitDTO): def __init__(self, full_name: FullName, email: str, phone: Phone): self.full_name = full_name self.email = email @@ -338,7 +338,7 @@ def from_json_api(l: List) -> List: return authorized_users -class WireCounterparty(object): +class WireCounterparty(UnitDTO): def __init__( self, routing_number: str, account_number: str, name: str, address: Address ): @@ -357,7 +357,7 @@ def from_json_api(data: Dict): ) -class Counterparty(object): +class Counterparty(UnitDTO): def __init__( self, routing_number: str, account_number: str, account_type: str, name: str ): @@ -376,7 +376,7 @@ def from_json_api(data: Dict): ) -class Coordinates(object): +class Coordinates(UnitDTO): def __init__(self, longitude: int, latitude: int): self.longitude = longitude self.latitude = latitude @@ -389,7 +389,7 @@ def from_json_api(data: Dict): return None -class Merchant(object): +class Merchant(UnitDTO): def __init__( self, name: str, type: int, category: Optional[str], location: Optional[str] ): @@ -405,7 +405,7 @@ def from_json_api(data: Dict): ) -class CardLevelLimits(object): +class CardLevelLimits(UnitDTO): def __init__( self, daily_withdrawal: int, @@ -428,7 +428,7 @@ def from_json_api(data: Dict): ) -class CardTotals(object): +class CardTotals(UnitDTO): def __init__(self, withdrawals: int, deposits: int, purchases: int): self.withdrawals = withdrawals self.deposits = deposits @@ -439,7 +439,7 @@ def from_json_api(data: Dict): return CardTotals(data["withdrawals"], data["deposits"], data["purchases"]) -class DeviceFingerprint(object): +class DeviceFingerprint(UnitDTO): def __init__(self, value: str, provider: str = "iovation"): self.value = value self.provider = provider From 88ad935c3ac19242cfa040a5eaa738818ed9f45c Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Fri, 4 Aug 2023 16:50:11 -0700 Subject: [PATCH 080/137] Add same day flag for ACH payments --- unit/models/payment.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index 502fb3f0..228198d2 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -149,9 +149,10 @@ def from_json_api(_id, _type, attributes, relationships): class CreatePaymentBaseRequest(UnitRequest): def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], idempotency_key: Optional[str], tags: Optional[Dict[str, str]], direction: str = "Credit", - type: str = "achPayment"): + same_day: Optional[bool] = False, type: str = "achPayment"): self.type = type self.amount = amount + self.same_day = same_day self.description = description self.direction = direction self.idempotency_key = idempotency_key @@ -173,6 +174,9 @@ def to_json_api(self) -> Dict: if self.idempotency_key: payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + if self.same_day: + payload["data"]["attributes"]["sameDay"] = self.same_day if self.tags: payload["data"]["attributes"]["tags"] = self.tags @@ -185,8 +189,8 @@ def __repr__(self): class CreateInlinePaymentRequest(CreatePaymentBaseRequest): def __init__(self, amount: int, description: str, counterparty: Counterparty, relationships: Dict[str, Relationship], addenda: Optional[str], idempotency_key: Optional[str], tags: Optional[Dict[str, str]], - direction: str = "Credit"): - CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction) + same_day: Optional[bool] = False, direction: str = "Credit"): + CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction, same_day) self.counterparty = counterparty self.addenda = addenda @@ -203,8 +207,8 @@ def to_json_api(self) -> Dict: class CreateLinkedPaymentRequest(CreatePaymentBaseRequest): def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], addenda: Optional[str], verify_counterparty_balance: Optional[bool], idempotency_key: Optional[str], - tags: Optional[Dict[str, str]], direction: str = "Credit"): - CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction) + same_day: Optional[bool] = False, tags: Optional[Dict[str, str]], direction: str = "Credit"): + CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction, same_day) self.addenda = addenda self.verify_counterparty_balance = verify_counterparty_balance From fe3576c9980bed0fa381729f41238f5c593f7cb9 Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Fri, 4 Aug 2023 17:36:46 -0700 Subject: [PATCH 081/137] Fix ordering of function parameters --- unit/models/payment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index 228198d2..3f87afd6 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -189,7 +189,7 @@ def __repr__(self): class CreateInlinePaymentRequest(CreatePaymentBaseRequest): def __init__(self, amount: int, description: str, counterparty: Counterparty, relationships: Dict[str, Relationship], addenda: Optional[str], idempotency_key: Optional[str], tags: Optional[Dict[str, str]], - same_day: Optional[bool] = False, direction: str = "Credit"): + direction: str = "Credit", same_day: Optional[bool] = False): CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction, same_day) self.counterparty = counterparty self.addenda = addenda @@ -207,7 +207,7 @@ def to_json_api(self) -> Dict: class CreateLinkedPaymentRequest(CreatePaymentBaseRequest): def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], addenda: Optional[str], verify_counterparty_balance: Optional[bool], idempotency_key: Optional[str], - same_day: Optional[bool] = False, tags: Optional[Dict[str, str]], direction: str = "Credit"): + tags: Optional[Dict[str, str]], same_day: Optional[bool] = False, direction: str = "Credit"): CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction, same_day) self.addenda = addenda self.verify_counterparty_balance = verify_counterparty_balance From c38cefa6c05e55149fadf03cf03256c254b8ad8d Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Tue, 8 Aug 2023 14:16:40 -0700 Subject: [PATCH 082/137] Fix book payments --- unit/models/payment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index 3f87afd6..17507b1b 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -149,7 +149,7 @@ def from_json_api(_id, _type, attributes, relationships): class CreatePaymentBaseRequest(UnitRequest): def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], idempotency_key: Optional[str], tags: Optional[Dict[str, str]], direction: str = "Credit", - same_day: Optional[bool] = False, type: str = "achPayment"): + type: str = "achPayment", same_day: Optional[bool] = False): self.type = type self.amount = amount self.same_day = same_day @@ -207,7 +207,7 @@ def to_json_api(self) -> Dict: class CreateLinkedPaymentRequest(CreatePaymentBaseRequest): def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], addenda: Optional[str], verify_counterparty_balance: Optional[bool], idempotency_key: Optional[str], - tags: Optional[Dict[str, str]], same_day: Optional[bool] = False, direction: str = "Credit"): + tags: Optional[Dict[str, str]], direction: str = "Credit", same_day: Optional[bool] = False): CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction, same_day) self.addenda = addenda self.verify_counterparty_balance = verify_counterparty_balance From b0be3ffbf2278625e796906510f935798e259fae Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Tue, 8 Aug 2023 17:15:33 -0700 Subject: [PATCH 083/137] Use keyword arg for same day to avoid errors --- unit/models/payment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index 17507b1b..e8a62392 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -190,7 +190,7 @@ class CreateInlinePaymentRequest(CreatePaymentBaseRequest): def __init__(self, amount: int, description: str, counterparty: Counterparty, relationships: Dict[str, Relationship], addenda: Optional[str], idempotency_key: Optional[str], tags: Optional[Dict[str, str]], direction: str = "Credit", same_day: Optional[bool] = False): - CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction, same_day) + CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction, same_day=same_day) self.counterparty = counterparty self.addenda = addenda @@ -208,7 +208,7 @@ class CreateLinkedPaymentRequest(CreatePaymentBaseRequest): def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], addenda: Optional[str], verify_counterparty_balance: Optional[bool], idempotency_key: Optional[str], tags: Optional[Dict[str, str]], direction: str = "Credit", same_day: Optional[bool] = False): - CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction, same_day) + CreatePaymentBaseRequest.__init__(self, amount, description, relationships, idempotency_key, tags, direction, same_day=same_day) self.addenda = addenda self.verify_counterparty_balance = verify_counterparty_balance From 8c7219ea93a1a65cb43b684d331098960da158c3 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Wed, 16 Aug 2023 14:11:18 -0700 Subject: [PATCH 084/137] replace sb_ with sandbox_ prefix --- unit/api/authorization_request_resource.py | 2 +- unit/api/transaction_resource.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/unit/api/authorization_request_resource.py b/unit/api/authorization_request_resource.py index e61f538e..dc8dca7b 100644 --- a/unit/api/authorization_request_resource.py +++ b/unit/api/authorization_request_resource.py @@ -44,7 +44,7 @@ def decline(self, request: DeclineAuthorizationRequest) -> Union[UnitResponse[Pu else: return UnitError.from_json_api(response.json()) - def sb_simulate(self, request: SimulateAuthorizationRequest) -> Union[UnitResponse[PurchaseAuthorizationRequestDTO], UnitError]: + def sandbox_simulate(self, request: SimulateAuthorizationRequest) -> Union[UnitResponse[PurchaseAuthorizationRequestDTO], UnitError]: payload = request.to_json_api() response = super().post(f"sandbox/{self.resource}/purchase", payload) if super().is_20x(response.status_code): diff --git a/unit/api/transaction_resource.py b/unit/api/transaction_resource.py index 126e304f..d9291033 100644 --- a/unit/api/transaction_resource.py +++ b/unit/api/transaction_resource.py @@ -39,7 +39,7 @@ def update(self, request: PatchTransactionRequest) -> Union[UnitResponse[Transac else: return UnitError.from_json_api(response.json()) - def sb_simulate_purchase_transaction(self, request: SimulatePurchaseTransaction) -> Union[UnitResponse[PurchaseTransactionDTO], UnitError]: + def sandbox_simulate_purchase_transaction(self, request: SimulatePurchaseTransaction) -> Union[UnitResponse[PurchaseTransactionDTO], UnitError]: payload = request.to_json_api() response = super().post(f"sandbox/purchases", payload) if super().is_20x(response.status_code): @@ -48,11 +48,11 @@ def sb_simulate_purchase_transaction(self, request: SimulatePurchaseTransaction) else: return UnitError.from_json_api(response.json()) - def sb_simulate_card_transaction(self, request: SimulateCardTransaction) -> Union[UnitResponse[CardTransactionDTO], UnitError]: + def sandbox_simulate_card_transaction(self, request: SimulateCardTransaction) -> Union[UnitResponse[CardTransactionDTO], UnitError]: payload = request.to_json_api() response = super().post(f"sandbox/card-transactions", payload) if super().is_20x(response.status_code): data = response.json().get("data") return UnitResponse[CardTransactionDTO](DtoDecoder.decode(data), None) else: - return UnitError.from_json_api(response.json()) \ No newline at end of file + return UnitError.from_json_api(response.json()) From 330d08caa75def04a516e59943a1fab88a97688d Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Wed, 16 Aug 2023 14:14:45 -0700 Subject: [PATCH 085/137] use merchant name --- unit/models/transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/transaction.py b/unit/models/transaction.py index 223525d1..10f15476 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -500,7 +500,7 @@ def to_json_api(self) -> Dict: "amount": self.amount, "direction": self.direction, "last4Digits": self.last_4_Digits, - "merchantName": "The Home Depot", + "merchantName": self.merchantName, "merchantType": self.merchantType, "merchantLocation": self.merchantLocation, "recurring": False From c13b1e79a89e6682117b79198347f9e9b0014343 Mon Sep 17 00:00:00 2001 From: Julia Park Date: Thu, 17 Aug 2023 12:17:55 -0400 Subject: [PATCH 086/137] Fix typo --- unit/models/fee.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/fee.py b/unit/models/fee.py index 296ce187..efbf2e0d 100644 --- a/unit/models/fee.py +++ b/unit/models/fee.py @@ -38,7 +38,7 @@ def to_json_api(self) -> Dict: } if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.tags + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key if self.tags: payload["data"]["attributes"]["tags"] = self.tags From d25d2dcc0e7788a2f34cb97f1025c151a0d5ee96 Mon Sep 17 00:00:00 2001 From: Julia Park Date: Thu, 17 Aug 2023 15:38:13 -0400 Subject: [PATCH 087/137] Add batch releases to Unit object --- unit/__init__.py | 2 ++ unit/api/batch_release_resource.py | 17 +++++++++ unit/models/batch_release.py | 57 ++++++++++++++++++++++++++++++ unit/models/codecs.py | 4 +++ 4 files changed, 80 insertions(+) create mode 100644 unit/api/batch_release_resource.py create mode 100644 unit/models/batch_release.py diff --git a/unit/__init__.py b/unit/__init__.py index bc74f256..c5902c7b 100644 --- a/unit/__init__.py +++ b/unit/__init__.py @@ -1,4 +1,5 @@ from unit.api.application_resource import ApplicationResource +from unit.api.batch_release_resource import BatchReleaseResource from unit.api.customer_resource import CustomerResource from unit.api.account_resource import AccountResource from unit.api.card_resource import CardResource @@ -52,3 +53,4 @@ def __init__(self, api_url, token): self.authorization_requests = AuthorizationRequestResource(api_url, token) self.account_end_of_day = AccountEndOfDayResource(api_url, token) self.rewards = RewardResource(api_url, token) + self.batchRelease = BatchReleaseResource(api_url, token) diff --git a/unit/api/batch_release_resource.py b/unit/api/batch_release_resource.py new file mode 100644 index 00000000..83f8482a --- /dev/null +++ b/unit/api/batch_release_resource.py @@ -0,0 +1,17 @@ +from unit.api.base_resource import BaseResource +from unit.models.batch_release import * +from unit.models.codecs import DtoDecoder + + +class BatchReleaseResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = 'batch-releases' + + def create(self, batch_releases: List[CreateBatchReleaseRequest]) -> Union[UnitResponse[List[BatchReleaseDTO]], UnitError]: + response = super().post(self.resource, {"data": batch_releases}) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[BatchReleaseDTO](DtoDecoder.decode(data), DtoDecoder.decode(data)) # is this right based on list?? + else: + return UnitError.from_json_api(response.json()) diff --git a/unit/models/batch_release.py b/unit/models/batch_release.py new file mode 100644 index 00000000..8d896e7f --- /dev/null +++ b/unit/models/batch_release.py @@ -0,0 +1,57 @@ +from unit.models import * + + +class BatchReleaseDTO(object): + def __init__(self, id: str, amount: int, description: str, sender_name: str, sender_address: Address, + sender_account_number: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = "batchRelease" + self.attributes = { + "amount": amount, + "description": description, + "senderName": sender_name, + "senderAccountNumber": sender_account_number, + "senderAddress": sender_address, + "tags": tags, + } + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return BatchReleaseDTO(_id, attributes["amount"], attributes["description"], attributes["senderName"], attributes["senderAddress"], attributes["senderAccountNumber"], attributes.get("tags"), relationships) + + +class CreateBatchReleaseRequest(object): + def __init__(self, amount: int, description: str, sender_name: str, sender_address: Address, + sender_account_number: str, relationships: Optional[Dict[str, Relationship]], tags: Optional[Dict[str, str]] = None, + idempotency_key: Optional[str] = None): + self.amount = amount + self.description = description + self.sender_name = sender_name + self.sender_address = sender_address + self.sender_account_number = sender_account_number + self.tags = tags + self.idempotency_key = idempotency_key + self.relationships = relationships + + def to_json_api(self) -> Dict: + payload = { + "type": "batchRelease", + "attributes": { + "amount": self.amount, + "description": self.description, + "senderName": self.sender_name, + "senderAccountNumber": self.sender_account_number, + "senderAddress": self.sender_address + }, + "relationships": self.relationships + } + + if self.idempotency_key: + payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + + if self.tags: + payload["data"]["attributes"]["tags"] = self.tags + + return payload diff --git a/unit/models/codecs.py b/unit/models/codecs.py index ed7d296e..d8fd1bb5 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -2,6 +2,7 @@ from unit.models import * from datetime import datetime, date +from unit.models.batch_release import BatchReleaseDTO from unit.models.reward import RewardDTO from unit.utils import date_utils from unit.models.applicationForm import ApplicationFormDTO @@ -275,6 +276,9 @@ "reward": lambda _id, _type, attributes, relationships: RewardDTO.from_json_api(_id, attributes, relationships), + "batchRelease": lambda _id, _type, attributes, relationships: + BatchReleaseDTO.from_json_api(_id, _type, attributes, relationships), + } From 47333ce1e4182958aa63af8afdbe7ab926d5a021 Mon Sep 17 00:00:00 2001 From: Julia Park Date: Thu, 17 Aug 2023 15:41:17 -0400 Subject: [PATCH 088/137] rename --- unit/api/batch_release_resource.py | 2 +- unit/models/batch_release.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/api/batch_release_resource.py b/unit/api/batch_release_resource.py index 83f8482a..28f5d00b 100644 --- a/unit/api/batch_release_resource.py +++ b/unit/api/batch_release_resource.py @@ -8,7 +8,7 @@ def __init__(self, api_url, token): super().__init__(api_url, token) self.resource = 'batch-releases' - def create(self, batch_releases: List[CreateBatchReleaseRequest]) -> Union[UnitResponse[List[BatchReleaseDTO]], UnitError]: + def create(self, batch_releases: List[CreateBatchRelease]) -> Union[UnitResponse[List[BatchReleaseDTO]], UnitError]: response = super().post(self.resource, {"data": batch_releases}) if super().is_20x(response.status_code): data = response.json().get("data") diff --git a/unit/models/batch_release.py b/unit/models/batch_release.py index 8d896e7f..1b0264e9 100644 --- a/unit/models/batch_release.py +++ b/unit/models/batch_release.py @@ -22,7 +22,7 @@ def from_json_api(_id, _type, attributes, relationships): return BatchReleaseDTO(_id, attributes["amount"], attributes["description"], attributes["senderName"], attributes["senderAddress"], attributes["senderAccountNumber"], attributes.get("tags"), relationships) -class CreateBatchReleaseRequest(object): +class CreateBatchRelease(object): def __init__(self, amount: int, description: str, sender_name: str, sender_address: Address, sender_account_number: str, relationships: Optional[Dict[str, Relationship]], tags: Optional[Dict[str, str]] = None, idempotency_key: Optional[str] = None): From 658772b428fada56470f200ba59e0b4527ba7c7d Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Thu, 17 Aug 2023 15:13:39 -0700 Subject: [PATCH 089/137] Fix purchaseTransactionDTO cardLast4Digits key error --- unit/models/transaction.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/unit/models/transaction.py b/unit/models/transaction.py index 10f15476..e89438d2 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -131,13 +131,13 @@ def from_json_api(_id, _type, attributes, relationships): class PurchaseTransactionDTO(BaseTransactionDTO): def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, last_4_digits: str, merchantName: str, merchantType:str, merchantLocation: str, + summary: str, card_last_4_digits: str, merchantName: str, merchantType:str, merchantLocation: str, coordinates: Coordinates, recurring: bool,interchange: Optional[int], ecommerce: bool, card_present: bool, payment_method: Optional[str], digital_wallet: Optional[str], card_verification_data, card_network: Optional[str], tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) self.type = 'purchaseTransaction' - self.attributes["last4Digits"] = last_4_digits + self.attributes["cardLast4Digits"] = card_last_4_digits self.attributes["merchantName"] = merchantName self.attributes["merchantType"] = merchantType self.attributes["merchantLocation"] = merchantLocation @@ -155,7 +155,7 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b def from_json_api(_id, _type, attributes, relationships): return PurchaseTransactionDTO( _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes.get("summary"), attributes["last4Digits"], + attributes["amount"], attributes["balance"], attributes.get("summary"), attributes["cardLast4Digits"], attributes.get("merchantName"), attributes.get("merchantType"), attributes.get("merchantLocation"), Coordinates.from_json_api(attributes.get("coordinates")), attributes["recurring"], attributes.get("interchange"), attributes.get("ecommerce"), @@ -499,7 +499,7 @@ def to_json_api(self) -> Dict: "attributes": { "amount": self.amount, "direction": self.direction, - "last4Digits": self.last_4_Digits, + "cardLast4Digits": self.card_last_4_Digits, "merchantName": self.merchantName, "merchantType": self.merchantType, "merchantLocation": self.merchantLocation, From dbe0074d8073071f0d50f4852ef736aca3a4dd6b Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Thu, 17 Aug 2023 16:05:43 -0700 Subject: [PATCH 090/137] Updates DTOs to accomodate purchase simulation responses --- unit/models/transaction.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/unit/models/transaction.py b/unit/models/transaction.py index e89438d2..ae1c3458 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -134,7 +134,7 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b summary: str, card_last_4_digits: str, merchantName: str, merchantType:str, merchantLocation: str, coordinates: Coordinates, recurring: bool,interchange: Optional[int], ecommerce: bool, card_present: bool, payment_method: Optional[str], digital_wallet: Optional[str], card_verification_data, card_network: Optional[str], - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]], last_4_digits: str = None): BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) self.type = 'purchaseTransaction' self.attributes["cardLast4Digits"] = card_last_4_digits @@ -151,17 +151,21 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b self.attributes["cardVerificationData"] = card_verification_data self.attributes["cardNetwork"] = card_network + # Unit incorrectly returns last4Digits for simulation responses + if last_4_digits: + self.attributes["last4Digits"] = last_4_digits + @staticmethod def from_json_api(_id, _type, attributes, relationships): return PurchaseTransactionDTO( _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes.get("summary"), attributes["cardLast4Digits"], + attributes["amount"], attributes["balance"], attributes.get("summary"), attributes.get("cardLast4Digits", None), attributes.get("merchantName"), attributes.get("merchantType"), attributes.get("merchantLocation"), Coordinates.from_json_api(attributes.get("coordinates")), attributes["recurring"], attributes.get("interchange"), attributes.get("ecommerce"), attributes.get("cardPresent"), attributes.get("paymentMethod"), attributes.get("digitalWallet"), attributes.get("cardVerificationData"), attributes.get("cardNetwork"), attributes.get("tags"), - relationships) + relationships, attributes.get("last4Digits", None)) class AtmTransactionDTO(BaseTransactionDTO): @@ -499,7 +503,7 @@ def to_json_api(self) -> Dict: "attributes": { "amount": self.amount, "direction": self.direction, - "cardLast4Digits": self.card_last_4_Digits, + "last4Digits": self.last_4_Digits, "merchantName": self.merchantName, "merchantType": self.merchantType, "merchantLocation": self.merchantLocation, From 5ce53918549826aaeb6cefe27cc64b08e7ef5cd5 Mon Sep 17 00:00:00 2001 From: Julia Park Date: Mon, 21 Aug 2023 09:19:27 -0400 Subject: [PATCH 091/137] fix error --- unit/models/batch_release.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unit/models/batch_release.py b/unit/models/batch_release.py index 1b0264e9..7374e2d2 100644 --- a/unit/models/batch_release.py +++ b/unit/models/batch_release.py @@ -30,7 +30,7 @@ def __init__(self, amount: int, description: str, sender_name: str, sender_addre self.description = description self.sender_name = sender_name self.sender_address = sender_address - self.sender_account_number = sender_account_number + self.sender_account_number = sender_account_number # BIN followed by last four digits? self.tags = tags self.idempotency_key = idempotency_key self.relationships = relationships @@ -49,9 +49,9 @@ def to_json_api(self) -> Dict: } if self.idempotency_key: - payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key + payload["attributes"]["idempotencyKey"] = self.idempotency_key if self.tags: - payload["data"]["attributes"]["tags"] = self.tags + payload["attributes"]["tags"] = self.tags return payload From 0a1ce8aa5e4f9201031afd609e5935ff7a61f966 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Tue, 22 Aug 2023 16:20:14 -0700 Subject: [PATCH 092/137] Adds CheckDepositPendingReviewEvent and CheckDepositPendingEvent events --- unit/models/event.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/unit/models/event.py b/unit/models/event.py index e6fcfa72..9e16a02f 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -242,6 +242,30 @@ def from_json_api(_id, _type, attributes, relationships): return CheckDepositCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], attributes.get("tags"), relationships) +class CheckDepositPendingReviewEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkDeposit.pendingReview' + self.attributes["status"] = status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckDepositCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["status"], attributes.get("tags"), relationships) + +class CheckDepositPendingEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkDeposit.pending' + self.attributes["status"] = status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckDepositCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["status"], attributes.get("tags"), relationships) + class CheckDepositClearingEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): From b734e22546e42073bdf3e0100843032268758fb0 Mon Sep 17 00:00:00 2001 From: Julia Park Date: Wed, 23 Aug 2023 13:47:01 -0400 Subject: [PATCH 093/137] fix issues --- unit/api/batch_release_resource.py | 4 ++-- unit/models/batch_release.py | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/unit/api/batch_release_resource.py b/unit/api/batch_release_resource.py index 28f5d00b..e8928741 100644 --- a/unit/api/batch_release_resource.py +++ b/unit/api/batch_release_resource.py @@ -8,10 +8,10 @@ def __init__(self, api_url, token): super().__init__(api_url, token) self.resource = 'batch-releases' - def create(self, batch_releases: List[CreateBatchRelease]) -> Union[UnitResponse[List[BatchReleaseDTO]], UnitError]: + def create(self, batch_releases: List[BatchReleaseDTO]) -> Union[UnitResponse[List[BatchReleaseDTO]], UnitError]: response = super().post(self.resource, {"data": batch_releases}) if super().is_20x(response.status_code): data = response.json().get("data") - return UnitResponse[BatchReleaseDTO](DtoDecoder.decode(data), DtoDecoder.decode(data)) # is this right based on list?? + return UnitResponse[List[BatchReleaseDTO]](DtoDecoder.decode(data), None) else: return UnitError.from_json_api(response.json()) diff --git a/unit/models/batch_release.py b/unit/models/batch_release.py index 7374e2d2..4e65af13 100644 --- a/unit/models/batch_release.py +++ b/unit/models/batch_release.py @@ -19,10 +19,10 @@ def __init__(self, id: str, amount: int, description: str, sender_name: str, sen @staticmethod def from_json_api(_id, _type, attributes, relationships): - return BatchReleaseDTO(_id, attributes["amount"], attributes["description"], attributes["senderName"], attributes["senderAddress"], attributes["senderAccountNumber"], attributes.get("tags"), relationships) + return BatchReleaseDTO(_id, attributes["amount"], attributes["description"], attributes["senderName"], Address.from_json_api(attributes["senderAddress"]), attributes["senderAccountNumber"], attributes.get("tags"), relationships) -class CreateBatchRelease(object): +class CreateBatchRelease(UnitRequest): def __init__(self, amount: int, description: str, sender_name: str, sender_address: Address, sender_account_number: str, relationships: Optional[Dict[str, Relationship]], tags: Optional[Dict[str, str]] = None, idempotency_key: Optional[str] = None): @@ -30,7 +30,7 @@ def __init__(self, amount: int, description: str, sender_name: str, sender_addre self.description = description self.sender_name = sender_name self.sender_address = sender_address - self.sender_account_number = sender_account_number # BIN followed by last four digits? + self.sender_account_number = sender_account_number self.tags = tags self.idempotency_key = idempotency_key self.relationships = relationships @@ -55,3 +55,6 @@ def to_json_api(self) -> Dict: payload["attributes"]["tags"] = self.tags return payload + + def __repr__(self): + return json.dumps(self.to_json_api()) From bed432b8bd7b0fafd53a5209af96b05ce5be88a8 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Mon, 28 Aug 2023 12:51:15 -0700 Subject: [PATCH 094/137] Check deposits --- unit/__init__.py | 2 + unit/api/check_deposit_resource.py | 23 +++++++++++ unit/models/__init__.py | 17 ++++++++ unit/models/check_deposit.py | 63 ++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 unit/api/check_deposit_resource.py create mode 100644 unit/models/check_deposit.py diff --git a/unit/__init__.py b/unit/__init__.py index bc74f256..12626407 100644 --- a/unit/__init__.py +++ b/unit/__init__.py @@ -1,4 +1,5 @@ from unit.api.application_resource import ApplicationResource +from unit.api.check_deposit_resource import CheckDepositResource from unit.api.customer_resource import CustomerResource from unit.api.account_resource import AccountResource from unit.api.card_resource import CardResource @@ -34,6 +35,7 @@ def __init__(self, api_url, token): self.cards = CardResource(api_url, token) self.transactions = TransactionResource(api_url, token) self.payments = PaymentResource(api_url, token) + self.check_deposits = CheckDepositResource(api_url, token) self.repayments = RepaymentResource(api_url, token) self.ach = AchResource(api_url, token) self.statements = StatementResource(api_url, token) diff --git a/unit/api/check_deposit_resource.py b/unit/api/check_deposit_resource.py new file mode 100644 index 00000000..c5b5bc13 --- /dev/null +++ b/unit/api/check_deposit_resource.py @@ -0,0 +1,23 @@ +from unit.api.base_resource import BaseResource +from unit.models.check_deposit import * +from unit.models.codecs import DtoDecoder +from unit.models.transaction import * + + +class CheckDepositResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "check-deposits" + + def get(self, check_deposit_id: str) -> Union[UnitResponse[CheckDepositDTO], UnitError]: + params = {} + response = super().get(f"{self.resource}/{check_deposit_id}", params) + if response.status_code == 200: + data = response.json().get("data") + included = response.json().get("included") + return UnitResponse[TransactionDTO](DtoDecoder.decode(data), DtoDecoder.decode(included)) + else: + return UnitError.from_json_api(response.json()) + + def list(self, params: ListTransactionParams = None) -> Union[UnitResponse[List[CheckDepositDTO]], UnitError]: + raise NotImplementedError() diff --git a/unit/models/__init__.py b/unit/models/__init__.py index 38ae37c8..1b534e31 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -376,6 +376,23 @@ def from_json_api(data: Dict): ) +class CheckCounterparty(UnitDTO): + def __init__( + self, routing_number: str, account_number: str, name: str + ): + self.routing_number = routing_number + self.account_number = account_number + self.name = name + + @staticmethod + def from_json_api(data: Dict): + return CheckCounterparty( + data["routingNumber"], + data["accountNumber"], + data["name"], + ) + + class Coordinates(UnitDTO): def __init__(self, longitude: int, latitude: int): self.longitude = longitude diff --git a/unit/models/check_deposit.py b/unit/models/check_deposit.py new file mode 100644 index 00000000..a240fe19 --- /dev/null +++ b/unit/models/check_deposit.py @@ -0,0 +1,63 @@ +import json +from typing import Optional +from unit.models import * +from unit.utils import date_utils + +CheckDepositStatus = Literal[ + "AwaitingImages", "AwaitingFrontImage", "AwaitingBackImage", "Pending", "PendingReview", "Rejected", "Clearing", "Sent", "Canceled", "Returned", +] + + +class CheckDepositDTO(object): + def __init__( + self, + id: str, + created_at: datetime, + status: CheckDepositStatus, + reason: Optional[str], + description: str, + amount: int, + check_number: str, + counterparty: Optional[CheckCounterparty], + settlement_date: Optional[datetime], + tags: Optional[Dict[str, str]], + relationships: Dict[str, Relationship] + ): + self.id = id + self.type = "authorization" + self.attributes = { + "createdAt": created_at, + "status": status, + "description": description, + "amount": amount, + "checkNumber": check_number, + } + if reason: + self.attributes["reason"] = reason + if counterparty: + self.attributes["counterparty"] = counterparty + if settlement_date: + self.attributes["settlementDate"] = settlement_date + if tags: + self.attributes["tages"] = tags + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + settlement_date = attributes.get("settlementDate") + if settlement_date: + settlement_date = date_utils.to_datetime(settlement_date) + + return CheckDepositDTO( + id=_id, + created_at=date_utils.to_datetime(attributes["createdAt"]), + status=attributes["status"], + reason=attributes.get("reason"), + description=attributes["description"], + amount=attributes["amount"], + check_number=attributes.get("checkNumber"), + counterparty=attributes.get("counterparty"), + settlementDate=settlement_date, + tags=attributes.get("tags"), + relationships=relationships, + ) From 40aa08fcfa1435ccb2f861844cada765cea7d375 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Tue, 29 Aug 2023 09:34:52 -0700 Subject: [PATCH 095/137] Check deposit webhook updates --- unit/models/event.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/unit/models/event.py b/unit/models/event.py index 9e16a02f..63ee5462 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -242,29 +242,32 @@ def from_json_api(_id, _type, attributes, relationships): return CheckDepositCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], attributes.get("tags"), relationships) + class CheckDepositPendingReviewEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'checkDeposit.pendingReview' - self.attributes["status"] = status + self.attributes["previousStatus"] = previous_status @staticmethod def from_json_api(_id, _type, attributes, relationships): return CheckDepositCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["status"], attributes.get("tags"), relationships) + attributes["previousStatus"], attributes.get("tags"), relationships) + class CheckDepositPendingEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, status: str, tags: Optional[Dict[str, str]], + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'checkDeposit.pending' - self.attributes["status"] = status + self.attributes["previousStatus"] = previous_status @staticmethod def from_json_api(_id, _type, attributes, relationships): return CheckDepositCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["status"], attributes.get("tags"), relationships) + attributes["previous_status"], attributes.get("tags"), relationships) + class CheckDepositClearingEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], @@ -292,6 +295,21 @@ def from_json_api(_id, _type, attributes, relationships): attributes["previousStatus"], attributes.get("tags"), relationships) +class CheckDepositRejectedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, reason: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkDeposit.rejected' + self.attributes["previousStatus"] = previous_status + self.attributes["reason"] = reason + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckDepositRejectedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["previousStatus"], attributes["reason"], attributes.get("tags"), relationships) + + + class CheckDepositReturnedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): @@ -305,6 +323,7 @@ def from_json_api(_id, _type, attributes, relationships): attributes["previousStatus"], attributes.get("tags"), relationships) + class CustomerCreatedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): From 6fd187d50f7de401ef14f7f31b6f9cbd2ef7dff1 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Thu, 7 Sep 2023 18:45:31 -0700 Subject: [PATCH 096/137] updates --- unit/models/event.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/unit/models/event.py b/unit/models/event.py index 63ee5462..9a32b7dc 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -483,14 +483,17 @@ def from_json_api(_id, _type, attributes, relationships): return AccountReopenedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), relationships) -EventDTO = Union[AccountClosedEvent, AccountFrozenEvent, ApplicationDeniedEvent, ApplicationAwaitingDocumentsEvent, - ApplicationPendingReviewEvent, CardActivatedEvent, CardStatusChangedEvent, - AuthorizationCreatedEvent, AuthorizationCanceledEvent, AuthorizationDeclinedEvent, - AuthorizationRequestDeclinedEvent, AuthorizationRequestPendingEvent, - AuthorizationRequestApprovedEvent, DocumentApprovedEvent, DocumentRejectedEvent, - CheckDepositCreatedEvent, CheckDepositClearingEvent, CheckDepositSentEvent, - CheckDepositReturnedEvent, CustomerCreatedEvent, PaymentClearingEvent, PaymentSentEvent, - PaymentReturnedEvent, StatementsCreatedEvent, TransactionCreatedEvent, AccountReopenedEvent, RawUnitObject] +EventDTO = Union[ + AccountClosedEvent, AccountFrozenEvent, ApplicationDeniedEvent, ApplicationAwaitingDocumentsEvent, + ApplicationPendingReviewEvent, CardActivatedEvent, CardStatusChangedEvent, + AuthorizationCreatedEvent, AuthorizationCanceledEvent, AuthorizationDeclinedEvent, + AuthorizationRequestDeclinedEvent, AuthorizationRequestPendingEvent, + AuthorizationRequestApprovedEvent, DocumentApprovedEvent, DocumentRejectedEvent, + CheckDepositCreatedEvent, CheckDepositPendingReviewEvent, CheckDepositPendingEvent, + CheckDepositClearingEvent, CheckDepositSentEvent, CheckDepositRejectedEvent, CheckDepositReturnedEvent, + CustomerCreatedEvent, PaymentClearingEvent, PaymentSentEvent, PaymentReturnedEvent, + StatementsCreatedEvent, TransactionCreatedEvent, AccountReopenedEvent, RawUnitObject, +] class ListEventParams(UnitParams): From 671f7b48dd6f0408031be2b30aa43a686b207d24 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Fri, 8 Sep 2023 09:56:19 -0700 Subject: [PATCH 097/137] Update codecs for Check Deposit Webhooks --- unit/models/codecs.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index d8fd1bb5..8c187234 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -204,12 +204,24 @@ "checkDeposit.created": lambda _id, _type, attributes, relationships: CheckDepositCreatedEvent.from_json_api(_id, _type, attributes, relationships), + "checkDeposit.pendingReview": lambda _id, _type, attributes, relationships: + CheckDepositPendingReviewEvent.from_json_api(_id, _type, attributes, relationships), + + "checkDeposit.pending": lambda _id, _type, attributes, relationships: + CheckDepositPendingEvent.from_json_api(_id, _type, attributes, relationships), + "checkDeposit.clearing": lambda _id, _type, attributes, relationships: CheckDepositClearingEvent.from_json_api(_id, _type, attributes, relationships), "checkDeposit.sent": lambda _id, _type, attributes, relationships: CheckDepositSentEvent.from_json_api(_id, _type, attributes, relationships), + "checkDeposit.rejected": lambda _id, _type, attributes, relationships: + CheckDepositRejectedEvent.from_json_api(_id, _type, attributes, relationships), + + "checkDeposit.returned": lambda _id, _type, attributes, relationships: + CheckDepositReturnedEvent.from_json_api(_id, _type, attributes, relationships), + "payment.clearing": lambda _id, _type, attributes, relationships: PaymentClearingEvent.from_json_api(_id, _type, attributes, relationships), From 284716bafb4ae44094ac7e7c7b3838e3859f8e1c Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Thu, 14 Sep 2023 15:12:30 -0700 Subject: [PATCH 098/137] Fix previous status typo --- unit/models/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/event.py b/unit/models/event.py index 9a32b7dc..97893ff8 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -266,7 +266,7 @@ def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Op @staticmethod def from_json_api(_id, _type, attributes, relationships): return CheckDepositCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["previous_status"], attributes.get("tags"), relationships) + attributes["previousStatus"], attributes.get("tags"), relationships) class CheckDepositClearingEvent(BaseEvent): From 586cb04192e175355046e7f2344b8a77ab4336b0 Mon Sep 17 00:00:00 2001 From: Julia Park Date: Wed, 20 Sep 2023 16:35:58 -0400 Subject: [PATCH 099/137] Add get_image --- unit/api/check_deposit_resource.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/unit/api/check_deposit_resource.py b/unit/api/check_deposit_resource.py index c5b5bc13..53f8e779 100644 --- a/unit/api/check_deposit_resource.py +++ b/unit/api/check_deposit_resource.py @@ -21,3 +21,11 @@ def get(self, check_deposit_id: str) -> Union[UnitResponse[CheckDepositDTO], Uni def list(self, params: ListTransactionParams = None) -> Union[UnitResponse[List[CheckDepositDTO]], UnitError]: raise NotImplementedError() + + def get_image(self, check_deposit_id: str, is_back_side: Optional[bool] = False) -> Union[UnitResponse[bytes], UnitError]: + params = {} + response = super().get(f"{self.resource}/{check_deposit_id}/{'back' if is_back_side else 'front'}", params) + if response.status_code == 200: + return UnitResponse[bytes](response.content, None) + else: + return UnitError.from_json_api(response.json()) From e24074dac28934200364e721a8a51f80beeeae7e Mon Sep 17 00:00:00 2001 From: Julia Park Date: Mon, 30 Oct 2023 16:26:56 -0400 Subject: [PATCH 100/137] Add account and routing numbers to ListCounterpartyParams --- unit/models/counterparty.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/unit/models/counterparty.py b/unit/models/counterparty.py index e043b414..987c4ba7 100644 --- a/unit/models/counterparty.py +++ b/unit/models/counterparty.py @@ -156,11 +156,14 @@ def from_json_api(_id, _type, attributes, relationships): class ListCounterpartyParams(UnitParams): def __init__(self, offset: int = 0, limit: int = 100, customer_id: Optional[str] = None, - tags: Optional[object] = None): + tags: Optional[object] = None, account_number: Optional[str] = None, + routing_number: Optional[str] = None): self.offset = offset self.limit = limit self.customer_id = customer_id self.tags = tags + self.account_number = account_number + self.routing_number = routing_number def to_dict(self) -> Dict: parameters = {"page[limit]": self.limit, "page[offset]": self.offset} @@ -168,5 +171,9 @@ def to_dict(self) -> Dict: parameters["filter[customerId]"] = self.customer_id if self.tags: parameters["filter[tags]"] = self.tags + if self.account_number: + parameters["filter[accountNumber]"] = self.account_number + if self.routing_number: + parameters["filter[routingNumber]"] = self.routing_number return parameters From ff578893636786d92a9bf66a056cd976099c8432 Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Mon, 13 Nov 2023 08:03:09 -0800 Subject: [PATCH 101/137] Category optional --- unit/models/authorization_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/authorization_request.py b/unit/models/authorization_request.py index 80a521cd..116047de 100644 --- a/unit/models/authorization_request.py +++ b/unit/models/authorization_request.py @@ -29,7 +29,7 @@ def from_json_api(_id, _type, attributes, relationships): attributes.get("partialApprovalAllowed"), attributes.get("approvedAmount"), attributes.get("declineReason"), attributes["merchant"]["name"], attributes["merchant"]["type"], - attributes["merchant"]["category"], + attributes["merchant"].get("category"), attributes["merchant"].get("location"), attributes["recurring"], attributes.get("tags"), relationships) From bcfb02490fbc27b0abb4219439c0074ef96bd970 Mon Sep 17 00:00:00 2001 From: Julia P <142916316+julia-truss@users.noreply.github.com> Date: Thu, 1 Feb 2024 18:04:21 -0500 Subject: [PATCH 102/137] Update purchase and merchant DTOs (#20) * Update purchase and merchant DTOs * Add default values for optional fields --- unit/models/__init__.py | 93 +++++++++++++++++++++++++++++++++++++- unit/models/transaction.py | 28 +++++++----- 2 files changed, 108 insertions(+), 13 deletions(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index 1b534e31..face4c16 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -408,17 +408,18 @@ def from_json_api(data: Dict): class Merchant(UnitDTO): def __init__( - self, name: str, type: int, category: Optional[str], location: Optional[str] + self, name: str, type: int, category: Optional[str] = None, location: Optional[str] = None, _id: Optional[str] = None, ): self.name = name self.type = type self.category = category self.location = location + self.id = _id @staticmethod def from_json_api(data: Dict): return Merchant( - data["name"], data["type"], data.get("category"), data.get("location") + data["name"], data["type"], data.get("category"), data.get("location"), data.get("id") ) @@ -470,3 +471,91 @@ def to_json_api(self): @classmethod def from_json_api(cls, data: Dict): return cls(value=data["value"], provider=data["provider"]) + + +class CurrencyConversion(UnitDTO): + def __init__(self, original_currency: str, amount_in_original_currency: int, fx_rate: Optional[str]): + self.original_currency = original_currency + self.amount_in_original_currency = amount_in_original_currency + self.fx_rate = fx_rate + + @staticmethod + def from_json_api(data: Dict): + if not data: + return None + + return CurrencyConversion(data["originalCurrency"], data["amountInOriginalCurrency"], data.get("fxRate")) + + +class RichMerchantDataFacilitator(object): + def __init__(self, name: str, _type: Optional[str], logo: Optional[str]): + self.name = name + self.type = _type + self.logo = logo + + @staticmethod + def from_json_api(data: Dict): + if not data: + return None + + arr = [] + for c in data: + arr.append(RichMerchantDataFacilitator(c["name"], c.get("type"), c.get("logo"))) + + return arr + + +class RichMerchantDataCategory(object): + def __init__(self, name: str, icon: str): + self.name = name + self.icon = icon + + @staticmethod + def from_json_api(data: Dict): + if not data: + return None + + arr = [] + for c in data: + arr.append(RichMerchantDataCategory(c["name"], c["icon"])) + + return arr + + +class RichMerchantDataAddress(object): + def __init__(self, city: str, state: str, country: str, street: Optional[str]): + self.city = city + self.state = state + self.country = country + self.street = street + + @staticmethod + def from_json_api(data: Dict): + if not data: + return None + + return RichMerchantDataAddress(data["city"], data["state"], data["country"], data.get("street")) + + +class RichMerchantData(UnitDTO): + def __init__(self, name: str, website: Optional[str], logo: Optional[str], phone: Optional[str], + categories: Optional[List[RichMerchantDataCategory]], address: Optional[RichMerchantDataAddress], + coordinates: Optional[Coordinates], facilitators: Optional[List[RichMerchantDataFacilitator]]): + self.name = name + self.website = website + self.logo = logo + self.phone = phone + self.categories = categories + self.address = address + self.coordinates = coordinates + self.facilitators = facilitators + + @staticmethod + def from_json_api(data: Dict): + if not data: + return None + + return RichMerchantData(data["name"], data.get("website"), data.get("logo"), data.get("phone"), + RichMerchantDataCategory.from_json_api(data.get("categories")), data.get("address"), + Coordinates.from_json_api(data.get("coordinates")), + RichMerchantDataFacilitator.from_json_api(data.get("facilitators"))) diff --git a/unit/models/transaction.py b/unit/models/transaction.py index ae1c3458..3cbc939f 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -131,16 +131,17 @@ def from_json_api(_id, _type, attributes, relationships): class PurchaseTransactionDTO(BaseTransactionDTO): def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, - summary: str, card_last_4_digits: str, merchantName: str, merchantType:str, merchantLocation: str, - coordinates: Coordinates, recurring: bool,interchange: Optional[int], ecommerce: bool, card_present: bool, payment_method: Optional[str], - digital_wallet: Optional[str], card_verification_data, card_network: Optional[str], - tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]], last_4_digits: str = None): + summary: str, card_last_4_digits: str, merchant: Merchant, coordinates: Optional[Coordinates], + recurring: bool, ecommerce: bool, card_present: bool, card_verification_data, + interchange: Optional[int] = None, payment_method: Optional[str] = None, digital_wallet: Optional[str] = None, + card_network: Optional[str] = None, tags: Optional[Dict[str, str]] = None, + relationships: Optional[Dict[str, Relationship]] = None, gross_interchange: Optional[str] = None, + cash_withdrawal_amount: Optional[int] = None, currency_conversion: Optional[CurrencyConversion] = None, + rich_merchant_data: Optional[RichMerchantData] = None, last_4_digits: str = None): BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) self.type = 'purchaseTransaction' self.attributes["cardLast4Digits"] = card_last_4_digits - self.attributes["merchantName"] = merchantName - self.attributes["merchantType"] = merchantType - self.attributes["merchantLocation"] = merchantLocation + self.attributes["merchant"] = merchant self.attributes["coordinates"] = coordinates self.attributes["recurring"] = recurring self.attributes["interchange"] = interchange @@ -150,6 +151,10 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b self.attributes["digitalWallet"] = digital_wallet self.attributes["cardVerificationData"] = card_verification_data self.attributes["cardNetwork"] = card_network + self.attributes["grossInterchange"] = gross_interchange + self.attributes["cashWithdrawalAmount"] = cash_withdrawal_amount + self.attributes["currencyConversion"] = currency_conversion + self.attributes["richMerchantData"] = rich_merchant_data # Unit incorrectly returns last4Digits for simulation responses if last_4_digits: @@ -159,13 +164,14 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b def from_json_api(_id, _type, attributes, relationships): return PurchaseTransactionDTO( _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes.get("summary"), attributes.get("cardLast4Digits", None), - attributes.get("merchantName"), attributes.get("merchantType"), attributes.get("merchantLocation"), - Coordinates.from_json_api(attributes.get("coordinates")), + attributes["amount"], attributes["balance"], attributes["summary"], attributes["cardLast4Digits"], + Merchant.from_json_api(attributes["merchant"]), Coordinates.from_json_api(attributes.get("coordinates")), attributes["recurring"], attributes.get("interchange"), attributes.get("ecommerce"), attributes.get("cardPresent"), attributes.get("paymentMethod"), attributes.get("digitalWallet"), attributes.get("cardVerificationData"), attributes.get("cardNetwork"), attributes.get("tags"), - relationships, attributes.get("last4Digits", None)) + relationships, attributes.get("grossInterchange"), attributes.get("cashWithdrawalAmount"), + CurrencyConversion.from_json_api(attributes.get("currencyConversion")), + RichMerchantData.from_json_api(attributes.get("richMerchantData"))) class AtmTransactionDTO(BaseTransactionDTO): From df9cb60913975a0d30844a271558189f3f8ffd56 Mon Sep 17 00:00:00 2001 From: Julia P <142916316+julia-truss@users.noreply.github.com> Date: Fri, 2 Feb 2024 13:31:27 -0500 Subject: [PATCH 103/137] Fix KeyError when converting from json (#21) * Fix KeyError when convertion from json * Fix argument ordering * Fix argument ordering again --- unit/models/transaction.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/unit/models/transaction.py b/unit/models/transaction.py index 3cbc939f..28d51bce 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -133,10 +133,11 @@ class PurchaseTransactionDTO(BaseTransactionDTO): def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, card_last_4_digits: str, merchant: Merchant, coordinates: Optional[Coordinates], recurring: bool, ecommerce: bool, card_present: bool, card_verification_data, - interchange: Optional[int] = None, payment_method: Optional[str] = None, digital_wallet: Optional[str] = None, - card_network: Optional[str] = None, tags: Optional[Dict[str, str]] = None, - relationships: Optional[Dict[str, Relationship]] = None, gross_interchange: Optional[str] = None, - cash_withdrawal_amount: Optional[int] = None, currency_conversion: Optional[CurrencyConversion] = None, + interchange: Optional[int] = None, payment_method: Optional[str] = None, + digital_wallet: Optional[str] = None, card_network: Optional[str] = None, + tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None, + gross_interchange: Optional[str] = None, cash_withdrawal_amount: Optional[int] = None, + currency_conversion: Optional[CurrencyConversion] = None, rich_merchant_data: Optional[RichMerchantData] = None, last_4_digits: str = None): BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) self.type = 'purchaseTransaction' @@ -163,15 +164,15 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b @staticmethod def from_json_api(_id, _type, attributes, relationships): return PurchaseTransactionDTO( - _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], - attributes["amount"], attributes["balance"], attributes["summary"], attributes["cardLast4Digits"], - Merchant.from_json_api(attributes["merchant"]), Coordinates.from_json_api(attributes.get("coordinates")), - attributes["recurring"], attributes.get("interchange"), attributes.get("ecommerce"), - attributes.get("cardPresent"), attributes.get("paymentMethod"), attributes.get("digitalWallet"), - attributes.get("cardVerificationData"), attributes.get("cardNetwork"), attributes.get("tags"), + _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], attributes["amount"], + attributes["balance"], attributes.get("summary"), attributes.get("cardLast4Digits", None), + Merchant.from_json_api(attributes.get("merchant")), Coordinates.from_json_api(attributes.get("coordinates")), + attributes["recurring"], attributes.get("ecommerce"), attributes.get("cardPresent"), + attributes.get("cardVerificationData"), attributes.get("interchange"), attributes.get("paymentMethod"), + attributes.get("digitalWallet"), attributes.get("cardNetwork"), attributes.get("tags"), relationships, attributes.get("grossInterchange"), attributes.get("cashWithdrawalAmount"), CurrencyConversion.from_json_api(attributes.get("currencyConversion")), - RichMerchantData.from_json_api(attributes.get("richMerchantData"))) + RichMerchantData.from_json_api(attributes.get("richMerchantData")), attributes.get("last4Digits", None)) class AtmTransactionDTO(BaseTransactionDTO): From 8dc3d7c6b7c08bdcd016d083313fcf0da925f235 Mon Sep 17 00:00:00 2001 From: Julia P <142916316+julia-truss@users.noreply.github.com> Date: Fri, 2 Feb 2024 14:51:22 -0500 Subject: [PATCH 104/137] Account for incorrect merchant fields for simulations (#22) --- unit/models/transaction.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/unit/models/transaction.py b/unit/models/transaction.py index 28d51bce..0b1b17c4 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -138,7 +138,7 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b tags: Optional[Dict[str, str]] = None, relationships: Optional[Dict[str, Relationship]] = None, gross_interchange: Optional[str] = None, cash_withdrawal_amount: Optional[int] = None, currency_conversion: Optional[CurrencyConversion] = None, - rich_merchant_data: Optional[RichMerchantData] = None, last_4_digits: str = None): + rich_merchant_data: Optional[RichMerchantData] = None, last_4_digits: str = None, ): BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) self.type = 'purchaseTransaction' self.attributes["cardLast4Digits"] = card_last_4_digits @@ -163,10 +163,16 @@ def __init__(self, id: str, created_at: datetime, direction: str, amount: int, b @staticmethod def from_json_api(_id, _type, attributes, relationships): + # Purchase simulations do not return the merchant attribute + simulation_merchant = dict( + name=attributes.get("merchantName", None), + type=attributes.get("merchantType", None), + location=attributes.get("merchantLocation", None), + ) return PurchaseTransactionDTO( _id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], attributes["amount"], attributes["balance"], attributes.get("summary"), attributes.get("cardLast4Digits", None), - Merchant.from_json_api(attributes.get("merchant")), Coordinates.from_json_api(attributes.get("coordinates")), + Merchant.from_json_api(attributes.get("merchant") or simulation_merchant), Coordinates.from_json_api(attributes.get("coordinates")), attributes["recurring"], attributes.get("ecommerce"), attributes.get("cardPresent"), attributes.get("cardVerificationData"), attributes.get("interchange"), attributes.get("paymentMethod"), attributes.get("digitalWallet"), attributes.get("cardNetwork"), attributes.get("tags"), From f3b7775d881594732c8fce0b8e6ee90105f27a09 Mon Sep 17 00:00:00 2001 From: Julia P <142916316+julia-truss@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:44:04 -0500 Subject: [PATCH 105/137] Response should output bytes (#23) --- unit/api/statement_resource.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/api/statement_resource.py b/unit/api/statement_resource.py index be711e1f..791acb7f 100644 --- a/unit/api/statement_resource.py +++ b/unit/api/statement_resource.py @@ -23,7 +23,7 @@ def get_bank_verification(self, account_id: str, include_proof_of_funds: Optiona response = super().get(f"{self.resource}/{account_id}/bank/pdf", {"includeProofOfFunds": include_proof_of_funds}) if response.status_code == 200: - return UnitResponse[str](response.text, None) + return UnitResponse[bytes](response.content, None) else: return UnitError.from_json_api(response.json()) From 6120e8dba426ff51f1677dadab30a984acfd5199 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Wed, 6 Mar 2024 16:34:28 -0800 Subject: [PATCH 106/137] astra pushToCard payment --- unit/models/payment.py | 39 +++++++++++++++++++++++++--- unit_python_sdk.egg-info/PKG-INFO | 1 + unit_python_sdk.egg-info/SOURCES.txt | 4 +++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index e8a62392..897a20ab 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -5,15 +5,20 @@ PaymentDirections = Literal["Debit", "Credit"] PaymentStatus = Literal["Pending", "Rejected", "Clearing", "Sent", "Canceled", "Returned"] + class BasePayment(object): def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: PaymentDirections, description: str, amount: int, reason: Optional[str], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]]): + relationships: Optional[Dict[str, Relationship]], astra_routine_id: Optional[str]): self.id = id self.attributes = {"createdAt": created_at, "status": status, "direction": direction, "description": description, "amount": amount, "reason": reason, "tags": tags} self.relationships = relationships + if astra_routine_id: + self.attributes["astraRoutineId"] = astra_routine_id + + class AchPaymentDTO(BasePayment): def __init__(self, id: str, created_at: datetime, status: PaymentStatus, counterparty: Counterparty, direction: str, description: str, amount: int, addenda: Optional[str], reason: Optional[str], @@ -108,6 +113,21 @@ def from_json_api(_id, _type, attributes, relationships): attributes["description"], attributes["amount"], attributes.get("reason"), attributes.get("tags"), relationships) + +class PushToCardPaymentDTO(BasePayment): + def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: Optional[str], description: str, + amount: int, astra_routine_id: str, reason: Optional[str], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BasePayment.__init__(self, id, created_at, status, direction, description, amount, reason, tags, relationships, astra_routine_id) + self.type = 'bookPayment' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return PushToCardPaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes.get("direction"), attributes["description"], attributes["amount"], + attributes["astraRoutineId"], attributes.get("reason"), attributes.get("tags"), + relationships) + class BillPaymentDTO(BasePayment): def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: str, description: str, amount: int, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): @@ -149,7 +169,7 @@ def from_json_api(_id, _type, attributes, relationships): class CreatePaymentBaseRequest(UnitRequest): def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], idempotency_key: Optional[str], tags: Optional[Dict[str, str]], direction: str = "Credit", - type: str = "achPayment", same_day: Optional[bool] = False): + type: str = "achPayment", same_day: Optional[bool] = False, configuration: dict = None): self.type = type self.amount = amount self.same_day = same_day @@ -158,6 +178,7 @@ def __init__(self, amount: int, description: str, relationships: Dict[str, Relat self.idempotency_key = idempotency_key self.tags = tags self.relationships = relationships + self.configuration = configuration def to_json_api(self) -> Dict: payload = { @@ -181,6 +202,9 @@ def to_json_api(self) -> Dict: if self.tags: payload["data"]["attributes"]["tags"] = self.tags + if self.configuration: + payload["data"]["attributes"]["configuration"] = self.configuration + return payload def __repr__(self): @@ -326,8 +350,17 @@ def to_json_api(self) -> Dict: payload["data"]["attributes"]["counterparty"] = self.counterparty return payload + +class CreatePushToCardPaymentRequest(CreatePaymentBaseRequest): + def __init__(self, amount: int, description: str, configuration: dict, + relationships: Dict[str, Relationship], + idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, + direction: str = "Credit"): + super().__init__(amount, description, relationships, idempotency_key, tags, direction, "pushToCardPayment", False, configuration) + + CreatePaymentRequest = Union[CreateInlinePaymentRequest, CreateLinkedPaymentRequest, CreateVerifiedPaymentRequest, - CreateBookPaymentRequest, CreateWirePaymentRequest] + CreateBookPaymentRequest, CreateWirePaymentRequest, CreatePushToCardPaymentRequest] class PatchAchPaymentRequest(object): def __init__(self, payment_id: str, tags: Dict[str, str]): diff --git a/unit_python_sdk.egg-info/PKG-INFO b/unit_python_sdk.egg-info/PKG-INFO index c3be286a..f8e13e2e 100644 --- a/unit_python_sdk.egg-info/PKG-INFO +++ b/unit_python_sdk.egg-info/PKG-INFO @@ -16,3 +16,4 @@ Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 License-File: LICENSE +Requires-Dist: requests diff --git a/unit_python_sdk.egg-info/SOURCES.txt b/unit_python_sdk.egg-info/SOURCES.txt index da93de6f..9f0dc940 100644 --- a/unit_python_sdk.egg-info/SOURCES.txt +++ b/unit_python_sdk.egg-info/SOURCES.txt @@ -14,8 +14,10 @@ unit/api/atmLocation_resource.py unit/api/authorization_request_resource.py unit/api/authorization_resource.py unit/api/base_resource.py +unit/api/batch_release_resource.py unit/api/bill_pay_resource.py unit/api/card_resource.py +unit/api/check_deposit_resource.py unit/api/counterparty_resource.py unit/api/customerToken_resource.py unit/api/customer_resource.py @@ -39,9 +41,11 @@ unit/models/applicationForm.py unit/models/atm_location.py unit/models/authorization.py unit/models/authorization_request.py +unit/models/batch_release.py unit/models/benificial_owner.py unit/models/bill_pay.py unit/models/card.py +unit/models/check_deposit.py unit/models/codecs.py unit/models/counterparty.py unit/models/customer.py From 465e84f68111bf32fb4eaef81f2f3fe251f10aee Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Wed, 6 Mar 2024 16:36:41 -0800 Subject: [PATCH 107/137] revert inadvertant changes --- unit_python_sdk.egg-info/PKG-INFO | 1 - unit_python_sdk.egg-info/SOURCES.txt | 4 ---- 2 files changed, 5 deletions(-) diff --git a/unit_python_sdk.egg-info/PKG-INFO b/unit_python_sdk.egg-info/PKG-INFO index f8e13e2e..c3be286a 100644 --- a/unit_python_sdk.egg-info/PKG-INFO +++ b/unit_python_sdk.egg-info/PKG-INFO @@ -16,4 +16,3 @@ Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 License-File: LICENSE -Requires-Dist: requests diff --git a/unit_python_sdk.egg-info/SOURCES.txt b/unit_python_sdk.egg-info/SOURCES.txt index 9f0dc940..da93de6f 100644 --- a/unit_python_sdk.egg-info/SOURCES.txt +++ b/unit_python_sdk.egg-info/SOURCES.txt @@ -14,10 +14,8 @@ unit/api/atmLocation_resource.py unit/api/authorization_request_resource.py unit/api/authorization_resource.py unit/api/base_resource.py -unit/api/batch_release_resource.py unit/api/bill_pay_resource.py unit/api/card_resource.py -unit/api/check_deposit_resource.py unit/api/counterparty_resource.py unit/api/customerToken_resource.py unit/api/customer_resource.py @@ -41,11 +39,9 @@ unit/models/applicationForm.py unit/models/atm_location.py unit/models/authorization.py unit/models/authorization_request.py -unit/models/batch_release.py unit/models/benificial_owner.py unit/models/bill_pay.py unit/models/card.py -unit/models/check_deposit.py unit/models/codecs.py unit/models/counterparty.py unit/models/customer.py From 5ce31e0c68de7289aaba67f7521ef880efb1956c Mon Sep 17 00:00:00 2001 From: Julia P <142916316+julia-truss@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:47:15 -0400 Subject: [PATCH 108/137] Update title literal and individual application (#25) --- unit/models/__init__.py | 3 ++- unit/models/application.py | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index face4c16..f9edce5d 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -161,7 +161,8 @@ def __str__(self): Status = Literal["Approved", "Denied", "PendingReview"] -Title = Literal["CEO", "COO", "CFO", "President"] +Title = Literal["CEO", "COO", "CFO", "President", "BenefitsAdministrationOfficer", "CIO", "VP", "AVP", "Treasurer", + "Secretary", "Controller", "Manager", "Partner", "Member"] EntityType = Literal["Corporation", "LLC", "Partnership"] diff --git a/unit/models/application.py b/unit/models/application.py index bfb450b7..488b3054 100644 --- a/unit/models/application.py +++ b/unit/models/application.py @@ -262,6 +262,7 @@ def __init__( device_fingerprints: Optional[List[DeviceFingerprint]] = None, idempotency_key: str = None, tags: Optional[Dict[str, str]] = None, + business_vertical: Optional[BusinessVertical] = None, ): self.full_name = full_name self.date_of_birth = date_of_birth @@ -278,6 +279,7 @@ def __init__( self.device_fingerprints = device_fingerprints self.idempotency_key = idempotency_key self.tags = tags + self.business_vertical = business_vertical def to_json_api(self) -> Dict: payload = { @@ -327,6 +329,9 @@ def to_json_api(self) -> Dict: if self.tags: payload["data"]["attributes"]["tags"] = self.tags + if self.business_vertical: + payload["data"]["attributes"]["businessVertical"] = self.business_vertical + return payload def __repr__(self): From 58aa3a28bee70bdf9544064ad1966645cf7c7d59 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Wed, 20 Mar 2024 11:14:19 -0700 Subject: [PATCH 109/137] pushToCardPayment on dto --- unit/models/payment.py | 2 +- unit_python_sdk.egg-info/PKG-INFO | 1 + unit_python_sdk.egg-info/SOURCES.txt | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index 897a20ab..e9836c83 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -119,7 +119,7 @@ def __init__(self, id: str, created_at: datetime, status: PaymentStatus, directi amount: int, astra_routine_id: str, reason: Optional[str], tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BasePayment.__init__(self, id, created_at, status, direction, description, amount, reason, tags, relationships, astra_routine_id) - self.type = 'bookPayment' + self.type = 'pushToCardPayment' @staticmethod def from_json_api(_id, _type, attributes, relationships): diff --git a/unit_python_sdk.egg-info/PKG-INFO b/unit_python_sdk.egg-info/PKG-INFO index c3be286a..f8e13e2e 100644 --- a/unit_python_sdk.egg-info/PKG-INFO +++ b/unit_python_sdk.egg-info/PKG-INFO @@ -16,3 +16,4 @@ Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 License-File: LICENSE +Requires-Dist: requests diff --git a/unit_python_sdk.egg-info/SOURCES.txt b/unit_python_sdk.egg-info/SOURCES.txt index da93de6f..9f0dc940 100644 --- a/unit_python_sdk.egg-info/SOURCES.txt +++ b/unit_python_sdk.egg-info/SOURCES.txt @@ -14,8 +14,10 @@ unit/api/atmLocation_resource.py unit/api/authorization_request_resource.py unit/api/authorization_resource.py unit/api/base_resource.py +unit/api/batch_release_resource.py unit/api/bill_pay_resource.py unit/api/card_resource.py +unit/api/check_deposit_resource.py unit/api/counterparty_resource.py unit/api/customerToken_resource.py unit/api/customer_resource.py @@ -39,9 +41,11 @@ unit/models/applicationForm.py unit/models/atm_location.py unit/models/authorization.py unit/models/authorization_request.py +unit/models/batch_release.py unit/models/benificial_owner.py unit/models/bill_pay.py unit/models/card.py +unit/models/check_deposit.py unit/models/codecs.py unit/models/counterparty.py unit/models/customer.py From 4e9899b6771ce0b9bf2e1933fdc26ccecd416eeb Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Wed, 20 Mar 2024 14:20:27 -0700 Subject: [PATCH 110/137] Fix payment dto --- unit/models/payment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index e9836c83..18a7bbf2 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -9,7 +9,7 @@ class BasePayment(object): def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: PaymentDirections, description: str, amount: int, reason: Optional[str], tags: Optional[Dict[str, str]], - relationships: Optional[Dict[str, Relationship]], astra_routine_id: Optional[str]): + relationships: Optional[Dict[str, Relationship]], astra_routine_id: Optional[str] = None): self.id = id self.attributes = {"createdAt": created_at, "status": status, "direction": direction, "description": description, "amount": amount, "reason": reason, "tags": tags} From eaef877a10f6c7089b8a4c63489cf3d00c9ef280 Mon Sep 17 00:00:00 2001 From: Julia P <142916316+julia-truss@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:26:15 -0400 Subject: [PATCH 111/137] Update push to card request #26 --- unit/models/payment.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index 18a7bbf2..a1afd0d6 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -168,7 +168,7 @@ def from_json_api(_id, _type, attributes, relationships): class CreatePaymentBaseRequest(UnitRequest): def __init__(self, amount: int, description: str, relationships: Dict[str, Relationship], - idempotency_key: Optional[str], tags: Optional[Dict[str, str]], direction: str = "Credit", + idempotency_key: Optional[str], tags: Optional[Dict[str, str]], direction: Optional[str], type: str = "achPayment", same_day: Optional[bool] = False, configuration: dict = None): self.type = type self.amount = amount @@ -186,13 +186,15 @@ def to_json_api(self) -> Dict: "type": self.type, "attributes": { "amount": self.amount, - "direction": self.direction, "description": self.description }, "relationships": self.relationships } } + if self.direction: + payload["data"]["attributes"]["direction"] = self.direction + if self.idempotency_key: payload["data"]["attributes"]["idempotencyKey"] = self.idempotency_key @@ -354,9 +356,8 @@ def to_json_api(self) -> Dict: class CreatePushToCardPaymentRequest(CreatePaymentBaseRequest): def __init__(self, amount: int, description: str, configuration: dict, relationships: Dict[str, Relationship], - idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None, - direction: str = "Credit"): - super().__init__(amount, description, relationships, idempotency_key, tags, direction, "pushToCardPayment", False, configuration) + idempotency_key: Optional[str] = None, tags: Optional[Dict[str, str]] = None): + super().__init__(amount, description, relationships, idempotency_key, tags, None, "pushToCardPayment", False, configuration) CreatePaymentRequest = Union[CreateInlinePaymentRequest, CreateLinkedPaymentRequest, CreateVerifiedPaymentRequest, From 2a8470e4b35d660e76856c524e63cdd5393549d8 Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Tue, 26 Mar 2024 13:13:50 -0700 Subject: [PATCH 112/137] Add check payment resources --- unit/api/check_payment_resource.py | 19 ++++ unit/models/check_payment.py | 141 +++++++++++++++++++++++++++++ unit/models/event.py | 13 +++ 3 files changed, 173 insertions(+) create mode 100644 unit/api/check_payment_resource.py create mode 100644 unit/models/check_payment.py diff --git a/unit/api/check_payment_resource.py b/unit/api/check_payment_resource.py new file mode 100644 index 00000000..78945a84 --- /dev/null +++ b/unit/api/check_payment_resource.py @@ -0,0 +1,19 @@ +from unit.api.base_resource import BaseResource +from unit.models.authorization_request import * +from unit.models.check_payment import ApproveCheckPaymentRequest, CheckPaymentDTO +from unit.models.codecs import DtoDecoder + + +class AuthorizationRequestResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "check-payments" + + def approve(self, request: ApproveCheckPaymentRequest) -> Union[UnitResponse[CheckPaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"{self.resource}/{request.check_payment_id}/approve", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[CheckPaymentDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/unit/models/check_payment.py b/unit/models/check_payment.py new file mode 100644 index 00000000..e4d85bbc --- /dev/null +++ b/unit/models/check_payment.py @@ -0,0 +1,141 @@ +import json +from typing import Optional, Literal +from unit.models import * +from unit.utils import date_utils + + +CheckPaymentStatus = Literal["New", "Pending", "PendingCancellation", "Canceled", "InDelivery", "Delivered", + "ReturnedToSender", "Processed", "PendingReview", "MarkedForReturn", "Returned", "Rejected"] +CheckPaymentAdditionalVerificationStatus = Literal[ + "NotSufficientFunds", + "UncollectedFundsHold", + "StopPayment", + "ClosedAccount", + "UnableToLocateAccount", + "FrozenOrBlockedAccount", + "StaleDated", + "PostDated", + "NotValidCheckOrCashItem", + "AlteredOrFictitious", + "UnableToProcess", + "ItemExceedsDollarLimit", + "NotAuthorized", + "ReferToMaker", + "UnusableImage", + "DuplicatePresentment", + "WarrantyBreach", + "UnauthorizedWarrantyBreach" +] +CheckPaymentDeliveryStatus = Literal["Required", "NotRequired", "Approved"] + + +class CheckPaymentDTO(object): + def __init__(self, id: str, created_at: datetime, updated_at: datetime, amount: int, status: CheckPaymentStatus, + description: str, check_number: str, originated: bool, on_us: Optional[str], + on_us_auxiliary: Optional[str], counterparty_routing_number: Optional[str], + return_status_reason: Optional[str], reject_reason: Optional[str], + pending_review_reasons: Optional[List[str]], return_cutoff_time: Optional[datetime], + additional_verification_status: Optional[CheckPaymentAdditionalVerificationStatus], + tags: Optional[Dict[str, str]], delivery_status: Optional[CheckPaymentDeliveryStatus], + tracked_at: Optional[datetime], postal_code: Optional[str], expiration_date: Optional[date], + expected_delivery: Optional[date], send_at: Optional[datetime], + counterparty_name: Optional[str], counterparty_moved: Optional[bool], + counterparty_street: Optional[str], + counterparty_street2: Optional[str], counterparty_city: Optional[str], + counterparty_state: Optional[str], + counterparty_postal_code: Optional[str], counterparty_country: Optional[str], memo: Optional[str], + relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = "checkPayment" + self.attributes = self.attributes = { + "createdAt": created_at, + "updatedAt": updated_at, + "amount": amount, + "status": status, + "description": description, + "checkNumber": check_number, + "originated": originated, + "onUs": on_us, + "onUsAuxiliary": on_us_auxiliary, + "counterpartyRoutingNumber": counterparty_routing_number, + "returnStatusReason": return_status_reason, + "rejectReason": reject_reason, + "pendingReviewReasons": pending_review_reasons, + "returnCutoffTime": return_cutoff_time, + "additionalVerificationStatus": additional_verification_status, + "tags": tags, + "deliveryStatus": delivery_status, + "trackedAt": tracked_at, + "postalCode": postal_code, + "expirationDate": expiration_date, + "expectedDelivery": expected_delivery, + "sendAt": send_at, + "counterparty": { + "name": counterparty_name, + "moved": counterparty_moved, + "address": { + "street": counterparty_street, + "street2": counterparty_street2, + "city": counterparty_city, + "state": counterparty_state, + "postalCode": counterparty_postal_code, + "country": counterparty_country, + } + }, + "memo": memo, + } + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentDTO( + id=_id, + created_at=date_utils.to_datetime(attributes["createdAt"]), + updated_at=date_utils.to_datetime(attributes["updatedAt"]), + amount=attributes["amount"], + status=attributes["status"], + description=attributes["description"], + check_number=attributes["checkNumber"], + originated=attributes["originated"], + on_us=attributes.get("onUs"), + on_us_auxiliary=attributes.get("onUsAuxiliary"), + counterparty_routing_number=attributes.get("counterpartyRoutingNumber"), + return_status_reason=attributes.get("returnStatusReason"), + reject_reason=attributes.get("rejectReason"), + pending_review_reasons=attributes.get("pendingReviewReasons"), + return_cutoff_time=attributes.get("returnCutoffTime"), + additional_verification_status=attributes.get("additionalVerificationStatus"), + tags=attributes.get("tags"), + delivery_status=attributes.get("deliveryStatus"), + tracked_at=attributes.get("trackedAt"), + postal_code=attributes.get("postalCode"), + expiration_date=attributes.get("expirationDate"), + expected_delivery=attributes.get("expectedDelivery"), + send_at=attributes.get("sendAt"), + counterparty_name=attributes.get("counterparty", {}).get("name"), + counterparty_moved=attributes.get("counterparty", {}).get("moved"), + counterparty_street=attributes.get("counterparty", {}).get("address", {}).get("street"), + counterparty_street2=attributes.get("counterparty", {}).get("address", {}).get("street2"), + counterparty_city=attributes.get("counterparty", {}).get("address", {}).get("city"), + counterparty_state=attributes.get("counterparty", {}).get("address", {}).get("state"), + counterparty_postal_code=attributes.get("counterparty", {}).get("address", {}).get("postalCode"), + counterparty_country=attributes.get("counterparty", {}).get("address", {}).get("country"), + memo=attributes.get("memo"), + relationships=relationships, + ) + + +class ApproveCheckPaymentRequest(UnitRequest): + def __init__(self, check_payment_id: str): + self.check_payment_id = check_payment_id + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "additionalVerification", + } + } + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) diff --git a/unit/models/event.py b/unit/models/event.py index 97893ff8..158d3218 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -323,6 +323,19 @@ def from_json_api(_id, _type, attributes, relationships): attributes["previousStatus"], attributes.get("tags"), relationships) +class CheckPaymentAdditionalVerificationRequiredEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, amount: int, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.additionalVerificationRequired' + self.attributes["status"] = status + self.attributes["amount"] = amount + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentAdditionalVerificationRequiredEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["status"], attributes["amount"], attributes.get("tags"), relationships) + class CustomerCreatedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], From 2dbfd38102823d6a8282b01033dfe05ad9700a32 Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Tue, 26 Mar 2024 13:24:49 -0700 Subject: [PATCH 113/137] Rename --- unit/api/check_payment_resource.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/api/check_payment_resource.py b/unit/api/check_payment_resource.py index 78945a84..1f7905a7 100644 --- a/unit/api/check_payment_resource.py +++ b/unit/api/check_payment_resource.py @@ -4,7 +4,7 @@ from unit.models.codecs import DtoDecoder -class AuthorizationRequestResource(BaseResource): +class CHeckPaymentResource(BaseResource): def __init__(self, api_url, token): super().__init__(api_url, token) self.resource = "check-payments" From 5e752e12be84690c79378e3155a338734d59ee83 Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Tue, 26 Mar 2024 13:25:19 -0700 Subject: [PATCH 114/137] Rename2 --- unit/api/check_payment_resource.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/api/check_payment_resource.py b/unit/api/check_payment_resource.py index 1f7905a7..e5841326 100644 --- a/unit/api/check_payment_resource.py +++ b/unit/api/check_payment_resource.py @@ -4,7 +4,7 @@ from unit.models.codecs import DtoDecoder -class CHeckPaymentResource(BaseResource): +class CheckPaymentResource(BaseResource): def __init__(self, api_url, token): super().__init__(api_url, token) self.resource = "check-payments" From b12c9805d1392875393033114d297127602aa802 Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Tue, 26 Mar 2024 13:29:58 -0700 Subject: [PATCH 115/137] Add check payments to client --- unit/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unit/__init__.py b/unit/__init__.py index 0144d264..27fa8aeb 100644 --- a/unit/__init__.py +++ b/unit/__init__.py @@ -1,6 +1,7 @@ from unit.api.application_resource import ApplicationResource from unit.api.check_deposit_resource import CheckDepositResource from unit.api.batch_release_resource import BatchReleaseResource +from unit.api.check_payment_resource import CheckPaymentResource from unit.api.customer_resource import CustomerResource from unit.api.account_resource import AccountResource from unit.api.card_resource import CardResource @@ -56,3 +57,4 @@ def __init__(self, api_url, token): self.account_end_of_day = AccountEndOfDayResource(api_url, token) self.rewards = RewardResource(api_url, token) self.batchRelease = BatchReleaseResource(api_url, token) + self.check_payments = CheckPaymentResource(api_url, token) From 915563f6bc576bf6528dff00baeeb43a32c54494 Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Tue, 26 Mar 2024 15:58:03 -0700 Subject: [PATCH 116/137] Fix enums and add get endpoint for check payments --- unit/api/check_payment_resource.py | 8 ++++++++ unit/models/check_payment.py | 7 ++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/unit/api/check_payment_resource.py b/unit/api/check_payment_resource.py index e5841326..a838d750 100644 --- a/unit/api/check_payment_resource.py +++ b/unit/api/check_payment_resource.py @@ -9,6 +9,14 @@ def __init__(self, api_url, token): super().__init__(api_url, token) self.resource = "check-payments" + def get(self, check_payment_id: str) -> Union[UnitResponse[CheckPaymentDTO], UnitError]: + response = super().get(f"{self.resource}/{check_payment_id}") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[check_payment_id](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) + def approve(self, request: ApproveCheckPaymentRequest) -> Union[UnitResponse[CheckPaymentDTO], UnitError]: payload = request.to_json_api() response = super().post(f"{self.resource}/{request.check_payment_id}/approve", payload) diff --git a/unit/models/check_payment.py b/unit/models/check_payment.py index e4d85bbc..77c9ceea 100644 --- a/unit/models/check_payment.py +++ b/unit/models/check_payment.py @@ -6,7 +6,8 @@ CheckPaymentStatus = Literal["New", "Pending", "PendingCancellation", "Canceled", "InDelivery", "Delivered", "ReturnedToSender", "Processed", "PendingReview", "MarkedForReturn", "Returned", "Rejected"] -CheckPaymentAdditionalVerificationStatus = Literal[ +CheckPaymentAdditionalVerificationStatus = Literal["Required", "NotRequired", "Approved"] +CheckPaymentReturnStatusReason = Literal[ "NotSufficientFunds", "UncollectedFundsHold", "StopPayment", @@ -26,14 +27,14 @@ "WarrantyBreach", "UnauthorizedWarrantyBreach" ] -CheckPaymentDeliveryStatus = Literal["Required", "NotRequired", "Approved"] +CheckPaymentDeliveryStatus = Literal["Mailed", "InLocalArea", "Delivered", "Rerouted", "ReturnedToSender"] class CheckPaymentDTO(object): def __init__(self, id: str, created_at: datetime, updated_at: datetime, amount: int, status: CheckPaymentStatus, description: str, check_number: str, originated: bool, on_us: Optional[str], on_us_auxiliary: Optional[str], counterparty_routing_number: Optional[str], - return_status_reason: Optional[str], reject_reason: Optional[str], + return_status_reason: Optional[CheckPaymentReturnStatusReason], reject_reason: Optional[str], pending_review_reasons: Optional[List[str]], return_cutoff_time: Optional[datetime], additional_verification_status: Optional[CheckPaymentAdditionalVerificationStatus], tags: Optional[Dict[str, str]], delivery_status: Optional[CheckPaymentDeliveryStatus], From b716cc200743be46bef531d046cc5a2760faf16b Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Sun, 7 Apr 2024 17:18:58 -0700 Subject: [PATCH 117/137] Add check payment transactions --- unit/models/transaction.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/unit/models/transaction.py b/unit/models/transaction.py index 0b1b17c4..1d824ea3 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -383,6 +383,35 @@ def from_json_api(_id, _type, attributes, relationships): attributes["amount"], attributes["balance"], attributes["summary"], attributes["reason"], attributes.get("tags"), relationships) + +class CheckPaymentTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'checkPaymentTransaction' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], + attributes.get("tags"), relationships) + + +class ReturnedCheckPaymentTransactionDTO(BaseTransactionDTO): + def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, + return_reason: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseTransactionDTO.__init__(self, id, created_at, direction, amount, balance, summary, tags, relationships) + self.type = 'returnedCheckPaymentTransaction' + self.attributes["returnReason"] = return_reason + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return ReturnedCheckPaymentTransactionDTO(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["direction"], + attributes["amount"], attributes["balance"], attributes["summary"], + attributes["returnReason"], attributes.get("tags"), relationships) + + class PaymentAdvanceTransactionDTO(BaseTransactionDTO): def __init__(self, id: str, created_at: datetime, direction: str, amount: int, balance: int, summary: str, reason: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): From ac8bc05426864c290b3e6220a480d23f9f6260ec Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Sun, 7 Apr 2024 17:28:14 -0700 Subject: [PATCH 118/137] Add check payments to other references --- unit/models/codecs.py | 13 +++++++++++++ unit/models/transaction.py | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 8c187234..a9705db4 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -3,6 +3,7 @@ from datetime import datetime, date from unit.models.batch_release import BatchReleaseDTO +from unit.models.check_payment import CheckPaymentDTO from unit.models.reward import RewardDTO from unit.utils import date_utils from unit.models.applicationForm import ApplicationFormDTO @@ -114,6 +115,12 @@ "returnedCheckDepositTransaction": lambda _id, _type, attributes, relationships: ReturnedCheckDepositTransactionDTO.from_json_api(_id, _type, attributes, relationships), + "checkPaymentTransaction": lambda _id, _type, attributes, relationships: + CheckPaymentTransactionDTO.from_json_api(_id, _type, attributes, relationships), + + "returnedCheckPaymentTransaction": lambda _id, _type, attributes, relationships: + ReturnedCheckPaymentTransactionDTO.from_json_api(_id, _type, attributes, relationships), + #"paymentAdvanceTransaction": lambda _id, _type, attributes, relationships: #PaymentAdvanceTransactionTransactionDTO.from_json_api(_id, _type, attributes, relationships), @@ -135,6 +142,9 @@ "achReceivedPayment": lambda _id, _type, attributes, relationships: AchReceivedPaymentDTO.from_json_api(_id, _type, attributes, relationships), + "checkPayment": lambda _id, _type, attributes, relationships: + CheckPaymentDTO.from_json_api(_id, _type, attributes, relationships), + "accountStatementDTO": lambda _id, _type, attributes, relationships: StatementDTO.from_json_api(_id, _type, attributes, relationships), @@ -222,6 +232,9 @@ "checkDeposit.returned": lambda _id, _type, attributes, relationships: CheckDepositReturnedEvent.from_json_api(_id, _type, attributes, relationships), + "checkPayment.additionalVerificationRequired": lambda _id, _type, attributes, relationships: + CheckPaymentAdditionalVerificationRequiredEvent.from_json_api(_id, _type, attributes, relationships), + "payment.clearing": lambda _id, _type, attributes, relationships: PaymentClearingEvent.from_json_api(_id, _type, attributes, relationships), diff --git a/unit/models/transaction.py b/unit/models/transaction.py index 1d824ea3..fab99333 100644 --- a/unit/models/transaction.py +++ b/unit/models/transaction.py @@ -443,7 +443,8 @@ def from_json_api(_id, _type, attributes, relationships): PurchaseTransactionDTO, AtmTransactionDTO, FeeTransactionDTO, CardTransactionDTO, CardReversalTransactionDTO, WireTransactionDTO, ReleaseTransactionDTO, AdjustmentTransactionDTO, InterestTransactionDTO, DisputeTransactionDTO, CheckDepositTransactionDTO, - ReturnedCheckDepositTransactionDTO, PaymentAdvanceTransactionDTO, + ReturnedCheckDepositTransactionDTO, CheckPaymentTransactionDTO, + ReturnedCheckPaymentTransactionDTO, PaymentAdvanceTransactionDTO, RepaidPaymentAdvanceTransactionDTO] From 8b9a8c0854166ad2723c5669ecb66ddb7909526b Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Wed, 10 Apr 2024 15:52:35 -0700 Subject: [PATCH 119/137] Add create check payment --- unit/api/check_payment_resource.py | 10 ++++++++++ unit/models/__init__.py | 17 ++++++++++++++++ unit/models/payment.py | 31 +++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/unit/api/check_payment_resource.py b/unit/api/check_payment_resource.py index a838d750..b7e353da 100644 --- a/unit/api/check_payment_resource.py +++ b/unit/api/check_payment_resource.py @@ -2,6 +2,7 @@ from unit.models.authorization_request import * from unit.models.check_payment import ApproveCheckPaymentRequest, CheckPaymentDTO from unit.models.codecs import DtoDecoder +from unit.models.payment import CreateCheckPaymentRequest class CheckPaymentResource(BaseResource): @@ -25,3 +26,12 @@ def approve(self, request: ApproveCheckPaymentRequest) -> Union[UnitResponse[Che return UnitResponse[CheckPaymentDTO](DtoDecoder.decode(data), None) else: return UnitError.from_json_api(response.json()) + + def create(self, request: CreateCheckPaymentRequest) -> Union[UnitResponse[CheckPaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"{self.resource}", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[CheckPaymentDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index f9edce5d..d2bd085b 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -394,6 +394,23 @@ def from_json_api(data: Dict): ) +class CheckPaymentCounterparty(UnitDTO): + def __init__( + self, name: str, counterparty_moved: bool, address: Address + ): + self.name = name + self.counterparty_moved = counterparty_moved + self.address = address + + @staticmethod + def from_json_api(data: Dict): + return CheckPaymentCounterparty( + data["name"], + data["counterpartyMoved"], + data["address"], + ) + + class Coordinates(UnitDTO): def __init__(self, longitude: int, latitude: int): self.longitude = longitude diff --git a/unit/models/payment.py b/unit/models/payment.py index a1afd0d6..b21cc4cd 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -360,8 +360,37 @@ def __init__(self, amount: int, description: str, configuration: dict, super().__init__(amount, description, relationships, idempotency_key, tags, None, "pushToCardPayment", False, configuration) +class CreateCheckPaymentRequest(UnitRequest): + def __init__( + self, + description: str, + amount: int, + send_date: str, + counterparty: CheckPaymentCounterparty, + idempotency_key: str, + relationships: Dict[str, Relationship], + tags: Optional[Dict[str, str]] = None, + ): + self.description = description + self.amount = amount + self.send_date = send_date + self.counterparty = counterparty + self.description = description + self.idempotency_key = idempotency_key + self.tags = tags + self.relationships = relationships + + def to_json_api(self) -> Dict: + payload = super().to_payload("checkPayment", self.relationships) + payload["data"]["attributes"]["counterparty"]["name"] = self.counterparty.name + payload["data"]["attributes"]["counterparty"]["counterpartyMoved"] = self.counterparty.counterparty_moved + payload["data"]["attributes"]["counterparty"]["address"] = self.counterparty.address + return payload + + CreatePaymentRequest = Union[CreateInlinePaymentRequest, CreateLinkedPaymentRequest, CreateVerifiedPaymentRequest, - CreateBookPaymentRequest, CreateWirePaymentRequest, CreatePushToCardPaymentRequest] + CreateBookPaymentRequest, CreateWirePaymentRequest, CreatePushToCardPaymentRequest, + CreateCheckPaymentRequest] class PatchAchPaymentRequest(object): def __init__(self, payment_id: str, tags: Dict[str, str]): From 276b244151d82322b9576b73fa346669950cf10b Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Wed, 10 Apr 2024 16:03:47 -0700 Subject: [PATCH 120/137] Include memo --- unit/models/payment.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index b21cc4cd..9722ed34 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -365,17 +365,18 @@ def __init__( self, description: str, amount: int, - send_date: str, counterparty: CheckPaymentCounterparty, idempotency_key: str, relationships: Dict[str, Relationship], + memo: Optional[str] = None, + send_date: Optional[str] = None, tags: Optional[Dict[str, str]] = None, ): self.description = description self.amount = amount self.send_date = send_date self.counterparty = counterparty - self.description = description + self.memo = memo self.idempotency_key = idempotency_key self.tags = tags self.relationships = relationships From 0da1b2c3344399ccee66e5cf445dcd27219f91b2 Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Thu, 11 Apr 2024 16:47:12 -0700 Subject: [PATCH 121/137] Remove item assignment --- unit/models/payment.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index 9722ed34..882ee828 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -382,11 +382,7 @@ def __init__( self.relationships = relationships def to_json_api(self) -> Dict: - payload = super().to_payload("checkPayment", self.relationships) - payload["data"]["attributes"]["counterparty"]["name"] = self.counterparty.name - payload["data"]["attributes"]["counterparty"]["counterpartyMoved"] = self.counterparty.counterparty_moved - payload["data"]["attributes"]["counterparty"]["address"] = self.counterparty.address - return payload + return super().to_payload("checkPayment", self.relationships) CreatePaymentRequest = Union[CreateInlinePaymentRequest, CreateLinkedPaymentRequest, CreateVerifiedPaymentRequest, From 815e018e4143c6cf542c63c78f0c681a7e50f7a6 Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Fri, 12 Apr 2024 11:59:53 -0700 Subject: [PATCH 122/137] Add check payment counterparty to json encoder --- unit/models/codecs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index a9705db4..ea472528 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -429,4 +429,10 @@ def default(self, obj): "accountType": obj.account_type, "name": obj.name} if isinstance(obj, Coordinates): return {"longitude": obj.longitude, "latitude": obj.latitude} + if isinstance(obj, CheckPaymentCounterparty): + return { + "name": obj.name, + "counterpartyMoved": obj.counterparty_moved, + "address": obj.address + } return json.JSONEncoder.default(self, obj) From 04fda054ec2723f6b2b50da8a6091d3b0d2ec0a6 Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Fri, 12 Apr 2024 15:40:18 -0700 Subject: [PATCH 123/137] Remove counterpartyMoved param --- unit/models/__init__.py | 4 +--- unit/models/codecs.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index d2bd085b..b8182af3 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -396,17 +396,15 @@ def from_json_api(data: Dict): class CheckPaymentCounterparty(UnitDTO): def __init__( - self, name: str, counterparty_moved: bool, address: Address + self, name: str, address: Address ): self.name = name - self.counterparty_moved = counterparty_moved self.address = address @staticmethod def from_json_api(data: Dict): return CheckPaymentCounterparty( data["name"], - data["counterpartyMoved"], data["address"], ) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index ea472528..560ddf4a 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -432,7 +432,6 @@ def default(self, obj): if isinstance(obj, CheckPaymentCounterparty): return { "name": obj.name, - "counterpartyMoved": obj.counterparty_moved, "address": obj.address } return json.JSONEncoder.default(self, obj) From 4cad6938b0e2be5b4c94d462e6c889c1fde8484d Mon Sep 17 00:00:00 2001 From: Julia P <142916316+julia-truss@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:45:36 -0400 Subject: [PATCH 124/137] Add events (#27) --- unit/models/event.py | 201 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/unit/models/event.py b/unit/models/event.py index 158d3218..a998069f 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -323,6 +323,188 @@ def from_json_api(_id, _type, attributes, relationships): attributes["previousStatus"], attributes.get("tags"), relationships) +class CheckPaymentCreatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, additional_verification_status: str, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.created' + self.attributes["status"] = status + self.attributes["additionalVerificationStatus"] = additional_verification_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["status"], attributes["additionalVerificationStatus"], + attributes.get("tags"), relationships) + + +class CheckPaymentMarkedForReturnEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.markedForReturn' + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentMarkedForReturnEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["previousStatus"], attributes.get("tags"), relationships) + + +class CheckPaymentProcessedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, additional_verification_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.processed' + self.attributes["previousStatus"] = previous_status + self.attributes["additionalVerificationStatus"] = additional_verification_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentProcessedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["previousStatus"], attributes["additionalVerificationStatus"], + attributes.get("tags"), relationships) + + +class CheckPaymentReturnedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.returned' + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentReturnedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["previousStatus"], attributes.get("tags"), relationships) + + +class CheckPaymentPendingEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, previous_status: str, + counterparty_moved: Optional[bool], tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.pending' + self.attributes["status"] = status + self.attributes["previousStatus"] = previous_status + self.attributes["counterpartyMoved"] = counterparty_moved + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentPendingEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["status"], attributes["previousStatus"], + attributes.get("counterpartyMoved"), attributes.get("tags"), relationships) + + +class CheckPaymentRejectedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, previous_status: str, + reject_reason: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.rejected' + self.attributes["status"] = status + self.attributes["previousStatus"] = previous_status + self.attributes["rejectReason"] = reject_reason + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentRejectedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["status"], attributes["previousStatus"], + attributes["rejectReason"], attributes.get("tags"), relationships) + + +class CheckPaymentInProductionEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.inProduction' + self.attributes["status"] = status + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentInProductionEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes["previousStatus"], attributes.get("tags"), relationships) + + +class CheckPaymentInDeliveryEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, previous_status: str, delivery_status: str, + tracked_at: datetime, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.inDelivery' + self.attributes["status"] = status + self.attributes["previousStatus"] = previous_status + self.attributes["deliveryStatus"] = delivery_status + self.attributes["trackedAt"] = tracked_at + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentInDeliveryEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes["previousStatus"], attributes["deliveryStatus"], + attributes["trackedAt"], attributes.get("tags"), relationships) + + +class CheckPaymentDeliveredEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.delivered' + self.attributes["status"] = status + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentDeliveredEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes["previousStatus"], attributes.get("tags"), relationships) + + +class CheckPaymentReturnToSenderEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.returnToSender' + self.attributes["status"] = status + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentDeliveredEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes["previousStatus"], attributes.get("tags"), relationships) + + +class CheckPaymentCanceledEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_status: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.canceled' + self.attributes["previousStatus"] = previous_status + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentCanceledEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["previousStatus"], attributes.get("tags"), relationships) + + +class CheckPaymentDeliveryStatusChangedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, previous_delivery_status: str, new_delivery_status: str, + tracked_at: datetime, postal_code: str, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.deliveryStatusChanged' + self.attributes["previousDeliveryStatus"] = previous_delivery_status + self.attributes["NewDeliveryStatus"] = new_delivery_status + self.attributes["trackedAt"] = tracked_at + self.attributes["postalCode"] = postal_code + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentDeliveryStatusChangedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["previousDeliveryStatus"], + attributes["NewDeliveryStatus"], attributes["trackedAt"], + attributes["postalCode"], attributes.get("tags"), relationships) + + class CheckPaymentAdditionalVerificationRequiredEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, status: str, amount: int, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): @@ -337,6 +519,20 @@ def from_json_api(_id, _type, attributes, relationships): attributes["status"], attributes["amount"], attributes.get("tags"), relationships) +class CheckPaymentAdditionalVerificationApprovedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, amount: int, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'checkPayment.additionalVerificationApproved' + self.attributes["status"] = status + self.attributes["amount"] = amount + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckPaymentAdditionalVerificationApprovedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), + attributes["status"], attributes["amount"], attributes.get("tags"), relationships) + + class CustomerCreatedEvent(BaseEvent): def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): @@ -504,6 +700,11 @@ def from_json_api(_id, _type, attributes, relationships): AuthorizationRequestApprovedEvent, DocumentApprovedEvent, DocumentRejectedEvent, CheckDepositCreatedEvent, CheckDepositPendingReviewEvent, CheckDepositPendingEvent, CheckDepositClearingEvent, CheckDepositSentEvent, CheckDepositRejectedEvent, CheckDepositReturnedEvent, + CheckPaymentCreatedEvent, CheckPaymentMarkedForReturnEvent, CheckPaymentProcessedEvent, CheckPaymentReturnedEvent, + CheckPaymentPendingEvent, CheckPaymentRejectedEvent, CheckPaymentInProductionEvent, CheckPaymentInDeliveryEvent, + CheckPaymentDeliveredEvent, CheckPaymentReturnToSenderEvent, CheckPaymentCanceledEvent, + CheckPaymentDeliveryStatusChangedEvent, CheckPaymentAdditionalVerificationRequiredEvent, + CheckPaymentAdditionalVerificationApprovedEvent, CustomerCreatedEvent, PaymentClearingEvent, PaymentSentEvent, PaymentReturnedEvent, StatementsCreatedEvent, TransactionCreatedEvent, AccountReopenedEvent, RawUnitObject, ] From 190adabf4723cfaa7471560b7e9f8a1b8a772bf7 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Tue, 23 Apr 2024 11:14:17 -0700 Subject: [PATCH 125/137] Check payment codecs --- unit/models/codecs.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 560ddf4a..9a2c39ef 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -145,6 +145,48 @@ "checkPayment": lambda _id, _type, attributes, relationships: CheckPaymentDTO.from_json_api(_id, _type, attributes, relationships), + "checkPayment.created": lambda _id, _type, attributes, relationships: + CheckPaymentCreatedEvent.from_json_api(_id, _type, attributes, relationships), + + "checkPayment.markedForReturn": lambda _id, _type, attributes, relationships: + CheckPaymentMarkedForReturnEvent.from_json_api(_id, _type, attributes, relationships), + + "checkPayment.processed": lambda _id, _type, attributes, relationships: + CheckPaymentProcessedEvent.from_json_api(_id, _type, attributes, relationships), + + "checkPayment.returned": lambda _id, _type, attributes, relationships: + CheckPaymentReturnedEvent.from_json_api(_id, _type, attributes, relationships), + + "checkPayment.pending": lambda _id, _type, attributes, relationships: + CheckPaymentPendingEvent.from_json_api(_id, _type, attributes, relationships), + + "checkPayment.rejected": lambda _id, _type, attributes, relationships: + CheckPaymentRejectedEvent.from_json_api(_id, _type, attributes, relationships), + + "checkPayment.inProduction": lambda _id, _type, attributes, relationships: + CheckPaymentInProductionEvent.from_json_api(_id, _type, attributes, relationships), + + "checkPayment.inDelivery": lambda _id, _type, attributes, relationships: + CheckPaymentInDeliveryEvent.from_json_api(_id, _type, attributes, relationships), + + "checkPayment.delivered": lambda _id, _type, attributes, relationships: + CheckPaymentDeliveredEvent.from_json_api(_id, _type, attributes, relationships), + + "checkPayment.returnToSender": lambda _id, _type, attributes, relationships: + CheckPaymentReturnToSenderEvent.from_json_api(_id, _type, attributes, relationships), + + "checkPayment.canceled": lambda _id, _type, attributes, relationships: + CheckPaymentCanceledEvent.from_json_api(_id, _type, attributes, relationships), + + "checkPayment.deliveryStatusChanged": lambda _id, _type, attributes, relationships: + CheckPaymentDeliveryStatusChangedEvent.from_json_api(_id, _type, attributes, relationships), + + "checkPayment.additionalVerificationRequired": lambda _id, _type, attributes, relationships: + CheckPaymentAdditionalVerificationRequiredEvent.from_json_api(_id, _type, attributes, relationships), + + "checkPayment.additionalVerificationApproved": lambda _id, _type, attributes, relationships: + CheckPaymentAdditionalVerificationApprovedEvent.from_json_api(_id, _type, attributes, relationships), + "accountStatementDTO": lambda _id, _type, attributes, relationships: StatementDTO.from_json_api(_id, _type, attributes, relationships), From 153149368bcf0afa4a88658fd5e0cb7a9e4a2214 Mon Sep 17 00:00:00 2001 From: Julia P <142916316+julia-truss@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:32:56 -0400 Subject: [PATCH 126/137] Cancel check payment and check stop payments (#28) * Add cancel for check payment resource * Add stop payments * Add to init * Add related events * Update codecs --- unit/__init__.py | 2 ++ unit/api/check_payment_resource.py | 8 +++++ unit/api/check_stop_payment_resource.py | 20 ++++++++++++ unit/models/check_stop_payment.py | 33 +++++++++++++++++++ unit/models/codecs.py | 9 ++++++ unit/models/event.py | 42 +++++++++++++++++++++++++ unit/models/payment.py | 20 +++++++++++- 7 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 unit/api/check_stop_payment_resource.py create mode 100644 unit/models/check_stop_payment.py diff --git a/unit/__init__.py b/unit/__init__.py index 27fa8aeb..e56f7d54 100644 --- a/unit/__init__.py +++ b/unit/__init__.py @@ -2,6 +2,7 @@ from unit.api.check_deposit_resource import CheckDepositResource from unit.api.batch_release_resource import BatchReleaseResource from unit.api.check_payment_resource import CheckPaymentResource +from unit.api.check_stop_payment_resource import CheckStopPaymentResource from unit.api.customer_resource import CustomerResource from unit.api.account_resource import AccountResource from unit.api.card_resource import CardResource @@ -58,3 +59,4 @@ def __init__(self, api_url, token): self.rewards = RewardResource(api_url, token) self.batchRelease = BatchReleaseResource(api_url, token) self.check_payments = CheckPaymentResource(api_url, token) + self.check_stop_payments = CheckStopPaymentResource(api_url, token) diff --git a/unit/api/check_payment_resource.py b/unit/api/check_payment_resource.py index b7e353da..96e28c77 100644 --- a/unit/api/check_payment_resource.py +++ b/unit/api/check_payment_resource.py @@ -35,3 +35,11 @@ def create(self, request: CreateCheckPaymentRequest) -> Union[UnitResponse[Check return UnitResponse[CheckPaymentDTO](DtoDecoder.decode(data), None) else: return UnitError.from_json_api(response.json()) + + def cancel(self, check_payment_id: str) -> Union[UnitResponse[CheckPaymentDTO], UnitError]: + response = super().post(f"{self.resource}/{check_payment_id}/cancel") + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[CheckPaymentDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/unit/api/check_stop_payment_resource.py b/unit/api/check_stop_payment_resource.py new file mode 100644 index 00000000..203fb7ef --- /dev/null +++ b/unit/api/check_stop_payment_resource.py @@ -0,0 +1,20 @@ +from unit.api.base_resource import BaseResource +from unit.models.authorization_request import * +from unit.models.check_stop_payment import CheckStopPaymentDTO +from unit.models.codecs import DtoDecoder +from unit.models.payment import CreateCheckStopPaymentRequest + + +class CheckStopPaymentResource(BaseResource): + def __init__(self, api_url, token): + super().__init__(api_url, token) + self.resource = "stop-payments" + + def create(self, request: CreateCheckStopPaymentRequest) -> Union[UnitResponse[CheckStopPaymentDTO], UnitError]: + payload = request.to_json_api() + response = super().post(f"{self.resource}", payload) + if super().is_20x(response.status_code): + data = response.json().get("data") + return UnitResponse[CheckStopPaymentDTO](DtoDecoder.decode(data), None) + else: + return UnitError.from_json_api(response.json()) diff --git a/unit/models/check_stop_payment.py b/unit/models/check_stop_payment.py new file mode 100644 index 00000000..e5598ac4 --- /dev/null +++ b/unit/models/check_stop_payment.py @@ -0,0 +1,33 @@ +from unit.models import * +from unit.utils import date_utils + + +StopPaymentStatus = Literal["Active", "Disabled"] + +class CheckStopPaymentDTO(object): + def __init__(self, id: str, created_at: datetime, updated_at: datetime, amount: int, status: StopPaymentStatus, + check_number: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + self.id = id + self.type = "checkStopPayment" + self.attributes = self.attributes = { + "createdAt": created_at, + "updatedAt": updated_at, + "amount": amount, + "status": status, + "checkNumber": check_number, + "tags": tags, + } + self.relationships = relationships + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return CheckStopPaymentDTO( + id=_id, + created_at=date_utils.to_datetime(attributes["createdAt"]), + updated_at=date_utils.to_datetime(attributes["updatedAt"]), + amount=attributes["amount"], + status=attributes["status"], + check_number=attributes["checkNumber"], + tags=attributes.get("tags"), + relationships=relationships, + ) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 9a2c39ef..4674ea34 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -187,6 +187,15 @@ "checkPayment.additionalVerificationApproved": lambda _id, _type, attributes, relationships: CheckPaymentAdditionalVerificationApprovedEvent.from_json_api(_id, _type, attributes, relationships), + "stopPayment.created": lambda _id, _type, attributes, relationships: + StopPaymentCreatedEvent.from_json_api(_id, _type, attributes, relationships), + + "stopPayment.paymentStopped": lambda _id, _type, attributes, relationships: + StopPaymentPaymentStoppedEvent.from_json_api(_id, _type, attributes, relationships), + + "stopPayment.disabled": lambda _id, _type, attributes, relationships: + StopPaymentDisabledEvent.from_json_api(_id, _type, attributes, relationships), + "accountStatementDTO": lambda _id, _type, attributes, relationships: StatementDTO.from_json_api(_id, _type, attributes, relationships), diff --git a/unit/models/event.py b/unit/models/event.py index a998069f..92a20a6f 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -1,6 +1,8 @@ import json from datetime import datetime, date from typing import Literal, Optional + +from unit.models.check_stop_payment import StopPaymentStatus from unit.utils import date_utils from unit.models import * @@ -692,6 +694,45 @@ def from_json_api(_id, _type, attributes, relationships): return AccountReopenedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), relationships) +class StopPaymentCreatedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, tags: Optional[Dict[str, str]], + relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.type = 'stopPayment.created' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return StopPaymentCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes.get("tags"), + relationships) + + +class StopPaymentPaymentStoppedEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, stopped_payment_type: str, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.attributes['stoppedPaymentType'] = stopped_payment_type + self.type = 'stopPayment.paymentStopped' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return StopPaymentPaymentStoppedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["stoppedPaymentType"], attributes.get("tags"), + relationships) + + +class StopPaymentDisabledEvent(BaseEvent): + def __init__(self, id: str, created_at: datetime, status: str, previous_status: str, + tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): + BaseEvent.__init__(self, id, created_at, tags, relationships) + self.attributes['status'] = status + self.attributes['previousStatus'] = previous_status + self.type = 'stopPayment.disabled' + + @staticmethod + def from_json_api(_id, _type, attributes, relationships): + return StopPaymentDisabledEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + attributes["previousStatus"], attributes.get("tags"), relationships) + + EventDTO = Union[ AccountClosedEvent, AccountFrozenEvent, ApplicationDeniedEvent, ApplicationAwaitingDocumentsEvent, ApplicationPendingReviewEvent, CardActivatedEvent, CardStatusChangedEvent, @@ -707,6 +748,7 @@ def from_json_api(_id, _type, attributes, relationships): CheckPaymentAdditionalVerificationApprovedEvent, CustomerCreatedEvent, PaymentClearingEvent, PaymentSentEvent, PaymentReturnedEvent, StatementsCreatedEvent, TransactionCreatedEvent, AccountReopenedEvent, RawUnitObject, + StopPaymentCreatedEvent, StopPaymentPaymentStoppedEvent, StopPaymentDisabledEvent, ] diff --git a/unit/models/payment.py b/unit/models/payment.py index 882ee828..ba7f5310 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -385,9 +385,27 @@ def to_json_api(self) -> Dict: return super().to_payload("checkPayment", self.relationships) +class CreateCheckStopPaymentRequest(UnitRequest): + def __init__( + self, + check_number: str, + relationships: Dict[str, Relationship], + amount: Optional[int] = None, + tags: Optional[Dict[str, str]] = None, + idempotency_key: Optional[str] = None + ): + self.amount = amount + self.check_number = check_number + self.tags = tags + self.relationships = relationships + + def to_json_api(self) -> Dict: + return super().to_payload("checkStopPayment", self.relationships) + + CreatePaymentRequest = Union[CreateInlinePaymentRequest, CreateLinkedPaymentRequest, CreateVerifiedPaymentRequest, CreateBookPaymentRequest, CreateWirePaymentRequest, CreatePushToCardPaymentRequest, - CreateCheckPaymentRequest] + CreateCheckPaymentRequest, CreateCheckStopPaymentRequest] class PatchAchPaymentRequest(object): def __init__(self, payment_id: str, tags: Dict[str, str]): From bf7760d5b1356ba32c5a21f33235affe019492c5 Mon Sep 17 00:00:00 2001 From: Julia P <142916316+julia-truss@users.noreply.github.com> Date: Wed, 24 Apr 2024 18:34:02 -0400 Subject: [PATCH 127/137] Fix check payment event bugs (#29) * Fix copy error and key errors * Fix another key error --- unit/models/event.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/unit/models/event.py b/unit/models/event.py index 92a20a6f..ad3cf82b 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -326,17 +326,17 @@ def from_json_api(_id, _type, attributes, relationships): class CheckPaymentCreatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, status: str, additional_verification_status: str, + def __init__(self, id: str, created_at: datetime, status: str, additional_verification_required: str, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'checkPayment.created' self.attributes["status"] = status - self.attributes["additionalVerificationStatus"] = additional_verification_status + self.attributes["additionalVerificationRequired"] = additional_verification_required @staticmethod def from_json_api(_id, _type, attributes, relationships): return CheckPaymentCreatedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["status"], attributes["additionalVerificationStatus"], + attributes["status"], attributes["additionalVerificationRequired"], attributes.get("tags"), relationships) @@ -471,7 +471,7 @@ def __init__(self, id: str, created_at: datetime, status: str, previous_status: @staticmethod def from_json_api(_id, _type, attributes, relationships): - return CheckPaymentDeliveredEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], + return CheckPaymentReturnToSenderEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], attributes["previousStatus"], attributes.get("tags"), relationships) @@ -495,7 +495,7 @@ def __init__(self, id: str, created_at: datetime, previous_delivery_status: str, BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'checkPayment.deliveryStatusChanged' self.attributes["previousDeliveryStatus"] = previous_delivery_status - self.attributes["NewDeliveryStatus"] = new_delivery_status + self.attributes["newDeliveryStatus"] = new_delivery_status self.attributes["trackedAt"] = tracked_at self.attributes["postalCode"] = postal_code @@ -503,7 +503,7 @@ def __init__(self, id: str, created_at: datetime, previous_delivery_status: str, def from_json_api(_id, _type, attributes, relationships): return CheckPaymentDeliveryStatusChangedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["previousDeliveryStatus"], - attributes["NewDeliveryStatus"], attributes["trackedAt"], + attributes["newDeliveryStatus"], attributes["trackedAt"], attributes["postalCode"], attributes.get("tags"), relationships) From 0ac8f97fd948c106b296f404cdde2e4a258a0e8c Mon Sep 17 00:00:00 2001 From: Julia P <142916316+julia-truss@users.noreply.github.com> Date: Thu, 25 Apr 2024 18:18:42 -0400 Subject: [PATCH 128/137] Fix key error on check payment processed event (#30) --- unit/models/event.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/unit/models/event.py b/unit/models/event.py index ad3cf82b..34b85086 100644 --- a/unit/models/event.py +++ b/unit/models/event.py @@ -326,7 +326,7 @@ def from_json_api(_id, _type, attributes, relationships): class CheckPaymentCreatedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, status: str, additional_verification_required: str, + def __init__(self, id: str, created_at: datetime, status: str, additional_verification_required: bool, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'checkPayment.created' @@ -354,17 +354,17 @@ def from_json_api(_id, _type, attributes, relationships): class CheckPaymentProcessedEvent(BaseEvent): - def __init__(self, id: str, created_at: datetime, previous_status: str, additional_verification_status: str, tags: Optional[Dict[str, str]], + def __init__(self, id: str, created_at: datetime, previous_status: str, additional_verification_required: bool, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): BaseEvent.__init__(self, id, created_at, tags, relationships) self.type = 'checkPayment.processed' self.attributes["previousStatus"] = previous_status - self.attributes["additionalVerificationStatus"] = additional_verification_status + self.attributes["additionalVerificationRequired"] = additional_verification_required @staticmethod def from_json_api(_id, _type, attributes, relationships): return CheckPaymentProcessedEvent(_id, date_utils.to_datetime(attributes["createdAt"]), - attributes["previousStatus"], attributes["additionalVerificationStatus"], + attributes["previousStatus"], attributes["additionalVerificationRequired"], attributes.get("tags"), relationships) From 3effb89399906a0740cf7c1a13c2c0101e3e6270 Mon Sep 17 00:00:00 2001 From: Julia P <142916316+julia-truss@users.noreply.github.com> Date: Thu, 2 May 2024 15:54:19 -0400 Subject: [PATCH 129/137] Make check_number optional (#31) --- unit/models/check_payment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/models/check_payment.py b/unit/models/check_payment.py index 77c9ceea..28c078ac 100644 --- a/unit/models/check_payment.py +++ b/unit/models/check_payment.py @@ -32,7 +32,7 @@ class CheckPaymentDTO(object): def __init__(self, id: str, created_at: datetime, updated_at: datetime, amount: int, status: CheckPaymentStatus, - description: str, check_number: str, originated: bool, on_us: Optional[str], + description: str, originated: bool, check_number: Optional[str], on_us: Optional[str], on_us_auxiliary: Optional[str], counterparty_routing_number: Optional[str], return_status_reason: Optional[CheckPaymentReturnStatusReason], reject_reason: Optional[str], pending_review_reasons: Optional[List[str]], return_cutoff_time: Optional[datetime], @@ -96,7 +96,7 @@ def from_json_api(_id, _type, attributes, relationships): amount=attributes["amount"], status=attributes["status"], description=attributes["description"], - check_number=attributes["checkNumber"], + check_number=attributes.get("checkNumber"), originated=attributes["originated"], on_us=attributes.get("onUs"), on_us_auxiliary=attributes.get("onUsAuxiliary"), From 699e266678ad604973bfa7954ecc449a34f79202 Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Wed, 22 May 2024 14:56:44 -0700 Subject: [PATCH 130/137] Add timeout handling to payment create --- unit/api/base_resource.py | 20 ++++++++++---------- unit/api/check_payment_resource.py | 4 ++-- unit/api/payment_resource.py | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/unit/api/base_resource.py b/unit/api/base_resource.py index b8b06547..2cab48ef 100644 --- a/unit/api/base_resource.py +++ b/unit/api/base_resource.py @@ -14,22 +14,22 @@ def __init__(self, api_url, token): "user-agent": "unit-python-sdk" } - def get(self, resource: str, params: Dict = None, headers: Optional[Dict[str, str]] = None): - return requests.get(f"{self.api_url}/{resource}", params=params, headers=self.__merge_headers(headers)) + def get(self, resource: str, params: Dict = None, headers: Optional[Dict[str, str]] = None, timeout: float = None): + return requests.get(f"{self.api_url}/{resource}", params=params, headers=self.__merge_headers(headers), timeout=timeout) - def post(self, resource: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None): + def post(self, resource: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None, timeout: float = None): data = json.dumps(data, cls=UnitEncoder) if data is not None else None - return requests.post(f"{self.api_url}/{resource}", data=data, headers=self.__merge_headers(headers)) + return requests.post(f"{self.api_url}/{resource}", data=data, headers=self.__merge_headers(headers), timeout=timeout) - def patch(self, resource: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None): + def patch(self, resource: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None, timeout: float = None): data = json.dumps(data, cls=UnitEncoder) if data is not None else None - return requests.patch(f"{self.api_url}/{resource}", data=data, headers=self.__merge_headers(headers)) + return requests.patch(f"{self.api_url}/{resource}", data=data, headers=self.__merge_headers(headers), timeout=timeout) - def delete(self, resource: str, params: Dict = None, headers: Optional[Dict[str, str]] = None): - return requests.delete(f"{self.api_url}/{resource}", params=params, headers=self.__merge_headers(headers)) + def delete(self, resource: str, params: Dict = None, headers: Optional[Dict[str, str]] = None, timeout: float = None): + return requests.delete(f"{self.api_url}/{resource}", params=params, headers=self.__merge_headers(headers), timeout=timeout) - def put(self, resource: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None): - return requests.put(f"{self.api_url}/{resource}", data=data, headers=self.__merge_headers(headers)) + def put(self, resource: str, data: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None, timeout: float = None): + return requests.put(f"{self.api_url}/{resource}", data=data, headers=self.__merge_headers(headers), timeout=timeout) def __merge_headers(self, headers: Optional[Dict[str, str]] = None): if not headers: diff --git a/unit/api/check_payment_resource.py b/unit/api/check_payment_resource.py index 96e28c77..6e5fd722 100644 --- a/unit/api/check_payment_resource.py +++ b/unit/api/check_payment_resource.py @@ -27,9 +27,9 @@ def approve(self, request: ApproveCheckPaymentRequest) -> Union[UnitResponse[Che else: return UnitError.from_json_api(response.json()) - def create(self, request: CreateCheckPaymentRequest) -> Union[UnitResponse[CheckPaymentDTO], UnitError]: + def create(self, request: CreateCheckPaymentRequest, timeout: float = None) -> Union[UnitResponse[CheckPaymentDTO], UnitError]: payload = request.to_json_api() - response = super().post(f"{self.resource}", payload) + response = super().post(f"{self.resource}", payload, timeout=timeout) if super().is_20x(response.status_code): data = response.json().get("data") return UnitResponse[CheckPaymentDTO](DtoDecoder.decode(data), None) diff --git a/unit/api/payment_resource.py b/unit/api/payment_resource.py index 33c13fda..28a571d5 100644 --- a/unit/api/payment_resource.py +++ b/unit/api/payment_resource.py @@ -8,9 +8,9 @@ def __init__(self, api_url, token): super().__init__(api_url, token) self.resource = "payments" - def create(self, request: CreatePaymentRequest) -> Union[UnitResponse[PaymentDTO], UnitError]: + def create(self, request: CreatePaymentRequest, timeout: float = None) -> Union[UnitResponse[PaymentDTO], UnitError]: payload = request.to_json_api() - response = super().post(self.resource, payload) + response = super().post(self.resource, payload, timeout=timeout) if super().is_20x(response.status_code): data = response.json().get("data") return UnitResponse[PaymentDTO](DtoDecoder.decode(data), None) From edf607221f92ee6e93671b744e4b650fa8bb47fd Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Tue, 23 Jul 2024 16:53:15 -0400 Subject: [PATCH 131/137] Update application form version to V2024_06 --- unit/api/applicationForm_resource.py | 8 +++++++- unit_python_sdk.egg-info/SOURCES.txt | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/unit/api/applicationForm_resource.py b/unit/api/applicationForm_resource.py index 3ef83f3e..1a54f1c0 100644 --- a/unit/api/applicationForm_resource.py +++ b/unit/api/applicationForm_resource.py @@ -10,7 +10,13 @@ def __init__(self, api_url, token): def create(self, request: CreateApplicationFormRequest) -> Union[UnitResponse[ApplicationFormDTO], UnitError]: payload = request.to_json_api() - response = super().post(self.resource, payload) + response = super().post( + self.resource, + payload, + headers={ + "X-Accept-Version": "V2024_06" + } + ) if super().is_20x(response.status_code): data = response.json().get("data") return UnitResponse[ApplicationFormDTO](DtoDecoder.decode(data), None) diff --git a/unit_python_sdk.egg-info/SOURCES.txt b/unit_python_sdk.egg-info/SOURCES.txt index 9f0dc940..9027fe29 100644 --- a/unit_python_sdk.egg-info/SOURCES.txt +++ b/unit_python_sdk.egg-info/SOURCES.txt @@ -18,6 +18,8 @@ unit/api/batch_release_resource.py unit/api/bill_pay_resource.py unit/api/card_resource.py unit/api/check_deposit_resource.py +unit/api/check_payment_resource.py +unit/api/check_stop_payment_resource.py unit/api/counterparty_resource.py unit/api/customerToken_resource.py unit/api/customer_resource.py @@ -46,6 +48,8 @@ unit/models/benificial_owner.py unit/models/bill_pay.py unit/models/card.py unit/models/check_deposit.py +unit/models/check_payment.py +unit/models/check_stop_payment.py unit/models/codecs.py unit/models/counterparty.py unit/models/customer.py From f8cda6f2a4784eb3450c5166d16aeeabd9583806 Mon Sep 17 00:00:00 2001 From: Nick Addison Date: Tue, 23 Jul 2024 16:06:34 -0700 Subject: [PATCH 132/137] Add patch check payment --- unit/models/payment.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index ba7f5310..3682c6c8 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -447,7 +447,27 @@ def to_json_api(self) -> Dict: def __repr__(self): json.dumps(self.to_json_api()) -PatchPaymentRequest = Union[PatchAchPaymentRequest, PatchBookPaymentRequest] +class PatchCheckPaymentRequest(object): + def __init__(self, payment_id: str, tags: Dict[str, str]): + self.payment_id = payment_id + self.tags = tags + + def to_json_api(self) -> Dict: + payload = { + "data": { + "type": "checkPayment", + "attributes": { + "tags": self.tags + } + } + } + + return payload + + def __repr__(self): + json.dumps(self.to_json_api()) + +PatchPaymentRequest = Union[PatchAchPaymentRequest, PatchBookPaymentRequest, PatchCheckPaymentRequest] class ListPaymentParams(UnitParams): def __init__(self, limit: int = 100, offset: int = 0, account_id: Optional[str] = None, From 78ecd3b9560d5ba0a874bc9fda6e6cf92c65da00 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Thu, 25 Jul 2024 09:56:08 -0400 Subject: [PATCH 133/137] Remove version from application form create resource --- unit/api/applicationForm_resource.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/unit/api/applicationForm_resource.py b/unit/api/applicationForm_resource.py index 1a54f1c0..0aa12c4a 100644 --- a/unit/api/applicationForm_resource.py +++ b/unit/api/applicationForm_resource.py @@ -12,10 +12,7 @@ def create(self, request: CreateApplicationFormRequest) -> Union[UnitResponse[Ap payload = request.to_json_api() response = super().post( self.resource, - payload, - headers={ - "X-Accept-Version": "V2024_06" - } + payload ) if super().is_20x(response.status_code): data = response.json().get("data") From d74ad1586ad411754c76ffbfa344eb525dfb06a7 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Thu, 10 Oct 2024 09:18:50 -0400 Subject: [PATCH 134/137] Adds pushToCardPayment codec --- unit/models/codecs.py | 5 ++++- unit/models/payment.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 4674ea34..96f8f17b 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -14,7 +14,7 @@ BusinessVirtualDebitCardDTO, PinStatusDTO, CardLimitsDTO from unit.models.transaction import * from unit.models.payment import AchPaymentDTO, BookPaymentDTO, WirePaymentDTO, AchReceivedPaymentDTO, BillPaymentDTO, \ - SimulateIncomingAchPaymentDTO + SimulateIncomingAchPaymentDTO, PushToCardPaymentDTO from unit.models.customerToken import CustomerTokenDTO, CustomerVerificationTokenDTO from unit.models.fee import FeeDTO from unit.models.event import * @@ -145,6 +145,9 @@ "checkPayment": lambda _id, _type, attributes, relationships: CheckPaymentDTO.from_json_api(_id, _type, attributes, relationships), + "pushToCardPayment": lambda _id, _type, attributes, relationships: + PushToCardPaymentDTO.from_json_api(_id, _type, attributes, relationships), + "checkPayment.created": lambda _id, _type, attributes, relationships: CheckPaymentCreatedEvent.from_json_api(_id, _type, attributes, relationships), diff --git a/unit/models/payment.py b/unit/models/payment.py index 3682c6c8..4eef3ed7 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -128,6 +128,7 @@ def from_json_api(_id, _type, attributes, relationships): attributes["astraRoutineId"], attributes.get("reason"), attributes.get("tags"), relationships) + class BillPaymentDTO(BasePayment): def __init__(self, id: str, created_at: datetime, status: PaymentStatus, direction: str, description: str, amount: int, tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]]): From 813df6091d5417c69019937353750461c9e571d4 Mon Sep 17 00:00:00 2001 From: Avery Kushner Date: Thu, 10 Oct 2024 09:50:54 -0400 Subject: [PATCH 135/137] Safe get on astraRoutineId --- unit/models/payment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/models/payment.py b/unit/models/payment.py index 4eef3ed7..984c69ba 100644 --- a/unit/models/payment.py +++ b/unit/models/payment.py @@ -125,7 +125,7 @@ def __init__(self, id: str, created_at: datetime, status: PaymentStatus, directi def from_json_api(_id, _type, attributes, relationships): return PushToCardPaymentDTO(_id, date_utils.to_datetime(attributes["createdAt"]), attributes["status"], attributes.get("direction"), attributes["description"], attributes["amount"], - attributes["astraRoutineId"], attributes.get("reason"), attributes.get("tags"), + attributes.get("astraRoutineId"), attributes.get("reason"), attributes.get("tags"), relationships) From 2ff164af8c18955e0f22b7cf1b8087ca543f0a25 Mon Sep 17 00:00:00 2001 From: Julia P <142916316+julia-truss@users.noreply.github.com> Date: Mon, 21 Oct 2024 16:02:09 -0400 Subject: [PATCH 136/137] Update officer dto and application literals (#35) --- unit/models/__init__.py | 53 +++++++++++++++++++------------------- unit/models/application.py | 14 +++++++++- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index b8182af3..7ec43493 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -160,10 +160,23 @@ def __str__(self): ) +Occupation = Literal["ArchitectOrEngineer", "BusinessAnalystAccountantOrFinancialAdvisor", + "CommunityAndSocialServicesWorker", "ConstructionMechanicOrMaintenanceWorker", "Doctor", + "Educator", "EntertainmentSportsArtsOrMedia", "ExecutiveOrManager", "FarmerFishermanForester", + "FoodServiceWorker", "GigWorker", "HospitalityOfficeOrAdministrativeSupportWorker", + "HouseholdManager", "JanitorHousekeeperLandscaper", "Lawyer", "ManufacturingOrProductionWorker", + "MilitaryOrPublicSafety", "NurseHealthcareTechnicianOrHealthcareSupport", + "PersonalCareOrServiceWorker", "PilotDriverOperator", "SalesRepresentativeBrokerAgent", + "ScientistOrTechnologist", "Student"] +AnnualIncome = Literal["UpTo10k", "Between10kAnd25k", "Between25kAnd50k", "Between50kAnd100k", "Between100kAnd250k", + "Over250k"] +SourceOfIncome = Literal["EmploymentOrPayrollIncome", "PartTimeOrContractorIncome", "InheritancesAndGifts", + "PersonalInvestments", "BusinessOwnershipInterests", "GovernmentBenefits"] Status = Literal["Approved", "Denied", "PendingReview"] Title = Literal["CEO", "COO", "CFO", "President", "BenefitsAdministrationOfficer", "CIO", "VP", "AVP", "Treasurer", "Secretary", "Controller", "Manager", "Partner", "Member"] -EntityType = Literal["Corporation", "LLC", "Partnership"] +EntityType = Literal["Corporation", "LLC", "Partnership", "PubliclyTradedCorporation", "PrivatelyHeldCorporation", + "NotForProfitOrganization"] class FullName(UnitDTO): @@ -235,19 +248,11 @@ def from_json_api(data: Dict): class Officer(UnitDTO): - def __init__( - self, - full_name: FullName, - date_of_birth: date, - address: Address, - phone: Phone, - email: str, - status: Optional[Status] = None, - title: Optional[Title] = None, - ssn: Optional[str] = None, - passport: Optional[str] = None, - nationality: Optional[str] = None, - ): + def __init__(self, full_name: FullName, date_of_birth: date, address: Address, phone: Phone, email: str, + status: Optional[Status] = None, title: Optional[Title] = None, ssn: Optional[str] = None, + passport: Optional[str] = None, nationality: Optional[str] = None, id_theft_score: Optional[str] = None, + occupation: Optional[Occupation] = None, annual_income: Optional[AnnualIncome] = None, + source_of_income: Optional[SourceOfIncome] = None): self.full_name = full_name self.date_of_birth = date_of_birth self.address = address @@ -258,21 +263,17 @@ def __init__( self.ssn = ssn self.passport = passport self.nationality = nationality + self.id_theft_score = id_theft_score + self.occupation = occupation + self.annual_income = annual_income + self.source_of_income = source_of_income @staticmethod def from_json_api(data: Dict): - return Officer( - data.get("fullName"), - data.get("dateOfBirth"), - data.get("address"), - data.get("phone"), - data.get("email"), - data.get("status"), - data.get("title"), - data.get("ssn"), - data.get("passport"), - data.get("nationality"), - ) + return Officer(data.get("fullName"), data.get("dateOfBirth"), data.get("address"), data.get("phone"), + data.get("email"), data.get("status"), data.get("title"), data.get("ssn"), data.get("passport"), + data.get("nationality"), data.get("idTheftScore"), data.get("occupation"), data.get("annualIncome"), + data.get("sourceOfIncome")) class BeneficialOwner(UnitDTO): diff --git a/unit/models/application.py b/unit/models/application.py index 488b3054..7cbe7b44 100644 --- a/unit/models/application.py +++ b/unit/models/application.py @@ -10,6 +10,18 @@ "AddressVerification", "CertificateOfIncorporation", "EmployerIdentificationNumberConfirmation", + "SocialSecurityCard", + "ClientRequested", + "SelfieVerification", +] + +ApplicationDocumentStatus = Literal[ + "Required", + "ReceivedBack", + "ReceivedFront", + "Invalid", + "Approved", + "PendingReview" ] ReasonCode = Literal[ @@ -435,7 +447,7 @@ class ApplicationDocumentDTO(object): def __init__( self, id: str, - status: ApplicationStatus, + status: ApplicationDocumentStatus, document_type: DocumentType, description: str, name: str, From 64d7728fa963ac839425218b002769aa2ad49fbf Mon Sep 17 00:00:00 2001 From: Julia P <142916316+julia-truss@users.noreply.github.com> Date: Fri, 1 Nov 2024 10:53:39 -0400 Subject: [PATCH 137/137] Update application request and beneficial owner (#36) * Update application * Update serialization --- unit/models/__init__.py | 9 ++++++ unit/models/application.py | 61 +++++++++++++++++++++++++++++++++----- unit/models/codecs.py | 14 +++++++++ 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/unit/models/__init__.py b/unit/models/__init__.py index 7ec43493..aef251eb 100644 --- a/unit/models/__init__.py +++ b/unit/models/__init__.py @@ -289,6 +289,9 @@ def __init__( passport: Optional[str] = None, nationality: Optional[str] = None, percentage: Optional[int] = None, + occupation: Optional[Occupation] = None, + annual_income: Optional[AnnualIncome] = None, + source_of_income: Optional[SourceOfIncome] = None ): self.full_name = full_name self.date_of_birth = date_of_birth @@ -300,6 +303,9 @@ def __init__( self.passport = passport self.nationality = nationality self.percentage = percentage + self.occupation = occupation + self.annual_income = annual_income + self.source_of_income = source_of_income @staticmethod def from_json_api(l: List): @@ -317,6 +323,9 @@ def from_json_api(l: List): data.get("passport"), data.get("nationality"), data.get("percentage"), + data.get("occupation"), + data.get("annualIncome"), + data.get("sourceOfIncome") ) ) return beneficial_owners diff --git a/unit/models/application.py b/unit/models/application.py index 7cbe7b44..454b9de9 100644 --- a/unit/models/application.py +++ b/unit/models/application.py @@ -203,6 +203,13 @@ def __init__( ip: Optional[str], ein: Optional[str], dba: Optional[str], + website: Optional[str], + year_of_incorporation: Optional[str], + business_vertical: Optional[BusinessVertical], + annual_revenue: Optional[AnnualRevenue], + number_of_employees: Optional[NumberOfEmployees], + cash_flow: Optional[CashFlow], + countries_of_operation: Optional[List[str]], tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]], ): @@ -216,12 +223,18 @@ def __init__( "status": status, "ssn": ssn, "stateOfIncorporation": state_of_incorporation, - "ssn": ssn, "message": message, "ip": ip, "ein": ein, "entityType": entity_type, "dba": dba, + "website": website, + "yearOfIncorporation": year_of_incorporation, + "businessVertical": business_vertical, + "annualRevenue": annual_revenue, + "numberOfEmployees": number_of_employees, + "cashFlow": cash_flow, + "countriesOfOperation": countries_of_operation, "contact": contact, "officer": officer, "beneficialOwners": beneficial_owners, @@ -248,6 +261,13 @@ def from_json_api(_id, _type, attributes, relationships): attributes.get("ip"), attributes.get("ein"), attributes.get("dba"), + attributes.get("website"), + attributes.get("year_of_incorporation"), + attributes.get("business_vertical"), + attributes.get("annual_revenue"), + attributes.get("number_of_employees"), + attributes.get("cash_flow"), + attributes.get("countries_of_operation"), attributes.get("tags"), relationships, ) @@ -274,6 +294,9 @@ def __init__( device_fingerprints: Optional[List[DeviceFingerprint]] = None, idempotency_key: str = None, tags: Optional[Dict[str, str]] = None, + website: str = None, + annual_revenue: Optional[AnnualRevenue] = None, + number_of_employees: Optional[NumberOfEmployees] = None, business_vertical: Optional[BusinessVertical] = None, ): self.full_name = full_name @@ -291,7 +314,10 @@ def __init__( self.device_fingerprints = device_fingerprints self.idempotency_key = idempotency_key self.tags = tags + self.annual_revenue = annual_revenue + self.number_of_employees = number_of_employees self.business_vertical = business_vertical + self.website = website def to_json_api(self) -> Dict: payload = { @@ -341,6 +367,15 @@ def to_json_api(self) -> Dict: if self.tags: payload["data"]["attributes"]["tags"] = self.tags + if self.website: + payload["data"]["attributes"]["website"] = self.website + + if self.annual_revenue: + payload["data"]["attributes"]["annualRevenue"] = self.annual_revenue + + if self.number_of_employees: + payload["data"]["attributes"]["numberOfEmployees"] = self.number_of_employees + if self.business_vertical: payload["data"]["attributes"]["businessVertical"] = self.business_vertical @@ -413,13 +448,7 @@ def to_json_api(self) -> dict: "officer": self.officer, "beneficialOwners": self.beneficial_owners, "entityType": self.entity_type, - "industry": self.industry, - "annualRevenue": self.annual_revenue, - "numberOfEmployees": self.number_of_employees, - "cashFlow": self.cash_flow, "yearOfIncorporation": self.year_of_incorporation, - "countriesOfOperation": self.countries_of_operation, - "stockSymbol": self.stock_symbol, "businessVertical": self.business_vertical, }, } @@ -437,6 +466,24 @@ def to_json_api(self) -> dict: if self.website: payload["data"]["attributes"]["website"] = self.website + if self.industry: + payload["data"]["attributes"]["industry"] = self.industry + + if self.annual_revenue: + payload["data"]["attributes"]["annualRevenue"] = self.annual_revenue + + if self.number_of_employees: + payload["data"]["attributes"]["numberOfEmployees"] = self.number_of_employees + + if self.cash_flow: + payload["data"]["attributes"]["cashFlow"] = self.cash_flow + + if self.countries_of_operation: + payload["data"]["attributes"]["countriesOfOperation"] = self.countries_of_operation + + if self.stock_symbol: + payload["data"]["attributes"]["stockSymbol"] = self.stock_symbol + return payload def __repr__(self): diff --git a/unit/models/codecs.py b/unit/models/codecs.py index 96f8f17b..fc83cfae 100644 --- a/unit/models/codecs.py +++ b/unit/models/codecs.py @@ -459,6 +459,14 @@ def default(self, obj): officer["passport"] = obj.passport if obj.nationality is not None: officer["nationality"] = obj.nationality + if obj.id_theft_score is not None: + officer["idTheftScore"] = obj.id_theft_score + if obj.occupation is not None: + officer["occupation"] = obj.occupation + if obj.annual_income is not None: + officer["annualIncome"] = obj.annual_income + if obj.source_of_income is not None: + officer["sourceOfIncome"] = obj.source_of_income return officer if isinstance(obj, BeneficialOwner): beneficial_owner = {"fullName": obj.full_name, "dateOfBirth": date_utils.to_date_str(obj.date_of_birth), @@ -473,6 +481,12 @@ def default(self, obj): beneficial_owner["nationality"] = obj.nationality if obj.percentage is not None: beneficial_owner["percentage"] = obj.percentage + if obj.occupation is not None: + beneficial_owner["occupation"] = obj.occupation + if obj.annual_income is not None: + beneficial_owner["annualIncome"] = obj.annual_income + if obj.source_of_income is not None: + beneficial_owner["sourceOfIncome"] = obj.source_of_income return beneficial_owner if isinstance(obj, RelationshipArray): return {"data": list(map(lambda r: r.to_dict(), obj.relationships))}