def list_accounts(): """ List all shared accounts (id, name, balance) """ # Ensure the whole account is included print("\nLoading Bunq API environment...") env = ApiEnvironmentType.PRODUCTION # Authenticate session & load context print("Authenticating and loading context...") api_context = ApiContext(ApiEnvironmentType.PRODUCTION, API_KEY, socket.gethostname()) api_context.ensure_session_active() BunqContext.load_api_context(api_context) # Get user info print("Loading user info...\n") user = endpoint.User.get().value.get_referenced_object() # Fetch account detials accounts = endpoint.MonetaryAccountJoint.list().value for account in accounts: print( f"[{account.id_}] {account.description} (\N{euro sign}{account.balance.value})" )
def authenticate(self): if self.parameter_manager.exists("/bunq/api_context"): self.api_context = self._get_api_context_from_aws() self._ensure_active_session() else: self.api_context = self._create_api_context() BunqContext.load_api_context(self.api_context)
def test_bad_request_with_response_id(self): """ """ BunqContext.load_api_context(self._get_api_context()) with self.assertRaises(ApiException) as caught_exception: MonetaryAccountBank.get(self._INVALID_MONETARY_ACCOUNT_ID) self.assertIsNotNone(caught_exception.exception.response_id)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Bunq Bank sensor platform.""" from bunq.sdk.context import ( ApiContext, ApiEnvironmentType, BunqContext) from bunq.sdk.exception import BunqException, ApiException from bunq.sdk.model.generated import endpoint from bunq.sdk.json import converter # set environment type api_environment = ApiEnvironmentType.SANDBOX \ if config.get(CONF_SANDBOX) else ApiEnvironmentType.PRODUCTION accs = [] try: # create the api context variable bunq_context = ApiContext( api_environment, config.get(CONF_API_KEY), 'Home Assistant' ) # ensure the key is active, or activate bunq_context.ensure_session_active() # load user context from api context (not IO) # checks if the user has active accounts # raises BunqException otherwise BunqContext.load_api_context(bunq_context) # call the account list endpoint accounts = converter.serialize( endpoint.MonetaryAccount.list().value) # create and add the devices to the list accs = [ BunqAccountSensor( acc['MonetaryAccountBank']['description'], acc['MonetaryAccountBank']['id'], acc['MonetaryAccountBank']['currency'], float(acc['MonetaryAccountBank']['balance']['value'])) for acc in accounts ] async_add_entities(accs) # create the refresh object data = BunqData(hass, bunq_context, accs) # schedule the first update await data.schedule_update(INTERVAL_TO_NEXT_REFRESH) except ApiException as err: # if there is something wrong with the user setup # such as a incorrect key or invalid IP address # log the error and raise HA error # nothing to setup further until the key is changed _LOGGER.error(err) except BunqException as err: # if the Bunq sdk errors out there is # such as API rate limit throtteling # log the error and raise PlatformNotReady to retry _LOGGER.error(err) raise PlatformNotReady
def _register_user(self): api_context = context.ApiContext( self._get_environment(), Config.get_option('API_KEY'), Config.get_option('DEVICE_DESCRIPTION'), [Config.get_option('PERMITTED_IP')]) api_context.save(Config.get_option('API_CONTEXT_FILE_PATH')) BunqContext.load_api_context(api_context) self._pin_certificate()
def setUpClass(cls): cls._PAYMENT_LISTING_PAGE_SIZE = 2 cls._PAYMENT_REQUIRED_COUNT_MINIMUM = cls._PAYMENT_LISTING_PAGE_SIZE * 2 cls._NUMBER_ZERO = 0 cls._PAYMENT_AMOUNT_EUR = '0.01' cls._PAYMENT_CURRENCY = 'EUR' cls._PAYMENT_DESCRIPTION = 'Python test Payment' BunqContext.load_api_context(cls._get_api_context())
def _setup_context(self): if not self._user_is_registered(): self._register_user() api_context = ApiContext.restore( Config.get_option('API_CONTEXT_FILE_PATH')) api_context.ensure_session_active() api_context.save(Config.get_option('API_CONTEXT_FILE_PATH')) BunqContext.load_api_context(api_context)
def test_auto_bunq_context_update(self): """ Tests the auto update of BunqContext. """ api_context: ApiContext = BunqContext.api_context() api_context_json: object = json.loads(api_context.to_json()) api_context_json[self.__FIELD_SESSION_CONTEXT][ self.__FIELD_EXPIRE_TIME] = self.__TIME_STAMP_IN_PAST expired_api_context = ApiContext.from_json(json.dumps(api_context_json)) self.assertNotEqual(api_context.session_context.expiry_time, expired_api_context.session_context.expiry_time) self.assertEqual(BunqContext.api_context().session_context.expiry_time, api_context.session_context.expiry_time) BunqContext.update_api_context(expired_api_context) BunqContext.user_context().refresh_user_context() self.assertNotEqual( BunqContext.api_context().session_context.expiry_time, api_context.session_context.expiry_time ) self.assertFalse(BunqContext.api_context().ensure_session_active())
def fetch_transactions(days): # Ensure the whole account is included print("\nLoading Bunq API environment...") env = ApiEnvironmentType.PRODUCTION # Authenticate session & load context print("Authenticating and loading context...") api_context = ApiContext(ApiEnvironmentType.PRODUCTION, API_KEY, socket.gethostname()) api_context.ensure_session_active() BunqContext.load_api_context(api_context) # Get user info print("Loading user info...") user = endpoint.User.get().value.get_referenced_object() # Fetch account detials account = endpoint.MonetaryAccountJoint.get(ACCOUNT_ID).value description = account.description balance = account.balance.value print( f"\nLoaded account '{description}' with current balance of \N{euro sign}{balance}" ) start_date = datetime.now() - timedelta(days) transactions = [] for transaction in iterate_transactions(ACCOUNT_ID): transaction_dict = { "timestamp": transaction.created, "amount": transaction.amount.value, "description": transaction.description.replace("\r", "").replace("\n", " "), "counterparty": transaction.counterparty_alias.label_monetary_account.display_name, } # Still in range if dateparse.parse(transaction.created) >= start_date: transactions.append(transaction_dict) else: break print(transactions)
def setup_context(self): if isfile(self.determine_bunq_conf_filename()): pass # Config is already present elif self.env == ApiEnvironmentType.SANDBOX: sandbox_user = self.generate_new_sandbox_user() ApiContext(ApiEnvironmentType.SANDBOX, sandbox_user.api_key, socket.gethostname()).save( self.determine_bunq_conf_filename()) else: raise BunqException(self._ERROR_COULD_NOT_DETIRMINE_CONF) api_context = ApiContext.restore(self.determine_bunq_conf_filename()) api_context.ensure_session_active() api_context.save(self.determine_bunq_conf_filename()) BunqContext.load_api_context(api_context)
def setup_context(self): if Path(self.determine_bunq_conf_filename()).is_file(): pass else: sandbox_user = self.generate_new_sandbox_user() ApiContext( ApiEnvironmentType.SANDBOX, sandbox_user.api_key, socket.gethostname() ).save(self.determine_bunq_conf_filename()) self._api_context = ApiContext.restore(self.determine_bunq_conf_filename()) self._api_context.ensure_session_active() self._api_context.save(self.determine_bunq_conf_filename()) BunqContext.load_api_context(self._api_context) self._user_context = BunqContext.user_context()
def test_session_delete(self): """ Tests the deletion and resetting of the current active session This test has no assertion as of its testing to see if the code runs without errors. Notes ----- time.sleep() is needed as of you can only make 1 POST call to Session endpoint per second. """ Session.delete(self._SESSION_ID) time.sleep(2) BunqContext.api_context().reset_session() BunqContext.api_context().save(self._BUNQ_CONFIG_FILE)
def _request(self, method, uri_relative, request_bytes, params, custom_headers): """ :type method: str :type uri_relative: str :type request_bytes: bytes :type params: dict[str, str] :type custom_headers: dict[str, str] :return: BunqResponseRaw """ uri_relative_with_params = self._append_params_to_uri(uri_relative, params) if uri_relative not in self._URIS_NOT_REQUIRING_ACTIVE_SESSION: if self._api_context.ensure_session_active(): from bunq.sdk.context import BunqContext BunqContext.update_api_context(self._api_context) all_headers = self._get_all_headers( method, uri_relative_with_params, request_bytes, custom_headers ) response = requests.request( method, self._get_uri_full(uri_relative_with_params), data=request_bytes, headers=all_headers, proxies={self._FIELD_PROXY_HTTPS: self._api_context.proxy_url}, ) self._assert_response_success(response) if self._api_context.installation_context is not None: security.validate_response( self._api_context.installation_context.public_key_server, response.status_code, response.content, response.headers ) return self._create_bunq_response_raw(response)
def main(): parser = argparse.ArgumentParser() parser.add_argument(OPTION_API_KEY) all_option = parser.parse_args() if all_option.api_key is None: raise BunqException(ERROR_OPTION_MISSING_API_KEY) api_context = ApiContext(ApiEnvironmentType.PRODUCTION, all_option.api_key, '*') BunqContext.load_api_context(api_context) end = 50 for i in range(0, end): endpoint.Payment.create(amount=Amount('0.01', 'EUR'), counterparty_alias=Pointer('IBAN', '', ''), description=str(round(i / end * 100, 2)) + " Prozent von deinem Geld", monetary_account_id=) time.sleep(0.33333)
def setup_context(self, reset_config_if_needed=True): if isfile(self.determine_bunq_conf_filename()): pass # Config is already present elif self.env == ApiEnvironmentType.SANDBOX: sandbox_user = self.generate_new_sandbox_user() ApiContext(ApiEnvironmentType.SANDBOX, sandbox_user.api_key, socket.gethostname()).save( self.determine_bunq_conf_filename()) else: raise BunqException(self._ERROR_COULD_NOT_DETERMINE_CONF) try: api_context = ApiContext.restore( self.determine_bunq_conf_filename()) api_context.ensure_session_active() api_context.save(self.determine_bunq_conf_filename()) BunqContext.load_api_context(api_context) except ForbiddenException as forbidden_exception: if reset_config_if_needed: self.__handle_forbidden_exception(forbidden_exception) else: raise forbidden_exception
def test_create_and_update_tab(self): """ Tests the creation of a Tab, adds a tab item to it and updates this tab This test has no assertion as of its testing to see if the code runs without errors """ if BunqContext.user_context().is_only_user_person_set(): return unittest.skip( self._ERROR_ONLY_USER_COMPANY_CAN_CREATE_TAB ) tab_uuid = TabUsageSingle.create(self._get_cash_register_id(), self._TAB_DESCRIPTION, self._STATUS_OPEN, Amount(self._AMOUNT_EUR, self._CURRENCY)).value self._add_item_to_tab(tab_uuid) self._update_tab(tab_uuid)
def test_order_debit_card(self): """ Tests ordering a new card and checks if the fields we have entered are indeed correct by retrieving the card from the card endpoint and checks this date against the data we have submitted """ second_line = self.second_line_random pin_code_assignment = CardPinAssignment( self._PIN_CODE_ASSIGNMENT_TYPE_PRIMARY, self._CARD_PIN_CODE, BunqContext.user_context().primary_monetary_account.id_ ) card_debit = CardDebit.create(second_line, self.card_name_allowed, self.alias_first, self._CARD_TYPE_MAESTRO, [pin_code_assignment]).value card = Card.get(card_debit.id_).value self.assertEqual(self.card_name_allowed, card.name_on_card) self.assertEqual(second_line, card.second_line) self.assertEqual(card_debit.created, card.created)
def all_transactions(dt=None): # This should be enough to ensure the whole account is included. if dt == None: dt = epoch env = ApiEnvironmentType.PRODUCTION if not isfile('bunq-production.conf'): raise Exception("No config file found, run start.py first.") # Reload the API context api_context = ApiContext.restore('bunq-production.conf') api_context.ensure_session_active() api_context.save('bunq-production.conf') BunqContext.load_api_context(api_context) # User info user = endpoint.User.get().value.get_referenced_object() # To get a list we want a pagination object. # When making a pagination object yourself you normally only set the 'count' # Then you get the url params from it using 'url_params_count_only' pagination = Pagination() pagination.count = 100 accounts = [] all_monetary_account_bank = endpoint.MonetaryAccountBank.list( pagination.url_params_count_only).value for monetary_account_bank in all_monetary_account_bank: if monetary_account_bank.status == "ACTIVE": accounts.append(monetary_account_bank) all_monetary_account_savings = endpoint.MonetaryAccountSavings.list( pagination.url_params_count_only).value for monetary_account_savings in all_monetary_account_savings: if monetary_account_savings.status == "ACTIVE": accounts.append(monetary_account_savings) # Reload where we where last time. try: with open("seen.pickle", "rb") as fp: seen = pickle.load(fp) except Exception: seen = set() # We will keep a list of transactions that are already processed in this set. # The transactions will contain: # - A set of the two possible roundings of the datestamp # - The ammount of money in absolute value # - The description # - A set containing the two accounts involved # The goal here is that this representation is the same for two accounts when shifting money arround. for a in accounts: aid = a.id_ # keep track of where we are print(a.description) for p in iter_payments(aid): # python can handle the dates we get back date = dateparse.parse(p.created) #round to the second to get a (sort of) unique, but not to precise timestamp since_epoch = int(unix_time(date)) row = [ p.created, p.amount.value, p.description.replace("\r", "").replace("\n", " "), p.alias.label_monetary_account.iban, p.counterparty_alias.label_monetary_account.iban ] # frozenset can be used to hash a set, so the order does not matter. summary = ( unique_float( since_epoch), #take both so there is norounding problem abs(float(p.amount.value)), p.description, frozenset([ p.alias.label_monetary_account.iban, p.counterparty_alias.label_monetary_account.iban ])) # Still in range if date >= dt: if summary in seen: continue else: seen.add(summary) yield (row) else: break with open("seen.pickle", "wb") as fp: pickle.dump(seen, fp)
from bunq.sdk.context import ApiContext, BunqContext with open('bunq.conf', 'r') as content_file: content = content_file.read() api = ApiContext.from_json(content) BunqContext.load_api_context(api) print(BunqContext.user_context().primary_monetary_account.balance.value)
from bunq.sdk.context import ApiContext, BunqContext with open('bunq.conf', 'r') as content_file: content = content_file.read() api = ApiContext.from_json(content) BunqContext.load_api_context(api) print(BunqContext.user_context().user_person.alias[0].value) print(BunqContext.user_context().user_person.alias[1].value)
def __should_request_spending_money(self): return self.env == ApiEnvironmentType.SANDBOX \ and float(BunqContext.user_context().primary_monetary_account.balance.value) <= self._ZERO_BALANCE
def update_context(self): BunqContext.api_context().save(self.determine_bunq_conf_filename())
from bunq.sdk.context import ApiContext, BunqContext from bunq.sdk.context import ApiEnvironmentType import socket apiContext = ApiContext( ApiEnvironmentType.SANDBOX, "sandbox_ae9afa8798dc521804bd1d8900167457243b9fd323e6799a9d42d75d", socket.gethostname()) apiContext.save("bunq.conf") BunqContext.load_api_context(apiContext)
def setup_context(self, api_key): api_context = ApiContext(ApiEnvironmentType.SANDBOX, api_key, socket.gethostname()) api_context.ensure_session_active() BunqContext.load_api_context(api_context)