def connect(plaid_credentials, bank, bank_credentials): client = Client(client_id=plaid_credentials.client_id, secret=plaid_credentials.secret) try: response = client.connect(bank, { 'username': bank_credentials.username, 'password': bank_credentials.password }) data = json.loads(response.content) if response.status_code == 200: print data if "access_token" in data: client_access_token = data["access_token"] client = Client(client_id=plaid_credentials.client_id, secret=plaid_credentials.secret, access_token=client_access_token) response = client.connect_get() data = json.loads(response.content) with open("tx.json", "w") as f: f.write(json.dumps(data)) else: print "Something went horribly wrong..." print data except plaid_errors.UnauthorizedError as e: # handle this print e
class Plaid: def __init__(self, clientId, clientSecret, accessToken=None): self.client = Client(client_id=clientId, secret=clientSecret, access_token=accessToken) def connect(self, accountType, username, password, email): connect = self.client.connect(account_type=accountType, username=username, password=password, email=email) if connect.ok: json_response = json.loads(connect.content) return json_response; def answerMFA(self, accountType, mfaAnswer): step = self.client.step(account_type=accountType, mfa=mfaAnswer) if step.ok: transactions = json.loads(step.content) return transactions def getTransactions(self, options=None): transactions = self.client.transactions(options) if transactions.ok: transactions = json.loads(transactions.content) return transactions def delete(self): self.client.delete_user()
def tool_bank(): user = g.user client = Client(client_id='test_id', secret='test_secret') connect = client.connect(account_type='wells', username='******', password='******', email='*****@*****.**') if connect.ok: step = client.step(account_type='wells', mfa='tomato') if step.ok: data = json.loads(step.content) transactions = data['transactions'] spent = { 'school': 0, 'essentials': 0, 'snacks': 0, 'gas': 0, 'clothing': 0, 'recreation': 0 } for t in transactions: if t['type']['primary'] in ['place', 'digital']: name = t['name'].lower() amount = t['amount'] category = analyze_name(name) spent[category] += amount pie = '[' for cat, val in spent.iteritems(): pie += '["%s",%d],' % (cat, val) pie = pie[:-1] pie += ']' return render_template('tool/bank.html', pie=pie) return render_template_string('Error')
def requestTransactions(self): if "token" not in self.credentials["plaid"]: client = Client(client_id=self.credentials["plaid"]["client"], secret=self.credentials["plaid"]["secret"]) data = "" try: response = client.connect(self.bank, { 'username': self.login, 'password': self.password }) data = response.json() except plaid_errors.UnauthorizedError as e: print("AHHH") print(e.message) exit() token = data['access_token'] self.credentials["plaid"]["token"] = token client = Client(client_id=self.credentials["plaid"]["client"], secret=self.credentials["plaid"]["secret"], access_token=self.credentials["plaid"]["token"]) if self.debug == True: options = {'pending': True, 'gte': '2017/01/01'} else: options = {'pending': True, 'gte': self.gte} response = client.connect_get(opts=options) self.data = response.json()
def test_connect_mfa_list(): client = Client('test_id', 'test_secret') # These credentials erroneously still force mfa in the sandbox # Should disambiguate by disallowing institution on API level # for this particular calling response = client.connect('chase', no_mfa_credentials) assert response.status_code == 201 assert to_json(response)['type'] == 'list'
def get_response(): client = Client(client_id='test_id', secret='test_secret', access_token='usertoken') client.config({'url':'https://tartan.plaid.com'}) account_type = 'ins_100088' user = '******' pwd = 'plaid_good' response = client.connect(account_type, { 'username': user, 'password': pwd }) response = client.connect_step(account_type, 'tomato') return response.json() # This is a dict
class PlaidData: client_id = '54137790d8675800005de8d2' secret = 'SKTzUuKXq2ZcxviiwW0fiJ' def __init__(self, username, password): self.client = Client(client_id=self.client_id, secret=self.secret) self.connect = self.client.connect(account_type='wells', username=username, password=password, email='*****@*****.**') def getJSONResponse(self): json_response = json.loads(self.connect.content) import pprint pprint.pprint(json_response) message = "Your last transaction was @ " + json_response['transactions'][0]['name'] Messaging.sendMessage(message, "+16508620978")
def test_connect(): with patch('requests.post') as mock_requests_post: mock_response = Mock() mock_response.content = '{}' mock_requests_post.return_value = mock_response client = Client('myclientid', 'mysecret') account_type = 'bofa' username = '******' password = '******' email = '*****@*****.**' response = client.connect(account_type, username, password, email) assert mock_response == response
def test_connection(self): client = Client( client_id='57f28ea8062e8c1d58ff9391', secret='580f72db7f49c97b4d279b504eda2d' ) try: response = client.connect(account_type, { 'username': '******', 'password': '******' }) self.assertEqual(response.json()['type'], 'questions') self.assertEqual(response.status_code, 201) response = client.connect_step(account_type, 'tomato') self.assertEqual(response.status_code, 200) response = client.connect_get() for account in response.json()["accounts"]: process_account_creation(account) for transaction in response.json()["transactions"]: process_transaction(transaction) print Account.objects.values_list('_id', 'pend_transfer') except plaid_errors.UnauthorizedError: traceback.print_exc()
def test_connect_mfa_question(): client = Client('test_id', 'test_secret') response = client.connect('bofa', no_mfa_credentials) assert response.status_code == 201 assert to_json(response)['type'] == 'questions'
def test_connect_no_mfa(): client = Client('test_id', 'test_secret') response = client.connect('amex', no_mfa_credentials) assert response.status_code == 200 assert to_json(response)['access_token'] == 'test_amex'
def test_ResourceNotFound_connect(): client = Client('test_id', 'test_secret') with pytest.raises(ResourceNotFoundError): client.connect('pnc', no_mfa_credentials)
class PlaidAccess(): def __init__(self, client_id=None, secret=None, account_id=None, access_token=None): if client_id and secret: self.client_id = client_id self.secret = secret else: self.client_id, self.secret = cm.get_plaid_config() self.client = Client(client_id=self.client_id, secret=self.secret) # if access_token: self.acceess_token = access_token # if account_id: self.account_id = account_id def get_transactions(self, access_token, account_id, start_date=None, end_date=None): """Get transaction for a given account for the given dates""" self.client.access_token = access_token options = {} options["account"] = account_id # options["pending"] = True #get all transaction up to and including today options['lte'] = datetime.date.today().strftime('%Y-%m-%d') self.connect_response = self.client.connect_get(options).json() return self.connect_response['transactions'] def add_account(self, nickname): try: self._get_available_institutions() except Exception as e: raise Exception( "There was a problem obtaining a list of institutions. Try again later." ) from e selected_institution = self._get_institution() if selected_institution is None: print("Quitting, no account added", file=sys.stdout) sys.exit(0) self.active_institution = self.available_institutions[ selected_institution][1] self.account_type = self.active_institution["type"] un, pwd, pin = self._get_needed_creds() # self.client.client_id = "test_id" # self.client.secret = "test_secret" # un = "plaid_test" # pwd = "plaid_good" login = {'username': un, 'password': pwd} if pin: login['pin'] = pin try: self.connect_response = self.client.connect( self.account_type, login) except plaid_errors.PlaidError as e: print(e.message) return else: cont = True try: while not self._check_status(): cont = self._process_mfa(self.connect_response.json()) if not cont: break except plaid_errors.PlaidError as e: print( "The following error has occurred, no account added:\n{}". format(e.message), file=sys.stderr) sys.exit(1) else: if not cont: print("Quitting, no account added", file=sys.stderr) sys.exit(1) accts = self.connect_response.json()['accounts'] if not self._present_accounts(accts): print("Quitting, no account added", file=sys.stderr) sys.exit(1) self._save_account_section(nickname) def _save_account_section(self, nickname): acct_section = OrderedDict() acct_section[nickname] = OrderedDict() section = acct_section[nickname] section['access_token'] = self.client.access_token section['account'] = self.selected_account['_id'] a = prompt( "What account is used for posting transactions [{}]: ".format( cm.CONFIG_DEFAULTS.posting_account)) if a: section['posting_account'] = a else: section['posting_account'] = cm.CONFIG_DEFAULTS.posting_account a = prompt( "What currency does this account use for posting transactions [{}]: " .format(cm.CONFIG_DEFAULTS.currency)) if a: section['currency'] = a else: section['currency'] = cm.CONFIG_DEFAULTS.currency a = self._present_file_options(nickname, 'mapping', cm.FILE_DEFAULTS.mapping_file) if a: section['mapping_file'] = a a = self._present_file_options(nickname, 'journal', cm.FILE_DEFAULTS.journal_file) if a: section['journal_file'] = a a = self._present_file_options(nickname, 'accounts', cm.FILE_DEFAULTS.accounts_file) if a: section['accounts_file'] = a a = self._present_file_options(nickname, 'template', cm.FILE_DEFAULTS.template_file) if a: section['template_file'] = a cm.write_section(acct_section) def _present_file_options(self, nickname, file_type, default_file): a = prompt( "Create a separate {} file configuration setting for this account [Y/n]: " .format(file_type), validator=YesNoValidator()).lower() if not bool(a) or a.startswith("y"): cont = True while cont: path = prompt( "Enter the path for the {} file used for this account [{}]: " .format(file_type, default_file), completer=PATH_COMPLETER) if path: path = os.path.expanduser(path) file_exists = os.path.isfile(path) if not file_exists: create = prompt( "{} does not exist. Do you want to create it? [Y/n]: " .format(path), validator=YesNoValidator()).lower() if not bool(create) or create.startswith("y"): if not os.path.exists(path): cm._create_directory_tree(path) cm._create_directory_tree(path) cm.touch(path) cont = False else: cont = True else: cont = False else: #use default create = prompt( "{} does not exist. Do you want to create it? [Y/n]: ". format(default_file), validator=YesNoValidator()).lower() if not bool(create) or create.startswith("y"): if not os.path.exists(default_file): cm._create_directory_tree(default_file) cm.touch(default_file) path = default_file cont = False else: cont = True if cont: print("Invalid path {}. Please enter a valid path.".format( path)) else: cont = False if not path: path = cm.get_custom_file_path(nickname, file_type, create_file=True) return path elif a.startswith("n"): return None def _process_mfa(self, data): type = data['type'] mfa = data['mfa'] if type == 'questions': return self._present_question([q['question'] for q in mfa]) elif type == 'list': return self._present_list(mfa) elif type == 'selection': return self._present_selection(mfa) else: raise Exception("Unknown mfa type from Plaid") def _present_question(self, question): clear_screen() print(question[0]) answer = prompt("Answer: ", validator=NullValidator( message="You must enter your answer", allow_quit=True)) if answer.lower() == "q": return False #we have abandoned ship self.connect_response = self.client.connect_step( self.account_type, answer) return True def _present_accounts(self, data): clear_screen() accounts = list(enumerate(data, start=1)) message = ["Which account do you want to add:\n"] for i, d in accounts: a = {} a['choice'] = str(i) a['name'] = d['meta']['name'] a['type'] = d['subtype'] if 'subtype' in d else d['type'] message.append("{choice:<2}. {name:<40} {type}\n".format(**a)) message.append("\nEnter your selection: ") answer = prompt( "".join(message), validator=NumberValidator( message="Enter the NUMBER of the account you want to add", allow_quit=True, max_number=len(accounts))) if answer.lower() == "q": return False #we have abandoned ship self.selected_account = accounts[int(answer) - 1][1] return True def _present_list(self, data): clear_screen() devices = list(enumerate(data, start=1)) message = ["Where do you want to send the verification code:\n"] for i, d in devices: message.append("{:<4}{}\n".format(i, d["mask"])) message.append("\nEnter your selection: ") answer = prompt( "".join(message), validator=NumberValidator( message= "Enter the NUMBER where you want to send verification code", allow_quit=True, max_number=len(devices))) if answer.lower() == "q": return False #we have abandoned ship dev = devices[int(answer) - 1][1] print("Code will be sent to: {}".format(dev["mask"])) self.connect_response = self.client.connect_step( self.account_type, None, options={'send_method': { 'type': dev['type'] }}) code = prompt("Enter the code you received: ", validator=NumberValidator()) self.connect_response = self.client.connect_step( self.account_type, code) return True def _present_selection(self, data): """ Could not test: needs implementation """ clear_screen() raise NotImplementedError( "MFA selections are not yet implemented, sorry.") def _get_needed_creds(self): credentials = self.active_institution["credentials"] clear_screen() print("Enter the required credentials for {}\n".format( self.active_institution["name"])) user_prompt = "Username ({}): ".format(credentials["username"]) pass_prompt = "Password ({}): ".format(credentials["password"]) pin = None username = prompt( user_prompt, validator=NullValidator(message="Please enter your username")) password = prompt( pass_prompt, is_password=True, validator=NullValidator(message="You must enter your password")) if "pin" in credentials: pin = prompt( "PIN: ", validator=NumLengthValidator( message="Pin must be at least {} characters long")) if not bool(pin) or pin.lower() == "q": pin = None #make sure we have something return username, password, pin def _get_available_institutions(self): # limit to just those institutions that have connect abilities, # as that is the service we will be using to get transactions institutions = self.client.institutions().json() self.available_institutions = list( enumerate([i for i in institutions if 'connect' in i['products']], start=1)) def _get_institution(self): accounts = [] total_institutions = len(self.available_institutions) for account in self.available_institutions: num, info = account accounts.append("{num:<4}{inst}".format(num=num, inst=info['name'])) institution_prompt = textwrap.dedent( """What bank is this account going to be for? \n{}\n\nEnter Number [q to quit]:""" .format('\n'.join(accounts))) clear_screen() res = prompt(institution_prompt, validator=NumberValidator( message="You must enter the chosen number", allow_quit=True, max_number=total_institutions)) if res.isdigit(): choice = int(res) - 1 elif res.lower() == "q": choice = None return choice def _check_status(self): status = self.connect_response.status_code if status == 200: # good, user connected return True elif status == 201: # MFA required return False else: return False
def initial_login(self, account_type, username, password, email): client = Client(client_id=self.client_id, secret=self.secret) connect = client.connect(account_type=account_type, username=username, password=password, email=email) json_response = json.loads(connect.content) return json_response
def answer_selections(selections): # We have magically inferred the answers # so we respond immediately # In the real world, we would present the selection # questions and choices to our user and submit their responses # in a JSON-encoded array with answers provided # in the same order as the given questions answer = json.dumps(['Yes', 'No']) return client.connect_step(account_type, answer) try: response = client.connect(account_type, { 'username': username, 'password': password }) except plaid_errors.PlaidError, e: pass else: if response.status_code == 200: # User connected data = json.loads(response.content) elif response.status_code == 201: # MFA required try: mfa_response = answer_mfa(json.loads(response.content)) except plaid_errors.PlaidError, e: pass else: # check for 200 vs 201 responses
class plaid(): account_type = 'chase' name = '' password = '' client = Client(client_id='57cccae6cfbf49f67b01fd5a', secret='2c13981884395fc691fda11148ed67') plaid_results = '' def __init__(self): print "created plaid object" Client.config({'url': 'https://tartan.plaid.com'}) def answer_mfa(data): print "answer_mfa" if data['type'] == 'questions': # Ask your user for the answer to the question[s]. # Although questions is a list, there is only ever a # single element in this list, at present return answer_question([q['question'] for q in data['mfa']]) elif data['type'] == 'list': return answer_list(data['mfa']) elif data['type'] == 'selection': return answer_selections(data['mfa']) else: raise Exception('Unknown mfa type from Plaid') def answer_question(questions): print "answer_question" # We have magically inferred the answer # so we respond immediately # In the real world, we would present questions[0] # to our user and submit their response answer = 'dogs' return client.connect_step(account_type, answer) def answer_list(devices): print "answer_list" print devices[1] # You should specify the device to which the passcode is sent. # The available devices are present in the devices list dog = client.connect_step( 'chase', None, options={'send_method': { 'mask': 'xxx-xxx-9793' }}) answer = raw_input("Please enter something: ") cat = client.connect_step(account_type, answer) print cat print cat.content # print dog # print type(dog) # new =dog.content # print new # print type(new) # token=new.access_token # print token return dog def answer_selections(selections): print "answer_selections" # We have magically inferred the answers # so we respond immediately # In the real world, we would present the selection # questions and choices to our user and submit their responses # in a JSON-encoded array with answers provided # in the same order as the given questions answer = json.dumps(['Yes', 'No']) return client.connect_step(account_type, answer) def use_token(self, username): obj = SQLConnection() token = obj.get_plaid_token(username) self.client = Client(client_id='57cccae6cfbf49f67b01fd5a', secret='2c13981884395fc691fda11148ed67', access_token=token) response = self.client.connect_get(token) self.plaid_results = response.content return response.content def create_user_plaid(self, username, account_type, bank_username, bank_password): try: response = self.client.connect(account_type, { 'username': bank_username, 'password': bank_password }) d = json.loads(response.content) access_token = d["access_token"] print access_token except plaid_errors.PlaidError, e: print e else:
from plaid import Client from plaid import errors as plaid_errors from plaid.utils import json id_plaid = '584f8ac239361943b6a40c2f' id_secret = '2eb94d1cfb298020b6d4c1e396eb75' bank_user = '******' bank_pass = '******' client = Client(client_id=id_plaid, secret=id_secret) account_type = 'bofa' try: response = client.connect(account_type, { 'username': bank_user, 'password': bank_pass }) except plaid_errors.PlaidError, e: pass else: if response.status_code == 200: # User connected data = resposnse.json() elif response.stat_code == 201: # MFA required try: mfa_response = answer_mfa(response.json()) except plaid_errors.PlaidError, e: pass else: None