from datetime import datetime
from prometeo import exceptions, base_client, base_session
from .models import (
Client as Client, Account as AccountModel, Movement, CreditCard as CreditCardModel,
Provider, ProviderDetail, PreprocessTransfer, ConfirmTransfer, TransferInstitution
)
from .exceptions import BankingClientError
TESTING_URL = 'https://test.prometeo.qualia.uy'
PRODUCTION_URL = 'https://prometeo.qualia.uy'
[docs]class Session(base_session.BaseSession):
"""
Encapsulates the user's session, returned by
:meth:`~prometeo.banking.client.BankingAPIClient.login`
"""
def __init__(self, client, status, session_key, context=None, field=None):
super(Session, self).__init__(client, status, session_key)
self._interactive_context = context
self._interactive_field = field
[docs] def get_clients(self):
"""
List the user's clients. Returns an empty list if the bank doesn't uses clients.
:rtype: List of :class:`~prometeo.banking.models.Client`
"""
return self._client.get_clients(self._session_key)
[docs] def select_client(self, client):
"""
Selects the client to use for this session.
:param client_id: The id of the client, obtained from listing the clients
:type client_id: str
"""
self._client.select_client(self._session_key, client.id)
[docs] def get_accounts(self):
"""
List all the user's accounts
:rtype: List of :class:`~prometeo.banking.models.Account`
"""
accounts_data = self._client.get_accounts(self._session_key)
accounts = []
for account_data in accounts_data:
accounts.append(Account(
self._client, self._session_key, account_data,
))
return accounts
[docs] def get_credit_cards(self):
"""
List all the user's credit cards
:rtype: List of :class:`~prometeo.banking.models.CreditCard`
"""
cards_data = self._client.get_credit_cards(self._session_key)
cards = []
for card_data in cards_data:
cards.append(CreditCard(
self._client, self._session_key, card_data,
))
return cards
[docs] def get_interactive_context(self):
"""
Necessary information to answer the login challenge, like a security question.
:rtype: str
"""
return self._interactive_context
[docs] def finish_login(self, provider, username, password, answer):
"""
Answer the security challenge, like an OTP or personal question.
:param provider: The provider used to login
:type provider: str
:param username: The username used to login
:type username: str
:param password: The password used to login
:type password: str
:param answer: The answer to the login challenge
:type answer: str
"""
self._client.call_api('POST', '/login/', data={
'provider': provider,
'username': username,
'password': password,
self._interactive_field: answer,
}, params={
'key': self._session_key,
})
[docs] def logout(self):
"""
Logs the user out and invalidates its session.
"""
self._client.logout(self._session_key)
[docs] def preprocess_transfer(self, origin_account, destination_institution,
destination_account, currency, amount, concept,
destination_owner_name, branch):
"""
Preprocess transfer.
:param origin_account: Account number from where it is transferred
:type origin_account: str
:param destination_institution: Id of the institution where it is transferred.
As provided by
:meth:`~prometeo.banking.client.BankingAPIClient.get_providers`
:type destination_institution: str
:param destination_account: Account number where it is transferred
:type destination_account: str
:param currency: Account currency in format ``ISO 4217``
:type currency: str
:param amount: Amount to transfer
:type amount: str
:param concept: Concept or description of the transfer
:type concept: str
:param destination_owner_name:
Name of the holder of the destination account (empty if not applicable)
:type destination_owner_name: str
:param branch: Branch number of the destination account
(empty if not applicable)
:type branch: str
:rtype: :class:`~prometeo.banking.models.PreprocessTransfer`
"""
return self._client.preprocess_transfer(self._session_key, origin_account,
destination_institution,
destination_account, currency, amount,
concept, destination_owner_name, branch)
[docs] def confirm_transfer(self, request_id, authorization_type, authorization_data):
"""
Confirm transfer.
:param request_id: Id of the request returned by the endpoint of
:meth:`~prometeo.banking.client.BankingAPIClient.preprocess_transfer`
:type request_id: str
:param authorization_type:
- ``cardCode`` Coordinates card
- ``pin`` Account personal pin
-
``otp`` One-time generated pin, sent by sms, email,
hard token or soft token.
- ``otp-api`` Hard token or soft token device, digitized by Promete
:type authorization_type: str
:param authorization_data: Verification value (pin number,
coordinates card response , etc) if there
are several values, they must be separated by commas.
:type authorization_data: str
:rtype: :class:`~prometeo.banking.models.ConfirmTransfer`
"""
return self._client.confirm_transfer(self._session_key, request_id,
authorization_type, authorization_data)
[docs] def list_transfer_institutions(self):
"""
List transfer institutions.
:rtype: :class:`~prometeo.banking.models.TransferInstitution`
"""
return self._client.list_transfer_institutions(self._session_key)
[docs]class BankingAPIClient(base_client.BaseClient):
"""
API Client for banking api
"""
ENVIRONMENTS = {
'testing': TESTING_URL,
'production': PRODUCTION_URL,
}
session_class = Session
[docs] def on_response(self, data):
if data['status'] == 'error':
if data['message'] == 'Invalid key':
raise exceptions.InvalidSessionKeyError(data['message'])
else:
raise BankingClientError(data['message'])
[docs] def login(self, provider, username, password, **kwargs):
"""
Start log in process with the provider
:param provider: Name of the provider,
use :meth:`~BankingAPIClient.get_providers`
to get a list of available providers
:type provider: str
:param username: Username used to log in to the banking app or web
:type username: str
:param password: User's password
:type password: str
:param kwargs: Extra login fields for providers that require it,
use :meth:`~BankingAPIClient.get_provider_detail`
to get a list of all the auth login fields
:type kwargs: dict
:rtype: :class:`~prometeo.banking.client.Session`
"""
data = {
'provider': provider,
'username': username,
'password': password,
}
data.update(kwargs)
response = self.call_api('POST', '/login/', data=data)
if response['status'] in ['logged_in', 'select_client']:
return Session(self, response['status'], response['key'])
elif response['status'] == 'interaction_required':
return Session(
self, response['status'], response['key'],
response['context'], response['field'],
)
elif response['status'] == 'wrong_credentials':
raise exceptions.WrongCredentialsError(response['message'])
else:
raise BankingClientError(response['message'])
def get_clients(self, session_key):
response = self.call_api('GET', '/client/', params={
'key': session_key,
})
clients = []
for id, name in response['clients'].items():
clients.append(Client(id=id, name=name))
return clients
def select_client(self, session_key, client_id):
self.call_api('GET', '/client/{}/'.format(client_id), params={
'key': session_key,
})
def get_accounts(self, session_key):
data = self.call_api('GET', '/account/', params={
'key': session_key,
})
return [
AccountModel(**account) for account in data['accounts']
]
def get_movements(
self, session_key, account_number, currency_code, date_start, date_end
):
data = self.call_api('GET', '/movement/', params={
'key': session_key,
'account': account_number,
'currency': currency_code,
'date_start': date_start.strftime('%d/%m/%Y'),
'date_end': date_end.strftime('%d/%m/%Y'),
})
return [
Movement(
id=movement['id'],
reference=movement['reference'],
date=datetime.strptime(movement['date'], "%d/%m/%Y"),
detail=movement['detail'],
debit=movement['debit'],
credit=movement['credit'],
)
for movement in data['movements']
]
def get_credit_cards(self, session_key):
data = self.call_api('GET', '/credit-card/', params={
'key': session_key,
})
return [
CreditCardModel(
id=credit_card['id'],
name=credit_card['name'],
number=credit_card['number'],
close_date=datetime.strptime(credit_card['close_date'], "%d/%m/%Y"),
due_date=datetime.strptime(credit_card['due_date'], "%d/%m/%Y"),
balance_local=credit_card['balance_local'],
balance_dollar=credit_card['balance_dollar'],
)
for credit_card in data['credit_cards']
]
def get_credit_card_movements(
self, session_key, card_number, currency_code, date_start, date_end
):
url = '/credit-card/{}/movements'.format(card_number)
data = self.call_api('GET', url, params={
'key': session_key,
'currency': currency_code,
'date_start': date_start.strftime('%d/%m/%Y'),
'date_end': date_end.strftime('%d/%m/%Y'),
})
return [
Movement(
id=movement['id'],
reference=movement['reference'],
date=datetime.strptime(movement['date'], "%d/%m/%Y"),
detail=movement['detail'],
debit=movement['debit'],
credit=movement['credit'],
)
for movement in data['movements']
]
[docs] def get_providers(self):
"""
List all available banks.
:rtype: :class:`~prometeo.banking.models.Provider`
"""
data = self.call_api('GET', '/provider/')
return [
Provider(**provider) for provider in data['providers']
]
[docs] def get_provider_detail(self, provider_code):
"""
Get more detailed information about a bank.
:param provider_code: Name of the provider, as returned in
:meth:`~prometeo.banking.client.BankingAPIClient.get_providers`
:type provider_code: str
:rtype: :class:`~prometeo.banking.models.ProviderDetail`
"""
data = self.call_api('GET', '/provider/{}/'.format(provider_code))
return ProviderDetail(**data['provider'])
[docs] def logout(self, session_key):
"""
Logs the user out and invalidates its session.
:param session_key: The session key.
:type session_key: str
"""
self.call_api('GET', '/logout/', params={
'key': session_key,
})
[docs] def preprocess_transfer(self, session_key, origin_account,
destination_institution, destination_account, currency,
amount, concept, destination_owner_name, branch):
"""
Preprocess transfer.
:param session_key: The session key.
:type session_key: str
:param origin_account: Account number from where it is transferred
:type origin_account: str
:param destination_institution: Id of the institution where it is transferred.
As provided by
:meth:`~prometeo.banking.client.BankingAPIClient.get_providers`
:type destination_institution: str
:param destination_account: Account number where it is transferred
:type destination_account: str
:param currency: Account currency in format ``ISO 4217``
:type currency: str
:param amount: Amount to transfer
:type amount: str
:param concept: Concept or description of the transfer
:type concept: str
:param destination_owner_name:
Name of the holder of the destination account (empty if not applicable)
:type destination_owner_name: str
:param branch:
Branch number of the destination account (empty if not applicable)
:type branch: str
:rtype: :class:`~prometeo.banking.models.PreprocessTransfer`
"""
data = self.call_api('POST', '/transfer/preprocess', params={
'key': session_key,
}, data={
'origin_account': origin_account,
'destination_institution': destination_institution,
'destination_account': destination_account,
'currency': currency,
'amount': amount,
'concept': concept,
'destination_owner_name': destination_owner_name,
'branch': branch,
})
return PreprocessTransfer(**data['result'])
[docs] def confirm_transfer(self, session_key, request_id, authorization_type,
authorization_data):
"""
Confirm transfer.
:param session_key: The session key.
:type session_key: str
:param request_id: Id of the request returned by the endpoint of
:meth:`~prometeo.banking.client.BankingAPIClient.preprocess_transfer`
:type request_id: str
:param authorization_type:
- ``cardCode`` Coordinates card
- ``pin`` Account personal pin
-
``otp`` One-time generated pin, sent by sms, email,
hard token or soft token.
- ``otp-api`` Hard token or soft token device, digitized by Prometeo
:type authorization_type: str
:param authorization_data: Verification value (pin number,
coordinates card response , etc) if there
are several values, they must be separated by commas.
:type authorization_data: str
:rtype: :class:`~prometeo.banking.models.ConfirmTransfer`
"""
data = self.call_api('POST', '/transfer/confirm', params={
'key': session_key,
}, data={
'request_id': request_id,
'authorization_type': authorization_type,
'authorization_data': authorization_data,
})
return ConfirmTransfer(**data['transfer'])
[docs] def list_transfer_institutions(self, session_key):
"""
List transfer institutions.
:param session_key: The session key.
:type session_key: str
:rtype: :class:`~prometeo.banking.models.TransferInstitution`
"""
data = self.call_api('GET', '/transfer/destinations', params={
'key': session_key,
})
return [
TransferInstitution(**institution) for institution in data['destinations']
]
[docs]class Account(object):
"""
A bank account, returned by :meth:`~prometeo.banking.client.Session.get_accounts`
"""
def __init__(self, client, session_key, account_data):
self._client = client
self._session_key = session_key
self.id = account_data.id
self.name = account_data.name
self.number = account_data.number
self.branch = account_data.branch
self.currency = account_data.currency
self.balance = account_data.balance
[docs] def get_movements(self, date_start, date_end):
"""
List an account's movements for a range of dates.
:param date_start: Start of the date range for movements.
:type date_start: :class:`~datetime.datetime`
:param date_end: End of the date range for movements.
:type date_end: :class:`~datetime.datetime`
:rtype: List of :class:`~prometeo.banking.models.Movement`
"""
return self._client.get_movements(
self._session_key, self.number, self.currency, date_start, date_end,
)
[docs]class CreditCard(object):
"""
A credit card, returned by :meth:`~prometeo.banking.client.Session.get_credit_cards`
"""
def __init__(self, client, session_key, card_data):
self._client = client
self._session_key = session_key
self.id = card_data.id
self.name = card_data.name
self.number = card_data.number
self.close_data = card_data.close_date
self.due_date = card_data.due_date
self.balance_local = card_data.balance_local
self.balance_dollar = card_data.balance_dollar
[docs] def get_movements(self, currency_code, date_start, date_end):
"""
List credit card's movements for a range of dates.
:param date_start: Start of the date range for movements.
:type date_start: :class:`~datetime.datetime`
:param date_end: End of the date range for movements.
:type date_end: :class:`~datetime.datetime`
:rtype: List of :class:`~prometeo.banking.models.Movement`
"""
return self._client.get_credit_card_movements(
self._session_key, self.number, currency_code, date_start, date_end,
)