Skip to content

Commit ec50802

Browse files
Merge pull request #232 from moorl/Extended-Validator
Extended SchemaValidator
2 parents 3a854b3 + 34fd1ab commit ec50802

13 files changed

Lines changed: 12387 additions & 3 deletions

doc/schema/camt.052.001.08.xsd

Lines changed: 2070 additions & 0 deletions
Large diffs are not rendered by default.

doc/schema/camt.053.001.08.xsd

Lines changed: 2070 additions & 0 deletions
Large diffs are not rendered by default.

doc/schema/pain.001.001.03.xsd

Lines changed: 921 additions & 0 deletions
Large diffs are not rendered by default.

doc/schema/pain.001.001.09.xsd

Lines changed: 1114 additions & 0 deletions
Large diffs are not rendered by default.

doc/schema/pain.001.001.09_GBIC_5.xsd

Lines changed: 2411 additions & 0 deletions
Large diffs are not rendered by default.

doc/schema/pain.008.001.02.xsd

Lines changed: 625 additions & 0 deletions
Large diffs are not rendered by default.

doc/schema/pain.008.001.08_GBIC_5.xsd

Lines changed: 2690 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace EbicsApi\Ebics\Builders\Document;
4+
5+
use DateTime;
6+
use EbicsApi\Ebics\Contracts\PostalAddressInterface;
7+
use EbicsApi\Ebics\Models\CustomerCreditTransfer;
8+
use InvalidArgumentException;
9+
10+
/**
11+
* CustomerCreditTransferBuilder
12+
* Corresponds to EbicsApi\Ebics\Builders\CustomerCreditTransfer\CustomerCreditTransferBuilder but with namespaces
13+
*
14+
* @license http://www.opensource.org/licenses/mit-license.html MIT License
15+
* @author Jan-Philipp Georg (moori)
16+
*/
17+
final class CustomerCreditTransferBuilder extends DocumentBuilder
18+
{
19+
public function createInstance(
20+
string $debitorFinInstBIC,
21+
string $debitorIBAN,
22+
string $debitorName,
23+
?DateTime $executionDate = null,
24+
bool $batchBooking = true,
25+
?string $msgId = null,
26+
?string $paymentReference = null,
27+
?string $chargeBearer = null
28+
): self {
29+
$this->instance = new CustomerCreditTransfer();
30+
$now = new DateTime();
31+
32+
$doc = $this->el('Document');
33+
$doc->setAttributeNS(
34+
'http://www.w3.org/2000/xmlns/',
35+
'xmlns:xsi',
36+
'http://www.w3.org/2001/XMLSchema-instance'
37+
);
38+
$doc->setAttributeNS(
39+
'http://www.w3.org/2001/XMLSchema-instance',
40+
'xsi:schemaLocation',
41+
$this->schemaLocation()
42+
);
43+
$this->instance->appendChild($doc);
44+
45+
$ccti = $this->el('CstmrCdtTrfInitn');
46+
$doc->appendChild($ccti);
47+
48+
$grpHdr = $this->el('GrpHdr');
49+
$ccti->appendChild($grpHdr);
50+
$grpHdr->appendChild(
51+
$this->el('MsgId', $msgId ?: $this->randomService->uniqueIdWithDate('msg'))
52+
);
53+
$grpHdr->appendChild($this->el('CreDtTm', $now->format('Y-m-d\TH:i:s\Z')));
54+
$grpHdr->appendChild($this->el('NbOfTxs', '0'));
55+
$grpHdr->appendChild($this->el('CtrlSum', '0'));
56+
57+
$initg = $this->el('InitgPty');
58+
$initg->appendChild($this->el('Nm', $debitorName));
59+
$grpHdr->appendChild($initg);
60+
61+
$pmtInf = $this->el('PmtInf');
62+
$ccti->appendChild($pmtInf);
63+
64+
$pmtInf->appendChild(
65+
$this->el('PmtInfId', $paymentReference ?: $this->randomService->uniqueIdWithDate('pmt'))
66+
);
67+
$pmtInf->appendChild($this->el('PmtMtd', 'TRF'));
68+
$pmtInf->appendChild($this->el('BtchBookg', $batchBooking ? 'true' : 'false'));
69+
$pmtInf->appendChild($this->el('NbOfTxs', '0'));
70+
$pmtInf->appendChild($this->el('CtrlSum', '0'));
71+
72+
$pmtTpInf = $this->el('PmtTpInf');
73+
$svcLvl = $this->el('SvcLvl');
74+
$svcLvl->appendChild($this->el('Cd', 'SEPA'));
75+
$pmtTpInf->appendChild($svcLvl);
76+
$pmtInf->appendChild($pmtTpInf);
77+
78+
$pmtInf->appendChild($this->buildReqdExctnDt($executionDate ?: $now));
79+
80+
$dbtr = $this->el('Dbtr');
81+
$dbtr->appendChild($this->el('Nm', $debitorName));
82+
$pmtInf->appendChild($dbtr);
83+
84+
$dbtrAcct = $this->el('DbtrAcct');
85+
$id = $this->el('Id');
86+
$id->appendChild($this->el('IBAN', $debitorIBAN));
87+
$dbtrAcct->appendChild($id);
88+
$pmtInf->appendChild($dbtrAcct);
89+
90+
$dbtrAgt = $this->el('DbtrAgt');
91+
$fin = $this->el('FinInstnId');
92+
$fin->appendChild($this->el('BICFI', $debitorFinInstBIC));
93+
$dbtrAgt->appendChild($fin);
94+
$pmtInf->appendChild($dbtrAgt);
95+
96+
$pmtInf->appendChild($this->el('ChrgBr', $chargeBearer ?: 'SLEV'));
97+
98+
return $this;
99+
}
100+
101+
public function addBankTransaction(
102+
string $creditorIBAN,
103+
string $creditorName,
104+
?PostalAddressInterface $postalAddress,
105+
float $amount,
106+
string $currency,
107+
?string $purposeText = null,
108+
?string $endToEndId = null,
109+
?string $purposeCode = null
110+
): self {
111+
if ($currency !== 'EUR') {
112+
throw new InvalidArgumentException('The SEPA transaction is restricted to EUR currency.');
113+
}
114+
115+
$tx = $this->createCreditTransferTransactionElement($amount, null, $endToEndId);
116+
$this->addAmountElement($tx, $amount, $currency);
117+
$this->addCreditor(
118+
$tx,
119+
null,
120+
$creditorIBAN,
121+
$creditorName,
122+
$postalAddress,
123+
$purposeText,
124+
$purposeCode
125+
);
126+
127+
return $this;
128+
}
129+
130+
public function addSEPATransaction(
131+
string $creditorFinInstBIC,
132+
string $creditorIBAN,
133+
string $creditorName,
134+
?PostalAddressInterface $postalAddress,
135+
float $amount,
136+
string $currency,
137+
?string $purposeText = null,
138+
?string $chargeBearer = null,
139+
?string $endToEndId = null,
140+
?string $purposeCode = null
141+
): self {
142+
if ($currency !== 'EUR') {
143+
throw new InvalidArgumentException('The SEPA transaction is restricted to EUR currency.');
144+
}
145+
146+
$tx = $this->createCreditTransferTransactionElement($amount, null, $endToEndId);
147+
148+
$this->addAmountElement($tx, $amount, $currency);
149+
150+
if ($chargeBearer !== null) {
151+
$tx->appendChild($this->el('ChrgBr', $chargeBearer));
152+
}
153+
154+
$this->addCreditor(
155+
$tx,
156+
$creditorFinInstBIC,
157+
$creditorIBAN,
158+
$creditorName,
159+
$postalAddress,
160+
$purposeText,
161+
$purposeCode
162+
);
163+
164+
return $this;
165+
}
166+
167+
public function addForeignTransaction(
168+
string $creditorFinInstBIC,
169+
string $creditorIBAN,
170+
string $creditorName,
171+
?PostalAddressInterface $postalAddress,
172+
float $amount,
173+
string $currency,
174+
?string $purposeText = null,
175+
?string $endToEndId = null,
176+
?string $purposeCode = null
177+
): self {
178+
$tx = $this->createCreditTransferTransactionElement($amount, null, $endToEndId);
179+
$this->addAmountElement($tx, $amount, $currency);
180+
$this->addCreditor(
181+
$tx,
182+
$creditorFinInstBIC,
183+
$creditorIBAN,
184+
$creditorName,
185+
$postalAddress,
186+
$purposeText,
187+
$purposeCode
188+
);
189+
190+
return $this;
191+
}
192+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace EbicsApi\Ebics\Builders\Document;
4+
5+
use DateTime;
6+
use DOMElement;
7+
use EbicsApi\Ebics\Contracts\OrderDataInterface;
8+
use EbicsApi\Ebics\Contracts\PostalAddressInterface;
9+
use EbicsApi\Ebics\Handlers\Traits\XPathTrait;
10+
use EbicsApi\Ebics\Models\DOMDocument;
11+
use EbicsApi\Ebics\Services\DOMHelper;
12+
use EbicsApi\Ebics\Services\RandomService;
13+
14+
/**
15+
* DocumentBuilder with most common methods
16+
* The validation against oder data need namespaced XML documents
17+
*
18+
* @license http://www.opensource.org/licenses/mit-license.html MIT License
19+
* @author Jan-Philipp Georg (moori)
20+
*/
21+
abstract class DocumentBuilder
22+
{
23+
use XPathTrait;
24+
25+
protected RandomService $randomService;
26+
protected ?DOMDocument $instance = null;
27+
protected string $ns;
28+
29+
public function __construct(
30+
protected string $schema = 'pain.001.001.09.xsd'
31+
) {
32+
$this->randomService = new RandomService();
33+
34+
$split = explode('_', $this->schema);
35+
$this->ns = sprintf("urn:iso:std:iso:20022:tech:xsd:%s", $split[0]);
36+
}
37+
38+
public function popInstance(): DOMDocument
39+
{
40+
$instance = $this->instance;
41+
$this->instance = null;
42+
return $instance;
43+
}
44+
45+
protected function createCreditTransferTransactionElement(
46+
float $amount,
47+
?string $instrId = null,
48+
?string $endToEndId = null
49+
): DOMElement {
50+
$xp = $this->xp();
51+
52+
$pmtInf = DOMHelper::safeItem($xp->query("//p:CstmrCdtTrfInitn/p:PmtInf"));
53+
54+
$tx = $this->el('CdtTrfTxInf');
55+
$pmtInf->appendChild($tx);
56+
57+
$pmtId = $this->el('PmtId');
58+
if ($instrId !== null) {
59+
$pmtId->appendChild($this->el('InstrId', $instrId));
60+
}
61+
$pmtId->appendChild($this->el('EndToEndId', $endToEndId ?: $this->randomService->uniqueIdWithDate('pete')));
62+
$tx->appendChild($pmtId);
63+
64+
$nbGrpEl = DOMHelper::safeItem($xp->query("//p:CstmrCdtTrfInitn/p:GrpHdr/p:NbOfTxs"));
65+
$sumGrpEl = DOMHelper::safeItem($xp->query("//p:CstmrCdtTrfInitn/p:GrpHdr/p:CtrlSum"));
66+
$nbPmtEl = DOMHelper::safeItem($xp->query("//p:CstmrCdtTrfInitn/p:PmtInf/p:NbOfTxs"));
67+
$sumPmtEl = DOMHelper::safeItem($xp->query("//p:CstmrCdtTrfInitn/p:PmtInf/p:CtrlSum"));
68+
69+
$nb = (int)trim($nbGrpEl->textContent);
70+
$nb++;
71+
$nbGrpEl->textContent = (string)$nb;
72+
$nbPmtEl->textContent = (string)$nb;
73+
74+
$currentSum = (float)str_replace(',', '.', trim($sumGrpEl->textContent));
75+
$newSum = $currentSum + $amount;
76+
$sumGrpEl->textContent = number_format($newSum, 2, '.', '');
77+
$sumPmtEl->textContent = number_format($newSum, 2, '.', '');
78+
79+
return $tx;
80+
}
81+
82+
protected function addAmountElement(DOMElement $tx, float $amount, string $currency): void
83+
{
84+
$amt = $this->el('Amt');
85+
$instd = $this->el('InstdAmt', number_format($amount, 2, '.', ''));
86+
$instd->setAttribute('Ccy', $currency);
87+
$amt->appendChild($instd);
88+
$tx->appendChild($amt);
89+
}
90+
91+
protected function addCreditor(
92+
DOMElement $tx,
93+
?string $creditorFinInstBIC,
94+
string $creditorIBAN,
95+
string $creditorName,
96+
?PostalAddressInterface $postalAddress,
97+
?string $purposeText = null,
98+
?string $purposeCode = null
99+
): void {
100+
if ($creditorFinInstBIC !== null) {
101+
$cdtrAgt = $this->el('CdtrAgt');
102+
$fi = $this->el('FinInstnId');
103+
$fi->appendChild($this->el('BICFI', $creditorFinInstBIC));
104+
$cdtrAgt->appendChild($fi);
105+
$tx->appendChild($cdtrAgt);
106+
}
107+
108+
$cdtr = $this->el('Cdtr');
109+
$cdtr->appendChild($this->el('Nm', $creditorName));
110+
if ($postalAddress !== null) {
111+
$cdtr->appendChild($postalAddress->toDomElement($this->instance));
112+
}
113+
$tx->appendChild($cdtr);
114+
115+
$cdtrAcct = $this->el('CdtrAcct');
116+
$id = $this->el('Id');
117+
$id->appendChild($this->el('IBAN', str_replace(' ', '', $creditorIBAN)));
118+
$cdtrAcct->appendChild($id);
119+
$tx->appendChild($cdtrAcct);
120+
121+
if ($purposeCode) {
122+
$purp = $this->el('Purp');
123+
$purp->appendChild($this->el('Cd', $purposeCode));
124+
$tx->appendChild($purp);
125+
}
126+
127+
if ($purposeText !== null) {
128+
$rmt = $this->el('RmtInf');
129+
$rmt->appendChild($this->el('Ustrd', $purposeText));
130+
$tx->appendChild($rmt);
131+
}
132+
}
133+
134+
protected function el(string $name, ?string $text = null): DOMElement
135+
{
136+
$el = $this->instance->createElementNS($this->ns, $name);
137+
if ($text !== null) {
138+
$el->nodeValue = $text;
139+
}
140+
return $el;
141+
}
142+
143+
protected function xp(): \DOMXPath
144+
{
145+
$xp = $this->prepareXPath($this->instance);
146+
$xp->registerNamespace('p', $this->ns);
147+
return $xp;
148+
}
149+
150+
protected function buildReqdExctnDt(DateTime $date): DOMElement
151+
{
152+
$req = $this->el('ReqdExctnDt');
153+
$req->appendChild($this->el('Dt', $date->format('Y-m-d')));
154+
return $req;
155+
}
156+
157+
protected function schemaLocation(): string
158+
{
159+
return sprintf("%s %s.xsd", $this->ns, $this->schema);
160+
}
161+
}

src/Builders/Request/BodyBuilder.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,25 @@ public function addTransferReceipt(Closure $callback): BodyBuilder
4646
return $this;
4747
}
4848

49+
public function addPreValidation(string $signatureVersion, ?string $digest = null): BodyBuilder
50+
{
51+
$preValidation = $this->createEmptyElement('PreValidation', [
52+
'authenticate' => 'true'
53+
]);
54+
55+
$this->instance->appendChild($preValidation);
56+
57+
$xmlDataDigest = $this->appendEmptyElementTo('DataDigest', $preValidation, [
58+
'SignatureVersion' => $signatureVersion,
59+
]);
60+
61+
if (null !== $digest) {
62+
$xmlDataDigest->nodeValue = base64_encode($digest);
63+
}
64+
65+
return $this;
66+
}
67+
4968
public function getInstance(): DOMElement
5069
{
5170
return $this->instance;

0 commit comments

Comments
 (0)