DATA_BLOB_MAGIC = b'python-fints_DATABLOB'
DATA_BLOB_MAGIC_RETRY = b'python-fints_RETRY_DATABLOB'
+
class FinTSOperations(Enum):
"""This enum is used as keys in the 'supported_operations' member of the get_information() response.
GET_SEPA_STANDING_DEBITS_SINGLE = ("HKDDB", )
SEPA_STANDING_DEBIT_SINGLE_DELETE = ("HKDDL", )
+
class NeedRetryResponse(SubclassesMixin, metaclass=ABCMeta):
"""Base class for Responses that need the operation to be externally retried.
raise Exception("Invalid data blob data or version")
+
class ResponseStatus(Enum):
"Error status of the response"
WARNING = 2 #: Response indicates a Warning
ERROR = 3 #: Response indicates an Error
+
_RESPONSE_STATUS_MAPPING = {
'0': ResponseStatus.SUCCESS,
'3': ResponseStatus.WARNING,
'9': ResponseStatus.ERROR,
}
+
class TransactionResponse:
"""Result of a FinTS operation.
def __repr__(self):
return "<{o.__class__.__name__}(status={o.status!r}, responses={o.responses!r}, data={o.data!r})>".format(o=self)
+
class FinTS3Client:
- def __init__(self, bank_identifier, user_id, customer_id=None, set_data:bytes=None):
+ def __init__(self, bank_identifier, user_id, customer_id=None, set_data: bytes=None):
self.accounts = []
if isinstance(bank_identifier, BankIdentifier):
self.bank_identifier = bank_identifier
retval['accounts'].append(acc)
return retval
-
def get_sepa_accounts(self):
"""
Returns a list of SEPA accounts
else:
return version_map.get(max_version.header.version)
-
def get_transactions(self, account: SEPAAccount, start_date: datetime.date = None, end_date: datetime.date = None):
"""
Fetches the list of transactions of a bank account in a certain timeframe.
return responses
def simple_sepa_transfer(self, account: SEPAAccount, iban: str, bic: str,
- recipient_name: str, amount: Decimal, account_name: str, reason: str,
- endtoend_id='NOTPROVIDED'):
+ recipient_name: str, amount: Decimal, account_name: str, reason: str,
+ endtoend_id='NOTPROVIDED'):
"""
Simple SEPA transfer.
return self.sepa_transfer(account, xml)
def sepa_transfer(self, account: SEPAAccount, pain_message: bytes, multiple=False,
- control_sum=None, currency='EUR', book_as_single=False,
- pain_descriptor='urn:iso:std:iso:20022:tech:xsd:pain.001.001.03'):
+ control_sum=None, currency='EUR', book_as_single=False,
+ pain_descriptor='urn:iso:std:iso:20022:tech:xsd:pain.001.001.03'):
"""
Custom SEPA transfer.
return retval
def sepa_debit(self, account: SEPAAccount, pain_message: str, multiple=False, cor1=False,
- control_sum=None, currency='EUR', book_as_single=False,
- pain_descriptor='urn:iso:std:iso:20022:tech:xsd:pain.008.003.01'):
+ control_sum=None, currency='EUR', book_as_single=False,
+ pain_descriptor='urn:iso:std:iso:20022:tech:xsd:pain.008.003.01'):
"""
Custom SEPA debit.
yield self
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
- challenge_matrix = None #: Matrix code challenge: tuple(mime_type, data)
+ challenge_matrix = None #: Matrix code challenge: tuple(mime_type, data)
def __init__(self, command_seg, tan_request, resume_method=None, tan_request_structured=False):
self.command_seg = command_seg
5: HKTAN5,
}
+
class FinTS3PinTanClient(FinTS3Client):
def __init__(self, bank_identifier, user_id, pin, server, customer_id=None, *args, **kwargs):
self.pin,
)]
- return FinTSDialog(self,
+ return FinTSDialog(
+ self,
lazy_init=lazy_init,
enc_mechanism=enc,
auth_mechanisms=auth,
self.pin.block()
raise FinTSClientPINError("Error during dialog initialization, PIN wrong?")
-
def get_tan_mechanisms(self):
"""
Get the available TAN mechanisms.
DIALOGUE_ID_UNASSIGNED = '0'
DATA_BLOB_MAGIC = b'python-fints_DIALOG_DATABLOB'
+
class FinTSDialog:
def __init__(self, client=None, lazy_init=False, enc_mechanism=None, auth_mechanisms=[]):
self.client = client
class FinTSError(Exception):
pass
+
class FinTSClientError(FinTSError):
pass
+
class FinTSClientPINError(FinTSClientError):
pass
+
class FinTSDialogError(FinTSError):
pass
+
class FinTSDialogStateError(FinTSDialogError):
pass
+
class FinTSDialogInitError(FinTSDialogError):
pass
+
class FinTSConnectionError(FinTSError):
pass
class DataElementField(DocTypeMixin, TypedField):
pass
+
class ContainerField(TypedField):
def _check_value(self, value):
if self.type:
class DataElementGroupField(DocTypeMixin, ContainerField):
pass
+
class GenericField(FieldRenderFormatStringMixin, DataElementField):
type = None
_FORMAT_STRING = "{}"
warnings.warn("Generic field used for type {!r} value {!r}".format(self.type, value))
return value
+
class GenericGroupField(DataElementGroupField):
type = None
warnings.warn("Generic field used for type {!r} value {!r}".format(self.type, value))
return value
+
class TextField(FieldRenderFormatStringMixin, DataElementField):
type = 'txt'
_DOC_TYPE = str
def _parse_value(self, value): return str(value)
+
class AlphanumericField(TextField):
type = 'an'
-
+
+
class DTAUSField(DataElementField):
type = 'dta'
+
class NumericField(FieldRenderFormatStringMixin, DataElementField):
type = 'num'
_DOC_TYPE = int
raise ValueError("Leading zeroes not allowed for value of type 'num': {!r}".format(value))
return int(_value, 10)
+
class ZeroPaddedNumericField(NumericField):
type = ''
_DOC_TYPE = int
_value = str(value)
return int(_value, 10)
+
class DigitsField(FieldRenderFormatStringMixin, DataElementField):
type = 'dig'
_DOC_TYPE = str
raise TypeError("Only digits allowed for value of type 'dig': {!r}".format(value))
return _value
+
class FloatField(DataElementField):
type = 'float'
_DOC_TYPE = float
self._check_value_length(retval)
return retval
+
class AmountField(FixedLengthMixin, FloatField):
type = 'wrt'
_FIXED_LENGTH = [None, None, 15]
# FIXME Needs test
+
class BinaryField(DataElementField):
type = 'bin'
_DOC_TYPE = bytes
def _parse_value(self, value): return bytes(value)
+
class IDField(FixedLengthMixin, AlphanumericField):
type = 'id'
_DOC_TYPE = str
_FIXED_LENGTH = [None, None, 30]
+
class BooleanField(FixedLengthMixin, AlphanumericField):
type = 'jn'
_DOC_TYPE = bool
else:
raise ValueError("Invalid value {!r} for BooleanField".format(value))
+
class CodeFieldMixin:
# FIXME Need tests
retval = retval + addendum
return retval
+
class CodeField(CodeFieldMixin, AlphanumericField):
type = 'code'
_DOC_TYPE = str
+
class IntCodeField(CodeFieldMixin, NumericField):
type = ''
_DOC_TYPE = int
_FORMAT_STRING = "{}"
+
class CountryField(FixedLengthMixin, DigitsField):
type = 'ctr'
_FIXED_LENGTH = [3]
+
class CurrencyField(FixedLengthMixin, AlphanumericField):
type = 'cur'
_FIXED_LENGTH = [3]
+
class DateField(FixedLengthMixin, NumericField):
type = 'dat' # FIXME Need test
_DOC_TYPE = datetime.date
val = int(val)
return super()._render_value(val)
+
class TimeField(FixedLengthMixin, DigitsField):
type = 'tim' # FIXME Need test
_DOC_TYPE = datetime.time
val = "{:02d}{:02d}{:02d}".format(value.hour, value.minute, value.second)
return super()._render_value(val)
+
class PasswordField(AlphanumericField):
type = ''
_DOC_TYPE = Password
def _render_value(self, value):
return str(value)
+
class SegmentSequenceField(DataElementField):
type = 'sf'
CUSTOMER_ID_ANONYMOUS = '9999999999'
+
class DataElementGroup(Container):
pass
+
class SegmentHeader(ShortReprMixin, DataElementGroup):
- "Segmentkopf"
+ """Segmentkopf"""
type = AlphanumericField(max_length=6, _d='Segmentkennung')
number = NumericField(max_length=3, _d='Segmentnummer')
version = NumericField(max_length=3, _d='Segmentversion')
reference = NumericField(max_length=3, required=False, _d='Bezugssegment')
+
class ReferenceMessage(DataElementGroup):
dialogue_id = DataElementField(type='id')
message_number = NumericField(max_length=4)
+
class SecurityMethod(RepresentableEnum):
DDV = 'DDV'
RAH = 'RAH'
RDH = 'RDH'
PIN = 'PIN'
+
class SecurityProfile(DataElementGroup):
- "Sicherheitsprofil"
+ """Sicherheitsprofil"""
security_method = CodeField(enum=SecurityMethod, length=3, _d="Sicherheitsverfahren")
security_method_version = DataElementField(type='num', _d="Version des Sicherheitsverfahrens")
+
class IdentifiedRole(RepresentableEnum):
MS = '1' #: Message Sender
MR = '2' #: Message Receiver
+
class SecurityIdentificationDetails(DataElementGroup):
identified_role = CodeField(IdentifiedRole, max_length=3)
cid = DataElementField(type='bin', max_length=256)
identifier = DataElementField(type='id')
+
class DateTimeType(RepresentableEnum):
- STS = '1' #: Sicherheitszeitstempel
- CRT = '6' #: Certificate Revocation Time
+ STS = '1' #: Sicherheitszeitstempel
+ CRT = '6' #: Certificate Revocation Time
+
class SecurityDateTime(DataElementGroup):
date_time_type = CodeField(DateTimeType, max_length=3)
date = DataElementField(type='dat', required=False)
time = DataElementField(type='tim', required=False)
+
class UsageEncryption(RepresentableEnum):
- OSY = '2' #: Owner Symmetric
+ OSY = '2' #: Owner Symmetric
+
class OperationMode(RepresentableEnum):
- CBC = '2' #: Cipher Block Chaining
- ISO_9796_1 = '16' #: ISO 9796-1 (bei RDH)
- ISO_9796_2_RANDOM = '17' #: ISO 9796-2 mit Zufallszahl (bei RDH)
- PKCS1V15 = '18' #: RSASSA-PKCS#1 V1.5 (bei RDH); RSAES-PKCS#1 V1.5 (bei RAH, RDH)
- PSS = '19' #: RSASSA-PSS (bei RAH, RDH)
- ZZZ = '999' #: Gegenseitig vereinbart (DDV: Retail-MAC)
+ CBC = '2' #: Cipher Block Chaining
+ ISO_9796_1 = '16' #: ISO 9796-1 (bei RDH)
+ ISO_9796_2_RANDOM = '17' #: ISO 9796-2 mit Zufallszahl (bei RDH)
+ PKCS1V15 = '18' #: RSASSA-PKCS#1 V1.5 (bei RDH); RSAES-PKCS#1 V1.5 (bei RAH, RDH)
+ PSS = '19' #: RSASSA-PSS (bei RAH, RDH)
+ ZZZ = '999' #: Gegenseitig vereinbart (DDV: Retail-MAC)
+
class EncryptionAlgorithmCoded(RepresentableEnum):
TWOKEY3DES = '13' #: 2-Key-Triple-DES
AES256 = '14' #: AES-256
+
class AlgorithmParameterName(RepresentableEnum):
KYE = '5' #: Symmetrischer Schlüssel, verschlüsselt mit symmetrischem Schlüssel
KYP = '6' #: Symmetrischer Schlüssel, verschlüsselt mit öffentlichem Schlüssel
+
class AlgorithmParameterIVName(RepresentableEnum):
IVC = '1' #: Initialization value, clear text
+
class EncryptionAlgorithm(DataElementGroup):
usage_encryption = CodeField(UsageEncryption, max_length=3)
operation_mode = CodeField(OperationMode, max_length=3)
algorithm_parameter_iv_name = CodeField(AlgorithmParameterIVName, max_length=3)
algorithm_parameter_iv_value = DataElementField(type='bin', max_length=512, required=False)
+
class HashAlgorithm(DataElementGroup):
usage_hash = DataElementField(type='code', max_length=3)
hash_algorithm = DataElementField(type='code', max_length=3)
algorithm_parameter_name = DataElementField(type='code', max_length=3)
algorithm_parameter_value = DataElementField(type='bin', max_length=512, required=False)
+
class SignatureAlgorithm(DataElementGroup):
usage_signature = DataElementField(type='code', max_length=3)
signature_algorithm = DataElementField(type='code', max_length=3)
operation_mode = DataElementField(type='code', max_length=3)
+
class BankIdentifier(DataElementGroup):
country_identifier = DataElementField(type='ctr')
bank_code = DataElementField(type='an', max_length=30)
+
class KeyType(RepresentableEnum):
- "Schlüsselart"
- D = 'D' #: Schlüssel zur Erzeugung digitaler Signaturen
- S = 'S' #: Signierschlüssel
- V = 'V' #: Chiffrierschlüssel
+ """Schlüsselart"""
+ D = 'D' #: Schlüssel zur Erzeugung digitaler Signaturen
+ S = 'S' #: Signierschlüssel
+ V = 'V' #: Chiffrierschlüssel
+
class KeyName(DataElementGroup):
bank_identifier = DataElementGroupField(type=BankIdentifier)
key_number = DataElementField(type='num', max_length=3)
key_version = DataElementField(type='num', max_length=3)
+
class Certificate(DataElementGroup):
certificate_type = DataElementField(type='code')
certificate_content = DataElementField(type='bin', max_length=4096)
+
class UserDefinedSignature(DataElementGroup):
pin = PasswordField(max_length=99)
tan = DataElementField(type='an', max_length=99, required=False)
+
class Response(DataElementGroup):
code = DataElementField(type='dig', length=4)
reference_element = DataElementField(type='an', max_length=7)
text = DataElementField(type='an', max_length=80)
parameters = DataElementField(type='an', max_length=35, max_count=10, required=False)
+
class Amount1(DataElementGroup):
"""Betrag
amount = DataElementField(type='wrt', _d="Wert")
currency = DataElementField(type='cur', _d="Währung")
+
class AccountInformation(DataElementGroup):
account_number = DataElementField(type='id')
subaccount_number = DataElementField(type='id')
bank_identifier = DataElementGroupField(type=BankIdentifier)
+
class AccountLimit(DataElementGroup):
limit_type = DataElementField(type='code', length=1)
limit_amount = DataElementGroupField(type=Amount1, required=False)
limit_days = DataElementField(type='num', max_length=3, required=False)
+
class AllowedTransaction(DataElementGroup):
transaction = DataElementField(type='an', max_length=6)
required_signatures = DataElementField(type='num', max_length=2)
limit_amount = DataElementGroupField(type=Amount1, required=False)
limit_days = DataElementField(type='num', max_length=3, required=False)
+
class TANTimeDialogAssociation(RepresentableEnum):
- NOT_ALLOWED = '1' #: TAN nicht zeitversetzt / dialogübergreifend erlaubt
- ALLOWED = '2' #: TAN zeitversetzt / dialogübergreifend erlaubt
- BOTH = '3' #: beide Verfahren unterstützt
- NOT_APPLICABLE = '4' #: nicht zutreffend
+ NOT_ALLOWED = '1' #: TAN nicht zeitversetzt / dialogübergreifend erlaubt
+ ALLOWED = '2' #: TAN zeitversetzt / dialogübergreifend erlaubt
+ BOTH = '3' #: beide Verfahren unterstützt
+ NOT_APPLICABLE = '4' #: nicht zutreffend
+
class AllowedFormat(RepresentableEnum):
- NUMERIC = '1' #: numerisch
- ALPHANUMERIC = '2' #: alfanumerisch
+ NUMERIC = '1' #: numerisch
+ ALPHANUMERIC = '2' #: alfanumerisch
+
class TANListNumberRequired(RepresentableEnum):
- NO = '0' #: Nein
- YES = '2' #: Ja
+ NO = '0' #: Nein
+ YES = '2' #: Ja
+
class InitialisationMode(RepresentableEnum):
- CLEARTEXT_PIN_NO_TAN = '00' #: Initialisierungsverfahren mit Klartext-PIN und ohne TAN
- ENCRYPTED_PIN_NO_TAN = '01' #: Schablone 01: Verschlüsselte PIN und ohne TAN
- MASK_02 = '02' #: Schablone 02: Reserviert, bei FinTS zur Zeit nicht verwendet
+ CLEARTEXT_PIN_NO_TAN = '00' #: Initialisierungsverfahren mit Klartext-PIN und ohne TAN
+ ENCRYPTED_PIN_NO_TAN = '01' #: Schablone 01: Verschlüsselte PIN und ohne TAN
+ MASK_02 = '02' #: Schablone 02: Reserviert, bei FinTS zur Zeit nicht verwendet
+
class DescriptionRequired(RepresentableEnum):
- MUST_NOT = '0' #: Bezeichnung des TAN-Mediums darf nicht angegeben werden
- MAY = '1' #: Bezeichnung des TAN-Mediums kann angegeben werden
- MUST = '2' #: Bezeichnung des TAN-Mediums muss angegeben werden
+ MUST_NOT = '0' #: Bezeichnung des TAN-Mediums darf nicht angegeben werden
+ MAY = '1' #: Bezeichnung des TAN-Mediums kann angegeben werden
+ MUST = '2' #: Bezeichnung des TAN-Mediums muss angegeben werden
+
class SMSChargeAccountRequired(RepresentableEnum):
- MUST_NOT = '0' #: SMS-Abbuchungskonto darf nicht angegeben werden
- MAY = '1' #: SMS-Abbuchungskonto kann angegeben werden
- MUST = '2' #: SMS-Abbuchungskonto muss angegeben werden
+ MUST_NOT = '0' #: SMS-Abbuchungskonto darf nicht angegeben werden
+ MAY = '1' #: SMS-Abbuchungskonto kann angegeben werden
+ MUST = '2' #: SMS-Abbuchungskonto muss angegeben werden
+
class PrincipalAccountRequired(RepresentableEnum):
- MUST_NOT = '0' #: Auftraggeberkonto darf nicht angegeben werden
- MUST = '2' #: Auftraggeberkonto muss angegeben werden, wenn im Geschäftsvorfall enthalten
+ MUST_NOT = '0' #: Auftraggeberkonto darf nicht angegeben werden
+ MUST = '2' #: Auftraggeberkonto muss angegeben werden, wenn im Geschäftsvorfall enthalten
+
class TaskHashAlgorithm(RepresentableEnum):
- NONE = '0' #: Auftrags-Hashwert nicht unterstützt
- RIPEMD_160 = '1' #: RIPEMD-160
- SHA_1 = '2' #: SHA-1
+ NONE = '0' #: Auftrags-Hashwert nicht unterstützt
+ RIPEMD_160 = '1' #: RIPEMD-160
+ SHA_1 = '2' #: SHA-1
+
class TwoStepParametersCommon(DataElementGroup):
@property
def VERSION(self):
- "TAN mechanism version"
+ """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")
tan_process = DataElementField(type='code', length=1, _d="TAN-Prozess")
tech_id = DataElementField(type='id', _d="Technische Identifikation TAN-Verfahren")
+
class TwoStepParameters1(TwoStepParametersCommon):
name = DataElementField(type='an', max_length=30, _d="Name des Zwei-Schritt-Verfahrens")
max_length_input = DataElementField(type='num', max_length=2, _d="Maximale Länge des Eingabewertes im Zwei-Schritt-Verfahren")
multiple_tans_allowed = DataElementField(type='jn', _d="Mehrfach-TAN erlaubt")
tan_time_delayed_allowed = DataElementField(type='jn', _d="TAN zeitversetzt/dialogübergreifend erlaubt")
+
class TwoStepParameters2(TwoStepParametersCommon):
name = DataElementField(type='an', max_length=30, _d="Name des Zwei-Schritt-Verfahrens")
max_length_input = DataElementField(type='num', max_length=2, _d="Maximale Länge des Eingabewertes im Zwei-Schritt-Verfahren")
challenge_class_required = DataElementField(type='jn', _d="Challenge-Klasse erforderlich")
challenge_value_required = DataElementField(type='jn', _d="Challenge-Betrag erforderlich")
+
class TwoStepParameters3(TwoStepParametersCommon):
name = DataElementField(type='an', max_length=30, _d="Name des Zwei-Schritt-Verfahrens")
max_length_input = DataElementField(type='num', max_length=2, _d="Maximale Länge des Eingabewertes im Zwei-Schritt-Verfahren")
description_required = CodeField(enum=DescriptionRequired, length=1, _d="Bezeichnung des TAN-Medium erforderlich")
supported_media_number = DataElementField(type='num', length=1, required=False, _d="Anzahl unterstützter aktiver TAN-Medien")
+
class TwoStepParameters4(TwoStepParametersCommon):
zka_id = DataElementField(type='an', max_length=32, _d="ZKA TAN-Verfahren")
zka_version = DataElementField(type='an', max_length=10, _d="Version ZKA TAN-Verfahren")
description_required = CodeField(enum=DescriptionRequired, length=1, _d="Bezeichnung des TAN-Medium erforderlich")
supported_media_number = DataElementField(type='num', length=1, required=False, _d="Anzahl unterstützter aktiver TAN-Medien")
+
class TwoStepParameters5(TwoStepParametersCommon):
zka_id = DataElementField(type='an', max_length=32, _d="ZKA TAN-Verfahren")
zka_version = DataElementField(type='an', max_length=10, _d="Version ZKA TAN-Verfahren")
description_required = CodeField(enum=DescriptionRequired, length=1, _d="Bezeichnung des TAN-Medium erforderlich")
supported_media_number = DataElementField(type='num', length=1, required=False, _d="Anzahl unterstützter aktiver TAN-Medien")
+
class TwoStepParameters6(TwoStepParametersCommon):
zka_id = DataElementField(type='an', max_length=32, _d="ZKA TAN-Verfahren")
zka_version = DataElementField(type='an', max_length=10, _d="Version ZKA TAN-Verfahren")
response_hhd_uc_required = DataElementField(type='jn', _d="Antwort HHD_UC erforderlich")
supported_media_number = DataElementField(type='num', length=1, required=False, _d="Anzahl unterstützter aktiver TAN-Medien")
+
class ParameterTwostepCommon(DataElementGroup):
onestep_method_allowed = DataElementField(type='jn')
multiple_tasks_allowed = DataElementField(type='jn')
task_hash_algorithm = CodeField(enum=TaskHashAlgorithm, length=1, _d="Auftrags-Hashwertverfahren")
+
class ParameterTwostepTAN1(ParameterTwostepCommon):
security_profile_bank_signature = DataElementField(type='code', length=1)
twostep_parameters = DataElementGroupField(type=TwoStepParameters1, min_count=1, max_count=98)
+
class ParameterTwostepTAN2(ParameterTwostepCommon):
twostep_parameters = DataElementGroupField(type=TwoStepParameters2, min_count=1, max_count=98)
+
class ParameterTwostepTAN3(ParameterTwostepCommon):
twostep_parameters = DataElementGroupField(type=TwoStepParameters3, min_count=1, max_count=98)
+
class ParameterTwostepTAN4(ParameterTwostepCommon):
twostep_parameters = DataElementGroupField(type=TwoStepParameters4, min_count=1, max_count=98)
+
class ParameterTwostepTAN5(ParameterTwostepCommon):
twostep_parameters = DataElementGroupField(type=TwoStepParameters5, min_count=1, max_count=98)
+
class ParameterTwostepTAN6(ParameterTwostepCommon):
twostep_parameters = DataElementGroupField(type=TwoStepParameters6, min_count=1, max_count=98)
+
class TransactionTanRequired(DataElementGroup):
transaction = DataElementField(type='an', max_length=6)
tan_required = DataElementField(type='jn')
+
class ParameterPinTan(DataElementGroup):
min_pin_length = DataElementField(type='num', max_length=2, required=False)
max_pin_length = DataElementField(type='num', max_length=2, required=False)
customer_id_field_text = DataElementField(type='an', max_length=30, required=False)
transaction_tans_required = DataElementGroupField(type=TransactionTanRequired, max_count=999, required=False)
+
class Language2(RepresentableEnum):
"""Dialogsprache
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals"""
- STANDARD = '0' #: Standard
- DE = '1' #: Deutsch, 'de', Subset Deutsch, Codeset 1 (Latin 1)
- EN = '2' #: Englisch, 'en', Subset Englisch, Codeset 1 (Latin 1)
- FR = '3' #: Französisch, 'fr', Subset Französisch, Codeset 1 (Latin 1)
+ STANDARD = '0' #: Standard
+ DE = '1' #: Deutsch, 'de', Subset Deutsch, Codeset 1 (Latin 1)
+ EN = '2' #: Englisch, 'en', Subset Englisch, Codeset 1 (Latin 1)
+ FR = '3' #: Französisch, 'fr', Subset Französisch, Codeset 1 (Latin 1)
+
class SupportedLanguages2(DataElementGroup):
languages = CodeField(enum=Language2, max_length=3, min_count=1, max_count=9)
+
class SupportedHBCIVersions2(DataElementGroup):
versions = DataElementField(type='code', max_length=3, min_count=1, max_count=9)
+
class KTZ1(DataElementGroup):
"""Kontoverbindung ZV international, version 1
)
)
+
class KTI1(DataElementGroup):
"""Kontoverbindung international, version 1
)
)
+
class Account2(DataElementGroup):
"""Kontoverbindung, version 2
bank_code=acc.blz,
)
+
class Account3(DataElementGroup):
"""Kontoverbindung, version 3
)
)
+
class SecurityRole(RepresentableEnum):
"""Rolle des Sicherheitslieferanten, kodiert, version 2
Die Wahl ist von der bankfachlichen Auslegung der Signatur, respektive vom vertraglichen Zustand zwischen Kunde und Kreditinstitut abhängig.
Source: FinTS Financial Transaction Services, Sicherheitsverfahren HBCI"""
- ISS = '1' #: Erfasser, Erstsignatur
- CON = '3' #: Unterstützer, Zweitsignatur
- WIT = '4' #: Zeuge/Übermittler, nicht Erfasser
+ ISS = '1' #: Erfasser, Erstsignatur
+ CON = '3' #: Unterstützer, Zweitsignatur
+ WIT = '4' #: Zeuge/Übermittler, nicht Erfasser
+
class CompressionFunction(RepresentableEnum):
"""Komprimierungsfunktion, version 2
Source: FinTS Financial Transaction Services, Sicherheitsverfahren HBCI"""
- NULL = '0' #: Keine Kompression
- LZW = '1' #: Lempel, Ziv, Welch
- COM = '2' #: Optimized LZW
- LZSS = '3' #: Lempel, Ziv
- LZHuf = '4' #: LZ + Huffman Coding
- ZIP = '5' #: PKZIP
- GZIP = '6' #: deflate (http://www.gzip.org/zlib)
- BZIP2 = '7' #: bzip2 (http://sourceware.cygnus.com/bzip2/)
- ZZZ = '999' #: Gegenseitig vereinbart
+ NULL = '0' #: Keine Kompression
+ LZW = '1' #: Lempel, Ziv, Welch
+ COM = '2' #: Optimized LZW
+ LZSS = '3' #: Lempel, Ziv
+ LZHuf = '4' #: LZ + Huffman Coding
+ ZIP = '5' #: PKZIP
+ GZIP = '6' #: deflate (http://www.gzip.org/zlib)
+ BZIP2 = '7' #: bzip2 (http://sourceware.cygnus.com/bzip2/)
+ ZZZ = '999' #: Gegenseitig vereinbart
+
class SecurityApplicationArea(RepresentableEnum):
"""Bereich der Sicherheitsapplikation, kodiert, version 2
Informationen darüber, welche Daten vom kryptographischen Prozess verarbeitet werden.
Source: FinTS Financial Transaction Services, Sicherheitsverfahren HBCI"""
- SHM = '1' #: Signaturkopf und HBCI-Nutzdaten
- SHT = '2' #: Von Signaturkopf bis Signaturabschluss
+ SHM = '1' #: Signaturkopf und HBCI-Nutzdaten
+ SHT = '2' #: Von Signaturkopf bis Signaturabschluss
+
class SecurityClass(RepresentableEnum):
"""Sicherheitsklasse, version 1
Die Sicherheitsklasse gibt für jede Signatur den erforderlichen Sicherheitsdienst an.
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals"""
- NONE = 0 #: Kein Sicherheitsdienst erforderlich
- AUTH = 1 #: Sicherheitsdienst 'Authentikation'
- AUTH_ADV = 2 #: Sicherheitsdienst 'Authentikation' mit fortgeschrittener elektronischer Signatur, optionaler Zertifikatsprüfung
- NON_REPUD = 3 #: Sicherheitsdienst 'Non-Repudiation' mit fortgeschrittener elektronischer Signatur, optionaler Zertifikatsprüfung
- NON_REPUD_QUAL = 4 #: Sicherheitsdienst 'Non-Repudiation' mit fortgeschrittener bzw. qualifizierter elektronischer Signatur, zwingende Zertifikatsprüfung
+ NONE = 0 #: Kein Sicherheitsdienst erforderlich
+ AUTH = 1 #: Sicherheitsdienst 'Authentikation'
+ AUTH_ADV = 2 #: Sicherheitsdienst 'Authentikation' mit fortgeschrittener elektronischer Signatur, optionaler Zertifikatsprüfung
+ NON_REPUD = 3 #: Sicherheitsdienst 'Non-Repudiation' mit fortgeschrittener elektronischer Signatur, optionaler Zertifikatsprüfung
+ NON_REPUD_QUAL = 4 #: Sicherheitsdienst 'Non-Repudiation' mit fortgeschrittener bzw. qualifizierter elektronischer Signatur, zwingende Zertifikatsprüfung
+
class UPDUsage(RepresentableEnum):
"""UPD-Verwendung, version 2
Kennzeichen dafür, wie diejenigen Geschäftsvorfälle zu interpretieren sind, die bei der Beschreibung der Kontoinformationen nicht unter den erlaubten Geschäftsvorfällen aufgeführt sind.
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals"""
- UPD_CONCLUSIVE = '0' #: Die nicht aufgeführten Geschäftsvorfälle sind gesperrt
- UPD_INCONCLUSIVE = '1' #: Bei nicht aufgeführten Geschäftsvorfällen ist keine Aussage möglich, ob diese erlaubt oder gesperrt sind
+ UPD_CONCLUSIVE = '0' #: Die nicht aufgeführten Geschäftsvorfälle sind gesperrt
+ UPD_INCONCLUSIVE = '1' #: Bei nicht aufgeführten Geschäftsvorfällen ist keine Aussage möglich, ob diese erlaubt oder gesperrt sind
+
class SystemIDStatus(RepresentableEnum):
"""Kundensystem-Status, version 2
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals"""
- ID_UNNECESSARY = '0' #: Kundensystem-ID wird nicht benötigt
- ID_NECESSARY = '1' #: Kundensystem-ID wird benötigt
+ ID_UNNECESSARY = '0' #: Kundensystem-ID wird nicht benötigt
+ ID_NECESSARY = '1' #: Kundensystem-ID wird benötigt
+
class SynchronisationMode(RepresentableEnum):
"""Synchronisierungsmodus, version 2
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals"""
- NEW_SYSTEM_ID = '0' #: Neue Kundensystem-ID zurückmelden
- LAST_MESSAGE = '1' #: Letzte verarbeitete Nachrichtennummer zurückmelden
- SIGNATURE_ID = '2' #: Signatur-ID zurückmelden
+ NEW_SYSTEM_ID = '0' #: Neue Kundensystem-ID zurückmelden
+ LAST_MESSAGE = '1' #: Letzte verarbeitete Nachrichtennummer zurückmelden
+ SIGNATURE_ID = '2' #: Signatur-ID zurückmelden
+
class CreditDebit2(RepresentableEnum):
"""Soll-Haben-Kennzeichen, version 2
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
- CREDIT = 'C' #: Haben
- DEBIT = 'D' #: Soll
+ CREDIT = 'C' #: Haben
+ DEBIT = 'D' #: Soll
+
class Balance1(DataElementGroup):
"""Saldo, version 1
currency=self.currency
)
+
class Balance2(DataElementGroup):
"""Saldo, version 2
currency=self.amount.currency
)
+
class Timestamp1(DataElementGroup):
"""Zeitstempel
date = DataElementField(type='dat', _d="Datum")
time = DataElementField(type='tim', required=False, _d="Uhrzeit")
+
class TANMediaType2(RepresentableEnum):
"""TAN-Medium-Art
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Sicherheitsverfahren PIN/TAN"""
- ALL = '0' #: Alle
- ACTIVE = '1' #: Aktiv
- AVAILABLE = '2' #: Verfügbar
+ ALL = '0' #: Alle
+ ACTIVE = '1' #: Aktiv
+ AVAILABLE = '2' #: Verfügbar
class TANMediaClass3(RepresentableEnum):
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Sicherheitsverfahren PIN/TAN"""
- ALL = 'A' #: Alle Medien
- LIST = 'L' #: Liste
- GENERATOR = 'G' #: TAN-Generator
- MOBILE = 'M' #: Mobiltelefon mit mobileTAN
- SECODER = 'S' #: Secoder
+ ALL = 'A' #: Alle Medien
+ LIST = 'L' #: Liste
+ GENERATOR = 'G' #: TAN-Generator
+ MOBILE = 'M' #: Mobiltelefon mit mobileTAN
+ SECODER = 'S' #: Secoder
class TANMediaClass4(RepresentableEnum):
"""TAN-Medium-Klasse, version 4
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Sicherheitsverfahren PIN/TAN"""
- ALL = 'A' #: Alle Medien
- LIST = 'L' #: Liste
- GENERATOR = 'G' #: TAN-Generator
- MOBILE = 'M' #: Mobiltelefon mit mobileTAN
- SECODER = 'S' #: Secoder
- BILATERAL = 'B' #: Bilateral vereinbart
+ ALL = 'A' #: Alle Medien
+ LIST = 'L' #: Liste
+ GENERATOR = 'G' #: TAN-Generator
+ MOBILE = 'M' #: Mobiltelefon mit mobileTAN
+ SECODER = 'S' #: Secoder
+ BILATERAL = 'B' #: Bilateral vereinbart
+
class TANMediumStatus(RepresentableEnum):
"""Status
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Sicherheitsverfahren PIN/TAN"""
- ACTIVE = '1' #: Aktiv
- AVAILABLE = '2' #: Verfügbar
- ACTIVE_SUCCESSOR = '3' #: Aktiv Folgekarte
- AVAILABLE_SUCCESSOR = '4' #: Verfügbar Folgekarte
+ ACTIVE = '1' #: Aktiv
+ AVAILABLE = '2' #: Verfügbar
+ ACTIVE_SUCCESSOR = '3' #: Aktiv Folgekarte
+ AVAILABLE_SUCCESSOR = '4' #: Verfügbar Folgekarte
+
class TANMedia4(DataElementGroup):
"""TAN-Medium-Liste, version 4
last_use = DataElementField(type='dat', required=False, _d="Letzte Benutzung")
active_since = DataElementField(type='dat', required=False, _d="Freigeschaltet am")
+
class TANMedia5(DataElementGroup):
"""TAN-Medium-Liste, version 5
last_use = DataElementField(type='dat', required=False, _d="Letzte Benutzung")
active_since = DataElementField(type='dat', required=False, _d="Freigeschaltet am")
+
class TANUsageOption(RepresentableEnum):
"""TAN-Einsatzoption
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Sicherheitsverfahren PIN/TAN"""
- ALL_ACTIVE = '0' #: Kunde kann alle "aktiven" Medien parallel nutzen
- EXACTLY_ONE = '1'#: Kunde kann genau ein Medium zu einer Zeit nutzen
- MOBILE_PLUS_GENERATOR = '2' #: Kunde kann ein Mobiltelefon und einen TAN-Generator parallel nutzen
+ ALL_ACTIVE = '0' #: Kunde kann alle "aktiven" Medien parallel nutzen
+ EXACTLY_ONE = '1' #: Kunde kann genau ein Medium zu einer Zeit nutzen
+ MOBILE_PLUS_GENERATOR = '2' #: Kunde kann ein Mobiltelefon und einen TAN-Generator parallel nutzen
+
class ParameterChallengeClass(DataElementGroup):
"""Parameter Challenge-Klasse
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Sicherheitsverfahren PIN/TAN"""
parameters = DataElementField(type='an', max_length=999, count=9, required=False)
+
class ResponseHHDUC(DataElementGroup):
"""Antwort HHD_UC
cvr = DataElementField(type='bin', max_length=256, _d="CVR")
version_info_chiptan = DataElementField(type='bin', max_length=256, _d="Versionsinfo der chipTAN-Applikation")
+
class ChallengeValidUntil(DataElementGroup):
"""Gültigkeitsdatum und -uhrzeit für Challenge
date = DataElementField(type='dat', _d="Datum")
time = DataElementField(type='tim', _d="Uhrzeit")
+
class BatchTransferParameter1(DataElementGroup):
"""Parameter SEPA-Sammelüberweisung, version 1
sum_amount_required = DataElementField(type='jn', _d="Summenfeld benötigt")
single_booking_allowed = DataElementField(type='jn', _d="Einzelbuchung erlaubt")
+
class ServiceType2(RepresentableEnum):
- T_ONLINE = 1 #: T-Online
- TCP_IP = 2 #: TCP/IP (Protokollstack SLIP/PPP)
- HTTPS = 3 #: https
+ T_ONLINE = 1 #: T-Online
+ TCP_IP = 2 #: TCP/IP (Protokollstack SLIP/PPP)
+ HTTPS = 3 #: https
+
class CommunicationParameter2(DataElementGroup):
"""Kommunikationsparameter, version 2
filter_function = DataElementField(type='an', length=3, required=False, _d="Filterfunktion")
filter_function_version = DataElementField(type='num', max_length=3, required=False, _d="Version der Filterfunktion")
+
class ScheduledDebitParameter1(DataElementGroup):
"""Parameter terminierte SEPA-Einzellastschrift einreichen, version 1
min_advance_notice_FRST_OOFF = DataElementField(type='num', max_length=4, _d="Minimale Vorlaufzeit FRST/OOFF")
max_advance_notice_FRST_OOFF = DataElementField(type='num', max_length=4, _d="Maximale Vorlaufzeit FRST/OOFF")
+
class ScheduledDebitParameter2(DataElementGroup):
"""Parameter terminierte SEPA-Einzellastschrift einreichen, version 2
allowed_purpose_codes = DataElementField(type='an', max_length=4096, required=False, _d="Zulässige purpose codes")
supported_sepa_formats = DataElementField(type='an', max_length=256, max_count=9, required=False, _d="Unterstützte SEPA-Datenformate")
+
class ScheduledBatchDebitParameter1(DataElementGroup):
"""Parameter terminierte SEPA-Sammellastschrift einreichen, version 1
sum_amount_required = DataElementField(type='jn', _d="Summenfeld benötigt")
single_booking_allowed = DataElementField(type='jn', _d="Einzelbuchung erlaubt")
+
class ScheduledBatchDebitParameter2(DataElementGroup):
"""Parameter terminierte SEPA-Sammellastschrift einreichen, version 2
allowed_purpose_codes = DataElementField(type='an', max_length=4096, required=False, _d="Zulässige purpose codes")
supported_sepa_formats = DataElementField(type='an', max_length=256, max_count=9, required=False, _d="Unterstützte SEPA-Datenformate")
+
class ScheduledCOR1DebitParameter1(DataElementGroup):
"""Parameter terminierte SEPA-COR1-Einzellastschrift, version 1
allowed_purpose_codes = DataElementField(type='an', max_length=4096, required=False, _d="Zulässige purpose codes")
supported_sepa_formats = DataElementField(type='an', max_length=256, max_count=9, required=False, _d="Unterstützte SEPA-Datenformate")
+
class ScheduledCOR1BatchDebitParameter1(DataElementGroup):
"""Parameter terminierte SEPA-COR1-Sammellastschrift, version 1
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
sepa_descriptors = DataElementField(type='an', max_length=256, max_count=99, _d="SEPA Descriptor")
+
class QueryScheduledDebitParameter1(DataElementGroup):
"""Parameter Bestand terminierter SEPA-Einzellastschriften, version 1
date_range_allowed = DataElementField(type='jn', _d="Zeitraum möglich")
max_number_responses_allowed = DataElementField(type='jn', _d="Eingabe Anzahl Einträge erlaubt")
+
class QueryScheduledDebitParameter2(DataElementGroup):
"""Parameter Bestand terminierter SEPA-Einzellastschriften, version 2
date_range_allowed = DataElementField(type='jn', _d="Zeitraum möglich")
supported_sepa_formats = DataElementField(type='an', max_length=256, max_count=9, required=False, _d="Unterstützte SEPA-Datenformate")
+
class QueryScheduledBatchDebitParameter1(DataElementGroup):
"""Parameter Bestand terminierter SEPA-Sammellastschriften, version 1
max_number_responses_allowed = DataElementField(type='jn', _d="Eingabe Anzahl Einträge erlaubt")
date_range_allowed = DataElementField(type='jn', _d="Zeitraum möglich")
+
class QueryCreditCardStatements2(DataElementGroup):
"""Parameter Kreditkartenumsätze anfordern, version 2
max_number_responses_allowed = DataElementField(type='jn', _d="Eingabe Anzahl Einträge erlaubt")
date_range_allowed = DataElementField(type='jn', _d="Zeitraum möglich")
+
class SEPACCode1(RepresentableEnum):
- REVERSAL = '1' #: Reversal
- REVOCATION = '2' #: Revocation
- DELETE = '3' #: Delete
+ REVERSAL = '1' #: Reversal
+ REVOCATION = '2' #: Revocation
+ DELETE = '3' #: Delete
+
class StatusSEPATask1(RepresentableEnum):
## FIXME Enum names in english
- PENDING = '1' #: In Terminierung
- DECLINED = '2' #: Abgelehnt von erster Inkassostelle
- IN_PROGRESS = '3' #: in Bearbeitung
- PROCESSED = '4' #: Creditoren-seitig verarbeitet, Buchung veranlasst
- REVOKED = '5' #: R-Transaktion wurde veranlasst
+ PENDING = '1' #: In Terminierung
+ DECLINED = '2' #: Abgelehnt von erster Inkassostelle
+ IN_PROGRESS = '3' #: in Bearbeitung
+ PROCESSED = '4' #: Creditoren-seitig verarbeitet, Buchung veranlasst
+ REVOKED = '5' #: R-Transaktion wurde veranlasst
+
class GetSEPAAccountParameter1(DataElementGroup):
"""Parameter SEPA-Kontoverbindung anfordern, version 1
FROM_CUSTOMER = 1
FROM_INSTITUTE = 2
+
class FinTSMessage(SegmentSequence):
DIRECTION = None
# Auto-Numbering, dialog relation, security base
if code is None or response.code == code:
yield response
+
class FinTSCustomerMessage(FinTSMessage):
DIRECTION = MessageDirection.FROM_CUSTOMER
# Identification, authentication
+
class FinTSInstituteMessage(FinTSMessage):
DIRECTION = MessageDirection.FROM_INSTITUTE
#: consumed, even in the presence of errors in this library.
robust_mode = True
+
class FinTSParserWarning(UserWarning):
pass
+
class FinTSParserError(ValueError):
pass
+
TOKEN_RE = re.compile(rb"""
^(?: (?: \? (?P<ECHAR>.) )
| (?P<CHAR>[^?:+@']+)
| (?: @ (?P<BINLEN>[0-9]+) @ )
)""", re.X | re.S)
+
class Token(Enum):
EOF = 'eof'
CHAR = 'char'
COLON = ':'
APOSTROPHE = "'"
+
class ParserState:
def __init__(self, data: bytes, start=0, end=None, encoding='iso-8859-1'):
self._token = None
yield Token.EOF, b''
+
class FinTS3Parser:
"""Parser for FinTS/HBCI 3.0 messages
"""
return retval
-
def parse_deg(self, clazz, data_i, required=True):
retval = clazz()
return retval
-
@staticmethod
def explode_segments(data: bytes, start=0, end=None):
segments = []
return segments
+
class FinTS3Serializer:
"""Serializer for FinTS/HBCI 3.0 messages
"""
return result
-
@staticmethod
def implode_segments(message: list):
level1 = []
import datetime
import random
+from fints.exceptions import FinTSError
from .formals import (
AlgorithmParameterIVName, AlgorithmParameterName, CompressionFunction,
DateTimeType, EncryptionAlgorithm, EncryptionAlgorithmCoded,
def decrypt(self, message: FinTSMessage):
raise NotImplemented()
+
class AuthenticationMechanism:
def sign_prepare(self, message: FinTSMessage):
raise NotImplemented()
def verify(self, message: FinTSMessage):
raise NotImplemented()
+
class PinTanDummyEncryptionMechanism(EncryptionMechanism):
def __init__(self, security_method_version=1):
super().__init__()
_now = datetime.datetime.now()
- message.segments.insert(1,
+ message.segments.insert(
+ 1,
HNVSK3(
security_profile=SecurityProfile(SecurityMethod.PIN, self.security_method_version),
security_function='998',
)
)
message.segments[1].header.number = 998
- message.segments.insert(2,
+ message.segments.insert(
+ 2,
HNVSD1(
data=SegmentSequence(segments=plain_segments)
)
def sign_commit(self, message: FinTSMessage):
if not self.pending_signature:
- raise Error("No signature is pending")
+ raise FinTSError("No signature is pending")
if self.pending_signature not in message.segments:
- raise Error("Cannot sign a message that was not prepared")
+ raise FinTSError("Cannot sign a message that was not prepared")
signature = HNSHA2(
security_reference = self.pending_signature.security_reference,
def verify(self, message: FinTSMessage):
pass
+
class PinTanOneStepAuthenticationMechanism(PinTanAuthenticationMechanism):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.security_function = '999'
+
class PinTanTwoStepAuthenticationMechanism(PinTanAuthenticationMechanism):
def __init__(self, client, security_function, *args, **kwargs):
super().__init__(*args, **kwargs)
"""
accounts = DataElementGroupField(type=Account3, max_count=999, required=False, _d="Kontoverbindung")
+
class HISPA1(FinTS3Segment):
"""SEPA-Kontoverbindung rückmelden, version 1
"""
accounts = DataElementGroupField(type=KTZ1, max_count=999, required=False, _d="SEPA-Kontoverbindung")
+
class HISPAS1(ParameterSegment):
"""SEPA-Kontoverbindung anfordern, Parameter, version 1
ben = DataElementField(type='an', max_length=99, required=False, _d="BEN")
tan_medium_name = DataElementField(type='an', max_length=32, required=False, _d="Bezeichnung des TAN-Mediums")
+
class HITAN5(FinTS3Segment):
"""Zwei-Schritt-TAN-Einreichung Rückmeldung, version 5
ben = DataElementField(type='an', max_length=99, required=False, _d="BEN")
tan_medium_name = DataElementField(type='an', max_length=32, required=False, _d="Bezeichnung des TAN-Mediums")
+
class HITAN6(FinTS3Segment):
"""Zwei-Schritt-TAN-Einreichung Rückmeldung, version 6
challenge_valid_until = DataElementGroupField(type=ChallengeValidUntil, required=False, _d="Gültigkeitsdatum und -uhrzeit für Challenge")
tan_medium_name = DataElementField(type='an', max_length=32, required=False, _d="Bezeichnung des TAN-Mediums")
+
class HKTAB4(FinTS3Segment):
"""TAN-Generator/Liste anzeigen Bestand, version 4
tan_media_type = CodeField(enum=TANMediaType2, _d="TAN-Medium-Art")
tan_media_class = CodeField(enum=TANMediaClass3, _d="TAN-Medium-Klasse")
+
class HITAB4(FinTS3Segment):
"""TAN-Generator/Liste anzeigen Bestand Rückmeldung, version 4
tan_usage_option = CodeField(enum=TANUsageOption, _d="TAN_Einsatzoption")
tan_media_list = DataElementGroupField(type=TANMedia4, max_count=99, required=False, _d="TAN-Medium-Liste")
+
class HKTAB5(FinTS3Segment):
"""TAN-Generator/Liste anzeigen Bestand, version 5
tan_media_type = CodeField(enum=TANMediaType2, _d="TAN-Medium-Art")
tan_media_class = CodeField(enum=TANMediaClass4, _d="TAN-Medium-Klasse")
+
class HITAB5(FinTS3Segment):
"""TAN-Generator/Liste anzeigen Bestand Rückmeldung, version 5
tan_usage_option = CodeField(enum=TANUsageOption, _d="TAN_Einsatzoption")
tan_media_list = DataElementGroupField(type=TANMedia5, max_count=99, required=False, _d="TAN-Medium-Liste")
+
class HITANSBase(ParameterSegment):
pass
+
class HITANS1(HITANSBase):
parameter = DataElementGroupField(type=ParameterTwostepTAN1)
+
class HITANS2(HITANSBase):
parameter = DataElementGroupField(type=ParameterTwostepTAN2)
+
class HITANS3(HITANSBase):
parameter = DataElementGroupField(type=ParameterTwostepTAN3)
+
class HITANS4(HITANSBase):
parameter = DataElementGroupField(type=ParameterTwostepTAN4)
+
class HITANS5(HITANSBase):
parameter = DataElementGroupField(type=ParameterTwostepTAN5)
+
class HITANS6(HITANSBase):
parameter = DataElementGroupField(type=ParameterTwostepTAN6)
+
class HIPINS1(ParameterSegment):
"""PIN/TAN-spezifische Informationen, version 1
min_timeout = DataElementField(type='num', max_length=4, required=False, _d="Minimaler Timeout-Wert")
max_timeout = DataElementField(type='num', max_length=4, required=False, _d="Maximaler Timeout-Wert")
+
class HIUPA4(FinTS3Segment):
- "Userparameter allgemein"
+ """Userparameter allgemein"""
user_identifier = DataElementField(type='id', _d="Benutzerkennung")
upd_version = DataElementField(type='num', max_length=3, _d="UPD-Version")
upd_usage = CodeField(UPDUsage, length=1, _d="UPD-Verwendung")
username = DataElementField(type='an', max_length=35, required=False, _d="Benutzername")
extension = DataElementField(type='an', max_length=2048, required=False, _d="Erweiterung, allgemein")
+
class HIUPD6(FinTS3Segment):
- "Kontoinformationen"
+ """Kontoinformationen"""
account_information = DataElementGroupField(type=AccountInformation, required=False, _d="Kontoverbindung")
iban = DataElementField(type='an', max_length=34, _d="IBAN")
customer_id = DataElementField(type='id', _d="Kunden-ID")
allowed_transactions = DataElementGroupField(type=AllowedTransaction, count=999, required=False, _d="Erlaubte Geschäftsvorfälle")
extension = DataElementField(type='an', max_length=2048, required=False, _d="Erweiterung, kontobezogen")
+
class HKKOM4(FinTS3Segment):
"""Kommunikationszugang anfordern, version 4
max_number_responses = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge")
touchdown_point = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt")
+
class HIKOM4(FinTS3Segment):
"""Kommunikationszugang rückmelden, version 4
TYPE_VERSION_RE = re.compile(r'^([A-Z]+)(\d+)$')
+
class FinTS3SegmentMeta(ContainerMeta):
@staticmethod
def _check_fields_recursive(instance):
FinTS3SegmentMeta._check_fields_recursive(retval)
return retval
+
class FinTS3Segment(Container, SubclassesMixin, metaclass=FinTS3SegmentMeta):
header = DataElementGroupField(type=SegmentHeader, _d="Segmentkopf")
max_number_tasks = DataElementField(type='num', max_length=3, _d="Maximale Anzahl Aufträge")
min_number_signatures = DataElementField(type='num', length=1, _d="Anzahl Signaturen mindestens")
+
class ParameterSegment(FinTS3Segment):
max_number_tasks = DataElementField(type='num', max_length=3, _d="Maximale Anzahl Aufträge")
min_number_signatures = DataElementField(type='num', length=1, _d="Anzahl Signaturen mindestens")
sepa_descriptor = DataElementField(type='an', max_length=256, _d="SEPA Descriptor")
sepa_pain_message = DataElementField(type='bin', _d="SEPA pain message")
+
class DebitResponseBase(FinTS3Segment):
task_id = DataElementField(type='an', max_length=99, required=False, _d="Auftragsidentifikation")
sepa_descriptor = DataElementField(type='an', max_length=256, _d="SEPA Descriptor")
sepa_pain_message = DataElementField(type='bin', _d="SEPA pain message")
+
class HIDSE2(DebitResponseBase):
"""Einreichung terminierter SEPA-Einzellastschrift bestätigen, version 2
parameter = DataElementGroupField(type=ScheduledDebitParameter2, _d="Parameter terminierte SEPA-Sammellastschrift einreichen")
-
class HKDME1(BatchDebitBase):
"""Einreichung terminierter SEPA-Sammellastschrift, version 1
parameter = DataElementGroupField(type=ScheduledBatchDebitParameter1, _d="Parameter terminierte SEPA-Sammellastschrift einreichen")
-
-
class HKDME2(BatchDebitBase):
"""Einreichung terminierter SEPA-Sammellastschrift, version 2
parameter = DataElementGroupField(type=ScheduledBatchDebitParameter2, _d="Parameter terminierte SEPA-Sammellastschrift einreichen")
-
class HKDSC1(FinTS3Segment):
"""Terminierte SEPA-COR1-Einzellastschrift einreichen, version 1
parameter = DataElementGroupField(type=ScheduledCOR1DebitParameter1, _d="Parameter terminierte SEPA-COR1-Einzellastschrift")
-
class HKDMC1(BatchDebitBase):
"""Terminierte SEPA-COR1-Sammellastschrift einreichen, version 1
parameter = DataElementGroupField(type=ScheduledCOR1BatchDebitParameter1, _d="Parameter terminierte SEPA-COR1-Sammellastschrift")
-
class HKDBS1(FinTS3Segment):
"""Bestand terminierter SEPA-Einzellastschriften anfordern, version 1
parameter = DataElementGroupField(type=QueryScheduledDebitParameter1, _d="Parameter Bestand terminierter SEPA-Einzellastschriften")
-
class HKDBS2(FinTS3Segment):
"""Bestand terminierter SEPA-Einzellastschriften anfordern, version 2
task_changeable = DataElementField(type='jn', required=False, _d="Auftrag änderbar")
status_sepa_task = CodeField(enum=StatusSEPATask1, _d="Status SEPA-Auftrag")
+
class HIDBSS2(ParameterSegment):
"""Bestand terminierter SEPA-Einzellastschriften Parameter, version 2
parameter = DataElementGroupField(type=QueryScheduledDebitParameter2, _d="Parameter Bestand terminierter SEPA-Einzellastschriften")
-
class HKDMB1(FinTS3Segment):
"""Bestand terminierter SEPA-Sammellastschriften anfordern, version 1
debit_count = DataElementField(type='num', max_length=6, _d="Anzahl der Aufträge")
sum_amount = DataElementGroupField(type=Amount1, _d="Summe der Beträge")
+
class HIDMBS1(ParameterSegment):
"""Bestand terminierter SEPA-Sammellastschriften Parameter, version 1
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals"""
synchronisation_mode = CodeField(enum=SynchronisationMode, length=1)
+
class HISYN4(FinTS3Segment):
- "Synchronisierungsantwort"
+ """Synchronisierungsantwort"""
system_id = DataElementField(type='id', _d="Kundensystem-ID")
message_number = DataElementField(type='num', max_length=4, required=False, _d="Nachrichtennummer")
security_reference_signature_key = DataElementField(type='num', max_length=16, required=False, _d="Sicherheitsreferenznummer für Signierschlüssel")
security_reference_digital_signature = DataElementField(type='num', max_length=16, required=False, _d="Sicherheitsreferenznummer für Digitale Signatur")
+
class HKEND1(FinTS3Segment):
"""Dialogende, version 1
Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals"""
dialogue_id = DataElementField(type='id', _d="Dialog-ID")
+
class HIRMG2(FinTS3Segment):
- "Rückmeldungen zur Gesamtnachricht"
+ """Rückmeldungen zur Gesamtnachricht"""
responses = DataElementGroupField(type=Response, min_count=1, max_count=99, _d="Rückmeldung")
+
class HIRMS2(FinTS3Segment):
- "Rückmeldungen zu Segmenten"
+ """Rückmeldungen zu Segmenten"""
responses = DataElementGroupField(type=Response, min_count=1, max_count=99, _d="Rückmeldung")
max_number_responses = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge")
touchdown_point = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt")
+
class HIPRO3(FinTS3Segment):
"""Statusprotokoll rückmelden, version 3
time = DataElementField(type='tim', _d="Uhrzeit")
responses = DataElementGroupField(type=Response, _d="Rückmeldung")
+
class HIPROS3(ParameterSegment_22):
"""Statusprotokoll Parameter, version 3
max_number_responses = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge")
touchdown_point = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt")
+
class HIPRO4(FinTS3Segment):
"""Statusprotokoll rückmelden, version 4
time = DataElementField(type='tim', _d="Uhrzeit")
responses = DataElementGroupField(type=Response, _d="Rückmeldung")
+
class HIPROS4(ParameterSegment):
"""Statusprotokoll Parameter, version 4
class HNHBK3(FinTS3Segment):
- "Nachrichtenkopf"
+ """Nachrichtenkopf"""
message_size = ZeroPaddedNumericField(length=12, _d="Größe der Nachricht (nach Verschlüsselung und Komprimierung)")
hbci_version = DataElementField(type='num', max_length=3, _d="HBCI-Version")
dialogue_id = DataElementField(type='id', _d="Dialog-ID")
message_number = DataElementField(type='num', max_length=4, _d="Nachrichtennummer")
reference_message = DataElementGroupField(type=ReferenceMessage, required=False, _d="Bezugsnachricht")
+
class HNHBS1(FinTS3Segment):
- "Nachrichtenabschluss"
+ """Nachrichtenabschluss"""
message_number = DataElementField(type='num', max_length=4, _d="Nachrichtennummer")
+
class HNVSK3(FinTS3Segment):
"""Verschlüsselungskopf, version 3
Source: FinTS Financial Transaction Services, Sicherheitsverfahren HBCI"""
data = SegmentSequenceField(_d="Daten, verschlüsselt")
+
class HNSHK4(FinTS3Segment):
"""Signaturkopf, version 4
key_name = DataElementGroupField(type=KeyName, _d="Schlüsselname")
certificate = DataElementGroupField(type=Certificate, required=False, _d="Zertifikat")
+
class HNSHA2(FinTS3Segment):
"""Signaturabschluss, version 2
max_number_responses = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge")
touchdown_point = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt")
+
class HISAL5(FinTS3Segment):
"""Saldenrückmeldung, version 5
booking_time = DataElementField(type='tim', required=False, _d="Buchungsuhrzeit des Saldos")
date_due = DataElementField(type='dat', required=False, _d="Fälligkeit")
+
class HKSAL6(FinTS3Segment):
"""Saldenabfrage, version 6
max_number_responses = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge")
touchdown_point = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt")
+
class HISAL6(FinTS3Segment):
"""Saldenrückmeldung, version 6
booking_timestamp = DataElementGroupField(type=Timestamp1, required=False, _d="Buchungszeitpunkt")
date_due = DataElementField(type='dat', required=False, _d="Fälligkeit")
+
class HKSAL7(FinTS3Segment):
"""Saldenabfrage, version 7
statement_booked = DataElementField(type='bin', _d="Gebuchte Umsätze")
statement_pending = DataElementField(type='bin', required=False, _d="Nicht gebuchte Umsätze")
+
class HKKAZ6(FinTS3Segment):
"""Kontoumsätze anfordern/Zeitraum, version 6
statement_booked = DataElementField(type='bin', _d="Gebuchte Umsätze")
statement_pending = DataElementField(type='bin', required=False, _d="Nicht gebuchte Umsätze")
+
class HKKAZ7(FinTS3Segment):
"""Kontoumsätze anfordern/Zeitraum, version 7
sepa_descriptor = DataElementField(type='an', max_length=256, _d="SEPA Descriptor")
sepa_pain_message = DataElementField(type='bin', _d="SEPA pain message")
+
class HKCCM1(FinTS3Segment):
"""SEPA-Sammelüberweisung, version 1
sepa_descriptor = DataElementField(type='an', max_length=256, _d="SEPA Descriptor")
sepa_pain_message = DataElementField(type='bin', _d="SEPA pain message")
+
class HICCMS1(ParameterSegment):
"""SEPA-Sammelüberweisung Parameter, version 1
return " # {}".format(d)
return ""
+
class TypedField(Field, SubclassesMixin):
def __new__(cls, *args, **kwargs):
target_cls = None
stream.write( (prefix + (level+1)*indent) + "# {} empty items skipped\n".format(skipped_items) )
stream.write( (prefix + level*indent) + "]{}\n".format(trailer) )
+
class SegmentSequence:
"""A sequence of FinTS3Segment objects"""
print(u"{}: {}".format(idx, seg.encode('utf-8')))
-
def fints_unescape(content):
return content.replace('??', '?').replace("?'", "'").replace('?+', '+').replace('?:', ':').replace('?@', '@')
def replace(self, *args, **kwargs):
return self.__str__().replace(*args, **kwargs)
+
class RepresentableEnum(Enum):
def __init__(self, *args, **kwargs):
Enum.__init__(self)
from decimal import Decimal
import pytest
+
@pytest.fixture
def fints_client(fints_server):
return FinTS3PinTanClient(
fints_server
)
+
def test_get_sepa_accounts(fints_client):
with fints_client:
accounts = fints_client.get_sepa_accounts()
assert accounts
+
def test_get_information(fints_client):
with fints_client:
information = fints_client.get_information()
assert information["bank"]["name"] == 'Test Bank'
+
def test_pin_wrong(fints_server):
client = FinTS3PinTanClient(
'12345678',
with pytest.raises(Exception, match="Refusing"):
str(client.pin)
+
def test_resume(fints_client, fints_server):
with fints_client:
system_id = fints_client.system_id
c_data = fints_client.get_data(including_private=True)
- client2 = FinTS3PinTanClient(
+ FinTS3PinTanClient(
'12345678',
'test1',
'1234',
assert dialog_id == fints_client._standing_dialog.dialogue_id
assert fints_client.bpd_version == 78
+
def test_transfer_1step(fints_client):
with fints_client:
accounts = fints_client.get_sepa_accounts()
assert a.responses[0].code == '0010'
assert a.responses[0].text == "Transfer 1.23 to DE111234567800000002 re 'Test transfer 1step'"
+
def test_transfer_1step_regression(fints_client):
# Passing a float is not officially supported, but should not fail with wrong values
# Decimal(1.23) is Decimal('1.229999999999999982236431605997495353221893310546875')
assert isinstance(a, TransactionResponse)
assert a.responses[0].text == "Transfer 1.23 to DE111234567800000002 re 'Test transfer 1step'"
+
def test_transfer_2step(fints_client):
with fints_client:
accounts = fints_client.get_sepa_accounts()
assert b.status == ResponseStatus.SUCCESS
assert b.responses[0].text == "Transfer 2.34 to DE111234567800000002 re 'Test transfer 2step'"
+
def test_transfer_2step_continue(fints_client):
with fints_client:
accounts = fints_client.get_sepa_accounts()
assert b.status == ResponseStatus.SUCCESS
assert b.responses[0].text == "Transfer 3.42 to DE111234567800000002 re 'Test transfer 2step'"
+
def test_tan_wrong(fints_client):
with fints_client:
accounts = fints_client.get_sepa_accounts()
b = fints_client.send_tan(a, '99881')
assert b.status == ResponseStatus.ERROR
+
def test_tan_hhduc(fints_client):
with fints_client:
accounts = fints_client.get_sepa_accounts()
b = fints_client.send_tan(a, flicker.startcode.data)
assert b.status == ResponseStatus.SUCCESS
+
def test_get_transactions(fints_client):
with fints_client:
accounts = fints_client.get_sepa_accounts()
del i2.b
assert i2.b is None
+
def test_container_nested():
class A(Container):
a = NumericField()
i1.b.a = '1'
assert i1.b.a == 1
+
def test_container_count_exact():
class A(Container):
a = NumericField(count=2)
del i1.a[1]
assert i1.a[1] is None
+
def test_container_count_variable_1():
class A(Container):
a = NumericField(min_count=2, max_count=5)
assert len(i1.a) == 4
+
def test_container_count_variable_2():
class A(Container):
a = NumericField(min_count=2)
assert len(i1.a) == 2
+
def test_container_count_variable_3():
class A(Container):
a = NumericField(max_count=4)
assert len(i1.a) == 0
+
def test_container_count_variable_iter():
class A(Container):
a = NumericField(max_count=4)
assert len(i1.a) == 3
+
def test_container_init():
class A(Container):
a = NumericField()
assert i1.a == 3
assert i1.b == '04'
+
def test_container_nested_init():
class A(Container):
a = NumericField()
i3 = B()
assert i3.b.a is None
+
def test_container_count_init():
class A(Container):
a = NumericField(count=4)
assert len(i1.a) == 4
+
def test_container_count_variable_init():
class A(Container):
a = NumericField(max_count=4)
assert i1.a == 23
assert i1.b == '42'
+
def test_invalid_spec():
with pytest.raises(ValueError):
class A(Container):
A(a=123)
+
def test_parse_restrictions():
class A(Container):
a = NumericField(min_length=2, max_length=3)
with pytest.raises(ValueError, match='min_length=2 not reached'):
A(a=1)
+
def test_unset():
class A(Container):
a = NumericField()
assert B(b=A()).is_unset()
assert not B(c=1).is_unset()
+
def test_sequence_repr():
s = SegmentSequence()
assert repr(s) == 'fints.types.SegmentSequence([])'
+
def test_valuelist_repr():
class A(Container):
a = NumericField(max_count=3)
i1 = A()
assert repr(i1.a) == "[]"
+
def test_empty_list():
class A(Container):
a = NumericField(max_count=3)
i2 = A(a=[None])
assert len(i2.a) == 0
+
def test_segmentheader_short():
h = SegmentHeader('HNHBS', 5, 1)
assert repr(h) == "fints.formals.SegmentHeader('HNHBS', 5, 1)"
+
def test_container_generic():
class A(Container):
a = DataElementGroupField()
with pytest.warns(UserWarning, match=r'Generic field used for type None value \[\]'):
i2 = A(a=[])
+
def test_find_1():
from conftest import TEST_MESSAGES
from fints.parser import FinTS3Parser
m = FinTS3Parser().parse_message(TEST_MESSAGES['basic_complicated'])
assert len(list(m.find_segments('HNHBK'))) == 1
- assert len(list(m.find_segments( ['HNHBK', 'HNHBS'] ))) == 2
+ assert len(list(m.find_segments(['HNHBK', 'HNHBS']))) == 2
- assert len(list(m.find_segments( ['HNHBK', 'HNHBS'], 1))) == 1
- assert len(list(m.find_segments( ['HNHBK', 'HNHBS'], (1, 3) ))) == 2
+ assert len(list(m.find_segments(['HNHBK', 'HNHBS'], 1))) == 1
+ assert len(list(m.find_segments(['HNHBK', 'HNHBS'], (1, 3)))) == 2
assert isinstance(m.find_segment_first('HNHBK'), HNHBK3)
assert isinstance(m.find_segment_first('HNHBS'), HNHBS1)
assert m.find_segment_first('ITST') is None
- assert len( m.find_segment_first('HITANS', 1).parameter.twostep_parameters ) == 2
- assert len( m.find_segment_first('HITANS', 3).parameter.twostep_parameters ) == 6
+ assert len(m.find_segment_first('HITANS', 1).parameter.twostep_parameters) == 2
+ assert len(m.find_segment_first('HITANS', 3).parameter.twostep_parameters) == 6
assert m.find_segment_first('HITANS', recurse=False) is None
+
def test_find_by_class():
from conftest import TEST_MESSAGES
from fints.parser import FinTS3Parser
if input_name.startswith('basic_'):
assert len(segments) == 4
+
@pytest.mark.parametrize("input_name", [k for k in TEST_MESSAGES.keys() if k.startswith('basic_')])
def test_parse_basic(input_name):
m = FinTS3Parser().parse_message(TEST_MESSAGES[input_name])
assert m.segments[3].__class__.__name__ == "HNHBS1"
m.print_nested()
+
@pytest.mark.parametrize("input_name", [k for k in TEST_MESSAGES.keys() if not k.startswith('basic_')])
def test_parse_other(input_name):
m = FinTS3Parser().parse_message(TEST_MESSAGES[input_name])
assert isinstance(m, SegmentSequence)
m.print_nested()
+
def test_parse_counted():
from fints.segments.base import FinTS3Segment
from fints.formals import NumericField, Container, ContainerField
class ITST3(FinTS3Segment):
b = ContainerField(type=InnerTest, max_count=99)
-
m5 = FinTS3Parser().parse_message(b"ITST:1:3+12:42+345+61:62:63'")
m5.print_nested()
assert m5.segments[0].b[0].a[0] == 12
def test_parse_HIRMG2():
d = b"HIRMG:3:2+0010::Nachricht entgegengenommen.+0100::Dialog beendet.'"
m = FinTS3Parser().parse_message(d)
-
+
seg = m.segments[0]
assert seg.header.type == 'HIRMG'
assert seg.responses[0].code == '0010'
assert seg.responses[1].code == '0100'
assert len(seg.responses) == 2
+
# Regression test, bug found in the wild
def test_extra_colon():
message1 = rb"""HIRMG:2:2:+3060::Teilweise liegen Warnungen/Hinweise vor.'"""
assert seg.header.version == 2
assert seg.header.reference is None
+
def test_invalid():
message1 = rb"""12"""
with pytest.raises(FinTSParserError, match='^Required field'):
m = FinTS3Parser().parse_message(message6)
+
def test_robust_mode(mock):
mock.patch('fints.parser.robust_mode', True)
class ITST1(FinTS3Segment):
a = NumericField(count=3)
- i1 = ITST1(a=[1,3])
+ i1 = ITST1(a=[1, 3])
i1.header.number = 42
serialization = rb"""ITST:42:1+1+3+'"""
assert FinTS3Serializer().serialize_message(i1) == serialization
+
def test_implode_1():
m = [
[
assert FinTS3Parser.explode_segments(s) == m
+
def test_implode_shorten():
m = [
[
message = FinTS3Parser().parse_message(segments)
assert FinTS3Serializer().serialize_message(message) == TEST_MESSAGES['basic_simple']
+
def test_escape():
assert b"a" == FinTS3Serializer.escape_value('a')
with pytest.raises(TypeError):
FinTS3Serializer.escape_value(1)
+
def test_serialize_2():
from fints.formals import SegmentSequence
import fints.formals, fints.segments
- s = SegmentSequence([fints.segments.message.HNHBK3(header=fints.formals.SegmentHeader('HNHBK', 1, 3), message_size='000000000428', hbci_version=300, dialogue_id='430711670077=043999659571CN9D=', message_number=2, reference_message=fints.formals.ReferenceMessage(dialogue_id='430711670077=043999659571CN9D=', message_number=2)), fints.segments.message.HNVSK3(header=fints.formals.SegmentHeader('HNVSK', 998, 3), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='998', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_datetime=fints.formals.SecurityDateTime(date_time_type='1'), encryption_algorithm=fints.formals.EncryptionAlgorithm(usage_encryption='2', operation_mode='2', encryption_algorithm='13', algorithm_parameter_value=b'00000000', algorithm_parameter_name='5', algorithm_parameter_iv_name='1'), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0), compression_function='0'), fints.segments.message.HNVSD1(header=fints.formals.SegmentHeader('HNVSD', 999, 1), data=SegmentSequence([fints.segments.message.HNSHK4(header=fints.formals.SegmentHeader('HNSHK', 2, 4), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='999', security_reference='9166926', security_application_area='1', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_reference_number=1, security_datetime=fints.formals.SecurityDateTime(date_time_type='1'), hash_algorithm=fints.formals.HashAlgorithm(usage_hash='1', hash_algorithm='999', algorithm_parameter_name='1'), signature_algorithm=fints.formals.SignatureAlgorithm(usage_signature='6', signature_algorithm='10', operation_mode='16'), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0)), fints.segments.dialog.HIRMG2(header=fints.formals.SegmentHeader('HIRMG', 3, 2), responses=[fints.formals.Response(code='0010', reference_element=None, text='Nachricht entgegengenommen.'), fints.formals.Response(code='0100', reference_element=None, text='Dialog beendet.')]), fints.segments.message.HNSHA2(header=fints.formals.SegmentHeader('HNSHA', 4, 2), security_reference='9166926')])), fints.segments.message.HNHBS1(header=fints.formals.SegmentHeader('HNHBS', 5, 1), message_number=2)])
-
+ s = SegmentSequence([fints.segments.message.HNHBK3(header=fints.formals.SegmentHeader('HNHBK', 1, 3), message_size='000000000428', hbci_version=300,
+ dialogue_id='430711670077=043999659571CN9D=', message_number=2,
+ reference_message=fints.formals.ReferenceMessage(dialogue_id='430711670077=043999659571CN9D=',
+ message_number=2)),
+ fints.segments.message.HNVSK3(header=fints.formals.SegmentHeader('HNVSK', 998, 3),
+ security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1),
+ security_function='998', security_role='1',
+ security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2',
+ cid=None,
+ identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'),
+ security_datetime=fints.formals.SecurityDateTime(date_time_type='1'),
+ encryption_algorithm=fints.formals.EncryptionAlgorithm(usage_encryption='2', operation_mode='2',
+ encryption_algorithm='13',
+ algorithm_parameter_value=b'00000000',
+ algorithm_parameter_name='5',
+ algorithm_parameter_iv_name='1'),
+ key_name=fints.formals.KeyName(
+ bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'),
+ user_id='hermes', key_type='S', key_number=0, key_version=0), compression_function='0'),
+ fints.segments.message.HNVSD1(header=fints.formals.SegmentHeader('HNVSD', 999, 1), data=SegmentSequence([fints.segments.message.HNSHK4(
+ header=fints.formals.SegmentHeader('HNSHK', 2, 4),
+ security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='999',
+ security_reference='9166926', security_application_area='1', security_role='1',
+ security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None,
+ identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'),
+ security_reference_number=1, security_datetime=fints.formals.SecurityDateTime(date_time_type='1'),
+ hash_algorithm=fints.formals.HashAlgorithm(usage_hash='1', hash_algorithm='999', algorithm_parameter_name='1'),
+ signature_algorithm=fints.formals.SignatureAlgorithm(usage_signature='6', signature_algorithm='10', operation_mode='16'),
+ key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'),
+ user_id='hermes', key_type='S', key_number=0, key_version=0)), fints.segments.dialog.HIRMG2(
+ header=fints.formals.SegmentHeader('HIRMG', 3, 2),
+ responses=[fints.formals.Response(code='0010', reference_element=None, text='Nachricht entgegengenommen.'),
+ fints.formals.Response(code='0100', reference_element=None, text='Dialog beendet.')]), fints.segments.message.HNSHA2(
+ header=fints.formals.SegmentHeader('HNSHA', 4, 2), security_reference='9166926')])),
+ fints.segments.message.HNHBS1(header=fints.formals.SegmentHeader('HNHBS', 5, 1), message_number=2)])
+
assert FinTS3Serializer().serialize_message(s) == TEST_MESSAGES['basic_simple']
+
def test_serialize_roundtrips_complex_1():
from fints.formals import SegmentSequence
m1 = SegmentSequence(TEST_MESSAGES['basic_complicated'])
assert list(a._fields) == ['header', 'message_size', 'hbci_version', 'dialogue_id', 'message_number', 'reference_message']
assert a._fields['header']
+
def test_fints3_only_de_and_deg():
from fints.formals import Field, Container, DataElementGroupField
class Foo(FinTS3Segment):
c = DataElementGroupField(type=B)
+
def test_segment_subclassing():
class Base1(FinTS3Segment):
a = NumericField()
class ISUBTST1(Base2):
c = NumericField()
- assert list( ISUBTST1._fields.keys() ) == ['header', 'a', 'b', 'c']
+ assert list(ISUBTST1._fields.keys()) == ['header', 'a', 'b', 'c']
+
def test_descriptor_subclassing():
a = DataElementField(type='an')
c = DataElementField(type='fnord')
assert isinstance(c, GenericField)
+
def test_descriptor_in_use():
assert isinstance(HNHBS1._fields['message_number'], NumericField)
+
def test_data_element_constructor():
a = HNHBS1(message_number='4')
assert a.message_number == 4
+
def test_fints3_constructor_auto_segmentheader():
a = HNHBS1(7)
assert a.message_number == 7
+
def test_data_element_constructor_double_arg():
with pytest.raises(TypeError):
HNHBS1(8, message_number='9')
+
def test_data_element_setgetdel():
a = HNHBS1()
del a.message_number
assert not a.message_number
+
def test_descriptors_independent():
a = HNHBK3()
b = HNHBK3()
assert a.hbci_version
assert not b.hbci_version
+
def test_parse_container():
a = ['HNHBS', '2', '1', ]
assert h.version == 1
assert h.reference is None
+
def test_segment_class_type_version():
assert HNHBS1.TYPE == 'HNHBS'
assert HNHBS1.VERSION == 1
assert HNHBK3.VERSION == 3
+
def test_find_subclass():
a = [
['HNHBS', '2', '1', ],
clazz = FinTS3Segment.find_subclass(a)
assert clazz is HNHBS1
+
def test_nested_output_evalable():
import fints.segments, fints.formals
- a = SegmentSequence([fints.segments.message.HNHBK3(header=fints.formals.SegmentHeader('HNHBK', 1, 3, None), message_size='000000000428', hbci_version=300, dialogue_id='430711670077=043999659571CN9D=', message_number=2, reference_message=fints.formals.ReferenceMessage(dialogue_id='430711670077=043999659571CN9D=', message_number=2)), fints.segments.message.HNVSK3(header=fints.formals.SegmentHeader('HNVSK', 998, 3, None), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='998', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_datetime=fints.formals.SecurityDateTime(date_time_type='1', date=None, time=None), encryption_algorithm=fints.formals.EncryptionAlgorithm(usage_encryption='2', operation_mode='2', encryption_algorithm='13', algorithm_parameter_value=b'00000000', algorithm_parameter_name='5', algorithm_parameter_iv_name='1', algorithm_parameter_iv_value=None), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0), compression_function='0', certificate=fints.formals.Certificate(certificate_type=None, certificate_content=None)), fints.segments.message.HNVSD1(header=fints.formals.SegmentHeader('HNVSD', 999, 1, None), data=SegmentSequence([fints.segments.message.HNSHK4(header=fints.formals.SegmentHeader('HNSHK', 2, 4, None), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='999', security_reference='9166926', security_application_area='1', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_reference_number=1, security_datetime=fints.formals.SecurityDateTime(date_time_type='1', date=None, time=None), hash_algorithm=fints.formals.HashAlgorithm(usage_hash='1', hash_algorithm='999', algorithm_parameter_name='1', algorithm_parameter_value=None), signature_algorithm=fints.formals.SignatureAlgorithm(usage_signature='6', signature_algorithm='10', operation_mode='16'), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0), certificate=fints.formals.Certificate(certificate_type=None, certificate_content=None)), fints.segments.base.FinTS3Segment(header=fints.formals.SegmentHeader('HIRMG', 3, 2, None), _additional_data=[['0010', None, 'Nachricht entgegengenommen.'], ['0100', None, 'Dialog beendet.']]), fints.segments.base.FinTS3Segment(header=fints.formals.SegmentHeader('HNSHA', 4, 2, None), _additional_data=['9166926'])])), fints.segments.message.HNHBS1(header=fints.formals.SegmentHeader('HNHBS', 5, 1, None), message_number=2)])
+ a = SegmentSequence([fints.segments.message.HNHBK3(header=fints.formals.SegmentHeader('HNHBK', 1, 3, None), message_size='000000000428', hbci_version=300,
+ dialogue_id='430711670077=043999659571CN9D=', message_number=2,
+ reference_message=fints.formals.ReferenceMessage(dialogue_id='430711670077=043999659571CN9D=',
+ message_number=2)),
+ fints.segments.message.HNVSK3(header=fints.formals.SegmentHeader('HNVSK', 998, 3, None),
+ security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1),
+ security_function='998', security_role='1',
+ security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2',
+ cid=None,
+ identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'),
+ security_datetime=fints.formals.SecurityDateTime(date_time_type='1', date=None, time=None),
+ encryption_algorithm=fints.formals.EncryptionAlgorithm(usage_encryption='2', operation_mode='2',
+ encryption_algorithm='13',
+ algorithm_parameter_value=b'00000000',
+ algorithm_parameter_name='5',
+ algorithm_parameter_iv_name='1',
+ algorithm_parameter_iv_value=None),
+ key_name=fints.formals.KeyName(
+ bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'),
+ user_id='hermes', key_type='S', key_number=0, key_version=0), compression_function='0',
+ certificate=fints.formals.Certificate(certificate_type=None, certificate_content=None)),
+ fints.segments.message.HNVSD1(header=fints.formals.SegmentHeader('HNVSD', 999, 1, None), data=SegmentSequence([
+ fints.segments.message.HNSHK4(
+ header=fints.formals.SegmentHeader(
+ 'HNSHK', 2,
+ 4, None),
+ security_profile=fints.formals.SecurityProfile(
+ security_method='PIN',
+ security_method_version=1),
+ security_function='999',
+ security_reference='9166926',
+ security_application_area='1',
+ security_role='1',
+ security_identification_details=fints.formals.SecurityIdentificationDetails(
+ identified_role='2',
+ cid=None,
+ identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'),
+ security_reference_number=1,
+ security_datetime=fints.formals.SecurityDateTime(
+ date_time_type='1',
+ date=None,
+ time=None),
+ hash_algorithm=fints.formals.HashAlgorithm(
+ usage_hash='1',
+ hash_algorithm='999',
+ algorithm_parameter_name='1',
+ algorithm_parameter_value=None),
+ signature_algorithm=fints.formals.SignatureAlgorithm(
+ usage_signature='6',
+ signature_algorithm='10',
+ operation_mode='16'),
+ key_name=fints.formals.KeyName(
+ bank_identifier=fints.formals.BankIdentifier(
+ country_identifier='280',
+ bank_code='15050500'),
+ user_id='hermes',
+ key_type='S',
+ key_number=0,
+ key_version=0),
+ certificate=fints.formals.Certificate(
+ certificate_type=None,
+ certificate_content=None)),
+ fints.segments.base.FinTS3Segment(
+ header=fints.formals.SegmentHeader(
+ 'HIRMG', 3,
+ 2, None),
+ _additional_data=[
+ ['0010',
+ None,
+ 'Nachricht entgegengenommen.'],
+ ['0100',
+ None,
+ 'Dialog beendet.']]),
+ fints.segments.base.FinTS3Segment(
+ header=fints.formals.SegmentHeader(
+ 'HNSHA', 4,
+ 2, None),
+ _additional_data=[
+ '9166926'])])),
+ fints.segments.message.HNHBS1(header=fints.formals.SegmentHeader('HNHBS', 5, 1, None), message_number=2)])
output = StringIO()
a.print_nested(stream=output)