Skip to content

VoP: asynchronous result (polling / Aufsetzpunkt) not handled — transfers fail with 3905/3945 at Atruvia banks (GLS/VR) #220

Description

@payne1979

VoP: asynchronous result (polling / Aufsetzpunkt) not handled — transfers fail with 3905/3945 at Atruvia banks (GLS/VR)

python-fints version: 5.0.0
Bank: Atruvia-hosted (GLS Bank / Volksbanken Raiffeisenbanken), e.g. BIC GENODEM1GLS / GENODED1BRS
Context: Verification of Payee (VoP) became mandatory in the EU on 2025-10-09.

Summary

A normal client.sepa_transfer(...) (HKCCS, and likewise multiple=True/HKCCM) cannot be
completed at these banks. The library does send the VoP request segment HKVPP
(_find_vop_format_for_segment returns the pain.002.001.10 descriptor and
_need_twostep_tan_for_segment is True), but the bank does not return the VoP result
inline. Instead it returns:

  • an empty HIVPP1 (vop_single_result → empty EVPE(), result is None),
  • response code 3040 with an Aufsetzpunkt/touchdown token (e.g. 'staticscrollref') on the HKVPP segment,
  • code 3905 ("Es wurde keine Challenge erzeugt"),
  • code 3945 ("Freigabe ohne VOP-Bestätigung nicht möglich").

Because _send_pay_with_possible_retry expects the VoP result inline in the first
response, it never produces a usable NeedVOPResponse and the transfer dead-ends on 3945.

Root cause

For these banks the VoP result is delivered asynchronously. The first HKVPP only
acknowledges the request and returns a polling_id, a wait_for_seconds hint and a touchdown
(3040). The actual result must be polled: re-send HKVPP with both polling_id
and the aufsetzpunkt token, honoring wait_for_seconds, until the response's HIVPP1
carries a payment_status_report (a pain.002.001.10) plus a vop_id — signalled by response
code 3090 ("Ergebnis des Namensabgleichs prüfen").

Important details discovered:

  • The poll must include polling_id and aufsetzpunkt; sending only one yields
    9210 ("VOP-Auftrag ungültig").
  • The per-transaction result lives in the pain.002 TxInfAndSts/TxSts
    (RCVC/RVMC/RVNM/RVNA), not in EVPE. For a close match (RVMC) the bank's
    on-file name is in the per-transaction TxInfAndSts/StsRsnInf/AddtlInf (the group-level
    StsRsnInf only contains the static legend). HIVPPS.parameter.report_complete == 'V'.
  • Confirmation works via approve_vop_responseHKVPA1(vop_id) (this part is fine once a
    correct vop_id is available). The re-submitted order must be byte-identical to the original
    (same pain.001), otherwise 9010 ("Auftrag weicht vom Ursprungsauftrag ab").
  • Minor robustness bug: when HIVPP1.vop_single_result is empty, the current
    if vop_result.result in (...) path risks an AttributeError on None.

Sanitized log (initial send + one poll)

3905 - Es wurde keine Challenge erzeugt.
3040 - Es liegen weitere Informationen vor. (['staticscrollref'])
3945 - Freigabe ohne VOP-Bestätigung nicht möglich.
HIVPP1: vop_id=None polling_id=<set> wait_for_seconds=2 payment_status_report=0 bytes  vop_single_result=EVPE()   # empty

# after sleeping wait_for_seconds and re-sending HKVPP(polling_id=..., aufsetzpunkt='staticscrollref'):
3090 - Ergebnis des Namensabgleichs prüfen.
HIVPP1: vop_id=<set> polling_id=None payment_status_report=<~3.6 kB pain.002.001.10>   # result ready

Suggested fix

In _send_pay_with_possible_retry (and the VoP branch generally): when the first HIVPP1
has no inline single result but provides a polling_id / wait_for_seconds (and/or a 3040
touchdown), poll HKVPP (with polling_id and aufsetzpunkt) until a
payment_status_report + vop_id are present, then return a NeedVOPResponse carrying that
vop_id (and ideally the parsed pain.002 per-transaction results). Also guard against an
empty EVPE (vop_single_result is None).

I have a working implementation of exactly this polling flow against a GLS/Atruvia account
(single HKCCS and batch HKCCM, real transfers executed) and am happy to turn it into a PR if
the approach looks right.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions