def main(): config = load_config('config.json') timestamp = datetime.today().strftime('%d.%m.%Y - %H:%M') for entry in config['accounts_nr'].items(): #Mit Konto verbinden try: f = FinTS3PinTanClient(entry[1]['blz'], entry[1]['nr'], entry[1]['pass'], 'https://fints.ing-diba.de/fints/') except: #If connection not working wait a bit and try again. Better solution later... time.sleep(300) f = FinTS3PinTanClient(entry[1]['blz'], entry[1]['nr'], entry[1]['pass'], 'https://fints.ing-diba.de/fints/') #Konten abrufen accountList = f.get_sepa_accounts() for account in accountList: #Wenn das Konto ein Depot ist: if account.accountnumber in config['depots_nr']: accountholdings = f.get_holdings(account) #Für jede Position die aktuellen Werte abrufen for accountholding in accountholdings: save_db(timestamp, entry[1]['name'], accountholding.ISIN, accountholding.name, accountholding.market_value, accountholding.value_symbol, accountholding.pieces, accountholding.total_value, accountholding.acquisitionprice) print(timestamp) #Add Bitcoin holdings lastBTCPrice = 0 BTCAmount = float(config['BTCAmount'][0]) try: r = requests.get('https://api.coindesk.com/v1/bpi/currentprice.json', timeout=10) BTCPrice = json.loads(r.text)['bpi']['EUR']['rate_float'] lastBTCPrice = BTCPrice save_db(timestamp, 'Gemeinschaftskonto', 'BTC', 'BTC', BTCPrice, 'EUR', BTCAmount, BTCPrice * BTCAmount, 4400) except requests.exceptions.RequestException as e: save_db(timestamp, 'Gemeinschaftskonto', 'BTC', 'BTC', lastBTCPrice, 'EUR', BTCAmount, lastBTCPrice * BTCAmount, 4400) print(e)
def getBankTransactions(pw_dict): logging.basicConfig(level=logging.DEBUG) f = FinTS3PinTanClient( pw_dict['VB_BLZ'], # Your bank's BLZ pw_dict['VB_ALIAS'], pw_dict['VB_PW'], pw_dict[ 'VB_ENDP'] # endpoint, e.g.: https://hbci-pintan.gad.de/cgi-bin/hbciservlet ) accounts = f.get_sepa_accounts() print(accounts) statement = f.get_statement(accounts[0], date(2017, 10, 21), date.today()) for t in statement: amount = t.data['amount'].amount vb_date = t.data['date'] try: transaction_details = t.data['transaction_details'] except: print("Hat kein transaction_details") parseIban(amount, vb_date, transaction_details) # The statement is a list of transaction objects as parsed by the mt940 parser, see # https://mt940.readthedocs.io/en/latest/mt940.html#mt940.models.Transaction # for documentation. Most information is contained in a dict accessible via their # ``data`` property # for retrieving the holdings of an account: holdings = f.get_holdings(accounts[0])
def balance(): user = request.headers.get('user') pin = request.headers.get('pin') iban_num = request.args.get('iban') bank = request.args.get('bank').lower() if iban.is_valid(iban_num) is not True: abort(403) if not user: abort(403) if not pin: abort(403) if not bank: abort(403) url = api_bank_list[bank]['url'] blz = api_bank_list[bank]['blz'] bic = api_bank_list[bank]['bic'] f = FinTS3PinTanClient( blz, # Your bank's BLZ user, pin, url) account_balance = f.get_balance( SEPAAccount(iban_num, bic, iban_num[12:], '', blz)) result = {'balance': str(account_balance.amount.amount)} return jsonify(result)
def process_account(account, accounts_api, budget_id, earliest): fints = FinTS3PinTanClient( account.fints_blz, account.fints_username, account.fints_password, account.fints_endpoint, ) existing_ids = [] if account.account_type == AccountType.HOLDING: transactions = retrieve_holdings(account, fints) else: transactions = retrieve_transactions(account, fints, start_date=earliest, end_date=TODAY) try: existing_transactions = accounts_api.get_transactions( budget_id, since_date=earliest) existing_ids = [ t.import_id for t in existing_transactions.data.transactions if t.import_id ] except Exception: logger.warning("Could not retrieve existing transactions") return transactions, existing_ids
def fetch_bank_data(num: int): fints = FinTS3PinTanClient( BANK_CODE, BANK_USERNAME, BANK_PIN, BANK_URL, ) accounts_f = fints.get_sepa_accounts() for account_f in accounts_f: account = db.query(Account).filter( Account.iban == account_f.iban).first() if not account: account = Account(account_f.iban, account_f.bic) db.add(account) db.flush() balance_f = fints.get_balance(account_f) balance = Balance(balance_f.amount.amount, balance_f.amount.currency, account) db.add(balance) db.flush() # statements = fints.get_statement(account, date.today() - timedelta(days=30), date.today()) # pprint.pprint(balance) # for statement in statements: # pprint.pprint(statement.data) db.commit()
def get_transactions(bank_config): f = FinTS3PinTanClient(bank_config.blz, bank_config.login, bank_config.pin, bank_config.fints_endpoint, product_id='33D93BB1B017D422A87837C01') # minimal_interactive_cli_bootstrap(f) # # with f: # # Since PSD2, a TAN might be needed for dialog initialization. Let's check if there is one required # if f.init_tan_response: # print("A TAN is required", f.init_tan_response.challenge) # tan = input('Please enter TAN:') # f.send_tan(f.init_tan_response, tan) # # Fetch accounts accounts = f.get_sepa_accounts() # get transactions account = next(filter(lambda a: a.iban == bank_config.iban, accounts), None) transactions = f.get_transactions(account, date.today() - timedelta(days=10)) return list( map( lambda t: transform_fints_transaction( t.data, parse_paypal=bank_config.parse_paypal), transactions))
def lambda_handler(event, context): logging.basicConfig(level=logging.ERROR) f = FinTS3PinTanClient( os.environ['BANKING_BLZ'], # Your bank's BLZ os.environ['BANKING_USERNAME'], os.environ['BANKING_PIN'], os.environ['BANKING_ENDPOINT']) accounts = f.get_sepa_accounts() balancesAllAccounts = {} for account in accounts: # conversion to float is okay here because we're not going to do # floating point arithmetic with the values. balancesAllAccounts[account.iban] = float( f.get_balance(account).amount.amount) print( json.dumps({ 'event': "bookkeeper:balances:read", 'balances': balancesAllAccounts, 'created_at': datetime.datetime.utcnow().isoformat() + 'Z' # this is ridiculous but Python violates ISO 8601 })) return None
def test_consors(): """ Ausprobieren, was man von Consors ermitteln kann :return: """ #logging.basicConfig(level=logging.DEBUG) try: f = FinTS3PinTanClient( '76030080', # Your bank's BLZ '970715353', # Your login name getpass.getpass('PIN:'), # Your banking PIN "https://brokerage-hbci.consorsbank.de/hbci", #'https://hbci-pintan.gad.de/cgi-bin/hbciservlet', product_id=None) print(f) except Exception as ex: print(str(ex)) #minimal_interactive_cli_bootstrap( f ) #with f: # Since PSD2, a TAN might be needed for dialog initialization. Let's check if there is one required if f.init_tan_response: print("A TAN is required", f.init_tan_response.challenge) tan = input('Please enter TAN:') f.send_tan(f.init_tan_response, tan) # Fetch accounts accounts = f.get_sepa_accounts() print(accounts)
def fints_client(fints_server): return FinTS3PinTanClient( '12345678', 'test1', '1234', fints_server )
def create_fints_client(): settings = pyramid.threadlocal.get_current_registry().settings return FinTS3PinTanClient(settings.get('banking.blz'), settings.get('banking.kto'), settings.get('banking.pin'), settings.get('banking.url'), mode=FinTSClientMode.INTERACTIVE, product_id=settings.get('banking.product_id'))
def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the sensor platform.""" from fints.client import FinTS3PinTanClient client = FinTS3PinTanClient(str(config['blz']), str(config['username']), str(config['pin']), str(config['endpoint'])) add_devices([ FintsSensor(client, account) for account in client.get_sepa_accounts() ])
def fints_client(self, fints_login, form=None): fints_user_login, _ignore = fints_login.user_login.get_or_create(user=self.request.user) if form: fints_user_login.login_name = form.cleaned_data['login_name'] if form.cleaned_data['pin'] == PIN_CACHED_SENTINEL: pin = self.request.securebox[_cache_label(fints_login)] else: pin = form.cleaned_data['pin'] else: pin = None client = FinTS3PinTanClient( fints_login.blz, fints_user_login.login_name, pin, fints_login.fints_url, product_id="F41CDA6B1F8E0DADA0DDA29FD", from_data=fints_user_login.fints_client_data, mode=FinTSClientMode.INTERACTIVE, ) client.add_response_callback(self.fints_callback) # FIXME HACK HACK HACK The python-fints API with regards to TAN media is not very useful yet # Circumvent it here if fints_user_login.selected_tan_medium: fake_tan_medium = TANMedia5(tan_medium_name = fints_user_login.selected_tan_medium) client.set_tan_medium(fake_tan_medium) try: yield client pin_correct = True except FinTSClientPINError: # PIN wrong, clear cached PIN, indicate error self.request.securebox.delete_value(_cache_label(fints_login)) if form: form.add_error(None, _("Can't establish FinTS dialog: Username/PIN wrong?")) pin_correct = False if pin_correct: client.set_tan_medium(None) # FIXME HACK HACK HACK fints_user_login.fints_client_data = client.deconstruct(including_private=True) fints_user_login.save(update_fields=['login_name', 'fints_client_data']) if form: if form.cleaned_data['store_pin'] == '1': storage = Storage.TRANSIENT_ONLY elif form.cleaned_data['store_pin'] == '2': storage = Storage.PERMANENT_ONLY else: storage = None if storage: if form.cleaned_data['pin'] != PIN_CACHED_SENTINEL: self.request.securebox.store_value(_cache_label(fints_login), pin, storage=storage) else: self.request.securebox.delete_value(_cache_label(fints_login))
def __init__(self, blz, account_number, pin, endpoint_url): self.blz = blz self.account_number = account_number self.pin = pin self.endpoint_url = endpoint_url logging.basicConfig(level=logging.ERROR) self.f = FinTS3PinTanClient(self.blz, self.account_number, self.pin, self.endpoint_url) self.accounts = self.f.get_sepa_accounts()
def get_client(config): login = config["fints"] client = FinTS3PinTanClient( login["blz"], login["user"], login["pin"], login["url"] ) return client
def client(self): """Get the client object. As the fints library is stateless, there is not benefit in caching the client objects. If that ever changes, consider caching the client object and also think about potential concurrency problems. """ from fints.client import FinTS3PinTanClient return FinTS3PinTanClient(self._credentials.blz, self._credentials.login, self._credentials.pin, self._credentials.url)
def GetTransactions(blz, username, password, api, acc): # Init fints Class fints = FinTS3PinTanClient( blz, username, password, api ) accounts = fints.get_sepa_accounts() account = next(account for account in accounts if account.accountnumber == acc) transactions = fints.get_transactions(account, startDate, endDate) return transactions
def test_account(iban, url, login, password): client = FinTS3PinTanClient(iban.bank_code, login, password, url.geturl()) # find account we are looking for sepa_accounts = client.get_sepa_accounts() try: sepa_account = [a for a in sepa_accounts if a.iban == iban][0] except IndexError: # remote sepa accounts don't include requested one logger.exception("Account with iban %s not found" % iban)
def check(self): """ Fetches the last transactions from the account and checks if they are unknown. """ fints_client = FinTS3PinTanClient(self.blz, self.user, self.password, self.url) accounts = fints_client.get_sepa_accounts() # Extract account. account = None for temp_account in accounts: if temp_account.iban.upper() == self.iban: account = temp_account break if account is None: raise ValueError("Account '%s' with IBAN '%s' not found." % (self.name, self.iban)) # Get all transaction from the last 5 days. start_date = datetime.date.today() - datetime.timedelta( days=self.delta_days) transactions = fints_client.get_transactions(account, start_date, datetime.date.today()) for transaction in transactions: currency = str(transaction.data["currency"]) amount = float(transaction.data["amount"].amount) date = transaction.data["date"] if not isinstance(date, datetime.date): raise ValueError("Account '%s' with IBAN '%s' contains " % (self.name, self.iban) + "illegal data") subject = str(transaction.data["purpose"]) rcpt_name = str(transaction.data["applicant_name"]) rcpt_iban = str(transaction.data["applicant_iban"]) obj = SimpleTransaction(rcpt_name, rcpt_iban, amount, currency, date, subject) if obj in self.triggered_transactions: continue if not self.rules.check_allowed(obj): logging.debug( "Transaction '%s' not whitelisted. Triggering event." % str(obj)) # Trigger events. for event in self.events: event.trigger(self, obj) self.triggered_transactions.add(obj)
def bank_accounts_import(): form = BankAccountActivitiesImportForm() form.account.choices = [(acc.id, acc.name) for acc in BankAccount.q.all()] (transactions, old_transactions) = ([], []) if form.validate_on_submit(): # login with fints bank_account = BankAccount.q.get(form.account.data) process = True try: fints = FinTS3PinTanClient(bank_account.routing_number, form.user.data, form.pin.data, bank_account.fints_endpoint) acc = next((a for a in fints.get_sepa_accounts() if a.iban == bank_account.iban), None) if acc is None: raise KeyError('BankAccount with IBAN {} not found.'.format( bank_account.iban)) start_date = map_or_default(bank_account.last_updated_at, datetime.date, date(2018, 1, 1)) statement = fints.get_statement(acc, start_date, date.today()) flash("Transaktionen vom {} bis {}.".format( start_date, date.today())) except FinTSDialogError: flash(u"Ungültige FinTS-Logindaten.", 'error') process = False except KeyError: flash( u'Das gewünschte Konto kann mit diesem Online-Banking-Zugang\ nicht erreicht werden.', 'error') process = False if process: (transactions, old_transactions) = finance.process_transactions( bank_account, statement) else: (transactions, old_transactions) = ([], []) if process and form.do_import.data is True: # save transactions to database session.add_all(transactions) session.commit() flash(u'Bankkontobewegungen wurden importiert.') return redirect( url_for(".accounts_show", account_id=bank_account.account_id)) return render_template('finance/bank_accounts_import.html', form=form, transactions=transactions, old_transactions=old_transactions)
def test_fints(config): click.echo('Creating FinTS client...') f = FinTS3PinTanClient( config['fints']['blz'], config['fints']['username'], get_pin(config), config['fints']['endpoint'], ) click.echo('Fetching SEPA account list...') accounts = f.get_sepa_accounts() click.echo('Looking for correct SEPA account...') accounts_matching = [ a for a in accounts if a.iban == config['fints']['iban'] ] if not accounts_matching: click.echo( click.style('The specified SEPA account %s could not be found.' % config['fints']['iban'], fg='red')) click.echo('Only the following SEPA accounts were detected:') click.echo(', '.join([a.iban for a in accounts])) sys.exit(1) elif len(accounts_matching) > 1: click.echo( click.style( 'Multiple SEPA accounts match the given IBAN. We currently can not handle this ' 'situation.', fg='red')) click.echo('Only the following SEPA accounts were detected:') click.echo(', '.join([a.iban for a in accounts])) sys.exit(1) account = accounts_matching[0] click.echo(click.style('Found matching SEPA account.', fg='green')) click.echo('Fetching statement of the last 30 days...') statement = f.get_statement(account, date.today() - timedelta(days=30), date.today()) if statement: click.echo( click.style('Found %d transactions. The last one is:' % len(statement), fg='green')) pprint.pprint(statement[-1].data) else: click.echo( 'No recent transaction found. Please check if this is correct.')
def main(): print("Start BalanceFetcher ", datetime.datetime.now()) print("==============================================") logging.basicConfig() get_conf() loginAccounts = get_accounts() for login in loginAccounts: f = FinTS3PinTanClient(login['bankcode'], login['banklogin'], login['bankpin'], login['bankurl']) userid = get_userid(f) accounts = f.get_sepa_accounts() for account in accounts: get_transactions(account, f, userid) balance = get_balance(account, f) save_balance(balance, account, userid) print("==============================================")
def client(self): """Get the client object. As the fints library is stateless, there is not benefit in caching the client objects. If that ever changes, consider caching the client object and also think about potential concurrency problems. Note: As of version 2, the fints library is not stateless anymore. This should be considered when reworking this integration. """ return FinTS3PinTanClient( self._credentials.blz, self._credentials.login, self._credentials.pin, self._credentials.url, )
def test_pin_locked(fints_server): client = FinTS3PinTanClient( '12345678', 'test1', '3938', fints_server, ) with pytest.raises(FinTSClientTemporaryAuthError): with client: pass assert client.pin.blocked with pytest.raises(Exception): with client: pass with pytest.raises(Exception, match="Refusing"): str(client.pin)
def test_pin_wrong(fints_server): client = FinTS3PinTanClient( '12345678', 'test1', '99999', fints_server, ) with pytest.raises(FinTSClientPINError): with client: pass assert client.pin.blocked with pytest.raises(Exception): with client: pass with pytest.raises(Exception, match="Refusing"): str(client.pin)
def test_resume(fints_client, fints_server): with fints_client: system_id = fints_client.system_id dialog_id = fints_client._standing_dialog.dialog_id assert fints_client.bpd_version == 78 d_data = fints_client.pause_dialog() c_data = fints_client.deconstruct(including_private=True) FinTS3PinTanClient('12345678', 'test1', '1234', fints_server, from_data=c_data) assert system_id == fints_client.system_id with fints_client.resume_dialog(d_data): assert dialog_id == fints_client._standing_dialog.dialog_id assert fints_client.bpd_version == 78
def __init_fints_connection(self): """Private: Initialise new fints connection. :return: None """ self.interactive.show_progress_realtime(_("Initialise connection"), 10, reload=False) try: if not hasattr(self, 'fints_connection'): password = self.fints_login.get_password('fints_password') self.fints_connection = FinTS3PinTanClient( self.fints_login.blz, self.fints_login.fints_login, password, self.fints_login.fints_url, mode=FinTSClientMode.INTERACTIVE) except Exception as e: frappe.throw( _("Could not conntect to fints server with error<br>{0}"). format(e))
def retrieveAndSave(self): client = FinTS3PinTanClient( self.config["fints"]["blz"], # Your bank's BLZ self.config["fints"]["account"], # your account number self.config["fints"]["password"], # e.g. 'https://fints.ing-diba.de/fints/' self.config["fints"]["endpoint"]) retriever = TRetriever(client, self.config["fints"]["selectedAccount"]) converter = CsvConverter(self.config["fints"]["csv_separator"]) csv_output = "\n".join( map( lambda transaction: converter.convert(transaction), retriever.get_hbci_transactions(self.config["fints"]["start"], Date.today()))) with open(self.config["files"]["csv_file"], 'w') as f: f.write(converter.get_headline()) f.write("\n") f.write(csv_output)
def api_transactions(): user = request.headers.get('user') pin = request.headers.get('pin') iban_num = request.args.get('iban') bank = request.args.get('bank').lower() if iban.is_valid(iban_num) is not True: abort(403) if not user: abort(403) if not pin: abort(403) if not bank: abort(403) url = api_bank_list[bank]['url'] blz = api_bank_list[bank]['blz'] bic = api_bank_list[bank]['bic'] f = FinTS3PinTanClient( blz, # Your bank's BLZ user, pin, url) today = date.today() first_day = today.replace(day=1) transaction = f.get_transactions( SEPAAccount(iban_num, bic, iban_num[12:], '', blz), first_day, today) result = [] for transaction in reversed(transaction): result.append({ 'date': transaction.data['date'], 'amount': str(transaction.data['amount'].amount), 'applicant_name': transaction.data['applicant_name'], 'purpose': transaction.data['purpose'], 'posting_text': transaction.data['posting_text'] }) f.deconstruct() return jsonify(result)
def import_fin_ts(): logging.basicConfig(level=logging.WARN) f = FinTS3PinTanClient(os.environ.get('CSA_ACCOUNT_BLZ'), os.environ.get('CSA_ACCOUNT_USERNAME'), os.environ.get('CSA_ACCOUNT_PASSWORD'), 'https://hbci-pintan.gad.de/cgi-bin/hbciservlet', mode=FinTSClientMode.INTERACTIVE, product_id=os.environ.get('CSA_HBCI_PRODUCT_ID', None)) f.fetch_tan_mechanisms() try: with f: m = f.get_tan_media() f.set_tan_medium(m[1][0]) except FinTSUnsupportedOperation: print("TAN Mediums not supported.") with f: if f.init_tan_response: print(f.init_tan_response.challenge) tan = input('Please enter TAN:') f.send_tan(f.init_tan_response, tan) res = get_new_transactions(f) while isinstance(res, NeedTANResponse): print(res.challenge) tan = input('Please enter TAN:') res = f.send_tan(res, tan) print("No more challenges to complete") for transaction in res: save_transaction(transaction)
def upload_transactions(config, days=30, ignore=None): ignore = ignore or [] ignore_patterns = [] for i in ignore: try: ignore_patterns.append(re.compile(i)) except re.error as e: click.echo( click.style('Not a valid regular expression: %s' % i, fg='red')) click.echo( click.style('"%s" at position %d' % (e.msg, e.pos), fg='red')) click.echo('Creating FinTS client...') f = FinTS3PinTanClient( config['fints']['blz'], config['fints']['username'], get_pin(config), config['fints']['endpoint'], ) click.echo('Fetching SEPA account list...') accounts = f.get_sepa_accounts() click.echo('Looking for correct SEPA account...') accounts_matching = [ a for a in accounts if a.iban == config['fints']['iban'] ] if not accounts_matching: click.echo( click.style('The specified SEPA account %s could not be found.' % config['fints']['iban'], fg='red')) click.echo('Only the following SEPA accounts were detected:') click.echo(', '.join([a.iban for a in accounts])) sys.exit(1) elif len(accounts_matching) > 1: click.echo( click.style( 'Multiple SEPA accounts match the given IBAN. We currently can not handle this ' 'situation.', fg='red')) click.echo('Only the following SEPA accounts were detected:') click.echo(', '.join([a.iban for a in accounts])) sys.exit(1) account = accounts_matching[0] click.echo(click.style('Found matching SEPA account.', fg='green')) click.echo('Fetching statement of the last %d days...' % days) statement = f.get_statement(account, date.today() - timedelta(days=days), date.today()) if statement: click.echo( click.style('Found %d transactions.' % len(statement), fg='green')) click.echo('Parsing...') transactions = [] ignored = 0 for transaction in statement: reference = ' '.join( transaction.data.get(t) for t in ('posting_text', 'purpose', 'bank_reference', 'customer_reference') if transaction.data.get(t)) payer = { 'name': transaction.data.get('applicant_name', ''), 'iban': transaction.data.get('applicant_iban', ''), } eref = transaction.data.get('end_to_end_reference', '') ignore = False for i in ignore_patterns: if i.search(reference): ignore = True ignored += 1 break if not ignore: transactions.append({ 'amount': str(transaction.data['amount'].amount), 'reference': reference + (' EREF: {}'.format(eref) if eref else ''), 'payer': ((payer.get('name') or '') + ' - ' + (payer.get('iban') or '')).strip(), 'date': transaction.data['date'].isoformat(), }) if ignored > 0: click.echo( click.style('Ignored %d transactions.' % ignored, fg='blue')) payload = {'event': None, 'transactions': transactions} click.echo('Uploading...') try: r = requests.post(get_endpoint(config), headers={ 'Authorization': 'Token {}'.format(config['pretix']['key']) }, json=payload) if r.status_code == 201: click.echo(click.style('Job uploaded.', fg='green')) else: click.echo( click.style('Invalid response code: %d' % r.status_code, fg='red')) click.echo(r.text) sys.exit(2) except (RequestException, OSError) as e: click.echo(click.style('Connection error: %s' % str(e), fg='red')) sys.exit(2) except ValueError as e: click.echo( click.style('Could not read response: %s' % str(e), fg='red')) sys.exit(2) else: click.echo('No recent transaction found.')