]> git.ipfire.org Git - thirdparty/python-fints.git/commitdiff
Update docs
authorHenryk Plötz <henryk@ploetzli.ch>
Sun, 16 Sep 2018 11:52:30 +0000 (13:52 +0200)
committerRaphael Michel <mail@raphaelmichel.de>
Mon, 3 Dec 2018 18:35:22 +0000 (19:35 +0100)
docs/debits.rst
docs/reading.rst
docs/tans.rst
docs/transfers.rst
fints/client.py
fints/formals.py

index f59837fcea37b8ab42c67859533cecc50c1e4449..d49a7a4ce270004273518c485fa5a4a910c288e9 100644 (file)
@@ -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.
index c37982ff57de0c1d00aaa480631b31f783e6db7b..506c3f7e2dad3e65a7737960a21f2935aafb713e 100644 (file)
@@ -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 <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>`_.
@@ -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:
 
index 83a3602584a5a0c42e8b96b2e1b1fdbc1da00329..7b481d7ef404c385d8b3ca1bd913f31c55af5b20 100644 (file)
@@ -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)
index da28c5cb9a9bd7afc45a8dc30adbec515a634caa..d39a0c4363234f6f4662dd7d25b36b8d4e89754b 100644 (file)
@@ -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)
index 9aeac90ae8af7177bfc5af038bd71df1fcaed329..bb1b7170fb6911cac9cf0e28d4a042e46b904f01 100644 (file)
@@ -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,
index b13b5be0afb5f0f00ef2e0c2274112e4fc84c003..e20736763db60d8a31df7254a78a7979760785d3 100644 (file)
@@ -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")