Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for direct debit transactions for business (CDB) #194

Merged
merged 1 commit into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading