Sending SEPA transfers

Simple mode

You can create a simple SEPA transfer using this convenient client method:

class fints.client.FinTS3Client(bank_identifier, user_id, customer_id=None, from_data: bytes = None, system_id=None, product_id=None, product_version='5.0.0', mode=FinTSClientMode.INTERACTIVE)[source]
simple_sepa_transfer(account: SEPAAccount, iban: str, bic: str, recipient_name: str, amount: Decimal, account_name: str, reason: str, instant_payment=False, endtoend_id='NOTPROVIDED')[source]

Simple SEPA transfer.

Parameters:
  • account – SEPAAccount to start the transfer from.

  • iban – Recipient’s IBAN

  • bic – Recipient’s BIC (Can be None if domestic)

  • recipient_name – Recipient name

  • amount – Amount as a Decimal

  • account_name – Sender account name

  • reason – Transfer reason

  • instant_payment – Whether to use instant payment (defaults to False)

  • endtoend_id – End-to-end-Id (defaults to NOTPROVIDED)

Returns:

Returns either a NeedRetryResponse or NeedVOPResponse or TransactionResponse

The return value may be a NeedVOPResponse in which case you need to call approve_vop_response to proceed.

At any point, you might receive a NeedTANResponse. You should then enter a TAN, read our chapter Working with TANs to find out more.

class fints.client.FinTS3PinTanClient(bank_identifier, user_id, pin, server, customer_id=None, tan_medium=None, *args, **kwargs)[source]
approve_vop_response(challenge: NeedVOPResponse)[source]

Approves an operation that had a non-match VoP (verification of payee) response.

Parameters:

challenge – NeedVOPResponse to respond to

Returns:

New response after sending VOP response

Advanced mode

If you want to use advanced methods, you can supply your own SEPA XML:

class fints.client.FinTS3Client(bank_identifier, user_id, customer_id=None, from_data: bytes = None, system_id=None, product_id=None, product_version='5.0.0', mode=FinTSClientMode.INTERACTIVE)[source]
sepa_transfer(account: SEPAAccount, pain_message: str, multiple=False, control_sum=None, currency='EUR', book_as_single=False, pain_descriptor='urn:iso:std:iso:20022:tech:xsd:pain.001.001.03', instant_payment=False)[source]

Custom SEPA transfer.

Parameters:
  • account – SEPAAccount to send the transfer from.

  • pain_message – SEPA PAIN message containing the transfer details.

  • multiple – Whether this message contains multiple transfers.

  • control_sum – Sum of all transfers (required if there are multiple)

  • currency – Transfer currency

  • book_as_single – Kindly ask the bank to put multiple transactions as separate lines on the bank statement (defaults to False)

  • pain_descriptor – URN of the PAIN message schema used.

  • instant_payment – Whether this is an instant transfer (defaults to False)

Returns:

Returns either a NeedRetryResponse or TransactionResponse

Full example

client = FinTS3PinTanClient(...)
minimal_interactive_cli_bootstrap(client)

with client:
    if client.init_tan_response:
        print("A TAN is required", client.init_tan_response.challenge)

        if getattr(client.init_tan_response, 'challenge_hhduc', None):
            try:
                terminal_flicker_unix(client.init_tan_response.challenge_hhduc)
            except KeyboardInterrupt:
                pass

        tan = input('Please enter TAN:')
        client.send_tan(client.init_tan_response, tan)

    res = client.simple_sepa_transfer(
        account=accounts[0],
        iban='DE12345',
        bic='BIC12345',
        amount=Decimal('7.00'),
        recipient_name='Foo',
        account_name='Test',
        reason='Birthday gift',
        endtoend_id='NOTPROVIDED',
    )

    while isinstance(res, NeedTANResponse | NeedVOPResponse):
        if isinstance(res, NeedTANResponse):
            print("A TAN is required", res.challenge)

            if getattr(res, 'challenge_hhduc', None):
                try:
                    terminal_flicker_unix(res.challenge_hhduc)
                except KeyboardInterrupt:
                    pass

            if result.decoupled:
                tan = input('Please press enter after confirming the transaction in your app:')
            else:
                tan = input('Please enter TAN:')
            res = client.send_tan(res, tan)
        elif isinstance(res, NeedVOPResponse):
            if res.vop_result.vop_single_result.result == "RCVC":
                print("Payee name is an exact match")
                if res.vop_result.vop_single_result.other_identification:
                    print("Other info retrieved by bank:", res.vop_result.vop_single_result.other_identification)
            elif res.vop_result.vop_single_result.result == "RVMC":
                print("Payee name is a close match")
                print("Name retrieved by bank:", res.vop_result.vop_single_result.close_match_name)
                if res.vop_result.vop_single_result.other_identification:
                    print("Other info retrieved by bank:", res.vop_result.vop_single_result.other_identification)
            elif res.vop_result.vop_single_result.result == "RVNM":
                print("Payee name does not match match")
            elif res.vop_result.vop_single_result.result == "RVNA":
                print("Payee name could not be verified")
                print("Reason:", res.vop_result.vop_single_result.na_reason)
            elif res.vop_result.vop_single_result.result == "PDNG":
                print("Payee name could not be verified (pending state, can't be handled by this library)")
            print("Do you want to continue? Your bank will not be liable if the money ends up in the wrong place.")
            input('Please press enter to confirm or Ctrl+C to cancel')
            res = client.approve_vop_response(res)

    print(res.status)
    print(res.responses)