Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion method/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __init__(self, opts: MethodErrorOpts):
def catch(fn):
def wrapper(*args, **kwargs):
res = fn(*args, **kwargs)
if (res is not None) and ('error' in res):
if (res is not None) and ('error' in res) and ('id' not in res):
raise MethodError.generate(res['error'])
return res
return wrapper
Expand Down
3 changes: 3 additions & 0 deletions method/method.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from method.resources.RoutingNumber import RoutingNumberResource
from method.resources.Webhook import WebhookResource
from method.resources.HealthCheck import PingResponse, HealthCheckResource
from method.resources.Connection import ConnectionResource


class Method:
Expand All @@ -22,6 +23,7 @@ class Method:
routing_numbers: RoutingNumberResource
webhooks: WebhookResource
healthcheck: HealthCheckResource
connections: ConnectionResource

def __init__(self, opts: ConfigurationOpts = None, **kwargs: ConfigurationOpts):
_opts: ConfigurationOpts = {**(opts or {}), **kwargs} # type: ignore
Expand All @@ -37,6 +39,7 @@ def __init__(self, opts: ConfigurationOpts = None, **kwargs: ConfigurationOpts):
self.routing_numbers = RoutingNumberResource(config)
self.webhooks = WebhookResource(config)
self.healthcheck = HealthCheckResource(config)
self.connections = ConnectionResource(config)

def ping(self) -> PingResponse:
return self.healthcheck.get()
78 changes: 77 additions & 1 deletion method/resources/Account.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@

AccountCapabilitiesLiterals = Literal[
'payments:receive',
'payments:send'
'payments:send',
'data:retrieve'
]


Expand All @@ -30,6 +31,13 @@
'disabled'
]

AccountDetailTypesLiterals = Literal[
'bnpl_loan',
'depository',
'credit_card',
'student_loan'
]


class AccountACH(TypedDict):
routing: int
Expand Down Expand Up @@ -74,6 +82,68 @@ class AccountCreateOpts(TypedDict):
metadata: Optional[Dict[str, Any]]


class AccountDetailBNPLLoanUpcomingPaymentDue(TypedDict):
amount: int
date: str


class AccountDetailBNPLLoan(TypedDict):
name: Optional[str]
reference_id: str
balance: int
purchase_date: str
next_payment_due_date: Optional[str]
total_payments_count: int
payments_made_count: int
remaining_payments_count: int
autopay_enabled: bool
payoff_progress: int
interest_rate: int
description: Optional[str]
total_cost: int
total_paid: int
status: Literal['paid_off', 'refunded', 'in_progress']
upcoming_payments_due: List[AccountDetailBNPLLoanUpcomingPaymentDue]


class AccountDetailDepository(TypedDict):
name: Optional[str]
reference_number: str
balance: int


class AccountDetailCreditCard(TypedDict):
name: Optional[str]
reference_number: str
balance: int
last_payment_amount: int
last_payment_date: Optional[str]
next_payment_due_date: Optional[str]
next_payment_minimum_amount: int


# TODO[mdelcarmen]
class AccountDetailStudentLoan(TypedDict):
pass


class AccountDetail(TypedDict):
type: AccountDetailTypesLiterals
bnpl_loan: Optional[AccountDetailBNPLLoan]
depository: Optional[AccountDetailDepository]
credit_card: Optional[AccountDetailCreditCard]
student_loan: Optional[AccountDetailStudentLoan]


class AccountTransaction(TypedDict):
id: str
reference_id: str
date: str
amount: int
status: Literal['pending', 'success']
description: Optional[str]


class AccountListOpts(TypedDict):
holder_id: Optional[str]

Expand All @@ -100,3 +170,9 @@ def list(self, params: Optional[AccountListOpts] = None) -> List[Account]:

def create(self, opts: AccountCreateOpts, request_opts: Optional[RequestOpts] = None) -> Account:
return super(AccountResource, self)._create(opts, request_opts)

def get_detail(self, _id: str) -> AccountDetail:
return super(AccountResource, self)._get_with_sub_path('{_id}/detail'.format(_id=_id))

def get_transactions(self, _id: str) -> List[AccountTransaction]:
return super(AccountResource, self)._get_with_sub_path('{_id}/transactions'.format(_id=_id))
56 changes: 56 additions & 0 deletions method/resources/Connection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from typing import TypedDict, Optional, List, Dict, Any, Literal

from method.resources import Account
from method.errors import ResourceError
from method.resource import Resource
from method.configuration import Configuration


ConnectionSourcesLiterals = Literal[
'plaid',
'mch_5500'
]


ConnectionStatusesLiterals = Literal[
'success',
'reauth_required',
'syncing',
'failed'
]


class Connection(TypedDict):
id: str
entity_id: str
accounts: List[str]
source: ConnectionSourcesLiterals
status: ConnectionStatusesLiterals
error: Optional[ResourceError]
created_at: str
updated_at: str
last_synced_at: str


class ConnectionUpdateOpts(TypedDict):
status: Literal['syncing']


class ConnectionResource(Resource):
def __init__(self, config: Configuration):
super(ConnectionResource, self).__init__(config.add_path('connections'))

def get(self, _id: str) -> Connection:
return super(ConnectionResource, self)._get_with_id(_id)

def list(self) -> List[Connection]:
return super(ConnectionResource, self)._list(None)

def update(self, _id: str, opts: ConnectionUpdateOpts) -> Connection:
return super(ConnectionResource, self)._update_with_id(_id, opts)

def get_accounts(self, _id: str) -> List[Account]:
return super(ConnectionResource, self)._get_with_sub_path('{_id}/accounts'.format(_id=_id))

def get_public_account_tokens(self, _id: str) -> List[str]:
return super(ConnectionResource, self)._get_with_sub_path('{_id}/public_account_tokens'.format(_id=_id))
8 changes: 6 additions & 2 deletions method/resources/Element.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import TypedDict, Optional, Literal
from typing import TypedDict, Optional, Literal, List

from method.resource import Resource
from method.configuration import Configuration
Expand All @@ -25,7 +25,8 @@ class Element(TypedDict):


class ElementExchangePublicAccountOpts(TypedDict):
public_account_token: str
public_account_token: Optional[str]
public_account_tokens: Optional[List[str]]


class ElementResource(Resource):
Expand All @@ -37,3 +38,6 @@ def create_token(self, opts: ElementTokenCreateOpts) -> Element:

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)
4 changes: 4 additions & 0 deletions method/resources/Webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
'account.update',
'entity.update',
'entity.create',
'payment_reversal.create',
'payment_reversal.update',
'connection.create',
'connection.update',
'account_verification.create',
'account_verification.update',
'account_verification.sent',
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name='method-python',
version='0.0.15',
version='0.0.16',
description='Python library for the Method API',
author='Marco del Carmen',
author_email='marco@mdelcarmen.me',
Expand Down