You can fetch the current balance of an account with the ``get_balance`` operation.
.. autoclass:: fints.client.FinTS3Client
- :noindex:
- :members: get_balance
+ :members: get_balance
+ :noindex:
This method will return a list of ``Balance`` objects from the ``mt-940`` library. You can find more information
in `their documentation <https://mt940.readthedocs.io/en/latest/mt940.html#mt940.models.Balance>`_.
-Reading account statements
---------------------------
+Reading account transactions
+----------------------------
-You can fetch the banking statement of an account within a certain timeframe with the ``get_statement``
+You can fetch the banking statement of an account within a certain timeframe with the ``get_transactions``
operation.
.. autoclass:: fints.client.FinTS3Client
+ :members: get_transactions
:noindex:
- :members: get_statement
This method will return a list of ``Transaction`` objects from the ``mt-940`` library. You can find more information
in `their documentation <https://mt940.readthedocs.io/en/latest/mt940.html#mt940.models.Transaction>`_.
.. autoclass:: fints.client.FinTS3Client
:noindex:
:members: get_holdings
+ :noindex:
This method will return a list of ``Holding`` objects:
TAN methods
-----------
-Before doing any operations involving TANs, you should get a list of supported TAN methods and select the TAN method
-you want to use:
+Before doing any operations involving TANs, you should get a list of supported TAN mechanisms:
.. code-block:: python
- methods = client.get_tan_methods()
- method = methods[0]
+ mechanisms = client.get_tan_mechanisms()
-The returned values have a subtype ``fints.models.TANMethod``, with varying parameters depending on the version
-used by the bank:
+The returned dictionary maps identifiers (generally: three-digit numerals) to instances of a
+:func:`~fints.formals.TwoStepParametersCommon` subclass with varying fields, depending on the
+version of the two-step process and the bank.
-.. warning:: If the ``description_required`` attribute is ``2``, you will need to get the description of the TAN medium
- you want to use and pass it as ``tan_description`` to some operations. You can send a request for this
- information with the ``client.get_tan_description()`` method call. Currently, this returns an unparsed
- response from the bank. In the future, we will probably return a structured result here.
+.. autoclass:: fints.formals.TwoStepParameters3
+ :noindex:
+ :undoc-members:
+ :members:
+ :inherited-members:
+ :member-order: bysource
+ :exclude-members: is_unset, naive_parse, print_nested
+
+.. autoclass:: fints.formals.TwoStepParameters5
+ :noindex:
+ :undoc-members:
+ :members:
+ :inherited-members:
+ :member-order: bysource
+ :exclude-members: is_unset, naive_parse, print_nested
+
+The `name` field provides a user-friendly name of the TAN mechanism that you can display to the user
+to choose from.
+
+To select a TAN mechanism/query the currently selected TAN mechanism use the appropriate functions which
+take/return the identifier used as key in the `get_tan_mechanisms` return value.
+
+.. autoclass:: fints.client.FinTS3PinTanClient
+ :members: get_tan_mechanisms, set_tan_mechanism, get_current_tan_mechanism
+ :noindex:
+ :undoc-members:
+
+.. warning:: If the ``description_required`` attribute for the TAN mechanism is :attr:`~fints.formals.DescriptionRequired.MUST`,
+ you will need to get a list of TAN media with `get_tan_media()` and select the appropriate
+ one with `set_tan_medium()`.
+
+## FIXME The TAN media stuff is probably wrongly documented (and badly tested)
+
+.. autoclass:: fints.client.FinTS3PinTanClient
+ :members: get_tan_media, set_tan_medium
+ :noindex:
+ :undoc-members:
+
+
+You may not change the active TAN mechanism or TAN medium within a standing dialog. (See XXX)
+The selection of the active TAN mechanism/medium is stored with the persistent client data. (See XXX)
TAN challenges
--------------
-You should then pass the chosen ``TANMethod`` object to your operation, e.g. ``start_simple_sepa_transfer``.
-If a TAN is required, this operation will return a ``TANChallenge``, again depending on the version used by the bank.
+When you try to perform an operation that requires a TAN to proceed, you will receive an object containing
+the bank's challenge (and some internal data to continue the operation once the TAN has been processed):
+
+.. autoclass:: fints.client.NeedTANResponse
+ :undoc-members:
+ :members:
The ``challenge`` attribute will contain human-readable instructions on how to proceed.
+The ``challenge_html`` attribute will possibly contain a nicer, formatted, HTML version of the challenge text
+that you should prefer if your primary interface can render HTML. The contents are guaranteed to be proper and
+clean (by using the `bleach` library): They can be used with `mark_safe` in Django.
+
+The ``challenge_hhduc`` attribute will contain the challenge to be used with a TAN generator device using the
+Hand Held Device Unidirectional Coupling specification (such as a Flicker-Code).
+
Flicker-Code / optiTAN
----------------------
If you want to use chipTAN with an optical TAN device, we provide utilities to print the flicker code on
-a unix terminal. Just pass the ``result.challenge_hhd_uc`` value to this method:
+a unix terminal. Just pass the ``challenge_hhd_uc`` value to this method:
.. autofunction:: fints.hhd.flicker.terminal_flicker_unix
except KeyboardInterrupt:
pass
+
Sending the TAN
---------------
Once obtained the TAN, you can send it with the ``send_tan`` client method:
-.. autoclass:: fints.client.FinTS3Client
- :noindex:
+.. autoclass:: fints.client.FinTS3PinTanClient
:members: send_tan
+ :noindex:
For example:
tan = input('Bitte die TAN eingeben.')
result = client.send_tan(result, tan)
+
+
+Storing and restoring TAN state
+-------------------------------
+
+The :func:`~fints.client.NeedTANResponse.get_data` method and
+:func:`~fints.client.NeedRetryResponse.from_data` factory method can be used to store and restore
+a TAN state object between steps.
+
+.. autoclass:: fints.client.NeedRetryResponse
+ :undoc-members:
+ :members: from_data
+
+You SHOULD use this facility together with the client and dialog state restoration facilities:
+
+
+.. code-block:: python
+ :caption: First step
+
+ client = FinTS3PinTanClient(...)
+ with client:
+ response = client.sepa_transfer(...)
+
+ dialog_data = client.pause_dialog()
+ client_data = client.get_data()
+ tan_data = response.get_data()
+
+.. code-block:: python
+ :caption: Second step
+
+ tan_request = NeedRetryResponse.from_data(tan_data)
+ print("TAN request: {}".format(tan_request.challenge))
+ tan = input('Enter TAN')
+
+.. code-block:: python
+ :caption: Third step
+
+ tan_request = NeedRetryResponse.from_data(tan_data)
+ client = FinTS3PinTanClient(..., set_data=client_data)
+ with client.resume_dialog(dialog_data):
+ response = client.send_tan(tan_request, tan)
+
+ print(response.status)
+ print(response.responses)
You can create a simple SEPA transfer using this convenient client method:
.. autoclass:: fints.client.FinTS3Client
- :members: start_simple_sepa_transfer
+ :members: simple_sepa_transfer
+ :noindex:
You should then enter a TAN, read our chapter :ref:`tans` to find out more.
If you want to use advanced methods, you can supply your own SEPA XML:
.. autoclass:: fints.client.FinTS3Client
- :members: start_sepa_transfer
+ :members: sepa_transfer
+ :noindex:
Example
-------
.. code-block:: python
- client = FinTS3PinTanClient(…)
+ client = FinTS3PinTanClient(...)
- accounts = f.get_sepa_accounts()
+ accounts = client.get_sepa_accounts()
account = accounts[0]
- methods = f.get_tan_methods()
- method = methods[0]
- assert method.description_required != '2'
+ mechanisms = client.get_tan_mechanisms()
+ mechanism = mechanisms[client.get_current_tan_mechanism()]
+ if mechanism.description_required == fints.formals.DescriptionRequired.MUST:
+ usage_option, media = client.get_tan_media()
- tan_desc = ''
- res = f.start_simple_sepa_transfer(
+ client.set_tan_medium(media[0])
+
+ res = client.simple_sepa_transfer(
account=accounts[0],
iban='DE12345',
bic='BIC12345',
account_name='Test',
reason='Birthday gift',
endtoend_id='NOTPROVIDED',
- tan_method=method
)
- print(res.challenge)
- if getattr(res, challenge_hhd_uc, None):
- try:
- terminal_flicker_unix(res.challenge_hhd_uc)
- except KeyboardInterrupt:
- pass
+ if isinstance(res, NeedTANResponse):
+ print(res.challenge)
+
+ if getattr(res, challenge_hhduc, None):
+ try:
+ terminal_flicker_unix(res.challenge_hhduc)
+ except KeyboardInterrupt:
+ pass
+
+ tan = input('Please enter TAN:')
+ res = client.send_tan(res, tan)
+
+ print(res.status)
+ print(res.responses)
- tan = input('Please enter TAN:')
- res = f.send_tan(res, tan)
Caller SHOULD ensure that the dialog is resumed (and properly ended) within a reasonable amount of time.
:Example:
-
+
::
client = FinTS3PinTanClient(..., set_data=None)
self._standing_dialog = None
class NeedTANResponse(NeedRetryResponse):
+ challenge_raw = None #: Raw challenge as received by the bank
+ challenge = None #: Textual challenge to be displayed to the user
+ challenge_html = None #: HTML-safe challenge text, possibly with formatting
+ challenge_hhduc = None #: HHD_UC challenge to be transmitted to the TAN generator
+
def __init__(self, command_seg, tan_request, resume_method=None, tan_request_structured=False):
self.command_seg = command_seg
self.tan_request = tan_request
raise Exception("Wrong blob data version")
- def get_data(self):
+ def get_data(self) -> bytes:
+ """Return a compressed datablob representing this object.
+
+ To restore the object, use :func:`fints.client.NeedRetryResponse.from_data`.
+ """
data = {
"_class_name": self.__class__.__name__,
"version": 1,