Skip to content

Commit d09962c

Browse files
committed
VoP generally works
1 parent 26f9249 commit d09962c

3 files changed

Lines changed: 92 additions & 14 deletions

File tree

fints/client.py

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
PinTanTwoStepAuthenticationMechanism,
2828
)
2929
from .segments.accounts import HISPA1, HKSPA1
30-
from .segments.auth import HIPINS1, HKTAB4, HKTAB5, HKTAN2, HKTAN3, HKTAN5, HKTAN6, HKTAN7
30+
from .segments.auth import HIPINS1, HKTAB4, HKTAB5, HKTAN2, HKTAN3, HKTAN5, HKTAN6, HKTAN7, HIVPPS1, HIVPP1, PSRD1, HKVPA1
3131
from .segments.bank import HIBPA3, HIUPA4, HKKOM4
3232
from .segments.debit import (
3333
HKDBS1, HKDBS2, HKDMB1, HKDMC1, HKDME1, HKDME2,
@@ -225,6 +225,10 @@ def process_response_message(self, dialog, message: FinTSInstituteMessage, inter
225225
message.find_segments('HIUPD')
226226
)
227227

228+
vpps = message.find_segment_first(HIVPPS1)
229+
if vpps:
230+
self.vpps = vpps
231+
228232
for seg in message.find_segments(HIRMG2):
229233
for response in seg.responses:
230234
if not internal_send:
@@ -828,7 +832,7 @@ def simple_sepa_transfer(self, account: SEPAAccount, iban: str, bic: str,
828832
"batch": False,
829833
"currency": "EUR",
830834
}
831-
version = self._find_supported_sepa_version(['pain.001.001.03'])
835+
version = self._find_supported_sepa_version(['pain.001.001.03', 'pain.001.003.03'])
832836
sepa = SepaTransfer(config, version)
833837
payment = {
834838
"name": recipient_name,
@@ -893,7 +897,7 @@ def sepa_transfer(self, account: SEPAAccount, pain_message: str, multiple=False,
893897
if book_as_single:
894898
seg.request_single_booking = True
895899

896-
return self._send_with_possible_retry(dialog, seg, self._continue_sepa_transfer)
900+
return self._send_pay_with_possible_retry(dialog, seg, self._continue_sepa_transfer)
897901

898902
def _continue_sepa_transfer(self, command_seg, response):
899903
retval = TransactionResponse(response)
@@ -1300,6 +1304,19 @@ def _get_tan_segment(self, orig_seg, tan_process, tan_seg=None):
13001304

13011305
return seg
13021306

1307+
def _find_vop_format_for_segment(self, seg):
1308+
needed = str(seg.header.type) in list(self.vpps.parameter.payment_order_segment)
1309+
1310+
if not needed:
1311+
return
1312+
1313+
bank_supported = str(self.vpps.parameter.supported_report_formats)
1314+
1315+
if "sepade.pain.002.001.10.xsd" != bank_supported:
1316+
logger.warning("No common supported SEPA version. Defaulting to what bank supports and hoping for the best: %s.", bank_supported)
1317+
1318+
return bank_supported
1319+
13031320
def _need_twostep_tan_for_segment(self, seg):
13041321
if not self.selected_security_function or self.selected_security_function == '999':
13051322
return False
@@ -1336,6 +1353,60 @@ def _send_with_possible_retry(self, dialog, command_seg, resume_func):
13361353
response = dialog.send(command_seg)
13371354

13381355
return resume_func(command_seg, response)
1356+
1357+
def _send_pay_with_possible_retry(self, dialog, command_seg, resume_func):
1358+
vop_seg = []
1359+
vop_standard = self._find_vop_format_for_segment(command_seg)
1360+
if vop_standard:
1361+
from .segments.auth import HKVPP1
1362+
print(self.vpps)
1363+
vop_seg = [HKVPP1(supported_reports=PSRD1(psrd=[vop_standard]))]
1364+
with dialog:
1365+
if self._need_twostep_tan_for_segment(command_seg):
1366+
tan_seg = self._get_tan_segment(command_seg, '4')
1367+
segments = vop_seg + [command_seg, tan_seg]
1368+
1369+
response = dialog.send(*segments)
1370+
1371+
hivpp = response.find_segment_first(HIVPP1)
1372+
1373+
if not hivpp:
1374+
raise Exception("Mising VoP reponse")
1375+
vop_result = hivpp.vop_single_result
1376+
print(hivpp)
1377+
if vop_result.result == 'RVNA':
1378+
# TODO: let the user decide if they want to proceed by displaying a warning with TAN
1379+
print(vop_result.na_reason)
1380+
vop_seg = [HKVPA1(vop_id=hivpp.vop_id)]
1381+
segments = vop_seg + [command_seg, tan_seg]
1382+
response = dialog.send(*segments)
1383+
elif vop_result.result == 'RVNM':
1384+
vop_seg = [HKVPA1(vop_id=hivpp.vop_id)]
1385+
segments = vop_seg + [command_seg, tan_seg]
1386+
response = dialog.send(*segments)
1387+
print("WARNING! Recipient name does not match.")
1388+
elif vop_result.result == 'RVMC':
1389+
vop_seg = [HKVPA1(vop_id=hivpp.vop_id)]
1390+
segments = vop_seg + [command_seg, tan_seg]
1391+
response = dialog.send(*segments)
1392+
print("WARNING! Recipient name differs:", vop_result.close_match_name)
1393+
1394+
print(vop_result.result)
1395+
for resp in response.responses(tan_seg):
1396+
if resp.code in ('0030', '3955'):
1397+
return NeedTANResponse(
1398+
command_seg,
1399+
response.find_segment_first('HITAN'),
1400+
resume_func,
1401+
self.is_challenge_structured(),
1402+
resp.code == '3955',
1403+
)
1404+
if resp.code.startswith('9'):
1405+
raise Exception("Error response: {!r}".format(response))
1406+
else:
1407+
response = dialog.send(command_seg)
1408+
1409+
return resume_func(command_seg, response)
13391410

13401411
def is_challenge_structured(self):
13411412
param = self.get_tan_mechanisms()[self.get_current_tan_mechanism()]

fints/segments/auth.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from fints.fields import CodeField, DataElementField, DataElementGroupField
22
from fints.formals import (
3+
DataElementGroup,
34
KTI1, BankIdentifier, ChallengeValidUntil, Language2,
45
ParameterChallengeClass, ParameterPinTan, ParameterTwostepTAN1,
56
ParameterTwostepTAN2, ParameterTwostepTAN3, ParameterTwostepTAN4,
@@ -104,15 +105,18 @@ class PSRD1(DataElementGroup):
104105
psrd = DataElementField(type='an', max_length=256, required=True, _d="Payment Status Report Descriptor", max_count=99)
105106
# urn:iso:std:iso:20022:tech:xsd:pain.002.001.14
106107

107-
class HKVPP(FinTS3Segment):
108+
class HKVPP1(FinTS3Segment):
108109
"""Namensabgleich Prüfauftrag, version 1
109110
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Verification of Payee"""
110-
supported_reports = DataElementField(type=PSRD1, required=True, _d="Unterstützte Payment Status Reports")
111+
supported_reports = DataElementGroupField(type=PSRD1, required=True, _d="Unterstützte Payment Status Reports")
111112
polling_id = DataElementField(type='bin', required=False, _d="Polling-ID")
112113
max_queries = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge")
113114
aufsetzpunkt = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt")
114115

115116

117+
a = HKVPP1(polling_id=b"fdf")
118+
HKVPP1(polling_id=a.polling_id, aufsetzpunkt="sas")
119+
116120
class EVPE(DataElementGroup):
117121
"""Ergebnis VOP-Prüfung Einzeltransaktion
118122
@@ -126,16 +130,16 @@ class EVPE(DataElementGroup):
126130
na_reason = DataElementField(type='an', max_length=256, required=False, _d="Grund RVNA")
127131

128132

129-
class HIVPP(FinTS3Segment):
133+
class HIVPP1(FinTS3Segment):
130134
"""Namensabgleich Namensabgleich Prüfergebnis, version 1
131135
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Verification of Payee"""
132136
vop_id = DataElementField(type='bin', required=False, _d="VOP-ID")
133-
vop_id_valid_until = DataElementField(type='tsp', required=False, _d="VOP-ID gültig bis")
137+
vop_id_valid_until = DataElementGroupField(type=ChallengeValidUntil, required=False, _d="VOP-ID gültig bis")
134138
polling_id = DataElementField(type='bin', required=False, _d="Polling-ID")
135139
payment_status_report_descriptor = DataElementField(type='an', max_length=256, required=False, _d="Payment Status Report Descriptor")
136140
payment_status_report = DataElementField(type='bin', required=False, _d="Payment Status Report")
137141
# Only for a single transaction. Mutually exclusive with payment status report.
138-
vop_single_result = DataElementField(type=EVPE, required=False, _d="Ergebnis VOP-Prüfung Einzeltransaktion")
142+
vop_single_result = DataElementGroupField(type=EVPE, required=False, _d="Ergebnis VOP-Prüfung Einzeltransaktion")
139143
manual_authorization_notice = DataElementField(type='an', max_length=65535, required=False, _d="Aufklärungstext Autorisierung trotz Abweichung")
140144
wait_for_seconds = DataElementField(type='num', length=1, required=False, _d="Wartezeit vor nächster Abfrage")
141145

@@ -144,20 +148,24 @@ class ParameterVoP(DataElementGroup):
144148
notice_is_structured = DataElementField(type='jn', required=False, _d="Aufklärungstext strukturiert")
145149
# complete: V, piecemeal: S
146150
report_complete = DataElementField(type='code', length=1, required=False, _d="Art der Lieferung Payment Status Report")
147-
batch_payment-allowed = DataElementField(type='jn', required=False, _d="Sammelzahlungen mit einem Auftrag erlaubt")
151+
batch_payment_allowed = DataElementField(type='jn', required=False, _d="Sammelzahlungen mit einem Auftrag erlaubt")
148152
multiple_allowed = DataElementField(type='jn', required=False, _d="Eingabe Anzahl Einträge erlaubt")
149153
supported_report_formats = DataElementField(type='an', max_length=1024, required=False, _d="Unterstützte Payment Status Report Daten-formate")
150-
# FIXME: count is "n"
151-
payment_order_segment = DataElementField(type='an', max_length=6, required=False, _d="VOP-pflichtiger Zahlungsverkehrsauftrag")
154+
payment_order_segment = DataElementField(type='an', min_count=1, max_length=6, required=False, _d="VOP-pflichtiger Zahlungsverkehrsauftrag")
152155

153156

154-
class HIVPPS(ParameterSegment):
157+
class HIVPPS1(ParameterSegment):
155158
"""Namensabgleich Prüfauftrag Parameter, version 1
156159
157160
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Verification of Payee"""
158161
parameter = DataElementGroupField(type=ParameterVoP, _d="Parameter Namensabgleich Prüfauftrag")
159162

160163

164+
class HKVPA1(FinTS3Segment):
165+
"""Namensabgleich Namensabgleich Ausführungsauftrag, version 1
166+
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Verification of Payee"""
167+
vop_id = DataElementField(type='bin', required=False, _d="VOP-ID")
168+
161169

162170
class HKTAN7(FinTS3Segment):
163171
"""Zwei-Schritt-TAN-Einreichung, version 7
@@ -252,7 +260,6 @@ class HKTAB4(FinTS3Segment):
252260
tan_media_type = CodeField(enum=TANMediaType2, _d="TAN-Medium-Art")
253261
tan_media_class = CodeField(enum=TANMediaClass3, _d="TAN-Medium-Klasse")
254262

255-
256263
class HITAB4(FinTS3Segment):
257264
"""TAN-Generator/Liste anzeigen Bestand Rückmeldung, version 4
258265

fints/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ def __init__(self, segments=None):
214214
if isinstance(segments, bytes):
215215
from .parser import FinTS3Parser
216216
parser = FinTS3Parser()
217-
data = parser.explode_segments(segments)
217+
data = list(parser.explode_segments(segments))
218218
segments = [parser.parse_segment(segment) for segment in data]
219219
self.segments = list(segments) if segments else []
220220

0 commit comments

Comments
 (0)