Skip to content

FinTS3Client._touchdown... attributes missing after restoring client #125

@tloebhard

Description

@tloebhard

Describe the bug
For getting older transactions from Skatbank and comdirect there is a TAN needed. I need to pause dialogue, deconstruct client (including private data) and store request. After restoring/resuming and sending TAN there is an exception in _continue_fetch_with_touchdowns, because self._touchdown_args (amonst others) is not set.
TAN seems to be processed already correctly and accepted by bank.

Bank I tested this with
Name of the bank: comdirect (using PhotoTAN)
FinTS URL: https://fints.comdirect.de/fints

Name of the bank: Skatbank (using VR-SecureGo (Push TAN))
FinTS URL: https://hbci11.fiducia.de/cgi-bin/hbciservlet

Expected behavior
After sending TAN it should return requested transactions

Code required to reproduce
(copied from https://python-fints.readthedocs.io/en/latest/trouble.html)

import datetime
import getpass
import logging
import sys
from decimal import Decimal

from fints.client import FinTS3PinTanClient, NeedTANResponse, FinTSUnsupportedOperation, NeedRetryResponse
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)]

    res = f.get_transactions(account, datetime.date.today() - datetime.timedelta(days=120),
                             datetime.date.today())

    # Test pausing and resuming the dialog
    dialog_data = f.pause_dialog()

client_data = f.deconstruct(including_private=True)
tan_request_data = res.get_data()

tan_request = NeedRetryResponse.from_data(tan_request_data)
f = FinTS3PinTanClient(*client_args, from_data=client_data)
with f.resume_dialog(dialog_data):
    res = ask_for_tan(tan_request)
    print("Found", len(res), "transactions")

Log output / error message

Traceback (most recent call last):
  File ".../PycharmProjects/fints_test/pyfintstest2.py", line 67, in <module>
    res = ask_for_tan(tan_request)
  File ".../PycharmProjects/fints_test/pyfintstest2.py", line 33, in ask_for_tan
    return f.send_tan(response, tan)
  File "...\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\fints\client.py", line 1264, in send_tan
    return resume_func(challenge.command_seg, response)
  File "...\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\fints\client.py", line 456, in _continue_fetch_with_touchdowns
    for resp in response.response_segments(command_seg, *self._touchdown_args, **self._touchdown_kwargs):
AttributeError: 'FinTS3PinTanClient' object has no attribute '_touchdown_args'

Solution / Quick hack
After setting the missing attributes in ask_for_tan before f.send_tan - it works: (just some dirty copy paste from _fetch_with_touchdowns and get_transactions )

from fints.utils import mt940_to_array
import fints.segments.statement

def ask_for_tan(response):
    [...]
    tan = input('Please enter TAN:')
    f._touchdown_args = ['HIKAZ']
    f._touchdown_kwargs = {}
    f._touchdown_responses = []
    f._touchdown_counter = 1
    f._touchdown_response_processor = lambda responses: mt940_to_array(''.join([seg.statement_booked.decode('iso-8859-1') for seg in responses]))
    hkkaz = f._find_highest_supported_command(fints.segments.statement.HKKAZ5,
                                                   fints.segments.statement.HKKAZ6,
                                                   fints.segments.statement.HKKAZ7)
    f._touchdown_segment_factory = lambda touchdown: hkkaz(
        account=hkkaz._fields['account'].type.from_sepa_account(account),
        all_accounts=False,
        date_start=datetime.date.today() - datetime.timedelta(days=120),
        date_end=datetime.date.today(),
        touchdown_point=touchdown,
    )
    return f.send_tan(response, tan)

I think it needs to be saved in deconstruct and restored in set_data ?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions