]> git.ipfire.org Git - thirdparty/python-fints.git/commitdiff
Update docs psd2 95/head
authorRaphael Michel <mail@raphaelmichel.de>
Sun, 22 Dec 2019 17:59:55 +0000 (18:59 +0100)
committerRaphael Michel <mail@raphaelmichel.de>
Sun, 22 Dec 2019 17:59:55 +0000 (18:59 +0100)
docs/debits.rst
docs/index.rst
docs/quickstart.rst
docs/reading.rst
docs/tans.rst
docs/tested.rst
docs/transfers.rst
docs/trouble.rst [new file with mode: 0644]
docs/upgrading_1_2.rst
docs/upgrading_2_3.rst [new file with mode: 0644]

index 2038338f7d3bfcfea400dd26d60fa7cc51a62735..7829cea0ca42edef75bb6f1772bc638fd107f195 100644 (file)
@@ -44,18 +44,21 @@ You can easily generate XML using the ``sepaxml`` python library:
     pain_message = sepa.export().decode()
 
     client = FinTS3PinTanClient(...)
+    minimal_interactive_cli_bootstrap(client)
 
-    accounts = client.get_sepa_accounts()
-    account = accounts[0]
+    with client:
+        if client.init_tan_response:
+            print("A TAN is required", client.init_tan_response.challenge)
 
-    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()
+            if getattr(client.init_tan_response, 'challenge_hhduc', None):
+                try:
+                    terminal_flicker_unix(client.init_tan_response.challenge_hhduc)
+                except KeyboardInterrupt:
+                    pass
 
-        client.set_tan_medium(media[0])
+            tan = input('Please enter TAN:')
+            client.send_tan(client.init_tan_response, tan)
 
-    with client:
         res = client.sepa_debit(
             account=accounts[0],
             data=pain_message,
@@ -65,7 +68,7 @@ You can easily generate XML using the ``sepaxml`` python library:
         )
 
         if isinstance(res, NeedTANResponse):
-            print(res.challenge)
+            print("A TAN is required", res.challenge)
 
             if getattr(res, 'challenge_hhduc', None):
                 try:
index 1ace6bd7e76750bd14301ac4a596db518c53634e..8746371fcc1186b97fb72b7fada191189fd4f213 100644 (file)
@@ -20,7 +20,9 @@ Library user documentation content
    transfers
    debits
    tested
+   upgrading_2_3
    upgrading_1_2
+   trouble
 
 
 Library developer documentation content
index 966971c5dc0b4c88fceb25cc1ad4b3f986cb6248..cfd4912d8e6a4a5554389f75a2162242497c64d7 100644 (file)
@@ -24,12 +24,28 @@ of your bank. Logging in with a signature file or chip card is currently not sup
         product_id='Your product ID'
     )
 
+Since the implementation of PSD2, you will in almost all cases need to be ready to deal with TANs. For a quick start,
+we included a minimal command-line utility to help choose a TAN method:
 
-You can then execute commands using the client instance:
+.. code-block:: python
+
+    from fints.utils import minimal_interactive_cli_bootstrap
+    minimal_interactive_cli_bootstrap(f)
+
+You can then open up a real communication dialog to the bank with a ``with`` statement and issue commands:
+commands using the client instance:
 
 .. code-block:: python
 
-    accounts = f.get_sepa_accounts()
+    with f:
+        # Since PSD2, a TAN might be needed for dialog initialization. Let's check if there is one required
+        if f.init_tan_response:
+            print("A TAN is required", f.init_tan_response.challenge)
+            tan = input('Please enter TAN:')
+            f.send_tan(f.init_tan_response, tan)
+
+        # Fetch accounts
+        accounts = f.get_sepa_accounts()
 
 Go on to the next pages to find out what commands are supported!
 
index e9b889e22e4bf88b37202c3905944e7f5dc1e932..e34b91d69cd10095ffb7ca47508c4a7e8963a264 100644 (file)
@@ -1,6 +1,11 @@
 Reading operations
 ==================
 
+.. note::
+
+   Starting from version 3, **all of the methods on this page** can return a ``NeedTANResponse`` instead of actual
+   data if your bank requires a TAN. You should then enter a TAN, read our chapter :ref:`tans` to find out more.
+
 Fetching your bank accounts
 ---------------------------
 
index c45ef2f44e4389e581c6c439575b95734b37d8bb..d3fe8360cf1bfad9bebd4f220ff8c429e01755d7 100644 (file)
@@ -30,6 +30,9 @@ If the ``description_required`` attribute for the TAN mechanism is :attr:`~fints
 you will need to get a list of TAN media with :func:`~fints.client.FinTS3PinTanClient.get_tan_media` and select the
 appropriate one with :func:`~fints.client.FinTS3PinTanClient.set_tan_medium`.
 
+Have a look at the source code of :func:`~fints.utils.minimal_interactive_cli_bootstrap` for an example on how to
+ask the user for these properties.
+
 You may not change the active TAN mechanism or TAN medium within a standing dialog (see :ref:`client-dialog-state`).
 
 The selection of the active TAN mechanism/medium is stored with the persistent client data (see :ref:`client-state`).
index 215ff9f55a3dccff17dbb022f2ff2013e3b82918..3a53e7828b64873c463c45b3e3074018133a69d9 100644 (file)
@@ -1,30 +1,27 @@
 Tested banks
 ============
 
-The following banks have been tested with version 2.x of this library:
+The following banks have been tested with version 3.x of this library:
 
 ======================================== ============ ======== ======== ======
 Bank                                     Transactions Holdings Transfer Debits
                                          and Balance
 ======================================== ============ ======== ======== ======
-GLS Bank eG                              Yes                   Yes      Yes
 Postbank                                 Yes
-Triodos Bank                             Yes                   Yes
-Volksbank Darmstadt-Südhessen            Yes                   Yes
-Deutsche Skatbank                        Yes                   Yes
 BBBank eG                                Yes                   Yes
-MLP Banking AG                           Yes
+Sparkasse Heidelberg                     Yes
 ======================================== ============ ======== ======== ======
 
 Tested security functions
 -------------------------
 
+* ``921`` "pushTAN"
+* ``930`` "mobile TAN"
 * ``942`` "mobile TAN"
 * ``962`` "Smart-TAN plus manuell"
 * ``972`` "Smart-TAN plus optisch"
 
 
-
 Legacy results
 ---------------
 
@@ -48,3 +45,18 @@ Volksbank (Fiducia)                      Yes
 Wüstenrot                                Yes
 1822direkt                               Yes           Yes
 ======================================== ============  ======== ======== ======
+
+The following banks have been tested with the old version 2.x of this library:
+
+======================================== ============ ======== ======== ======
+Bank                                     Transactions Holdings Transfer Debits
+                                         and Balance
+======================================== ============ ======== ======== ======
+GLS Bank eG                              Yes                   Yes      Yes
+Postbank                                 Yes
+Triodos Bank                             Yes                   Yes
+Volksbank Darmstadt-Südhessen            Yes                   Yes
+Deutsche Skatbank                        Yes                   Yes
+BBBank eG                                Yes                   Yes
+MLP Banking AG                           Yes
+======================================== ============ ======== ======== ======
index 9e0447b8738b0fb851a6ab7a1f08110f03a83b81..28d6a0a683e7539e0a53ab0ebdbb07c242749ea4 100644 (file)
@@ -29,18 +29,21 @@ Full example
 .. code-block:: python
 
     client = FinTS3PinTanClient(...)
+    minimal_interactive_cli_bootstrap(client)
 
-    accounts = client.get_sepa_accounts()
-    account = accounts[0]
+    with client:
+        if client.init_tan_response:
+            print("A TAN is required", client.init_tan_response.challenge)
 
-    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()
+            if getattr(client.init_tan_response, 'challenge_hhduc', None):
+                try:
+                    terminal_flicker_unix(client.init_tan_response.challenge_hhduc)
+                except KeyboardInterrupt:
+                    pass
 
-        client.set_tan_medium(media[0])
+            tan = input('Please enter TAN:')
+            client.send_tan(client.init_tan_response, tan)
 
-    with client:
         res = client.simple_sepa_transfer(
             account=accounts[0],
             iban='DE12345',
@@ -53,7 +56,7 @@ Full example
         )
 
         if isinstance(res, NeedTANResponse):
-            print(res.challenge)
+            print("A TAN is required", res.challenge)
 
             if getattr(res, 'challenge_hhduc', None):
                 try:
diff --git a/docs/trouble.rst b/docs/trouble.rst
new file mode 100644 (file)
index 0000000..264123b
--- /dev/null
@@ -0,0 +1,173 @@
+Troubleshooting and bug reporting
+=================================
+
+The FinTS specification is long and complicated and in many parts leaves things open to interpretation -- or sometimes
+implementors interpret things differently even though they're not really open to interpretation. This is valid for us,
+but also for the banks. Making the library work with many diffrent banks is hard, and often impossible without access
+to a test account. Therefore, we ask you for patience when reporting issues with different banks -- and you need to be
+ready that we might not be able to help you because we do not have the time or bank account required to dig deeper.
+
+Therefore, if you run into trouble with this library, you first need to ask yourself a very important question: **Is it
+me or the library?** To answer this question for most cases, we have attached a script below, that we ask you to use
+to try the affected feature of the library in a well-documented way. Apart from changing the arguments (i.e. your bank's
+parameters and your credentials) at the top, we ask you **not to make any modifications**. Pasting this bit by bit into
+a Jupyter notebook **is a modification**. If your issue does not include information as to whether the script below works
+or does not work for your bank, **we will close your issue without further comment.**
+
+**If the script below does not work for you**, there is probably a compatibility issue between this library and your
+bank. Feel free to open an issue, but make sure the issue title includes the name of the bank and the text includes
+what operations specifically fail.
+
+**If the script below does work for you**, there is probably something wrong with your usage of the library or our
+documentation. Feel free to open an issue, but **include full working example code** that is necessary to reproduce
+the problem.
+
+.. note:: Before posting anything on GitHub, make sure it does not contain your username, PIN, IBAN, or similarly sensitive data.
+
+.. code-block:: python
+
+    import datetime
+    import getpass
+    import logging
+    import sys
+    from decimal import Decimal
+
+    from fints.client import FinTS3PinTanClient, NeedTANResponse, FinTSUnsupportedOperation
+    from fints.hhd.flicker import terminal_flicker_unix
+    from fints.utils import minimal_interactive_cli_bootstrap
+
+    logging.basicConfig(level=logging.DEBUG)
+
+    client_args = (
+        'REPLACEME',  # BLZ
+        'REPLACEME',  # USER
+        getpass.getpass('PIN: '),
+        'REPLACEME'  # ENDPOINT
+    )
+
+    f = FinTS3PinTanClient(*client_args)
+    minimal_interactive_cli_bootstrap(f)
+
+
+    def ask_for_tan(response):
+        print("A TAN is required")
+        print(response.challenge)
+        if getattr(response, 'challenge_hhduc', None):
+            try:
+                terminal_flicker_unix(response.challenge_hhduc)
+            except KeyboardInterrupt:
+                pass
+        tan = input('Please enter TAN:')
+        return f.send_tan(response, tan)
+
+
+    # Open the actual dialog
+    with f:
+        # Since PSD2, a TAN might be needed for dialog initialization. Let's check if there is one required
+        if f.init_tan_response:
+            ask_for_tan(f.init_tan_response)
+
+        # Fetch accounts
+        accounts = f.get_sepa_accounts()
+        if isinstance(accounts, NeedTANResponse):
+            accounts = ask_for_tan(accounts)
+        if len(accounts) == 1:
+            account = accounts[0]
+        else:
+            print("Multiple accounts available, choose one")
+            for i, mm in enumerate(accounts):
+                print(i, mm.iban)
+            choice = input("Choice: ").strip()
+            account = accounts[int(choice)]
+
+        # Test pausing and resuming the dialog
+        dialog_data = f.pause_dialog()
+
+    client_data = f.deconstruct(including_private=True)
+
+    f = FinTS3PinTanClient(*client_args, from_data=client_data)
+    with f.resume_dialog(dialog_data):
+        while True:
+            operations = [
+                "End dialog",
+                "Fetch transactions of the last 30 days",
+                "Fetch transactions of the last 120 days",
+                "Fetch transactions XML of the last 30 days",
+                "Fetch transactions XML of the last 120 days",
+                "Fetch information",
+                "Fetch balance",
+                "Fetch holdings",
+                "Fetch scheduled debits",
+                "Fetch status protocol",
+                "Make a simple transfer"
+            ]
+
+            print("Choose an operation")
+            for i, o in enumerate(operations):
+                print(i, o)
+            choice = int(input("Choice: ").strip())
+            try:
+                if choice == 0:
+                    break
+                elif choice == 1:
+                    res = f.get_transactions(account, datetime.date.today() - datetime.timedelta(days=30),
+                                             datetime.date.today())
+                    while isinstance(res, NeedTANResponse):
+                        res = ask_for_tan(res)
+                    print("Found", len(res), "transactions")
+                elif choice == 2:
+                    res = f.get_transactions(account, datetime.date.today() - datetime.timedelta(days=120),
+                                             datetime.date.today())
+                    while isinstance(res, NeedTANResponse):
+                        res = ask_for_tan(res)
+                    print("Found", len(res), "transactions")
+                elif choice == 3:
+                    res = f.get_transactions_xml(account, datetime.date.today() - datetime.timedelta(days=30),
+                                                 datetime.date.today())
+                    while isinstance(res, NeedTANResponse):
+                        res = ask_for_tan(res)
+                    print("Found", len(res[0]) + len(res[1]), "XML documents")
+                elif choice == 4:
+                    res = f.get_transactions_xml(account, datetime.date.today() - datetime.timedelta(days=120),
+                                                 datetime.date.today())
+                    while isinstance(res, NeedTANResponse):
+                        res = ask_for_tan(res)
+                    print("Found", len(res[0]) + len(res[1]), "XML documents")
+                elif choice == 5:
+                    print(f.get_information())
+                elif choice == 6:
+                    res = f.get_balance(account)
+                    while isinstance(res, NeedTANResponse):
+                        res = ask_for_tan(res)
+                    print(res)
+                elif choice == 7:
+                    res = f.get_holdings(account)
+                    while isinstance(res, NeedTANResponse):
+                        res = ask_for_tan(res)
+                    print(res)
+                elif choice == 8:
+                    res = f.get_scheduled_debits(account)
+                    while isinstance(res, NeedTANResponse):
+                        res = ask_for_tan(res)
+                    print(res)
+                elif choice == 9:
+                    res = f.get_status_protocol()
+                    while isinstance(res, NeedTANResponse):
+                        res = ask_for_tan(res)
+                    print(res)
+                elif choice == 10:
+                    res = f.simple_sepa_transfer(
+                        account=accounts[0],
+                        iban=input('Target IBAN:'),
+                        bic=input('Target BIC:'),
+                        amount=Decimal(input('Amount:')),
+                        recipient_name=input('Recipient name:'),
+                        account_name=input('Your name:'),
+                        reason=input('Reason:'),
+                        endtoend_id='NOTPROVIDED',
+                    )
+
+                    if isinstance(res, NeedTANResponse):
+                        ask_for_tan(res)
+            except FinTSUnsupportedOperation as e:
+                print("This operation is not supported by this bank:", e)
\ No newline at end of file
index d4f51b61b83cd39a28e9c11b42243c497a6517b6..6342a7343fe39a1b5a063a1cfa2246dff16de2a7 100644 (file)
@@ -1,5 +1,5 @@
-Upgrading from python-fints 1.x
-===============================
+Upgrading from python-fints 1.x to 2.x
+======================================
 
 This library has seen a major rewrite in version 2.0 and the API has changed in a lot of places. These are the most
 important changes to know:
diff --git a/docs/upgrading_2_3.rst b/docs/upgrading_2_3.rst
new file mode 100644 (file)
index 0000000..10d308a
--- /dev/null
@@ -0,0 +1,15 @@
+Upgrading from python-fints 2.x to 3.x
+======================================
+
+Release 3.0 of this library was made to adjust to changes made by the banks as part of their PSD2 implementation
+in 2019. Here's what you should know when porting your code:
+
+* A TAN can now be required for dialog initialization. In this case, ``client.init_tan_response`` will contain a
+  ``NeedTANResponse``.
+
+* Basically every method of the client class can now return a ``NeedTANResponse``, so you should always expect this
+  case and handle it gracefully.
+
+* Since everything can require a TAN, everything requires a standing dialog. Issuing interactive commands outside of a
+  ``with client:`` statement is now deprecated. It still might work in very few cases, so we didn't disable it, but we
+  do not support it any longer. This affects you mostly when you work with this on a Python REPL or e.g. in a Notebook.