From: Henryk Plötz Date: Sun, 16 Sep 2018 11:52:30 +0000 (+0200) Subject: Update docs X-Git-Tag: v2.0.0~1^2~32 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=679ac209a6eb13c309bc8d364d5c6eae67410b47;p=thirdparty%2Fpython-fints.git Update docs --- diff --git a/docs/debits.rst b/docs/debits.rst index f59837f..d49a7a4 100644 --- a/docs/debits.rst +++ b/docs/debits.rst @@ -1,10 +1,10 @@ Creating SEPA debits ==================== -You can submit a SEPA debit XML file to the bank with the ``start_sepa_debit`` method: +You can submit a SEPA debit XML file to the bank with the ``sepa_debit`` method: .. autoclass:: fints.client.FinTS3Client - :members: start_sepa_debit + :members: sepa_debit :noindex: You should then enter a TAN, read our chapter :ref:`tans` to find out more. diff --git a/docs/reading.rst b/docs/reading.rst index c37982f..506c3f7 100644 --- a/docs/reading.rst +++ b/docs/reading.rst @@ -34,22 +34,22 @@ Fetching account balances 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 `_. -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 `_. @@ -63,6 +63,7 @@ You can fetch the holdings of an account with the ``get_holdings`` method: .. autoclass:: fints.client.FinTS3Client :noindex: :members: get_holdings + :noindex: This method will return a list of ``Holding`` objects: diff --git a/docs/tans.rst b/docs/tans.rst index 83a3602..7b481d7 100644 --- a/docs/tans.rst +++ b/docs/tans.rst @@ -6,35 +6,82 @@ Working with TANs 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 @@ -48,14 +95,15 @@ with the TAN: 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: @@ -63,3 +111,47 @@ 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) diff --git a/docs/transfers.rst b/docs/transfers.rst index da28c5c..d39a0c4 100644 --- a/docs/transfers.rst +++ b/docs/transfers.rst @@ -7,7 +7,8 @@ Simple mode 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. @@ -17,24 +18,27 @@ Advanced mode 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', @@ -43,15 +47,20 @@ Example 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) diff --git a/fints/client.py b/fints/client.py index 9aeac90..bb1b717 100644 --- a/fints/client.py +++ b/fints/client.py @@ -778,7 +778,7 @@ class FinTS3Client: Caller SHOULD ensure that the dialog is resumed (and properly ended) within a reasonable amount of time. :Example: - + :: client = FinTS3PinTanClient(..., set_data=None) @@ -817,6 +817,11 @@ class FinTS3Client: 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 @@ -838,7 +843,11 @@ class NeedTANResponse(NeedRetryResponse): 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, diff --git a/fints/formals.py b/fints/formals.py index b13b5be..e207367 100644 --- a/fints/formals.py +++ b/fints/formals.py @@ -187,6 +187,7 @@ class TaskHashAlgorithm(RepresentableEnum): class TwoStepParametersCommon(DataElementGroup): @property def VERSION(self): + "TAN mechanism version" return int( re.match(r'^(\D+)(\d+)$', self.__class__.__name__).group(2) ) security_function = DataElementField(type='code', max_length=3, _d="Sicherheitsfunktion kodiert")