def create_access_token(django_user, public_token): client = PlaidClient(client_id=client_id, secret=secret) resp = client.exchange_token(public_token) if resp.status_code != 200: logger.error( "Unable to exchange public token for access token for user {0}". format(django_user)) return None # else "client.access_token" should now be populated with # a valid access_token for making authenticated requests # Get the plaid user for this django user; make one if nec. if not getattr(django_user, 'plaid_user', False): plaid_user = PlaidUser(user=django_user) plaid_user.save() plaid_user = django_user.plaid_user plaid_user.access_token = client.access_token plaid_user.save() return True
def test_exchange(): client = Client('test_id', 'test_secret') response = client.exchange_token('test,chase,connected') assert response.status_code == 200 assert to_json(response)['access_token'] == 'test_chase' assert client.access_token == 'test_chase'
class PlaidClient: """ A wrapper class around the Plaid API client. """ def __init__(self, access_token=None): """ Initializes an instance. NOTE: we don't use the Lazy pattern here because the Plaid Client caches access tokens between method calls. """ self._client = Client( client_id=current_app.config['PLAID_CLIENT_ID'], secret=current_app.config['PLAID_SECRET'], access_token=access_token ) self._client.config({ 'url': current_app.config['PLAID_URL'] }) def get_transactions( self, start: datetime.datetime=datetime.datetime.utcnow() - datetime.timedelta(days=30), end: datetime.datetime=datetime.datetime.utcnow(), pending=False, account_id=None ): """ Retrieves transactions from the institution specified by the stored access token. Args: start: The start date for the transaction history set. Default one month before today. end: The end date for the transaction history set. Default today. pending: Whether or not to fetch pending transactions. account_id: If not None, will fetch transactions only from this account id. Returns: TODO, a dictionary for now. Raises: TODO """ options = { 'pending': pending, 'gte': start.isoformat(), 'end': end.isoformat(), 'account': account_id } response = self._client.connect_get(options) return self._process_transactions(response) def delete_user(self): """ Deletes the user associated with this client from Plaid's cache. They will have to log in again next time. """ response = self._client.connect_delete() if not response.ok: raise exceptions.BadRequest('TODO: SOMETHING F****D UP') def exchange_token(self, public_token): """ Exchanges the public_token returned by the Plaid Link module for a complete Plaid access token. """ response = self._client.exchange_token(public_token) if not response.ok: raise exceptions.BadRequest('TODO: SOMETHING F****D UP') return response.json()['access_token'] def _process_transactions(self, response): """ Processes a response with data containing transactions from Plaid to be in a specific format. Args: response: A response from plaid.Client. Returns: TODO Raises: TODO """ if not response.ok: raise exceptions.BadRequest('TODO: SOMETHING F****D UP') response_json = response.json() transactions = [ { '_id': transaction['_id'], '_account': transaction['_account'], 'date': self._process_transaction_date(transaction['date']), 'amount': transaction['amount'], 'name': self._process_transaction_name(transaction['name']), 'pending': transaction['pending'] } for transaction in response_json['transactions'] ] return transactions @staticmethod def _process_transaction_name(name): """ Prettify the names of transactions. TODO: Should we do this at all, or send whatever we get from Plaid? """ return titlecase(name) @staticmethod def _process_transaction_date(date): """Converts `date` from YYYY-MM-DD to epoch time.""" return datetime.datetime.strptime(date, '%Y-%m-%d')
class PlaidAPI(): client_id = '554e702a0effc0670b8c9454' public_key = '341f751f231458d8f651e072dd3184' secret = '09a1032a267857c25718ff0fdc102b' env = 'tartan' if (False): client_id = 'test_id' secret = 'test_secret' def exchangeLinkTokenForAccount(self, user, public_token): Client.config({ 'url': 'https://tartan.plaid.com' }) print('creating client') self.client = Client(client_id=PlaidAPI.client_id, secret=PlaidAPI.secret) print('getting response') response = self.client.exchange_token(public_token) print('response') print(response) access_token = self.client.access_token print("Access token {0}".format(access_token)) ##create account and store in db plaiduser = PlaidUserToken() # is there a good constructor syntax? plaiduser.user = user plaiduser.access_token = access_token plaiduser.status = 'new' plaiduser.status_text = "Created {0}".format(timezone.now()) plaiduser.save() print('saved') return plaiduser def getTransactions(self, usertoken): # retrive transactions from a plaid usertoken # @todo: need to specifiy retrieval since day X ago self.client = Client(client_id=PlaidAPI.client_id, secret=PlaidAPI.secret, access_token=usertoken.access_token) response = self.client.connect_get() print('parsing response') json_data = json.loads(response.content.decode('utf-8')) ##load in the accounts json_accounts = json_data['accounts'] for a in json_accounts: account = PlaidAccount.objects.filter(_id=a['_id'].encode('utf-8')) if account: account=account.get() created='updated' else: account = PlaidAccount() created='created' account.usertoken = usertoken account._id = a['_id'].encode('utf-8') account._item = a['_item'].encode('utf-8') account._user = a['_user'].encode('utf-8') account.name = a['meta']['name'].encode('utf-8') account.type = a['type'] account.institution_type = a['institution_type'].encode('utf-8') account.save() #tmp_id = unicodedata.normalize('NFKD', account._id).encode('ascii','ignore') #tmp_name = line = unicodedata.normalize('NFKD', account.name).encode('ascii','ignore') print("{0} account: {1} id: {2}".format(created,account.name, account._id) ) ##Load in the accounts json_transactions = json_data['transactions'] new_transaction_list=[] for t in json_transactions: transaction = PlaidTransaction.objects.filter(_id=t['_id'].encode('utf-8')) if transaction: transaction=transaction.get() created='updated' else: transaction = PlaidTransaction() created='created' transaction.state=PlaidTransaction.STATE_NEW new_transaction_list.append(transaction) transaction.usertoken = usertoken transaction._id = t['_id'].encode('utf-8') transaction._account = t['_account'].encode('utf-8') transaction.account=PlaidAccount.objects.filter(_id=t['_account'].encode('utf-8')).get() transaction.amount = t['amount'] transaction.name = t['name'].encode('utf-8') transaction.date = t['date'] transaction.type = t['type'] if 'category' in t: transaction.category = t['category'] if 'meta' in t: transaction.meta = t['meta'] transaction.meta_score = t['score'] transaction.save() print("{0} tx: {1} {2} ".format(created,transaction.date,transaction.name) ) # update the user token usertoken.status_text = "Synced on {0}. Retrieved {1} accounts and {2} transactions ({3} new)".format(timezone.now(),len(json_accounts),len(json_transactions),len(new_transaction_list)) usertoken.last_sync = timezone.now() usertoken.save() return new_transaction_list