Fix #54: Überweisung im SEPA-Format der Bank

Die Nachricht wurde fest als pain.001.001.03 erzeugt. Banken, die nur das
ältere DK-Format pain.001.003.03 führen, weisen die Überweisung damit schon
beim Einreichen ab, noch vor der TAN.

FinTsUeberweisung liest die unterstützten Formate nach der Anmeldung aus dem
Segment HISPAS; SepaXml::schemaWaehlen() bestimmt daraus den Namensraum –
bevorzugt ISO, sonst DK. Für die Einzelüberweisung sind beide Fassungen im
Aufbau gleich, es genügt der andere Namensraum. Das DK-Schema liegt der
Testsuite bei, damit die Nachricht wirklich dagegen geprüft wird.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013iURiWYZJQYVGQNXFFGyWu
This commit is contained in:
fds2610-bot 2026-09-01 18:22:04 +02:00
parent 2f524c72da
commit 3444f8f6ec
5 changed files with 711 additions and 6 deletions

View file

@ -1,5 +1,22 @@
# Änderungen
## Zahlungsverkehr: SEPA-Format der Bank (01.09.2026)
Die Überweisung wurde bisher immer als `pain.001.001.03` eingereicht. Banken, die nur
das ältere DK-Format `pain.001.003.03` führen, weisen sie damit ab, noch bevor eine TAN
angefordert wird ("The bank does not support the XML schema …").
`FinTsUeberweisung` liest die unterstützten Formate jetzt nach der Anmeldung aus den
Bankparameterdaten (Segment HISPAS) und `SepaXml::schemaWaehlen()` bestimmt daraus den
Namensraum der Nachricht – bevorzugt das ISO-Format, sonst das DK-Format. Der Zusatz
`_GBIC_x`, den manche Banken anhängen, stört den Vergleich nicht. Meldet die Bank
ausschließlich Formate, die der Finanzmanager nicht erzeugt, nennt die Fehlermeldung sie
jetzt beim Namen, statt die Bibliothek durchschlagen zu lassen.
Für die Einzelüberweisung sind beide Fassungen im Aufbau gleich; es genügt der andere
Namensraum. `tests/schemata/pain.001.003.03.xsd` (DFÜ-Abkommen Anlage 3, Version 2.7) liegt
der Testsuite bei, damit die erzeugte Nachricht wirklich gegen das Schema geprüft wird.
## Adressverwaltung in der Verwaltung, Typeahead im Zahlungsverkehr (31.08.2026)
Das Adressbuch (bisher nur über die Verträge-Kachel erreichbar) bekommt in der Verwaltung

View file

@ -12,6 +12,7 @@ use Fhp\Model\SEPAAccount;
use Fhp\Model\TanRequestChallengeImage;
use Fhp\Options\Credentials;
use Fhp\Options\FinTsOptions;
use Fhp\Segment\SPA\HISPAS;
use Finanzmanager\Auth;
use Finanzmanager\Models\Bankzugang;
use Finanzmanager\Models\Einstellung;
@ -193,7 +194,16 @@ class FinTsUeberweisung
return $this->fehler('Das Auftragskonto ist nicht mehr lesbar. Bitte erneut senden.');
}
$xml = SepaXml::ueberweisung($this->auftragsdaten($auftrag, $sepa));
$formate = $this->sepaFormate($fints);
$schema = SepaXml::schemaWaehlen($formate);
if ($schema === null) {
return $this->fehler(
'Die Bank nimmt nur SEPA-Formate an, die der Finanzmanager nicht erzeugt: '
. implode(', ', $formate)
);
}
$xml = SepaXml::ueberweisung($this->auftragsdaten($auftrag, $sepa), $schema);
$aktion = SendSEPATransfer::create($sepa, $xml);
$fints->execute($aktion);
@ -211,6 +221,28 @@ class FinTsUeberweisung
return $this->fehler('Der Auftrag konnte nicht abgeschlossen werden.');
}
/**
* Die SEPA-Datenformate, die die Bank annimmt.
*
* Sie stehen in den Bankparameterdaten (Segment HISPAS) und liegen nach der
* Anmeldung vor. Ältere Häuser führen ausschließlich das DK-Format
* pain.001.003.03 und weisen alles andere schon beim Einreichen ab.
*
* @return list<string>
*/
private function sepaFormate(FinTs $fints): array
{
try {
$hispas = $fints->getBpd()->getLatestSupportedParameters('HISPAS');
} catch (\Throwable) {
return [];
}
return $hispas instanceof HISPAS
? array_values($hispas->getParameter()->getUnterstuetzteSEPADatenformate())
: [];
}
/**
* @param array<string,mixed> $auftrag
* @return array<string,mixed>

View file

@ -13,12 +13,19 @@ namespace Finanzmanager\Services;
* Einzelüberweisung, um die es hier geht, ist das Format überschaubar und in der
* Spezifikation der Deutschen Kreditwirtschaft vollständig beschrieben.
*
* Erzeugt wird **pain.001.001.03** – die Fassung, die die deutschen Banken für
* FinTS-Überweisungen erwarten.
* Erzeugt wird **pain.001.001.03** oder – wenn die Bank nur das ältere
* DK-Format führt – **pain.001.003.03**. Für die Einzelüberweisung sind beide
* Fassungen im Aufbau gleich (Reihenfolge und Pflichtfelder stimmen überein),
* sie unterscheiden sich nur im Namensraum. Welches Format die Bank annimmt,
* steht im FinTS-Segment HISPAS – siehe `Services\FinTsUeberweisung`.
*/
class SepaXml
{
private const SCHEMA = 'urn:iso:std:iso:20022:tech:xsd:pain.001.001.03';
public const SCHEMA_PAIN_001_001_03 = 'urn:iso:std:iso:20022:tech:xsd:pain.001.001.03';
public const SCHEMA_PAIN_001_003_03 = 'urn:iso:std:iso:20022:tech:xsd:pain.001.003.03';
/** Die Formate, die hier erzeugt werden – in der Reihenfolge des Vorzugs. */
private const SCHEMATA = [self::SCHEMA_PAIN_001_001_03, self::SCHEMA_PAIN_001_003_03];
/**
* @param array{
@ -27,8 +34,9 @@ class SepaXml
* betrag: float, waehrung?: string, verwendungszweck?: string,
* ausfuehrung_am?: ?string
* } $auftrag
* @param string $schema Namensraum der Nachricht, siehe `schemaWaehlen()`.
*/
public static function ueberweisung(array $auftrag): string
public static function ueberweisung(array $auftrag, string $schema = self::SCHEMA_PAIN_001_001_03): string
{
$betrag = number_format(abs((float)$auftrag['betrag']), 2, '.', '');
if ((float)$betrag <= 0.0) {
@ -50,7 +58,7 @@ class SepaXml
$dokument = new \DOMDocument('1.0', 'UTF-8');
$dokument->formatOutput = true;
$wurzel = $dokument->createElementNS(self::SCHEMA, 'Document');
$wurzel = $dokument->createElementNS($schema, 'Document');
$dokument->appendChild($wurzel);
$nachricht = $dokument->createElement('CstmrCdtTrfInitn');
@ -151,6 +159,33 @@ class SepaXml
return (string)$dokument->saveXML();
}
/**
* Das Format, das zu den von der Bank gemeldeten SEPA-Datenformaten passt.
*
* Die Banken melden ihre Formate in HISPAS, manche mit dem Zusatz `_GBIC_4`
* – deshalb der Präfixvergleich. Meldet die Bank nichts, bleibt es beim
* Vorzugsformat; meldet sie nur Formate, die hier nicht erzeugt werden,
* kommt `null` zurück.
*
* @param list<string> $unterstuetzt
*/
public static function schemaWaehlen(array $unterstuetzt): ?string
{
if ($unterstuetzt === []) {
return self::SCHEMA_PAIN_001_001_03;
}
foreach (self::SCHEMATA as $schema) {
foreach ($unterstuetzt as $format) {
if (str_starts_with(trim($format), $schema)) {
return $schema;
}
}
}
return null;
}
private static function kind(\DOMDocument $dokument, \DOMElement $eltern, string $name, string $wert): void
{
// Über createTextNode statt als zweites Argument von createElement:

147
tests/39_SepaSchemaTest.php Normal file
View file

@ -0,0 +1,147 @@
<?php
declare(strict_types=1);
use Finanzmanager\Services\SepaXml;
/**
* Die SEPA-Nachricht im Format, das die Bank annimmt.
*
* Nicht jede Bank führt das ISO-Format pain.001.001.03. Etliche Häuser nehmen
* ausschließlich das ältere DK-Format pain.001.003.03 an und weisen alles
* andere schon beim Einreichen ab ("The bank does not support the XML schema
* …"). Welche Formate eine Bank führt, meldet sie im Segment HISPAS.
*
* Das DK-Schema liegt als `tests/schemata/pain.001.003.03.xsd` bei, damit die
* erzeugte Nachricht hier wirklich geprüft wird und nicht nur auf Textbausteine
* hin abgeklopft.
*/
class SepaSchemaTest extends TestCase
{
protected string $testName = 'Test 39: SEPA-Schema der Überweisung';
private const XSD = __DIR__ . '/schemata/pain.001.003.03.xsd';
public function run(): void
{
$this->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<string,array<string,mixed>> */
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,
],
];
}
}

View file

@ -0,0 +1,474 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Version gemäß DFÜ-Abkommen Anlage 3, Version 2.7, gültig ab November 2013 mit Umsetzung von IBAN Only gemäß EPC SCT 7.0, zudem Erweiterung Service Level auf Externe Codeliste-->
<!-- Mit XMLSpy v2008 am 29.11.2012 von der SIZ GmbH bearbeitet -->
<xs:schema xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.003.03" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:iso:std:iso:20022:tech:xsd:pain.001.003.03" elementFormDefault="qualified">
<xs:element name="Document" type="Document"/>
<xs:complexType name="AccountIdentificationSEPA">
<xs:sequence>
<xs:element name="IBAN" type="IBAN2007Identifier"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="ActiveOrHistoricCurrencyAndAmount_SimpleTypeSEPA">
<xs:restriction base="xs:decimal">
<xs:minInclusive value="0.01"/>
<xs:maxInclusive value="999999999.99"/>
<xs:fractionDigits value="2"/>
<xs:totalDigits value="11"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ActiveOrHistoricCurrencyCode">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{3,3}"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ActiveOrHistoricCurrencyAndAmountSEPA">
<xs:simpleContent>
<xs:extension base="ActiveOrHistoricCurrencyAndAmount_SimpleTypeSEPA">
<xs:attribute name="Ccy" type="ActiveOrHistoricCurrencyCodeEUR" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="ActiveOrHistoricCurrencyCodeEUR">
<xs:restriction base="xs:string">
<xs:enumeration value="EUR"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="AmountTypeSEPA">
<xs:sequence>
<xs:element name="InstdAmt" type="ActiveOrHistoricCurrencyAndAmountSEPA"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="AnyBICIdentifier">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="BICIdentifier">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="BatchBookingIndicator">
<xs:restriction base="xs:boolean"/>
</xs:simpleType>
<xs:complexType name="BranchAndFinancialInstitutionIdentificationSEPA1">
<xs:sequence>
<xs:element name="FinInstnId" type="FinancialInstitutionIdentificationSEPA1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="BranchAndFinancialInstitutionIdentificationSEPA3">
<xs:sequence>
<xs:element name="FinInstnId" type="FinancialInstitutionIdentificationSEPA3"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CashAccountSEPA1">
<xs:sequence>
<xs:element name="Id" type="AccountIdentificationSEPA"/>
<xs:element name="Ccy" type="ActiveOrHistoricCurrencyCode" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CashAccountSEPA2">
<xs:sequence>
<xs:element name="Id" type="AccountIdentificationSEPA"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CategoryPurposeSEPA">
<xs:sequence>
<xs:element name="Cd" type="ExternalCategoryPurpose1Code"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="ChargeBearerTypeSEPACode">
<xs:restriction base="xs:string">
<xs:enumeration value="SLEV"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="CountryCode">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{2,2}"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="CreditTransferTransactionInformationSCT">
<xs:sequence>
<xs:element name="PmtId" type="PaymentIdentificationSEPA"/>
<xs:element name="PmtTpInf" type="PaymentTypeInformationSCT2" minOccurs="0">
<xs:annotation>
<xs:documentation>If used, it is recommended to be used at ‘Payment Information’ level and not at ‘Credit Transfer Transaction Information’ level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Amt" type="AmountTypeSEPA"/>
<xs:element name="ChrgBr" type="ChargeBearerTypeSEPACode" minOccurs="0">
<xs:annotation>
<xs:documentation>It is recommended that this element be specified at ‘Payment Information’ level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="UltmtDbtr" type="PartyIdentificationSEPA1" minOccurs="0">
<xs:annotation>
<xs:documentation>This data element may be present either at ‘Payment Information’ or at ‘Credit Transfer Transaction Information’ level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="CdtrAgt" type="BranchAndFinancialInstitutionIdentificationSEPA1" minOccurs="0"/>
<xs:element name="Cdtr" type="PartyIdentificationSEPA2"/>
<xs:element name="CdtrAcct" type="CashAccountSEPA2"/>
<xs:element name="UltmtCdtr" type="PartyIdentificationSEPA1" minOccurs="0"/>
<xs:element name="Purp" type="PurposeSEPA" minOccurs="0"/>
<xs:element name="RmtInf" type="RemittanceInformationSEPA1Choice" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CreditorReferenceInformationSEPA1">
<xs:sequence>
<xs:element name="Tp" type="CreditorReferenceTypeSEPA"/>
<xs:element name="Ref" type="Max35Text">
<xs:annotation>
<xs:documentation>If a Creditor Reference contains a check digit, the receiving bank is not required to validate this.
If the receiving bank validates the check digit and if this validation fails, the bank may continue its processing and send the transaction to the next party in the chain.
RF Creditor Reference may be used (ISO 11649).</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CreditorReferenceTypeSEPA">
<xs:sequence>
<xs:element name="CdOrPrtry" type="CreditorReferenceTypeCodeSEPA"/>
<xs:element name="Issr" type="Max35Text" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CreditorReferenceTypeCodeSEPA">
<xs:sequence>
<xs:element name="Cd" type="DocumentType3CodeSEPA"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="CustomerCreditTransferInitiationV03">
<xs:sequence>
<xs:element name="GrpHdr" type="GroupHeaderSCT"/>
<xs:element name="PmtInf" type="PaymentInstructionInformationSCT" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="DateAndPlaceOfBirth">
<xs:sequence>
<xs:element name="BirthDt" type="ISODate"/>
<xs:element name="PrvcOfBirth" type="Max35Text" minOccurs="0"/>
<xs:element name="CityOfBirth" type="Max35Text"/>
<xs:element name="CtryOfBirth" type="CountryCode"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="DecimalNumber">
<xs:restriction base="xs:decimal">
<xs:fractionDigits value="17"/>
<xs:totalDigits value="18"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Document">
<xs:sequence>
<xs:element name="CstmrCdtTrfInitn" type="CustomerCreditTransferInitiationV03"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="DocumentType3CodeSEPA">
<xs:restriction base="xs:string">
<xs:enumeration value="SCOR"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalCategoryPurpose1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalOrganisationIdentification1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalPersonIdentification1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalPurpose1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ExternalServiceLevel1Code">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="4"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="FinancialInstitutionIdentificationSEPA1">
<xs:sequence>
<xs:element name="BIC" type="BICIdentifier"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="FinancialInstitutionIdentificationSEPA3">
<xs:sequence>
<xs:choice>
<xs:element name="BIC" type="BICIdentifier"/>
<xs:element name="Othr" type="OthrIdentification"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="OthrIdentification">
<xs:sequence>
<xs:element name="Id" type="OthrIdentificationCode"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="OthrIdentificationCode">
<xs:restriction base="xs:string">
<xs:enumeration value="NOTPROVIDED"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="GenericOrganisationIdentification1">
<xs:sequence>
<xs:element name="Id" type="Max35Text"/>
<xs:element name="SchmeNm" type="OrganisationIdentificationSchemeName1Choice" minOccurs="0"/>
<xs:element name="Issr" type="Max35Text" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GenericPersonIdentification1">
<xs:sequence>
<xs:element name="Id" type="Max35Text"/>
<xs:element name="SchmeNm" type="PersonIdentificationSchemeName1Choice" minOccurs="0"/>
<xs:element name="Issr" type="Max35Text" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GroupHeaderSCT">
<xs:sequence>
<xs:element name="MsgId" type="RestrictedIdentificationSEPA1"/>
<xs:element name="CreDtTm" type="ISODateTime"/>
<xs:element name="NbOfTxs" type="Max15NumericText"/>
<xs:element name="CtrlSum" type="DecimalNumber" minOccurs="0"/>
<xs:element name="InitgPty" type="PartyIdentificationSEPA1"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="IBAN2007Identifier">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{2,2}[0-9]{2,2}[a-zA-Z0-9]{1,30}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ISODate">
<xs:restriction base="xs:date"/>
</xs:simpleType>
<xs:simpleType name="ISODateTime">
<xs:restriction base="xs:dateTime"/>
</xs:simpleType>
<xs:simpleType name="Max140Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="140"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max15NumericText">
<xs:restriction base="xs:string">
<xs:pattern value="[0-9]{1,15}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max35Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="35"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Max70Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="70"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="OrganisationIdentificationSEPAChoice">
<xs:sequence>
<xs:choice>
<xs:element name="BICOrBEI" type="AnyBICIdentifier"/>
<xs:element name="Othr" type="GenericOrganisationIdentification1"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="OrganisationIdentificationSchemeName1Choice">
<xs:sequence>
<xs:choice>
<xs:element name="Cd" type="ExternalOrganisationIdentification1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartySEPAChoice">
<xs:sequence>
<xs:choice>
<xs:element name="OrgId" type="OrganisationIdentificationSEPAChoice">
<xs:annotation>
<xs:documentation>Either ‘BIC or BEI’ or one
occurrence of ‘Other’ is allowed.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="PrvtId" type="PersonIdentificationSEPA1Choice">
<xs:annotation>
<xs:documentation>Either ‘Date and Place of Birth’ or one occurrence of ‘Other’ is allowed.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartyIdentificationSEPA1">
<xs:sequence>
<xs:element name="Nm" type="Max70Text" minOccurs="0">
<xs:annotation>
<xs:documentation>‘Name’ is limited to 70 characters
in length.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Id" type="PartySEPAChoice" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PartyIdentificationSEPA2">
<xs:sequence>
<xs:element name="Nm" type="Max70Text">
<xs:annotation>
<xs:documentation>‘Name’ is limited to 70 characters
in length.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="PstlAdr" type="PostalAddressSEPA" minOccurs="0"/>
<xs:element name="Id" type="PartySEPAChoice" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PaymentIdentificationSEPA">
<xs:sequence>
<xs:element name="InstrId" type="RestrictedIdentificationSEPA1" minOccurs="0"/>
<xs:element name="EndToEndId" type="RestrictedIdentificationSEPA1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PaymentInstructionInformationSCT">
<xs:sequence>
<xs:element name="PmtInfId" type="RestrictedIdentificationSEPA1"/>
<xs:element name="PmtMtd" type="PaymentMethodSCTCode">
<xs:annotation>
<xs:documentation>Only ‘TRF’ is allowed.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="BtchBookg" type="BatchBookingIndicator" minOccurs="0">
<xs:annotation>
<xs:documentation>If present and contains ‘true’, batch booking is requested. If present and contains ‘false’, booking per transaction is requested. If element is not present, pre-agreed customer-to-bank conditions apply.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="NbOfTxs" type="Max15NumericText" minOccurs="0"/>
<xs:element name="CtrlSum" type="DecimalNumber" minOccurs="0"/>
<xs:element name="PmtTpInf" type="PaymentTypeInformationSCT1" minOccurs="0">
<xs:annotation>
<xs:documentation>If used, it is recommended to be used only at ‘Payment Information’ level and not at Credit Transfer Transaction Information’ level.
When Instruction Priority is to be used, ‘Payment Type Information’ must be present at ‘Payment Information’ level. </xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ReqdExctnDt" type="ISODate"/>
<xs:element name="Dbtr" type="PartyIdentificationSEPA2"/>
<xs:element name="DbtrAcct" type="CashAccountSEPA1"/>
<xs:element name="DbtrAgt" type="BranchAndFinancialInstitutionIdentificationSEPA3"/>
<xs:element name="UltmtDbtr" type="PartyIdentificationSEPA1" minOccurs="0">
<xs:annotation>
<xs:documentation>This data element may be present either at ‘Payment Information’ or at ‘Credit Transfer Transaction Information’ level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ChrgBr" type="ChargeBearerTypeSEPACode" minOccurs="0">
<xs:annotation>
<xs:documentation>It is recommended that this element be specified at ‘Payment Information’ level.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="CdtTrfTxInf" type="CreditTransferTransactionInformationSCT" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="PaymentMethodSCTCode">
<xs:restriction base="xs:string">
<xs:enumeration value="TRF"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="PaymentTypeInformationSCT1">
<xs:sequence>
<xs:element name="InstrPrty" type="Priority2Code" minOccurs="0">
<xs:annotation>
<xs:documentation>If present, pre-agreed customer-to-bank conditions apply.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="SvcLvl" type="ServiceLevelSEPA"/>
<xs:element name="CtgyPurp" type="CategoryPurposeSEPA" minOccurs="0">
<xs:annotation>
<xs:documentation>Depending on the agreement between the Originator and the Originator Bank, ‘Category Purpose’ may be forwarded to the Beneficiary Bank.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PaymentTypeInformationSCT2">
<xs:sequence>
<xs:element name="SvcLvl" type="ServiceLevelSEPA"/>
<xs:element name="CtgyPurp" type="CategoryPurposeSEPA" minOccurs="0">
<xs:annotation>
<xs:documentation>Depending on the agreement between the Originator and the Originator Bank, ‘Category Purpose’ may be forwarded to the Beneficiary Bank.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PersonIdentificationSEPA1Choice">
<xs:sequence>
<xs:choice>
<xs:element name="DtAndPlcOfBirth" type="DateAndPlaceOfBirth"/>
<xs:element name="Othr" type="GenericPersonIdentification1"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PersonIdentificationSchemeName1Choice">
<xs:sequence>
<xs:choice>
<xs:element name="Cd" type="ExternalPersonIdentification1Code"/>
<xs:element name="Prtry" type="Max35Text"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PostalAddressSEPA">
<xs:sequence>
<xs:element name="Ctry" type="CountryCode" minOccurs="0"/>
<xs:element name="AdrLine" type="Max70Text" minOccurs="0" maxOccurs="2"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="Priority2Code">
<xs:restriction base="xs:string">
<xs:enumeration value="HIGH"/>
<xs:enumeration value="NORM"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="PurposeSEPA">
<xs:sequence>
<xs:element name="Cd" type="ExternalPurpose1Code">
<xs:annotation>
<xs:documentation>Only codes from the ISO 20022 ExternalPurposeCode list are allowed.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RemittanceInformationSEPA1Choice">
<xs:sequence>
<xs:choice>
<xs:element name="Ustrd" type="Max140Text"/>
<xs:element name="Strd" type="StructuredRemittanceInformationSEPA1"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ServiceLevelSEPA">
<xs:sequence>
<xs:element name="Cd" type="ExternalServiceLevel1Code"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="StructuredRemittanceInformationSEPA1">
<xs:sequence>
<xs:element name="CdtrRefInf" type="CreditorReferenceInformationSEPA1" minOccurs="0">
<xs:annotation>
<xs:documentation>When present, the receiving bank is not obliged to validate the the reference information. </xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="RestrictedIdentificationSEPA1">
<xs:restriction base="xs:string">
<xs:pattern value="([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|'| ]){1,35}"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>