From 1d914fca6a7daaaf7466b6e9b3d9a0b62edd72ee Mon Sep 17 00:00:00 2001 From: Mike Ossig Date: Fri, 5 Apr 2024 11:45:14 -0400 Subject: [PATCH 01/22] removes duplicate method --- method/resources/Account.py | 6 ------ setup.py | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/method/resources/Account.py b/method/resources/Account.py index 6acdf4e..9dcfb78 100644 --- a/method/resources/Account.py +++ b/method/resources/Account.py @@ -549,9 +549,3 @@ def get_payoff(self, _id: str, pyf_id: str) -> AccountPayoff: def create_payoff(self, _id: str) -> AccountPayoff: return super(AccountResource, self)._create_with_sub_path('{_id}/payoffs'.format(_id=_id), {}) - - def withdraw_consent(self, _id: str) -> Account: - return super(AccountResource, self)._create_with_sub_path( - '{_id}/consent'.format(_id=_id), - {'type': 'withdraw', 'reason': 'holder_withdrew_consent'} - ) diff --git a/setup.py b/setup.py index aee2d6e..a276f4b 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='method-python', - version='0.0.43', + version='0.0.44', description='Python library for the Method API', long_description='Python library for the Method API', long_description_content_type='text/x-rst', From 80ba01382ebd2d4f4ae047b8c76432311849d661 Mon Sep 17 00:00:00 2001 From: Mike Ossig Date: Mon, 8 Apr 2024 10:48:50 -0400 Subject: [PATCH 02/22] rebase --- method/resources/{ => Account}/Account.py | 40 +-- method/resources/Account/ExternalTypes.py | 262 ++++++++++++++++++ method/resources/Account/Payoffs.py | 35 +++ .../{AccountSync.py => Account/Syncs.py} | 7 +- .../resources/Account/VerificationSessions.py | 161 +++++++++++ method/resources/Account/__init__.py | 4 + method/resources/Entity/Connect.py | 33 +++ method/resources/Entity/CreditScores.py | 47 ++++ method/resources/{ => Entity}/Entity.py | 79 ++---- method/resources/Entity/Identities.py | 34 +++ method/resources/Entity/Syncs.py | 36 +++ .../resources/Entity/VerificationSessions.py | 113 ++++++++ method/resources/Entity/__init__.py | 6 + method/resources/__init__.py | 7 +- 14 files changed, 772 insertions(+), 92 deletions(-) rename method/resources/{ => Account}/Account.py (93%) create mode 100644 method/resources/Account/ExternalTypes.py create mode 100644 method/resources/Account/Payoffs.py rename method/resources/{AccountSync.py => Account/Syncs.py} (81%) create mode 100644 method/resources/Account/VerificationSessions.py create mode 100644 method/resources/Account/__init__.py create mode 100644 method/resources/Entity/Connect.py create mode 100644 method/resources/Entity/CreditScores.py rename method/resources/{ => Entity}/Entity.py (81%) create mode 100644 method/resources/Entity/Identities.py create mode 100644 method/resources/Entity/Syncs.py create mode 100644 method/resources/Entity/VerificationSessions.py create mode 100644 method/resources/Entity/__init__.py diff --git a/method/resources/Account.py b/method/resources/Account/Account.py similarity index 93% rename from method/resources/Account.py rename to method/resources/Account/Account.py index 9dcfb78..b0b6b58 100644 --- a/method/resources/Account.py +++ b/method/resources/Account/Account.py @@ -4,7 +4,7 @@ from method.configuration import Configuration from method.errors import ResourceError from method.resources.Verification import VerificationResource -from method.resources.AccountSync import AccountSyncResource, AccountSync +from method.resources.Account import AccountSyncResource, AccountSync, AccountVerificationSessionResource, AccountPayoffsResource # Literals, keep ordered alphabetically @@ -127,12 +127,6 @@ 'over_120' ] -AccountPayoffStatusesLiterals = Literal[ - 'completed', - 'in_progress', - 'pending', - 'failed' -] class AccountACH(TypedDict): routing: int @@ -479,24 +473,19 @@ class AccountPaymentHistory(TypedDict): payment_history: List[CreditReportTradelinePaymentHistoryItem] -class AccountPayoff(TypedDict): - id: str - status: AccountPayoffStatusesLiterals - amount: Optional[int] - term: Optional[int] - per_diem_amount: Optional[int] - error: Optional[ResourceError] - created_at: str - updated_at: str - - class AccountSubResources: verification: VerificationResource - sync: AccountSyncResource + syncs: AccountSyncResource + payoffs: AccountPayoffsResource + verification_sessions: AccountVerificationSessionResource + def __init__(self, _id: str, config: Configuration): self.verification = VerificationResource(config.add_path(_id)) self.syncs = AccountSyncResource(config.add_path(_id)) + self.payoffs = AccountPayoffsResource(config.add_path(_id)) + self.verification_sessions = AccountVerificationSessionResource(config.add_path(_id)) + class AccountResource(Resource): def __init__(self, config: Configuration): @@ -505,7 +494,7 @@ def __init__(self, config: Configuration): def __call__(self, _id: str) -> AccountSubResources: return AccountSubResources(_id, self.config) - def get(self, _id: str) -> Account: + def retrieve(self, _id: str) -> Account: return super(AccountResource, self)._get_with_id(_id) def update(self, _id: str, opts: LiabilityUpdateOpts) -> Account: @@ -517,10 +506,10 @@ def list(self, params: Optional[AccountListOpts] = None) -> List[Account]: def create(self, opts: Union[AccountACHCreateOpts, AccountLiabilityCreateOpts, AccountClearingCreateOpts], request_opts: Optional[RequestOpts] = None) -> Account: return super(AccountResource, self)._create(opts, request_opts) - def get_payment_history(self, _id: str) -> AccountPaymentHistory: + def retrieve_payment_history(self, _id: str) -> AccountPaymentHistory: return super(AccountResource, self)._get_with_sub_path('{_id}/payment_history'.format(_id=_id)) - def get_details(self, _id: str) -> AccountDetail: + def retrieve_details(self, _id: str) -> AccountDetail: return super(AccountResource, self)._get_with_sub_path('{_id}/details'.format(_id=_id)) def bulk_sync(self, acc_ids: AccountCreateBulkSyncOpts) -> AccountCreateBulkSyncResponse: @@ -543,9 +532,4 @@ def unenroll_auto_syncs(self, _id: str) -> Account: def withdraw_consent(self, _id: str, data: AccountWithdrawConsentOpts = { 'type': 'withdraw', 'reason': 'holder_withdrew_consent' }) -> Account: return super(AccountResource, self)._create_with_sub_path('{_id}/consent'.format(_id=_id), data) - - def get_payoff(self, _id: str, pyf_id: str) -> AccountPayoff: - return super(AccountResource, self)._get_with_sub_path('{_id}/payoffs/{pyf_id}'.format(_id=_id, pyf_id=pyf_id)) - - def create_payoff(self, _id: str) -> AccountPayoff: - return super(AccountResource, self)._create_with_sub_path('{_id}/payoffs'.format(_id=_id), {}) + diff --git a/method/resources/Account/ExternalTypes.py b/method/resources/Account/ExternalTypes.py new file mode 100644 index 0000000..5ed9fbe --- /dev/null +++ b/method/resources/Account/ExternalTypes.py @@ -0,0 +1,262 @@ +from typing import TypedDict, Optional, List, Literal + + +PlaidTransactionTypesLiterals = Literal[ + 'digital', + 'place', + 'special', + 'unresolved' +] + + +PlaidTransactionPaymentChannelTypesLiterals = Literal[ + 'online', + 'in_store', + 'other' +] + + +PlaidTransactionCodeLiterals = Literal[ + 'adjustment', + 'atm', + 'bank charge', + 'bill payment', + 'cash', + 'cashback', + 'cheque', + 'direct debit', + 'interest', + 'purchase', + 'standing order', + 'transfer', + 'null' +] + + +PlaidCounterpartyTypeLiterals = Literal[ + 'merchant', + 'financial_institution', + 'payment_app', + 'marketplace', + 'payment_terminal', + 'income_source' +] + + +class PlaidBalance(TypedDict): + available: Optional[int] + current: Optional[int] + iso_currency_code: Optional[str] + limit: Optional[int] + unofficial_currency_code: Optional[str] + + +class PlaidLocation(TypedDict): + address: Optional[str] + city: Optional[str] + region: Optional[str] + postal_code: Optional[str] + country: Optional[str] + lat: Optional[int] + lon: Optional[int] + store_number: Optional[str] + + +class PlaidPaymentMeta(TypedDict): + reference_number: Optional[str] + ppd_id: Optional[str] + payee: Optional[str] + by_order_of: Optional[str] + payer: Optional[str] + payment_method: Optional[str] + payment_processor: Optional[str] + reason: Optional[str] + + +class PlaidPersonalFinanceCategory(TypedDict): + primary: str + detailed: str + confidence_level: Optional[str] + + +class PlaidTransactionCounterparty(TypedDict): + name: str + entity_id: Optional[str] + type: PlaidCounterpartyTypeLiterals; + website: Optional[str] + logo_url: Optional[str] + confidence_level: Optional[str] + + +class PlaidTransaction(TypedDict): + account_id: str + amount: int + iso_currency_code: Optional[str] + unofficial_currency_code: Optional[str] + category: Optional[List[str]] + category_id: Optional[str] + check_number: Optional[str] + date: str + location: PlaidLocation; + name: str + merchant_name: Optional[str] + original_description: Optional[str] + payment_meta: PlaidPaymentMeta; + pending: bool + pending_transaction_id: Optional[str] + account_owner: Optional[str] + transaction_id: str + transaction_type: Optional[PlaidTransactionTypesLiterals]; + logo_url: Optional[str] + website: Optional[str] + authorized_date: Optional[str] + authorized_datetime: Optional[str] + datetime: Optional[str] + payment_channel: PlaidTransactionPaymentChannelTypesLiterals + personal_finance_category: Optional[PlaidPersonalFinanceCategory] + transaction_code: Optional[PlaidTransactionCodeLiterals] + personal_finance_category_icon_url: str + counterparties: List[PlaidTransactionCounterparty] + merchant_entity_id: Optional[str] + + +class MXAccount(TypedDict): + account_number: str + account_ownership: str + annuity_policy_to_date: str + annuity_provider: str + annuity_term_year: int + apr: int + apy: int + available_balance: int + available_credit: int + balance: int + cash_balance: int + cash_surrender_value: int + created_at: str + credit_limit: int + currency_code: str + day_payment_is_due: int + death_benefit: int + guid: str + holdings_value: int + id: str + imported_at: str + interest_rate: int + institution_code: str + insured_name: str + is_closed: bool + is_hidden: bool + is_manual: bool + last_payment: int + last_payment_at: str + loan_amount: int + margin_balance: int + matures_on: str + member_guid: str + member_id: str + member_is_managed_by_user: bool + metadata: str + minimum_balance: int + minimum_payment: int + name: str + nickname: str + original_balance: int + pay_out_amount: int + payment_due_at: str + payoff_balance: int + premium_amount: int + property_type: str + routing_number: str + skip_webhook: bool + started_on: str + subtype: str + today_ugl_amount: int + today_ugl_percentage: int + total_account_value: int + type: str + updated_at: str + user_guid: str + user_id: str + + +class MXTransaction(TypedDict): + account_guid: str + account_id: str + amount: int + category: str + category_guid: str + check_number_string: str + created_at: str + currency_code: str + date: str + description: str + extended_transaction_type: str + guid: str + id: str + is_bill_pay: bool + is_direct_deposit: bool + is_expense: bool + is_fee: bool + is_income: bool + is_international: bool + is_overdraft_fee: bool + is_payroll_advance: bool + is_recurring: bool + is_subscription: bool + latitude: int + localized_description: str + localized_memo: str + longitude: int + member_guid: str + member_is_managed_by_user: bool + memo: str + merchant_category_code: int + merchant_guid: str + merchant_location_guid: str + metadata: str + original_description: str + posted_at: str + status: str + top_level_category: str + transacted_at: str + type: str + updated_at: str + user_guid: str + user_id: str + + +class TellerLinks(TypedDict): + account: str + self: str + + +class TellerBalance(TypedDict): + ledger: int + links: TellerLinks + account_id: str + available: int + + +class TellerTransactionCounterparty(TypedDict): + type: str + name: str + + +class TellerTransactionDetails(TypedDict): + category: str + counterparty: TellerTransactionCounterparty + processing_status: str + + +class TellerTransaction(TypedDict): + running_balance: Optional[int] + details: TellerTransactionDetails + description: str + account_id: str + date: str + id: str + links: TellerLinks + amount: int + type: str + status: str diff --git a/method/resources/Account/Payoffs.py b/method/resources/Account/Payoffs.py new file mode 100644 index 0000000..09a4a90 --- /dev/null +++ b/method/resources/Account/Payoffs.py @@ -0,0 +1,35 @@ +from typing import TypedDict, Optional, Literal + +from method.resource import Resource +from method.configuration import Configuration +from method.errors import ResourceError + + +AccountPayoffStatusesLiterals = Literal[ + 'completed', + 'in_progress', + 'pending', + 'failed' +] + + +class AccountPayoff(TypedDict): + id: str + status: AccountPayoffStatusesLiterals + amount: Optional[int] + term: Optional[int] + per_diem_amount: Optional[int] + error: Optional[ResourceError] + created_at: str + updated_at: str + + +class AccountPayoffsResource(Resource): + def __init__(self, config: Configuration): + super(AccountPayoffsResource, self).__init__(config.add_path('payoffs')) + + def retrieve(self, pyf_id: str) -> AccountPayoff: + return super(AccountPayoffsResource, self)._get_with_id(pyf_id) + + def create(self) -> AccountPayoff: + return super(AccountPayoffsResource, self)._create({}) \ No newline at end of file diff --git a/method/resources/AccountSync.py b/method/resources/Account/Syncs.py similarity index 81% rename from method/resources/AccountSync.py rename to method/resources/Account/Syncs.py index dba089e..f841230 100644 --- a/method/resources/AccountSync.py +++ b/method/resources/Account/Syncs.py @@ -1,9 +1,8 @@ from typing import TypedDict, Optional -from method.resource import Resource, RequestOpts +from method.resource import Resource from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Verification import VerificationResource class AccountSync(TypedDict): @@ -14,14 +13,16 @@ class AccountSync(TypedDict): created_at: str updated_at: str + class AccountSyncCreateOpts(TypedDict): pass + class AccountSyncResource(Resource): def __init__(self, config: Configuration): super(AccountSyncResource, self).__init__(config.add_path('syncs')) - def get(self, _id: str) -> AccountSync: + def retrieve(self, _id: str) -> AccountSync: return super(AccountSyncResource, self)._get_with_id(_id) def create(self, opts: Optional[AccountSyncCreateOpts] = {}) -> AccountSync: diff --git a/method/resources/Account/VerificationSessions.py b/method/resources/Account/VerificationSessions.py new file mode 100644 index 0000000..b1455da --- /dev/null +++ b/method/resources/Account/VerificationSessions.py @@ -0,0 +1,161 @@ +from typing import TypedDict, Optional, List, Literal, Union + +from method.resource import Resource +from method.configuration import Configuration +from method.errors import ResourceError +from method.resources.Account.ExternalTypes import PlaidBalance, PlaidTransaction, MXAccount, MXTransaction, TellerBalance, TellerTransaction + + +AccountVerificationSessionStatusesLiterals = Literal[ + 'pending', + 'in_progress', + 'verified', + 'failed' +] + + +AccountVerificationSessionTypesLiterals = Literal[ + 'micro_deposits', + 'plaid', + 'mx', + 'teller', + 'standard', + 'instant', + 'pre_auth' +] + +AccountVerificationPassFailLiterals = Literal[ + 'pass', + 'fail' +] + + +class AccountVerificationSessionMicroDeposits(TypedDict): + amounts: List[int] + + +class AccountVerificationSessionPlaid(TypedDict): + balances: PlaidBalance + transactions: List[PlaidTransaction] + + +class AccountVerificationSessionMX(TypedDict): + accounts: MXAccount + transactions: List[MXTransaction] + + +class AccountVerificationSessionTeller(TypedDict): + balances: TellerBalance + transactions: List[TellerTransaction] + + +class AccountVerificationSessionTrustProvisioner(TypedDict): + pass + + +class AccountVerificationSessionAutoVerify(TypedDict): + pass + + +class AccountVerificationSessionThreeDS(TypedDict): + pass + + +class AccountVerificationSessionIssuer(TypedDict): + pass + + +class AccountVerificationSessionStandard(TypedDict): + number: str + + +class AccountVerificationSessionInstant(TypedDict): + exp_year: Optional[str] + exp_month: Optional[str] + exp_check: Optional[AccountVerificationPassFailLiterals] + number: Optional[str] + + +class AccountVerificationSessionPreAuth(AccountVerificationSessionInstant): + cvv: str + cvv_check: Optional[AccountVerificationPassFailLiterals] + billing_zip_code: str + billing_zip_code_check: Optional[AccountVerificationPassFailLiterals] + pre_auth_check: Optional[AccountVerificationPassFailLiterals] + + +class AccountVerificationSessionCreateOpts(TypedDict): + type: AccountVerificationSessionTypesLiterals + + +class AccountVerificationSessionMicroDepositsUpdateOpts(TypedDict): + micro_deposits: AccountVerificationSessionMicroDeposits + + +class AccountVerificationSessionPlaidUpdateOpts(TypedDict): + plaid: AccountVerificationSessionPlaid + + +class AccountVerificationSessionMXUpdateOpts(TypedDict): + mx: AccountVerificationSessionMX + + +class AccountVerificationSessionTellerUpdateOpts(TypedDict): + teller: AccountVerificationSessionTeller + + +class AccountVerificationSessionStandardUpdateOpts(TypedDict): + standard: AccountVerificationSessionStandard + + +class AccountVerificationSessionInstantUpdateOpts(TypedDict): + instant: AccountVerificationSessionInstant + + +class AccountVerificationSessionPreAuthUpdateOpts(TypedDict): + pre_auth: AccountVerificationSessionPreAuth + + +AccountVerificationSessionUpdateOpts = Union[ + AccountVerificationSessionMicroDepositsUpdateOpts, + AccountVerificationSessionPlaidUpdateOpts, + AccountVerificationSessionMXUpdateOpts, + AccountVerificationSessionTellerUpdateOpts, + AccountVerificationSessionStandardUpdateOpts, + AccountVerificationSessionInstantUpdateOpts, + AccountVerificationSessionPreAuthUpdateOpts +] + + +class AccountVerificationSession(TypedDict): + id: str + status: AccountVerificationSessionStatusesLiterals + type: AccountVerificationSessionTypesLiterals + error: Optional[ResourceError] + plaid: Optional[AccountVerificationSessionPlaid] + mx: Optional[AccountVerificationSessionMX] + teller: Optional[AccountVerificationSessionTeller] + micro_deposits: Optional[AccountVerificationSessionMicroDeposits] + trust_provisioner: Optional[AccountVerificationSessionTrustProvisioner] + auto_verify: Optional[AccountVerificationSessionAutoVerify] + standard: Optional[AccountVerificationSessionStandard] + instant: Optional[AccountVerificationSessionInstant] + pre_auth: Optional[AccountVerificationSessionPreAuth] + three_ds: Optional[AccountVerificationSessionThreeDS] + issuer: Optional[AccountVerificationSessionIssuer] + created_at: str + updated_at: str + + +class AccountVerificationSessionResource(Resource): + def __init__(self, config: Configuration): + super(AccountVerificationSessionResource, self).__init__(config.add_path('verification_sessions')) + + def create(self, opts: AccountVerificationSessionCreateOpts) -> AccountVerificationSession: + return super(AccountVerificationSessionResource, self)._create(opts) + + def retrieve(self, avs_id: str) -> AccountVerificationSession: + return super(AccountVerificationSessionResource, self)._get_with_id(avs_id) + + def update(self, avs_id: str, opts: AccountVerificationSessionUpdateOpts) -> AccountVerificationSession: + return super(AccountVerificationSessionResource, self)._update_with_id(avs_id, opts) \ No newline at end of file diff --git a/method/resources/Account/__init__.py b/method/resources/Account/__init__.py new file mode 100644 index 0000000..628149e --- /dev/null +++ b/method/resources/Account/__init__.py @@ -0,0 +1,4 @@ +from method.resources.Account.Account import Account, AccountResource +from method.resources.Account.Syncs import AccountSync, AccountSyncResource +from method.resources.Account.Payoffs import AccountPayoff, AccountPayoffsResource +from method.resources.Account.VerificationSessions import AccountVerificationSession, AccountVerificationSessionResource \ No newline at end of file diff --git a/method/resources/Entity/Connect.py b/method/resources/Entity/Connect.py new file mode 100644 index 0000000..f3ba10f --- /dev/null +++ b/method/resources/Entity/Connect.py @@ -0,0 +1,33 @@ +from typing import TypedDict, Optional, Literal, List + +from method.resource import Resource +from method.configuration import Configuration +from method.errors import ResourceError + + +EntityConnectResponseStatusLiterals = Literal[ + 'completed', + 'in_progress', + 'pending', + 'failed' +] + + +class EntityConnect(TypedDict): + id: str + status: EntityConnectResponseStatusLiterals + accounts: Optional[List[str]] + error: Optional[ResourceError] + created_at: str + updated_at: str + + +class EntityConnectResource(Resource): + def __init__(self, config: Configuration): + super(EntityConnectResource, self).__init__(config.add_path('connect')) + + def retrieve(self, cxn_id: str) -> EntityConnect: + return super(EntityConnectResource, self)._get_with_id(cxn_id) + + def create(self) -> EntityConnect: + return super(EntityConnectResource, self)._create({}) \ No newline at end of file diff --git a/method/resources/Entity/CreditScores.py b/method/resources/Entity/CreditScores.py new file mode 100644 index 0000000..2810843 --- /dev/null +++ b/method/resources/Entity/CreditScores.py @@ -0,0 +1,47 @@ +from typing import TypedDict, Optional, List, Literal + +from method.resource import Resource +from method.configuration import Configuration +from method.errors import ResourceError +from method.resources.Entity.Entity import EntityStatusesLiterals, CreditReportBureausLiterals + + +CreditScoresModelLiterals = Literal[ + 'vantage_4', + 'vantage_3' +] + + +class EntityCreditScoresFactorsType(TypedDict): + code: str + description: str + + +class EntityCreditScoresType(TypedDict): + score: int + source: CreditReportBureausLiterals + model: CreditScoresModelLiterals + factors: List[EntityCreditScoresFactorsType] + created_at: str + factors: EntityCreditScoresFactorsType + created_at: str + + +class EntityCreditScores(TypedDict): + id: str + status: EntityStatusesLiterals + credit_scores: Optional[List[EntityCreditScoresType]] + error: Optional[ResourceError] + created_at: str + updated_at: str + + +class EntityCreditScoresResource(Resource): + def __init__(self, config: Configuration): + super(EntityCreditScoresResource, self).__init__(config.add_path('credit_scores')) + + def retrieve_credit_scores(self, crs_id: str) -> EntityCreditScores: + return super(EntityCreditScoresResource, self)._get_with_id(crs_id) + + def create_credit_scores(self) -> EntityCreditScores: + return super(EntityCreditScoresResource, self)._create({}) \ No newline at end of file diff --git a/method/resources/Entity.py b/method/resources/Entity/Entity.py similarity index 81% rename from method/resources/Entity.py rename to method/resources/Entity/Entity.py index b55037c..3d6dfba 100644 --- a/method/resources/Entity.py +++ b/method/resources/Entity/Entity.py @@ -3,6 +3,7 @@ from method.resource import Resource, RequestOpts from method.configuration import Configuration from method.errors import ResourceError +from method.resources.Entity import EntityConnectResource, EntityCreditScoresResource, EntityIdentityResource, EntitySyncResource, EntityVerificationSessionResource EntityTypesLiterals = Literal[ @@ -55,12 +56,6 @@ ] -CreditScoresModelLiterals = Literal[ - 'vantage_4', - 'vantage_3' -] - - CreditReportBureausLiterals = Literal[ 'experian', 'equifax', @@ -186,47 +181,6 @@ class EntityCreditScoresFactorsType(TypedDict): description: str -class EntityCreditScoresType(TypedDict): - score: int - source: CreditReportBureausLiterals - model: CreditScoresModelLiterals - factors: List[EntityCreditScoresFactorsType] - created_at: str - factors: EntityCreditScoresFactorsType - created_at: str - - -class EntityCreditScoresResponse(TypedDict): - id: str - status: EntityStatusesLiterals - scores: Optional[List[EntityCreditScoresType]] - error: Optional[ResourceError] - created_at: str - updated_at: str - - -class EntityCreditScoresFactorsType(TypedDict): - code: str - description: str - - -class EntityCreditScoresType(TypedDict): - score: int - source: CreditReportBureausLiterals - model: str - factors: List[EntityCreditScoresFactorsType] - created_at: str - - -class EntityCreditScoresResponse(TypedDict): - id: str - status: EntityStatusesLiterals - credit_scores: Optional[List[EntityCreditScoresType]] - error: Optional[ResourceError] - created_at: str - updated_at: str - - class AnswerOpts(TypedDict): question_id: str answer_id: str @@ -305,17 +259,35 @@ class EntitySensitiveResponse(TypedDict): identities: List[EntityIdentity] +class EntitySubResources: + connect: EntityConnectResource + credit_scores: EntityCreditScoresResource + identities: EntityIdentityResource + syncs: EntitySyncResource + verification_sessions: EntityVerificationSessionResource + + def __init__(self, _id: str, config: Configuration): + self.connect = EntityConnectResource(config.add_path(_id)) + self.credit_scores = EntityCreditScoresResource(config.add_path(_id)) + self.identities = EntityIdentityResource(config.add_path(_id)) + self.syncs = EntitySyncResource(config.add_path(_id)) + self.verification_sessions = EntityVerificationSessionResource(config.add_path(_id)) + + class EntityResource(Resource): def __init__(self, config: Configuration): super(EntityResource, self).__init__(config.add_path('entities')) + def __call__(self, _id: str) -> EntitySubResources: + return EntitySubResources(_id, self.config) + def create(self, opts: EntityCreateOpts, request_opts: Optional[RequestOpts] = None) -> Entity: return super(EntityResource, self)._create(opts, request_opts) def update(self, _id: str, opts: EntityCreateOpts) -> Entity: return super(EntityResource, self)._update_with_id(_id, opts) - def get(self, _id: str) -> Entity: + def retrieve(self, _id: str) -> Entity: return super(EntityResource, self)._get_with_id(_id) def list(self, params: EntityListOpts = None) -> List[Entity]: @@ -324,15 +296,9 @@ def list(self, params: EntityListOpts = None) -> List[Entity]: def create_auth_session(self, _id: str) -> EntityQuestionResponse: return super(EntityResource, self)._create_with_sub_path('{_id}/auth_session'.format(_id=_id), {}) - def get_credit_score(self, _id: str) -> EntityQuestionResponse: + def retrieve_credit_score(self, _id: str) -> EntityQuestionResponse: return super(EntityResource, self)._get_with_sub_path('{_id}/credit_score'.format(_id=_id)) - def get_credit_scores(self, _id: str, crs_id: str) -> EntityCreditScoresResponse: - return super(EntityResource, self)._get_with_sub_path('{_id}/credit_scores/{crs_id}'.format(_id=_id, crs_id=crs_id)) - - def create_credit_scores(self, _id: str) -> EntityCreditScoresResponse: - return super(EntityResource, self)._create_with_sub_path('{_id}/credit_scores'.format(_id=_id), {}) - def update_auth_session(self, _id: str, opts: EntityUpdateAuthOpts) -> EntityUpdateAuthResponse: return super(EntityResource, self)._update_with_sub_path('{_id}/auth_session'.format(_id=_id), opts) @@ -345,9 +311,6 @@ def update_manual_auth_session(self, _id: str, opts: EntityManualAuthOpts) -> En def refresh_capabilities(self, _id: str) -> Entity: return super(EntityResource, self)._create_with_sub_path('{_id}/refresh_capabilities'.format(_id=_id), {}) - def get_credit_score(self, _id: str) -> EntityQuestionResponse: - return super(EntityResource, self)._get_with_sub_path('{_id}/credit_score'.format(_id=_id)) - def get_sensitive_fields(self, _id: str, fields: List[EntitySensitiveFieldsLiterals]) -> EntitySensitiveResponse: return super(EntityResource, self)._get_with_sub_path_and_params( '{_id}/sensitive'.format(_id=_id), diff --git a/method/resources/Entity/Identities.py b/method/resources/Entity/Identities.py new file mode 100644 index 0000000..f8344d1 --- /dev/null +++ b/method/resources/Entity/Identities.py @@ -0,0 +1,34 @@ +from typing import TypedDict, Optional, Literal, List + +from method.resource import Resource +from method.configuration import Configuration +from method.errors import ResourceError +from method.resources.Entity.Entity import EntityIdentity + + +EntityVerificationSessionStatusLiterals = Literal[ + 'completed', + 'in_progress', + 'pending', + 'failed' +] + + +class EntityIdentity(TypedDict): + id: str + status: EntityVerificationSessionStatusLiterals + identities: List[EntityIdentity] + error: Optional[ResourceError] + created_at: str + updated_at: str + + +class EntityIdentityResource(Resource): + def __init__(self, config: Configuration): + super(EntityIdentityResource, self).__init__(config.add_path('identities')) + + def retrieve(self, identity_id: str) -> EntityIdentity: + return super(EntityIdentityResource, self)._get_with_id(identity_id) + + def create(self, opts: Optional[EntityIdentity] = {}) -> EntityIdentity: + return super(EntityIdentityResource, self)._create(opts) \ No newline at end of file diff --git a/method/resources/Entity/Syncs.py b/method/resources/Entity/Syncs.py new file mode 100644 index 0000000..1f52818 --- /dev/null +++ b/method/resources/Entity/Syncs.py @@ -0,0 +1,36 @@ +from typing import TypedDict, Optional, Literal + +from method.resource import Resource +from method.configuration import Configuration +from method.errors import ResourceError + + +EntitySyncTypeLiterals = Literal[ + 'capabilities', + 'accounts' +] + + +class EntitySync(TypedDict): + id: str + acc_id: str + status: str + type: str + error: Optional[ResourceError] + created_at: str + updated_at: str + + +class EntitySyncCreateOpts(TypedDict): + type: EntitySyncTypeLiterals + + +class EntitySyncResource(Resource): + def __init__(self, config: Configuration): + super(EntitySyncResource, self).__init__(config.add_path('syncs')) + + def retrieve(self, acc_sync_id: str) -> EntitySync: + return super(EntitySyncResource, self)._get_with_id(acc_sync_id) + + def create(self, opts: Optional[EntitySyncCreateOpts] = {}) -> EntitySync: + return super(EntitySyncResource, self)._create(opts) diff --git a/method/resources/Entity/VerificationSessions.py b/method/resources/Entity/VerificationSessions.py new file mode 100644 index 0000000..3c8126a --- /dev/null +++ b/method/resources/Entity/VerificationSessions.py @@ -0,0 +1,113 @@ +from typing import TypedDict, Optional, List, Literal + +from method.resource import Resource +from method.configuration import Configuration +from method.errors import ResourceError + + +EntityVerificationSessionStatusLiterals = Literal[ + 'completed', + 'in_progress', + 'pending', + 'failed' +] + + +EntityVerificationSessionTypeLiterals = Literal[ + 'phone_method_sms', + 'phone_method_sna', + 'phone_byo_sms', + 'identity_byo_kyc', + 'identity_method_kba', + 'identity_method_auth_element' +] + + +EntityVerificationSessionCategoryLiterals = Literal[ + 'phone', + 'identity' +] + + +class EntityPhoneSmsVerification(TypedDict): + timestamp: str + + +class EntityPhoneSmsVerificationUpdate(TypedDict): + sms_code: str + + +class EntityPhoneSnaVerification(TypedDict): + urls: str + + +class EntityByoKycVerification(TypedDict): + authenticated: bool + + +class EntityKbaVerificationAnswer(TypedDict): + id: str + text: str + + +class EntityKbaVerificationAnswerUpdate(TypedDict): + question_id: str + answer_id: str + + +class EntityKbaVerificationQuestion(TypedDict): + selected_answer: Optional[str] + id: str + text: str + answers: List[EntityKbaVerificationAnswer] + + +class EntityKbaVerification(TypedDict): + questions: List[EntityKbaVerificationQuestion] + authenticated: bool + + +class EntityVerificationSessionCreateOpts(TypedDict): + type: EntityVerificationSessionTypeLiterals + phone_method_sms: Optional[object] + phone_method_sna: Optional[object] + phone_byo_sms: Optional[EntityPhoneSmsVerification] + identity_byo_kyc: Optional[object] + identity_method_kba: Optional[object] + + +class EntityVerificationSessionUpdateOpts(TypedDict): + type: EntityVerificationSessionTypeLiterals + phone_method_sms: Optional[EntityPhoneSmsVerificationUpdate] + phone_method_sma: Optional[object] + identity_method_kba: Optional[EntityKbaVerificationAnswerUpdate] + + +class EntityVerificationSession(TypedDict): + id: str + entity_id: str + status: EntityVerificationSessionStatusLiterals + type: EntityVerificationSessionTypeLiterals + category: EntityVerificationSessionCategoryLiterals + phone_method_sms: Optional[EntityPhoneSmsVerification] + phone_method_sna: Optional[EntityPhoneSnaVerification] + phone_byo_sms: Optional[EntityPhoneSmsVerification] + identity_byo_kyc: Optional[EntityByoKycVerification] + identity_method_kba: Optional[EntityKbaVerification] + error: Optional[ResourceError] + created_at: str + updated_at: str + + +class EntityVerificationSessionResource(Resource): + def __init__(self, config: Configuration): + super(EntityVerificationSessionResource, self).__init__(config.add_path('verification_sessions')) + + def retrieve(self, verification_session_id: str) -> EntityVerificationSession: + return super(EntityVerificationSessionResource, self)._get_with_id(verification_session_id) + + def create(self, opts: Optional[EntityVerificationSessionCreateOpts] = {}) -> EntityVerificationSession: + return super(EntityVerificationSessionResource, self)._create(opts) + + def update(self, verification_session_id: str, opts: EntityVerificationSessionUpdateOpts) -> EntityVerificationSession: + return super(EntityVerificationSessionResource, self)._update_with_id(verification_session_id, opts) diff --git a/method/resources/Entity/__init__.py b/method/resources/Entity/__init__.py new file mode 100644 index 0000000..1d08768 --- /dev/null +++ b/method/resources/Entity/__init__.py @@ -0,0 +1,6 @@ +from method.resources.Entity.Entity import Entity, EntityResource +from method.resources.Entity.Syncs import EntitySync, EntitySyncResource +from method.resources.Entity.CreditScores import EntityCreditScores, EntityCreditScoresResource +from method.resources.Entity.Identities import EntityIdentity, EntityIdentityResource +from method.resources.Entity.Connect import EntityConnect, EntityConnectResource +from method.resources.Entity.VerificationSessions import EntityVerificationSession, EntityVerificationSessionResource \ No newline at end of file diff --git a/method/resources/__init__.py b/method/resources/__init__.py index 01ae365..8e6a6a4 100644 --- a/method/resources/__init__.py +++ b/method/resources/__init__.py @@ -1,9 +1,10 @@ # type: ignore -from method.resources.Account import Account, AccountResource -from method.resources.AccountSync import AccountSyncResource +from method.resources.Account import Account, AccountResource, AccountPayoffsResource, \ + AccountSyncResource, AccountVerificationSessionResource from method.resources.Bin import Bin, BinResource from method.resources.Element import Element, ElementResource -from method.resources.Entity import Entity, EntityResource +from method.resources.Entity import Entity, EntityResource, EntityConnectResource, \ + EntityCreditScoresResource, EntityIdentityResource, EntitySyncResource, EntityVerificationSessionResource from method.resources.Merchant import Merchant, MerchantResource from method.resources.Payment import Payment, PaymentResource from method.resources.Report import Report, ReportResource From 4592b5177f150f54487b38af8d6027d5f1157e9a Mon Sep 17 00:00:00 2001 From: Mike Ossig Date: Mon, 8 Apr 2024 10:39:51 -0400 Subject: [PATCH 03/22] changed all gets to retrieve --- method/resources/Bin.py | 2 +- method/resources/Element.py | 2 +- method/resources/Entity/Entity.py | 2 +- method/resources/HealthCheck.py | 2 +- method/resources/Merchant.py | 2 +- method/resources/Payment.py | 2 +- method/resources/Report.py | 2 +- method/resources/Reversal.py | 2 +- method/resources/RoutingNumber.py | 2 +- method/resources/Transaction.py | 2 +- method/resources/Verification.py | 4 ++-- method/resources/Webhook.py | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/method/resources/Bin.py b/method/resources/Bin.py index 9bf7ce3..2a51794 100644 --- a/method/resources/Bin.py +++ b/method/resources/Bin.py @@ -33,5 +33,5 @@ class BinResource(Resource): def __init__(self, config: Configuration): super(BinResource, self).__init__(config.add_path('bins')) - def get(self, _id: str) -> Bin: + def retrieve(self, _id: str) -> Bin: return super(BinResource, self)._get_with_params({'bin': _id}) diff --git a/method/resources/Element.py b/method/resources/Element.py index 958a756..c2c0880 100644 --- a/method/resources/Element.py +++ b/method/resources/Element.py @@ -90,7 +90,7 @@ def __init__(self, config: Configuration): def create_token(self, opts: ElementTokenCreateOpts) -> Element: return super(ElementResource, self)._create_with_sub_path('token', opts) - def get_session_results(self, _id: str) -> TokenSessionResult: + def retrieve_session_results(self, _id: str) -> TokenSessionResult: return super(ElementResource, self)._get_with_sub_path('token/{_id}/results'.format(_id=_id)) def exchange_public_account_token(self, opts: ElementExchangePublicAccountOpts) -> Account: diff --git a/method/resources/Entity/Entity.py b/method/resources/Entity/Entity.py index 3d6dfba..8de94e4 100644 --- a/method/resources/Entity/Entity.py +++ b/method/resources/Entity/Entity.py @@ -311,7 +311,7 @@ def update_manual_auth_session(self, _id: str, opts: EntityManualAuthOpts) -> En def refresh_capabilities(self, _id: str) -> Entity: return super(EntityResource, self)._create_with_sub_path('{_id}/refresh_capabilities'.format(_id=_id), {}) - def get_sensitive_fields(self, _id: str, fields: List[EntitySensitiveFieldsLiterals]) -> EntitySensitiveResponse: + def retrieve_sensitive_fields(self, _id: str, fields: List[EntitySensitiveFieldsLiterals]) -> EntitySensitiveResponse: return super(EntityResource, self)._get_with_sub_path_and_params( '{_id}/sensitive'.format(_id=_id), {'fields[]': fields}, diff --git a/method/resources/HealthCheck.py b/method/resources/HealthCheck.py index 1388410..d670cd0 100644 --- a/method/resources/HealthCheck.py +++ b/method/resources/HealthCheck.py @@ -14,5 +14,5 @@ class HealthCheckResource(Resource): def __init__(self, config: Configuration): super(HealthCheckResource, self).__init__(config.add_path('ping')) - def get(self) -> PingResponse: + def retrieve(self) -> PingResponse: return super(HealthCheckResource, self)._get_raw() diff --git a/method/resources/Merchant.py b/method/resources/Merchant.py index 2fbea0e..c3ec20b 100644 --- a/method/resources/Merchant.py +++ b/method/resources/Merchant.py @@ -60,7 +60,7 @@ class MerchantResource(Resource): def __init__(self, config: Configuration): super(MerchantResource, self).__init__(config.add_path('merchants')) - def get(self, _id: str) -> Merchant: + def retrieve(self, _id: str) -> Merchant: return super(MerchantResource, self)._get_with_id(_id) def list(self, opts: Optional[MerchantListOpts] = None) -> List[Merchant]: diff --git a/method/resources/Payment.py b/method/resources/Payment.py index 404735a..e526b30 100644 --- a/method/resources/Payment.py +++ b/method/resources/Payment.py @@ -111,7 +111,7 @@ def __init__(self, config: Configuration): def __call__(self, _id: str) -> PaymentSubResources: return PaymentSubResources(_id, self.config) - def get(self, _id: str) -> Payment: + def retrieve(self, _id: str) -> Payment: return super(PaymentResource, self)._get_with_id(_id) def list(self, params: Optional[PaymentListOpts] = None) -> List[Payment]: diff --git a/method/resources/Report.py b/method/resources/Report.py index cae5593..7d7231d 100644 --- a/method/resources/Report.py +++ b/method/resources/Report.py @@ -44,7 +44,7 @@ class ReportResource(Resource): def __init__(self, config: Configuration): super(ReportResource, self).__init__(config.add_path('reports')) - def get(self, _id: str) -> Report: + def retrieve(self, _id: str) -> Report: return super(ReportResource, self)._get_with_id(_id) def create(self, opts: ReportCreateOpts, request_opts: Optional[RequestOpts] = None) -> Report: diff --git a/method/resources/Reversal.py b/method/resources/Reversal.py index ae4deff..e35f930 100644 --- a/method/resources/Reversal.py +++ b/method/resources/Reversal.py @@ -43,7 +43,7 @@ class ReversalResource(Resource): def __init__(self, config: Configuration): super(ReversalResource, self).__init__(config.add_path('reversals')) - def get(self, _id: str) -> Reversal: + def retrieve(self, _id: str) -> Reversal: return super(ReversalResource, self)._get_with_id(_id) def update(self, _id: str, opts: ReversalUpdateOpts) -> Reversal: diff --git a/method/resources/RoutingNumber.py b/method/resources/RoutingNumber.py index 220f65b..29fdbd1 100644 --- a/method/resources/RoutingNumber.py +++ b/method/resources/RoutingNumber.py @@ -33,5 +33,5 @@ class RoutingNumberResource(Resource): def __init__(self, config: Configuration): super(RoutingNumberResource, self).__init__(config.add_path('routing_numbers')) - def get(self, routing_number: str) -> RoutingNumber: + def retrieve(self, routing_number: str) -> RoutingNumber: return super(RoutingNumberResource, self)._get_with_params({'routing_number': routing_number}) diff --git a/method/resources/Transaction.py b/method/resources/Transaction.py index 4c52c10..98f82ae 100644 --- a/method/resources/Transaction.py +++ b/method/resources/Transaction.py @@ -26,5 +26,5 @@ def __init__(self, config: Configuration): def list(self) -> List[Transaction]: return super(TransactionResource, self)._list(None) - def get(self, _id: str) -> Transaction: + def retrieve(self, _id: str) -> Transaction: return super(TransactionResource, self)._get_with_id(_id) diff --git a/method/resources/Verification.py b/method/resources/Verification.py index c5083ab..a471406 100644 --- a/method/resources/Verification.py +++ b/method/resources/Verification.py @@ -73,7 +73,7 @@ class VerificationResource(Resource): def __init__(self, config: Configuration): super(VerificationResource, self).__init__(config.add_path('verification')) - def get(self) -> Verification: + def retrieve(self) -> Verification: return super(VerificationResource, self)._get() def update(self, opts: VerificationUpdateOpts) -> Verification: @@ -82,5 +82,5 @@ def update(self, opts: VerificationUpdateOpts) -> Verification: def create(self, opts: VerificationCreateOpts) -> Verification: return super(VerificationResource, self)._create(opts) - def get_test_amounts(self) -> VerificationTestAmountsResponse: + def retrieve_test_amounts(self) -> VerificationTestAmountsResponse: return super(VerificationResource, self)._get_with_sub_path('amounts') diff --git a/method/resources/Webhook.py b/method/resources/Webhook.py index ecd2740..642f0d9 100644 --- a/method/resources/Webhook.py +++ b/method/resources/Webhook.py @@ -50,7 +50,7 @@ class WebhookResource(Resource): def __init__(self, config: Configuration): super(WebhookResource, self).__init__(config.add_path('webhooks')) - def get(self, _id: str) -> Webhook: + def retrieve(self, _id: str) -> Webhook: return super(WebhookResource, self)._get_with_id(_id) def delete(self, _id: str) -> Webhook: From 5e712c7fcc8d1259e4217897e450e880ace1bd2d Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Tue, 9 Apr 2024 14:23:36 -0400 Subject: [PATCH 04/22] adds Balances to Account --- method/resources/Account/Account.py | 5 ++++- method/resources/Account/Balances.py | 30 ++++++++++++++++++++++++++++ method/resources/Account/__init__.py | 3 ++- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 method/resources/Account/Balances.py diff --git a/method/resources/Account/Account.py b/method/resources/Account/Account.py index b0b6b58..821dbe4 100644 --- a/method/resources/Account/Account.py +++ b/method/resources/Account/Account.py @@ -4,7 +4,8 @@ from method.configuration import Configuration from method.errors import ResourceError from method.resources.Verification import VerificationResource -from method.resources.Account import AccountSyncResource, AccountSync, AccountVerificationSessionResource, AccountPayoffsResource +from method.resources.Account import AccountSyncResource, AccountSync, \ + AccountVerificationSessionResource, AccountPayoffsResource, AccountBalancesResource # Literals, keep ordered alphabetically @@ -478,6 +479,7 @@ class AccountSubResources: syncs: AccountSyncResource payoffs: AccountPayoffsResource verification_sessions: AccountVerificationSessionResource + balances: AccountBalancesResource def __init__(self, _id: str, config: Configuration): @@ -485,6 +487,7 @@ def __init__(self, _id: str, config: Configuration): self.syncs = AccountSyncResource(config.add_path(_id)) self.payoffs = AccountPayoffsResource(config.add_path(_id)) self.verification_sessions = AccountVerificationSessionResource(config.add_path(_id)) + self.balances = AccountBalancesResource(config.add_path(_id)) class AccountResource(Resource): diff --git a/method/resources/Account/Balances.py b/method/resources/Account/Balances.py new file mode 100644 index 0000000..0ba7d69 --- /dev/null +++ b/method/resources/Account/Balances.py @@ -0,0 +1,30 @@ +from typing import TypedDict, Optional, Literal + +from method.resource import Resource +from method.configuration import Configuration +from method.errors import ResourceError + +AccountBalanceStatusLiterals = Literal[ + 'completed', + 'in_progress', + 'pending', + 'failed' +] + +class AccountBalances(TypedDict): + id: str + status: AccountBalanceStatusLiterals + balance: Optional[int] + error: Optional[ResourceError] + created_at: str + updated_at: str + +class AccountBalancesResource(Resource): + def __init__(self, config: Configuration): + super(AccountBalancesResource, self).__init__(config.add_path('balances')) + + def retrieve(self, bal_id: str) -> AccountBalances: + return super(AccountBalancesResource, self)._get_with_id(bal_id) + + def create(self) -> AccountBalances: + return super(AccountBalancesResource, self)._create({}) \ No newline at end of file diff --git a/method/resources/Account/__init__.py b/method/resources/Account/__init__.py index 628149e..03c0864 100644 --- a/method/resources/Account/__init__.py +++ b/method/resources/Account/__init__.py @@ -1,4 +1,5 @@ from method.resources.Account.Account import Account, AccountResource from method.resources.Account.Syncs import AccountSync, AccountSyncResource from method.resources.Account.Payoffs import AccountPayoff, AccountPayoffsResource -from method.resources.Account.VerificationSessions import AccountVerificationSession, AccountVerificationSessionResource \ No newline at end of file +from method.resources.Account.VerificationSessions import AccountVerificationSession, AccountVerificationSessionResource +from method.resources.Account.Balances import AccountBalance, AccountBalancesResource \ No newline at end of file From 1ea4b7619248b94372e8cde457b54e136aea03d2 Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Wed, 24 Apr 2024 16:37:49 -0400 Subject: [PATCH 05/22] added the rest of the products for entities and accounts, general file structure clean up --- method/method.py | 17 +--- method/resource.py | 17 +++- method/resources/Account/Syncs.py | 29 ------ method/resources/Account/__init__.py | 5 -- .../{Account => Accounts}/Account.py | 89 ++++--------------- .../{Account => Accounts}/Balances.py | 0 method/resources/Accounts/Cards.py | 36 ++++++++ .../{Account => Accounts}/ExternalTypes.py | 0 .../{Account => Accounts}/Payoffs.py | 0 method/resources/Accounts/Sensitive.py | 43 +++++++++ method/resources/Accounts/Subscriptions.py | 40 +++++++++ method/resources/Accounts/Transactions.py | 69 ++++++++++++++ method/resources/Accounts/Updates.py | 44 +++++++++ .../VerificationSessions.py | 2 +- method/resources/Accounts/__init__.py | 9 ++ method/resources/Bin.py | 37 -------- .../resources/{Entity => Entities}/Connect.py | 0 .../{Entity => Entities}/CreditScores.py | 2 +- .../resources/{Entity => Entities}/Entity.py | 73 +++------------ .../{Entity => Entities}/Identities.py | 2 +- method/resources/Entities/Products.py | 40 +++++++++ method/resources/Entities/Sensitive.py | 29 ++++++ method/resources/Entities/Subscriptions.py | 57 ++++++++++++ method/resources/Entities/Types.py | 17 ++++ .../VerificationSessions.py | 0 method/resources/Entities/__init__.py | 8 ++ method/resources/Entity/Syncs.py | 36 -------- method/resources/Entity/__init__.py | 6 -- method/resources/Merchant.py | 3 + method/resources/{ => Payments}/Payment.py | 11 +-- method/resources/{ => Payments}/Reversal.py | 0 method/resources/Payments/__init__.py | 2 + method/resources/RoutingNumber.py | 37 -------- .../Payments.py} | 2 +- method/resources/{ => Simulate}/Simulate.py | 5 +- method/resources/Simulate/Transactions.py | 10 +++ method/resources/Simulate/__init__.py | 3 + method/resources/Transaction.py | 30 ------- method/resources/Verification.py | 86 ------------------ method/resources/__init__.py | 16 ++-- 40 files changed, 473 insertions(+), 439 deletions(-) delete mode 100644 method/resources/Account/Syncs.py delete mode 100644 method/resources/Account/__init__.py rename method/resources/{Account => Accounts}/Account.py (82%) rename method/resources/{Account => Accounts}/Balances.py (100%) create mode 100644 method/resources/Accounts/Cards.py rename method/resources/{Account => Accounts}/ExternalTypes.py (100%) rename method/resources/{Account => Accounts}/Payoffs.py (100%) create mode 100644 method/resources/Accounts/Sensitive.py create mode 100644 method/resources/Accounts/Subscriptions.py create mode 100644 method/resources/Accounts/Transactions.py create mode 100644 method/resources/Accounts/Updates.py rename method/resources/{Account => Accounts}/VerificationSessions.py (97%) create mode 100644 method/resources/Accounts/__init__.py delete mode 100644 method/resources/Bin.py rename method/resources/{Entity => Entities}/Connect.py (100%) rename method/resources/{Entity => Entities}/CreditScores.py (92%) rename method/resources/{Entity => Entities}/Entity.py (70%) rename method/resources/{Entity => Entities}/Identities.py (94%) create mode 100644 method/resources/Entities/Products.py create mode 100644 method/resources/Entities/Sensitive.py create mode 100644 method/resources/Entities/Subscriptions.py create mode 100644 method/resources/Entities/Types.py rename method/resources/{Entity => Entities}/VerificationSessions.py (100%) create mode 100644 method/resources/Entities/__init__.py delete mode 100644 method/resources/Entity/Syncs.py delete mode 100644 method/resources/Entity/__init__.py rename method/resources/{ => Payments}/Payment.py (91%) rename method/resources/{ => Payments}/Reversal.py (100%) create mode 100644 method/resources/Payments/__init__.py delete mode 100644 method/resources/RoutingNumber.py rename method/resources/{SimulatePayment.py => Simulate/Payments.py} (87%) rename method/resources/{ => Simulate}/Simulate.py (58%) create mode 100644 method/resources/Simulate/Transactions.py create mode 100644 method/resources/Simulate/__init__.py delete mode 100644 method/resources/Transaction.py delete mode 100644 method/resources/Verification.py diff --git a/method/method.py b/method/method.py index 8abe147..2b626d5 100644 --- a/method/method.py +++ b/method/method.py @@ -1,48 +1,39 @@ from method.configuration import Configuration, ConfigurationOpts -from method.resources.Account import AccountResource -from method.resources.Bin import BinResource -from method.resources.Entity import EntityResource +from method.resources.Accounts import AccountResource +from method.resources.Entities import EntityResource from method.resources.Element import ElementResource from method.resources.Merchant import MerchantResource -from method.resources.Payment import PaymentResource +from method.resources.Payments import PaymentResource from method.resources.Report import ReportResource -from method.resources.RoutingNumber import RoutingNumberResource from method.resources.Webhook import WebhookResource from method.resources.HealthCheck import PingResponse, HealthCheckResource from method.resources.Simulate import SimulateResource -from method.resources.Transaction import TransactionResource class Method: accounts: AccountResource - bins: BinResource entities: EntityResource elements: ElementResource merchants: MerchantResource payments: PaymentResource reports: ReportResource - routing_numbers: RoutingNumberResource webhooks: WebhookResource healthcheck: HealthCheckResource simulate: SimulateResource - transaction: TransactionResource def __init__(self, opts: ConfigurationOpts = None, **kwargs: ConfigurationOpts): _opts: ConfigurationOpts = {**(opts or {}), **kwargs} # type: ignore config = Configuration(_opts) self.accounts = AccountResource(config) - self.bins = BinResource(config) self.entities = EntityResource(config) self.elements = ElementResource(config) self.merchants = MerchantResource(config) self.payments = PaymentResource(config) self.reports = ReportResource(config) - self.routing_numbers = RoutingNumberResource(config) self.webhooks = WebhookResource(config) self.healthcheck = HealthCheckResource(config) self.simulate = SimulateResource(config) - self.transaction = TransactionResource(config) def ping(self) -> PingResponse: - return self.healthcheck.get() + return self.healthcheck.retrieve() diff --git a/method/resource.py b/method/resource.py index aeb916b..f6783c3 100644 --- a/method/resource.py +++ b/method/resource.py @@ -1,14 +1,28 @@ from importlib.metadata import version import json -from typing import Optional, List, Dict, Any, TypedDict +from typing import Optional, List, Dict, Any, TypedDict, Literal from hammock import Hammock as Client # type: ignore from method.configuration import Configuration from method.errors import MethodError +ResourceStatusLiterals = Literal[ + 'completed', + 'in_progress', + 'pending', + 'failed' +] + class RequestOpts(TypedDict): idempotency_key: Optional[str] +class ResourceListOpts(TypedDict): + from_date: Optional[str] + to_date: Optional[str] + page: Optional[int | str] + page_limit: Optional[int | str] + page_cursor: Optional[int | str] + class Resource: config: Configuration @@ -20,6 +34,7 @@ def __init__(self, config: Configuration): 'Authorization': 'Bearer {token}'.format(token=config.api_key), 'Content-Type': 'application/json', 'User-Agent': 'Method-Python/v{version}'.format(version=version('method-python')), + 'method-version': '2024-04-04' }) @MethodError.catch diff --git a/method/resources/Account/Syncs.py b/method/resources/Account/Syncs.py deleted file mode 100644 index f841230..0000000 --- a/method/resources/Account/Syncs.py +++ /dev/null @@ -1,29 +0,0 @@ -from typing import TypedDict, Optional - -from method.resource import Resource -from method.configuration import Configuration -from method.errors import ResourceError - - -class AccountSync(TypedDict): - id: str - acc_id: str - status: str - error: Optional[ResourceError] - created_at: str - updated_at: str - - -class AccountSyncCreateOpts(TypedDict): - pass - - -class AccountSyncResource(Resource): - def __init__(self, config: Configuration): - super(AccountSyncResource, self).__init__(config.add_path('syncs')) - - def retrieve(self, _id: str) -> AccountSync: - return super(AccountSyncResource, self)._get_with_id(_id) - - def create(self, opts: Optional[AccountSyncCreateOpts] = {}) -> AccountSync: - return super(AccountSyncResource, self)._create(opts) diff --git a/method/resources/Account/__init__.py b/method/resources/Account/__init__.py deleted file mode 100644 index 03c0864..0000000 --- a/method/resources/Account/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from method.resources.Account.Account import Account, AccountResource -from method.resources.Account.Syncs import AccountSync, AccountSyncResource -from method.resources.Account.Payoffs import AccountPayoff, AccountPayoffsResource -from method.resources.Account.VerificationSessions import AccountVerificationSession, AccountVerificationSessionResource -from method.resources.Account.Balances import AccountBalance, AccountBalancesResource \ No newline at end of file diff --git a/method/resources/Account/Account.py b/method/resources/Accounts/Account.py similarity index 82% rename from method/resources/Account/Account.py rename to method/resources/Accounts/Account.py index 821dbe4..a5ff371 100644 --- a/method/resources/Account/Account.py +++ b/method/resources/Accounts/Account.py @@ -3,9 +3,9 @@ from method.resource import Resource, RequestOpts from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Verification import VerificationResource -from method.resources.Account import AccountSyncResource, AccountSync, \ - AccountVerificationSessionResource, AccountPayoffsResource, AccountBalancesResource +from method.resources.Accounts import AccountBalancesResource, AccountCardsResource, AccountPayoffsResource, \ + AccountSensitiveResource, AccountSubscriptionsResource, AccountTransactionsResource, AccountUpdatesResource, \ + AccountVerificationSessionResource # Literals, keep ordered alphabetically @@ -384,19 +384,6 @@ class Account(TypedDict): metadata: Optional[Dict[str, Any]] -class AccountDetail(TypedDict): - id: str - type: AccountTypesLiterals - aggregator: Optional[str] - name: str - institution_name: str - institution_logo: str - mask: str - created_at: str - updated_at: str - metadata: Optional[Dict[str, Any]] - - AccountListOpts = TypedDict('AccountListOpts', { 'to_date': Optional[str], 'from_date': Optional[str], @@ -411,34 +398,6 @@ class AccountDetail(TypedDict): }) -class AccountCreateBulkSyncOpts(TypedDict): - acc_ids: List[str] - - -class AccountCreateBulkSyncResponse(TypedDict): - success: List[str] - failed: List[str] - results: List[AccountSync] - - -class AccountSensitive(TypedDict): - number: Optional[str] - encrypted_number: Optional[str] - bin_4: Optional[str] - bin_6: Optional[str] - payment_address: Optional[Any] - - -class AccountCreateBulkSensitiveResponse(TypedDict): - success: List[str] - failed: List[str] - results: List[AccountSensitive] - - -class AccountCreateBulkSensitiveOpts(TypedDict): - acc_ids: List[str] - - class AccountWithdrawConsentOpts(TypedDict): type: Literal['withdraw'] reason: Optional[Literal['holder_withdrew_consent']] @@ -475,19 +434,25 @@ class AccountPaymentHistory(TypedDict): class AccountSubResources: - verification: VerificationResource - syncs: AccountSyncResource + balances: AccountBalancesResource + cards: AccountCardsResource payoffs: AccountPayoffsResource + sensitive: AccountSensitiveResource + subscriptions: AccountSubscriptionsResource + transactions: AccountTransactionsResource + updates: AccountUpdatesResource verification_sessions: AccountVerificationSessionResource - balances: AccountBalancesResource def __init__(self, _id: str, config: Configuration): - self.verification = VerificationResource(config.add_path(_id)) - self.syncs = AccountSyncResource(config.add_path(_id)) + self.balances = AccountBalancesResource(config.add_path(_id)) + self.cards = AccountCardsResource(config.add_path(_id)) self.payoffs = AccountPayoffsResource(config.add_path(_id)) + self.sensitive = AccountSensitiveResource(config.add_path(_id)) + self.subscriptions = AccountSubscriptionsResource(config.add_path(_id)) + self.transactions = AccountTransactionsResource(config.add_path(_id)) + self.updates = AccountUpdatesResource(config.add_path(_id)) self.verification_sessions = AccountVerificationSessionResource(config.add_path(_id)) - self.balances = AccountBalancesResource(config.add_path(_id)) class AccountResource(Resource): @@ -509,30 +474,6 @@ def list(self, params: Optional[AccountListOpts] = None) -> List[Account]: def create(self, opts: Union[AccountACHCreateOpts, AccountLiabilityCreateOpts, AccountClearingCreateOpts], request_opts: Optional[RequestOpts] = None) -> Account: return super(AccountResource, self)._create(opts, request_opts) - def retrieve_payment_history(self, _id: str) -> AccountPaymentHistory: - return super(AccountResource, self)._get_with_sub_path('{_id}/payment_history'.format(_id=_id)) - - def retrieve_details(self, _id: str) -> AccountDetail: - return super(AccountResource, self)._get_with_sub_path('{_id}/details'.format(_id=_id)) - - def bulk_sync(self, acc_ids: AccountCreateBulkSyncOpts) -> AccountCreateBulkSyncResponse: - return super(AccountResource, self)._create_with_sub_path('bulk_sync',{ acc_ids }) - - def sync(self, _id: str) -> AccountSync: - return super(AccountResource, self)._create_with_sub_path('{_id}/syncs'.format(_id=_id), {}) - - def bulk_sensitive(self, acc_ids: AccountCreateBulkSensitiveOpts) -> AccountCreateBulkSensitiveResponse: - return super(AccountResource, self)._create_with_sub_path('bulk_sensitive',{ acc_ids }) - - def sensitive(self, _id: str) -> AccountSensitive: - return super(AccountResource, self)._get_with_sub_path('{_id}/sensitive'.format(_id=_id)) - - def enroll_auto_syncs(self, _id: str) -> Account: - return super(AccountResource, self)._create_with_sub_path('{_id}/sync_enrollment'.format(_id=_id), {}) - - def unenroll_auto_syncs(self, _id: str) -> Account: - return super(AccountResource, self)._delete_with_sub_path('{_id}/sync_enrollment'.format(_id=_id)) - def withdraw_consent(self, _id: str, data: AccountWithdrawConsentOpts = { 'type': 'withdraw', 'reason': 'holder_withdrew_consent' }) -> Account: return super(AccountResource, self)._create_with_sub_path('{_id}/consent'.format(_id=_id), data) diff --git a/method/resources/Account/Balances.py b/method/resources/Accounts/Balances.py similarity index 100% rename from method/resources/Account/Balances.py rename to method/resources/Accounts/Balances.py diff --git a/method/resources/Accounts/Cards.py b/method/resources/Accounts/Cards.py new file mode 100644 index 0000000..0efe3bf --- /dev/null +++ b/method/resources/Accounts/Cards.py @@ -0,0 +1,36 @@ +from typing import TypedDict, Optional, Literal, List + +from method.resource import Resource +from method.configuration import Configuration +from method.errors import ResourceError + + +class AccountCardBrand(TypedDict): + art_id: str + url: str + name: str + + +class AccountCard(TypedDict): + id: str + account_id: str + network: str + issuer: str + last4: str + brands: List[AccountCardBrand] + status: Literal['completed', 'failed'] + shared: bool + error: Optional[ResourceError] + created_at: str + updated_at: str + + +class AccountCardsResource(Resource): + def __init__(self, config: Configuration): + super(AccountCardsResource, self).__init__(config.add_path('cards')) + + def retrieve(self, crd_id: str) -> AccountCard: + return super(AccountCardsResource, self)._get_with_id(crd_id) + + def _create(self) -> AccountCard: + return super(AccountCardsResource, self)._create({}) \ No newline at end of file diff --git a/method/resources/Account/ExternalTypes.py b/method/resources/Accounts/ExternalTypes.py similarity index 100% rename from method/resources/Account/ExternalTypes.py rename to method/resources/Accounts/ExternalTypes.py diff --git a/method/resources/Account/Payoffs.py b/method/resources/Accounts/Payoffs.py similarity index 100% rename from method/resources/Account/Payoffs.py rename to method/resources/Accounts/Payoffs.py diff --git a/method/resources/Accounts/Sensitive.py b/method/resources/Accounts/Sensitive.py new file mode 100644 index 0000000..1610dd9 --- /dev/null +++ b/method/resources/Accounts/Sensitive.py @@ -0,0 +1,43 @@ +from typing import TypedDict, Optional, Literal, List + +from method.resource import Resource +from method.configuration import Configuration +from method.errors import ResourceError + + +AccountBalanceSensitiveFeildsLiterals = Literal[ + 'number', + 'billing_zip_code', + 'exp_month', + 'exp_year', + 'cvv', +] + + +class AccountSensitive(TypedDict): + id: str + account_id: str + number: Optional[str] + exp_month: Optional[str] + exp_year: Optional[str] + cvv: Optional[str] + billing_zip_code: Optional[str] + status: Literal['completed', 'failed'] + error: Optional[ResourceError] + created_at: str + updated_at: str + + +class AccountSensitiveCreateOpts(TypedDict): + expand: List[AccountBalanceSensitiveFeildsLiterals] + + +class AccountSensitiveResource(Resource): + def __init__(self, config: Configuration): + super(AccountSensitiveResource, self).__init__(config.add_path('sensitive')) + + def retrieve(self, astv_id: str) -> AccountSensitive: + return super(AccountSensitiveResource, self)._get_with_id(astv_id) + + def create(self, data: AccountSensitiveCreateOpts) -> AccountSensitive: + return super(AccountSensitiveResource, self)._create(data) \ No newline at end of file diff --git a/method/resources/Accounts/Subscriptions.py b/method/resources/Accounts/Subscriptions.py new file mode 100644 index 0000000..ea9967e --- /dev/null +++ b/method/resources/Accounts/Subscriptions.py @@ -0,0 +1,40 @@ +from typing import TypedDict, Optional, Literal, List + +from method.resource import Resource +from method.configuration import Configuration + + +AccountSubscriptionTypesLiterals = Literal[ + 'transactions', + 'update', + 'update.snapshot' +] + + +class AccountSubscription(TypedDict): + id: str + name: AccountSubscriptionTypesLiterals + status: Literal['active'] + latest_transaction_id: str + created_at: str + updated_at: str + + +class AccountSubscriptionTransactions(TypedDict): + subscription: AccountSubscription + + +class AccountSubscriptionResponse(TypedDict): + transactions: Optional[AccountSubscriptionTransactions] + + +class AccountSubscriptionCreateOpts(TypedDict): + enroll: List[AccountSubscriptionTypesLiterals] + + +class AccountSubscriptionsResource(Resource): + def __init__(self, config: Configuration): + super(AccountSubscriptionsResource, self).__init__(config.add_path('subscriptions')) + + def create(self, data: AccountSubscriptionCreateOpts) -> AccountSubscription: + return super(AccountSubscriptionsResource, self)._create(data) \ No newline at end of file diff --git a/method/resources/Accounts/Transactions.py b/method/resources/Accounts/Transactions.py new file mode 100644 index 0000000..45868d0 --- /dev/null +++ b/method/resources/Accounts/Transactions.py @@ -0,0 +1,69 @@ +from typing import TypedDict, Optional, Literal, List + +from method.resource import Resource, ResourceListOpts +from method.configuration import Configuration +from method.errors import ResourceError + + +AccountCurrencyTypesLiterals = Literal[ + 'USD' +] + + +AccountTransactionStatusLiterals = Literal[ + 'cleared', + 'auth', + 'refund', + 'unknown' +] + + +class ProductBaseStatusHistoryItem(TypedDict): + status: AccountTransactionStatusLiterals + timestamp: str + metadata: Optional[dict] + + +class AccountTransactionMerchant(TypedDict): + name: str + category_code: str + city: str + state: str + country: str + acquirer_bin: str + acquirer_card_acceptor_id: str + + +class AccountTransactionNetworkData(TypedDict): + visa_merchant_id: Optional[str] + visa_merchant_name: Optional[str] + visa_store_id: Optional[str] + visa_store_name: Optional[str] + + +class AccountTransaction(TypedDict): + id: str + account_id: str + merchant: AccountTransactionMerchant + network: str + network_data: AccountTransactionNetworkData + amount: int + currency: AccountCurrencyTypesLiterals + billing_amount: int + billing_currency: AccountCurrencyTypesLiterals + status: AccountTransactionStatusLiterals + status_history: List[ProductBaseStatusHistoryItem] + error: Optional[ResourceError] + created_at: str + updated_at: str + + +class AccountTransactionsResource(Resource): + def __init__(self, config: Configuration): + super(AccountTransactionsResource, self).__init__(config.add_path('transactions')) + + def retrieve(self, txn_id: str) -> AccountTransaction: + return super(AccountTransactionsResource, self)._get_with_id(txn_id) + + def list(self, params: ResourceListOpts) -> List[AccountTransaction]: + return super(AccountTransactionsResource, self)._list(params) diff --git a/method/resources/Accounts/Updates.py b/method/resources/Accounts/Updates.py new file mode 100644 index 0000000..c2550f6 --- /dev/null +++ b/method/resources/Accounts/Updates.py @@ -0,0 +1,44 @@ +from typing import TypedDict, Optional, List + +from method.resource import Resource, ResourceListOpts, ResourceStatusLiterals +from method.configuration import Configuration +from method.errors import ResourceError +from method.resources.Accounts.Account import AccountLiabilityTypesLiterals, AccountLiabilityAutoLoan, AccountLiabilityCollection, \ + AccountLiabilityCreditCard, AccountLiabilityCreditBuilder, AccountLiabilityMortgage, AccountLiabilityStudentLoan, AccountLiabilityStudentLoans, \ + AccountLiabilityInsurance, AccountLiabilityLoan, AccountLiabilityMedical, AccountLiabilityPersonalLoan, AccountLiabilityUtility + + +class AccountUpdate(TypedDict): + id: str + status: ResourceStatusLiterals + account_id: str + type: AccountLiabilityTypesLiterals + auto_loan: Optional[AccountLiabilityAutoLoan] + collection: Optional[AccountLiabilityCollection] + credit_builder: Optional[AccountLiabilityCreditBuilder] + credit_card: Optional[AccountLiabilityCreditCard] + insurance: Optional[AccountLiabilityInsurance] + loan: Optional[AccountLiabilityLoan] + medical: Optional[AccountLiabilityMedical] + mortgage: Optional[AccountLiabilityMortgage] + personal_loan: Optional[AccountLiabilityPersonalLoan] + student_loan: Optional[AccountLiabilityStudentLoan] + student_loans: Optional[AccountLiabilityStudentLoans] + utility: Optional[AccountLiabilityUtility] + error: Optional[ResourceError] + created_at: str + updated_at: str + + +class AccountUpdatesResource(Resource): + def __init__(self, config: Configuration): + super(AccountUpdatesResource, self).__init__(config.add_path('updates')) + + def retrieve(self, upt_id: str) -> AccountUpdate: + return super(AccountUpdatesResource, self)._get_with_id(upt_id) + + def list(self, params: ResourceListOpts) -> List[AccountUpdate]: + return super(AccountUpdatesResource, self)._list(params) + + def create(self) -> AccountUpdate: + return super(AccountUpdatesResource, self)._create({}) \ No newline at end of file diff --git a/method/resources/Account/VerificationSessions.py b/method/resources/Accounts/VerificationSessions.py similarity index 97% rename from method/resources/Account/VerificationSessions.py rename to method/resources/Accounts/VerificationSessions.py index b1455da..1bf5c03 100644 --- a/method/resources/Account/VerificationSessions.py +++ b/method/resources/Accounts/VerificationSessions.py @@ -3,7 +3,7 @@ from method.resource import Resource from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Account.ExternalTypes import PlaidBalance, PlaidTransaction, MXAccount, MXTransaction, TellerBalance, TellerTransaction +from method.resources.Accounts.ExternalTypes import PlaidBalance, PlaidTransaction, MXAccount, MXTransaction, TellerBalance, TellerTransaction AccountVerificationSessionStatusesLiterals = Literal[ diff --git a/method/resources/Accounts/__init__.py b/method/resources/Accounts/__init__.py new file mode 100644 index 0000000..5ae3c6e --- /dev/null +++ b/method/resources/Accounts/__init__.py @@ -0,0 +1,9 @@ +from method.resources.Accounts.Account import Account, AccountResource +from method.resources.Accounts.Balances import AccountBalance, AccountBalancesResource +from method.resources.Accounts.Cards import AccountCard, AccountCardsResource +from method.resources.Accounts.Payoffs import AccountPayoff, AccountPayoffsResource +from method.resources.Accounts.Sensitive import AccountSensitive, AccountSensitiveResource +from method.resources.Accounts.Subscriptions import AccountSubscription, AccountSubscriptionsResource +from method.resources.Accounts.Transactions import AccountTransaction, AccountTransactionsResource +from method.resources.Accounts.Updates import AccountUpdate, AccountUpdatesResource +from method.resources.Accounts.VerificationSessions import AccountVerificationSession, AccountVerificationSessionResource \ No newline at end of file diff --git a/method/resources/Bin.py b/method/resources/Bin.py deleted file mode 100644 index 2a51794..0000000 --- a/method/resources/Bin.py +++ /dev/null @@ -1,37 +0,0 @@ -from typing import TypedDict, Optional, Literal - -from method.resource import Resource -from method.configuration import Configuration - - -BinBrandsLiterals = Literal[ - 'amex', - 'visa', - 'mastercard', - 'discover', - 'diners_club' -] - -BinTypesLiterals = Literal[ - 'debit', - 'credit' -] - - -class Bin(TypedDict): - id: Optional[str] - bin: Optional[str] - brand: BinBrandsLiterals - issuer: Optional[str] - type: Optional[BinTypesLiterals] - category: Optional[str] - bank_url: Optional[str] - sample_pan: Optional[str] - - -class BinResource(Resource): - def __init__(self, config: Configuration): - super(BinResource, self).__init__(config.add_path('bins')) - - def retrieve(self, _id: str) -> Bin: - return super(BinResource, self)._get_with_params({'bin': _id}) diff --git a/method/resources/Entity/Connect.py b/method/resources/Entities/Connect.py similarity index 100% rename from method/resources/Entity/Connect.py rename to method/resources/Entities/Connect.py diff --git a/method/resources/Entity/CreditScores.py b/method/resources/Entities/CreditScores.py similarity index 92% rename from method/resources/Entity/CreditScores.py rename to method/resources/Entities/CreditScores.py index 2810843..c90f405 100644 --- a/method/resources/Entity/CreditScores.py +++ b/method/resources/Entities/CreditScores.py @@ -3,7 +3,7 @@ from method.resource import Resource from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Entity.Entity import EntityStatusesLiterals, CreditReportBureausLiterals +from method.resources.Entities.Entity import EntityStatusesLiterals, CreditReportBureausLiterals CreditScoresModelLiterals = Literal[ diff --git a/method/resources/Entity/Entity.py b/method/resources/Entities/Entity.py similarity index 70% rename from method/resources/Entity/Entity.py rename to method/resources/Entities/Entity.py index 8de94e4..f61180b 100644 --- a/method/resources/Entity/Entity.py +++ b/method/resources/Entities/Entity.py @@ -1,9 +1,10 @@ from typing import TypedDict, Optional, List, Dict, Any, Literal -from method.resource import Resource, RequestOpts +from method.resource import Resource, RequestOpts, ResourceListOpts from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Entity import EntityConnectResource, EntityCreditScoresResource, EntityIdentityResource, EntitySyncResource, EntityVerificationSessionResource +from method.resources.Entities import EntityConnectResource, EntityCreditScoresResource, EntityIdentityResource, \ + EntityProductResource, EntitySensitiveResource, EntitySubscriptionsResource, EntityVerificationSessionResource EntityTypesLiterals = Literal[ @@ -148,12 +149,7 @@ class EntityUpdateOpts(TypedDict): address: Optional[EntityAddress] -class EntityListOpts(TypedDict): - to_date: Optional[str] - from_date: Optional[str] - page: Optional[int] - page_limit: Optional[int] - page_cursor: Optional[str] +class EntityListOpts(ResourceListOpts): status: Optional[str] type: Optional[str] @@ -227,50 +223,22 @@ class EntityUpdateAuthResponse(TypedDict): cxn_id: Optional[str] -class EntityKYCAddressRecordData(TypedDict): - address: str - city: str - postal_code: str - state: str - address_term: int - - -class EntityIdentity(TypedDict): - first_name: Optional[str] - last_name: Optional[str] - phone: Optional[str] - dob: Optional[str] - address: Optional[EntityKYCAddressRecordData] - ssn: Optional[str] - - -class EntitySensitiveResponse(TypedDict): - first_name: Optional[str] - last_name: Optional[str] - phone: Optional[str] - phone_history: Optional[List[str]] - email: Optional[str] - dob: Optional[str] - address: Optional[EntityKYCAddressRecordData] - address_history: List[EntityKYCAddressRecordData] - ssn_4: Optional[str] - ssn_6: Optional[str] - ssn_9: Optional[str] - identities: List[EntityIdentity] - - class EntitySubResources: connect: EntityConnectResource credit_scores: EntityCreditScoresResource identities: EntityIdentityResource - syncs: EntitySyncResource + products: EntityProductResource + sensitive: EntitySensitiveResource + subscriptions: EntitySubscriptionsResource verification_sessions: EntityVerificationSessionResource def __init__(self, _id: str, config: Configuration): self.connect = EntityConnectResource(config.add_path(_id)) self.credit_scores = EntityCreditScoresResource(config.add_path(_id)) self.identities = EntityIdentityResource(config.add_path(_id)) - self.syncs = EntitySyncResource(config.add_path(_id)) + self.products = EntityProductResource(config.add_path(_id)) + self.sensitive = EntitySensitiveResource(config.add_path(_id)) + self.subscriptions = EntitySubscriptionsResource(config.add_path(_id)) self.verification_sessions = EntityVerificationSessionResource(config.add_path(_id)) @@ -293,30 +261,9 @@ def retrieve(self, _id: str) -> Entity: def list(self, params: EntityListOpts = None) -> List[Entity]: return super(EntityResource, self)._list(params) - def create_auth_session(self, _id: str) -> EntityQuestionResponse: - return super(EntityResource, self)._create_with_sub_path('{_id}/auth_session'.format(_id=_id), {}) - - def retrieve_credit_score(self, _id: str) -> EntityQuestionResponse: - return super(EntityResource, self)._get_with_sub_path('{_id}/credit_score'.format(_id=_id)) - - def update_auth_session(self, _id: str, opts: EntityUpdateAuthOpts) -> EntityUpdateAuthResponse: - return super(EntityResource, self)._update_with_sub_path('{_id}/auth_session'.format(_id=_id), opts) - - def create_manual_auth_session(self, _id: str, opts: EntityManualAuthOpts) -> EntityManualAuthResponse: - return super(EntityResource, self)._create_with_sub_path('{_id}/manual_auth_session'.format(_id=_id), opts) - - def update_manual_auth_session(self, _id: str, opts: EntityManualAuthOpts) -> EntityManualAuthResponse: - return super(EntityResource, self)._update_with_sub_path('{_id}/manual_auth_session'.format(_id=_id), opts) - def refresh_capabilities(self, _id: str) -> Entity: return super(EntityResource, self)._create_with_sub_path('{_id}/refresh_capabilities'.format(_id=_id), {}) - def retrieve_sensitive_fields(self, _id: str, fields: List[EntitySensitiveFieldsLiterals]) -> EntitySensitiveResponse: - return super(EntityResource, self)._get_with_sub_path_and_params( - '{_id}/sensitive'.format(_id=_id), - {'fields[]': fields}, - ) - def withdraw_consent(self, _id: str) -> Entity: return super(EntityResource, self)._create_with_sub_path( '{_id}/consent'.format(_id=_id), diff --git a/method/resources/Entity/Identities.py b/method/resources/Entities/Identities.py similarity index 94% rename from method/resources/Entity/Identities.py rename to method/resources/Entities/Identities.py index f8344d1..2ae3fae 100644 --- a/method/resources/Entity/Identities.py +++ b/method/resources/Entities/Identities.py @@ -3,7 +3,7 @@ from method.resource import Resource from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Entity.Entity import EntityIdentity +from method.resources.Entities.Entity import EntityIdentity EntityVerificationSessionStatusLiterals = Literal[ diff --git a/method/resources/Entities/Products.py b/method/resources/Entities/Products.py new file mode 100644 index 0000000..262c9c3 --- /dev/null +++ b/method/resources/Entities/Products.py @@ -0,0 +1,40 @@ +from typing import TypedDict, Optional, Literal + +from method.resource import Resource +from method.configuration import Configuration +from method.errors import ResourceError + + +EntityProductTypeStatusLiterals = Literal[ + 'unavailable', + 'available', + 'restricted' +] + + +class EntityProduct(TypedDict): + id: str + name: str + status: EntityProductTypeStatusLiterals + status_error: Optional[ResourceError] + latest_request_id: str + is_subscribable: bool + created_at: str + updated_at: str + + +class EntityProductListResponse(TypedDict): + connect: Optional[EntityProduct] + credit_score: Optional[EntityProduct] + identity: Optional[EntityProduct] + + +class EntityProductResource(Resource): + def __init__(self, config: Configuration): + super(EntityProductResource, self).__init__(config.add_path('products')) + + def retrieve(self, prd_id: str) -> EntityProduct: + return super(EntityProductResource, self)._get_with_id(prd_id) + + def list(self) -> EntityProductListResponse: + return super(EntityProductResource, self)._list() \ No newline at end of file diff --git a/method/resources/Entities/Sensitive.py b/method/resources/Entities/Sensitive.py new file mode 100644 index 0000000..fbe4b8c --- /dev/null +++ b/method/resources/Entities/Sensitive.py @@ -0,0 +1,29 @@ +from typing import TypedDict, Optional, List + +from method.resource import Resource +from method.configuration import Configuration + +from method.resources.Entities.Types import EntityKYCAddressRecordData, EntityIdentity + + +class EntitySensitive(TypedDict): + first_name: Optional[str] + last_name: Optional[str] + phone: Optional[str] + phone_history: List[str] + email: Optional[str] + dob: Optional[str] + address: Optional[EntityKYCAddressRecordData] + address_history: List[EntityKYCAddressRecordData] + ssn_4: Optional[str] + ssn_6: Optional[str] + ssn_9: Optional[str] + identities: List[EntityIdentity] + + +class EntitySensitiveResource(Resource): + def __init__(self, config: Configuration): + super(EntitySensitiveResource, self).__init__(config.add_path('sensitive')) + + def retrieve(self) -> EntitySensitive: + return super(EntitySensitive, self)._get() \ No newline at end of file diff --git a/method/resources/Entities/Subscriptions.py b/method/resources/Entities/Subscriptions.py new file mode 100644 index 0000000..94cf79d --- /dev/null +++ b/method/resources/Entities/Subscriptions.py @@ -0,0 +1,57 @@ +from typing import TypedDict, Optional, List, Literal + +from method.resource import Resource +from method.configuration import Configuration +from method.errors import ResourceError + + +EntitySubscriptionNamesLiterals = Literal[ + 'connect', + 'credit_score' +] + + +EntitySubscriptionStatusesLiterals = Literal[ + 'active', + 'inactive' +] + + +class EntitySubscription(TypedDict): + id: str + name: EntitySubscriptionNamesLiterals + status: EntitySubscriptionStatusesLiterals + last_request_id: Optional[str] + created_at: str + updated_at: str + + +class EntitySubscriptionResponseOpts(TypedDict): + subscription: Optional[EntitySubscription] + error: Optional[ResourceError] + + +class EntitySubscriptionCreateOpts(TypedDict): + enroll: List[EntitySubscriptionNamesLiterals] + + +class EntitySubscriptionListResponse(TypedDict): + connect: Optional[EntitySubscriptionResponseOpts] + credit_score: Optional[EntitySubscriptionResponseOpts] + + +class EntitySubscriptionsResource(Resource): + def __init__(self, config: Configuration): + super(EntitySubscriptionsResource, self).__init__(config.add_path('subscriptions')) + + def retrieve(self, sub_id: str) -> EntitySubscriptionResponseOpts: + return super(EntitySubscriptionsResource, self)._get_with_id(sub_id) + + def list(self) -> EntitySubscriptionListResponse: + return super(EntitySubscriptionsResource, self)._list() + + def create(self, opts: EntitySubscriptionCreateOpts) -> EntitySubscriptionResponseOpts: + return super(EntitySubscriptionsResource, self)._create(opts) + + def delete(self, sub_id: str) -> EntitySubscriptionResponseOpts: + return super(EntitySubscriptionsResource, self)._delete(sub_id) \ No newline at end of file diff --git a/method/resources/Entities/Types.py b/method/resources/Entities/Types.py new file mode 100644 index 0000000..ece2c16 --- /dev/null +++ b/method/resources/Entities/Types.py @@ -0,0 +1,17 @@ +from typing import TypedDict, Optional + +class EntityKYCAddressRecordData(TypedDict): + address: str + city: str + postal_code: str + state: str + address_term: int + + +class EntityIdentity(TypedDict): + first_name: Optional[str] + last_name: Optional[str] + phone: Optional[str] + dob: Optional[str] + address: Optional[EntityKYCAddressRecordData] + ssn: Optional[str] diff --git a/method/resources/Entity/VerificationSessions.py b/method/resources/Entities/VerificationSessions.py similarity index 100% rename from method/resources/Entity/VerificationSessions.py rename to method/resources/Entities/VerificationSessions.py diff --git a/method/resources/Entities/__init__.py b/method/resources/Entities/__init__.py new file mode 100644 index 0000000..6b425d3 --- /dev/null +++ b/method/resources/Entities/__init__.py @@ -0,0 +1,8 @@ +from method.resources.Entities.Entity import Entity, EntityResource +from method.resources.Entities.Connect import EntityConnect, EntityConnectResource +from method.resources.Entities.CreditScores import EntityCreditScores, EntityCreditScoresResource +from method.resources.Entities.Identities import EntityIdentity, EntityIdentityResource +from method.resources.Entities.Products import EntityProduct, EntityProductResource +from method.resources.Entities.Sensitive import EntitySensitive, EntitySensitiveResource +from method.resources.Entities.Subscriptions import EntitySubscription, EntitySubscriptionsResource +from method.resources.Entities.VerificationSessions import EntityVerificationSession, EntityVerificationSessionResource \ No newline at end of file diff --git a/method/resources/Entity/Syncs.py b/method/resources/Entity/Syncs.py deleted file mode 100644 index 1f52818..0000000 --- a/method/resources/Entity/Syncs.py +++ /dev/null @@ -1,36 +0,0 @@ -from typing import TypedDict, Optional, Literal - -from method.resource import Resource -from method.configuration import Configuration -from method.errors import ResourceError - - -EntitySyncTypeLiterals = Literal[ - 'capabilities', - 'accounts' -] - - -class EntitySync(TypedDict): - id: str - acc_id: str - status: str - type: str - error: Optional[ResourceError] - created_at: str - updated_at: str - - -class EntitySyncCreateOpts(TypedDict): - type: EntitySyncTypeLiterals - - -class EntitySyncResource(Resource): - def __init__(self, config: Configuration): - super(EntitySyncResource, self).__init__(config.add_path('syncs')) - - def retrieve(self, acc_sync_id: str) -> EntitySync: - return super(EntitySyncResource, self)._get_with_id(acc_sync_id) - - def create(self, opts: Optional[EntitySyncCreateOpts] = {}) -> EntitySync: - return super(EntitySyncResource, self)._create(opts) diff --git a/method/resources/Entity/__init__.py b/method/resources/Entity/__init__.py deleted file mode 100644 index 1d08768..0000000 --- a/method/resources/Entity/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from method.resources.Entity.Entity import Entity, EntityResource -from method.resources.Entity.Syncs import EntitySync, EntitySyncResource -from method.resources.Entity.CreditScores import EntityCreditScores, EntityCreditScoresResource -from method.resources.Entity.Identities import EntityIdentity, EntityIdentityResource -from method.resources.Entity.Connect import EntityConnect, EntityConnectResource -from method.resources.Entity.VerificationSessions import EntityVerificationSession, EntityVerificationSessionResource \ No newline at end of file diff --git a/method/resources/Merchant.py b/method/resources/Merchant.py index c3ec20b..28982de 100644 --- a/method/resources/Merchant.py +++ b/method/resources/Merchant.py @@ -49,6 +49,9 @@ class Merchant(TypedDict): is_temp: bool MerchantListOpts = TypedDict('MerchantListOpts', { + 'page': Optional[str | int], + 'page_limit': Optional[str | int], + 'type': Optional[MerchantTypesLiterals], 'name': Optional[str], 'provider_id.plaid': Optional[str], 'provider_id.mx': Optional[str], diff --git a/method/resources/Payment.py b/method/resources/Payments/Payment.py similarity index 91% rename from method/resources/Payment.py rename to method/resources/Payments/Payment.py index e526b30..da28630 100644 --- a/method/resources/Payment.py +++ b/method/resources/Payments/Payment.py @@ -1,9 +1,9 @@ from typing import TypedDict, Optional, List, Dict, Any, Literal -from method.resource import Resource, RequestOpts +from method.resource import Resource, RequestOpts, ResourceListOpts from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Reversal import ReversalResource +from method.resources.Payments.Reversal import ReversalResource PaymentStatusesLiterals = Literal[ @@ -80,12 +80,7 @@ class PaymentCreateOpts(TypedDict): fee: Optional[PaymentFee] -class PaymentListOpts(TypedDict): - to_date: Optional[str] - from_date: Optional[str] - page: Optional[int] - page_limit: Optional[int] - page_cursor: Optional[str] +class PaymentListOpts(ResourceListOpts): status: Optional[str] type: Optional[str] source: Optional[str] diff --git a/method/resources/Reversal.py b/method/resources/Payments/Reversal.py similarity index 100% rename from method/resources/Reversal.py rename to method/resources/Payments/Reversal.py diff --git a/method/resources/Payments/__init__.py b/method/resources/Payments/__init__.py new file mode 100644 index 0000000..7a02ae7 --- /dev/null +++ b/method/resources/Payments/__init__.py @@ -0,0 +1,2 @@ +from method.resources.Payments.Payment import Payment, PaymentResource +from method.resources.Payments.Reversal import ReversalResource \ No newline at end of file diff --git a/method/resources/RoutingNumber.py b/method/resources/RoutingNumber.py deleted file mode 100644 index 29fdbd1..0000000 --- a/method/resources/RoutingNumber.py +++ /dev/null @@ -1,37 +0,0 @@ -from typing import TypedDict, Optional, Literal - -from method.resource import Resource -from method.configuration import Configuration - - -RoutingNumberOfficeTypesLiterals = Literal[ - 'main', - 'branch' -] - - -class RoutingNumberAddress(TypedDict): - line1: str - line2: Optional[str] - city: str - state: str - zip: str - - -class RoutingNumber(TypedDict): - id: str - institution_name: str - routing_number: str - logo: str - office_type: RoutingNumberOfficeTypesLiterals - change_date: str - address: RoutingNumberAddress - phone: Optional[str] - - -class RoutingNumberResource(Resource): - def __init__(self, config: Configuration): - super(RoutingNumberResource, self).__init__(config.add_path('routing_numbers')) - - def retrieve(self, routing_number: str) -> RoutingNumber: - return super(RoutingNumberResource, self)._get_with_params({'routing_number': routing_number}) diff --git a/method/resources/SimulatePayment.py b/method/resources/Simulate/Payments.py similarity index 87% rename from method/resources/SimulatePayment.py rename to method/resources/Simulate/Payments.py index ffe6f4b..df355f9 100644 --- a/method/resources/SimulatePayment.py +++ b/method/resources/Simulate/Payments.py @@ -2,7 +2,7 @@ from method.resource import Resource from method.configuration import Configuration -from method.resources.Payment import Payment, PaymentStatusesLiterals +from method.resources.Payments.Payment import Payment, PaymentStatusesLiterals class SimulatePaymentUpdateOpts(TypedDict): diff --git a/method/resources/Simulate.py b/method/resources/Simulate/Simulate.py similarity index 58% rename from method/resources/Simulate.py rename to method/resources/Simulate/Simulate.py index 8eb9eaf..88524b7 100644 --- a/method/resources/Simulate.py +++ b/method/resources/Simulate/Simulate.py @@ -1,12 +1,15 @@ from method.resource import Resource from method.configuration import Configuration -from method.resources.SimulatePayment import SimulatePaymentResource +from method.resources.Simulate.Payments import SimulatePaymentResource +from method.resources.Simulate.Transactions import SimulateTransactionsResource class SimulateResource(Resource): payments: SimulatePaymentResource + transactions: SimulateTransactionsResource def __init__(self, config: Configuration): _config = config.add_path('simulate') super(SimulateResource, self).__init__(_config) self.payments = SimulatePaymentResource(_config) + self.transactions = SimulateTransactionsResource(_config) diff --git a/method/resources/Simulate/Transactions.py b/method/resources/Simulate/Transactions.py new file mode 100644 index 0000000..7794f64 --- /dev/null +++ b/method/resources/Simulate/Transactions.py @@ -0,0 +1,10 @@ +from method.resource import Resource +from method.configuration import Configuration +from method.resources.Accounts.Transactions import AccountTransaction + +class SimulateTransactionsResource(Resource): + def __init__(self, config: Configuration): + super(SimulateTransactionsResource, self).__init__(config.add_path('transactions')) + + def create(self) -> AccountTransaction: + return super(SimulateTransactionsResource, self)._create({}) \ No newline at end of file diff --git a/method/resources/Simulate/__init__.py b/method/resources/Simulate/__init__.py new file mode 100644 index 0000000..2e5df6c --- /dev/null +++ b/method/resources/Simulate/__init__.py @@ -0,0 +1,3 @@ +from method.resources.Simulate.Simulate import SimulateResource +from method.resources.Simulate.Transactions import SimulateTransactionsResource +from method.resources.Simulate.Payments import SimulatePaymentResource diff --git a/method/resources/Transaction.py b/method/resources/Transaction.py deleted file mode 100644 index 98f82ae..0000000 --- a/method/resources/Transaction.py +++ /dev/null @@ -1,30 +0,0 @@ -from typing import TypedDict, List - -from method.resource import Resource -from method.configuration import Configuration - - -class Transaction(TypedDict): - id: str - acc_id: str - mcc: str - description: str - presentable_description: str - amount: str - currency: str - billing_amount: str - billing_currency: str - status: str - created_at: str - updated_at: str - - -class TransactionResource(Resource): - def __init__(self, config: Configuration): - super(TransactionResource, self).__init__(config.add_path('transactions')) - - def list(self) -> List[Transaction]: - return super(TransactionResource, self)._list(None) - - def retrieve(self, _id: str) -> Transaction: - return super(TransactionResource, self)._get_with_id(_id) diff --git a/method/resources/Verification.py b/method/resources/Verification.py deleted file mode 100644 index a471406..0000000 --- a/method/resources/Verification.py +++ /dev/null @@ -1,86 +0,0 @@ -from typing import TypedDict, List, Literal, Dict, Any, Optional - -from method.resource import Resource -from method.configuration import Configuration -from method.errors import ResourceError - - -VerificationStatusesLiterals = Literal[ - 'initiated', - 'pending', - 'verified', - 'disabled' -] - - -VerificationTypesLiterals = Literal[ - 'micro_deposits', - 'plaid', - 'mx', - 'teller', - 'auto_verify', - 'trusted_provisioner' -] - - -class Verification(TypedDict): - id: str - status: VerificationStatusesLiterals - type: VerificationTypesLiterals - error: Optional[ResourceError] - initiated_at: str - pending_at: str - verified_at: str - disabled_at: str - created_at: str - updated_at: str - - -class VerificationMicroDepositsUpdate(TypedDict): - amounts: List[int] - - -class VerificationUpdateOpts(TypedDict): - micro_deposits: VerificationMicroDepositsUpdate - - -class VerificationPlaidCreate(TypedDict): - balances: Dict[str, Any] - transactions: List[Dict[str, Any]] - - -class VerificationMXCreate(TypedDict): - account: Dict[str, Any] - transactions: List[Dict[str, Any]] - -class VerificationTellerCreate(TypedDict): - balances: Dict[str, Any] - transactions: List[Dict[str, Any]] - - -class VerificationCreateOpts(TypedDict): - type: VerificationTypesLiterals - plaid: Optional[VerificationPlaidCreate] - mx: Optional[VerificationMXCreate] - teller: Optional[VerificationTellerCreate] - - -class VerificationTestAmountsResponse(TypedDict): - amounts: List[int] - - -class VerificationResource(Resource): - def __init__(self, config: Configuration): - super(VerificationResource, self).__init__(config.add_path('verification')) - - def retrieve(self) -> Verification: - return super(VerificationResource, self)._get() - - def update(self, opts: VerificationUpdateOpts) -> Verification: - return super(VerificationResource, self)._update(opts) - - def create(self, opts: VerificationCreateOpts) -> Verification: - return super(VerificationResource, self)._create(opts) - - def retrieve_test_amounts(self) -> VerificationTestAmountsResponse: - return super(VerificationResource, self)._get_with_sub_path('amounts') diff --git a/method/resources/__init__.py b/method/resources/__init__.py index 8e6a6a4..94f9acd 100644 --- a/method/resources/__init__.py +++ b/method/resources/__init__.py @@ -1,16 +1,14 @@ # type: ignore -from method.resources.Account import Account, AccountResource, AccountPayoffsResource, \ - AccountSyncResource, AccountVerificationSessionResource -from method.resources.Bin import Bin, BinResource +from method.resources.Accounts import Account, AccountResource, AccountPayoffsResource, \ + AccountBalancesResource, AccountCardsResource, AccountSensitiveResource, AccountVerificationSessionResource, \ + AccountSubscriptionsResource, AccountTransactionsResource, AccountUpdatesResource from method.resources.Element import Element, ElementResource -from method.resources.Entity import Entity, EntityResource, EntityConnectResource, \ - EntityCreditScoresResource, EntityIdentityResource, EntitySyncResource, EntityVerificationSessionResource +from method.resources.Entities import Entity, EntityResource, EntityConnectResource, \ + EntityCreditScoresResource, EntityIdentityResource, EntityVerificationSessionResource, \ + EntityProductResource, EntitySensitiveResource, EntitySubscriptionsResource from method.resources.Merchant import Merchant, MerchantResource -from method.resources.Payment import Payment, PaymentResource +from method.resources.Payments import Payment, PaymentResource from method.resources.Report import Report, ReportResource -from method.resources.RoutingNumber import RoutingNumber, RoutingNumberResource -from method.resources.Verification import Verification, VerificationResource from method.resources.Webhook import Webhook, WebhookResource from method.resources.HealthCheck import HealthCheckResource from method.resources.Simulate import SimulatePaymentResource -from method.resources.Transaction import TransactionResource From c479cb6147ddd3a2cf2781a00629bbe6c869ad56 Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Wed, 24 Apr 2024 16:39:39 -0400 Subject: [PATCH 06/22] nit fix --- method/resource.py | 1 + 1 file changed, 1 insertion(+) diff --git a/method/resource.py b/method/resource.py index f6783c3..531c3c2 100644 --- a/method/resource.py +++ b/method/resource.py @@ -16,6 +16,7 @@ class RequestOpts(TypedDict): idempotency_key: Optional[str] + class ResourceListOpts(TypedDict): from_date: Optional[str] to_date: Optional[str] From ac704a5b76977a0697ba1bd98a3e488b189e8aae Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Wed, 24 Apr 2024 16:39:54 -0400 Subject: [PATCH 07/22] nit fix --- method/resource.py | 1 + 1 file changed, 1 insertion(+) diff --git a/method/resource.py b/method/resource.py index 531c3c2..df99933 100644 --- a/method/resource.py +++ b/method/resource.py @@ -13,6 +13,7 @@ 'failed' ] + class RequestOpts(TypedDict): idempotency_key: Optional[str] From 3a8f36bb85d26f02f6c6aa9f6fe26925573197de Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Wed, 24 Apr 2024 16:46:03 -0400 Subject: [PATCH 08/22] bumped version to 1.0.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a276f4b..d1b2f06 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='method-python', - version='0.0.44', + version='1.0.0', description='Python library for the Method API', long_description='Python library for the Method API', long_description_content_type='text/x-rst', From 0d142d71a0906aeeaf678ac23f1a3d04ba0a8272 Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Mon, 20 May 2024 12:29:33 -0500 Subject: [PATCH 09/22] updating to match node SDK --- method/resources/Accounts/Account.py | 407 ++++++------------ method/resources/Accounts/Balances.py | 9 +- .../Accounts/{Cards.py => CardBrands.py} | 14 +- method/resources/Accounts/Payoffs.py | 1 + method/resources/Accounts/Sensitive.py | 35 +- method/resources/Accounts/Subscriptions.py | 23 +- method/resources/Accounts/Transactions.py | 7 - method/resources/Accounts/Updates.py | 7 - method/resources/Accounts/__init__.py | 2 +- 9 files changed, 177 insertions(+), 328 deletions(-) rename method/resources/Accounts/{Cards.py => CardBrands.py} (58%) diff --git a/method/resources/Accounts/Account.py b/method/resources/Accounts/Account.py index a5ff371..470640f 100644 --- a/method/resources/Accounts/Account.py +++ b/method/resources/Accounts/Account.py @@ -3,23 +3,9 @@ from method.resource import Resource, RequestOpts from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Accounts import AccountBalancesResource, AccountCardsResource, AccountPayoffsResource, \ - AccountSensitiveResource, AccountSubscriptionsResource, AccountTransactionsResource, AccountUpdatesResource, \ - AccountVerificationSessionResource - - -# Literals, keep ordered alphabetically -AccountCapabilitiesLiterals = Literal[ - 'payments:receive', - 'payments:send', - 'data:retrieve', - 'data:sync' -] - - -AccountClearingSubTypesLiterals = Literal[ - 'single_use' -] +from method.resources.Accounts import AccountBalance, AccountBalancesResource, AccountCardBrand, AccountCardBrandsResource, \ + AccountPayoff, AccountPayoffsResource, AccountSensitive, AccountSensitiveResource, AccountSubscription, AccountSubscriptionsResource, \ + AccountTransaction, AccountTransactionsResource, AccountUpdate, AccountUpdatesResource, AccountVerificationSession, AccountVerificationSessionResource AccountLiabilityDataSourcesLiterls = Literal[ @@ -38,21 +24,24 @@ ] -AccountLiabilityPaymentStatuesLiterals = Literal[ - 'active', - 'activating', - 'unavailable' +AccountProductTypesLiterals = Literal[ + 'payment', + 'balance', + 'sensitive', + 'card_brand', + 'payoff', + 'update' ] -AccountLiabilitySyncTypesLiterals = Literal[ - 'manual', - 'auto' +AccountSubscriptionTypesLiterals = Literal[ + 'transactions', + 'update', + 'update.snapshot' ] AccountLiabilityTypesLiterals = Literal[ - 'student_loan', 'student_loans', 'credit_card', 'mortgage', @@ -77,22 +66,9 @@ ] -AccountSubTypesLiterals = Literal[ - 'checking', - 'savings' -] - - AccountTypesLiterals = Literal[ 'ach', - 'liability', - 'clearing' -] - -AutoPayStatusesLiterals = Literal[ - 'unknown', - 'active', - 'inactive' + 'liability' ] @@ -104,241 +80,136 @@ ] -PastDueStatusesLiterals = Literal[ - 'unknown', - 'overdue', - 'on_time' +AccountInterestRateTypesLiterals = Literal[ + 'fixed', + 'variable' ] -DelinquencyStatusLiterals = Literal[ - 'good_standing', - 'past_due', - 'major_delinquency', - 'unavailable' +AccountInterestRateSourcesLiterals = Literal[ + 'financial_institution', + 'public_data', + 'method' ] -DelinquencyPeriodLiterals = Literal[ - 'less_than_30', - '30', - '60', - '90', - '120', - 'over_120' +AchAccountSubTypesLiterals = Literal[ + 'checking', + 'savings' ] -class AccountACH(TypedDict): - routing: int - number: int - type: AccountSubTypesLiterals +AccountLiabilityAutoLoanSubTypesLiterals = Literal[ + 'lease', + 'loan' +] -class AccountLiabilityLoan(TypedDict): - name: str - balance: Optional[int] - opened_at: Optional[str] - original_loan_amount: Optional[int] - sub_type: Optional[str] - term_length: Optional[int] - closed_at: Optional[str] - last_payment_amount: Optional[int] - last_payment_date: Optional[str] - next_payment_minimum_amount: Optional[int] - next_payment_due_date: Optional[str] - interest_rate_type: Literal['fixed', 'variable'] - interest_rate_percentage: Optional[int] - interest_rate_source: Optional[Literal['financial_institution', 'public_data', 'method']] +AccountLiabilityCreditCardSubTypesLiterals = Literal[ + 'flexible_spending', + 'charge', + 'secured', + 'unsecured', + 'purchase', + 'business' +] -class AccountLiabilityCreditCard(AccountLiabilityLoan): - sub_type: Optional[Literal['flexible_spending', 'charge', 'secured', 'unsecured', 'purchase', 'business']] - last_statement_balance: Optional[int] - remaining_statement_balance: Optional[int] - available_credit: Optional[int] - auto_pay_status: Optional[AutoPayStatusesLiterals] - auto_pay_amount: Optional[int] - auto_pay_date: Optional[str] - past_due_status: Optional[PastDueStatusesLiterals] - past_due_balance: Optional[int] - past_due_date: Optional[str] - credit_limit: Optional[int] - pending_purchase_authorization_amount: Optional[int] - pending_credit_authorization_amount: Optional[int] - interest_saving_balance: Optional[int] - next_statement_date: Optional[str] - - -class AccountLiabilityAutoLoan(AccountLiabilityLoan): - sub_type: Optional[Literal['lease', 'loan']] - payoff_amount: Optional[int] - payoff_amount_term: Optional[int] - past_due_status: Optional[PastDueStatusesLiterals] - past_due_balance: Optional[int] - past_due_date: Optional[str] - late_fees_amount: Optional[int] - expected_payoff_date: Optional[str] - principal_balance: Optional[int] - per_diem_amount: Optional[int] - mileage_allocation: Optional[int] +AccountLiabilityCreditCardUsageTypesLiterals = Literal[ + 'transactor', + 'revolver', + 'dormant', + 'unknown' +] -class AccountLiabilityStudentLoan(AccountLiabilityLoan): - sub_type: Optional[Literal['federal', 'private']] - sequence: Optional[int] - disbursed_at: Optional[str] - expected_payoff_date: Optional[str] - payoff_amount: Optional[int] - payoff_amount_term: Optional[int] - principal_balance: Optional[int] - - -class DelinquencyHistoryItem(TypedDict): - start_date: str - end_date: str - status: DelinquencyStatusLiterals - period: Optional[DelinquencyPeriodLiterals] - - -class TrendedDataItem(TypedDict): - month: Optional[int] - year: Optional[int] +AccountLiabilityPersonalLoanSubTypesLiterals = Literal[ + 'secured', + 'unsecured', + 'note', + 'line_of_credit', + 'heloc' +] + + +AccountLiabilityStudentLoanSubTypesLiterals = Literal[ + 'federal', + 'private' +] + + +AccountLiabilityMortgageSubTypesLiterals = Literal[ + 'loan' +] + + +class AccountLiabilityBase(TypedDict): balance: Optional[int] - available_credit: Optional[int] - scheduled_payment: Optional[int] - actual_payment: Optional[int] - high_credit: Optional[int] - credit_limit: Optional[int] - amount_past_due: Optional[int] + closed_at: Optional[str] + last_payment_amount: Optional[int] last_payment_date: Optional[str] - account_status: str - payment_status:str - - -class AccountLiabilityStudentLoansDisbursement(AccountLiabilityLoan): - sequence: int - disbursed_at: Optional[str] - expected_payoff_date: Optional[str] - delinquent_status: Optional[str] - delinquent_amount: Optional[int] - delinquent_period: Optional[int] - delinquent_action: Optional[str] - delinquent_start_date: Optional[str] - delinquent_major_start_date: Optional[str] - delinquent_status_updated_at: Optional[str] - delinquent_history: Optional[List[DelinquencyHistoryItem]] - delinquent_action: Optional[List[TrendedDataItem]] - - -class AccountLiabilityStudentLoans(AccountLiabilityLoan): - sub_type: Optional[Literal['federal', 'private']] - disbursed_at: Optional[str] - expected_payoff_date: Optional[str] - interest_rate_type: Optional[Literal['fixed', 'variable']] - expected_payoff_date: Optional[str] - disbursements: Optional[AccountLiabilityStudentLoansDisbursement] + next_payment_due_date: Optional[str] + next_payment_minimum_amount: Optional[int] + opened_at: Optional[str] -class AccountLiabilityMortgage(AccountLiabilityLoan): - principal_balance: Optional[int] - expected_payoff_date: Optional[str] - address_street: Optional[str] - address_city: Optional[str] - address_state: Optional[str] - address_zip: Optional[str] - property_value: Optional[int] - past_due_status: Optional[PastDueStatusesLiterals] - past_due_balance: Optional[int] - past_due_date: Optional[str] - payoff_amount: Optional[int] - payoff_amount_term: Optional[int] - year_to_date_interest_paid: Optional[int] - year_to_date_principal_paid: Optional[int] - year_to_date_taxes_paid: Optional[int] - year_start_principal_balance: Optional[int] - escrow_balance: Optional[int] - - -class AccountLiabilityPersonalLoan(AccountLiabilityLoan): - sub_type: Optional[Literal['line_of_credit', 'heloc', 'secured', 'unsecured', 'note']] +class AccountLiabilityLoanBase(AccountLiabilityBase): expected_payoff_date: Optional[str] - available_credit: Optional[int] - principal_balance: Optional[int] - year_to_date_interest_paid: Optional[int] - + interest_rate_percentage: Optional[float] + interest_rate_source: Optional[AccountInterestRateSourcesLiterals] + interest_rate_type: Optional[AccountInterestRateTypesLiterals] + original_loan_amount: Optional[int] + term_length: Optional[int] -class AccountLiabilityCreditBuilder(AccountLiabilityLoan): - pass +class AccountLiabilityAutoLoan(AccountLiabilityLoanBase): + sub_type: Optional[AccountLiabilityAutoLoanSubTypesLiterals] -class AccountLiabilityCollection(AccountLiabilityLoan): - pass +class AccountLiabilityCreditCard(AccountLiabilityBase): + available_credit: Optional[int] + credit_limit: Optional[int] + interest_rate_percentage_max: Optional[float] + interest_rate_percentage_min: Optional[float] + interest_rate_type: Optional[AccountInterestRateTypesLiterals] + sub_type: Optional[AccountLiabilityCreditCardSubTypesLiterals] + usage_pattern: Optional[AccountLiabilityCreditCardUsageTypesLiterals] -class AccountLiabilityBusinessLoan(TypedDict): - name: str - balance: Optional[int] - opened_at: Optional[str] +class AccountLiabilityMortgage(AccountLiabilityLoanBase): + sub_type: Optional[AccountLiabilityMortgageSubTypesLiterals] -class AccountLiabilityInsurance(TypedDict): - name: str - balance: Optional[int] - opened_at: Optional[str] +class AccountLiabilityPersonalLoan(AccountLiabilityLoanBase): + sub_type: Optional[AccountLiabilityPersonalLoanSubTypesLiterals] + available_credit: Optional[int] + -class AccountLiabilitySubscription(TypedDict): - name: str - balance: Optional[int] - opened_at: Optional[str] +class AccountLiabilityStudentLoansDisbursement(AccountLiabilityLoanBase): + sequence: int + disbursed_at: Optional[str] -class AccountLiabilityUtility(TypedDict): - name: str - balance: Optional[int] - opened_at: Optional[str] +class AccountLiabilityStudentLoans(AccountLiabilityBase): + disbursements: Optional[AccountLiabilityStudentLoansDisbursement] + sub_type: Optional[AccountLiabilityStudentLoanSubTypesLiterals] + original_loan_amount: Optional[int] + term_length: Optional[int] -class AccountLiabilityMedical(TypedDict): - name: str - balance: Optional[int] - opened_at: Optional[str] +class AccountACH(TypedDict): + routing: int + number: int + type: AchAccountSubTypesLiterals class AccountLiability(TypedDict): mch_id: str - mask: str - payment_status: AccountLiabilityPaymentStatuesLiterals - data_status: AccountLiabilityDataStatusesLiterals - data_last_successful_sync: Optional[str] - data_status_error: Optional[ResourceError] - data_source: AccountLiabilityDataSourcesLiterls - data_updated_at: Optional[str] - data_sync_type: AccountLiabilitySyncTypesLiterals - ownership: TradelineAccountOwnershipLiterals - hash: str - fingerprint: str - type: AccountLiabilityTypesLiterals - loan: Optional[AccountLiabilityLoan] - student_loan: Optional[AccountLiabilityStudentLoan] - student_loans: Optional[AccountLiabilityStudentLoans] - credit_card: Optional[AccountLiabilityCreditCard] - mortgage: Optional[AccountLiabilityMortgage] - auto_loan: Optional[AccountLiabilityAutoLoan] - personal_loan: Optional[AccountLiabilityPersonalLoan] - business_loan: Optional[AccountLiabilityBusinessLoan] - collection: Optional[AccountLiabilityCollection] - insurance: Optional[AccountLiabilityInsurance] - credit_builder: Optional[AccountLiabilityCreditBuilder] - subscription: Optional[AccountLiabilitySubscription] - utility: Optional[AccountLiabilityUtility] - medical: Optional[AccountLiabilityMedical] - - -class AccountClearing(TypedDict): - routing: int - number: int + mask: Optional[str] + ownership: Optional[TradelineAccountOwnershipLiterals] + fingerprint: Optional[str] + type: Optional[AccountLiabilityTypesLiterals] + name: Optional[str] class AccountCreateOpts(TypedDict): @@ -360,14 +231,6 @@ class AccountLiabilityCreateOpts(AccountCreateOpts): liability: LiabilityCreateOpts -class ClearingCreateOpts(TypedDict): - type: AccountClearingSubTypesLiterals - - -class AccountClearingCreateOpts(AccountCreateOpts): - clearing: ClearingCreateOpts - - class Account(TypedDict): id: str holder_id: str @@ -375,9 +238,18 @@ class Account(TypedDict): type: AccountTypesLiterals ach: Optional[AccountACH] liability: Optional[AccountLiability] - clearing: Optional[AccountClearing] - capabilities: List[AccountCapabilitiesLiterals] - available_capabilities: List[AccountCapabilitiesLiterals] + products: List[AccountProductTypesLiterals] + restricted_products: List[AccountProductTypesLiterals] + subscriptions: Optional[List[AccountSubscriptionTypesLiterals]] + available_subscriptions: Optional[List[AccountSubscriptionTypesLiterals]] + restricted_subscriptions: Optional[List[AccountSubscriptionTypesLiterals]] + sensitive: Optional[Union[str, AccountSensitive]] + balance: Optional[Union[str, AccountBalance]] + card_brand: Optional[Union[str, AccountCardBrand]] + payoff: Optional[Union[str, AccountPayoff]] + transaction: Optional[Union[str, AccountTransaction]] + update: Optional[Union[str, AccountUpdate]] + latest_verification_session: Optional[Union[str, AccountVerificationSession]] error: Optional[ResourceError] created_at: str updated_at: str @@ -403,39 +275,9 @@ class AccountWithdrawConsentOpts(TypedDict): reason: Optional[Literal['holder_withdrew_consent']] -class LiabilityMortgageUpdateOpts(TypedDict): - address_street: str - address_city: str - address_state: str - address_zip: str - - -class LiabilityCreditCardUpdateNumberOpts(TypedDict): - number: str - - -class LiabilityCreditCardUpdateExpirationOpts(TypedDict): - expiration_month: int - expiration_year: int - - -class LiabilityUpdateOpts(TypedDict): - mortgage: Optional[LiabilityMortgageUpdateOpts] - credit_card: Union[LiabilityCreditCardUpdateNumberOpts, LiabilityCreditCardUpdateExpirationOpts] - - -class CreditReportTradelinePaymentHistoryItem(TypedDict): - code: int - date: str - - -class AccountPaymentHistory(TypedDict): - payment_history: List[CreditReportTradelinePaymentHistoryItem] - - class AccountSubResources: balances: AccountBalancesResource - cards: AccountCardsResource + card_brands: AccountCardBrandsResource payoffs: AccountPayoffsResource sensitive: AccountSensitiveResource subscriptions: AccountSubscriptionsResource @@ -446,7 +288,7 @@ class AccountSubResources: def __init__(self, _id: str, config: Configuration): self.balances = AccountBalancesResource(config.add_path(_id)) - self.cards = AccountCardsResource(config.add_path(_id)) + self.card_brands = AccountCardBrandsResource(config.add_path(_id)) self.payoffs = AccountPayoffsResource(config.add_path(_id)) self.sensitive = AccountSensitiveResource(config.add_path(_id)) self.subscriptions = AccountSubscriptionsResource(config.add_path(_id)) @@ -465,13 +307,10 @@ def __call__(self, _id: str) -> AccountSubResources: def retrieve(self, _id: str) -> Account: return super(AccountResource, self)._get_with_id(_id) - def update(self, _id: str, opts: LiabilityUpdateOpts) -> Account: - return super(AccountResource, self)._update_with_id(_id, opts) - def list(self, params: Optional[AccountListOpts] = None) -> List[Account]: return super(AccountResource, self)._list(params) - def create(self, opts: Union[AccountACHCreateOpts, AccountLiabilityCreateOpts, AccountClearingCreateOpts], request_opts: Optional[RequestOpts] = None) -> Account: + def create(self, opts: Union[AccountACHCreateOpts, AccountLiabilityCreateOpts], request_opts: Optional[RequestOpts] = None) -> Account: return super(AccountResource, self)._create(opts, request_opts) def withdraw_consent(self, _id: str, data: AccountWithdrawConsentOpts = { 'type': 'withdraw', 'reason': 'holder_withdrew_consent' }) -> Account: diff --git a/method/resources/Accounts/Balances.py b/method/resources/Accounts/Balances.py index 0ba7d69..9b021b9 100644 --- a/method/resources/Accounts/Balances.py +++ b/method/resources/Accounts/Balances.py @@ -11,10 +11,11 @@ 'failed' ] -class AccountBalances(TypedDict): +class AccountBalance(TypedDict): id: str + account_id: str status: AccountBalanceStatusLiterals - balance: Optional[int] + amount: Optional[int] error: Optional[ResourceError] created_at: str updated_at: str @@ -23,8 +24,8 @@ class AccountBalancesResource(Resource): def __init__(self, config: Configuration): super(AccountBalancesResource, self).__init__(config.add_path('balances')) - def retrieve(self, bal_id: str) -> AccountBalances: + def retrieve(self, bal_id: str) -> AccountBalance: return super(AccountBalancesResource, self)._get_with_id(bal_id) - def create(self) -> AccountBalances: + def create(self) -> AccountBalance: return super(AccountBalancesResource, self)._create({}) \ No newline at end of file diff --git a/method/resources/Accounts/Cards.py b/method/resources/Accounts/CardBrands.py similarity index 58% rename from method/resources/Accounts/Cards.py rename to method/resources/Accounts/CardBrands.py index 0efe3bf..019d72d 100644 --- a/method/resources/Accounts/Cards.py +++ b/method/resources/Accounts/CardBrands.py @@ -11,7 +11,7 @@ class AccountCardBrand(TypedDict): name: str -class AccountCard(TypedDict): +class AccountCardBrand(TypedDict): id: str account_id: str network: str @@ -25,12 +25,12 @@ class AccountCard(TypedDict): updated_at: str -class AccountCardsResource(Resource): +class AccountCardBrandsResource(Resource): def __init__(self, config: Configuration): - super(AccountCardsResource, self).__init__(config.add_path('cards')) + super(AccountCardBrandsResource, self).__init__(config.add_path('cards')) - def retrieve(self, crd_id: str) -> AccountCard: - return super(AccountCardsResource, self)._get_with_id(crd_id) + def retrieve(self, crbd_id: str) -> AccountCardBrand: + return super(AccountCardBrandsResource, self)._get_with_id(crbd_id) - def _create(self) -> AccountCard: - return super(AccountCardsResource, self)._create({}) \ No newline at end of file + def _create(self) -> AccountCardBrand: + return super(AccountCardBrandsResource, self)._create({}) \ No newline at end of file diff --git a/method/resources/Accounts/Payoffs.py b/method/resources/Accounts/Payoffs.py index 09a4a90..3552a52 100644 --- a/method/resources/Accounts/Payoffs.py +++ b/method/resources/Accounts/Payoffs.py @@ -15,6 +15,7 @@ class AccountPayoff(TypedDict): id: str + account_id: str status: AccountPayoffStatusesLiterals amount: Optional[int] term: Optional[int] diff --git a/method/resources/Accounts/Sensitive.py b/method/resources/Accounts/Sensitive.py index 1610dd9..3e642f5 100644 --- a/method/resources/Accounts/Sensitive.py +++ b/method/resources/Accounts/Sensitive.py @@ -5,23 +5,36 @@ from method.errors import ResourceError -AccountBalanceSensitiveFeildsLiterals = Literal[ - 'number', - 'billing_zip_code', - 'exp_month', - 'exp_year', - 'cvv', +AccountSensitiveFieldsLiterals = Literal[ + 'auto_loan.number', + 'mortgage.number', + 'personal_loan.number', + 'credit_card.number', + 'credit_card.billing_zip_code', + 'credit_card.exp_month', + 'credit_card.exp_year', + 'credit_card.cvv' ] +class AccountSensitiveLoan(TypedDict): + number: str -class AccountSensitive(TypedDict): - id: str - account_id: str + +class AccountSensitiveCreditCard(TypedDict): number: Optional[str] + billing_zip_code: Optional[str] exp_month: Optional[str] exp_year: Optional[str] cvv: Optional[str] - billing_zip_code: Optional[str] + + +class AccountSensitive(TypedDict): + id: str + account_id: str + auto_loan: Optional[AccountSensitiveLoan] + credit_card: Optional[AccountSensitiveCreditCard] + mortgage: Optional[AccountSensitiveLoan] + personal_loan: Optional[AccountSensitiveLoan] status: Literal['completed', 'failed'] error: Optional[ResourceError] created_at: str @@ -29,7 +42,7 @@ class AccountSensitive(TypedDict): class AccountSensitiveCreateOpts(TypedDict): - expand: List[AccountBalanceSensitiveFeildsLiterals] + expand: List[AccountSensitiveFieldsLiterals] class AccountSensitiveResource(Resource): diff --git a/method/resources/Accounts/Subscriptions.py b/method/resources/Accounts/Subscriptions.py index ea9967e..943c050 100644 --- a/method/resources/Accounts/Subscriptions.py +++ b/method/resources/Accounts/Subscriptions.py @@ -20,12 +20,11 @@ class AccountSubscription(TypedDict): updated_at: str -class AccountSubscriptionTransactions(TypedDict): - subscription: AccountSubscription - - -class AccountSubscriptionResponse(TypedDict): - transactions: Optional[AccountSubscriptionTransactions] +AccountSubscriptionsResponse = TypedDict('AccountSubscriptionsResponse', { + 'transactions': Optional[AccountSubscription], + 'update': Optional[AccountSubscription], + 'update.snapshot': Optional[AccountSubscription] +}) class AccountSubscriptionCreateOpts(TypedDict): @@ -37,4 +36,14 @@ def __init__(self, config: Configuration): super(AccountSubscriptionsResource, self).__init__(config.add_path('subscriptions')) def create(self, data: AccountSubscriptionCreateOpts) -> AccountSubscription: - return super(AccountSubscriptionsResource, self)._create(data) \ No newline at end of file + return super(AccountSubscriptionsResource, self)._create(data) + + def list(self) -> List[AccountSubscription]: + return super(AccountSubscriptionsResource, self)._get() + + def retrieve(self, sub_id: str) -> AccountSubscriptionsResponse: + return super(AccountSubscriptionsResource, self)._get_with_id(sub_id) + + def delete(self, sub_id: str) -> AccountSubscription: + return super(AccountSubscriptionsResource, self)._delete(sub_id) + \ No newline at end of file diff --git a/method/resources/Accounts/Transactions.py b/method/resources/Accounts/Transactions.py index 45868d0..efb36b3 100644 --- a/method/resources/Accounts/Transactions.py +++ b/method/resources/Accounts/Transactions.py @@ -18,12 +18,6 @@ ] -class ProductBaseStatusHistoryItem(TypedDict): - status: AccountTransactionStatusLiterals - timestamp: str - metadata: Optional[dict] - - class AccountTransactionMerchant(TypedDict): name: str category_code: str @@ -52,7 +46,6 @@ class AccountTransaction(TypedDict): billing_amount: int billing_currency: AccountCurrencyTypesLiterals status: AccountTransactionStatusLiterals - status_history: List[ProductBaseStatusHistoryItem] error: Optional[ResourceError] created_at: str updated_at: str diff --git a/method/resources/Accounts/Updates.py b/method/resources/Accounts/Updates.py index c2550f6..70dd9a0 100644 --- a/method/resources/Accounts/Updates.py +++ b/method/resources/Accounts/Updates.py @@ -14,17 +14,10 @@ class AccountUpdate(TypedDict): account_id: str type: AccountLiabilityTypesLiterals auto_loan: Optional[AccountLiabilityAutoLoan] - collection: Optional[AccountLiabilityCollection] - credit_builder: Optional[AccountLiabilityCreditBuilder] credit_card: Optional[AccountLiabilityCreditCard] - insurance: Optional[AccountLiabilityInsurance] - loan: Optional[AccountLiabilityLoan] - medical: Optional[AccountLiabilityMedical] mortgage: Optional[AccountLiabilityMortgage] personal_loan: Optional[AccountLiabilityPersonalLoan] - student_loan: Optional[AccountLiabilityStudentLoan] student_loans: Optional[AccountLiabilityStudentLoans] - utility: Optional[AccountLiabilityUtility] error: Optional[ResourceError] created_at: str updated_at: str diff --git a/method/resources/Accounts/__init__.py b/method/resources/Accounts/__init__.py index 5ae3c6e..6f9a3e5 100644 --- a/method/resources/Accounts/__init__.py +++ b/method/resources/Accounts/__init__.py @@ -1,6 +1,6 @@ from method.resources.Accounts.Account import Account, AccountResource from method.resources.Accounts.Balances import AccountBalance, AccountBalancesResource -from method.resources.Accounts.Cards import AccountCard, AccountCardsResource +from method.resources.Accounts.CardBrands import AccountCardBrand, AccountCardBrandsResource from method.resources.Accounts.Payoffs import AccountPayoff, AccountPayoffsResource from method.resources.Accounts.Sensitive import AccountSensitive, AccountSensitiveResource from method.resources.Accounts.Subscriptions import AccountSubscription, AccountSubscriptionsResource From 4c5e7875267649c68896c8aaeda2edd7a0ced76f Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Wed, 29 May 2024 12:04:54 -0400 Subject: [PATCH 10/22] started adding tests, fixed circular deps --- conftest.py | 0 method/resources/Accounts/Account.py | 203 ++-------------------- method/resources/Accounts/Types.py | 188 ++++++++++++++++++++ method/resources/Accounts/Updates.py | 5 +- method/resources/Accounts/__init__.py | 10 +- method/resources/Element.py | 2 +- method/resources/Entities/CreditScores.py | 2 +- method/resources/Entities/Entity.py | 124 +------------ method/resources/Entities/Identities.py | 2 +- method/resources/Entities/Types.py | 112 +++++++++++- method/resources/Entities/__init__.py | 9 +- method/resources/__init__.py | 8 +- pytest.ini | 2 + requirements.txt | 3 +- test/resources/test_account.py | 86 +++++++++ 15 files changed, 418 insertions(+), 338 deletions(-) create mode 100644 conftest.py create mode 100644 method/resources/Accounts/Types.py create mode 100644 pytest.ini create mode 100644 test/resources/test_account.py diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..e69de29 diff --git a/method/resources/Accounts/Account.py b/method/resources/Accounts/Account.py index 470640f..551ff89 100644 --- a/method/resources/Accounts/Account.py +++ b/method/resources/Accounts/Account.py @@ -3,198 +3,17 @@ from method.resource import Resource, RequestOpts from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Accounts import AccountBalance, AccountBalancesResource, AccountCardBrand, AccountCardBrandsResource, \ - AccountPayoff, AccountPayoffsResource, AccountSensitive, AccountSensitiveResource, AccountSubscription, AccountSubscriptionsResource, \ - AccountTransaction, AccountTransactionsResource, AccountUpdate, AccountUpdatesResource, AccountVerificationSession, AccountVerificationSessionResource - - -AccountLiabilityDataSourcesLiterls = Literal[ - 'credit_report', - 'financial_institution', - 'unavailable' -] - - -AccountLiabilityDataStatusesLiterals = Literal[ - 'active', - 'syncing', - 'unavailable', - 'failed', - 'pending' -] - - -AccountProductTypesLiterals = Literal[ - 'payment', - 'balance', - 'sensitive', - 'card_brand', - 'payoff', - 'update' -] - - -AccountSubscriptionTypesLiterals = Literal[ - 'transactions', - 'update', - 'update.snapshot' -] - - -AccountLiabilityTypesLiterals = Literal[ - 'student_loans', - 'credit_card', - 'mortgage', - 'auto_loan', - 'collection', - 'personal_loan', - 'business_loan', - 'insurance', - 'credit_builder', - 'subscription', - 'utility', - 'medical', - 'loan' -] - - -AccountStatusesLiterals = Literal[ - 'active', - 'disabled', - 'closed', - 'processing' -] - - -AccountTypesLiterals = Literal[ - 'ach', - 'liability' -] - - -TradelineAccountOwnershipLiterals = Literal[ - 'primary', - 'authorized', - 'joint', - 'unknown' -] - - -AccountInterestRateTypesLiterals = Literal[ - 'fixed', - 'variable' -] - - -AccountInterestRateSourcesLiterals = Literal[ - 'financial_institution', - 'public_data', - 'method' -] - - -AchAccountSubTypesLiterals = Literal[ - 'checking', - 'savings' -] - - -AccountLiabilityAutoLoanSubTypesLiterals = Literal[ - 'lease', - 'loan' -] - - -AccountLiabilityCreditCardSubTypesLiterals = Literal[ - 'flexible_spending', - 'charge', - 'secured', - 'unsecured', - 'purchase', - 'business' -] - - -AccountLiabilityCreditCardUsageTypesLiterals = Literal[ - 'transactor', - 'revolver', - 'dormant', - 'unknown' -] - - -AccountLiabilityPersonalLoanSubTypesLiterals = Literal[ - 'secured', - 'unsecured', - 'note', - 'line_of_credit', - 'heloc' -] - - -AccountLiabilityStudentLoanSubTypesLiterals = Literal[ - 'federal', - 'private' -] - - -AccountLiabilityMortgageSubTypesLiterals = Literal[ - 'loan' -] - - -class AccountLiabilityBase(TypedDict): - balance: Optional[int] - closed_at: Optional[str] - last_payment_amount: Optional[int] - last_payment_date: Optional[str] - next_payment_due_date: Optional[str] - next_payment_minimum_amount: Optional[int] - opened_at: Optional[str] - - -class AccountLiabilityLoanBase(AccountLiabilityBase): - expected_payoff_date: Optional[str] - interest_rate_percentage: Optional[float] - interest_rate_source: Optional[AccountInterestRateSourcesLiterals] - interest_rate_type: Optional[AccountInterestRateTypesLiterals] - original_loan_amount: Optional[int] - term_length: Optional[int] - - -class AccountLiabilityAutoLoan(AccountLiabilityLoanBase): - sub_type: Optional[AccountLiabilityAutoLoanSubTypesLiterals] - - -class AccountLiabilityCreditCard(AccountLiabilityBase): - available_credit: Optional[int] - credit_limit: Optional[int] - interest_rate_percentage_max: Optional[float] - interest_rate_percentage_min: Optional[float] - interest_rate_type: Optional[AccountInterestRateTypesLiterals] - sub_type: Optional[AccountLiabilityCreditCardSubTypesLiterals] - usage_pattern: Optional[AccountLiabilityCreditCardUsageTypesLiterals] - - -class AccountLiabilityMortgage(AccountLiabilityLoanBase): - sub_type: Optional[AccountLiabilityMortgageSubTypesLiterals] - - -class AccountLiabilityPersonalLoan(AccountLiabilityLoanBase): - sub_type: Optional[AccountLiabilityPersonalLoanSubTypesLiterals] - available_credit: Optional[int] - - -class AccountLiabilityStudentLoansDisbursement(AccountLiabilityLoanBase): - sequence: int - disbursed_at: Optional[str] - - -class AccountLiabilityStudentLoans(AccountLiabilityBase): - disbursements: Optional[AccountLiabilityStudentLoansDisbursement] - sub_type: Optional[AccountLiabilityStudentLoanSubTypesLiterals] - original_loan_amount: Optional[int] - term_length: Optional[int] +from method.resources.Accounts.Types import AccountLiabilityTypesLiterals, AchAccountSubTypesLiterals, \ + AccountStatusesLiterals, AccountTypesLiterals, AccountProductTypesLiterals, AccountSubscriptionTypesLiterals, \ + AccountLiabilityTypesLiterals, TradelineAccountOwnershipLiterals +from method.resources.Accounts.Balances import AccountBalance, AccountBalancesResource +from method.resources.Accounts.CardBrands import AccountCardBrand, AccountCardBrandsResource +from method.resources.Accounts.Payoffs import AccountPayoff, AccountPayoffsResource +from method.resources.Accounts.Sensitive import AccountSensitive, AccountSensitiveResource +from method.resources.Accounts.Subscriptions import AccountSubscription, AccountSubscriptionsResource +from method.resources.Accounts.Transactions import AccountTransaction, AccountTransactionsResource +from method.resources.Accounts.Updates import AccountUpdate, AccountUpdatesResource +from method.resources.Accounts.VerificationSessions import AccountVerificationSession, AccountVerificationSessionResource class AccountACH(TypedDict): diff --git a/method/resources/Accounts/Types.py b/method/resources/Accounts/Types.py new file mode 100644 index 0000000..a56f482 --- /dev/null +++ b/method/resources/Accounts/Types.py @@ -0,0 +1,188 @@ +from typing import Literal, Optional, TypedDict + +AccountLiabilityTypesLiterals = Literal[ + 'student_loans', + 'credit_card', + 'mortgage', + 'auto_loan', + 'collection', + 'personal_loan', + 'business_loan', + 'insurance', + 'credit_builder', + 'subscription', + 'utility', + 'medical', + 'loan' +] + +AccountLiabilityDataSourcesLiterls = Literal[ + 'credit_report', + 'financial_institution', + 'unavailable' +] + + +AccountLiabilityDataStatusesLiterals = Literal[ + 'active', + 'syncing', + 'unavailable', + 'failed', + 'pending' +] + + +AccountProductTypesLiterals = Literal[ + 'payment', + 'balance', + 'sensitive', + 'card_brand', + 'payoff', + 'update' +] + + +AccountSubscriptionTypesLiterals = Literal[ + 'transactions', + 'update', + 'update.snapshot' +] + + +AccountStatusesLiterals = Literal[ + 'active', + 'disabled', + 'closed', + 'processing' +] + + +AccountTypesLiterals = Literal[ + 'ach', + 'liability' +] + + +TradelineAccountOwnershipLiterals = Literal[ + 'primary', + 'authorized', + 'joint', + 'unknown' +] + + +AccountInterestRateTypesLiterals = Literal[ + 'fixed', + 'variable' +] + + +AccountInterestRateSourcesLiterals = Literal[ + 'financial_institution', + 'public_data', + 'method' +] + + +AchAccountSubTypesLiterals = Literal[ + 'checking', + 'savings' +] + + +AccountLiabilityAutoLoanSubTypesLiterals = Literal[ + 'lease', + 'loan' +] + + +AccountLiabilityCreditCardSubTypesLiterals = Literal[ + 'flexible_spending', + 'charge', + 'secured', + 'unsecured', + 'purchase', + 'business' +] + + +AccountLiabilityCreditCardUsageTypesLiterals = Literal[ + 'transactor', + 'revolver', + 'dormant', + 'unknown' +] + + +AccountLiabilityPersonalLoanSubTypesLiterals = Literal[ + 'secured', + 'unsecured', + 'note', + 'line_of_credit', + 'heloc' +] + + +AccountLiabilityStudentLoanSubTypesLiterals = Literal[ + 'federal', + 'private' +] + + +AccountLiabilityMortgageSubTypesLiterals = Literal[ + 'loan' +] + + +class AccountLiabilityBase(TypedDict): + balance: Optional[int] + closed_at: Optional[str] + last_payment_amount: Optional[int] + last_payment_date: Optional[str] + next_payment_due_date: Optional[str] + next_payment_minimum_amount: Optional[int] + opened_at: Optional[str] + + +class AccountLiabilityLoanBase(AccountLiabilityBase): + expected_payoff_date: Optional[str] + interest_rate_percentage: Optional[float] + interest_rate_source: Optional[AccountInterestRateSourcesLiterals] + interest_rate_type: Optional[AccountInterestRateTypesLiterals] + original_loan_amount: Optional[int] + term_length: Optional[int] + + +class AccountLiabilityAutoLoan(AccountLiabilityLoanBase): + sub_type: Optional[AccountLiabilityAutoLoanSubTypesLiterals] + + +class AccountLiabilityCreditCard(AccountLiabilityBase): + available_credit: Optional[int] + credit_limit: Optional[int] + interest_rate_percentage_max: Optional[float] + interest_rate_percentage_min: Optional[float] + interest_rate_type: Optional[AccountInterestRateTypesLiterals] + sub_type: Optional[AccountLiabilityCreditCardSubTypesLiterals] + usage_pattern: Optional[AccountLiabilityCreditCardUsageTypesLiterals] + + +class AccountLiabilityMortgage(AccountLiabilityLoanBase): + sub_type: Optional[AccountLiabilityMortgageSubTypesLiterals] + + +class AccountLiabilityPersonalLoan(AccountLiabilityLoanBase): + sub_type: Optional[AccountLiabilityPersonalLoanSubTypesLiterals] + available_credit: Optional[int] + + +class AccountLiabilityStudentLoansDisbursement(AccountLiabilityLoanBase): + sequence: int + disbursed_at: Optional[str] + + +class AccountLiabilityStudentLoans(AccountLiabilityBase): + disbursements: Optional[AccountLiabilityStudentLoansDisbursement] + sub_type: Optional[AccountLiabilityStudentLoanSubTypesLiterals] + original_loan_amount: Optional[int] + term_length: Optional[int] \ No newline at end of file diff --git a/method/resources/Accounts/Updates.py b/method/resources/Accounts/Updates.py index 70dd9a0..29dc924 100644 --- a/method/resources/Accounts/Updates.py +++ b/method/resources/Accounts/Updates.py @@ -3,9 +3,8 @@ from method.resource import Resource, ResourceListOpts, ResourceStatusLiterals from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Accounts.Account import AccountLiabilityTypesLiterals, AccountLiabilityAutoLoan, AccountLiabilityCollection, \ - AccountLiabilityCreditCard, AccountLiabilityCreditBuilder, AccountLiabilityMortgage, AccountLiabilityStudentLoan, AccountLiabilityStudentLoans, \ - AccountLiabilityInsurance, AccountLiabilityLoan, AccountLiabilityMedical, AccountLiabilityPersonalLoan, AccountLiabilityUtility +from method.resources.Accounts.Types import AccountLiabilityTypesLiterals, AccountLiabilityAutoLoan, \ + AccountLiabilityCreditCard, AccountLiabilityMortgage, AccountLiabilityStudentLoans, AccountLiabilityPersonalLoan class AccountUpdate(TypedDict): diff --git a/method/resources/Accounts/__init__.py b/method/resources/Accounts/__init__.py index 6f9a3e5..dc14354 100644 --- a/method/resources/Accounts/__init__.py +++ b/method/resources/Accounts/__init__.py @@ -1,9 +1 @@ -from method.resources.Accounts.Account import Account, AccountResource -from method.resources.Accounts.Balances import AccountBalance, AccountBalancesResource -from method.resources.Accounts.CardBrands import AccountCardBrand, AccountCardBrandsResource -from method.resources.Accounts.Payoffs import AccountPayoff, AccountPayoffsResource -from method.resources.Accounts.Sensitive import AccountSensitive, AccountSensitiveResource -from method.resources.Accounts.Subscriptions import AccountSubscription, AccountSubscriptionsResource -from method.resources.Accounts.Transactions import AccountTransaction, AccountTransactionsResource -from method.resources.Accounts.Updates import AccountUpdate, AccountUpdatesResource -from method.resources.Accounts.VerificationSessions import AccountVerificationSession, AccountVerificationSessionResource \ No newline at end of file +from method.resources.Accounts.Account import Account, AccountResource \ No newline at end of file diff --git a/method/resources/Element.py b/method/resources/Element.py index c2c0880..6a66496 100644 --- a/method/resources/Element.py +++ b/method/resources/Element.py @@ -2,7 +2,7 @@ from method.resource import Resource from method.configuration import Configuration -from method.resources.Account import Account +from method.resources.Accounts import Account ElementTypesLiterals = Literal[ diff --git a/method/resources/Entities/CreditScores.py b/method/resources/Entities/CreditScores.py index c90f405..83a304e 100644 --- a/method/resources/Entities/CreditScores.py +++ b/method/resources/Entities/CreditScores.py @@ -3,7 +3,7 @@ from method.resource import Resource from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Entities.Entity import EntityStatusesLiterals, CreditReportBureausLiterals +from method.resources.Entities.Types import EntityStatusesLiterals, CreditReportBureausLiterals CreditScoresModelLiterals = Literal[ diff --git a/method/resources/Entities/Entity.py b/method/resources/Entities/Entity.py index f61180b..260413b 100644 --- a/method/resources/Entities/Entity.py +++ b/method/resources/Entities/Entity.py @@ -3,118 +3,15 @@ from method.resource import Resource, RequestOpts, ResourceListOpts from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Entities import EntityConnectResource, EntityCreditScoresResource, EntityIdentityResource, \ - EntityProductResource, EntitySensitiveResource, EntitySubscriptionsResource, EntityVerificationSessionResource - - -EntityTypesLiterals = Literal[ - 'individual', - 'c_corporation', - 's_corporation', - 'llc', - 'partnership', - 'sole_proprietorship', - 'receive_only' -] - - -EntityCapabilitiesLiterals = Literal[ - 'payments:send', - 'payments:receive', - 'payments:limited-send', - 'data:retrieve' -] - - -EntityStatusesLiterals = Literal[ - 'active', - 'incomplete', - 'disabled' -] - - -CreditScoreStatusesLiterals = Literal[ - 'completed', - 'in_progress', - 'pending', - 'failed' -] - - -EntityIndividualPhoneVerificationTypesLiterals = Literal[ - 'method_sms', - 'method_verified', - 'sms', - 'tos' -] - - -CreditScoreStatusesLiterals = Literal[ - 'completed', - 'in_progress', - 'pending', - 'failed' -] - - -CreditReportBureausLiterals = Literal[ - 'experian', - 'equifax', - 'transunion' -] - -EntitySensitiveFieldsLiterals = Literal[ - 'first_name', - 'last_name', - 'phone', - 'phone_history', - 'email', - 'dob', - 'address', - 'address_history', - 'ssn_4', - 'ssn_6', - 'ssn_9', - 'identities' -] - - -class EntityIndividual(TypedDict): - first_name: Optional[str] - last_name: Optional[str] - phone: Optional[str] - email: Optional[str] - dob: Optional[str] - - -class EntityAddress(TypedDict): - line1: Optional[str] - line2: Optional[str] - city: Optional[str] - state: Optional[str] - zip: Optional[str] - - -class EntityCorporationOwner(TypedDict): - first_name: Optional[str] - last_name: Optional[str] - phone: Optional[str] - email: Optional[str] - dob: Optional[str] - address: EntityAddress - - -class EntityCorporation(TypedDict): - name: Optional[str] - dba: Optional[str] - ein: Optional[str] - owners: List[EntityCorporationOwner] - - -class EntityReceiveOnly(TypedDict): - name: str - phone: Optional[str] - email: Optional[str] +from method.resources.Entities.Types import EntityTypesLiterals, EntityCapabilitiesLiterals, EntityStatusesLiterals, \ + CreditReportBureausLiterals, EntityIndividual, EntityCorporation, EntityReceiveOnly, EntityAddress +from method.resources.Entities.Connect import EntityConnectResource +from method.resources.Entities.CreditScores import EntityCreditScoresResource +from method.resources.Entities.Identities import EntityIdentityResource +from method.resources.Entities.Products import EntityProductResource +from method.resources.Entities.Sensitive import EntitySensitiveResource +from method.resources.Entities.Subscriptions import EntitySubscriptionsResource +from method.resources.Entities.VerificationSessions import EntityVerificationSessionResource class Entity(TypedDict): @@ -261,9 +158,6 @@ def retrieve(self, _id: str) -> Entity: def list(self, params: EntityListOpts = None) -> List[Entity]: return super(EntityResource, self)._list(params) - def refresh_capabilities(self, _id: str) -> Entity: - return super(EntityResource, self)._create_with_sub_path('{_id}/refresh_capabilities'.format(_id=_id), {}) - def withdraw_consent(self, _id: str) -> Entity: return super(EntityResource, self)._create_with_sub_path( '{_id}/consent'.format(_id=_id), diff --git a/method/resources/Entities/Identities.py b/method/resources/Entities/Identities.py index 2ae3fae..40ca4a0 100644 --- a/method/resources/Entities/Identities.py +++ b/method/resources/Entities/Identities.py @@ -3,7 +3,7 @@ from method.resource import Resource from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Entities.Entity import EntityIdentity +from method.resources.Entities.Types import EntityIdentity EntityVerificationSessionStatusLiterals = Literal[ diff --git a/method/resources/Entities/Types.py b/method/resources/Entities/Types.py index ece2c16..406b6f0 100644 --- a/method/resources/Entities/Types.py +++ b/method/resources/Entities/Types.py @@ -1,4 +1,114 @@ -from typing import TypedDict, Optional +from typing import TypedDict, Optional, Literal, List + + +EntityTypesLiterals = Literal[ + 'individual', + 'c_corporation', + 's_corporation', + 'llc', + 'partnership', + 'sole_proprietorship', + 'receive_only' +] + + +EntityCapabilitiesLiterals = Literal[ + 'payments:send', + 'payments:receive', + 'payments:limited-send', + 'data:retrieve' +] + + +EntityStatusesLiterals = Literal[ + 'active', + 'incomplete', + 'disabled' +] + + +CreditScoreStatusesLiterals = Literal[ + 'completed', + 'in_progress', + 'pending', + 'failed' +] + + +EntityIndividualPhoneVerificationTypesLiterals = Literal[ + 'method_sms', + 'method_verified', + 'sms', + 'tos' +] + + +CreditScoreStatusesLiterals = Literal[ + 'completed', + 'in_progress', + 'pending', + 'failed' +] + + +CreditReportBureausLiterals = Literal[ + 'experian', + 'equifax', + 'transunion' +] + +EntitySensitiveFieldsLiterals = Literal[ + 'first_name', + 'last_name', + 'phone', + 'phone_history', + 'email', + 'dob', + 'address', + 'address_history', + 'ssn_4', + 'ssn_6', + 'ssn_9', + 'identities' +] + + +class EntityIndividual(TypedDict): + first_name: Optional[str] + last_name: Optional[str] + phone: Optional[str] + email: Optional[str] + dob: Optional[str] + + +class EntityAddress(TypedDict): + line1: Optional[str] + line2: Optional[str] + city: Optional[str] + state: Optional[str] + zip: Optional[str] + + +class EntityCorporationOwner(TypedDict): + first_name: Optional[str] + last_name: Optional[str] + phone: Optional[str] + email: Optional[str] + dob: Optional[str] + address: EntityAddress + + +class EntityCorporation(TypedDict): + name: Optional[str] + dba: Optional[str] + ein: Optional[str] + owners: List[EntityCorporationOwner] + + +class EntityReceiveOnly(TypedDict): + name: str + phone: Optional[str] + email: Optional[str] class EntityKYCAddressRecordData(TypedDict): address: str diff --git a/method/resources/Entities/__init__.py b/method/resources/Entities/__init__.py index 6b425d3..aac81d9 100644 --- a/method/resources/Entities/__init__.py +++ b/method/resources/Entities/__init__.py @@ -1,8 +1 @@ -from method.resources.Entities.Entity import Entity, EntityResource -from method.resources.Entities.Connect import EntityConnect, EntityConnectResource -from method.resources.Entities.CreditScores import EntityCreditScores, EntityCreditScoresResource -from method.resources.Entities.Identities import EntityIdentity, EntityIdentityResource -from method.resources.Entities.Products import EntityProduct, EntityProductResource -from method.resources.Entities.Sensitive import EntitySensitive, EntitySensitiveResource -from method.resources.Entities.Subscriptions import EntitySubscription, EntitySubscriptionsResource -from method.resources.Entities.VerificationSessions import EntityVerificationSession, EntityVerificationSessionResource \ No newline at end of file +from method.resources.Entities.Entity import Entity, EntityResource \ No newline at end of file diff --git a/method/resources/__init__.py b/method/resources/__init__.py index 94f9acd..1fddf6b 100644 --- a/method/resources/__init__.py +++ b/method/resources/__init__.py @@ -1,11 +1,7 @@ # type: ignore -from method.resources.Accounts import Account, AccountResource, AccountPayoffsResource, \ - AccountBalancesResource, AccountCardsResource, AccountSensitiveResource, AccountVerificationSessionResource, \ - AccountSubscriptionsResource, AccountTransactionsResource, AccountUpdatesResource +from method.resources.Accounts import Account, AccountResource from method.resources.Element import Element, ElementResource -from method.resources.Entities import Entity, EntityResource, EntityConnectResource, \ - EntityCreditScoresResource, EntityIdentityResource, EntityVerificationSessionResource, \ - EntityProductResource, EntitySensitiveResource, EntitySubscriptionsResource +from method.resources.Entities import Entity, EntityResource from method.resources.Merchant import Merchant, MerchantResource from method.resources.Payments import Payment, PaymentResource from method.resources.Report import Report, ReportResource diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..2f4c80e --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +asyncio_mode = auto diff --git a/requirements.txt b/requirements.txt index b077a73..73c47e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -hammock==0.2.4 \ No newline at end of file +hammock==0.2.4 +pytest==8.2.1 \ No newline at end of file diff --git a/test/resources/test_account.py b/test/resources/test_account.py new file mode 100644 index 0000000..3ba622a --- /dev/null +++ b/test/resources/test_account.py @@ -0,0 +1,86 @@ +import pytest +from method import Method + +# Create an instance of Method +method = Method(env='dev', api_key='{API_KEY}') + +@pytest.fixture(scope="module") +def setup(): + holder_1_response = method.entities.create({ + 'type': 'individual', + 'individual': { + 'first_name': 'Test', + 'last_name': 'McTesterson', + 'phone': '+15121231111' + } + }) + + method.entities(holder_1_response['id']).verification_sessions.create({ + 'type': 'phone', + 'method': 'byo_sms', + 'byo_sms': { + 'timestamp': '2021-09-01T00:00:00.000Z', + }, + }) + + method.entities(holder_1_response['id']).verification_sessions.create({ + 'type': 'identity', + 'method': 'kba', + 'kba': {}, + }) + + holder_connect_response = method.entities(holder_1_response['id']).connect.create() + + test_credit_card_accounts = method.accounts.list({ + 'holder_id': holder_1_response['id'], + 'liability.type': 'credit_card', + 'liability.mch_id': 'mch_302086', + }) + test_credit_card_account = test_credit_card_accounts[0] + + test_auto_loan_accounts = method.accounts.list({ + 'holder_id': holder_1_response['id'], + 'liability.type': 'auto_loan', + 'liability.mch_id': 'mch_2347', + }) + test_auto_loan_account = test_auto_loan_accounts[0] + + return { + 'holder_1_response': holder_1_response, + 'holder_connect_response': holder_connect_response, + 'test_credit_card_account': test_credit_card_account, + 'test_auto_loan_account': test_auto_loan_account, + } + +def test_create_ach_account(setup): + holder_1_response = setup['holder_1_response'] + + accounts_create_ach_response = method.accounts.create({ + 'holder_id': holder_1_response['id'], + 'ach': { + 'routing': '062103000', + 'number': '123456789', + 'type': 'checking', + }, + }) + + expect_results = { + 'id': accounts_create_ach_response['id'], + 'holder_id': holder_1_response['id'], + 'type': 'ach', + 'ach': { + 'routing': '062103000', + 'number': '123456789', + 'type': 'checking', + }, + 'latest_verification_session': accounts_create_ach_response['latest_verification_session'], + 'products': ['payment'], + 'restricted_products': [], + 'status': 'active', + 'error': None, + 'metadata': None, + 'created_at': accounts_create_ach_response['created_at'], + 'updated_at': accounts_create_ach_response['updated_at'], + } + + assert accounts_create_ach_response == expect_results From e8826b23cd6dd86d1b44112e064001f23da0e683 Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Thu, 30 May 2024 16:22:13 -0400 Subject: [PATCH 11/22] added more account tests, updated simulate transactions, updated requirements to include testing modules, added testing utils --- method/resources/Accounts/Account.py | 72 +-- method/resources/Accounts/CardBrands.py | 4 +- method/resources/Accounts/Subscriptions.py | 6 +- method/resources/Accounts/Transactions.py | 2 +- method/resources/Accounts/Types.py | 108 ++-- method/resources/Simulate/Accounts.py | 19 + method/resources/Simulate/Simulate.py | 6 +- requirements.txt | 4 +- test/resources/Account_test.py | 650 +++++++++++++++++++++ test/resources/test_account.py | 86 --- test/resources/utils.py | 20 + 11 files changed, 790 insertions(+), 187 deletions(-) create mode 100644 method/resources/Simulate/Accounts.py create mode 100644 test/resources/Account_test.py delete mode 100644 test/resources/test_account.py create mode 100644 test/resources/utils.py diff --git a/method/resources/Accounts/Account.py b/method/resources/Accounts/Account.py index 551ff89..bb7f2eb 100644 --- a/method/resources/Accounts/Account.py +++ b/method/resources/Accounts/Account.py @@ -1,36 +1,20 @@ -from typing import TypedDict, Optional, Dict, List, Any, Literal, Union - +from typing import TypedDict, Optional, Dict, List, Any, Literal, Union, TypeVar from method.resource import Resource, RequestOpts -from method.configuration import Configuration from method.errors import ResourceError -from method.resources.Accounts.Types import AccountLiabilityTypesLiterals, AchAccountSubTypesLiterals, \ - AccountStatusesLiterals, AccountTypesLiterals, AccountProductTypesLiterals, AccountSubscriptionTypesLiterals, \ - AccountLiabilityTypesLiterals, TradelineAccountOwnershipLiterals +from method.configuration import Configuration +from method.resources.Accounts.Types import AccountACH, AccountStatusesLiterals, AccountTypesLiterals, \ + AccountProductTypesLiterals, AccountSubscriptionTypesLiterals, AccountExpandableFieldsLiterals, \ + AccountLiability from method.resources.Accounts.Balances import AccountBalance, AccountBalancesResource from method.resources.Accounts.CardBrands import AccountCardBrand, AccountCardBrandsResource from method.resources.Accounts.Payoffs import AccountPayoff, AccountPayoffsResource from method.resources.Accounts.Sensitive import AccountSensitive, AccountSensitiveResource -from method.resources.Accounts.Subscriptions import AccountSubscription, AccountSubscriptionsResource +from method.resources.Accounts.Subscriptions import AccountSubscriptionsResource from method.resources.Accounts.Transactions import AccountTransaction, AccountTransactionsResource from method.resources.Accounts.Updates import AccountUpdate, AccountUpdatesResource from method.resources.Accounts.VerificationSessions import AccountVerificationSession, AccountVerificationSessionResource -class AccountACH(TypedDict): - routing: int - number: int - type: AchAccountSubTypesLiterals - - -class AccountLiability(TypedDict): - mch_id: str - mask: Optional[str] - ownership: Optional[TradelineAccountOwnershipLiterals] - fingerprint: Optional[str] - type: Optional[AccountLiabilityTypesLiterals] - name: Optional[str] - - class AccountCreateOpts(TypedDict): holder_id: str metadata: Optional[Dict[str, Any]] @@ -50,6 +34,21 @@ class AccountLiabilityCreateOpts(AccountCreateOpts): liability: LiabilityCreateOpts +AccountListOpts = TypedDict('AccountListOpts', { + 'to_date': Optional[str], + 'from_date': Optional[str], + 'page': Optional[int], + 'page_limit': Optional[int], + 'page_cursor': Optional[str], + 'status': Optional[str], + 'type': Optional[str], + 'holder_id': Optional[str], + 'expand': Optional[List[AccountExpandableFieldsLiterals]], + 'liability.mch_id': Optional[str], + 'liability.type': Optional[str] +}) + + class Account(TypedDict): id: str holder_id: str @@ -72,21 +71,10 @@ class Account(TypedDict): error: Optional[ResourceError] created_at: str updated_at: str - metadata: Optional[Dict[str, Any]] + metadata: Optional[Dict[str, str]] -AccountListOpts = TypedDict('AccountListOpts', { - 'to_date': Optional[str], - 'from_date': Optional[str], - 'page': Optional[int], - 'page_limit': Optional[int], - 'page_cursor': Optional[str], - 'status': Optional[str], - 'type': Optional[str], - 'holder_id': Optional[str], - 'liability.mch_id': Optional[str], - 'liability.type': Optional[str] -}) +T = TypeVar('T', bound='Account') class AccountWithdrawConsentOpts(TypedDict): @@ -120,18 +108,18 @@ class AccountResource(Resource): def __init__(self, config: Configuration): super(AccountResource, self).__init__(config.add_path('accounts')) - def __call__(self, _id: str) -> AccountSubResources: - return AccountSubResources(_id, self.config) + def __call__(self, acc_id: str) -> AccountSubResources: + return AccountSubResources(acc_id, self.config) - def retrieve(self, _id: str) -> Account: - return super(AccountResource, self)._get_with_id(_id) + def retrieve(self, acc_id: str, params: Optional[Dict[str, List[AccountExpandableFieldsLiterals]]] = None) -> Account: + return super(AccountResource, self)._get_with_sub_path_and_params(acc_id, params) - def list(self, params: Optional[AccountListOpts] = None) -> List[Account]: + def list(self, params: Optional[AccountListOpts[T]] = None) -> List[Account]: return super(AccountResource, self)._list(params) def create(self, opts: Union[AccountACHCreateOpts, AccountLiabilityCreateOpts], request_opts: Optional[RequestOpts] = None) -> Account: return super(AccountResource, self)._create(opts, request_opts) - def withdraw_consent(self, _id: str, data: AccountWithdrawConsentOpts = { 'type': 'withdraw', 'reason': 'holder_withdrew_consent' }) -> Account: - return super(AccountResource, self)._create_with_sub_path('{_id}/consent'.format(_id=_id), data) + def withdraw_consent(self, acc_id: str, data: AccountWithdrawConsentOpts = { 'type': 'withdraw', 'reason': 'holder_withdrew_consent' }) -> Account: + return super(AccountResource, self)._create_with_sub_path('{acc_id}/consent'.format(acc_id=acc_id), data) diff --git a/method/resources/Accounts/CardBrands.py b/method/resources/Accounts/CardBrands.py index 019d72d..856a182 100644 --- a/method/resources/Accounts/CardBrands.py +++ b/method/resources/Accounts/CardBrands.py @@ -27,10 +27,10 @@ class AccountCardBrand(TypedDict): class AccountCardBrandsResource(Resource): def __init__(self, config: Configuration): - super(AccountCardBrandsResource, self).__init__(config.add_path('cards')) + super(AccountCardBrandsResource, self).__init__(config.add_path('card_brands')) def retrieve(self, crbd_id: str) -> AccountCardBrand: return super(AccountCardBrandsResource, self)._get_with_id(crbd_id) - def _create(self) -> AccountCardBrand: + def create(self) -> AccountCardBrand: return super(AccountCardBrandsResource, self)._create({}) \ No newline at end of file diff --git a/method/resources/Accounts/Subscriptions.py b/method/resources/Accounts/Subscriptions.py index 943c050..6f9b72c 100644 --- a/method/resources/Accounts/Subscriptions.py +++ b/method/resources/Accounts/Subscriptions.py @@ -28,15 +28,15 @@ class AccountSubscription(TypedDict): class AccountSubscriptionCreateOpts(TypedDict): - enroll: List[AccountSubscriptionTypesLiterals] + enroll: AccountSubscriptionTypesLiterals class AccountSubscriptionsResource(Resource): def __init__(self, config: Configuration): super(AccountSubscriptionsResource, self).__init__(config.add_path('subscriptions')) - def create(self, data: AccountSubscriptionCreateOpts) -> AccountSubscription: - return super(AccountSubscriptionsResource, self)._create(data) + def create(self, sub_name: AccountSubscriptionCreateOpts) -> AccountSubscription: + return super(AccountSubscriptionsResource, self)._create({ 'enroll': sub_name }) def list(self) -> List[AccountSubscription]: return super(AccountSubscriptionsResource, self)._get() diff --git a/method/resources/Accounts/Transactions.py b/method/resources/Accounts/Transactions.py index efb36b3..544a28f 100644 --- a/method/resources/Accounts/Transactions.py +++ b/method/resources/Accounts/Transactions.py @@ -58,5 +58,5 @@ def __init__(self, config: Configuration): def retrieve(self, txn_id: str) -> AccountTransaction: return super(AccountTransactionsResource, self)._get_with_id(txn_id) - def list(self, params: ResourceListOpts) -> List[AccountTransaction]: + def list(self, params: Optional[ResourceListOpts] = None) -> List[AccountTransaction]: return super(AccountTransactionsResource, self)._list(params) diff --git a/method/resources/Accounts/Types.py b/method/resources/Accounts/Types.py index a56f482..4ef0284 100644 --- a/method/resources/Accounts/Types.py +++ b/method/resources/Accounts/Types.py @@ -1,34 +1,16 @@ from typing import Literal, Optional, TypedDict -AccountLiabilityTypesLiterals = Literal[ - 'student_loans', - 'credit_card', - 'mortgage', - 'auto_loan', - 'collection', - 'personal_loan', - 'business_loan', - 'insurance', - 'credit_builder', - 'subscription', - 'utility', - 'medical', - 'loan' -] -AccountLiabilityDataSourcesLiterls = Literal[ - 'credit_report', - 'financial_institution', - 'unavailable' +AccountTypesLiterals = Literal[ + 'ach', + 'liability' ] -AccountLiabilityDataStatusesLiterals = Literal[ +AccountStatusesLiterals = Literal[ 'active', - 'syncing', - 'unavailable', - 'failed', - 'pending' + 'disabled', + 'closed' ] @@ -49,25 +31,39 @@ ] -AccountStatusesLiterals = Literal[ - 'active', - 'disabled', - 'closed', - 'processing' +AccountOwnershipLiterals = Literal[ + 'primary', + 'authorized', + 'joint', + 'unknown' ] -AccountTypesLiterals = Literal[ - 'ach', - 'liability' +AccountUpdateSourceLiterals = Literal[ + 'direct', + 'snapshot' ] -TradelineAccountOwnershipLiterals = Literal[ - 'primary', - 'authorized', - 'joint', - 'unknown' +AccountLiabilityTypesLiterals = Literal[ + 'auto_loan', + 'credit_card', + 'collection', + 'mortgage', + 'personal_loan', + 'student_loans' +] + + +AchAccountSubTypesLiterals = Literal[ + 'checking', + 'savings' +] + + +AccountExpandableFieldsLiterals = Literal[ + AccountProductTypesLiterals, + 'latest_verification_session' ] @@ -84,11 +80,6 @@ ] -AchAccountSubTypesLiterals = Literal[ - 'checking', - 'savings' -] - AccountLiabilityAutoLoanSubTypesLiterals = Literal[ 'lease', @@ -114,6 +105,11 @@ ] +AccountLiabilityMortgageSubTypesLiterals = Literal[ + 'loan' +] + + AccountLiabilityPersonalLoanSubTypesLiterals = Literal[ 'secured', 'unsecured', @@ -129,11 +125,6 @@ ] -AccountLiabilityMortgageSubTypesLiterals = Literal[ - 'loan' -] - - class AccountLiabilityBase(TypedDict): balance: Optional[int] closed_at: Optional[str] @@ -167,13 +158,17 @@ class AccountLiabilityCreditCard(AccountLiabilityBase): usage_pattern: Optional[AccountLiabilityCreditCardUsageTypesLiterals] +class AccountLiabilityCollection(AccountLiabilityBase): + pass + + class AccountLiabilityMortgage(AccountLiabilityLoanBase): sub_type: Optional[AccountLiabilityMortgageSubTypesLiterals] class AccountLiabilityPersonalLoan(AccountLiabilityLoanBase): - sub_type: Optional[AccountLiabilityPersonalLoanSubTypesLiterals] available_credit: Optional[int] + sub_type: Optional[AccountLiabilityPersonalLoanSubTypesLiterals] class AccountLiabilityStudentLoansDisbursement(AccountLiabilityLoanBase): @@ -185,4 +180,19 @@ class AccountLiabilityStudentLoans(AccountLiabilityBase): disbursements: Optional[AccountLiabilityStudentLoansDisbursement] sub_type: Optional[AccountLiabilityStudentLoanSubTypesLiterals] original_loan_amount: Optional[int] - term_length: Optional[int] \ No newline at end of file + term_length: Optional[int] + + +class AccountLiability(TypedDict): + mch_id: str + mask: Optional[str] + ownership: Optional[AccountOwnershipLiterals] + fingerprint: Optional[str] + type: Optional[AccountLiabilityTypesLiterals] + name: Optional[str] + + +class AccountACH(TypedDict): + routing: int + number: int + type: AchAccountSubTypesLiterals diff --git a/method/resources/Simulate/Accounts.py b/method/resources/Simulate/Accounts.py new file mode 100644 index 0000000..92970c7 --- /dev/null +++ b/method/resources/Simulate/Accounts.py @@ -0,0 +1,19 @@ +from typing import Any +from method.resource import Resource +from method.configuration import Configuration +from method.resources.Simulate.Transactions import SimulateTransactionsResource + + +class SimulateAccountSubResources: + transactions: SimulateTransactionsResource + + def __init__(self, _id: str, config: Configuration): + self.transactions = SimulateTransactionsResource(config.add_path(_id)) + + +class SimulateAccountResource(Resource): + def __init__(self, config: Configuration): + super(SimulateAccountResource, self).__init__(config.add_path('accounts')) + + def __call__(self, acc_id) -> SimulateAccountSubResources: + return SimulateAccountSubResources(acc_id, self.config) \ No newline at end of file diff --git a/method/resources/Simulate/Simulate.py b/method/resources/Simulate/Simulate.py index 88524b7..db3b21f 100644 --- a/method/resources/Simulate/Simulate.py +++ b/method/resources/Simulate/Simulate.py @@ -1,15 +1,15 @@ from method.resource import Resource from method.configuration import Configuration from method.resources.Simulate.Payments import SimulatePaymentResource -from method.resources.Simulate.Transactions import SimulateTransactionsResource +from method.resources.Simulate.Accounts import SimulateAccountResource class SimulateResource(Resource): payments: SimulatePaymentResource - transactions: SimulateTransactionsResource + accounts: SimulateAccountResource def __init__(self, config: Configuration): _config = config.add_path('simulate') super(SimulateResource, self).__init__(_config) self.payments = SimulatePaymentResource(_config) - self.transactions = SimulateTransactionsResource(_config) + self.accounts = SimulateAccountResource(_config) diff --git a/requirements.txt b/requirements.txt index 73c47e3..0f3ff42 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ hammock==0.2.4 -pytest==8.2.1 \ No newline at end of file +pytest==8.2.1 +pytest-asyncio==0.23.7 +python-dotenv==1.0.1 \ No newline at end of file diff --git a/test/resources/Account_test.py b/test/resources/Account_test.py new file mode 100644 index 0000000..075eafe --- /dev/null +++ b/test/resources/Account_test.py @@ -0,0 +1,650 @@ +import os +import pytest +from method import Method +from dotenv import load_dotenv, dotenv_values +from utils import await_results + +load_dotenv() + +API_KEY = os.getenv('API_KEY') + +# Create an instance of Method +method = Method(env='dev', api_key=API_KEY) + +accounts_create_ach_response = None +accounts_create_liability_response = None +accounts_retrieve_response = None +accounts_list_response = None +balances_create_response = None +card_brand_create_response = None +payoff_create_response = None +verification_session_create = None +verification_session_update = None +sensitive_data_response = None +transactions_response = None +create_txn_subscriptions_response = None +create_update_subscriptions_response = None +create_update_snapshot_subscriptions_response = None +create_updates_response = None + + +@pytest.fixture(scope="module") +def setup(): + holder_1_response = method.entities.create({ + 'type': 'individual', + 'individual': { + 'first_name': 'Test', + 'last_name': 'McTesterson', + 'phone': '+15121231111' + } + }) + + phone_verification = method.entities(holder_1_response['id']).verification_sessions.create({ + 'type': 'phone', + 'method': 'byo_sms', + 'byo_sms': { + 'timestamp': '2021-09-01T00:00:00.000Z', + }, + }) + + identity_verification = method.entities(holder_1_response['id']).verification_sessions.create({ + 'type': 'identity', + 'method': 'kba', + 'kba': {}, + }) + + holder_connect_response = method.entities(holder_1_response['id']).connect.create() + + test_credit_card_accounts = method.accounts.list({ + 'holder_id': holder_1_response['id'], + 'liability.type': 'credit_card', + 'liability.mch_id': 'mch_302086', + }) + test_credit_card_account = test_credit_card_accounts[0] + + test_auto_loan_accounts = method.accounts.list({ + 'holder_id': holder_1_response['id'], + 'liability.type': 'auto_loan', + 'liability.mch_id': 'mch_2347', + }) + test_auto_loan_account = test_auto_loan_accounts[0] + + return { + 'holder_1_response': holder_1_response, + 'holder_connect_response': holder_connect_response, + 'test_credit_card_account': test_credit_card_account, + 'test_auto_loan_account': test_auto_loan_account, + 'phone_verification': phone_verification, + 'identity_verification': identity_verification, + } + + +def test_create_ach_account(setup): + holder_1_response = setup['holder_1_response'] + global accounts_create_ach_response + + accounts_create_ach_response = method.accounts.create({ + 'holder_id': holder_1_response['id'], + 'ach': { + 'routing': '062103000', + 'number': '123456789', + 'type': 'checking', + }, + }) + + expect_results = { + 'id': accounts_create_ach_response['id'], + 'holder_id': holder_1_response['id'], + 'type': 'ach', + 'ach': { + 'routing': '062103000', + 'number': '123456789', + 'type': 'checking', + }, + 'latest_verification_session': accounts_create_ach_response['latest_verification_session'], + 'products': ['payment'], + 'restricted_products': [], + 'status': 'active', + 'error': None, + 'metadata': None, + 'created_at': accounts_create_ach_response['created_at'], + 'updated_at': accounts_create_ach_response['updated_at'], + } + + assert accounts_create_ach_response == expect_results + + +def test_create_liability_account(setup): + holder_1_response = setup['holder_1_response'] + global accounts_create_liability_response + + accounts_create_liability_response = method.accounts.create({ + 'holder_id': holder_1_response['id'], + 'liability': { + 'mch_id': 'mch_302086', + 'account_number': '4936494462408721', + }, + }) + + accounts_create_liability_response['products'] = accounts_create_liability_response['products'].sort() + + expect_results = { + 'id': accounts_create_liability_response['id'], + 'holder_id': holder_1_response['id'], + 'type': 'liability', + 'liability': { + 'fingerprint': None, + 'mch_id': 'mch_183', + 'mask': '8721', + 'ownership': 'unknown', + 'type': 'credit_card', + 'name': 'Chase Sapphire Reserve', + }, + 'latest_verification_session': accounts_create_liability_response['latest_verification_session'], + 'balance': None, + 'update': accounts_create_liability_response['update'], + 'card_brand': None, + 'products': [ 'balance', 'payment', 'sensitive', 'update' ].sort(), + 'restricted_products': accounts_create_liability_response['restricted_products'], + 'subscriptions': accounts_create_liability_response['subscriptions'], + 'available_subscriptions': [ 'update' ], + 'restricted_subscriptions': [], + 'status': 'active', + 'error': None, + 'metadata': None, + 'created_at': accounts_create_liability_response['created_at'], + 'updated_at': accounts_create_liability_response['updated_at'] + } + + assert accounts_create_liability_response == expect_results + + +def test_retrieve_account(setup): + accounts_retrieve_response = method.accounts.retrieve(accounts_create_ach_response['id']) + + expect_results = { + 'id': accounts_create_ach_response['id'], + 'holder_id': setup['holder_1_response']['id'], + 'type': 'ach', + 'ach': { + 'routing': '062103000', + 'number': '123456789', + 'type': 'checking', + }, + 'latest_verification_session': accounts_create_ach_response['latest_verification_session'], + 'products': ['payment'], + 'restricted_products': [], + 'status': 'active', + 'error': None, + 'metadata': None, + 'created_at': accounts_retrieve_response['created_at'], + 'updated_at': accounts_retrieve_response['updated_at'], + } + + assert accounts_retrieve_response == expect_results + + +def test_list_accounts(setup): + holder_1_response = setup['holder_1_response'] + holder_connect_response = setup['holder_connect_response'] + accounts_list_response = method.accounts.list({ + 'holder_id': holder_1_response['id'], + 'type': 'liability', + 'status': 'active', + }) + + account_ids = sorted( + [account['id'] for account in accounts_list_response if account['id'] != accounts_create_liability_response['id']], + reverse=True + )[:len(holder_connect_response['accounts']) if holder_connect_response['accounts'] else 0] + + connect_acc_ids = sorted(holder_connect_response['accounts'], reverse=True) if holder_connect_response['accounts'] else ['no_data'] + dupes = account_ids + connect_acc_ids + test_length = len(set(dupes)) + + assert accounts_list_response is not None + assert isinstance(accounts_list_response, list) + assert test_length == len(connect_acc_ids) + + +def test_create_balances(setup): + global balances_create_response + test_credit_card_account = setup['test_credit_card_account'] + + balances_create_response = method.accounts(test_credit_card_account['id']).balances.create() + + expect_results = { + 'id': balances_create_response['id'], + 'account_id': test_credit_card_account['id'], + 'status': 'pending', + 'amount': None, + 'error': None, + 'created_at': balances_create_response['created_at'], + 'updated_at': balances_create_response['updated_at'], + } + + assert balances_create_response == expect_results + + +@pytest.mark.asyncio +async def test_retrieve_balance(setup): + test_credit_card_account = setup['test_credit_card_account'] + + def get_account_balances(): + return method.accounts(test_credit_card_account['id']).balances.retrieve(balances_create_response['id']) + + balances_retrieve_response = await await_results(get_account_balances) + + expect_results = { + 'id': balances_create_response['id'], + 'account_id': test_credit_card_account['id'], + 'status': 'completed', + 'amount': 1866688, + 'error': None, + 'created_at': balances_retrieve_response['created_at'], + 'updated_at': balances_retrieve_response['updated_at'], + } + + assert balances_retrieve_response == expect_results + + +def test_create_card_brands(setup): + global card_brand_create_response + test_credit_card_account = setup['test_credit_card_account'] + + card_brand_create_response = method.accounts(test_credit_card_account['id']).card_brands.create() + + expect_results = { + 'id': card_brand_create_response['id'], + 'account_id': test_credit_card_account['id'], + 'network': 'visa', + 'status': 'completed', + 'issuer': None, + 'last4': '1580', + 'brands': card_brand_create_response['brands'], + 'shared': False, + 'error': None, + 'created_at': card_brand_create_response['created_at'], + 'updated_at': card_brand_create_response['updated_at'], + } + + assert card_brand_create_response == expect_results + + +def test_retrieve_card_brands(setup): + test_credit_card_account = setup['test_credit_card_account'] + card_retrieve_response = method.accounts(test_credit_card_account['id']).card_brands.retrieve(card_brand_create_response['id']) + + expect_results = { + 'id': card_brand_create_response['id'], + 'account_id': test_credit_card_account['id'], + 'network': 'visa', + 'status': 'completed', + 'issuer': None, + 'last4': '1580', + 'brands': card_brand_create_response['brands'], + 'shared': False, + 'error': None, + 'created_at': card_retrieve_response['created_at'], + 'updated_at': card_retrieve_response['updated_at'], + } + + assert card_retrieve_response == expect_results + + +def test_create_payoffs(setup): + global payoff_create_response + test_auto_loan_account = setup['test_auto_loan_account'] + + payoff_create_response = method.accounts(test_auto_loan_account['id']).payoffs.create() + + expect_results = { + 'id': payoff_create_response['id'], + 'account_id': test_auto_loan_account['id'], + 'amount': None, + 'per_diem_amount': None, + 'term': None, + 'status': 'pending', + 'error': None, + 'created_at': payoff_create_response['created_at'], + 'updated_at': payoff_create_response['updated_at'], + } + + assert payoff_create_response == expect_results + + +@pytest.mark.asyncio +async def test_retrieve_payoffs(setup): + test_auto_loan_account = setup['test_auto_loan_account'] + def get_payoff(): + return method.accounts(test_auto_loan_account['id']).payoffs.retrieve(payoff_create_response['id']) + + payoff_retrieve_response = await await_results(get_payoff) + + expect_results = { + 'id': payoff_create_response['id'], + 'account_id': test_auto_loan_account['id'], + 'amount': 6083988, + 'per_diem_amount': None, + 'term': 15, + 'status': 'completed', + 'error': None, + 'created_at': payoff_retrieve_response['created_at'], + 'updated_at': payoff_retrieve_response['updated_at'], + } + + assert payoff_retrieve_response == expect_results + + +def test_create_account_verification_sessions(setup): + global verification_session_create + test_credit_card_account = setup['test_credit_card_account'] + + verification_session_create = method.accounts(test_credit_card_account['id']).verification_sessions.create({ + 'type': 'pre_auth' + }) + + expect_results = { + 'id': verification_session_create['id'], + 'account_id': test_credit_card_account['id'], + 'status': 'pending', + 'type': 'pre_auth', + 'error': None, + 'pre_auth': { + 'billing_zip_code': 'xxxxx', + 'billing_zip_code_check': None, + 'cvv': None, + 'cvv_check': None, + 'exp_check': None, + 'exp_month': 'xx', + 'exp_year': 'xxxx', + 'number': 'xxxxxxxxxxxxxxxx', + 'pre_auth_check': None, + }, + 'created_at': verification_session_create['created_at'], + 'updated_at': verification_session_create['updated_at'], + } + + assert verification_session_create == expect_results + + +def test_update_account_verification_sessions(setup): + global verification_session_update + test_credit_card_account = setup['test_credit_card_account'] + + verification_session_update = method.accounts(test_credit_card_account['id']).verification_sessions.update(verification_session_create['id'], { + 'pre_auth': { + 'exp_month': '03', + 'exp_year': '2028', + 'billing_zip_code': '78758', + 'cvv': '878' + } + }) + + expect_results = { + 'id': verification_session_create['id'], + 'account_id': test_credit_card_account['id'], + 'status': 'verified', + 'type': 'pre_auth', + 'error': None, + 'pre_auth': { + 'billing_zip_code': 'xxxxx', + 'billing_zip_code_check': 'pass', + 'cvv': 'xxx', + 'cvv_check': 'pass', + 'exp_check': 'pass', + 'exp_month': 'xx', + 'exp_year': 'xxxx', + 'number': 'xxxxxxxxxxxxxxxx', + 'pre_auth_check': 'pass', + }, + 'created_at': verification_session_update['created_at'], + 'updated_at': verification_session_update['updated_at'], + } + + assert verification_session_update == expect_results + +@pytest.mark.asyncio +async def test_retrieve_account_verification_session(setup): + test_credit_card_account = setup['test_credit_card_account'] + + def get_verification_session(): + return method.accounts(test_credit_card_account['id']).verification_sessions.retrieve(verification_session_update['id']) + + verification_session_retrieve_response = await await_results(get_verification_session) + + expect_results = { + 'id': verification_session_update['id'], + 'account_id': test_credit_card_account['id'], + 'status': 'verified', + 'type': 'pre_auth', + 'error': None, + 'pre_auth': { + 'billing_zip_code': 'xxxxx', + 'billing_zip_code_check': 'pass', + 'cvv': 'xxx', + 'cvv_check': 'pass', + 'exp_check': 'pass', + 'exp_month': 'xx', + 'exp_year': 'xxxx', + 'number': 'xxxxxxxxxxxxxxxx', + 'pre_auth_check': 'pass', + }, + 'created_at': verification_session_retrieve_response['created_at'], + 'updated_at': verification_session_retrieve_response['updated_at'], + } + + assert verification_session_retrieve_response == expect_results + + +def test_create_account_sensitive(setup): + global sensitive_data_response + test_credit_card_account = setup['test_credit_card_account'] + + sensitive_data_response = method.accounts(test_credit_card_account['id']).sensitive.create({ + 'expand': [ + 'credit_card.number', + 'credit_card.exp_month', + 'credit_card.exp_year' + ] + }) + + expect_results = { + 'id': sensitive_data_response['id'], + 'account_id': test_credit_card_account['id'], + 'type': 'credit_card', + 'credit_card': { + 'billing_zip_code': None, + 'number': '5555555555551580', + 'exp_month': '03', + 'exp_year': '2028', + 'cvv': None + }, + 'status': 'completed', + 'error': None, + 'created_at': sensitive_data_response['created_at'], + 'updated_at': sensitive_data_response['updated_at'] + } + + assert sensitive_data_response == expect_results + + +def test_create_transaction_subscription(setup): + global create_txn_subscriptions_response + test_credit_card_account = setup['test_credit_card_account'] + + create_txn_subscriptions_response = method.accounts(test_credit_card_account['id']).subscriptions.create('transactions') + + expect_results = { + 'id': create_txn_subscriptions_response['id'], + 'name': 'transactions', + 'status': 'active', + 'latest_request_id': None, + 'created_at': create_txn_subscriptions_response['created_at'], + 'updated_at': create_txn_subscriptions_response['updated_at'], + } + + assert create_txn_subscriptions_response == expect_results + + +def test_create_update_subscription(setup): + global create_update_subscriptions_response + test_credit_card_account = setup['test_credit_card_account'] + + create_update_subscriptions_response = method.accounts(test_credit_card_account['id']).subscriptions.create('update') + + expect_results = { + 'id': create_update_subscriptions_response['id'], + 'name': 'update', + 'status': 'active', + 'latest_request_id': None, + 'created_at': create_update_subscriptions_response['created_at'], + 'updated_at': create_update_subscriptions_response['updated_at'], + } + + assert create_update_subscriptions_response == expect_results + + +def test_create_snapshot_subscription(setup): + global create_update_snapshot_subscriptions_response + test_auto_loan_account = setup['test_auto_loan_account'] + + create_update_snapshot_subscriptions_response = method.accounts(test_auto_loan_account['id']).subscriptions.create('update.snapshot') + + expect_results = { + 'id': create_update_snapshot_subscriptions_response['id'], + 'name': 'update.snapshot', + 'status': 'active', + 'latest_request_id': None, + 'created_at': create_update_snapshot_subscriptions_response['created_at'], + 'updated_at': create_update_snapshot_subscriptions_response['updated_at'], + } + + assert create_update_snapshot_subscriptions_response == expect_results + + +def test_list_subscriptions(setup): + test_credit_card_account = setup['test_credit_card_account'] + test_auto_loan_account = setup['test_auto_loan_account'] + + subscriptions_list_response = method.accounts(test_credit_card_account['id']).subscriptions.list() + subscriptions_update_snapshot_list_response = method.accounts(test_auto_loan_account['id']).subscriptions.list() + + expect_results_card = { + 'transactions': { + 'id': create_txn_subscriptions_response['id'], + 'name': 'transactions', + 'status': 'active', + 'latest_request_id': None, + 'created_at': create_txn_subscriptions_response['created_at'], + 'updated_at': create_txn_subscriptions_response['updated_at'] + }, + 'update': { + 'id': create_update_subscriptions_response['id'], + 'name': 'update', + 'status': 'active', + 'latest_request_id': None, + 'created_at': create_update_subscriptions_response['created_at'], + 'updated_at': create_update_subscriptions_response['updated_at'] + } + } + + expect_results_auto_loan = { + 'update.snapshot': { + 'id': create_update_snapshot_subscriptions_response['id'], + 'name': 'update.snapshot', + 'status': 'active', + 'latest_request_id': None, + 'created_at': create_update_snapshot_subscriptions_response['created_at'], + 'updated_at': create_update_snapshot_subscriptions_response['updated_at'] + } + } + + assert subscriptions_list_response == expect_results_card + assert subscriptions_update_snapshot_list_response == expect_results_auto_loan + + +def test_retrieve_subscription(setup): + test_credit_card_account = setup['test_credit_card_account'] + test_auto_loan_account = setup['test_auto_loan_account'] + + retrieve_txn_subscription_response = method.accounts(test_credit_card_account['id']).subscriptions.retrieve(create_txn_subscriptions_response['id']) + retrieve_update_subscription_response = method.accounts(test_credit_card_account['id']).subscriptions.retrieve(create_update_subscriptions_response['id']) + retrieve_update_snapshot_subscription_response = method.accounts(test_auto_loan_account['id']).subscriptions.retrieve(create_update_snapshot_subscriptions_response['id']) + + expect_results_txn = { + 'id': create_txn_subscriptions_response['id'], + 'name': 'transactions', + 'status': 'active', + 'latest_request_id': None, + 'created_at': retrieve_txn_subscription_response['created_at'], + 'updated_at': retrieve_txn_subscription_response['updated_at'], + } + + expect_results_update = { + 'id': create_update_subscriptions_response['id'], + 'name': 'update', + 'status': 'active', + 'latest_request_id': None, + 'created_at': retrieve_update_subscription_response['created_at'], + 'updated_at': retrieve_update_subscription_response['updated_at'], + } + + expect_results_update_snapshot = { + 'id': create_update_snapshot_subscriptions_response['id'], + 'name': 'update.snapshot', + 'status': 'active', + 'latest_request_id': None, + 'created_at': retrieve_update_snapshot_subscription_response['created_at'], + 'updated_at': retrieve_update_snapshot_subscription_response['updated_at'], + } + + assert retrieve_txn_subscription_response == expect_results_txn + assert retrieve_update_subscription_response == expect_results_update + assert retrieve_update_snapshot_subscription_response == expect_results_update_snapshot + + +def test_delete_subscription(setup): + test_auto_loan_account = setup['test_auto_loan_account'] + + delete_update_snapshot_subscription_response = method.accounts(test_auto_loan_account['id']).subscriptions.delete(create_update_snapshot_subscriptions_response['id']) + + expect_results_update_snapshot = { + 'id': create_update_snapshot_subscriptions_response['id'], + 'name': 'update.snapshot', + 'status': 'inactive', + 'latest_request_id': None, + 'created_at': delete_update_snapshot_subscription_response['created_at'], + 'updated_at': delete_update_snapshot_subscription_response['updated_at'], + } + + assert delete_update_snapshot_subscription_response == expect_results_update_snapshot + + +def test_list_transactions(setup): + global transactions_response + + test_credit_card_account = setup['test_credit_card_account'] + simulated_transaction = method.simulate.accounts(test_credit_card_account['id']).transactions.create() + + transactions_response = method.accounts(test_credit_card_account['id']).transactions.list() + + transactions_response = transactions_response[0] + + expect_results = { + 'id': transactions_response['id'], + 'account_id': test_credit_card_account['id'], + 'merchant': simulated_transaction['merchant'], + 'network': 'visa', + 'network_data': None, + 'amount': simulated_transaction['amount'], + 'currency': 'USD', + 'billing_amount': simulated_transaction['billing_amount'], + 'billing_currency': 'USD', + 'status': 'cleared', + 'error': None, + 'created_at': transactions_response['created_at'], + 'updated_at': transactions_response['updated_at'], + } + + assert transactions_response == expect_results \ No newline at end of file diff --git a/test/resources/test_account.py b/test/resources/test_account.py deleted file mode 100644 index 3ba622a..0000000 --- a/test/resources/test_account.py +++ /dev/null @@ -1,86 +0,0 @@ -import pytest -from method import Method - -# Create an instance of Method -method = Method(env='dev', api_key='{API_KEY}') - -@pytest.fixture(scope="module") -def setup(): - holder_1_response = method.entities.create({ - 'type': 'individual', - 'individual': { - 'first_name': 'Test', - 'last_name': 'McTesterson', - 'phone': '+15121231111' - } - }) - - method.entities(holder_1_response['id']).verification_sessions.create({ - 'type': 'phone', - 'method': 'byo_sms', - 'byo_sms': { - 'timestamp': '2021-09-01T00:00:00.000Z', - }, - }) - - method.entities(holder_1_response['id']).verification_sessions.create({ - 'type': 'identity', - 'method': 'kba', - 'kba': {}, - }) - - holder_connect_response = method.entities(holder_1_response['id']).connect.create() - - test_credit_card_accounts = method.accounts.list({ - 'holder_id': holder_1_response['id'], - 'liability.type': 'credit_card', - 'liability.mch_id': 'mch_302086', - }) - test_credit_card_account = test_credit_card_accounts[0] - - test_auto_loan_accounts = method.accounts.list({ - 'holder_id': holder_1_response['id'], - 'liability.type': 'auto_loan', - 'liability.mch_id': 'mch_2347', - }) - test_auto_loan_account = test_auto_loan_accounts[0] - - return { - 'holder_1_response': holder_1_response, - 'holder_connect_response': holder_connect_response, - 'test_credit_card_account': test_credit_card_account, - 'test_auto_loan_account': test_auto_loan_account, - } - -def test_create_ach_account(setup): - holder_1_response = setup['holder_1_response'] - - accounts_create_ach_response = method.accounts.create({ - 'holder_id': holder_1_response['id'], - 'ach': { - 'routing': '062103000', - 'number': '123456789', - 'type': 'checking', - }, - }) - - expect_results = { - 'id': accounts_create_ach_response['id'], - 'holder_id': holder_1_response['id'], - 'type': 'ach', - 'ach': { - 'routing': '062103000', - 'number': '123456789', - 'type': 'checking', - }, - 'latest_verification_session': accounts_create_ach_response['latest_verification_session'], - 'products': ['payment'], - 'restricted_products': [], - 'status': 'active', - 'error': None, - 'metadata': None, - 'created_at': accounts_create_ach_response['created_at'], - 'updated_at': accounts_create_ach_response['updated_at'], - } - - assert accounts_create_ach_response == expect_results diff --git a/test/resources/utils.py b/test/resources/utils.py new file mode 100644 index 0000000..5379768 --- /dev/null +++ b/test/resources/utils.py @@ -0,0 +1,20 @@ +import asyncio + +async def sleep(ms: int): + await asyncio.sleep(ms / 1000) # Convert milliseconds to seconds + +async def await_results(fn): + result = None + retries = 5 + while retries > 0: + try: + result = fn() + if result['status'] in ['completed', 'failed']: + break + await sleep(5000) + except Exception as error: + print('Error occurred while retrieving account balances:', error) + raise error # Rethrow the error to fail the test + retries -= 1 + + return result From 83d95b2fb7d3c2e92b3481881fc414617c163f7d Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Thu, 30 May 2024 20:34:46 -0400 Subject: [PATCH 12/22] started Entity tests, fixed typings, finished Account tests --- method/resources/Accounts/Updates.py | 2 +- test/resources/Account_test.py | 205 +++++++++++++++++++++++---- test/resources/Entity_test.py | 93 ++++++++++++ 3 files changed, 274 insertions(+), 26 deletions(-) create mode 100644 test/resources/Entity_test.py diff --git a/method/resources/Accounts/Updates.py b/method/resources/Accounts/Updates.py index 29dc924..f9d3ca8 100644 --- a/method/resources/Accounts/Updates.py +++ b/method/resources/Accounts/Updates.py @@ -29,7 +29,7 @@ def __init__(self, config: Configuration): def retrieve(self, upt_id: str) -> AccountUpdate: return super(AccountUpdatesResource, self)._get_with_id(upt_id) - def list(self, params: ResourceListOpts) -> List[AccountUpdate]: + def list(self, params: Optional[ResourceListOpts] = None) -> List[AccountUpdate]: return super(AccountUpdatesResource, self)._list(params) def create(self) -> AccountUpdate: diff --git a/test/resources/Account_test.py b/test/resources/Account_test.py index 075eafe..7930385 100644 --- a/test/resources/Account_test.py +++ b/test/resources/Account_test.py @@ -1,14 +1,22 @@ import os import pytest from method import Method -from dotenv import load_dotenv, dotenv_values +from dotenv import load_dotenv from utils import await_results +from method.resources.Accounts.Account import Account +from method.resources.Accounts.Balances import AccountBalance +from method.resources.Accounts.CardBrands import AccountCardBrand +from method.resources.Accounts.Payoffs import AccountPayoff +from method.resources.Accounts.Sensitive import AccountSensitive +from method.resources.Accounts.Subscriptions import AccountSubscription, AccountSubscriptionsResponse +from method.resources.Accounts.Transactions import AccountTransaction +from method.resources.Accounts.Updates import AccountUpdate +from method.resources.Accounts.VerificationSessions import AccountVerificationSession load_dotenv() API_KEY = os.getenv('API_KEY') -# Create an instance of Method method = Method(env='dev', api_key=API_KEY) accounts_create_ach_response = None @@ -92,7 +100,7 @@ def test_create_ach_account(setup): }, }) - expect_results = { + expect_results: Account = { 'id': accounts_create_ach_response['id'], 'holder_id': holder_1_response['id'], 'type': 'ach', @@ -128,7 +136,7 @@ def test_create_liability_account(setup): accounts_create_liability_response['products'] = accounts_create_liability_response['products'].sort() - expect_results = { + expect_results: Account = { 'id': accounts_create_liability_response['id'], 'holder_id': holder_1_response['id'], 'type': 'liability', @@ -162,7 +170,7 @@ def test_create_liability_account(setup): def test_retrieve_account(setup): accounts_retrieve_response = method.accounts.retrieve(accounts_create_ach_response['id']) - expect_results = { + expect_results: Account = { 'id': accounts_create_ach_response['id'], 'holder_id': setup['holder_1_response']['id'], 'type': 'ach', @@ -213,7 +221,7 @@ def test_create_balances(setup): balances_create_response = method.accounts(test_credit_card_account['id']).balances.create() - expect_results = { + expect_results: AccountBalance = { 'id': balances_create_response['id'], 'account_id': test_credit_card_account['id'], 'status': 'pending', @@ -235,7 +243,7 @@ def get_account_balances(): balances_retrieve_response = await await_results(get_account_balances) - expect_results = { + expect_results: AccountBalance = { 'id': balances_create_response['id'], 'account_id': test_credit_card_account['id'], 'status': 'completed', @@ -254,7 +262,7 @@ def test_create_card_brands(setup): card_brand_create_response = method.accounts(test_credit_card_account['id']).card_brands.create() - expect_results = { + expect_results: AccountCardBrand = { 'id': card_brand_create_response['id'], 'account_id': test_credit_card_account['id'], 'network': 'visa', @@ -275,7 +283,7 @@ def test_retrieve_card_brands(setup): test_credit_card_account = setup['test_credit_card_account'] card_retrieve_response = method.accounts(test_credit_card_account['id']).card_brands.retrieve(card_brand_create_response['id']) - expect_results = { + expect_results: AccountCardBrand = { 'id': card_brand_create_response['id'], 'account_id': test_credit_card_account['id'], 'network': 'visa', @@ -298,7 +306,7 @@ def test_create_payoffs(setup): payoff_create_response = method.accounts(test_auto_loan_account['id']).payoffs.create() - expect_results = { + expect_results: AccountPayoff = { 'id': payoff_create_response['id'], 'account_id': test_auto_loan_account['id'], 'amount': None, @@ -321,7 +329,7 @@ def get_payoff(): payoff_retrieve_response = await await_results(get_payoff) - expect_results = { + expect_results: AccountPayoff = { 'id': payoff_create_response['id'], 'account_id': test_auto_loan_account['id'], 'amount': 6083988, @@ -344,7 +352,7 @@ def test_create_account_verification_sessions(setup): 'type': 'pre_auth' }) - expect_results = { + expect_results: AccountVerificationSession = { 'id': verification_session_create['id'], 'account_id': test_credit_card_account['id'], 'status': 'pending', @@ -381,7 +389,7 @@ def test_update_account_verification_sessions(setup): } }) - expect_results = { + expect_results: AccountVerificationSession = { 'id': verification_session_create['id'], 'account_id': test_credit_card_account['id'], 'status': 'verified', @@ -404,6 +412,7 @@ def test_update_account_verification_sessions(setup): assert verification_session_update == expect_results + @pytest.mark.asyncio async def test_retrieve_account_verification_session(setup): test_credit_card_account = setup['test_credit_card_account'] @@ -413,7 +422,7 @@ def get_verification_session(): verification_session_retrieve_response = await await_results(get_verification_session) - expect_results = { + expect_results: AccountVerificationSession = { 'id': verification_session_update['id'], 'account_id': test_credit_card_account['id'], 'status': 'verified', @@ -449,7 +458,7 @@ def test_create_account_sensitive(setup): ] }) - expect_results = { + expect_results: AccountSensitive = { 'id': sensitive_data_response['id'], 'account_id': test_credit_card_account['id'], 'type': 'credit_card', @@ -475,7 +484,7 @@ def test_create_transaction_subscription(setup): create_txn_subscriptions_response = method.accounts(test_credit_card_account['id']).subscriptions.create('transactions') - expect_results = { + expect_results: AccountSubscription = { 'id': create_txn_subscriptions_response['id'], 'name': 'transactions', 'status': 'active', @@ -493,7 +502,7 @@ def test_create_update_subscription(setup): create_update_subscriptions_response = method.accounts(test_credit_card_account['id']).subscriptions.create('update') - expect_results = { + expect_results: AccountSubscription = { 'id': create_update_subscriptions_response['id'], 'name': 'update', 'status': 'active', @@ -511,7 +520,7 @@ def test_create_snapshot_subscription(setup): create_update_snapshot_subscriptions_response = method.accounts(test_auto_loan_account['id']).subscriptions.create('update.snapshot') - expect_results = { + expect_results: AccountSubscription = { 'id': create_update_snapshot_subscriptions_response['id'], 'name': 'update.snapshot', 'status': 'active', @@ -530,7 +539,7 @@ def test_list_subscriptions(setup): subscriptions_list_response = method.accounts(test_credit_card_account['id']).subscriptions.list() subscriptions_update_snapshot_list_response = method.accounts(test_auto_loan_account['id']).subscriptions.list() - expect_results_card = { + expect_results_card: AccountSubscriptionsResponse = { 'transactions': { 'id': create_txn_subscriptions_response['id'], 'name': 'transactions', @@ -549,7 +558,7 @@ def test_list_subscriptions(setup): } } - expect_results_auto_loan = { + expect_results_auto_loan: AccountSubscriptionsResponse = { 'update.snapshot': { 'id': create_update_snapshot_subscriptions_response['id'], 'name': 'update.snapshot', @@ -572,7 +581,7 @@ def test_retrieve_subscription(setup): retrieve_update_subscription_response = method.accounts(test_credit_card_account['id']).subscriptions.retrieve(create_update_subscriptions_response['id']) retrieve_update_snapshot_subscription_response = method.accounts(test_auto_loan_account['id']).subscriptions.retrieve(create_update_snapshot_subscriptions_response['id']) - expect_results_txn = { + expect_results_txn: AccountSubscription = { 'id': create_txn_subscriptions_response['id'], 'name': 'transactions', 'status': 'active', @@ -581,7 +590,7 @@ def test_retrieve_subscription(setup): 'updated_at': retrieve_txn_subscription_response['updated_at'], } - expect_results_update = { + expect_results_update: AccountSubscription = { 'id': create_update_subscriptions_response['id'], 'name': 'update', 'status': 'active', @@ -590,7 +599,7 @@ def test_retrieve_subscription(setup): 'updated_at': retrieve_update_subscription_response['updated_at'], } - expect_results_update_snapshot = { + expect_results_update_snapshot: AccountSubscription = { 'id': create_update_snapshot_subscriptions_response['id'], 'name': 'update.snapshot', 'status': 'active', @@ -631,7 +640,7 @@ def test_list_transactions(setup): transactions_response = transactions_response[0] - expect_results = { + expect_results: AccountTransaction = { 'id': transactions_response['id'], 'account_id': test_credit_card_account['id'], 'merchant': simulated_transaction['merchant'], @@ -647,4 +656,150 @@ def test_list_transactions(setup): 'updated_at': transactions_response['updated_at'], } - assert transactions_response == expect_results \ No newline at end of file + assert transactions_response == expect_results + + +def test_create_updates(setup): + global create_updates_response + test_credit_card_account = setup['test_credit_card_account'] + + create_updates_response = method.accounts(test_credit_card_account['id']).updates.create() + + expect_results: AccountUpdate = { + 'id': create_updates_response['id'], + 'account_id': test_credit_card_account['id'], + 'status': 'pending', + 'source': 'direct', + 'type': 'credit_card', + 'credit_card': { + 'sub_type': None, + 'opened_at': None, + 'closed_at': None, + 'balance': None, + 'last_payment_amount': None, + 'last_payment_date': None, + 'next_payment_due_date': None, + 'next_payment_minimum_amount': None, + 'interest_rate_type': None, + 'interest_rate_percentage_max': None, + 'interest_rate_percentage_min': None, + 'available_credit': None, + 'credit_limit': None, + 'usage_pattern': None + }, + 'error': None, + 'created_at': create_updates_response['created_at'], + 'updated_at': create_updates_response['updated_at'], + } + + assert create_updates_response == expect_results + +@pytest.mark.asyncio +async def test_retrieve_updates(setup): + test_credit_card_account = setup['test_credit_card_account'] + + def get_updates(): + return method.accounts(test_credit_card_account['id']).updates.retrieve(create_updates_response['id']) + + updates_retrieve_response = await await_results(get_updates) + + expect_results: AccountUpdate = { + 'id': create_updates_response['id'], + 'account_id': test_credit_card_account['id'], + 'status': 'completed', + 'source': 'direct', + 'type': 'credit_card', + 'credit_card': { + 'sub_type': 'flexible_spending', + 'opened_at': '2016-12-20', + 'closed_at': None, + 'balance': 1866688, + 'last_payment_amount': 100000, + 'last_payment_date': '2023-01-04', + 'next_payment_due_date': '2023-02-09', + 'next_payment_minimum_amount': 51060, + 'interest_rate_type': 'variable', + 'interest_rate_percentage_max': 27.5, + 'interest_rate_percentage_min': 20.5, + 'available_credit': 930000, + 'credit_limit': 2800000, + 'usage_pattern': None + }, + 'error': None, + 'created_at': updates_retrieve_response['created_at'], + 'updated_at': updates_retrieve_response['updated_at'], + } + + assert updates_retrieve_response == expect_results + + + +def test_list_updates_for_account(setup): + test_credit_card_account = setup['test_credit_card_account'] + + list_updates_response = method.accounts(test_credit_card_account['id']).updates.list() + + update_to_check = next((update for update in list_updates_response if update['id'] == create_updates_response['id']), None) + + expect_results: AccountUpdate = { + 'id': create_updates_response['id'], + 'account_id': test_credit_card_account['id'], + 'status': 'completed', + 'source': 'direct', + 'type': 'credit_card', + 'credit_card': { + 'sub_type': 'flexible_spending', + 'opened_at': '2016-12-20', + 'closed_at': None, + 'balance': 1866688, + 'last_payment_amount': 100000, + 'last_payment_date': '2023-01-04', + 'next_payment_due_date': '2023-02-09', + 'next_payment_minimum_amount': 51060, + 'interest_rate_type': 'variable', + 'interest_rate_percentage_max': 27.5, + 'interest_rate_percentage_min': 20.5, + 'available_credit': 930000, + 'credit_limit': 2800000, + 'usage_pattern': None + }, + 'error': None, + 'created_at': update_to_check['created_at'] if update_to_check else None, + 'updated_at': update_to_check['updated_at'] if update_to_check else None + } + + + assert update_to_check == expect_results + + +def test_withdraw_account_consent(setup): + test_credit_card_account = setup['test_credit_card_account'] + holder_1_response = setup['holder_1_response'] + + withdraw_consent_response = method.accounts.withdraw_consent(test_credit_card_account['id']) + + expect_results: Account = { + 'id': withdraw_consent_response['id'], + 'holder_id': holder_1_response['id'], + 'status': 'disabled', + 'type': None, + 'ach': None, + 'liability': None, + 'clearing': None, + 'products': [], + 'restricted_products': [], + 'subscriptions': [], + 'available_subscriptions': [], + 'restricted_subscriptions': [], + 'error': { + 'type': 'ACCOUNT_DISABLED', + 'sub_type': 'ACCOUNT_CONSENT_WITHDRAWN', + 'code': 11004, + 'message': 'Account was disabled due to consent withdrawal.', + }, + 'metadata': None, + 'created_at': withdraw_consent_response['created_at'], + 'updated_at': withdraw_consent_response['updated_at'], + } + + assert withdraw_consent_response == expect_results diff --git a/test/resources/Entity_test.py b/test/resources/Entity_test.py new file mode 100644 index 0000000..57282f2 --- /dev/null +++ b/test/resources/Entity_test.py @@ -0,0 +1,93 @@ +import os +import pytest +from method import Method +from dotenv import load_dotenv +from utils import await_results +from method.resources.Entities.Entity import Entity + +load_dotenv() + +API_KEY = os.getenv('API_KEY') + +method = Method(env='dev', api_key=API_KEY) + +entities_create_response = None +entitiy_with_identity_cap = None +entities_retrieve_response = None +entities_update_response = None +entities_list_response = None +entities_connect_create_response = None +entities_account_list_response = None +entities_account_ids = None +entities_create_credit_score_response = None +entities_create_idenitity_response = None +entities_retrieve_product_list_response = None +entities_create_connect_subscription_response = None +entities_create_credit_score_subscription_response = None +entities_create_verification_session_response = None + +def test_create_entity(): + global entities_create_response + entities_create_response = method.entities.create({ + 'type': 'individual', + 'individual': {}, + 'metadata': {} + }) + + entities_create_response['restricted_subscriptions'] = entities_create_response['restricted_subscriptions'].sort() + + expect_results: Entity = { + 'id': entities_create_response['id'], + 'type': 'individual', + 'individual': { + 'first_name': None, + 'last_name': None, + 'phone': None, + 'dob': None, + 'email': None, + 'ssn': None, + 'ssn_4': None, + }, + 'address': { + 'line1': None, + 'line2': None, + 'city': None, + 'state': None, + 'zip': None, + }, + 'verification': { + 'identity': { + 'verified': False, + 'matched': False, + 'latest_verification_session': None, + 'methods': [ + 'element', + 'kba', + ], + }, + 'phone': { + 'verified': False, + 'latest_verification_session': None, + 'methods': [ + 'element', + 'sna', + 'sms', + 'byo_sms' + ], + }, + }, + 'connect': None, + 'credit_score': None, + 'products': [], + 'restricted_products': entities_create_response['restricted_products'], + 'subscriptions': [], + 'available_subscriptions': [], + 'restricted_subscriptions': [ 'connect', 'credit_score' ].sort(), + 'status': 'incomplete', + 'error': None, + 'metadata': {}, + 'created_at': entities_create_response['created_at'], + 'updated_at': entities_create_response['updated_at'], + } + + assert entities_create_response == expect_results \ No newline at end of file From 2faf3956ee586d6cf8ea99e5e106129cb5e9bab6 Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Fri, 31 May 2024 10:10:50 -0400 Subject: [PATCH 13/22] added verification session and connect tests for entity --- .../Entities/VerificationSessions.py | 49 ++-- test/resources/Entity_test.py | 214 +++++++++++++++++- 2 files changed, 239 insertions(+), 24 deletions(-) diff --git a/method/resources/Entities/VerificationSessions.py b/method/resources/Entities/VerificationSessions.py index 3c8126a..9226652 100644 --- a/method/resources/Entities/VerificationSessions.py +++ b/method/resources/Entities/VerificationSessions.py @@ -6,24 +6,25 @@ EntityVerificationSessionStatusLiterals = Literal[ - 'completed', + 'verified', 'in_progress', 'pending', 'failed' ] -EntityVerificationSessionTypeLiterals = Literal[ - 'phone_method_sms', - 'phone_method_sna', - 'phone_byo_sms', - 'identity_byo_kyc', - 'identity_method_kba', - 'identity_method_auth_element' +EntityVerificationSessionMethodsLiterals = Literal[ + 'sms', + 'sna', + 'byo_sms', + 'byo_kyc', + 'kba', + 'element', + 'method_verified' ] -EntityVerificationSessionCategoryLiterals = Literal[ +EntityVerificationSessionTypeLiterals = Literal[ 'phone', 'identity' ] @@ -69,18 +70,20 @@ class EntityKbaVerification(TypedDict): class EntityVerificationSessionCreateOpts(TypedDict): type: EntityVerificationSessionTypeLiterals - phone_method_sms: Optional[object] - phone_method_sna: Optional[object] - phone_byo_sms: Optional[EntityPhoneSmsVerification] - identity_byo_kyc: Optional[object] - identity_method_kba: Optional[object] + method: EntityVerificationSessionMethodsLiterals + sms: Optional[object] + sna: Optional[object] + byo_sms: Optional[EntityPhoneSmsVerification] + byo_kyc: Optional[object] + kba: Optional[object] class EntityVerificationSessionUpdateOpts(TypedDict): type: EntityVerificationSessionTypeLiterals - phone_method_sms: Optional[EntityPhoneSmsVerificationUpdate] - phone_method_sma: Optional[object] - identity_method_kba: Optional[EntityKbaVerificationAnswerUpdate] + method: EntityVerificationSessionMethodsLiterals + sms: Optional[EntityPhoneSmsVerificationUpdate] + sma: Optional[object] + kba: Optional[EntityKbaVerificationAnswerUpdate] class EntityVerificationSession(TypedDict): @@ -88,12 +91,12 @@ class EntityVerificationSession(TypedDict): entity_id: str status: EntityVerificationSessionStatusLiterals type: EntityVerificationSessionTypeLiterals - category: EntityVerificationSessionCategoryLiterals - phone_method_sms: Optional[EntityPhoneSmsVerification] - phone_method_sna: Optional[EntityPhoneSnaVerification] - phone_byo_sms: Optional[EntityPhoneSmsVerification] - identity_byo_kyc: Optional[EntityByoKycVerification] - identity_method_kba: Optional[EntityKbaVerification] + method: EntityVerificationSessionMethodsLiterals + sms: Optional[EntityPhoneSmsVerification] + sna: Optional[EntityPhoneSnaVerification] + byo_sms: Optional[EntityPhoneSmsVerification] + byo_kyc: Optional[EntityByoKycVerification] + kba: Optional[EntityKbaVerification] error: Optional[ResourceError] created_at: str updated_at: str diff --git a/test/resources/Entity_test.py b/test/resources/Entity_test.py index 57282f2..2b1394b 100644 --- a/test/resources/Entity_test.py +++ b/test/resources/Entity_test.py @@ -4,6 +4,8 @@ from dotenv import load_dotenv from utils import await_results from method.resources.Entities.Entity import Entity +from method.resources.Entities.Connect import EntityConnect +from method.resources.Entities.VerificationSessions import EntityVerificationSession load_dotenv() @@ -90,4 +92,214 @@ def test_create_entity(): 'updated_at': entities_create_response['updated_at'], } - assert entities_create_response == expect_results \ No newline at end of file + assert entities_create_response == expect_results + + +def test_retrieve_entity(): + global entities_retrieve_response + entities_retrieve_response = method.entities.retrieve(entities_create_response['id']) + entities_retrieve_response['restricted_subscriptions'] = entities_retrieve_response['restricted_subscriptions'].sort() + + expect_results: Entity = { + 'id': entities_create_response['id'], + 'type': 'individual', + 'individual': { + 'first_name': None, + 'last_name': None, + 'phone': None, + 'dob': None, + 'email': None, + 'ssn': None, + 'ssn_4': None, + }, + 'address': { + 'line1': None, + 'line2': None, + 'city': None, + 'state': None, + 'zip': None, + }, + 'verification': { + 'identity': { + 'verified': False, + 'matched': False, + 'latest_verification_session': None, + 'methods': [ + 'element', + 'kba', + ], + }, + 'phone': { + 'verified': False, + 'latest_verification_session': None, + 'methods': [ + 'element', + 'sna', + 'sms', + 'byo_sms' + ], + }, + }, + 'connect': None, + 'credit_score': None, + 'products': [], + 'restricted_products': entities_retrieve_response['restricted_products'], + 'subscriptions': [], + 'available_subscriptions': [], + 'restricted_subscriptions': [ 'connect', 'credit_score' ].sort(), + 'status': 'incomplete', + 'error': None, + 'metadata': {}, + 'created_at': entities_retrieve_response['created_at'], + 'updated_at': entities_retrieve_response['updated_at'], + } + + assert entities_retrieve_response == expect_results + + +def test_update_entity(): + global entities_update_response + entities_update_response = method.entities.update(entities_create_response['id'], { + 'individual': { + 'first_name': 'John', + 'last_name': 'Doe', + 'phone': '+15121231111' + } + }) + + entities_update_response['restricted_subscriptions'] = entities_update_response['restricted_subscriptions'].sort() + entities_update_response['restricted_products'] = entities_update_response['restricted_products'].sort() + + expect_results: Entity = { + 'id': entities_create_response['id'], + 'type': 'individual', + 'individual': { + 'first_name': 'John', + 'last_name': 'Doe', + 'phone': '+15121231111', + 'dob': None, + 'email': None, + 'ssn': None, + 'ssn_4': None, + }, + 'address': { + 'line1': None, + 'line2': None, + 'city': None, + 'state': None, + 'zip': None, + }, + 'verification': { + 'identity': { + 'verified': False, + 'matched': True, + 'latest_verification_session': entities_update_response['verification']['identity']['latest_verification_session'], + 'methods': [ + 'element', + 'kba', + ], + }, + 'phone': { + 'verified': False, + 'latest_verification_session': entities_update_response['verification']['identity']['latest_verification_session'], + 'methods': [ + 'element', + 'sna', + 'sms', + 'byo_sms' + ], + }, + }, + 'connect': None, + 'credit_score': None, + 'products': [ 'identity' ], + 'restricted_products': [ 'connect', 'credit_score' ].sort(), + 'subscriptions': [], + 'available_subscriptions': [], + 'restricted_subscriptions': [ 'connect', 'credit_score' ].sort(), + 'status': 'incomplete', + 'error': None, + 'metadata': {}, + 'created_at': entities_update_response['created_at'], + 'updated_at': entities_update_response['updated_at'], + } + + assert entities_update_response == expect_results + +def test_list_entities(): + global entities_list_response + entities_list_response = method.entities.list() + entities_list_response = [entity['id'] for entity in entities_list_response] + + assert entities_create_response['id'] in entities_list_response + + +def test_create_entity_phone_verification(): + entity_create_phone_verification_response = method.entities(entities_create_response['id']).verification_sessions.create({ + 'type': 'phone', + 'method': 'byo_sms', + 'byo_sms': { + 'timestamp': '2021-09-01T00:00:00.000Z', + } + }) + + expect_results: EntityVerificationSession = { + 'id': entity_create_phone_verification_response['id'], + 'entity_id': entities_create_response['id'], + 'byo_sms': { + 'timestamp': '2021-09-01T00:00:00.000Z', + }, + 'method': 'byo_sms', + 'status': 'verified', + 'type': 'phone', + 'error': None, + 'created_at': entity_create_phone_verification_response['created_at'], + 'updated_at': entity_create_phone_verification_response['updated_at'], + } + + assert entity_create_phone_verification_response == expect_results + + +def test_create_entity_individual_verification(): + entity_create_individual_verification_response = method.entities(entities_create_response['id']).verification_sessions.create({ + 'type': 'identity', + 'method': 'kba', + 'kba': {} + }) + + expect_results: EntityVerificationSession = { + 'id': entity_create_individual_verification_response['id'], + 'entity_id': entities_create_response['id'], + 'kba': { + 'authenticated': True, + 'questions': [], + }, + 'method': 'kba', + 'status': 'verified', + 'type': 'identity', + 'error': None, + 'created_at': entity_create_individual_verification_response['created_at'], + 'updated_at': entity_create_individual_verification_response['updated_at'], + } + + assert entity_create_individual_verification_response == expect_results + + +def test_create_entity_connect(): + global entities_connect_create_response + entities_connect_create_response = method.entities(entities_create_response['id']).connect.create() + entities_connect_create_response['accounts'] = entities_connect_create_response['accounts'].sort() + entities_account_list_response = method.accounts.list({ 'holder_id': entities_create_response['id'], 'type': 'liability' }) + entities_account_ids = [account['id'] for account in entities_account_list_response].sort() + + expect_results: EntityConnect = { + 'id': entities_connect_create_response['id'], + 'entity_id': entities_create_response['id'], + 'status': 'completed', + 'accounts': entities_account_ids, + 'error': None, + 'created_at': entities_connect_create_response['created_at'], + 'updated_at': entities_connect_create_response['updated_at'], + } + + assert entities_connect_create_response == expect_results \ No newline at end of file From 3fbd852dbcefa368d787a6edf381bcd9ab6641bc Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Fri, 31 May 2024 12:04:32 -0400 Subject: [PATCH 14/22] updated credit score methods, added more entity tests --- method/resources/Entities/CreditScores.py | 4 +- test/resources/Entity_test.py | 65 ++++++++++++++++++++++- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/method/resources/Entities/CreditScores.py b/method/resources/Entities/CreditScores.py index 83a304e..98de297 100644 --- a/method/resources/Entities/CreditScores.py +++ b/method/resources/Entities/CreditScores.py @@ -40,8 +40,8 @@ class EntityCreditScoresResource(Resource): def __init__(self, config: Configuration): super(EntityCreditScoresResource, self).__init__(config.add_path('credit_scores')) - def retrieve_credit_scores(self, crs_id: str) -> EntityCreditScores: + def retrieve(self, crs_id: str) -> EntityCreditScores: return super(EntityCreditScoresResource, self)._get_with_id(crs_id) - def create_credit_scores(self) -> EntityCreditScores: + def create(self) -> EntityCreditScores: return super(EntityCreditScoresResource, self)._create({}) \ No newline at end of file diff --git a/test/resources/Entity_test.py b/test/resources/Entity_test.py index 2b1394b..3cf7d35 100644 --- a/test/resources/Entity_test.py +++ b/test/resources/Entity_test.py @@ -5,6 +5,7 @@ from utils import await_results from method.resources.Entities.Entity import Entity from method.resources.Entities.Connect import EntityConnect +from method.resources.Entities.CreditScores import EntityCreditScores from method.resources.Entities.VerificationSessions import EntityVerificationSession load_dotenv() @@ -287,6 +288,7 @@ def test_create_entity_individual_verification(): def test_create_entity_connect(): global entities_connect_create_response + global entities_account_ids entities_connect_create_response = method.entities(entities_create_response['id']).connect.create() entities_connect_create_response['accounts'] = entities_connect_create_response['accounts'].sort() entities_account_list_response = method.accounts.list({ 'holder_id': entities_create_response['id'], 'type': 'liability' }) @@ -302,4 +304,65 @@ def test_create_entity_connect(): 'updated_at': entities_connect_create_response['updated_at'], } - assert entities_connect_create_response == expect_results \ No newline at end of file + assert entities_connect_create_response == expect_results + +def test_retrieve_entity_connect(): + entities_connect_retrieve_response = method.entities(entities_create_response['id']).connect.retrieve(entities_connect_create_response['id']) + entities_connect_retrieve_response['accounts'] = entities_connect_retrieve_response['accounts'].sort() + + expect_results: EntityConnect = { + 'id': entities_connect_create_response['id'], + 'entity_id': entities_create_response['id'], + 'status': 'completed', + 'accounts': entities_account_ids, + 'error': None, + 'created_at': entities_connect_create_response['created_at'], + 'updated_at': entities_connect_create_response['updated_at'], + } + + assert entities_connect_retrieve_response == expect_results + + +def test_create_entity_credit_score(): + global entities_create_credit_score_response + entities_create_credit_score_response = method.entities(entities_create_response['id']).credit_scores.create() + + expect_results: EntityCreditScores = { + 'id': entities_create_credit_score_response['id'], + 'entity_id': entities_create_response['id'], + 'status': 'pending', + 'scores': None, + 'error': None, + 'created_at': entities_create_credit_score_response['created_at'], + 'updated_at': entities_create_credit_score_response['updated_at'], + } + + assert entities_create_credit_score_response == expect_results + + +@pytest.mark.asyncio +async def test_retrieve_entity_credit_score(): + def get_credit_score(): + return method.entities(entities_create_response['id']).credit_scores.retrieve(entities_create_credit_score_response['id']) + + credit_score_retrieve_response = await await_results(get_credit_score) + + expect_results: EntityCreditScores = { + 'id': entities_create_credit_score_response['id'], + 'entity_id': entities_create_response['id'], + 'status': 'completed', + 'scores': [ + { + 'score': credit_score_retrieve_response['scores'][0]['score'], + 'source': 'equifax', + 'model': 'vantage_3', + 'factors': credit_score_retrieve_response['scores'][0]['factors'], + 'created_at': credit_score_retrieve_response['scores'][0]['created_at'] + } + ], + 'error': None, + 'created_at': credit_score_retrieve_response['created_at'], + 'updated_at': credit_score_retrieve_response['updated_at'] + } + + assert credit_score_retrieve_response == expect_results From 7c4eb67729e01b621af7a26d69570468c98ffb9d Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Mon, 3 Jun 2024 12:29:51 -0400 Subject: [PATCH 15/22] finished python tests, fixed element to use correct structure --- method/method.py | 2 +- method/resources/Element.py | 100 ------- method/resources/Elements/Element.py | 13 + method/resources/Elements/Token.py | 183 +++++++++++++ method/resources/Elements/__init__.py | 2 + method/resources/Entities/Subscriptions.py | 8 +- method/resources/__init__.py | 10 - setup.py | 2 +- test/resources/Element_test.py | 62 +++++ test/resources/Entity_test.py | 292 +++++++++++++++++++++ test/resources/Merchant_test.py | 75 ++++++ test/resources/Payment_test.py | 174 ++++++++++++ test/resources/Report_test.py | 59 +++++ test/resources/Webhook_test.py | 68 +++++ 14 files changed, 932 insertions(+), 118 deletions(-) delete mode 100644 method/resources/Element.py create mode 100644 method/resources/Elements/Element.py create mode 100644 method/resources/Elements/Token.py create mode 100644 method/resources/Elements/__init__.py delete mode 100644 method/resources/__init__.py create mode 100644 test/resources/Element_test.py create mode 100644 test/resources/Merchant_test.py create mode 100644 test/resources/Payment_test.py create mode 100644 test/resources/Report_test.py create mode 100644 test/resources/Webhook_test.py diff --git a/method/method.py b/method/method.py index 2b626d5..ed2901f 100644 --- a/method/method.py +++ b/method/method.py @@ -1,7 +1,7 @@ from method.configuration import Configuration, ConfigurationOpts from method.resources.Accounts import AccountResource from method.resources.Entities import EntityResource -from method.resources.Element import ElementResource +from method.resources.Elements import ElementResource from method.resources.Merchant import MerchantResource from method.resources.Payments import PaymentResource from method.resources.Report import ReportResource diff --git a/method/resources/Element.py b/method/resources/Element.py deleted file mode 100644 index 6a66496..0000000 --- a/method/resources/Element.py +++ /dev/null @@ -1,100 +0,0 @@ -from typing import TypedDict, Optional, Literal, List, Dict - -from method.resource import Resource -from method.configuration import Configuration -from method.resources.Accounts import Account - - -ElementTypesLiterals = Literal[ - 'link' -] - - -UserEventTypeLiterals = Literal[ - 'auth_intro_open', - 'auth_intro_continue', - 'auth_intro_close', - 'auth_name_open', - 'auth_name_continue', - 'auth_name_close', - 'auth_phone_open', - 'auth_phone_continue', - 'auth_phone_close', - 'auth_phone_verify_open', - 'auth_phone_verify_submit', - 'auth_phone_verify_close', - 'auth_dob_open', - 'auth_dob_continue', - 'auth_dob_close', - 'auth_address_open', - 'auth_address_continue', - 'auth_address_close', - 'auth_incorrect_info_open', - 'auth_incorrect_info_try_again', - 'auth_invalid_info_open', - 'auth_invalid_info_exit', - 'auth_secq_open', - 'auth_secq_continue', - 'auth_secq_close', - 'auth_secq_incorrect_open', - 'auth_secq_incorrect_try_again', - 'auth_secq_incorrect_close', - 'auth_consent_open', - 'auth_consent_continue', - 'auth_consent_close', - 'auth_success_open', - 'auth_success_continue', - 'auth_failure_open', - 'auth_failure_continue' -] - - -class LinkElementLinkCreateOpts(TypedDict): - mch_id: Optional[str] - mask: Optional[str] - - -class ElementTokenCreateOpts(TypedDict): - entity_id: str - type: ElementTypesLiterals - team_name: Optional[str] - link: Optional[LinkElementLinkCreateOpts] - - -class Element(TypedDict): - element_token: str - - -class ElementUserEvent(TypedDict): - type: UserEventTypeLiterals - timestamp: str - metadata: Optional[Dict[str, any]] - -class TokenSessionResult(TypedDict): - authenticated: bool - cxn_id: Optional[str] - accounts: List[str] - entity_id: Optional[str] - events: List[ElementUserEvent] - - -class ElementExchangePublicAccountOpts(TypedDict): - public_account_token: Optional[str] - public_account_tokens: Optional[List[str]] - - -class ElementResource(Resource): - def __init__(self, config: Configuration): - super(ElementResource, self).__init__(config.add_path('elements')) - - def create_token(self, opts: ElementTokenCreateOpts) -> Element: - return super(ElementResource, self)._create_with_sub_path('token', opts) - - def retrieve_session_results(self, _id: str) -> TokenSessionResult: - return super(ElementResource, self)._get_with_sub_path('token/{_id}/results'.format(_id=_id)) - - def exchange_public_account_token(self, opts: ElementExchangePublicAccountOpts) -> Account: - return super(ElementResource, self)._create_with_sub_path('accounts/exchange', opts) - - def exchange_public_account_tokens(self, opts: ElementExchangePublicAccountOpts) -> Account: - return super(ElementResource, self)._create_with_sub_path('accounts/exchange', opts) diff --git a/method/resources/Elements/Element.py b/method/resources/Elements/Element.py new file mode 100644 index 0000000..f3f3091 --- /dev/null +++ b/method/resources/Elements/Element.py @@ -0,0 +1,13 @@ +from method.resource import Resource +from method.configuration import Configuration +from method.resources.Elements.Token import ElementTokenResource + + +class ElementResource(Resource): + token: ElementTokenResource + + def __init__(self, config: Configuration): + _config = config.add_path('elements') + super(ElementResource, self).__init__(_config) + self.token = ElementTokenResource(_config) + diff --git a/method/resources/Elements/Token.py b/method/resources/Elements/Token.py new file mode 100644 index 0000000..c2711f9 --- /dev/null +++ b/method/resources/Elements/Token.py @@ -0,0 +1,183 @@ +from typing import TypedDict, Optional, Literal, List, Dict + +from method.resource import Resource +from method.configuration import Configuration +from method.resources.Accounts.Types import AccountLiabilityTypesLiterals + + +UserEventTypeLiterals = Literal[ + 'AUTH_INTRO_OPEN', + 'AUTH_INTRO_CONTINUE', + 'AUTH_INTRO_CLOSE', + + 'AUTH_NAME_OPEN', + 'AUTH_NAME_CONTINUE', + 'AUTH_NAME_CLOSE', + + 'AUTH_PHONE_OPEN', + 'AUTH_PHONE_CONTINUE', + 'AUTH_PHONE_CLOSE', + + 'AUTH_PHONE_VERIFY_OPEN', + 'AUTH_PHONE_VERIFY_SUBMIT', + 'AUTH_PHONE_VERIFY_RESEND_CODE', + 'AUTH_PHONE_VERIFY_CLOSE', + + 'AUTH_DOB_OPEN', + 'AUTH_DOB_CONTINUE', + 'AUTH_DOB_CLOSE', + + 'AUTH_ADDRESS_OPEN', + 'AUTH_ADDRESS_CONTINUE', + 'AUTH_ADDRESS_CLOSE', + + 'AUTH_SSN4_OPEN', + 'AUTH_SSN4_CONTINUE', + 'AUTH_SSN4_CLOSE', + + 'AUTH_SECQ_OPEN', + 'AUTH_SECQ_CONTINUE', + 'AUTH_SECQ_CLOSE', + 'AUTH_SECQ_INCORRECT_TRY_AGAIN', + + 'AUTH_CONSENT_OPEN', + 'AUTH_CONSENT_CONTINUE', + 'AUTH_CONSENT_CLOSE', + + 'AUTH_SUCCESS_OPEN', + 'AUTH_SUCCESS_CONTINUE', + + 'AUTH_FAILURE_OPEN', + 'AUTH_FAILURE_CONTINUE', + + 'AVF_ACCOUNT_LIST_OPEN', + 'AVF_ACCOUNT_LIST_CLOSE', + + 'AVF_LEARN_MORE_OPEN', + 'AVF_LEARN_MORE_CLOSE', + + 'AVF_ACCOUNT_VERIFY_OPEN', + 'AVF_ACCOUNT_VERIFY_SUBMIT', + 'AVF_ACCOUNT_VERIFY_CLOSE', + + 'AVF_SUCCESS_OPEN', + 'AVF_SUCCESS_CONTINUE', + + 'AVF_EMPTY_SUCCESS_OPEN', + 'AVF_EMPTY_SUCCESS_CONTINUE', + + 'AVF_SKIP_ALL', + 'AVF_ERROR', +] + + +ElementTypesLiterals = Literal[ + 'connect', + 'balance_transfer' +] + +ElementProducts = Literal[ + 'balance', + 'payoff', + 'transactions', + 'card_brand', + 'update', + 'sensitive', + 'payment' +] + + +ElementMetadataOpTypes = Literal[ + 'open', + 'continue', + 'close', + 'success', + 'exit' +] + + +ElementSelectionTypes = Literal[ + 'single', + 'multiple' +] + + +class ElementUserEvent(TypedDict): + type: UserEventTypeLiterals + timestamp: str + metadata: Optional[Dict[str, any]] + + +class IndividualOpts(TypedDict): + first_name: Optional[str] + last_name: Optional[str] + dob: Optional[str] + email: Optional[str] + phone: Optional[str] + phone_verification_type: Optional[str] + phone_verification_timestamp: Optional[str] + + +class ElementEntityOpts(TypedDict): + type: Literal['individual'] + individual: IndividualOpts + + +class ConnectElementFilterOpts(TypedDict): + selection_type: Optional[ElementSelectionTypes] + liability_types: Optional[List[AccountLiabilityTypesLiterals]] + + +class ConnectElementCreateOpts(TypedDict): + products: Optional[List[ElementProducts]] + accounts: Optional[List[str]] + entity: Optional[ElementEntityOpts] + account_filters: Optional[ConnectElementFilterOpts] + + +class BalanceTransferElementCreateOpts(TypedDict): + payment_mount_min: int + minimum_loan_amount: int + payout_residual_amount_max: int + loan_details_requested_amount: int + loan_details_requested_rate: int + loan_details_requested_term: int + loan_details_requested_monthly_payment: int + + +class ElementTokenCreateOpts(TypedDict): + entity_id: str + type: ElementTypesLiterals + team_name: Optional[str] + team_logo: Optional[str] + team_icon: Optional[str] + connect: Optional[ConnectElementCreateOpts] + balance_transfer: Optional[BalanceTransferElementCreateOpts] + + +class ElementToken(TypedDict): + element_token: str + + +class ElementMetadata(TypedDict): + element_type: ElementTypesLiterals + op: ElementMetadataOpTypes + +class ElementResults(TypedDict): + authenticated: bool + cxn_id: Optional[str] + accounts: List[str] + entity_id: Optional[str] + events: List[ElementUserEvent] + + +class ElementTokenResource(Resource): + def __init__(self, config: Configuration): + super(ElementTokenResource, self).__init__(config.add_path('token')) + + def create(self, opts: ElementTokenCreateOpts) -> ElementToken: + return super(ElementTokenResource, self)._create(opts) + + def results(self, pk_elem_id: str) -> ElementResults: + return super(ElementTokenResource, self)._get_with_sub_path('{_id}/results'.format(_id=pk_elem_id)) + diff --git a/method/resources/Elements/__init__.py b/method/resources/Elements/__init__.py new file mode 100644 index 0000000..1113597 --- /dev/null +++ b/method/resources/Elements/__init__.py @@ -0,0 +1,2 @@ +from method.resources.Elements.Element import ElementResource +from method.resources.Elements.Token import ElementTokenResource, ElementToken diff --git a/method/resources/Entities/Subscriptions.py b/method/resources/Entities/Subscriptions.py index 94cf79d..b9fcfeb 100644 --- a/method/resources/Entities/Subscriptions.py +++ b/method/resources/Entities/Subscriptions.py @@ -31,10 +31,6 @@ class EntitySubscriptionResponseOpts(TypedDict): error: Optional[ResourceError] -class EntitySubscriptionCreateOpts(TypedDict): - enroll: List[EntitySubscriptionNamesLiterals] - - class EntitySubscriptionListResponse(TypedDict): connect: Optional[EntitySubscriptionResponseOpts] credit_score: Optional[EntitySubscriptionResponseOpts] @@ -50,8 +46,8 @@ def retrieve(self, sub_id: str) -> EntitySubscriptionResponseOpts: def list(self) -> EntitySubscriptionListResponse: return super(EntitySubscriptionsResource, self)._list() - def create(self, opts: EntitySubscriptionCreateOpts) -> EntitySubscriptionResponseOpts: - return super(EntitySubscriptionsResource, self)._create(opts) + def create(self, sub_name: EntitySubscriptionNamesLiterals) -> EntitySubscriptionResponseOpts: + return super(EntitySubscriptionsResource, self)._create({ 'enroll': sub_name }) def delete(self, sub_id: str) -> EntitySubscriptionResponseOpts: return super(EntitySubscriptionsResource, self)._delete(sub_id) \ No newline at end of file diff --git a/method/resources/__init__.py b/method/resources/__init__.py deleted file mode 100644 index 1fddf6b..0000000 --- a/method/resources/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# type: ignore -from method.resources.Accounts import Account, AccountResource -from method.resources.Element import Element, ElementResource -from method.resources.Entities import Entity, EntityResource -from method.resources.Merchant import Merchant, MerchantResource -from method.resources.Payments import Payment, PaymentResource -from method.resources.Report import Report, ReportResource -from method.resources.Webhook import Webhook, WebhookResource -from method.resources.HealthCheck import HealthCheckResource -from method.resources.Simulate import SimulatePaymentResource diff --git a/setup.py b/setup.py index d1b2f06..5c3fec6 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ author_email='marco@mdelcarmen.me', url='https://github.com/MethodFi/method-python', license='MIT', - packages=find_packages(exclude='tests'), + packages=find_packages(exclude='test'), package_data={'README': ['README.md']}, python_requires=">=3.6", install_requires=[ diff --git a/test/resources/Element_test.py b/test/resources/Element_test.py new file mode 100644 index 0000000..159bf06 --- /dev/null +++ b/test/resources/Element_test.py @@ -0,0 +1,62 @@ +import os +import pytest +from method import Method +from dotenv import load_dotenv + +load_dotenv() + +API_KEY = os.getenv('API_KEY') + +method = Method(env='dev', api_key=API_KEY) + +element_create_connect_token_response = None + +@pytest.fixture(scope='module') +def setup(): + entity_1_response = method.entities.create({ + 'type': 'individual', + 'individual': { + 'first_name': 'Kevin', + 'last_name': 'Doyle', + 'dob': '1930-03-11', + 'email': 'kevin.doyle@gmail.com', + 'phone': '+15121231111', + }, + }) + + return { + 'entity_1_id': entity_1_response['id'], + } + + +def test_create_connect_token(setup): + global element_create_connect_token_response + + element_create_connect_token_response = method.elements.token.create({ + 'entity_id': setup['entity_1_id'], + 'type': 'connect', + 'connect': { + 'products': [ 'balance' ], + 'account_filters': { + 'selection_type': 'multiple', + 'liability_types': [ 'credit_card' ] + } + } + }) + + assert 'element_token' in element_create_connect_token_response + assert len(element_create_connect_token_response) == 1 + + +def test_retrieve_element_results(setup): + element_retrieve_results_response = method.elements.token.results(element_create_connect_token_response['element_token']) + + expect_results = { + 'authenticated': False, + 'cxn_id': None, + 'accounts': [], + 'entity_id': setup['entity_1_id'], + 'events': [] + } + + assert element_retrieve_results_response == expect_results diff --git a/test/resources/Entity_test.py b/test/resources/Entity_test.py index 3cf7d35..ec22668 100644 --- a/test/resources/Entity_test.py +++ b/test/resources/Entity_test.py @@ -6,6 +6,9 @@ from method.resources.Entities.Entity import Entity from method.resources.Entities.Connect import EntityConnect from method.resources.Entities.CreditScores import EntityCreditScores +from method.resources.Entities.Identities import EntityIdentity +from method.resources.Entities.Products import EntityProduct, EntityProductListResponse +from method.resources.Entities.Subscriptions import EntitySubscription from method.resources.Entities.VerificationSessions import EntityVerificationSession load_dotenv() @@ -29,6 +32,8 @@ entities_create_credit_score_subscription_response = None entities_create_verification_session_response = None +# ENTITY CORE METHODS TESTS + def test_create_entity(): global entities_create_response entities_create_response = method.entities.create({ @@ -234,6 +239,7 @@ def test_list_entities(): assert entities_create_response['id'] in entities_list_response +# ENTITY VERIFICATION TESTS def test_create_entity_phone_verification(): entity_create_phone_verification_response = method.entities(entities_create_response['id']).verification_sessions.create({ @@ -286,6 +292,8 @@ def test_create_entity_individual_verification(): assert entity_create_individual_verification_response == expect_results +# ENTITY CONNECT TESTS + def test_create_entity_connect(): global entities_connect_create_response global entities_account_ids @@ -306,6 +314,7 @@ def test_create_entity_connect(): assert entities_connect_create_response == expect_results + def test_retrieve_entity_connect(): entities_connect_retrieve_response = method.entities(entities_create_response['id']).connect.retrieve(entities_connect_create_response['id']) entities_connect_retrieve_response['accounts'] = entities_connect_retrieve_response['accounts'].sort() @@ -323,6 +332,8 @@ def test_retrieve_entity_connect(): assert entities_connect_retrieve_response == expect_results +# ENTITY CREDIT SCORE TESTS + def test_create_entity_credit_score(): global entities_create_credit_score_response entities_create_credit_score_response = method.entities(entities_create_response['id']).credit_scores.create() @@ -366,3 +377,284 @@ def get_credit_score(): } assert credit_score_retrieve_response == expect_results + +# ENTITY IDENTITY TESTS + +def test_create_entity_identity(): + global entitiy_with_identity_cap + global entities_create_idenitity_response + + entitiy_with_identity_cap = method.entities.create({ + 'type': 'individual', + 'individual': { + 'first_name': 'Kevin', + 'last_name': 'Doyle', + 'phone': '+16505551115', + } + }) + + entities_create_idenitity_response = method.entities(entitiy_with_identity_cap['id']).identities.create() + + expect_results: EntityIdentity = { + 'id': entities_create_idenitity_response['id'], + 'entity_id': entitiy_with_identity_cap['id'], + 'status': 'completed', + 'identities': [ + { + 'address': { + 'address': '3300 N INTERSTATE 35', + 'city': 'AUSTIN', + 'postal_code': '78705', + 'state': 'TX' + }, + 'dob': '1997-03-18', + 'first_name': 'KEVIN', + 'last_name': 'DOYLE', + 'phone': '+16505551115', + 'ssn': '111223333' + }, + { + 'address': { + 'address': '3300 N INTERSTATE 35', + 'city': 'AUSTIN', + 'postal_code': '78705', + 'state': 'TX' + }, + 'dob': '1997-03-18', + 'first_name': 'KEVIN', + 'last_name': 'DOYLE', + 'phone': '+16505551115', + 'ssn': '123456789' + } + ], + 'error': None, + 'created_at': entities_create_idenitity_response['created_at'], + 'updated_at': entities_create_idenitity_response['updated_at'] + }; + + assert entities_create_idenitity_response == expect_results + + +@pytest.mark.asyncio +async def test_retrieve_entity_identity(): + def get_identity(): + return method.entities(entitiy_with_identity_cap['id']).identities.retrieve(entities_create_idenitity_response['id']) + + identity_retrieve_response = await await_results(get_identity) + + expect_results: EntityIdentity = { + 'id': entities_create_idenitity_response['id'], + 'entity_id': entitiy_with_identity_cap['id'], + 'status': 'completed', + 'identities': [ + { + 'address': { + 'address': '3300 N INTERSTATE 35', + 'city': 'AUSTIN', + 'postal_code': '78705', + 'state': 'TX' + }, + 'dob': '1997-03-18', + 'first_name': 'KEVIN', + 'last_name': 'DOYLE', + 'phone': '+16505551115', + 'ssn': '111223333' + }, + { + 'address': { + 'address': '3300 N INTERSTATE 35', + 'city': 'AUSTIN', + 'postal_code': '78705', + 'state': 'TX' + }, + 'dob': '1997-03-18', + 'first_name': 'KEVIN', + 'last_name': 'DOYLE', + 'phone': '+16505551115', + 'ssn': '123456789' + } + ], + 'error': None, + 'created_at': identity_retrieve_response['created_at'], + 'updated_at': identity_retrieve_response['updated_at'] + }; + + assert identity_retrieve_response == expect_results + +# ENTITY PRODUCT TESTS + +def test_retrieve_entity_product_list(): + global entities_retrieve_product_list_response + entities_retrieve_product_list_response = method.entities(entities_create_response['id']).products.list() + + expect_results: EntityProductListResponse = { + 'connect': { + 'id': entities_retrieve_product_list_response.get('connect', {}).get('id', ''), + 'name': 'connect', + 'status': 'available', + 'status_error': None, + 'latest_request_id': entities_retrieve_product_list_response.get('connect', {}).get('latest_request_id', None), + 'is_subscribable': True, + 'created_at': entities_retrieve_product_list_response.get('connect', {}).get('created_at', ''), + 'updated_at': entities_retrieve_product_list_response.get('connect', {}).get('updated_at', ''), + }, + 'credit_score': { + 'id': entities_retrieve_product_list_response.get('credit_score', {}).get('id', ''), + 'name': 'credit_score', + 'status': 'available', + 'status_error': None, + 'latest_request_id': entities_retrieve_product_list_response.get('credit_score', {}).get('latest_request_id', None), + 'is_subscribable': True, + 'created_at': entities_retrieve_product_list_response.get('credit_score', {}).get('created_at', ''), + 'updated_at': entities_retrieve_product_list_response.get('credit_score', {}).get('updated_at', ''), + }, + 'identity': { + 'id': entities_retrieve_product_list_response.get('identity', {}).get('id', ''), + 'name': 'identity', + 'status': 'available', + 'status_error': None, + 'latest_request_id': entities_retrieve_product_list_response.get('identity', {}).get('latest_request_id', None), + 'is_subscribable': False, + 'created_at': entities_retrieve_product_list_response.get('identity', {}).get('created_at', ''), + 'updated_at': entities_retrieve_product_list_response.get('identity', {}).get('updated_at', ''), + } + } + + assert entities_retrieve_product_list_response == expect_results + + +def test_retrieve_entity_product(): + entity_connect_product_id = entities_retrieve_product_list_response.get('connect', {}).get('id', '') + entity_credit_score_product_id = entities_retrieve_product_list_response.get('credit_score', {}).get('id', '') + entity_identity_product_id = entities_retrieve_product_list_response.get('identity', {}).get('id', '') + + entity_connect_product_response = method.entities(entities_create_response['id']).products.retrieve(entity_connect_product_id) + entity_credit_score_product_response = method.entities(entities_create_response['id']).products.retrieve(entity_credit_score_product_id) + entity_identity_product_response = method.entities(entities_create_response['id']).products.retrieve(entity_identity_product_id) + + expect_connect_results: EntityProduct = { + 'id': entity_connect_product_id, + 'name': 'connect', + 'status': 'available', + 'status_error': None, + 'latest_request_id': entity_connect_product_response['latest_request_id'], + 'is_subscribable': True, + 'created_at': entity_connect_product_response['created_at'], + 'updated_at': entity_connect_product_response['updated_at'] + } + + expect_credit_score_results: EntityProduct = { + 'id': entity_credit_score_product_id, + 'name': 'credit_score', + 'status': 'available', + 'status_error': None, + 'latest_request_id': entity_credit_score_product_response['latest_request_id'], + 'is_subscribable': True, + 'created_at': entity_credit_score_product_response['created_at'], + 'updated_at': entity_credit_score_product_response['updated_at'] + } + + expect_identity_results: EntityProduct = { + 'id': entity_identity_product_id, + 'name': 'identity', + 'status': 'available', + 'status_error': None, + 'latest_request_id': entity_identity_product_response['latest_request_id'], + 'is_subscribable': False, + 'created_at': entity_identity_product_response['created_at'], + 'updated_at': entity_identity_product_response['updated_at'] + } + + assert entity_connect_product_response == expect_connect_results + assert entity_credit_score_product_response == expect_credit_score_results + assert entity_identity_product_response == expect_identity_results + +# ENTITY SUBSCRIPTION TESTS + +def test_create_entity_connect_subscription(): + global entities_create_connect_subscription_response + entities_create_connect_subscription_response = method.entities(entities_create_response['id']).subscriptions.create('connect') + + expect_results: EntitySubscription = { + 'id': entities_create_connect_subscription_response['id'], + 'name': 'connect', + 'status': 'active', + 'latest_request_id': None, + 'created_at': entities_create_connect_subscription_response['created_at'], + 'updated_at': entities_create_connect_subscription_response['updated_at'] + } + + assert entities_create_connect_subscription_response == expect_results + + +def test_create_entity_credit_score_subscription(): + global entities_create_credit_score_subscription_response + entities_create_credit_score_subscription_response = method.entities(entities_create_response['id']).subscriptions.create('credit_score') + + expect_results: EntitySubscription = { + 'id': entities_create_credit_score_subscription_response['id'], + 'name': 'credit_score', + 'status': 'active', + 'latest_request_id': None, + 'created_at': entities_create_credit_score_subscription_response['created_at'], + 'updated_at': entities_create_credit_score_subscription_response['updated_at'] + } + + assert entities_create_credit_score_subscription_response == expect_results + + +def test_retrieve_entity_subscription(): + entity_connect_subscription_id = entities_create_connect_subscription_response['id'] + entity_credit_score_subscription_id = entities_create_credit_score_subscription_response['id'] + + entity_connect_subscription_response = method.entities(entities_create_response['id']).subscriptions.retrieve(entity_connect_subscription_id) + entity_credit_score_subscription_response = method.entities(entities_create_response['id']).subscriptions.retrieve(entity_credit_score_subscription_id) + + expect_connect_results: EntitySubscription = { + 'id': entity_connect_subscription_id, + 'name': 'connect', + 'status': 'active', + 'latest_request_id': None, + 'created_at': entity_connect_subscription_response['created_at'], + 'updated_at': entity_connect_subscription_response['updated_at'] + } + + expect_credit_score_results: EntitySubscription = { + 'id': entity_credit_score_subscription_id, + 'name': 'credit_score', + 'status': 'active', + 'latest_request_id': None, + 'created_at': entity_credit_score_subscription_response['created_at'], + 'updated_at': entity_credit_score_subscription_response['updated_at'] + } + + assert entity_connect_subscription_response == expect_connect_results + assert entity_credit_score_subscription_response == expect_credit_score_results + +# ENTITY CONSENT TESTS + +def test_withdraw_entity_consent(): + entity_withdraw_consent_response = method.entities.withdraw_consent(entities_create_response['id']) + + expect_results: Entity = { + 'id': entities_create_response['id'], + 'type': None, + 'individual': None, + 'corporation': None, + 'receive_only': None, + 'verification': None, + 'error': { + 'type': 'ENTITY_DISABLED', + 'sub_type': 'ENTITY_CONSENT_WITHDRAWN', + 'code': 12004, + 'message': 'Entity was disabled due to consent withdrawal.' + }, + 'address': {}, + 'status': 'disabled', + 'metadata': None, + 'created_at': entities_update_response['created_at'], + 'updated_at': entity_withdraw_consent_response['updated_at'], + } + + assert entity_withdraw_consent_response == expect_results + \ No newline at end of file diff --git a/test/resources/Merchant_test.py b/test/resources/Merchant_test.py new file mode 100644 index 0000000..3bb84bb --- /dev/null +++ b/test/resources/Merchant_test.py @@ -0,0 +1,75 @@ +import os +from method import Method +from dotenv import load_dotenv + +load_dotenv() + +API_KEY = os.getenv('API_KEY') + +method = Method(env='dev', api_key=API_KEY) + +merchant_retrieve_response = None +merchants_list_response = None +amex_mch_id = 'mch_3' +amex_provider_id_plaid = 'ins_10' + + +def test_retrieve_merchant(): + global merchant_retrieve_response + + merchant_retrieve_response = method.merchants.retrieve(amex_mch_id) + + expect_results = { + "id": "mch_3", + "parent_name": "American Express", + "name": "American Express - Credit Cards", + "logo": "https://static.methodfi.com/mch_logos/mch_3.png", + "type": "credit_card", + "provider_ids": { + "plaid": ["ins_10"], + "mx": ["amex"], + "finicity": [], + "dpp": ["120", "18954427", "11859365", "18947131", "16255844"] + }, + "is_temp": False, + "account_number_formats": [] + } + + assert merchant_retrieve_response == expect_results + + +def test_list_merchants(): + global merchants_list_response + + merchants_list_response = method.merchants.list({ 'provider_id.plaid': amex_provider_id_plaid }) + merchant_to_use = merchants_list_response[0] + + expect_results = { + "id": "mch_300485", + "parent_name": "American Express", + "name": "American Express Credit Card", + "logo": "https://static.methodfi.com/mch_logos/mch_300485.png", + "type": "credit_card", + "provider_ids": { + "plaid": ["ins_10"], + "mx": ["amex"], + "finicity": [], + "dpp": [ + '7929257', + '120', + '18391555', + '18954427', + '11859365', + '18947131', + '16255844' + ] + }, + "is_temp": False, + "account_number_formats": [ + '###############' + ] + } + + assert merchants_list_response != None + assert isinstance(merchants_list_response, list) + assert merchant_to_use == expect_results diff --git a/test/resources/Payment_test.py b/test/resources/Payment_test.py new file mode 100644 index 0000000..9fd9d98 --- /dev/null +++ b/test/resources/Payment_test.py @@ -0,0 +1,174 @@ +import os +import pytest +from method import Method +from dotenv import load_dotenv + +load_dotenv() + +API_KEY = os.getenv('API_KEY') + +method = Method(env='dev', api_key=API_KEY) + +payments_create_response = None +payments_retrieve_response = None +payments_list_response = None +payments_delete_response = None + +@pytest.fixture(scope='module') +def setup(): + holder_1_response = method.entities.create({ + 'type': 'individual', + 'individual': { + 'first_name': 'Kevin', + 'last_name': 'Doyle', + 'dob': '1930-03-11', + 'email': 'kevin.doyle@gmail.com', + 'phone': '+15121231111', + } + }) + + phone_verification_response = method.entities(holder_1_response['id']).verification_sessions.create({ + 'type': 'phone', + 'method': 'byo_sms', + 'byo_sms': { + 'timestamp': '2021-09-01T00:00:00.000Z' + } + }) + + identity_verification_response = method.entities(holder_1_response['id']).verification_sessions.create({ + 'type': 'identity', + 'method': 'kba', + 'kba': {} + }) + + source_1_response = method.accounts.create({ + 'holder_id': holder_1_response['id'], + 'ach': { + 'routing': '062103000', + 'number': '123456789', + 'type': 'checking', + } + }) + + destination_1_response = method.accounts.create({ + 'holder_id': holder_1_response['id'], + 'liability': { + 'mch_id': 'mch_3', + 'account_number': '123456789', + } + }) + + return { + 'holder_1_id': holder_1_response['id'], + 'source_1_id': source_1_response['id'], + 'destination_1_id': destination_1_response['id'], + } + + +def test_create_payment(setup): + global payments_create_response + + payments_create_response = method.payments.create({ + 'amount': 5000, + 'source': setup['source_1_id'], + 'destination': setup['destination_1_id'], + 'description': 'MethodPy' + }) + + expect_results = { + 'id': payments_create_response['id'], + 'source': setup['source_1_id'], + 'destination': setup['destination_1_id'], + 'amount': 5000, + 'description': 'MethodPy', + 'status': 'pending', + 'estimated_completion_date': payments_create_response['estimated_completion_date'], + 'source_trace_id': None, + 'source_settlement_date': payments_create_response['source_settlement_date'], + 'source_status': 'pending', + 'destination_trace_id': None, + 'destination_settlement_date': payments_create_response['destination_settlement_date'], + 'destination_status': 'pending', + 'reversal_id': None, + 'fee': None, + 'type': 'standard', + 'error': None, + 'metadata': None, + 'created_at': payments_create_response['created_at'], + 'updated_at': payments_create_response['updated_at'], + } + + + assert payments_create_response == expect_results + + +def test_retrieve_payment(setup): + global payments_retrieve_response + + payments_retrieve_response = method.payments.retrieve(payments_create_response['id']) + + expect_results = { + 'id': payments_create_response['id'], + 'source': setup['source_1_id'], + 'destination': setup['destination_1_id'], + 'amount': 5000, + 'description': 'MethodPy', + 'status': 'pending', + 'estimated_completion_date': payments_create_response['estimated_completion_date'], + 'source_trace_id': None, + 'source_settlement_date': payments_create_response['source_settlement_date'], + 'source_status': 'pending', + 'destination_trace_id': None, + 'destination_settlement_date': payments_create_response['destination_settlement_date'], + 'destination_status': 'pending', + 'reversal_id': None, + 'fee': None, + 'type': 'standard', + 'error': None, + 'metadata': None, + 'created_at': payments_retrieve_response['created_at'], + 'updated_at': payments_retrieve_response['updated_at'], + 'fund_status': 'pending' + } + + assert payments_retrieve_response == expect_results + + +def test_list_payments(setup): + global payments_list_response + + payments_list_response = method.payments.list({ 'source': setup['source_1_id'] }) + payment_ids = [payment['id'] for payment in payments_list_response] + + assert payments_create_response['id'] in payment_ids + + +def test_delete_payment(setup): + global payments_delete_response + + payments_delete_response = method.payments.delete(payments_create_response['id']) + + expect_results = { + 'id': payments_create_response['id'], + 'source': setup['source_1_id'], + 'destination': setup['destination_1_id'], + 'amount': 5000, + 'description': 'MethodPy', + 'status': 'canceled', + 'estimated_completion_date': payments_create_response['estimated_completion_date'], + 'source_trace_id': None, + 'source_settlement_date': payments_create_response['source_settlement_date'], + 'source_status': 'canceled', + 'destination_trace_id': None, + 'destination_settlement_date': payments_create_response['destination_settlement_date'], + 'destination_status': 'canceled', + 'reversal_id': None, + 'fee': None, + 'type': 'standard', + 'error': None, + 'metadata': None, + 'created_at': payments_delete_response['created_at'], + 'updated_at': payments_delete_response['updated_at'], + } + + assert payments_delete_response == expect_results \ No newline at end of file diff --git a/test/resources/Report_test.py b/test/resources/Report_test.py new file mode 100644 index 0000000..6524ebd --- /dev/null +++ b/test/resources/Report_test.py @@ -0,0 +1,59 @@ +import os +from method import Method +from dotenv import load_dotenv + +load_dotenv() + +API_KEY = os.getenv('API_KEY') + +method = Method(env='dev', api_key=API_KEY) + +report_create_response = None +report_retrieve_response = None +report_download_response = None + + +def test_create_report(): + global report_create_response + + report_create_response = method.reports.create({ + 'type': 'payments.created.current', + }) + + expect_results = { + "id": report_create_response["id"], + "type": "payments.created.current", + "url": f"https://dev.methodfi.com/reports/{report_create_response['id']}/download", + "status": "completed", + "metadata": None, + "created_at": report_create_response["created_at"], + "updated_at": report_create_response["updated_at"], + } + + assert report_create_response == expect_results + + +def test_retrieve_report(): + global report_retrieve_response + + report_retrieve_response = method.reports.retrieve(report_create_response['id']) + + expect_results = { + "id": report_create_response["id"], + "type": "payments.created.current", + "url": f"https://dev.methodfi.com/reports/{report_create_response['id']}/download", + "status": "completed", + "metadata": None, + "created_at": report_retrieve_response["created_at"], + "updated_at": report_retrieve_response["updated_at"], + } + + assert report_retrieve_response == expect_results + + +def test_download_report(): + global report_download_response + + report_download_response = method.reports.download(report_create_response['id']) + + assert report_download_response is not None \ No newline at end of file diff --git a/test/resources/Webhook_test.py b/test/resources/Webhook_test.py new file mode 100644 index 0000000..7f044f5 --- /dev/null +++ b/test/resources/Webhook_test.py @@ -0,0 +1,68 @@ +import os +from method import Method +from dotenv import load_dotenv + +load_dotenv() + +API_KEY = os.getenv('API_KEY') + +method = Method(env='dev', api_key=API_KEY) + +webhooks_create_response = None +webhooks_retrieve_response = None +webhooks_list_response = None +webhooks_delete_response = None + +def test_create_webhooks(): + global webhooks_create_response + + webhooks_create_response = method.webhooks.create({ + 'type': 'payment.create', + 'url': 'https://dev.methodfi.com', + 'auth_token': 'test_auth_token' + }) + + expect_results = { + 'id': webhooks_create_response['id'], + 'type': 'payment.create', + 'url': 'https://dev.methodfi.com', + 'metadata': None, + 'created_at': webhooks_create_response['created_at'], + 'updated_at': webhooks_create_response['updated_at'] + } + + assert webhooks_create_response == expect_results + + +def test_retrieve_webhook(): + global webhooks_retrieve_response + + webhooks_retrieve_response = method.webhooks.retrieve(webhooks_create_response['id']) + + expect_results = { + 'id': webhooks_create_response['id'], + 'type': 'payment.create', + 'url': 'https://dev.methodfi.com', + 'metadata': None, + 'created_at': webhooks_retrieve_response['created_at'], + 'updated_at': webhooks_retrieve_response['updated_at'] + } + + assert webhooks_retrieve_response == expect_results + + +def test_list_webhooks(): + global webhooks_list_response + + webhooks_list_response = method.webhooks.list() + webhook_ids = [webhook['id'] for webhook in webhooks_list_response] + + assert webhooks_create_response['id'] in webhook_ids + + +def test_delete_webhook(): + global webhooks_delete_response + + webhooks_delete_response = method.webhooks.delete(webhooks_create_response['id']) + + assert webhooks_delete_response == None \ No newline at end of file From 433cc92001fe024a6583a047a3cce70fa2bb454e Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Mon, 3 Jun 2024 12:40:52 -0400 Subject: [PATCH 16/22] updated README with v2 endpoints --- README.md | 956 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 774 insertions(+), 182 deletions(-) diff --git a/README.md b/README.md index 9901e04..5d93cab 100644 --- a/README.md +++ b/README.md @@ -21,173 +21,713 @@ method = Method({'env': 'production', 'api_key': '{API_KEY}'}) ## Entities -### Create Individual Entity +Entities are a representation of your application’s end-users (individuals or corporation). Throughout Method’s ecosystem, an Entity is the legal owner of an account, and is the primary object for many of Method’s API endpoints. + +> Entities should be persisted with a 1:1 relationship throughout the lifecycle of your end-user. + +### PII Requirements + +Entity PII requirements are pre-defined during onboarding based on your team’s specific use case. Entities require at a minimum name + phone number for most services. Some Products and Subscriptions may require additional information to be provided to Method to enable certain features. Contact your Method CSM for more information. + +### Core Methods + +#### Create Individual Entity ```python -entity = method.entities.create({ - 'type': 'individual', +const entity = await method.entities.create({ + type: "individual", 'individual': { - 'first_name': 'Kevin', - 'last_name': 'Doyle', - 'phone': '+16505555555', - 'email': 'kevin.doyle@gmail.com', - 'dob': '1997-03-18', + 'first_name': "Kevin", + 'last_name': "Doyle", + 'phone': "+16505555555", + 'email': "kevin.doyle@gmail.com", + 'dob': "1997-03-18", }, 'address': { - 'line1': '3300 N Interstate 35', + 'line1': "3300 N Interstate 35", 'line2': None, - 'city': 'Austin', - 'state': 'TX', - 'zip': '78705' - } -}) + 'city': "Austin", + 'state': "TX", + 'zip': "78705", + }, +}); ``` -### Create Corporation Entity +#### Create Corporation Entity ```python -entity = method.entities.create({ - 'type': 'c_corporation', - 'corporation': { - 'name': 'Alphabet Inc.', - 'dba': 'Google', - 'ein': '641234567', - 'owners': [ +const entity = await method.entities.create({ + type: 'corporation', + corporation: { + name: 'Alphabet Inc.', + dba: 'Google', + ein: '641234567', + owners: [ { - 'first_name': 'Sergey', - 'last_name': 'Brin', - 'phone': '+16505555555', - 'email': 'sergey@google.com', - 'dob': '1973-08-21', - 'address': { - 'line1': '600 Amphitheatre Parkway', - 'line2': None, - 'city': 'Mountain View', - 'state': 'CA', - 'zip': '94043' - } - } - ] + first_name: 'Sergey', + last_name: 'Brin', + phone: '+16505555555', + email: 'sergey@google.com', + dob: '1973-08-21', + address: { + line1: '600 Amphitheatre Parkway', + line2: None, + city: 'Mountain View', + state: 'CA', + zip: '94043', + }, + }, + ], }, - 'address': { - 'line1': '1600 Amphitheatre Parkway', - 'line2': None, - 'city': 'Mountain View', - 'state': 'CA', - 'zip': '94043' - } -}) + address: { + line1: '1600 Amphitheatre Parkway', + line2: None, + city: 'Mountain View', + state: 'CA', + zip: '94043', + }, +}); ``` -### Retrieve Entity +#### Retrieve Entity ```python -entity = method.entities.get('ent_au22b1fbFJbp8') +const entity = await method.entities.retrieve('ent_au22b1fbFJbp8'); ``` -### Update Entity +#### Update Entity ```python -entity = method.entities.update('ent_au22b1fbFJbp8', { +const entity = await method.entities.update('ent_au22b1fbFJbp8', { 'individual': { 'first_name': 'Kevin', 'last_name': 'Doyle', 'email': 'kevin.doyle@gmail.com', 'dob': '1997-03-18', - } -}) + }, +}); +``` + +#### List Entities + +```python +const entities = await method.entities.list(); +``` + +#### Withdraw an Entity's consent + +```python +const entity = await method.entities.withdraw_consent('ent_au22b1fbFJbp8'); +``` + +### Connect + +The Connect endpoint identifies & connects all the liability accounts (e.g. Credit Card, Mortgage, Auto Loans, Student Loans, etc.) for an Entity across Method’s network of 1500+ FI / Lenders. + +#### Create a Connect + +```python +const entity = await method + .entities("ent_qKNBB68bfHGNA") + .connect + .create(); +``` + +#### Retrieve a Connect + +```python +const entity = await method + .entities("ent_qKNBB68bfHGNA") + .connect + .retrieve("cxn_4ewMmBbjYDMR4"); ``` -### List Entities +### Entity Verification Sessions + +The Entity Verification Sessions manages phone and identity verification sessions for Entities. Entities need to verify their identity and/or phone in order for them to be used throughout the Method API. +​ +####Verification Requirements + +Entity verification requirements differ on a team-by-team basis. A team’s unique verification process is pre-defined during onboarding based on your team’s specific use case. Contact your Method CSM for more information. + +The method key in entity.verification object will enumerate the phone & identity verifications available for your Entity. Refer to the Entity Object. + +>Any Entity Verification Session will expire 10 minutes after creation. If the verification is not completed within that time limit, another verification session will need to be created. + +#### Retrieve a Verification Session ```python -entities = method.entities.list() +const response = await method + .entities("ent_au22b1fbFJbp8") + .verification_sessions + .retrieve("evf_qTNNzCQ63zHJ9"); ``` -### Refresh Capabilities +#### Create a BYO KYC Verification ```python -entity = method.entities.refresh_capabilities('ent_au22b1fbFJbp8') +const response = await method + .entities("ent_XgYkTdiHyaz3e") + .verification_sessions + .create({ + 'type': "identity", + 'method': "byo_kyc", + 'byo_kyc': {}, + }); ``` -### Create Individual Auth Session +#### Create a KBA Verification ```python -response = method.entities.create_auth_session('ent_au22b1fbFJbp8') +const response = await method + .entities("ent_hy3xhPDfWDVxi") + .verification_sessions + .create({ + 'type': "identity", + 'method': "kba", + 'kba': {}, + }); ``` -### Update Individual Auth Session +#### Update a KBA Verification ```python -response = method.entities.update_auth_session('ent_au22b1fbFJbp8', { - "answers": [ - { - "question_id": "qtn_ywWqCnXDGGmmg", - "answer_id": "ans_74H68MJjqNhk8" +const response = await method + .entities("ent_hy3xhPDfWDVxi") + .verification_sessions + .update("evf_ywizPrR6WDxDG", { + 'type': "identity", + 'method': "kba", + 'kba': { + 'answers': [ + { + 'question_id': "qtn_xgP6cGhq34fHW", + 'answer_id': "ans_dbKCwDGwrrBgi" + }, + { + 'question_id': "qtn_kmfdEftQ9zc6T", + 'answer_id': "ans_LXN83xnJAUNFb" + }, + { + 'question_id': "qtn_6mWegPLBpAFxb", + 'answer_id': "ans_EKi47D8wA6YN3" + } + ] + } + }); +``` + +#### Create a BYO SMS Verification + +```python +const response = await method + .entities("ent_XgYkTdiHyaz3e") + .verification_sessions + .create({ + 'type': "phone", + 'method': "byo_sms", + 'byo_sms': { + 'timestamp': "2023-12-28T14:35:15.816Z", }, - ... - ] -}) + }); +``` + +#### Create a SMS Verification + +```python +const response = await method + .entities("ent_au22b1fbFJbp8") + .verification_sessions + .create({ + 'type': "phone", + 'method': "sms", + 'sms': {}, + }); +``` + +#### Update a SMS Verification + +```python +const response = await method + .entities("ent_au22b1fbFJbp8") + .verification_sessions + .update("evf_3VT3bHTCnPbrm", { + 'type': "phone", + 'method': "sms", + 'sms': { 'sms_code': "884134" }, + }); +``` + +#### Create a SNA Verification + +```python +const response = await method + .entities("ent_au22b1fbFJbp8") + .verification_sessions + .create({ + 'type': "phone", + 'method': "sna", + 'sna': {}, + }); +``` + +#### Update a SNA Verification + +```python +const response = await method + .entities("ent_BYdNCVApmp7Gx") + .verification_sessions + .update("evf_qTNNzCQ63zHJ9", { + 'type': "phone", + 'method': "sna", + 'sna': {}, + }); +``` + +### Credit Scores + +The Credit Score endpoint returns the latest credit score and score factors for an Entity. + +#### Create Individual Credit Scores + +```python +const entity = await method + .entities('ent_au22b1fbFJbp8') + .credit_scores + .create(); +``` + +#### Retrieve Individual Credit Scores + +```python +const entity = await method + .entities('ent_au22b1fbFJbp8') + .credit_scores + .retrieve('crs_pn4ca33GXFaCE'); +``` + +### Identities + +The identities endpoint is used to retrieve the underlying identity (PII) of an Entity. The Identity endpoint requires an Entity to have at least a name and phone number. + +For an active entity or entities with a matched identity (verification.identity.matched) the identity endpoint will return a single identity with the PII. + +For all other entities the identity endpoint could return multiple identities as the provided PII was not enough to disambiguate and match a single identity. + +>The Identity endpoint is restricted to most teams. Contact your Method CSM for more information. + +#### Create Identities + +```python +const entity = await method + .entities("ent_au22b1fbFJbp8") + .identities + .create(); +``` + +#### Retrieve Identities + +```python +const entity = await method + .entities('ent_au22b1fbFJbp8') + .identities + .retrieve('idn_NhTRUVEknYaFM'); +``` + +### Entity Products + +The Entity Products endpoint outlines the Products (capabilities) an Entity has access to, and provides an overview of the status of all the Products. + +>Access to most products requires an Entity to be active. However, some products have restricted access requiring team-by-team enablement. + +#### List all Products + +```python +const response = await method + .entities('ent_TYHMaRJUUeJ7U') + .products + .list(); +``` + +#### Retrieve a Product + +```python +const response = await method + .entities("ent_TYHMaRJUUeJ7U") + .products + .retrieve("prd_jPRDcQPMk43Ek"); +``` + +### Entity Subscriptions + +The Entity Subscriptions endpoint controls the state of all Subscriptions for an Entity. + +Subscriptions are Products that can provide continuous updates via Webhooks. (e.g. Credit Score Subscription provides updates on an Entity’s credit score) + +>Most subscriptions are accessible by default. However, some subscriptions have restricted access requiring team-by-team enablement and elevated account verification. + +#### Create a Subscription + +```python +const response = await method + .entities("ent_TYHMaRJUUeJ7U") + .subscriptions + .create('credit_score'); +``` + +#### List all Subscriptions + +```python +const response = await method + .entities('ent_TYHMaRJUUeJ7U') + .subscriptions + .list(); +``` + +#### Retrieve a Subscription + +```python +const response = await method + .entities("ent_TYHMaRJUUeJ7U") + .subscriptions + .retrieve("sub_6f7XtMLymQx3f"); +``` + +#### Delete a Subscription + +```python +const response = await method + .entities('ent_TYHMaRJUUeJ7U') + .subscriptions + .delete('sub_6f7XtMLymQx3f'); ``` ## Accounts -### Create Ach Account +Accounts are a representation of an Entity’s financial accounts. An Account can be a checking or savings account (ACH) or a credit card, student loan, mortgage, personal loan, etc. (Liability). + +### Core + +#### Create Ach Account ```python -account = method.accounts.create({ +const account = await method.accounts.create({ 'holder_id': 'ent_y1a9e1fbnJ1f3', 'ach': { 'routing': '367537407', 'number': '57838927', - 'type': 'checking' - } -}) + 'type': 'checking', + }, +}); ``` -### Create Liability Account +#### Create Liability Account ```python -account = method.accounts.create({ +const account = await method.accounts.create({ 'holder_id': 'ent_au22b1fbFJbp8', 'liability': { 'mch_id': 'mch_2', - 'account_number': '1122334455' + 'account_number': '1122334455', } -}) +}); ``` -### Retrieve Account +#### Retrieve Account ```python -account = method.accounts.get('acc_Zc4F2aTLt8CBt') +const account = await method.accounts.retrieve('acc_Zc4F2aTLt8CBt'); ``` -### List Accounts +#### List Accounts ```python -accounts = method.accounts.list() +const accounts = await method.accounts.list(); ``` -## ACH Verification +#### Withdraw an Account's consent + +```python +const account = await method.accounts.withdraw_consent('acc_yVf3mkzbhz9tj'); +``` -### Create Micro-Deposits Verification +### Updates + +The Updates endpoint retrieves in real-time account data including Balance, due dates, APRs, directly from the Account’s financial institution. + +#### Create an Update ```python -verification = method - .accounts('acc_b9q2XVAnNFbp3') - .verification - .create({ 'type': 'micro_deposits' }) +const response = await method + .accounts('acc_aEBDiLxiR8bqc') + .updates + .create(); ``` -### Create Plaid Verification +#### List all Updates ```python -verification = method - .accounts('acc_b9q2XVAnNFbp3') - .verification +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .updates + .list(); +``` + +#### Retrieve an Update + +```python +const response = await method + .accounts('acc_aEBDiLxiR8bqc') + .updates + .retrieve('upt_NYV5kfjskTTCJ'); +``` + +### Transactions + +The Transactions endpoint retrieves real-time transaction (authorization, clearing, etc) notifications for Credit Card Accounts directly from the card networks. + +>Subscription to Transactions is required before receiving transactional data for an account + +#### List all Transactions + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .transactions + .list({ + 'from_date': '2024-03-13', + 'to_date': '2024-03-15', + }); +``` + +#### Retrieve a Transaction + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .transactions + .retrieve('txn_aRrDMAmEAtHti'); +``` + +### Card Brand + +The CardBrand endpoint retrieves the associated credit card metadata (Product / Brand Name, Art, etc) directly from the card issuer. + +#### Create a Card Brand + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .card_brands + .create(); +``` + +#### Retrieve a Card Brand + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .card_brands + .retrieve('cbrd_eVMDNUPfrFk3e'); +``` + +### Payoffs + +The Payoffs endpoint retrieves a payoff quote in real-time from the Account’s financial institution / lender. + +>Payoffs are currently only available for Auto Loan accounts + +#### Create a Payoff + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .payoffs + .create(); +``` + +#### List all Payoffs + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .payoffs + .list(); +``` + +#### Retrieve a Payoff + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .payoffs + .retrieve('pyf_ELGT4hfikTTCJ'); +``` + +### Balances + +The Balance endpoint retrieves the real-time balance from the Account’s financial institution. + +#### Create a Balance + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .balances + .create(); +``` + +#### List all Balances + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .balances + .list(); +``` + +#### Retrieve a Balance + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .balances + .retrieve('bal_ebzh8KaR9HCBG'); +``` + +### Account Sensitive + +The Sensitive endpoint returns underlying sensitive Account information (e.g. PAN, CVV, account number). This product is only available for Liabilities. + +>The Sensitive endpoint is restricted to most teams, and requires PCI compliance to access. Contact your Method CSM for more information. + +#### Create a Sensitive + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .sensitive .create({ - 'type': 'plaid', + 'expand': [ + 'credit_card.number', + 'credit_card.exp_month', + 'credit_card.exp_year', + 'credit_card.cvv' + ], + }); +``` + +#### Retrieve a Sensitive + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .sensitive + .retrieve('astv_9WBBA6TH7n7iX'); +``` + +### Account Products + +The Account Products endpoint outlines the Products (capabilities) an Account has access to, and provides an overview of the status of all the Products. + +>Most products are accessible by default. However, some products have restricted access requiring team-by-team enablement and elevated account verification. + +#### List all Products + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .products + .list(); +``` + +#### Retrieve a Product + +```python +const response = await method + .accounts("acc_yVf3mkzbhz9tj") + .products + .retrieve("prd_FQFHqVNiCRb7J"); +``` +### Account Subscriptions + +The Account Subscriptions endpoint controls the state of all Subscriptions for an Account. + +Subscriptions are Products that can provide continuous updates via Webhooks. (e.g. Transaction Subscription provides real-time updates on a Credit Card’s transactions) + +>Most subscriptions are accessible by default. However, some subscriptions have restricted access requiring team-by-team enablement and elevated account verification. + +#### Create a Subscription + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .subscriptions + .create('update'); +``` + +#### List all Subscriptions + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .subscriptions + .list(); +``` + +#### Retrieve a Subscription + +```python +const response = await method + .accounts("acc_yVf3mkzbhz9tj") + .subscriptions + .retrieve("sub_P8c4bjj6xajxF"); +``` + +#### Delete a Subscription + +```python +const response = await method + .accounts('acc_yVf3mkzbhz9tj') + .subscriptions + .delete('sub_xM4VcfRWcJP8D'); +``` + +### Account Verification Sessions + +The account verification manages required verification to enable products for ACH or Liability accounts. + +For example, ACH Accounts require a verified AccountVerificationSession before they can be used as a source for Payments. + +#### Create Verification + +```python +const verification = await method + .accounts('acc_b9q2XVAnNFbp3') + .verification_sessions + .create({ type: '' }); +``` + +#### Update Micro Deposits Verification + +```python +const verification = await method + .accounts('acc_yVf3mkzbhz9tj') + .verification_sessions + .update('avf_yBQQNKmjRBTqF', { + 'micro_deposits': { + 'amounts': [10, 34] + } + }); +``` + +#### Update Plaid Verification + +```python +const verification = await method + .accounts('acc_yVf3mkzbhz9tj') + .verification_sessions + .update('avf_DjkdemgTQfqRD', { 'plaid': { 'balances': { 'available': 100, @@ -200,65 +740,42 @@ verification = method ... ] } - }) + }); ``` -### Create Teller Verification +#### Update Teller Verification ```python -verification = method - .accounts('acc_b9q2XVAnNFbp3') - .verification - .create({ - 'type': 'teller', +const verification = await method + .accounts('acc_yVf3mkzbhz9tj') + .verification_sessions + .update('avf_DjkdemgTQfqRD', { 'teller': { 'balances': { 'account_id': 'acc_ns9gkibeia6ad0rr6s00q', 'available': '93011.13', 'ledger': '93011.13', 'links': { - 'account': 'https://api.teller.io/accounts/acc_ns9gkibeia6ad0rr6s00q', - 'self': 'https://api.teller.io/accounts/acc_ns9gkibeia6ad0rr6s00q/balances' + 'account': 'https://reference.teller.io/accounts/acc_ns9gkibeia6ad0rr6s00q', + 'self': 'https://reference.teller.io/accounts/acc_ns9gkibeia6ad0rr6s00q/balances' } }, 'transactions': [ - { - 'account_id': 'acc_ns9gkia42a6ad0rr6s000', - 'amount': '-51.19', - 'date': '2022-01-04', - 'description': 'Venmo Payment', - 'details': { - 'category': 'services', - 'counterparty': { - 'name': 'LOUISE BENTLEY', - 'type': 'person' - }, - 'processing_status': 'complete' - }, - 'id': 'txn_ns9gkiph2a6ad0rr6s000', - 'links': { - 'account': 'https://api.teller.io/accounts/acc_ns9gkia42a6ad0rr6s000', - 'self': 'https://api.teller.io/accounts/acc_ns9gkia42a6ad0rr6s000/transactions/txn_ns9gkiph2a6ad0rr6s000' - }, - 'running_balance': None, - 'status': 'pending', - 'type': 'digital_payment' - } + ... ] } - }) + }); ``` -### Create MX Verification +#### Update MX Verification ```python -verification = method - .accounts('acc_b9q2XVAnNFbp3') - .verification - .create({ - 'type': 'mx', +const verification = await method + .accounts('acc_yVf3mkzbhz9tj') + .verification_sessions + .update('avf_DjkdemgTQfqRD', { 'mx': { - 'account ': { + 'account': { 'institution_code': 'chase', 'guid': 'ACT-06d7f44b-caae-0f6e-1384-01f52e75dcb1', 'account_number': None, @@ -269,152 +786,227 @@ verification = method 'balance': 1000.23, 'cash_balance': 1000.32, 'cash_surrender_value': 1000.23, - 'created_at': '2016-10-13T17:57:37+00:00' + 'created_at': '2016-10-13T17:57:37+00:00', ... }, 'transactions': [ ... ] - } - ) + } + }); ``` -### Update Verification +#### Update Standard Verification ```python -verification = method - .accounts('acc_b9q2XVAnNFbp3') - .verification - .update({ - 'micro_deposits': { - 'amounts': [10, 4] +const verification = await method + .accounts('acc_yVf3mkzbhz9tj') + .verification_sessions + .update('avf_DjkdemgTQfqRD', { + 'standard': { + 'number': '4111111111111111', + } + }); +``` + +#### Update Pre-auth Verification + +```python +const verification = await method + .accounts('acc_yVf3mkzbhz9tj') + .verification_sessions + .update('avf_DjkdemgTQfqRD', { + 'pre_auth': { + 'cvv': '031' } - }) + }); ``` -### Retrieve Verification +#### Retrieve Verification ```python -verification = method +const verification = await method .accounts('acc_b9q2XVAnNFbp3') - .verification - .get() + .verification_sessions + .retrieve('avf_DjkdemgTQfqRD'); ``` -## Merchants +## Merchants -### List Merchants +Merchants are resources that represent a specific type of liability for a financial institution. Method supports the majority of the financial institutions in the U.S. + +>Financial institutions that offer multiple liability products are represented in Method as separate Merchants. + +### Core + +#### List Merchants ```python -merchants = method.merchants.list() +const merchants = await method.merchants.list(); ``` -### Retrieve Merchant +#### Retrieve Merchant ```python -merchant = method.merchants.get('mch_1') +const merchant = await method.merchants.retrieve('mch_1'); ``` ## Payments -### Create Payment +A Payment is the transfer of funds from a source checking or savings bank account to a destination credit card, auto loan, mortgage, student loan, and more. + +All Payments are processed electronically between the source and destination, and take 2-3 business days depending on the receiving financial institution. + +### Core +#### Create Payment ```python -payment = method.payments.create({ +const payment = await method.payments.create({ 'amount': 5000, 'source': 'acc_JMJZT6r7iHi8e', 'destination': 'acc_AXthnzpBnxxWP', - 'description': 'Loan Pmt' -}) + 'description': 'Loan Pmt', +}); ``` -### Retrieve Payment +#### Retrieve Payment ```python -payment = method.payments.get('pmt_rPrDPEwyCVUcm') +const payment = await method.payments.retrieve('pmt_rPrDPEwyCVUcm'); ``` -### Delete Payment +#### Delete Payment ```python -payment = method.payments.delete('pmt_rPrDPEwyCVUcm') +const payment = await method.payments.delete('pmt_rPrDPEwyCVUcm'); ``` -### List Payments +#### List Payments ```python -payments = method.payments.list() +const payments = await method.payments.list(); ``` -## Reversals +### Reversals -### Retrieve Reversal +#### Retrieve Reversal ```python -reversal = method.payments('pmt_rPrDPEwyCVUcm').reversals.get('rvs_eaBAUJtetgMdR') +const reversal = await method.payments('pmt_rPrDPEwyCVUcm').reversals.retrieve('rvs_eaBAUJtetgMdR'); ``` -### Update Reversal +#### Update Reversal ```python -reversal = method +const reversal = await method .payments('pmt_rPrDPEwyCVUcm') .reversals - .update('rvs_eaBAUJtetgMdR', { 'status': 'pending' }) + .update('rvs_eaBAUJtetgMdR', { 'status': 'pending' }); ``` -### List Reversals for Payment +#### List Reversals for Payment ```python -reversals = method.payments('pmt_rPrDPEwyCVUcm').reversals.list() +const reversals = await method.payments('pmt_rPrDPEwyCVUcm').reversals.list(); ``` ## Webhooks -### Create Webhook +Webhooks allow the Method API to notify your application when certain events occur. + +To receive Webhook notifications, create a Webhook by registering a URL pointing to your application where triggered events should be sent to. This URL is where Method will send event information in an HTTPS POST request. + +​ +Handling webhooks +A Webhook event is considered successfully delivered when the corresponding URL endpoint responds with an HTTP status code of 200 within 5 seconds. + +If the criteria is not met, Method will reattempt 4 more times with each new attempt being delayed according to an exponential backoff algorithm, where the delay period between each attempt exponentially increases. + +>Webhooks that consistently fail to respond with a 200 will automatically be disabled. + +### Core + +#### Create Webhook ```python -webhook = method.webhooks.create({ +const webhook = await method.webhooks.create({ 'type': 'payment.update', 'url': 'https://api.example.app/webhook', - 'auth_token': 'md7UqcTSmvXCBzPORDwOkE' -}) + 'auth_token': 'md7UqcTSmvXCBzPORDwOkE', +}); ``` -### Retrieve Webhook +#### Retrieve Webhook ```python -webhook = method.webhooks.get('whk_cSGjA6d9N8y8R') +const webhook = await method.webhooks.retrieve('whk_cSGjA6d9N8y8R'); ``` -### Delete Webhoook +#### Delete Webhoook ```python -webhook = method.webhooks.delete('whk_cSGjA6d9N8y8R') +const webhook = await method.webhooks.delete('whk_cSGjA6d9N8y8R'); ``` -### List Webhooks +#### List Webhooks ```python -webhooks = method.webhooks.list() +const webhooks = await method.webhooks.list(); ``` ## Reports -### Create Report +Reports provide a filtered snapshot view of a specific resource. Method provides a fixed set of filters (Report Types) which include the following: + + - The resource to filter + - The applied filter + - The snapshot window + +### Core + +#### Create Report + +```python +const report = await method.reports.create({ 'type': 'payments.created.current' }); +``` + +#### Retrieve Report ```python -report = method.reports.create({ 'type': 'payments.created.current' }) +const report = await method.reports.retrieve('rpt_cj2mkA3hFyHT5'); ``` -### Retrieve Report +#### Download Report + +```python +const reportCSV = await method.reports.download('rpt_cj2mkA3hFyHT5'); +``` + +## Simulations + +To provide a seamless integration experience with Method in the development environment, you can simulate creations or updates for specific resources on-demand. + +This ensures that your application handles all cases for multistep flows that would naturally occur in live environments (sandbox and production). + +>Simulation endpoints are only accessible in the development environment. Attempts to access these endpoints in sandbox or production will result in a 403 Forbidden error. + +### Core + +#### Update a payment's status ```python -report = method.reports.get('rpt_cj2mkA3hFyHT5') +const payment = await method + .simulate + .payments + .update('pmt_rPrDPEwyCVUcm', { 'status': 'processing' }); ``` -### Download Report +#### Create a Transaction ```python -report_csv = method.reports.download('rpt_cj2mkA3hFyHT5') +const transaction = await method + .simulate + .accounts('acc_r6JUYN67HhCEM') + .transactions + .create(); ``` From 852cb6762eee930a5c7a30f798dd766b780930ad Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Mon, 3 Jun 2024 13:00:31 -0400 Subject: [PATCH 17/22] fixed quotes in README --- README.md | 420 +++++++++++++++++++++++++++--------------------------- 1 file changed, 210 insertions(+), 210 deletions(-) diff --git a/README.md b/README.md index 5d93cab..a970860 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,11 @@ pip install method-python ```python from method import Method -method = Method(env='production', api_key='{API_KEY}') +method = Method(env="production", api_key="{API_KEY}") # or -method = Method({'env': 'production', 'api_key': '{API_KEY}'}) +method = Method({"env": "production", "api_key": "{API_KEY}"}) ``` ## Entities @@ -35,20 +35,20 @@ Entity PII requirements are pre-defined during onboarding based on your team’s ```python const entity = await method.entities.create({ - type: "individual", - 'individual': { - 'first_name': "Kevin", - 'last_name': "Doyle", - 'phone': "+16505555555", - 'email': "kevin.doyle@gmail.com", - 'dob': "1997-03-18", + "type": "individual", + "individual": { + "first_name": "Kevin", + "last_name": "Doyle", + "phone": "+16505555555", + "email": "kevin.doyle@gmail.com", + "dob": "1997-03-18", }, - 'address': { - 'line1': "3300 N Interstate 35", - 'line2': None, - 'city': "Austin", - 'state': "TX", - 'zip': "78705", + "address": { + "line1": "3300 N Interstate 35", + "line2": None, + "city": "Austin", + "state": "TX", + "zip": "78705", }, }); ``` @@ -57,34 +57,34 @@ const entity = await method.entities.create({ ```python const entity = await method.entities.create({ - type: 'corporation', - corporation: { - name: 'Alphabet Inc.', - dba: 'Google', - ein: '641234567', - owners: [ + "type": "corporation", + "corporation": { + "name": "Alphabet Inc.", + "dba": "Google", + "ein": "641234567", + "owners": [ { - first_name: 'Sergey', - last_name: 'Brin', - phone: '+16505555555', - email: 'sergey@google.com', - dob: '1973-08-21', - address: { - line1: '600 Amphitheatre Parkway', - line2: None, - city: 'Mountain View', - state: 'CA', - zip: '94043', + "first_name": "Sergey", + "last_name": "Brin", + "phone": "+16505555555", + "email": "sergey@google.com", + "dob": "1973-08-21", + "address": { + "line1": "600 Amphitheatre Parkway", + "line2": None, + "city": "Mountain View", + "state": "CA", + "zip": "94043", }, }, ], }, - address: { - line1: '1600 Amphitheatre Parkway', - line2: None, - city: 'Mountain View', - state: 'CA', - zip: '94043', + "address": { + "line1": "1600 Amphitheatre Parkway", + "line2": None, + "city": "Mountain View", + "state": "CA", + "zip": "94043", }, }); ``` @@ -92,18 +92,18 @@ const entity = await method.entities.create({ #### Retrieve Entity ```python -const entity = await method.entities.retrieve('ent_au22b1fbFJbp8'); +const entity = await method.entities.retrieve("ent_au22b1fbFJbp8"); ``` #### Update Entity ```python -const entity = await method.entities.update('ent_au22b1fbFJbp8', { - 'individual': { - 'first_name': 'Kevin', - 'last_name': 'Doyle', - 'email': 'kevin.doyle@gmail.com', - 'dob': '1997-03-18', +const entity = await method.entities.update("ent_au22b1fbFJbp8", { + "individual": { + "first_name": "Kevin", + "last_name": "Doyle", + "email": "kevin.doyle@gmail.com", + "dob": "1997-03-18", }, }); ``` @@ -114,10 +114,10 @@ const entity = await method.entities.update('ent_au22b1fbFJbp8', { const entities = await method.entities.list(); ``` -#### Withdraw an Entity's consent +#### Withdraw an Entity"s consent ```python -const entity = await method.entities.withdraw_consent('ent_au22b1fbFJbp8'); +const entity = await method.entities.withdraw_consent("ent_au22b1fbFJbp8"); ``` ### Connect @@ -170,9 +170,9 @@ const response = await method .entities("ent_XgYkTdiHyaz3e") .verification_sessions .create({ - 'type': "identity", - 'method': "byo_kyc", - 'byo_kyc': {}, + "type": "identity", + "method": "byo_kyc", + "byo_kyc": {}, }); ``` @@ -183,9 +183,9 @@ const response = await method .entities("ent_hy3xhPDfWDVxi") .verification_sessions .create({ - 'type': "identity", - 'method': "kba", - 'kba': {}, + "type": "identity", + "method": "kba", + "kba": {}, }); ``` @@ -196,21 +196,21 @@ const response = await method .entities("ent_hy3xhPDfWDVxi") .verification_sessions .update("evf_ywizPrR6WDxDG", { - 'type': "identity", - 'method': "kba", - 'kba': { - 'answers': [ + "type": "identity", + "method": "kba", + "kba": { + "answers": [ { - 'question_id': "qtn_xgP6cGhq34fHW", - 'answer_id': "ans_dbKCwDGwrrBgi" + "question_id": "qtn_xgP6cGhq34fHW", + "answer_id": "ans_dbKCwDGwrrBgi" }, { - 'question_id': "qtn_kmfdEftQ9zc6T", - 'answer_id': "ans_LXN83xnJAUNFb" + "question_id": "qtn_kmfdEftQ9zc6T", + "answer_id": "ans_LXN83xnJAUNFb" }, { - 'question_id': "qtn_6mWegPLBpAFxb", - 'answer_id': "ans_EKi47D8wA6YN3" + "question_id": "qtn_6mWegPLBpAFxb", + "answer_id": "ans_EKi47D8wA6YN3" } ] } @@ -224,10 +224,10 @@ const response = await method .entities("ent_XgYkTdiHyaz3e") .verification_sessions .create({ - 'type': "phone", - 'method': "byo_sms", - 'byo_sms': { - 'timestamp': "2023-12-28T14:35:15.816Z", + "type": "phone", + "method": "byo_sms", + "byo_sms": { + "timestamp": "2023-12-28T14:35:15.816Z", }, }); ``` @@ -239,9 +239,9 @@ const response = await method .entities("ent_au22b1fbFJbp8") .verification_sessions .create({ - 'type': "phone", - 'method': "sms", - 'sms': {}, + "type": "phone", + "method": "sms", + "sms": {}, }); ``` @@ -252,9 +252,9 @@ const response = await method .entities("ent_au22b1fbFJbp8") .verification_sessions .update("evf_3VT3bHTCnPbrm", { - 'type': "phone", - 'method': "sms", - 'sms': { 'sms_code': "884134" }, + "type": "phone", + "method": "sms", + "sms": { "sms_code": "884134" }, }); ``` @@ -265,9 +265,9 @@ const response = await method .entities("ent_au22b1fbFJbp8") .verification_sessions .create({ - 'type': "phone", - 'method': "sna", - 'sna': {}, + "type": "phone", + "method": "sna", + "sna": {}, }); ``` @@ -278,9 +278,9 @@ const response = await method .entities("ent_BYdNCVApmp7Gx") .verification_sessions .update("evf_qTNNzCQ63zHJ9", { - 'type': "phone", - 'method': "sna", - 'sna': {}, + "type": "phone", + "method": "sna", + "sna": {}, }); ``` @@ -292,7 +292,7 @@ The Credit Score endpoint returns the latest credit score and score factors for ```python const entity = await method - .entities('ent_au22b1fbFJbp8') + .entities("ent_au22b1fbFJbp8") .credit_scores .create(); ``` @@ -301,9 +301,9 @@ const entity = await method ```python const entity = await method - .entities('ent_au22b1fbFJbp8') + .entities("ent_au22b1fbFJbp8") .credit_scores - .retrieve('crs_pn4ca33GXFaCE'); + .retrieve("crs_pn4ca33GXFaCE"); ``` ### Identities @@ -329,9 +329,9 @@ const entity = await method ```python const entity = await method - .entities('ent_au22b1fbFJbp8') + .entities("ent_au22b1fbFJbp8") .identities - .retrieve('idn_NhTRUVEknYaFM'); + .retrieve("idn_NhTRUVEknYaFM"); ``` ### Entity Products @@ -344,7 +344,7 @@ The Entity Products endpoint outlines the Products (capabilities) an Entity has ```python const response = await method - .entities('ent_TYHMaRJUUeJ7U') + .entities("ent_TYHMaRJUUeJ7U") .products .list(); ``` @@ -372,14 +372,14 @@ Subscriptions are Products that can provide continuous updates via Webhooks. (e. const response = await method .entities("ent_TYHMaRJUUeJ7U") .subscriptions - .create('credit_score'); + .create("credit_score"); ``` #### List all Subscriptions ```python const response = await method - .entities('ent_TYHMaRJUUeJ7U') + .entities("ent_TYHMaRJUUeJ7U") .subscriptions .list(); ``` @@ -397,9 +397,9 @@ const response = await method ```python const response = await method - .entities('ent_TYHMaRJUUeJ7U') + .entities("ent_TYHMaRJUUeJ7U") .subscriptions - .delete('sub_6f7XtMLymQx3f'); + .delete("sub_6f7XtMLymQx3f"); ``` ## Accounts @@ -412,11 +412,11 @@ Accounts are a representation of an Entity’s financial accounts. An Account ca ```python const account = await method.accounts.create({ - 'holder_id': 'ent_y1a9e1fbnJ1f3', - 'ach': { - 'routing': '367537407', - 'number': '57838927', - 'type': 'checking', + "holder_id": "ent_y1a9e1fbnJ1f3", + "ach": { + "routing": "367537407", + "number": "57838927", + "type": "checking", }, }); ``` @@ -425,10 +425,10 @@ const account = await method.accounts.create({ ```python const account = await method.accounts.create({ - 'holder_id': 'ent_au22b1fbFJbp8', - 'liability': { - 'mch_id': 'mch_2', - 'account_number': '1122334455', + "holder_id": "ent_au22b1fbFJbp8", + "liability": { + "mch_id": "mch_2", + "account_number": "1122334455", } }); ``` @@ -436,7 +436,7 @@ const account = await method.accounts.create({ #### Retrieve Account ```python -const account = await method.accounts.retrieve('acc_Zc4F2aTLt8CBt'); +const account = await method.accounts.retrieve("acc_Zc4F2aTLt8CBt"); ``` #### List Accounts @@ -445,10 +445,10 @@ const account = await method.accounts.retrieve('acc_Zc4F2aTLt8CBt'); const accounts = await method.accounts.list(); ``` -#### Withdraw an Account's consent +#### Withdraw an Account"s consent ```python -const account = await method.accounts.withdraw_consent('acc_yVf3mkzbhz9tj'); +const account = await method.accounts.withdraw_consent("acc_yVf3mkzbhz9tj"); ``` ### Updates @@ -459,7 +459,7 @@ The Updates endpoint retrieves in real-time account data including Balance, due ```python const response = await method - .accounts('acc_aEBDiLxiR8bqc') + .accounts("acc_aEBDiLxiR8bqc") .updates .create(); ``` @@ -468,7 +468,7 @@ const response = await method ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .updates .list(); ``` @@ -477,9 +477,9 @@ const response = await method ```python const response = await method - .accounts('acc_aEBDiLxiR8bqc') + .accounts("acc_aEBDiLxiR8bqc") .updates - .retrieve('upt_NYV5kfjskTTCJ'); + .retrieve("upt_NYV5kfjskTTCJ"); ``` ### Transactions @@ -492,11 +492,11 @@ The Transactions endpoint retrieves real-time transaction (authorization, cleari ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .transactions .list({ - 'from_date': '2024-03-13', - 'to_date': '2024-03-15', + "from_date": "2024-03-13", + "to_date": "2024-03-15", }); ``` @@ -504,9 +504,9 @@ const response = await method ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .transactions - .retrieve('txn_aRrDMAmEAtHti'); + .retrieve("txn_aRrDMAmEAtHti"); ``` ### Card Brand @@ -517,7 +517,7 @@ The CardBrand endpoint retrieves the associated credit card metadata (Product / ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .card_brands .create(); ``` @@ -526,9 +526,9 @@ const response = await method ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .card_brands - .retrieve('cbrd_eVMDNUPfrFk3e'); + .retrieve("cbrd_eVMDNUPfrFk3e"); ``` ### Payoffs @@ -541,7 +541,7 @@ The Payoffs endpoint retrieves a payoff quote in real-time from the Account’s ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .payoffs .create(); ``` @@ -550,7 +550,7 @@ const response = await method ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .payoffs .list(); ``` @@ -559,9 +559,9 @@ const response = await method ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .payoffs - .retrieve('pyf_ELGT4hfikTTCJ'); + .retrieve("pyf_ELGT4hfikTTCJ"); ``` ### Balances @@ -572,7 +572,7 @@ The Balance endpoint retrieves the real-time balance from the Account’s financ ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .balances .create(); ``` @@ -581,7 +581,7 @@ const response = await method ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .balances .list(); ``` @@ -590,9 +590,9 @@ const response = await method ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .balances - .retrieve('bal_ebzh8KaR9HCBG'); + .retrieve("bal_ebzh8KaR9HCBG"); ``` ### Account Sensitive @@ -605,14 +605,14 @@ The Sensitive endpoint returns underlying sensitive Account information (e.g. PA ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .sensitive .create({ - 'expand': [ - 'credit_card.number', - 'credit_card.exp_month', - 'credit_card.exp_year', - 'credit_card.cvv' + "expand": [ + "credit_card.number", + "credit_card.exp_month", + "credit_card.exp_year", + "credit_card.cvv" ], }); ``` @@ -621,9 +621,9 @@ const response = await method ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .sensitive - .retrieve('astv_9WBBA6TH7n7iX'); + .retrieve("astv_9WBBA6TH7n7iX"); ``` ### Account Products @@ -636,7 +636,7 @@ The Account Products endpoint outlines the Products (capabilities) an Account ha ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .products .list(); ``` @@ -661,16 +661,16 @@ Subscriptions are Products that can provide continuous updates via Webhooks. (e. ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .subscriptions - .create('update'); + .create("update"); ``` #### List all Subscriptions ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .subscriptions .list(); ``` @@ -688,9 +688,9 @@ const response = await method ```python const response = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .subscriptions - .delete('sub_xM4VcfRWcJP8D'); + .delete("sub_xM4VcfRWcJP8D"); ``` ### Account Verification Sessions @@ -703,20 +703,20 @@ For example, ACH Accounts require a verified AccountVerificationSession before t ```python const verification = await method - .accounts('acc_b9q2XVAnNFbp3') + .accounts("acc_b9q2XVAnNFbp3") .verification_sessions - .create({ type: '' }); + .create({ "type": "" }); ``` #### Update Micro Deposits Verification ```python const verification = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .verification_sessions - .update('avf_yBQQNKmjRBTqF', { - 'micro_deposits': { - 'amounts': [10, 34] + .update("avf_yBQQNKmjRBTqF", { + "micro_deposits": { + "amounts": [10, 34] } }); ``` @@ -725,18 +725,18 @@ const verification = await method ```python const verification = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .verification_sessions - .update('avf_DjkdemgTQfqRD', { - 'plaid': { - 'balances': { - 'available': 100, - 'current': 110, - 'iso_currency_code': 'USD', - 'limit': None, - 'unofficial_currency_code': None + .update("avf_DjkdemgTQfqRD", { + "plaid": { + "balances": { + "available": 100, + "current": 110, + "iso_currency_code": "USD", + "limit": None, + "unofficial_currency_code": None }, - 'transactions': [ + "transactions": [ ... ] } @@ -747,20 +747,20 @@ const verification = await method ```python const verification = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .verification_sessions - .update('avf_DjkdemgTQfqRD', { - 'teller': { - 'balances': { - 'account_id': 'acc_ns9gkibeia6ad0rr6s00q', - 'available': '93011.13', - 'ledger': '93011.13', - 'links': { - 'account': 'https://reference.teller.io/accounts/acc_ns9gkibeia6ad0rr6s00q', - 'self': 'https://reference.teller.io/accounts/acc_ns9gkibeia6ad0rr6s00q/balances' + .update("avf_DjkdemgTQfqRD", { + "teller": { + "balances": { + "account_id": "acc_ns9gkibeia6ad0rr6s00q", + "available": "93011.13", + "ledger": "93011.13", + "links": { + "account": "https://reference.teller.io/accounts/acc_ns9gkibeia6ad0rr6s00q", + "self": "https://reference.teller.io/accounts/acc_ns9gkibeia6ad0rr6s00q/balances" } }, - 'transactions': [ + "transactions": [ ... ] } @@ -771,25 +771,25 @@ const verification = await method ```python const verification = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .verification_sessions - .update('avf_DjkdemgTQfqRD', { - 'mx': { - 'account': { - 'institution_code': 'chase', - 'guid': 'ACT-06d7f44b-caae-0f6e-1384-01f52e75dcb1', - 'account_number': None, - 'apr': None, - 'apy': None, - 'available_balance': 1000.23, - 'available_credit': None, - 'balance': 1000.23, - 'cash_balance': 1000.32, - 'cash_surrender_value': 1000.23, - 'created_at': '2016-10-13T17:57:37+00:00', + .update("avf_DjkdemgTQfqRD", { + "mx": { + "account": { + "institution_code": "chase", + "guid": "ACT-06d7f44b-caae-0f6e-1384-01f52e75dcb1", + "account_number": None, + "apr": None, + "apy": None, + "available_balance": 1000.23, + "available_credit": None, + "balance": 1000.23, + "cash_balance": 1000.32, + "cash_surrender_value": 1000.23, + "created_at": "2016-10-13T17:57:37+00:00", ... }, - 'transactions': [ + "transactions": [ ... ] } @@ -800,11 +800,11 @@ const verification = await method ```python const verification = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .verification_sessions - .update('avf_DjkdemgTQfqRD', { - 'standard': { - 'number': '4111111111111111', + .update("avf_DjkdemgTQfqRD", { + "standard": { + "number": "4111111111111111", } }); ``` @@ -813,11 +813,11 @@ const verification = await method ```python const verification = await method - .accounts('acc_yVf3mkzbhz9tj') + .accounts("acc_yVf3mkzbhz9tj") .verification_sessions - .update('avf_DjkdemgTQfqRD', { - 'pre_auth': { - 'cvv': '031' + .update("avf_DjkdemgTQfqRD", { + "pre_auth": { + "cvv": "031" } }); ``` @@ -826,9 +826,9 @@ const verification = await method ```python const verification = await method - .accounts('acc_b9q2XVAnNFbp3') + .accounts("acc_b9q2XVAnNFbp3") .verification_sessions - .retrieve('avf_DjkdemgTQfqRD'); + .retrieve("avf_DjkdemgTQfqRD"); ``` ## Merchants @@ -848,7 +848,7 @@ const merchants = await method.merchants.list(); #### Retrieve Merchant ```python -const merchant = await method.merchants.retrieve('mch_1'); +const merchant = await method.merchants.retrieve("mch_1"); ``` ## Payments @@ -862,23 +862,23 @@ All Payments are processed electronically between the source and destination, an #### Create Payment ```python const payment = await method.payments.create({ - 'amount': 5000, - 'source': 'acc_JMJZT6r7iHi8e', - 'destination': 'acc_AXthnzpBnxxWP', - 'description': 'Loan Pmt', + "amount": 5000, + "source": "acc_JMJZT6r7iHi8e", + "destination": "acc_AXthnzpBnxxWP", + "description": "Loan Pmt", }); ``` #### Retrieve Payment ```python -const payment = await method.payments.retrieve('pmt_rPrDPEwyCVUcm'); +const payment = await method.payments.retrieve("pmt_rPrDPEwyCVUcm"); ``` #### Delete Payment ```python -const payment = await method.payments.delete('pmt_rPrDPEwyCVUcm'); +const payment = await method.payments.delete("pmt_rPrDPEwyCVUcm"); ``` #### List Payments @@ -892,22 +892,22 @@ const payments = await method.payments.list(); #### Retrieve Reversal ```python -const reversal = await method.payments('pmt_rPrDPEwyCVUcm').reversals.retrieve('rvs_eaBAUJtetgMdR'); +const reversal = await method.payments("pmt_rPrDPEwyCVUcm").reversals.retrieve("rvs_eaBAUJtetgMdR"); ``` #### Update Reversal ```python const reversal = await method - .payments('pmt_rPrDPEwyCVUcm') + .payments("pmt_rPrDPEwyCVUcm") .reversals - .update('rvs_eaBAUJtetgMdR', { 'status': 'pending' }); + .update("rvs_eaBAUJtetgMdR", { "status": "pending" }); ``` #### List Reversals for Payment ```python -const reversals = await method.payments('pmt_rPrDPEwyCVUcm').reversals.list(); +const reversals = await method.payments("pmt_rPrDPEwyCVUcm").reversals.list(); ``` ## Webhooks @@ -930,22 +930,22 @@ If the criteria is not met, Method will reattempt 4 more times with each new att ```python const webhook = await method.webhooks.create({ - 'type': 'payment.update', - 'url': 'https://api.example.app/webhook', - 'auth_token': 'md7UqcTSmvXCBzPORDwOkE', + "type": "payment.update", + "url": "https://api.example.app/webhook", + "auth_token": "md7UqcTSmvXCBzPORDwOkE", }); ``` #### Retrieve Webhook ```python -const webhook = await method.webhooks.retrieve('whk_cSGjA6d9N8y8R'); +const webhook = await method.webhooks.retrieve("whk_cSGjA6d9N8y8R"); ``` #### Delete Webhoook ```python -const webhook = await method.webhooks.delete('whk_cSGjA6d9N8y8R'); +const webhook = await method.webhooks.delete("whk_cSGjA6d9N8y8R"); ``` #### List Webhooks @@ -967,19 +967,19 @@ Reports provide a filtered snapshot view of a specific resource. Method provides #### Create Report ```python -const report = await method.reports.create({ 'type': 'payments.created.current' }); +const report = await method.reports.create({ "type": "payments.created.current" }); ``` #### Retrieve Report ```python -const report = await method.reports.retrieve('rpt_cj2mkA3hFyHT5'); +const report = await method.reports.retrieve("rpt_cj2mkA3hFyHT5"); ``` #### Download Report ```python -const reportCSV = await method.reports.download('rpt_cj2mkA3hFyHT5'); +const reportCSV = await method.reports.download("rpt_cj2mkA3hFyHT5"); ``` ## Simulations @@ -992,13 +992,13 @@ This ensures that your application handles all cases for multistep flows that wo ### Core -#### Update a payment's status +#### Update a payment"s status ```python const payment = await method .simulate .payments - .update('pmt_rPrDPEwyCVUcm', { 'status': 'processing' }); + .update("pmt_rPrDPEwyCVUcm", { "status": "processing" }); ``` #### Create a Transaction @@ -1006,7 +1006,7 @@ const payment = await method ```python const transaction = await method .simulate - .accounts('acc_r6JUYN67HhCEM') + .accounts("acc_r6JUYN67HhCEM") .transactions .create(); ``` From 4c8b55253a5fe0431d79a3e4656dbf6dcf9f7ec2 Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Mon, 3 Jun 2024 13:14:23 -0400 Subject: [PATCH 18/22] nit new line at end of file --- method/resources/Accounts/Account.py | 1 - method/resources/Accounts/Balances.py | 2 +- method/resources/Accounts/CardBrands.py | 2 +- method/resources/Accounts/Payoffs.py | 2 +- method/resources/Accounts/Sensitive.py | 2 +- method/resources/Accounts/Subscriptions.py | 2 +- method/resources/Accounts/Updates.py | 2 +- method/resources/Accounts/VerificationSessions.py | 2 +- method/resources/Accounts/__init__.py | 2 +- method/resources/Elements/Element.py | 1 - method/resources/Elements/Token.py | 1 - method/resources/Entities/Connect.py | 2 +- method/resources/Entities/CreditScores.py | 2 +- method/resources/Entities/Identities.py | 2 +- method/resources/Entities/Products.py | 2 +- method/resources/Entities/Sensitive.py | 2 +- method/resources/Entities/Subscriptions.py | 2 +- method/resources/Entities/__init__.py | 2 +- method/resources/Payments/__init__.py | 2 +- method/resources/Simulate/Accounts.py | 2 +- method/resources/Simulate/Transactions.py | 2 +- test/resources/Payment_test.py | 2 +- test/resources/Report_test.py | 2 +- test/resources/Webhook_test.py | 2 +- 24 files changed, 21 insertions(+), 24 deletions(-) diff --git a/method/resources/Accounts/Account.py b/method/resources/Accounts/Account.py index bb7f2eb..8309306 100644 --- a/method/resources/Accounts/Account.py +++ b/method/resources/Accounts/Account.py @@ -122,4 +122,3 @@ def create(self, opts: Union[AccountACHCreateOpts, AccountLiabilityCreateOpts], def withdraw_consent(self, acc_id: str, data: AccountWithdrawConsentOpts = { 'type': 'withdraw', 'reason': 'holder_withdrew_consent' }) -> Account: return super(AccountResource, self)._create_with_sub_path('{acc_id}/consent'.format(acc_id=acc_id), data) - diff --git a/method/resources/Accounts/Balances.py b/method/resources/Accounts/Balances.py index 9b021b9..fa897c5 100644 --- a/method/resources/Accounts/Balances.py +++ b/method/resources/Accounts/Balances.py @@ -28,4 +28,4 @@ def retrieve(self, bal_id: str) -> AccountBalance: return super(AccountBalancesResource, self)._get_with_id(bal_id) def create(self) -> AccountBalance: - return super(AccountBalancesResource, self)._create({}) \ No newline at end of file + return super(AccountBalancesResource, self)._create({}) diff --git a/method/resources/Accounts/CardBrands.py b/method/resources/Accounts/CardBrands.py index 856a182..18cfbde 100644 --- a/method/resources/Accounts/CardBrands.py +++ b/method/resources/Accounts/CardBrands.py @@ -33,4 +33,4 @@ def retrieve(self, crbd_id: str) -> AccountCardBrand: return super(AccountCardBrandsResource, self)._get_with_id(crbd_id) def create(self) -> AccountCardBrand: - return super(AccountCardBrandsResource, self)._create({}) \ No newline at end of file + return super(AccountCardBrandsResource, self)._create({}) diff --git a/method/resources/Accounts/Payoffs.py b/method/resources/Accounts/Payoffs.py index 3552a52..2a32b5a 100644 --- a/method/resources/Accounts/Payoffs.py +++ b/method/resources/Accounts/Payoffs.py @@ -33,4 +33,4 @@ def retrieve(self, pyf_id: str) -> AccountPayoff: return super(AccountPayoffsResource, self)._get_with_id(pyf_id) def create(self) -> AccountPayoff: - return super(AccountPayoffsResource, self)._create({}) \ No newline at end of file + return super(AccountPayoffsResource, self)._create({}) diff --git a/method/resources/Accounts/Sensitive.py b/method/resources/Accounts/Sensitive.py index 3e642f5..22cd61f 100644 --- a/method/resources/Accounts/Sensitive.py +++ b/method/resources/Accounts/Sensitive.py @@ -53,4 +53,4 @@ def retrieve(self, astv_id: str) -> AccountSensitive: return super(AccountSensitiveResource, self)._get_with_id(astv_id) def create(self, data: AccountSensitiveCreateOpts) -> AccountSensitive: - return super(AccountSensitiveResource, self)._create(data) \ No newline at end of file + return super(AccountSensitiveResource, self)._create(data) diff --git a/method/resources/Accounts/Subscriptions.py b/method/resources/Accounts/Subscriptions.py index 6f9b72c..d9e4abe 100644 --- a/method/resources/Accounts/Subscriptions.py +++ b/method/resources/Accounts/Subscriptions.py @@ -46,4 +46,4 @@ def retrieve(self, sub_id: str) -> AccountSubscriptionsResponse: def delete(self, sub_id: str) -> AccountSubscription: return super(AccountSubscriptionsResource, self)._delete(sub_id) - \ No newline at end of file + \ No newline at end of file diff --git a/method/resources/Accounts/Updates.py b/method/resources/Accounts/Updates.py index f9d3ca8..76c6d56 100644 --- a/method/resources/Accounts/Updates.py +++ b/method/resources/Accounts/Updates.py @@ -33,4 +33,4 @@ def list(self, params: Optional[ResourceListOpts] = None) -> List[AccountUpdate] return super(AccountUpdatesResource, self)._list(params) def create(self) -> AccountUpdate: - return super(AccountUpdatesResource, self)._create({}) \ No newline at end of file + return super(AccountUpdatesResource, self)._create({}) diff --git a/method/resources/Accounts/VerificationSessions.py b/method/resources/Accounts/VerificationSessions.py index 1bf5c03..096cf11 100644 --- a/method/resources/Accounts/VerificationSessions.py +++ b/method/resources/Accounts/VerificationSessions.py @@ -158,4 +158,4 @@ def retrieve(self, avs_id: str) -> AccountVerificationSession: return super(AccountVerificationSessionResource, self)._get_with_id(avs_id) def update(self, avs_id: str, opts: AccountVerificationSessionUpdateOpts) -> AccountVerificationSession: - return super(AccountVerificationSessionResource, self)._update_with_id(avs_id, opts) \ No newline at end of file + return super(AccountVerificationSessionResource, self)._update_with_id(avs_id, opts) diff --git a/method/resources/Accounts/__init__.py b/method/resources/Accounts/__init__.py index dc14354..b5d6d0f 100644 --- a/method/resources/Accounts/__init__.py +++ b/method/resources/Accounts/__init__.py @@ -1 +1 @@ -from method.resources.Accounts.Account import Account, AccountResource \ No newline at end of file +from method.resources.Accounts.Account import Account, AccountResource diff --git a/method/resources/Elements/Element.py b/method/resources/Elements/Element.py index f3f3091..c4935c0 100644 --- a/method/resources/Elements/Element.py +++ b/method/resources/Elements/Element.py @@ -10,4 +10,3 @@ def __init__(self, config: Configuration): _config = config.add_path('elements') super(ElementResource, self).__init__(_config) self.token = ElementTokenResource(_config) - diff --git a/method/resources/Elements/Token.py b/method/resources/Elements/Token.py index c2711f9..7e7563e 100644 --- a/method/resources/Elements/Token.py +++ b/method/resources/Elements/Token.py @@ -180,4 +180,3 @@ def create(self, opts: ElementTokenCreateOpts) -> ElementToken: def results(self, pk_elem_id: str) -> ElementResults: return super(ElementTokenResource, self)._get_with_sub_path('{_id}/results'.format(_id=pk_elem_id)) - diff --git a/method/resources/Entities/Connect.py b/method/resources/Entities/Connect.py index f3ba10f..dfa30e7 100644 --- a/method/resources/Entities/Connect.py +++ b/method/resources/Entities/Connect.py @@ -30,4 +30,4 @@ def retrieve(self, cxn_id: str) -> EntityConnect: return super(EntityConnectResource, self)._get_with_id(cxn_id) def create(self) -> EntityConnect: - return super(EntityConnectResource, self)._create({}) \ No newline at end of file + return super(EntityConnectResource, self)._create({}) diff --git a/method/resources/Entities/CreditScores.py b/method/resources/Entities/CreditScores.py index 98de297..7e5dfbb 100644 --- a/method/resources/Entities/CreditScores.py +++ b/method/resources/Entities/CreditScores.py @@ -44,4 +44,4 @@ def retrieve(self, crs_id: str) -> EntityCreditScores: return super(EntityCreditScoresResource, self)._get_with_id(crs_id) def create(self) -> EntityCreditScores: - return super(EntityCreditScoresResource, self)._create({}) \ No newline at end of file + return super(EntityCreditScoresResource, self)._create({}) diff --git a/method/resources/Entities/Identities.py b/method/resources/Entities/Identities.py index 40ca4a0..5b9d511 100644 --- a/method/resources/Entities/Identities.py +++ b/method/resources/Entities/Identities.py @@ -31,4 +31,4 @@ def retrieve(self, identity_id: str) -> EntityIdentity: return super(EntityIdentityResource, self)._get_with_id(identity_id) def create(self, opts: Optional[EntityIdentity] = {}) -> EntityIdentity: - return super(EntityIdentityResource, self)._create(opts) \ No newline at end of file + return super(EntityIdentityResource, self)._create(opts) diff --git a/method/resources/Entities/Products.py b/method/resources/Entities/Products.py index 262c9c3..b043eae 100644 --- a/method/resources/Entities/Products.py +++ b/method/resources/Entities/Products.py @@ -37,4 +37,4 @@ def retrieve(self, prd_id: str) -> EntityProduct: return super(EntityProductResource, self)._get_with_id(prd_id) def list(self) -> EntityProductListResponse: - return super(EntityProductResource, self)._list() \ No newline at end of file + return super(EntityProductResource, self)._list() diff --git a/method/resources/Entities/Sensitive.py b/method/resources/Entities/Sensitive.py index fbe4b8c..45c2cdd 100644 --- a/method/resources/Entities/Sensitive.py +++ b/method/resources/Entities/Sensitive.py @@ -26,4 +26,4 @@ def __init__(self, config: Configuration): super(EntitySensitiveResource, self).__init__(config.add_path('sensitive')) def retrieve(self) -> EntitySensitive: - return super(EntitySensitive, self)._get() \ No newline at end of file + return super(EntitySensitive, self)._get() diff --git a/method/resources/Entities/Subscriptions.py b/method/resources/Entities/Subscriptions.py index b9fcfeb..d71d541 100644 --- a/method/resources/Entities/Subscriptions.py +++ b/method/resources/Entities/Subscriptions.py @@ -50,4 +50,4 @@ def create(self, sub_name: EntitySubscriptionNamesLiterals) -> EntitySubscriptio return super(EntitySubscriptionsResource, self)._create({ 'enroll': sub_name }) def delete(self, sub_id: str) -> EntitySubscriptionResponseOpts: - return super(EntitySubscriptionsResource, self)._delete(sub_id) \ No newline at end of file + return super(EntitySubscriptionsResource, self)._delete(sub_id) diff --git a/method/resources/Entities/__init__.py b/method/resources/Entities/__init__.py index aac81d9..8ff4a61 100644 --- a/method/resources/Entities/__init__.py +++ b/method/resources/Entities/__init__.py @@ -1 +1 @@ -from method.resources.Entities.Entity import Entity, EntityResource \ No newline at end of file +from method.resources.Entities.Entity import Entity, EntityResource diff --git a/method/resources/Payments/__init__.py b/method/resources/Payments/__init__.py index 7a02ae7..138ae4b 100644 --- a/method/resources/Payments/__init__.py +++ b/method/resources/Payments/__init__.py @@ -1,2 +1,2 @@ from method.resources.Payments.Payment import Payment, PaymentResource -from method.resources.Payments.Reversal import ReversalResource \ No newline at end of file +from method.resources.Payments.Reversal import ReversalResource diff --git a/method/resources/Simulate/Accounts.py b/method/resources/Simulate/Accounts.py index 92970c7..fd21f05 100644 --- a/method/resources/Simulate/Accounts.py +++ b/method/resources/Simulate/Accounts.py @@ -16,4 +16,4 @@ def __init__(self, config: Configuration): super(SimulateAccountResource, self).__init__(config.add_path('accounts')) def __call__(self, acc_id) -> SimulateAccountSubResources: - return SimulateAccountSubResources(acc_id, self.config) \ No newline at end of file + return SimulateAccountSubResources(acc_id, self.config) diff --git a/method/resources/Simulate/Transactions.py b/method/resources/Simulate/Transactions.py index 7794f64..6b805ca 100644 --- a/method/resources/Simulate/Transactions.py +++ b/method/resources/Simulate/Transactions.py @@ -7,4 +7,4 @@ def __init__(self, config: Configuration): super(SimulateTransactionsResource, self).__init__(config.add_path('transactions')) def create(self) -> AccountTransaction: - return super(SimulateTransactionsResource, self)._create({}) \ No newline at end of file + return super(SimulateTransactionsResource, self)._create({}) diff --git a/test/resources/Payment_test.py b/test/resources/Payment_test.py index 9fd9d98..2f5de4f 100644 --- a/test/resources/Payment_test.py +++ b/test/resources/Payment_test.py @@ -171,4 +171,4 @@ def test_delete_payment(setup): 'updated_at': payments_delete_response['updated_at'], } - assert payments_delete_response == expect_results \ No newline at end of file + assert payments_delete_response == expect_results diff --git a/test/resources/Report_test.py b/test/resources/Report_test.py index 6524ebd..939dab1 100644 --- a/test/resources/Report_test.py +++ b/test/resources/Report_test.py @@ -56,4 +56,4 @@ def test_download_report(): report_download_response = method.reports.download(report_create_response['id']) - assert report_download_response is not None \ No newline at end of file + assert report_download_response is not None diff --git a/test/resources/Webhook_test.py b/test/resources/Webhook_test.py index 7f044f5..889bf93 100644 --- a/test/resources/Webhook_test.py +++ b/test/resources/Webhook_test.py @@ -65,4 +65,4 @@ def test_delete_webhook(): webhooks_delete_response = method.webhooks.delete(webhooks_create_response['id']) - assert webhooks_delete_response == None \ No newline at end of file + assert webhooks_delete_response == None From 41aaf7b1f2439f76ee66d0765982d67077b44b29 Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Tue, 4 Jun 2024 11:10:22 -0400 Subject: [PATCH 19/22] nit spacing fixes --- method/resources/Accounts/Balances.py | 2 ++ method/resources/Accounts/Sensitive.py | 1 + method/resources/Accounts/Types.py | 1 - method/resources/Accounts/VerificationSessions.py | 1 + method/resources/Elements/Token.py | 1 + method/resources/Entities/Types.py | 2 ++ method/resources/Simulate/Transactions.py | 1 + 7 files changed, 8 insertions(+), 1 deletion(-) diff --git a/method/resources/Accounts/Balances.py b/method/resources/Accounts/Balances.py index fa897c5..2a8dc8f 100644 --- a/method/resources/Accounts/Balances.py +++ b/method/resources/Accounts/Balances.py @@ -11,6 +11,7 @@ 'failed' ] + class AccountBalance(TypedDict): id: str account_id: str @@ -19,6 +20,7 @@ class AccountBalance(TypedDict): error: Optional[ResourceError] created_at: str updated_at: str + class AccountBalancesResource(Resource): def __init__(self, config: Configuration): diff --git a/method/resources/Accounts/Sensitive.py b/method/resources/Accounts/Sensitive.py index 22cd61f..636d8b2 100644 --- a/method/resources/Accounts/Sensitive.py +++ b/method/resources/Accounts/Sensitive.py @@ -16,6 +16,7 @@ 'credit_card.cvv' ] + class AccountSensitiveLoan(TypedDict): number: str diff --git a/method/resources/Accounts/Types.py b/method/resources/Accounts/Types.py index 4ef0284..868094f 100644 --- a/method/resources/Accounts/Types.py +++ b/method/resources/Accounts/Types.py @@ -80,7 +80,6 @@ ] - AccountLiabilityAutoLoanSubTypesLiterals = Literal[ 'lease', 'loan' diff --git a/method/resources/Accounts/VerificationSessions.py b/method/resources/Accounts/VerificationSessions.py index 096cf11..559ec80 100644 --- a/method/resources/Accounts/VerificationSessions.py +++ b/method/resources/Accounts/VerificationSessions.py @@ -24,6 +24,7 @@ 'pre_auth' ] + AccountVerificationPassFailLiterals = Literal[ 'pass', 'fail' diff --git a/method/resources/Elements/Token.py b/method/resources/Elements/Token.py index 7e7563e..f1895e9 100644 --- a/method/resources/Elements/Token.py +++ b/method/resources/Elements/Token.py @@ -76,6 +76,7 @@ 'balance_transfer' ] + ElementProducts = Literal[ 'balance', 'payoff', diff --git a/method/resources/Entities/Types.py b/method/resources/Entities/Types.py index 406b6f0..19e4424 100644 --- a/method/resources/Entities/Types.py +++ b/method/resources/Entities/Types.py @@ -57,6 +57,7 @@ 'transunion' ] + EntitySensitiveFieldsLiterals = Literal[ 'first_name', 'last_name', @@ -110,6 +111,7 @@ class EntityReceiveOnly(TypedDict): phone: Optional[str] email: Optional[str] + class EntityKYCAddressRecordData(TypedDict): address: str city: str diff --git a/method/resources/Simulate/Transactions.py b/method/resources/Simulate/Transactions.py index 6b805ca..377253f 100644 --- a/method/resources/Simulate/Transactions.py +++ b/method/resources/Simulate/Transactions.py @@ -2,6 +2,7 @@ from method.configuration import Configuration from method.resources.Accounts.Transactions import AccountTransaction + class SimulateTransactionsResource(Resource): def __init__(self, config: Configuration): super(SimulateTransactionsResource, self).__init__(config.add_path('transactions')) From 6857ecc8c243bb07786625d08146d6ae4dc7147d Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Wed, 5 Jun 2024 13:19:22 -0400 Subject: [PATCH 20/22] removed const and awaits from README --- README.md | 158 +++++++++++++++++++++++++++--------------------------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index a970860..6989686 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Entity PII requirements are pre-defined during onboarding based on your team’s #### Create Individual Entity ```python -const entity = await method.entities.create({ +entity = method.entities.create({ "type": "individual", "individual": { "first_name": "Kevin", @@ -56,7 +56,7 @@ const entity = await method.entities.create({ #### Create Corporation Entity ```python -const entity = await method.entities.create({ +entity = method.entities.create({ "type": "corporation", "corporation": { "name": "Alphabet Inc.", @@ -92,13 +92,13 @@ const entity = await method.entities.create({ #### Retrieve Entity ```python -const entity = await method.entities.retrieve("ent_au22b1fbFJbp8"); +entity = method.entities.retrieve("ent_au22b1fbFJbp8"); ``` #### Update Entity ```python -const entity = await method.entities.update("ent_au22b1fbFJbp8", { +entity = method.entities.update("ent_au22b1fbFJbp8", { "individual": { "first_name": "Kevin", "last_name": "Doyle", @@ -111,13 +111,13 @@ const entity = await method.entities.update("ent_au22b1fbFJbp8", { #### List Entities ```python -const entities = await method.entities.list(); +entities = method.entities.list(); ``` #### Withdraw an Entity"s consent ```python -const entity = await method.entities.withdraw_consent("ent_au22b1fbFJbp8"); +entity = method.entities.withdraw_consent("ent_au22b1fbFJbp8"); ``` ### Connect @@ -127,7 +127,7 @@ The Connect endpoint identifies & connects all the liability accounts (e.g. Cred #### Create a Connect ```python -const entity = await method +entity = method .entities("ent_qKNBB68bfHGNA") .connect .create(); @@ -136,7 +136,7 @@ const entity = await method #### Retrieve a Connect ```python -const entity = await method +entity = method .entities("ent_qKNBB68bfHGNA") .connect .retrieve("cxn_4ewMmBbjYDMR4"); @@ -157,7 +157,7 @@ The method key in entity.verification object will enumerate the phone & identity #### Retrieve a Verification Session ```python -const response = await method +response = method .entities("ent_au22b1fbFJbp8") .verification_sessions .retrieve("evf_qTNNzCQ63zHJ9"); @@ -166,7 +166,7 @@ const response = await method #### Create a BYO KYC Verification ```python -const response = await method +response = method .entities("ent_XgYkTdiHyaz3e") .verification_sessions .create({ @@ -179,7 +179,7 @@ const response = await method #### Create a KBA Verification ```python -const response = await method +response = method .entities("ent_hy3xhPDfWDVxi") .verification_sessions .create({ @@ -192,7 +192,7 @@ const response = await method #### Update a KBA Verification ```python -const response = await method +response = method .entities("ent_hy3xhPDfWDVxi") .verification_sessions .update("evf_ywizPrR6WDxDG", { @@ -220,7 +220,7 @@ const response = await method #### Create a BYO SMS Verification ```python -const response = await method +response = method .entities("ent_XgYkTdiHyaz3e") .verification_sessions .create({ @@ -235,7 +235,7 @@ const response = await method #### Create a SMS Verification ```python -const response = await method +response = method .entities("ent_au22b1fbFJbp8") .verification_sessions .create({ @@ -248,7 +248,7 @@ const response = await method #### Update a SMS Verification ```python -const response = await method +response = method .entities("ent_au22b1fbFJbp8") .verification_sessions .update("evf_3VT3bHTCnPbrm", { @@ -261,7 +261,7 @@ const response = await method #### Create a SNA Verification ```python -const response = await method +response = method .entities("ent_au22b1fbFJbp8") .verification_sessions .create({ @@ -274,7 +274,7 @@ const response = await method #### Update a SNA Verification ```python -const response = await method +response = method .entities("ent_BYdNCVApmp7Gx") .verification_sessions .update("evf_qTNNzCQ63zHJ9", { @@ -291,7 +291,7 @@ The Credit Score endpoint returns the latest credit score and score factors for #### Create Individual Credit Scores ```python -const entity = await method +entity = method .entities("ent_au22b1fbFJbp8") .credit_scores .create(); @@ -300,7 +300,7 @@ const entity = await method #### Retrieve Individual Credit Scores ```python -const entity = await method +entity = method .entities("ent_au22b1fbFJbp8") .credit_scores .retrieve("crs_pn4ca33GXFaCE"); @@ -319,7 +319,7 @@ For all other entities the identity endpoint could return multiple identities as #### Create Identities ```python -const entity = await method +entity = method .entities("ent_au22b1fbFJbp8") .identities .create(); @@ -328,7 +328,7 @@ const entity = await method #### Retrieve Identities ```python -const entity = await method +entity = method .entities("ent_au22b1fbFJbp8") .identities .retrieve("idn_NhTRUVEknYaFM"); @@ -343,7 +343,7 @@ The Entity Products endpoint outlines the Products (capabilities) an Entity has #### List all Products ```python -const response = await method +response = method .entities("ent_TYHMaRJUUeJ7U") .products .list(); @@ -352,7 +352,7 @@ const response = await method #### Retrieve a Product ```python -const response = await method +response = method .entities("ent_TYHMaRJUUeJ7U") .products .retrieve("prd_jPRDcQPMk43Ek"); @@ -369,7 +369,7 @@ Subscriptions are Products that can provide continuous updates via Webhooks. (e. #### Create a Subscription ```python -const response = await method +response = method .entities("ent_TYHMaRJUUeJ7U") .subscriptions .create("credit_score"); @@ -378,7 +378,7 @@ const response = await method #### List all Subscriptions ```python -const response = await method +response = method .entities("ent_TYHMaRJUUeJ7U") .subscriptions .list(); @@ -387,7 +387,7 @@ const response = await method #### Retrieve a Subscription ```python -const response = await method +response = method .entities("ent_TYHMaRJUUeJ7U") .subscriptions .retrieve("sub_6f7XtMLymQx3f"); @@ -396,7 +396,7 @@ const response = await method #### Delete a Subscription ```python -const response = await method +response = method .entities("ent_TYHMaRJUUeJ7U") .subscriptions .delete("sub_6f7XtMLymQx3f"); @@ -411,7 +411,7 @@ Accounts are a representation of an Entity’s financial accounts. An Account ca #### Create Ach Account ```python -const account = await method.accounts.create({ +account = method.accounts.create({ "holder_id": "ent_y1a9e1fbnJ1f3", "ach": { "routing": "367537407", @@ -424,7 +424,7 @@ const account = await method.accounts.create({ #### Create Liability Account ```python -const account = await method.accounts.create({ +account = method.accounts.create({ "holder_id": "ent_au22b1fbFJbp8", "liability": { "mch_id": "mch_2", @@ -436,19 +436,19 @@ const account = await method.accounts.create({ #### Retrieve Account ```python -const account = await method.accounts.retrieve("acc_Zc4F2aTLt8CBt"); +account = method.accounts.retrieve("acc_Zc4F2aTLt8CBt"); ``` #### List Accounts ```python -const accounts = await method.accounts.list(); +accounts = method.accounts.list(); ``` #### Withdraw an Account"s consent ```python -const account = await method.accounts.withdraw_consent("acc_yVf3mkzbhz9tj"); +account = method.accounts.withdraw_consent("acc_yVf3mkzbhz9tj"); ``` ### Updates @@ -458,7 +458,7 @@ The Updates endpoint retrieves in real-time account data including Balance, due #### Create an Update ```python -const response = await method +response = method .accounts("acc_aEBDiLxiR8bqc") .updates .create(); @@ -467,7 +467,7 @@ const response = await method #### List all Updates ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .updates .list(); @@ -476,7 +476,7 @@ const response = await method #### Retrieve an Update ```python -const response = await method +response = method .accounts("acc_aEBDiLxiR8bqc") .updates .retrieve("upt_NYV5kfjskTTCJ"); @@ -491,7 +491,7 @@ The Transactions endpoint retrieves real-time transaction (authorization, cleari #### List all Transactions ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .transactions .list({ @@ -503,7 +503,7 @@ const response = await method #### Retrieve a Transaction ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .transactions .retrieve("txn_aRrDMAmEAtHti"); @@ -516,7 +516,7 @@ The CardBrand endpoint retrieves the associated credit card metadata (Product / #### Create a Card Brand ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .card_brands .create(); @@ -525,7 +525,7 @@ const response = await method #### Retrieve a Card Brand ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .card_brands .retrieve("cbrd_eVMDNUPfrFk3e"); @@ -540,7 +540,7 @@ The Payoffs endpoint retrieves a payoff quote in real-time from the Account’s #### Create a Payoff ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .payoffs .create(); @@ -549,7 +549,7 @@ const response = await method #### List all Payoffs ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .payoffs .list(); @@ -558,7 +558,7 @@ const response = await method #### Retrieve a Payoff ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .payoffs .retrieve("pyf_ELGT4hfikTTCJ"); @@ -571,7 +571,7 @@ The Balance endpoint retrieves the real-time balance from the Account’s financ #### Create a Balance ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .balances .create(); @@ -580,7 +580,7 @@ const response = await method #### List all Balances ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .balances .list(); @@ -589,7 +589,7 @@ const response = await method #### Retrieve a Balance ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .balances .retrieve("bal_ebzh8KaR9HCBG"); @@ -604,7 +604,7 @@ The Sensitive endpoint returns underlying sensitive Account information (e.g. PA #### Create a Sensitive ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .sensitive .create({ @@ -620,7 +620,7 @@ const response = await method #### Retrieve a Sensitive ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .sensitive .retrieve("astv_9WBBA6TH7n7iX"); @@ -635,7 +635,7 @@ The Account Products endpoint outlines the Products (capabilities) an Account ha #### List all Products ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .products .list(); @@ -644,7 +644,7 @@ const response = await method #### Retrieve a Product ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .products .retrieve("prd_FQFHqVNiCRb7J"); @@ -660,7 +660,7 @@ Subscriptions are Products that can provide continuous updates via Webhooks. (e. #### Create a Subscription ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .subscriptions .create("update"); @@ -669,7 +669,7 @@ const response = await method #### List all Subscriptions ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .subscriptions .list(); @@ -678,7 +678,7 @@ const response = await method #### Retrieve a Subscription ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .subscriptions .retrieve("sub_P8c4bjj6xajxF"); @@ -687,7 +687,7 @@ const response = await method #### Delete a Subscription ```python -const response = await method +response = method .accounts("acc_yVf3mkzbhz9tj") .subscriptions .delete("sub_xM4VcfRWcJP8D"); @@ -702,7 +702,7 @@ For example, ACH Accounts require a verified AccountVerificationSession before t #### Create Verification ```python -const verification = await method +verification = method .accounts("acc_b9q2XVAnNFbp3") .verification_sessions .create({ "type": "" }); @@ -711,7 +711,7 @@ const verification = await method #### Update Micro Deposits Verification ```python -const verification = await method +verification = method .accounts("acc_yVf3mkzbhz9tj") .verification_sessions .update("avf_yBQQNKmjRBTqF", { @@ -724,7 +724,7 @@ const verification = await method #### Update Plaid Verification ```python -const verification = await method +verification = method .accounts("acc_yVf3mkzbhz9tj") .verification_sessions .update("avf_DjkdemgTQfqRD", { @@ -746,7 +746,7 @@ const verification = await method #### Update Teller Verification ```python -const verification = await method +verification = method .accounts("acc_yVf3mkzbhz9tj") .verification_sessions .update("avf_DjkdemgTQfqRD", { @@ -770,7 +770,7 @@ const verification = await method #### Update MX Verification ```python -const verification = await method +verification = method .accounts("acc_yVf3mkzbhz9tj") .verification_sessions .update("avf_DjkdemgTQfqRD", { @@ -799,7 +799,7 @@ const verification = await method #### Update Standard Verification ```python -const verification = await method +verification = method .accounts("acc_yVf3mkzbhz9tj") .verification_sessions .update("avf_DjkdemgTQfqRD", { @@ -812,7 +812,7 @@ const verification = await method #### Update Pre-auth Verification ```python -const verification = await method +verification = method .accounts("acc_yVf3mkzbhz9tj") .verification_sessions .update("avf_DjkdemgTQfqRD", { @@ -825,7 +825,7 @@ const verification = await method #### Retrieve Verification ```python -const verification = await method +verification = method .accounts("acc_b9q2XVAnNFbp3") .verification_sessions .retrieve("avf_DjkdemgTQfqRD"); @@ -842,13 +842,13 @@ Merchants are resources that represent a specific type of liability for a financ #### List Merchants ```python -const merchants = await method.merchants.list(); +merchants = method.merchants.list(); ``` #### Retrieve Merchant ```python -const merchant = await method.merchants.retrieve("mch_1"); +merchant = method.merchants.retrieve("mch_1"); ``` ## Payments @@ -861,7 +861,7 @@ All Payments are processed electronically between the source and destination, an #### Create Payment ```python -const payment = await method.payments.create({ +payment = method.payments.create({ "amount": 5000, "source": "acc_JMJZT6r7iHi8e", "destination": "acc_AXthnzpBnxxWP", @@ -872,19 +872,19 @@ const payment = await method.payments.create({ #### Retrieve Payment ```python -const payment = await method.payments.retrieve("pmt_rPrDPEwyCVUcm"); +payment = method.payments.retrieve("pmt_rPrDPEwyCVUcm"); ``` #### Delete Payment ```python -const payment = await method.payments.delete("pmt_rPrDPEwyCVUcm"); +payment = method.payments.delete("pmt_rPrDPEwyCVUcm"); ``` #### List Payments ```python -const payments = await method.payments.list(); +payments = method.payments.list(); ``` ### Reversals @@ -892,13 +892,13 @@ const payments = await method.payments.list(); #### Retrieve Reversal ```python -const reversal = await method.payments("pmt_rPrDPEwyCVUcm").reversals.retrieve("rvs_eaBAUJtetgMdR"); +reversal = method.payments("pmt_rPrDPEwyCVUcm").reversals.retrieve("rvs_eaBAUJtetgMdR"); ``` #### Update Reversal ```python -const reversal = await method +reversal = method .payments("pmt_rPrDPEwyCVUcm") .reversals .update("rvs_eaBAUJtetgMdR", { "status": "pending" }); @@ -907,7 +907,7 @@ const reversal = await method #### List Reversals for Payment ```python -const reversals = await method.payments("pmt_rPrDPEwyCVUcm").reversals.list(); +reversals = method.payments("pmt_rPrDPEwyCVUcm").reversals.list(); ``` ## Webhooks @@ -929,7 +929,7 @@ If the criteria is not met, Method will reattempt 4 more times with each new att #### Create Webhook ```python -const webhook = await method.webhooks.create({ +webhook = method.webhooks.create({ "type": "payment.update", "url": "https://api.example.app/webhook", "auth_token": "md7UqcTSmvXCBzPORDwOkE", @@ -939,19 +939,19 @@ const webhook = await method.webhooks.create({ #### Retrieve Webhook ```python -const webhook = await method.webhooks.retrieve("whk_cSGjA6d9N8y8R"); +webhook = method.webhooks.retrieve("whk_cSGjA6d9N8y8R"); ``` #### Delete Webhoook ```python -const webhook = await method.webhooks.delete("whk_cSGjA6d9N8y8R"); +webhook = method.webhooks.delete("whk_cSGjA6d9N8y8R"); ``` #### List Webhooks ```python -const webhooks = await method.webhooks.list(); +webhooks = method.webhooks.list(); ``` ## Reports @@ -967,19 +967,19 @@ Reports provide a filtered snapshot view of a specific resource. Method provides #### Create Report ```python -const report = await method.reports.create({ "type": "payments.created.current" }); +report = method.reports.create({ "type": "payments.created.current" }); ``` #### Retrieve Report ```python -const report = await method.reports.retrieve("rpt_cj2mkA3hFyHT5"); +report = method.reports.retrieve("rpt_cj2mkA3hFyHT5"); ``` #### Download Report ```python -const reportCSV = await method.reports.download("rpt_cj2mkA3hFyHT5"); +reportCSV = method.reports.download("rpt_cj2mkA3hFyHT5"); ``` ## Simulations @@ -995,7 +995,7 @@ This ensures that your application handles all cases for multistep flows that wo #### Update a payment"s status ```python -const payment = await method +payment = method .simulate .payments .update("pmt_rPrDPEwyCVUcm", { "status": "processing" }); @@ -1004,7 +1004,7 @@ const payment = await method #### Create a Transaction ```python -const transaction = await method +transaction = method .simulate .accounts("acc_r6JUYN67HhCEM") .transactions From d650c3186447c9600f83a9e0c9a0da0d54a8cdae Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Wed, 5 Jun 2024 13:46:13 -0400 Subject: [PATCH 21/22] fixed Entity type to include new changes --- method/resources/Entities/Entity.py | 36 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/method/resources/Entities/Entity.py b/method/resources/Entities/Entity.py index 260413b..051f9db 100644 --- a/method/resources/Entities/Entity.py +++ b/method/resources/Entities/Entity.py @@ -4,7 +4,7 @@ from method.configuration import Configuration from method.errors import ResourceError from method.resources.Entities.Types import EntityTypesLiterals, EntityCapabilitiesLiterals, EntityStatusesLiterals, \ - CreditReportBureausLiterals, EntityIndividual, EntityCorporation, EntityReceiveOnly, EntityAddress + CreditReportBureausLiterals, EntityIndividual, EntityCorporation, EntityAddress from method.resources.Entities.Connect import EntityConnectResource from method.resources.Entities.CreditScores import EntityCreditScoresResource from method.resources.Entities.Identities import EntityIdentityResource @@ -14,28 +14,10 @@ from method.resources.Entities.VerificationSessions import EntityVerificationSessionResource -class Entity(TypedDict): - id: str - type: EntityTypesLiterals - individual: Optional[EntityIndividual] - corporation: Optional[EntityCorporation] - receive_only: Optional[EntityReceiveOnly] - capabilities: List[EntityCapabilitiesLiterals] - available_capabilities: List[EntityCapabilitiesLiterals] - pending_capabilities: List[EntityCapabilitiesLiterals] - address: EntityAddress - status: EntityStatusesLiterals - error: Optional[ResourceError] - metadata: Optional[Dict[str, Any]] - created_at: str - updated_at: str - - class EntityCreateOpts(TypedDict): type: EntityTypesLiterals individual: Optional[EntityIndividual] corporation: Optional[EntityCorporation] - receive_only: Optional[EntityReceiveOnly] address: Optional[EntityAddress] metadata: Optional[Dict[str, Any]] @@ -120,6 +102,22 @@ class EntityUpdateAuthResponse(TypedDict): cxn_id: Optional[str] +class Entity(TypedDict): + id: str + type: EntityTypesLiterals + individual: Optional[EntityIndividual] + corporation: Optional[EntityCorporation] + capabilities: List[EntityCapabilitiesLiterals] + available_capabilities: List[EntityCapabilitiesLiterals] + pending_capabilities: List[EntityCapabilitiesLiterals] + address: EntityAddress + status: EntityStatusesLiterals + error: Optional[ResourceError] + metadata: Optional[Dict[str, Any]] + created_at: str + updated_at: str + + class EntitySubResources: connect: EntityConnectResource credit_scores: EntityCreditScoresResource From 2fabec1e6d179c132bae5d11a906d6ed98f18f02 Mon Sep 17 00:00:00 2001 From: Michael Ossig Date: Wed, 5 Jun 2024 13:53:22 -0400 Subject: [PATCH 22/22] updated tests with new structures, increased retries --- test/resources/Account_test.py | 2 -- test/resources/Entity_test.py | 2 -- test/resources/utils.py | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/test/resources/Account_test.py b/test/resources/Account_test.py index 7930385..c6ce8a4 100644 --- a/test/resources/Account_test.py +++ b/test/resources/Account_test.py @@ -783,9 +783,7 @@ def test_withdraw_account_consent(setup): 'holder_id': holder_1_response['id'], 'status': 'disabled', 'type': None, - 'ach': None, 'liability': None, - 'clearing': None, 'products': [], 'restricted_products': [], 'subscriptions': [], diff --git a/test/resources/Entity_test.py b/test/resources/Entity_test.py index ec22668..1df2191 100644 --- a/test/resources/Entity_test.py +++ b/test/resources/Entity_test.py @@ -640,8 +640,6 @@ def test_withdraw_entity_consent(): 'id': entities_create_response['id'], 'type': None, 'individual': None, - 'corporation': None, - 'receive_only': None, 'verification': None, 'error': { 'type': 'ENTITY_DISABLED', diff --git a/test/resources/utils.py b/test/resources/utils.py index 5379768..9695d9b 100644 --- a/test/resources/utils.py +++ b/test/resources/utils.py @@ -5,7 +5,7 @@ async def sleep(ms: int): async def await_results(fn): result = None - retries = 5 + retries = 10 while retries > 0: try: result = fn()