Ejemplo n.º 1
0
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})"
        )
Ejemplo n.º 2
0
    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)
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
    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())
Ejemplo n.º 7
0
    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)
Ejemplo n.º 8
0
    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())
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
    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)
Ejemplo n.º 11
0
    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()
Ejemplo n.º 12
0
    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)
Ejemplo n.º 13
0
    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)
Ejemplo n.º 14
0
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)
Ejemplo n.º 15
0
    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
Ejemplo n.º 16
0
    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)
Ejemplo n.º 17
0
    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)
Ejemplo n.º 18
0
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)
Ejemplo n.º 19
0
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)
Ejemplo n.º 20
0
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)
Ejemplo n.º 21
0
 def __should_request_spending_money(self):
     return self.env == ApiEnvironmentType.SANDBOX \
            and float(BunqContext.user_context().primary_monetary_account.balance.value) <= self._ZERO_BALANCE
Ejemplo n.º 22
0
 def update_context(self):
     BunqContext.api_context().save(self.determine_bunq_conf_filename())
Ejemplo n.º 23
0
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)
Ejemplo n.º 24
0
 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)