diff --git a/Tests/Unit/Protocol/BPDReparseTest.php b/Tests/Unit/Protocol/BPDReparseTest.php new file mode 100644 index 00000000..27cee6e0 --- /dev/null +++ b/Tests/Unit/Protocol/BPDReparseTest.php @@ -0,0 +1,66 @@ +assertNull($bpd->getLatestSupportedParameters('DIKKUS')); + + $bpd->reparseAnonymousSegments(); + + /** @var DIKKUSv2 $dikkus */ + $dikkus = $bpd->getLatestSupportedParameters('DIKKUS'); + $this->assertInstanceOf(DIKKUSv2::class, $dikkus); + $this->assertSame(90, $dikkus->getParameter()->speicherzeitraum); + // Still unknown segments are left alone. + $this->assertInstanceOf(AnonymousSegment::class, $bpd->parameters['HIXYZS'][1]); + } + + public function testPersistedInstanceIsReparsedOnLoad() + { + $options = new FinTsOptions(); + $options->url = 'https://bank.example/fints'; + $options->bankCode = '12345678'; + $options->productName = 'TEST'; + $options->productVersion = '1.0'; + $credentials = Credentials::create('user', 'pin'); + + $persisted = serialize([2, self::bpdWithAnonymousSegments(), null, null, null, null, null, null, 1]); + $fints = FinTs::new($options, $credentials, $persisted); + + $this->assertInstanceOf(DIKKUSv2::class, $fints->getBpd()->getLatestSupportedParameters('DIKKUS')); + } + + private static function bpdWithAnonymousSegments(): BPD + { + $bpd = new BPD(); + $bpd->hibpa = HIBPAv3::parse("HIBPA:4:3:3+10+280:12345678+Testbank+1+1+300+500'"); + $dikkus = Parser::parseAnonymousSegment(self::RAW_DIKKUS); + $unknown = Parser::parseAnonymousSegment(self::RAW_UNKNOWN); + $bpd->parameters['DIKKUS'][$dikkus->getVersion()] = $dikkus; + $bpd->parameters['HIXYZS'][$unknown->getVersion()] = $unknown; + return $bpd; + } +} diff --git a/src/FinTs.php b/src/FinTs.php index aac493e6..1ee0ecd9 100644 --- a/src/FinTs.php +++ b/src/FinTs.php @@ -235,6 +235,11 @@ private function loadPersistedInstanceVersion2(array $data): void $this->dialogId, $this->messageNumber, ) = $data; + + // The persisted BPD may predate segments that this library version implements. + if ($this->bpd !== null) { + $this->bpd->reparseAnonymousSegments(); + } } /** @noinspection PhpUnused */ diff --git a/src/Protocol/BPD.php b/src/Protocol/BPD.php index ec9fbd30..44b969ce 100644 --- a/src/Protocol/BPD.php +++ b/src/Protocol/BPD.php @@ -11,6 +11,7 @@ use Fhp\Segment\SegmentInterface; use Fhp\Segment\TAN\HITANS; use Fhp\Segment\VPP\HIVPPSv1; +use Fhp\Syntax\Parser; /** * Segmentfolge: Bankparameterdaten (Version 3) @@ -70,6 +71,27 @@ public function getBankCode() return $this->hibpa->kreditinstitutskennung->kreditinstitutscode; } + /** + * Parses parameter segments again that were stored as {@link AnonymousSegment} because the library version that + * received them did not implement them yet. Persisted BPD survive library upgrades (the bank only resends the BPD + * when *its* version changes), so without this, a business transaction that a newer library version supports + * would still look unsupported to {@link getLatestSupportedParameters()} until the bank happens to bump the BPD. + */ + public function reparseAnonymousSegments(): void + { + foreach ($this->parameters as $type => $versions) { + foreach ($versions as $version => $segment) { + if (!$segment instanceof AnonymousSegment) { + continue; + } + $reparsed = Parser::detectAndParseSegment((string) $segment); + if (!$reparsed instanceof AnonymousSegment) { + $this->parameters[$type][$version] = $reparsed; + } + } + } + } + public function getBankName() { return $this->hibpa->kreditinstitutsbezeichnung;