Skip to content

Commit

Permalink
Merge pull request #194 from NicoRsu/add_cdb_support_v2_5
Browse files Browse the repository at this point in the history
Add support for direct debit transactions for business (CDB)
  • Loading branch information
andrew-svirin authored Jul 23, 2024
2 parents 45b3938 + 30d2338 commit adadb0d
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ try {
| XE3 | Upload SEPA Direct Debit Initiation, CH definitions, CORE (i.e available in Switzerland). |
| YCT | Upload Credit transfer CGI (SEPA & non SEPA). |
| CDD | Upload initiation of the direct debit transaction. |
| CDB | Upload initiation of the direct debit transaction for business. |
| BTD | Download request files of any BTF structure. |
| BTU | Upload the files to the bank. |
| HVU | Download List the orders for which the user is authorized as a signatory. |
Expand Down
13 changes: 13 additions & 0 deletions src/Contracts/EbicsClientInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,19 @@ public function CCT(OrderDataInterface $orderData, DateTimeInterface $dateTime =
*/
public function CDD(OrderDataInterface $orderData, DateTimeInterface $dateTime = null): UploadOrderResult;

/**
* Upload initiation of the direct debit transaction for business.
* The CDB order type uses the protocol version H00X.
* FileFormat pain.008.001.02
* OrderType:BTU, Service Name:SDD, Scope:SDD,Service Option:COR Container:, MsgName:pain.008
*
* @param OrderDataInterface $orderData
* @param DateTimeInterface|null $dateTime
*
* @return UploadOrderResult
*/
public function CDB(OrderDataInterface $orderData, DateTimeInterface $dateTime = null): UploadOrderResult;

/**
* Upload initiation credit transfer per Swiss Payments specification set by Six banking services.
* XE2 is an upload order type that uses the protocol version H00X.
Expand Down
24 changes: 24 additions & 0 deletions src/EbicsClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,30 @@ public function CDD(OrderDataInterface $orderData, DateTimeInterface $dateTime =
return $this->createUploadOrderResult($transaction, $orderData);
}

/**
* @inheritDoc
* @throws Exceptions\EbicsResponseException
* @throws EbicsException
*/
public function CDB(OrderDataInterface $orderData, DateTimeInterface $dateTime = null): UploadOrderResult
{
if (null === $dateTime) {
$dateTime = new DateTime();
}

$transaction = $this->uploadTransaction(function (UploadTransaction $transaction) use ($dateTime, $orderData) {
$transaction->setOrderData($orderData->getContent());
$transaction->setDigest($this->cryptService->hash($transaction->getOrderData()));

return $this->requestFactory->createCDB(
$dateTime,
$transaction
);
});

return $this->createUploadOrderResult($transaction, $orderData);
}

/**
* @inheritDoc
* @throws Exceptions\EbicsResponseException
Expand Down
2 changes: 2 additions & 0 deletions src/Factories/RequestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,8 @@ abstract public function createCCT(DateTimeInterface $dateTime, UploadTransactio

abstract public function createCDD(DateTimeInterface $dateTime, UploadTransaction $transaction): Request;

abstract public function createCDB(DateTimeInterface $dateTime, UploadTransaction $transaction): Request;

abstract public function createXE2(DateTimeInterface $dateTime, UploadTransaction $transaction): Request;

abstract public function createXE3(DateTimeInterface $dateTime, UploadTransaction $transaction): Request;
Expand Down
5 changes: 5 additions & 0 deletions src/Factories/RequestFactoryV24.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ public function createCDD(DateTimeInterface $dateTime, UploadTransaction $transa
throw new LogicException('Method not implemented yet for EBICS 2.4');
}

public function createCDB(DateTimeInterface $dateTime, UploadTransaction $transaction): Request
{
throw new LogicException('Method not implemented yet for EBICS 2.4');
}

public function createXE2(DateTimeInterface $dateTime, UploadTransaction $transaction): Request
{
throw new LogicException('Method not implemented yet for EBICS 2.4');
Expand Down
65 changes: 65 additions & 0 deletions src/Factories/RequestFactoryV25.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ protected function addOrderType(OrderDetailsBuilder $orderDetailsBuilder, string
break;
case 'CCT':
case 'CDD':
case 'CDB':
case 'XE2':
case 'XE3':
case 'CIP':
Expand Down Expand Up @@ -681,6 +682,70 @@ public function createCDD(DateTimeInterface $dateTime, UploadTransaction $transa
return $request;
}

/**
* @throws EbicsException
*/
public function createCDB(DateTimeInterface $dateTime, UploadTransaction $transaction): Request
{
$signatureData = new UserSignature();
$this->userSignatureHandler->handle($signatureData, $transaction->getDigest());

$context = (new RequestContext())
->setBank($this->bank)
->setUser($this->user)
->setKeyring($this->keyring)
->setDateTime($dateTime)
->setTransactionKey($transaction->getKey())
->setNumSegments($transaction->getNumSegments())
->setSignatureData($signatureData);

$request = $this
->createRequestBuilderInstance()
->addContainerSecured(function (XmlBuilder $builder) use ($context) {
$builder->addHeader(function (HeaderBuilder $builder) use ($context) {
$builder->addStatic(function (StaticBuilder $builder) use ($context) {
$builder
->addHostId($context->getBank()->getHostId())
->addRandomNonce()
->addTimestamp($context->getDateTime())
->addPartnerId($context->getUser()->getPartnerId())
->addUserId($context->getUser()->getUserId())
->addProduct('Ebics client PHP', 'de')
->addOrderDetails(function (OrderDetailsBuilder $orderDetailsBuilder) {
$this
->addOrderType($orderDetailsBuilder, 'CDB')
->addStandardOrderParams();
})
->addBankPubKeyDigests(
$context->getKeyring()->getBankSignatureXVersion(),
$this->digestResolver->digest($context->getKeyring()->getBankSignatureX()),
$context->getKeyring()->getBankSignatureEVersion(),
$this->digestResolver->digest($context->getKeyring()->getBankSignatureE())
)
->addSecurityMedium(StaticBuilder::SECURITY_MEDIUM_0000)
->addNumSegments($context->getNumSegments());
})->addMutable(function (MutableBuilder $builder) {
$builder->addTransactionPhase(MutableBuilder::PHASE_INITIALIZATION);
});
})->addBody(function (BodyBuilder $builder) use ($context) {
$builder->addDataTransfer(function (DataTransferBuilder $builder) use ($context) {
$builder
->addDataEncryptionInfo(function (DataEncryptionInfoBuilder $builder) use ($context) {
$builder
->addEncryptionPubKeyDigest($context->getKeyring())
->addTransactionKey($context->getTransactionKey(), $context->getKeyring());
})
->addSignatureData($context->getSignatureData(), $context->getTransactionKey());
});
});
})
->popInstance();

$this->authSignatureHandler->handle($request);

return $request;
}

/**
* @throws EbicsException
*/
Expand Down
6 changes: 6 additions & 0 deletions src/Factories/RequestFactoryV3.php
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,12 @@ public function createCDD(DateTimeInterface $dateTime, UploadTransaction $transa
throw new LogicException('Method not implemented yet for EBICS 3.0');
}

public function createCDB(DateTimeInterface $dateTime, UploadTransaction $transaction): Request
{
throw new LogicException('Method not implemented yet for EBICS 3.0');
}


public function createXE2(DateTimeInterface $dateTime, UploadTransaction $transaction): Request
{
throw new LogicException('Method not implemented yet for EBICS 3.0');
Expand Down
37 changes: 37 additions & 0 deletions tests/EbicsClientV25Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,38 @@ public function testCDD(int $credentialsId, array $codes, X509GeneratorInterface
$this->assertResponseOk($code, $reportText);
}

/**
* @dataProvider serversDataProvider
*
* @group CDB
*
* @param int $credentialsId
* @param array $codes
* @param X509GeneratorInterface|null $x509Generator
*
* @covers
*/
public function testCDB(int $credentialsId, array $codes, X509GeneratorInterface $x509Generator = null)
{
$client = $this->setupClientV25($credentialsId, $x509Generator, $codes['CDB']['fake']);

$this->assertExceptionCode($codes['CDB']['code']);

$customerDirectDebit = $this->buildCustomerDirectDebit('urn:iso:std:iso:20022:tech:xsd:pain.008.001.02');

$cdb = $client->CDB($customerDirectDebit);

$responseHandler = $client->getResponseHandler();
$code = $responseHandler->retrieveH00XReturnCode($cdb->getTransaction()->getLastSegment()->getResponse());
$reportText = $responseHandler->retrieveH00XReportText($cdb->getTransaction()->getLastSegment()->getResponse());
$this->assertResponseOk($code, $reportText);

$code = $responseHandler->retrieveH00XReturnCode($cdb->getTransaction()->getInitialization()->getResponse());
$reportText = $responseHandler->retrieveH00XReportText($cdb->getTransaction()->getInitialization()->getResponse());

$this->assertResponseOk($code, $reportText);
}

/**
* @dataProvider serversDataProvider
*
Expand Down Expand Up @@ -1039,6 +1071,7 @@ public function serversDataProvider()
'XE2' => ['code' => null, 'fake' => false],
'XE3' => ['code' => null, 'fake' => false],
'CDD' => ['code' => null, 'fake' => false],
'CDB' => ['code' => '090003', 'fake' => false],
'CIP' => ['code' => '091005', 'fake' => false],
'HVU' => ['code' => '090003', 'fake' => false],
'HVZ' => ['code' => '090003', 'fake' => false],
Expand Down Expand Up @@ -1084,6 +1117,7 @@ public function serversDataProvider()
'XE2' => ['code' => null, 'fake' => false],
'XE3' => ['code' => null, 'fake' => false],
'CDD' => ['code' => null, 'fake' => false],
'CDB' => ['code' => null, 'fake' => false],
'CIP' => ['code' => null, 'fake' => false],
'HVU' => ['code' => '091006', 'fake' => false],
'HVZ' => ['code' => '091006', 'fake' => false],
Expand Down Expand Up @@ -1130,6 +1164,7 @@ public function serversDataProvider()
'XE2' => ['code' => null, 'fake' => false],
'XE3' => ['code' => null, 'fake' => false],
'CDD' => ['code' => null, 'fake' => false],
'CDB' => ['code' => null, 'fake' => false],
'CIP' => ['code' => '091005', 'fake' => false],
'HVU' => ['code' => '090003', 'fake' => false],
'HVZ' => ['code' => '090003', 'fake' => false],
Expand Down Expand Up @@ -1175,6 +1210,7 @@ public function serversDataProvider()
'XE2' => ['code' => null, 'fake' => false],
'XE3' => ['code' => null, 'fake' => false],
'CDD' => ['code' => '090003', 'fake' => false],
'CDB' => ['code' => '090003', 'fake' => false],
'CIP' => ['code' => '091005', 'fake' => false],
'HVU' => ['code' => '090003', 'fake' => false],
'HVZ' => ['code' => '090003', 'fake' => false],
Expand Down Expand Up @@ -1220,6 +1256,7 @@ public function serversDataProvider()
'XE2' => ['code' => null, 'fake' => false],
'XE3' => ['code' => null, 'fake' => false],
'CDD' => ['code' => null, 'fake' => false],
'CDB' => ['code' => null, 'fake' => false],
'CIP' => ['code' => '091005', 'fake' => false],
'HVU' => ['code' => '090003', 'fake' => false],
'HVZ' => ['code' => '090003', 'fake' => false],
Expand Down
5 changes: 4 additions & 1 deletion tests/FakerHttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,17 @@ private function fixtureOrderType(string $orderType, array $options = null): Res
case 'CDD':
$fileName = 'cdd.xml';
break;
case 'CDB':
$fileName = 'cdb.xml';
break;
default:
throw new LogicException(sprintf('Faked order type `%s` not supported.', $orderType));
}

$fixturePath = $this->fixturesDir . '/' . $fileName;

if (!is_file($fixturePath)) {
throw new LogicException('Fixtures file doe not exists.');
throw new LogicException('Fixtures file does not exists.');
}

$response = new Response();
Expand Down

0 comments on commit adadb0d

Please sign in to comment.