python-fints icon indicating copy to clipboard operation
python-fints copied to clipboard

Fix TAN request with response code 3955 during initialization of the dialog

Open meyerj opened this issue 1 year ago • 8 comments

This patch fixed a similar issue as in #155 for me with the "Sparkasse Vorderpfalz": Since a few days a push TAN is required even to request an account balance or get transactions of the last view days. Before, this was working with only the PIN.

I tracked the issue down to two issues by debugging the exchanged messages as described in the documentation, for which I propose a fix here.

  • The string "DUMMY" sent as the tan_medium_name was somehow rejected by the bank. Even the call to client.get_tan_media() in minimal_interactive_cli_bootstrap() already triggered the error as described in #121, with the first response being

    [...]
                      fints.segments.dialog.HIRMS2( # Rückmeldungen zu Segmenten
                          header = fints.formals.SegmentHeader('HIRMS', 4, 2, 4), # Segmentkopf
                          responses = [ # Rückmeldung
                                      fints.formals.Response( # Rückmeldung
                                          code = '3920',
                                          reference_element = None,
                                          text = 'Zugelassene Zwei-Schritt-Verfahren für den Benutzer.',
                                          parameters = [
                                                  '923',
                                              ],
                                      ),
                                      fints.formals.Response( # Rückmeldung
                                          code = '9955',
                                          reference_element = None,
                                          text = 'Auftrag nicht ausgeführt - Die Gerätebezeichnung ist unbekannt. (MBV07390100255)',
                                      ),
                              ],
                      ),
    [...]
    

    followed by

    [...]
                      fints.segments.dialog.HIRMG2( # Rückmeldungen zur Gesamtnachricht
                          header = fints.formals.SegmentHeader('HIRMG', 3, 2), # Segmentkopf
                          responses = [ # Rückmeldung
                                      fints.formals.Response( # Rückmeldung
                                          code = '9050',
                                          reference_element = None,
                                          text = 'Die Nachricht enthält Fehler.',
                                      ),
                                      fints.formals.Response( # Rückmeldung
                                          code = '9800',
                                          reference_element = None,
                                          text = 'Dialog abgebrochen',
                                      ),
                                      fints.formals.Response( # Rückmeldung
                                          code = '9010',
                                          reference_element = None,
                                          text = 'Die angegebene Bankreferenz/Dialog-ID ist nicht gültig.',
                                      ),
                              ],
                      ),
                      fints.segments.dialog.HIRMS2( # Rückmeldungen zu Segmenten
                          header = fints.formals.SegmentHeader('HIRMS', 4, 2, 3), # Segmentkopf
                          responses = [ # Rückmeldung
                                      fints.formals.Response( # Rückmeldung
                                          code = '9010',
                                          reference_element = None,
                                          text = 'Auftrag wegen genereller Fehler in Auftragsnachricht nicht verarbeitet.',
                                      ),
                              ],
                      ),
    [...]
    

    after the subsequent call to client.get_sepa_accounts() (HKSPA1), likely because the dialog was never actually accepted by the server. Should a 9xxx response code already trigger an exception in FinTSDialog.init()?

    Sending an empty tan_medium_name works and triggers the response in the next bullet point. So I set client.selected_tan_medium = '' before calling minimal_interactive_cli_bootstrap(client) to already skip the call to get_tan_media() (with my patch). I guess this only works because there is only one for my account. I tried different strings, the name of my device and the UUID shown in the Push Tan app in "Verbindungen", nothing worked. And even get_tan_media() requires a TAN when the dialog starts, so it is a kind of chicken-and-egg problem and I have not found a way to not add the HKTAN7 segment to the dialog initialization request triggered by get_tan_media().

  • With the empty tan_medium_name the bank server responds with a TAN request and code 3955. That was added in https://github.com/raphaelm/python-fints/pull/162 to the client's _send_with_possible_retry() method to react on a TAN request when sending a request after the dialog has been established, but not in FinTSDialog.init() if the bank already sends that response:

    [...]
                      fints.segments.dialog.HIRMG2( # Rückmeldungen zur Gesamtnachricht
                          header = fints.formals.SegmentHeader('HIRMG', 3, 2), # Segmentkopf
                          responses = [ # Rückmeldung
                                      fints.formals.Response( # Rückmeldung
                                          code = '3060',
                                          reference_element = None,
                                          text = 'Bitte beachten Sie die enthaltenen Warnungen/Hinweise.',
                                      ),
                                      fints.formals.Response( # Rückmeldung
                                          code = '3920',
                                          reference_element = None,
                                          text = 'Zugelassene Zwei-Schritt-Verfahren für den Benutzer.',
                                          parameters = [
                                                  '923',
                                              ],
                                      ),
                              ],
                      ),
                      fints.segments.dialog.HIRMS2( # Rückmeldungen zu Segmenten
                          header = fints.formals.SegmentHeader('HIRMS', 4, 2, 5), # Segmentkopf
                          responses = [ # Rückmeldung
                                      fints.formals.Response( # Rückmeldung
                                          code = '3955',
                                          reference_element = None,
                                          text = 'Auftrag empfangen - Bitte Auftrag in Ihrer App freigeben.(MBT62870200005)',
                                      ),
                              ],
                      ),
                      fints.segments.auth.HITAN7( # Zwei-Schritt-TAN-Einreichung Rückmeldung, version 7
                          header = fints.formals.SegmentHeader('HITAN', 5, 7, 5), # Segmentkopf
                          tan_process = '4', # TAN-Prozess
                          task_reference = '3115-09-28-19.28.05.705284', # Auftragsreferenz
                          challenge = 'Bitte Auftrag in Ihrer App freigeben.', # Challenge
                      ),
    [...]
    

    So I assume it was an oversight in #162 to not also patch the other method?

So these two changes work for me with an application similar to the one in the troubleshooting guide. I have not found a way to retrieve transactions without having to confirm that with a push TAN via the app interactively. Ideally there would be a way to remember that "device" at least for a couple of days, like it also works with interactive logins via the web page.

This patch may resolve https://github.com/raphaelm/python-fints/issues/121 and https://github.com/raphaelm/python-fints/issues/165, at least when also setting selected_tan_medium to an empty string (or something else if you already know a valid value) before the call to minimal_interactive_cli_bootstrap(f):

f = FinTS3PinTanClient(*client_args, product_id=product_id)
f.selected_tan_medium = ''
minimal_interactive_cli_bootstrap(f)

meyerj avatar Sep 28 '24 17:09 meyerj