schemaFolgtDenAngabenDerBank(); $this->unbekannteFormateFuehrenZuNull(); $this->dkFormatIstSchemakonform(); $this->dasFormatStehtImNamensraum(); } private function schemaFolgtDenAngabenDerBank(): void { $this->assertEquals( SepaXml::SCHEMA_PAIN_001_001_03, SepaXml::schemaWaehlen([]), 'Ohne Angabe der Bank bleibt es beim Vorzugsformat' ); $this->assertEquals( SepaXml::SCHEMA_PAIN_001_003_03, SepaXml::schemaWaehlen([SepaXml::SCHEMA_PAIN_001_003_03, SepaXml::SCHEMA_PAIN_001_003_03]), 'Führt die Bank nur das DK-Format, wird es genommen' ); $this->assertEquals( SepaXml::SCHEMA_PAIN_001_001_03, SepaXml::schemaWaehlen([SepaXml::SCHEMA_PAIN_001_003_03, SepaXml::SCHEMA_PAIN_001_001_03]), 'Bei mehreren Formaten hat das ISO-Format Vorrang' ); // Manche Banken hängen dem Namensraum eine Fassung des Deutschen // Kreditgewerbes an – der Vergleich muss das aushalten. $this->assertEquals( SepaXml::SCHEMA_PAIN_001_001_03, SepaXml::schemaWaehlen([SepaXml::SCHEMA_PAIN_001_001_03 . '_GBIC_4']), 'Der Zusatz _GBIC_4 stört den Vergleich nicht' ); } private function unbekannteFormateFuehrenZuNull(): void { $this->assertNull( SepaXml::schemaWaehlen(['urn:iso:std:iso:20022:tech:xsd:pain.001.001.09']), 'Führt die Bank nur Formate, die hier niemand erzeugt, kommt null zurück' ); } private function dkFormatIstSchemakonform(): void { foreach ($this->auftraege() as $bezeichnung => $auftrag) { $xml = SepaXml::ueberweisung($auftrag, SepaXml::SCHEMA_PAIN_001_003_03); $dokument = new DOMDocument(); $dokument->loadXML($xml); $vorher = libxml_use_internal_errors(true); $gueltig = $dokument->schemaValidate(self::XSD); $meldungen = array_map( static fn(LibXMLError $fehler): string => trim($fehler->message), libxml_get_errors() ); libxml_clear_errors(); libxml_use_internal_errors($vorher); $this->assert( $gueltig, 'pain.001.003.03 (' . $bezeichnung . ') ist schemakonform' . ($gueltig ? '' : ': ' . implode(' | ', $meldungen)) ); } } private function dasFormatStehtImNamensraum(): void { $auftrag = $this->auftraege()['mit BIC']; $iso = SepaXml::ueberweisung($auftrag, SepaXml::SCHEMA_PAIN_001_001_03); $dk = SepaXml::ueberweisung($auftrag, SepaXml::SCHEMA_PAIN_001_003_03); $this->assertEquals( SepaXml::SCHEMA_PAIN_001_001_03, (string)(simplexml_load_string($iso)?->getDocNamespaces()[''] ?? ''), 'Der Namensraum des ISO-Formats' ); $this->assertEquals( SepaXml::SCHEMA_PAIN_001_003_03, (string)(simplexml_load_string($dk)?->getDocNamespaces()[''] ?? ''), 'Der Namensraum des DK-Formats' ); // Ohne ausdrückliches Format bleibt es beim ISO-Format – der Aufruf aus // dem Zahlungsverkehr setzt es sonst still um. $this->assert( str_contains(SepaXml::ueberweisung($auftrag), 'pain.001.001.03'), 'Ohne Angabe wird weiterhin pain.001.001.03 erzeugt' ); } /** @return array> */ private function auftraege(): array { return [ 'mit BIC' => [ 'absender_name' => 'Max Mustermann', 'absender_iban' => 'DE02 1203 0000 0000 2020 51', 'absender_bic' => 'BYLADEM1001', 'empfaenger_name' => 'Stadtwerke Größenwahn', 'empfaenger_iban' => 'DE02500105170137075030', 'empfaenger_bic' => 'INGDDEFFXXX', 'betrag' => 123.45, 'waehrung' => 'EUR', 'verwendungszweck' => 'Abschlag Strom & Wasser 09/2026', 'ausfuehrung_am' => '2026-09-05', ], 'ohne BIC' => [ 'absender_name' => 'Max Mustermann', 'absender_iban' => 'DE02120300000000202051', 'empfaenger_name' => 'Stadtwerke', 'empfaenger_iban' => 'DE02500105170137075030', 'betrag' => 1.0, 'verwendungszweck' => '', 'ausfuehrung_am' => null, ], ]; } }