diff --git a/.githooks/pre-commit b/.githooks/pre-commit
new file mode 100755
index 0000000..fdfe632
--- /dev/null
+++ b/.githooks/pre-commit
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -eu
+
+if git diff --cached --name-only --diff-filter=ACM | grep -qE '\.php$'; then
+ echo "PHP files changed — running unit tests..."
+ make test_unit
+
+ make lint
+else
+ echo "No PHP changes — skipping unit tests."
+fi
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7d5f141..81aa3d5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,30 +1,47 @@
name: CI
-
on: [push]
-
jobs:
build-test:
runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ # add 8.4 soon
+ php-version: ['8.3']
steps:
- name: Checkout code
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Setup .env
run: cp .env.example .env
+ - name: Setup ownership
+ run: git config --global --add safe.directory /app
+
- name: Replace placeholders with GitHub Secrets
run: |
sed -i "s/BLUEM_SENDER_ID=S/BLUEM_SENDER_ID=${{ secrets.BLUEM_SENDER_ID }}/g" .env
sed -i "s/BLUEM_TEST_ACCESS_TOKEN=/BLUEM_TEST_ACCESS_TOKEN=${{ secrets.BLUEM_TEST_ACCESS_TOKEN }}/g" .env
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-version }}
+ extensions: mbstring, intl
+ coverage: xdebug
+
- name: Run composer
- uses: php-actions/composer@v5
+ uses: php-actions/composer@v6
+ with:
+ php_version: ${{ matrix.php-version }}
+
+ - name: PHPCS Lint
+ run: make lint
- name: PHPUnit Tests
- uses: php-actions/phpunit@v3
+ uses: php-actions/phpunit@v4
with:
- version: 9.5
- php_version: 8.1
+ php_version: ${{ matrix.php-version }}
+ version: 11
bootstrap: ./vendor/autoload.php
configuration: ./.github/workflows/phpunit.xml
diff --git a/.github/workflows/phpunit.xml b/.github/workflows/phpunit.xml
index bc8631b..811d8cf 100644
--- a/.github/workflows/phpunit.xml
+++ b/.github/workflows/phpunit.xml
@@ -7,7 +7,13 @@
>
- ../../tests
+ ../../tests/Unit
+
+
+ ../../tests/Integration
+
+
+ ../../tests/Acceptance
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..9a2707e
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,35 @@
+# AGENTS.md
+
+## Project at a glance
+- `bluem-php` is a PHP 8.3 library for Bluem payment flows: Payments, eMandates, iDIN, and IBAN-name check.
+- `src/Bluem.php` is the main orchestration layer: it builds requests, validates XML, sends them through `Transport/`, and turns responses into `Responses/*` objects.
+- `src/Webhook.php` handles inbound webhook XML and is intentionally strict: HTTPS POST + `text/xml; charset=UTF-8` + XML/signature validation.
+
+## Code structure to preserve
+- `src/Contexts/*` defines service-specific bank/BIC sets and XSD schema paths (`IdentityContext`, `PaymentsContext`, `MandatesContext`).
+- `src/Requests/*` owns XML/endpoint generation; `src/Responses/*` wraps `SimpleXMLElement` parsing.
+- `src/Transport/` isolates HTTP; `CurlHttpTransport` is the default and is injected into `Bluem` for testability.
+- `src/Validators/*` enforces XML and webhook constraints; do not bypass these checks in higher layers.
+
+## Conventions specific to this repo
+- Public API names are legacy-compatible and intentionally mixed-case in places (`CreateMandateRequest`, `PerformRequest`, `getConfig`, `Webhook::getPurchaseID()`); avoid renaming unless you are ready to update downstream consumers.
+- `phpcs.xml.dist` uses PSR-12 with narrow exceptions for legacy method/property naming and file-header ordering.
+- Existing code mixes older style and newer strict typing; prefer small, behavior-preserving edits over broad refactors.
+
+## Developer workflow
+- Install deps with `composer install`.
+- Lint with `make lint`; auto-fix style with `make lint_fix`.
+- Run unit tests with `make test_unit` or `./vendor/bin/phpunit tests/Unit`.
+- Run live tests with `make test_integration` / `make test_acceptance`; these require `.env`.
+- CI (`.github/workflows/ci.yml`) runs on PHP 8.3, then `make lint`, then PHPUnit.
+
+## Environment and testing
+- Copy `.env.example` to `.env` for integration/acceptance tests.
+- Required env vars are enforced in `tests/Integration/BluemGenericTestCase.php`: `BLUEM_ENV`, `BLUEM_SENDER_ID`, `BLUEM_BRANDID`, `BLUEM_TEST_ACCESS_TOKEN`, `BLUEM_MERCHANTID`, `BLUEM_MERCHANTRETURNURLBASE`.
+- Unit tests typically use a fake transport; integration tests extend the shared base case and hit live Bluem services.
+
+## When changing code
+- Update request/response/context pieces together so XML shape, endpoint URL, and validation stay aligned.
+- Check `validation/*.xsd` and `examples/` when touching service-specific payloads.
+- Keep webhook validation strict; relaxing HTTPS, content-type, or signature checks is a security regression.
+
diff --git a/Makefile b/Makefile
index e4bd8d1..835842f 100644
--- a/Makefile
+++ b/Makefile
@@ -5,4 +5,26 @@ test_unit:
test_integration:
@printf 'Running integration tests:\n';
@echo "Note: Ensure you have the necessary environment variables set for integration tests in the .env file."
- ./vendor/bin/phpunit tests/Integration
+ ./vendor/bin/phpunit tests/Integration --testdox --display-errors --display-warnings --display-deprecations --display-phpunit-deprecations --display-notices
+
+test_acceptance:
+ @printf 'Running acceptance tests:\n';
+ ./vendor/bin/phpunit tests/Acceptance --testdox --display-errors --display-warnings --display-deprecations --display-phpunit-deprecations --display-notices
+
+test:
+ @printf 'Running all tests:\n';
+ make test_unit;
+ make test_acceptance;
+ make test_integration;
+
+lint:
+ ./vendor/bin/phpcs --standard=phpcs.xml.dist --extensions=php --ignore=vendor/ .
+
+lint_fix:
+ ./vendor/bin/phpcbf --standard=phpcs.xml.dist --extensions=php --ignore=vendor/ .
+
+
+setup-git-hooks:
+ @echo "Setting up Git hooks..."
+ git config core.hooksPath .githooks
+ chmod +x .githooks/pre-commit
diff --git a/README.md b/README.md
index 271d62f..1512ec4 100644
--- a/README.md
+++ b/README.md
@@ -150,6 +150,8 @@ No earlier changelog was recorded. Please refer to the [commit log](https://gith
## Testing
For improving future features, unit testing is introduced since november 2021.
+The repository also has a PHPCS linting setup: `make lint` runs the shared ruleset from `phpcs.xml.dist`, and CI uses the same lint step so local and automated checks stay aligned. That XML file also contains a few narrow exceptions for legacy naming and file-header issues, which lets the existing public API stay intact without failing the build on historical style violations.
+
Tests are located in the `tests` folder
To run tests:
```
diff --git a/composer.json b/composer.json
index ce8dc32..bd42b45 100644
--- a/composer.json
+++ b/composer.json
@@ -14,6 +14,11 @@
"support": {
"issues": "https://github.com/bluem-development/bluem-php"
},
+ "config": {
+ "platform": {
+ "php": "8.3"
+ }
+ },
"require": {
"php": ">=8.3",
"ext-dom": "*",
diff --git a/composer.lock b/composer.lock
index f3a1bd5..563a487 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "bf2f21ae54543d3cef8f39aa5b643035",
+ "content-hash": "485d06fa2a6dd9b490a4a98f9f960a2a",
"packages": [
{
"name": "selective/xmldsig",
@@ -110,29 +110,30 @@
},
{
"name": "doctrine/instantiator",
- "version": "2.1.0",
+ "version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
- "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7"
+ "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/instantiator/zipball/23da848e1a2308728fe5fdddabf4be17ff9720c7",
- "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
+ "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
"shasum": ""
},
"require": {
- "php": "^8.4"
+ "php": "^8.1"
},
"require-dev": {
- "doctrine/coding-standard": "^14",
+ "doctrine/coding-standard": "^11",
"ext-pdo": "*",
"ext-phar": "*",
"phpbench/phpbench": "^1.2",
- "phpstan/phpstan": "^2.1",
- "phpstan/phpstan-phpunit": "^2.0",
- "phpunit/phpunit": "^10.5.58"
+ "phpstan/phpstan": "^1.9.4",
+ "phpstan/phpstan-phpunit": "^1.3",
+ "phpunit/phpunit": "^9.5.27",
+ "vimeo/psalm": "^5.4"
},
"type": "library",
"autoload": {
@@ -159,7 +160,7 @@
],
"support": {
"issues": "https://github.com/doctrine/instantiator/issues",
- "source": "https://github.com/doctrine/instantiator/tree/2.1.0"
+ "source": "https://github.com/doctrine/instantiator/tree/2.0.0"
},
"funding": [
{
@@ -175,7 +176,7 @@
"type": "tidelift"
}
],
- "time": "2026-01-05T06:47:08+00:00"
+ "time": "2022-12-30T00:23:10+00:00"
},
{
"name": "graham-campbell/result-type",
@@ -3124,5 +3125,8 @@
"ext-openssl": "*"
},
"platform-dev": {},
+ "platform-overrides": {
+ "php": "8.3"
+ },
"plugin-api-version": "2.6.0"
}
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
new file mode 100644
index 0000000..6bbcea4
--- /dev/null
+++ b/phpcs.xml.dist
@@ -0,0 +1,29 @@
+
+
+ Project PHPCS rules with narrow exceptions for the legacy public API.
+
+
+
+ src
+ tests
+ examples
+ rector.php
+
+ vendor/*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/phpunit.xml b/phpunit.xml
index db121f5..c34213f 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,5 +1,5 @@
-
+
./tests/Unit
@@ -7,5 +7,8 @@
./tests/Integration
+
+ ./tests/Acceptance
+
diff --git a/src/Bluem.php b/src/Bluem.php
index c1e61dc..8158b0a 100644
--- a/src/Bluem.php
+++ b/src/Bluem.php
@@ -34,10 +34,13 @@
use Bluem\BluemPHP\Responses\MandateTransactionBluemResponse;
use Bluem\BluemPHP\Responses\PaymentStatusBluemResponse;
use Bluem\BluemPHP\Responses\PaymentTransactionBluemResponse;
+use Bluem\BluemPHP\Transport\CurlHttpTransport;
+use Bluem\BluemPHP\Transport\HttpTransportInterface;
use Bluem\BluemPHP\Validators\BluemXMLValidator;
use DOMException;
use Exception;
use RuntimeException;
+use SimpleXMLElement;
use Throwable;
if (!defined("BLUEM_ENVIRONMENT_PRODUCTION")) {
@@ -68,6 +71,8 @@ class Bluem
private BluemConfiguration $configuration;
+ private HttpTransportInterface $transport;
+
/**
* Bluem constructor.
@@ -76,9 +81,9 @@ class Bluem
*
* @throws InvalidBluemConfigurationException
*/
- public function __construct(mixed $rawConfig)
+ public function __construct(mixed $rawConfig, ?HttpTransportInterface $transport = null)
{
- if ($rawConfig ===null) {
+ if ($rawConfig === null) {
throw new InvalidBluemConfigurationException('No configuration given');
}
@@ -87,6 +92,8 @@ public function __construct(mixed $rawConfig)
} catch (Exception $exception) {
throw new InvalidBluemConfigurationException($exception->getMessage(), $exception->getCode(), $exception);
}
+
+ $this->transport = $transport ?? new CurlHttpTransport();
}
@@ -202,13 +209,15 @@ public function CreateMandateID(string $order_id, string $customer_id): string
* @throws DOMException
* @throws Exception
*/
- public function PerformRequest(BluemRequestInterface $transaction_request): BluemResponseInterface
- {
+ public function PerformRequest(
+ BluemRequestInterface $transaction_request
+ ): ErrorBluemResponse|IBANNameCheckBluemResponse|IdentityStatusBluemResponse|IdentityTransactionBluemResponse|MandateStatusBluemResponse|MandateTransactionBluemResponse|PaymentStatusBluemResponse|PaymentTransactionBluemResponse {
$validator = new BluemXMLValidator();
- if (! $validator->validate(
- $transaction_request->RequestContext(),
- $transaction_request->XmlString()
- )
+ if (
+ ! $validator->validate(
+ $transaction_request->RequestContext(),
+ $transaction_request->XmlString()
+ )
) {
return new ErrorBluemResponse(
"Error: Request is not formed correctly. More details: " .
@@ -229,11 +238,11 @@ public function PerformRequest(BluemRequestInterface $transaction_request): Blue
// function to allow for Carbon 1.21 legacy compatibility
$xttrs_date = $now->rfc1123();
- $request_url = $transaction_request->HttpRequestUrl();
+ $request_url = $transaction_request->HttpRequestURL();
$curl_xml = $transaction_request->XmlString();
- $curl_headers = [
+ $headers = [
'Access-Control-Allow-Origin: *',
'Content-Type: application/xml; type=' . $transaction_request->transaction_code . '; charset=UTF-8',
'x-ttrs-date: ' . $xttrs_date,
@@ -241,61 +250,41 @@ public function PerformRequest(BluemRequestInterface $transaction_request): Blue
'x-ttrs-filename: ' . $xttrs_filename,
];
- $curl = curl_init();
-
- $curl_options = [
- CURLOPT_POST => true,
- CURLOPT_POSTFIELDS => "xmlRequest=" . $curl_xml,
- // CURLOPT_POSTFIELDS => http_build_query($params),
- CURLOPT_URL => $request_url,
- CURLOPT_HTTPHEADER => $curl_headers,
- CURLOPT_RETURNTRANSFER => 1,
- CURLOPT_SSL_VERIFYPEER => true, // @todo: check if we can set this to true
- CURLOPT_FOLLOWLOCATION => 1,
- CURLOPT_TIMEOUT => 30
- ];
-
- // Set options to cURL request
- curl_setopt_array($curl, $curl_options);
-
try {
- // Execute cURL request
- $response = curl_exec($curl);
-
- // Get response HTTP status code
- $response_status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
-
- // Convert the XML result into array
- $array_data = json_decode(json_encode(simplexml_load_string($response)), true);
+ $transport_response = $this->transport->send(
+ url: $request_url,
+ headers: $headers,
+ body: "xmlRequest=" . $curl_xml
+ );
- curl_close($curl);
+ $response_status = $transport_response->statusCode;
+ $responseBody = $transport_response->body;
switch ($response_status) {
case 200:
- if ($response === false || ($response === '' || $response === '0')) {
+ if ($responseBody === '' || $responseBody === '0') {
return new ErrorBluemResponse("Error: Empty response returned");
}
+ $xml = $this->parseResponseXml($responseBody);
+ if ($xml === null) {
+ return new ErrorBluemResponse('Error: Could not parse Bluem response XML');
+ }
try {
- $response = $this->fabricateResponseObject($transaction_request->transaction_code, $response);
+ $bluemResponse = $this->fabricateResponseObject($transaction_request->transaction_code, $responseBody);
} catch (Throwable $th) {
return new ErrorBluemResponse("Error: Could not create Bluem Response object. More details: " . $th->getMessage());
}
- if ($array_data['@attributes']['type'] === "ErrorResponse") {
- $errorMessage = match ((string) $transaction_request->transaction_code) {
- 'SRX', 'SUD', 'TRX', 'TRS' => (string) $response->EMandateErrorResponse->Error->ErrorMessage,
- 'PSU', 'PSX', 'PTS', 'PTX' => (string) $response->PaymentErrorResponse->Error->ErrorMessage,
- 'ITS', 'ITX', 'ISU', 'ISX' => (string) $response->IdentityErrorResponse->Error->ErrorMessage,
- 'INS', 'INX' => (string) $response->IBANCheckErrorResponse->Error->ErrorMessage,
- default => throw new RuntimeException("Invalid transaction type requested"),
- };
+ $rootAttributes = $xml->attributes();
+ if ($rootAttributes !== null && isset($rootAttributes['type']) && (string) $rootAttributes['type'] === 'ErrorResponse') {
+ $errorMessage = $this->extractErrorMessage($xml, (string) $transaction_request->transaction_code);
// @todo: move into a separate function
return new ErrorBluemResponse("Error: " . ( $errorMessage ));
}
- if (! $response->Status()) {
- return new ErrorBluemResponse("Error: " . ( $response->Error->ErrorMessage ));
+ if (! $bluemResponse->Status()) {
+ return new ErrorBluemResponse("Error: " . $bluemResponse->Error());
}
- return $response;
+ return $bluemResponse;
case 400:
return new ErrorBluemResponse('Your request was not formed correctly.');
case 401:
@@ -306,11 +295,42 @@ public function PerformRequest(BluemRequestInterface $transaction_request): Blue
return new ErrorBluemResponse('Unexpected / erroneous response (code ' . $response_status . ')');
}
} catch (Throwable $throwable) {
- return new ErrorBluemResponse('HTTP Request Error'. $throwable->getMessage());
+ return new ErrorBluemResponse('HTTP Request Error' . $throwable->getMessage());
// @todo improve request return exceptions; add our own exception type
}
}
+ private function parseResponseXml(string $response): ?SimpleXMLElement
+ {
+ $previousUseInternalErrors = libxml_use_internal_errors(true);
+ $xml = simplexml_load_string($response);
+ libxml_clear_errors();
+ libxml_use_internal_errors($previousUseInternalErrors);
+
+ if ($xml === false) {
+ return null;
+ }
+
+ return $xml;
+ }
+
+ private function extractErrorMessage(SimpleXMLElement $xml, string $transactionCode): string
+ {
+ $errorNodeName = match ($transactionCode) {
+ 'SRX', 'SUD', 'TRX', 'TRS' => 'EMandateErrorResponse',
+ 'PSU', 'PSX', 'PTS', 'PTX' => 'PaymentErrorResponse',
+ 'ITS', 'ITX', 'ISU', 'ISX' => 'IdentityErrorResponse',
+ 'INS', 'INX' => 'IBANCheckErrorResponse',
+ default => throw new RuntimeException('Invalid transaction type requested'),
+ };
+
+ if (!isset($xml->{$errorNodeName}->Error->ErrorMessage)) {
+ return '';
+ }
+
+ return (string) $xml->{$errorNodeName}->Error->ErrorMessage;
+ }
+
/**
* Create the proper response object class
*
@@ -319,7 +339,7 @@ public function PerformRequest(BluemRequestInterface $transaction_request): Blue
*
* @throws Exception
*/
- private function fabricateResponseObject($type, $response_xml): BluemResponseInterface
+ private function fabricateResponseObject($type, $response_xml): ErrorBluemResponse|IBANNameCheckBluemResponse|IdentityStatusBluemResponse|IdentityTransactionBluemResponse|MandateStatusBluemResponse|MandateTransactionBluemResponse|PaymentStatusBluemResponse|PaymentTransactionBluemResponse
{
return match ($type) {
'SRX', 'SUD' => new MandateStatusBluemResponse($response_xml),
@@ -376,7 +396,6 @@ public function GetMaximumAmountFromTransactionResponse($response): object
* @param $amount
*
* @throws DOMException
- * @throws HTTP_Request2_LogicException
* @throws RuntimeException
*/
public function Payment(
@@ -618,7 +637,6 @@ public function GetIdentityRequestTypes(): array
* @param string $debtorReference An optional given debtor reference
* to append to the check request
* @throws DOMException
- * @throws HTTP_Request2_LogicException
* @throws Exception
*/
public function IBANNameCheck(string $iban, string $name, string $debtorReference = ""): ErrorBluemResponse|IBANNameCheckBluemResponse|IdentityStatusBluemResponse|IdentityTransactionBluemResponse|MandateStatusBluemResponse|MandateTransactionBluemResponse|PaymentStatusBluemResponse|PaymentTransactionBluemResponse
diff --git a/src/Constants.php b/src/Constants.php
new file mode 100644
index 0000000..079ac99
--- /dev/null
+++ b/src/Constants.php
@@ -0,0 +1,40 @@
+validateDetails();
if ($validationErrors !== []) {
- throw new RuntimeException('Invalid details given: '. implode(', ', $validationErrors));
+ throw new RuntimeException('Invalid details given: ' . implode(', ', $validationErrors));
}
$this->paymentMethodDetails = $details;
diff --git a/src/Contexts/MandatesContext.php b/src/Contexts/MandatesContext.php
index 7a5e484..6786866 100644
--- a/src/Contexts/MandatesContext.php
+++ b/src/Contexts/MandatesContext.php
@@ -8,6 +8,7 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Contexts;
use Bluem\BluemPHP\Helpers\BIC;
@@ -78,7 +79,7 @@ public function addPaymentMethodDetails(array $details = []): void
{
$validationErrors = $this->validateDetails();
if ($validationErrors !== []) {
- throw new RuntimeException('Invalid details given: '. implode(', ', $validationErrors));
+ throw new RuntimeException('Invalid details given: ' . implode(', ', $validationErrors));
}
$this->paymentMethodDetails = $details;
diff --git a/src/Contexts/PaymentsContext.php b/src/Contexts/PaymentsContext.php
index cc1f448..464aa68 100644
--- a/src/Contexts/PaymentsContext.php
+++ b/src/Contexts/PaymentsContext.php
@@ -8,6 +8,7 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Contexts;
use Bluem\BluemPHP\Helpers\BIC;
@@ -75,7 +76,7 @@ public function addPaymentMethodDetails(array $details = []): void
{
$validationErrors = $this->validateDetails();
if ($validationErrors !== []) {
- throw new RuntimeException('Invalid details given: '. implode(', ', $validationErrors));
+ throw new RuntimeException('Invalid details given: ' . implode(', ', $validationErrors));
}
$this->paymentMethodDetails = $details;
diff --git a/src/Exceptions/InvalidBluemConfigurationException.php b/src/Exceptions/InvalidBluemConfigurationException.php
index 1cea7db..2f25d1e 100644
--- a/src/Exceptions/InvalidBluemConfigurationException.php
+++ b/src/Exceptions/InvalidBluemConfigurationException.php
@@ -8,11 +8,11 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Exceptions;
use Exception;
class InvalidBluemConfigurationException extends Exception
{
-
}
diff --git a/src/Exceptions/InvalidBluemRequestException.php b/src/Exceptions/InvalidBluemRequestException.php
index 66fd9f3..f676dbb 100644
--- a/src/Exceptions/InvalidBluemRequestException.php
+++ b/src/Exceptions/InvalidBluemRequestException.php
@@ -8,11 +8,11 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Exceptions;
use Exception;
class InvalidBluemRequestException extends Exception
{
-
}
diff --git a/src/Exceptions/InvalidContextException.php b/src/Exceptions/InvalidContextException.php
index 45fc9df..a67a7bc 100644
--- a/src/Exceptions/InvalidContextException.php
+++ b/src/Exceptions/InvalidContextException.php
@@ -8,6 +8,7 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Exceptions;
use Exception;
diff --git a/src/Extensions/IPAPI.php b/src/Extensions/IPAPI.php
index b5a46e3..375f04d 100644
--- a/src/Extensions/IPAPI.php
+++ b/src/Extensions/IPAPI.php
@@ -1,4 +1,5 @@
*
@@ -34,7 +35,8 @@ public function CheckIsNetherlands(string $ip = ""): bool
$result = $this->QueryIP($ip);
// if we encountered an error, return true for now
- if (isset($result['success'])
+ if (
+ isset($result['success'])
&& $result['success'] === false
) {
return true;
diff --git a/src/Helpers/BIC.php b/src/Helpers/BIC.php
index c614c1f..0a72284 100644
--- a/src/Helpers/BIC.php
+++ b/src/Helpers/BIC.php
@@ -8,6 +8,7 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Helpers;
class BIC
diff --git a/src/Helpers/BluemConfiguration.php b/src/Helpers/BluemConfiguration.php
index 8a6ad56..ff1d2d2 100644
--- a/src/Helpers/BluemConfiguration.php
+++ b/src/Helpers/BluemConfiguration.php
@@ -1,4 +1,5 @@
*
@@ -6,7 +7,6 @@
* with this source code in the file LICENSE.
*/
-
namespace Bluem\BluemPHP\Helpers;
use Bluem\BluemPHP\Exceptions\InvalidBluemConfigurationException;
@@ -58,6 +58,8 @@ class BluemConfiguration
*/
public bool $webhookDebug = false;
+ public ?string $paymentBrandID = '';
+
/**
* An object containing the configuration for the Bluem integration. Can be an array or object
*
@@ -75,7 +77,9 @@ public function __construct(object|array $raw)
$validated = $this->validator->validate($raw);
if ($validated === false) {
- throw new InvalidBluemConfigurationException('Bluem Configuration is not valid: ' . $this->errorsAsString());
+ throw new InvalidBluemConfigurationException(
+ 'Bluem Configuration is not valid: ' . $this->errorsAsString()
+ );
}
$this->environment = $validated->environment ?? self::TESTING_ENVIRONMENT;
@@ -127,7 +131,7 @@ private function _assumeBrandID(string $service, string $brandID): string
}
$prefix = str_replace($available_services, '', $brandID);
- return $prefix.ucfirst($service);
+ return $prefix . ucfirst($service);
}
/**
diff --git a/src/Helpers/BluemCurrency.php b/src/Helpers/BluemCurrency.php
index 93eb4e4..bfd7f78 100644
--- a/src/Helpers/BluemCurrency.php
+++ b/src/Helpers/BluemCurrency.php
@@ -8,6 +8,7 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Helpers;
use RuntimeException;
@@ -15,7 +16,6 @@
class BluemCurrency implements Stringable
{
-
private const string EURO_CURRENCY = 'EUR';
private const string US_DOLLAR_CURRENCY = 'USD';
diff --git a/src/Helpers/BluemIdentityCategoryList.php b/src/Helpers/BluemIdentityCategoryList.php
index 4c0042f..edbfa07 100644
--- a/src/Helpers/BluemIdentityCategoryList.php
+++ b/src/Helpers/BluemIdentityCategoryList.php
@@ -8,11 +8,11 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Helpers;
class BluemIdentityCategoryList
{
-
/**
* @var string[] $categories
*/
diff --git a/src/Helpers/BluemMaxAmount.php b/src/Helpers/BluemMaxAmount.php
index 71000d5..c3fd93b 100644
--- a/src/Helpers/BluemMaxAmount.php
+++ b/src/Helpers/BluemMaxAmount.php
@@ -8,11 +8,11 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Helpers;
class BluemMaxAmount implements \Stringable
{
-
public BluemCurrency $currency;
public function __construct(
@@ -20,7 +20,6 @@ public function __construct(
string $currencyCode
) {
try {
-
$this->currency = new BluemCurrency($currencyCode);
} catch (\Exception) {
$this->currency = new BluemCurrency();
diff --git a/src/Interfaces/BluemContextInterface.php b/src/Interfaces/BluemContextInterface.php
index b217734..24cf7fa 100644
--- a/src/Interfaces/BluemContextInterface.php
+++ b/src/Interfaces/BluemContextInterface.php
@@ -8,6 +8,7 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Interfaces;
interface BluemContextInterface
diff --git a/src/Interfaces/BluemRequestInterface.php b/src/Interfaces/BluemRequestInterface.php
index e2b7a3d..9a758da 100644
--- a/src/Interfaces/BluemRequestInterface.php
+++ b/src/Interfaces/BluemRequestInterface.php
@@ -8,6 +8,7 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Interfaces;
use Bluem\BluemPHP\Requests\BluemRequest;
diff --git a/src/Interfaces/BluemResponseInterface.php b/src/Interfaces/BluemResponseInterface.php
index c74ab5c..25c11d9 100644
--- a/src/Interfaces/BluemResponseInterface.php
+++ b/src/Interfaces/BluemResponseInterface.php
@@ -8,6 +8,7 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Interfaces;
interface BluemResponseInterface
diff --git a/src/Interfaces/WebhookInterface.php b/src/Interfaces/WebhookInterface.php
index 853f192..5cf676c 100644
--- a/src/Interfaces/WebhookInterface.php
+++ b/src/Interfaces/WebhookInterface.php
@@ -8,9 +8,9 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Interfaces;
interface WebhookInterface
{
-
}
diff --git a/src/Requests/BluemRequest.php b/src/Requests/BluemRequest.php
index 356aaf8..234bdce 100644
--- a/src/Requests/BluemRequest.php
+++ b/src/Requests/BluemRequest.php
@@ -1,4 +1,5 @@
*
@@ -8,6 +9,8 @@
namespace Bluem\BluemPHP\Requests;
+use Bluem\BluemPHP\Bluem;
+use Bluem\BluemPHP\Constants;
use Bluem\BluemPHP\Exceptions\InvalidBluemRequestException;
use Bluem\BluemPHP\Helpers\BluemConfiguration;
use Bluem\BluemPHP\Helpers\Now;
@@ -116,7 +119,7 @@ class BluemRequest implements BluemRequestInterface
* @throws InvalidBluemRequestException
*/
public function __construct(
- $config,
+ BluemConfiguration $config,
string $entranceCode = "",
string $expectedReturn = ""
) {
@@ -158,7 +161,7 @@ private function entranceCode(string $expectedReturn = 'none'): string
$entranceCode = (new Now())->format("YmdHisv");
$prefix = "";
- if ($this->environment === BLUEM_ENVIRONMENT_TESTING) {
+ if ($this->environment === Constants::TESTING_ENVIRONMENT) {
switch ($expectedReturn) {
case 'success':
$prefix = "HIO100OIH";
@@ -181,7 +184,7 @@ private function entranceCode(string $expectedReturn = 'none'): string
case '':
case 'none':
default:
- break;
+ break;
}
}
diff --git a/src/Requests/EmandateBluemRequest.php b/src/Requests/EmandateBluemRequest.php
index fbff6d6..fc6d673 100644
--- a/src/Requests/EmandateBluemRequest.php
+++ b/src/Requests/EmandateBluemRequest.php
@@ -1,4 +1,5 @@
*
@@ -6,9 +7,9 @@
* with this source code in the file LICENSE.
*/
-
namespace Bluem\BluemPHP\Requests;
+use Bluem\BluemPHP\Constants;
use Bluem\BluemPHP\Contexts\MandatesContext;
use Bluem\BluemPHP\Helpers\BluemConfiguration;
@@ -90,8 +91,8 @@ public function __construct(BluemConfiguration $config, private $debtorReference
$this->merchantID = $config->merchantID ?? "";
// override with hardcoded merchantID when in test environment, according to documentation
- if ($this->environment === BLUEM_ENVIRONMENT_TESTING) {
- $this->merchantID = "0020000387";
+ if ($this->environment === Constants::TESTING_ENVIRONMENT) {
+ $this->merchantID = Constants::BLUEM_STATIC_MERCHANT_ID;
}
$this->merchantSubID = $config->merchantSubID ?? "0";
@@ -161,30 +162,30 @@ public function TransactionType(): string
private function XmlWrapDebtorWalletForPaymentMethod(): string
{
- $res = '';
-
- if ($this->context->isMandate()) {
- $bic = '';
+ if (!$this->context->isMandate()) {
+ return '';
+ }
- if (empty($this->context->getPaymentDetail('BIC'))) {
- if (!empty($this->debtorWallet)) {
- $bic = $this->debtorWallet;
- }
+ if (empty($this->context->getPaymentDetail('BIC'))) {
+ if (!empty($this->debtorWallet)) {
+ $bic = $this->debtorWallet;
} else {
- $bic = $this->context->getPaymentDetail('BIC');
+ $bic = '';
}
+ } else {
+ $bic = $this->context->getPaymentDetail('BIC');
+ }
- if (empty($bic)) {
- return '';
- }
+ if (empty($bic)) {
+ return '';
+ }
- $res = PHP_EOL . "" . PHP_EOL;
- $res .= sprintf('<%s>', $this->context->debtorWalletElementName);
- $res .= "" . $bic . "";
- $res .= sprintf('%s>', $this->context->debtorWalletElementName) . PHP_EOL;
+ $res = PHP_EOL . "" . PHP_EOL;
+ $res .= sprintf('<%s>', $this->context->debtorWalletElementName);
+ $res .= "" . $bic . "";
+ $res .= sprintf('%s>', $this->context->debtorWalletElementName) . PHP_EOL;
- return $res . ("" . PHP_EOL);
- }
+ return $res . ("" . PHP_EOL);
}
/**
diff --git a/src/Requests/EmandateStatusBluemRequest.php b/src/Requests/EmandateStatusBluemRequest.php
index 1e95c61..ed397ed 100644
--- a/src/Requests/EmandateStatusBluemRequest.php
+++ b/src/Requests/EmandateStatusBluemRequest.php
@@ -1,4 +1,5 @@
*
diff --git a/src/Requests/IBANBluemRequest.php b/src/Requests/IBANBluemRequest.php
index 3f0d0dd..d4532af 100644
--- a/src/Requests/IBANBluemRequest.php
+++ b/src/Requests/IBANBluemRequest.php
@@ -1,4 +1,5 @@
*
diff --git a/src/Requests/IdentityBluemRequest.php b/src/Requests/IdentityBluemRequest.php
index 08ab4d2..e49b9e9 100644
--- a/src/Requests/IdentityBluemRequest.php
+++ b/src/Requests/IdentityBluemRequest.php
@@ -1,4 +1,5 @@
*
@@ -189,30 +190,30 @@ public function enableStatusGUI()
private function XmlWrapDebtorWalletForPaymentMethod(): string
{
- $res = '';
-
if ($this->context->isIDIN()) {
- $bic = '';
+ return '';
+ }
- if (empty($this->context->getPaymentDetail('BIC'))) {
- if (!empty($this->debtorWallet)) {
- $bic = $this->debtorWallet;
- }
+ if (empty($this->context->getPaymentDetail('BIC'))) {
+ if (!empty($this->debtorWallet)) {
+ $bic = $this->debtorWallet;
} else {
- $bic = $this->context->getPaymentDetail('BIC');
+ $bic = '';
}
+ } else {
+ $bic = $this->context->getPaymentDetail('BIC');
+ }
- if (empty($bic)) {
- return '';
- }
+ if (empty($bic)) {
+ return '';
+ }
- $res = PHP_EOL . "" . PHP_EOL;
- $res .= sprintf('<%s>', $this->context->debtorWalletElementName);
- $res .= "" . $bic . "";
- $res .= sprintf('%s>', $this->context->debtorWalletElementName) . PHP_EOL;
+ $res = PHP_EOL . "" . PHP_EOL;
+ $res .= sprintf('<%s>', $this->context->debtorWalletElementName);
+ $res .= "" . $bic . "";
+ $res .= sprintf('%s>', $this->context->debtorWalletElementName) . PHP_EOL;
- return $res . ("" . PHP_EOL);
- }
+ return $res . ("" . PHP_EOL);
}
/**
diff --git a/src/Requests/IdentityStatusBluemRequest.php b/src/Requests/IdentityStatusBluemRequest.php
index 307733a..7cf758c 100644
--- a/src/Requests/IdentityStatusBluemRequest.php
+++ b/src/Requests/IdentityStatusBluemRequest.php
@@ -1,4 +1,6 @@
-
*
* This source file is subject to the license that is bundled
diff --git a/src/Requests/PaymentBluemRequest.php b/src/Requests/PaymentBluemRequest.php
index 0c67ca4..79e10bf 100644
--- a/src/Requests/PaymentBluemRequest.php
+++ b/src/Requests/PaymentBluemRequest.php
@@ -1,4 +1,5 @@
*
@@ -204,7 +205,7 @@ public function setPaymentMethodToIDEAL($BIC = ""): self
if (!empty($BIC)) {
$this->context->addPaymentMethodDetails(
[
- 'BIC'=>$BIC
+ 'BIC' => $BIC
]
);
}
@@ -222,7 +223,7 @@ public function setPaymentMethodToPayPal($payPalAccount = ""): self
if (!empty($payPalAccount)) {
$this->context->addPaymentMethodDetails(
[
- 'PayPalAccount'=>$payPalAccount
+ 'PayPalAccount' => $payPalAccount
]
);
}
@@ -242,16 +243,17 @@ public function setPaymentMethodToCreditCard(
/**
* Prepared for future use.
*/
- if ($cardNumber !== '' && $cardNumber !== '0' || $name !== '' && $name !== '0' || $securityCode !== '' && $securityCode !== '0'
- || $expirationDateMonth !== '' && $expirationDateMonth !== '0' || $expirationDateYear !== '' && $expirationDateYear !== '0'
+ if (
+ !empty($cardNumber) || !empty($name) || !empty($securityCode)
+ || !empty($expirationDateMonth) || !empty($expirationDateYear)
) {
$this->context->addPaymentMethodDetails(
[
- 'CardNumber'=>$cardNumber,
- 'Name'=>$name,
- 'SecurityCode'=>$securityCode,
- 'ExpirationDateMonth'=>$expirationDateMonth,
- 'ExpirationDateYear'=>$expirationDateYear,
+ 'CardNumber' => $cardNumber,
+ 'Name' => $name,
+ 'SecurityCode' => $securityCode,
+ 'ExpirationDateMonth' => $expirationDateMonth,
+ 'ExpirationDateYear' => $expirationDateYear,
]
);
}
@@ -301,11 +303,7 @@ public function getDueDateTime(mixed $dueDateTime): string
private function XmlWrapDebtorWalletForPaymentMethod(): string
{
- $res = '';
-
if ($this->context->isIDEAL()) {
- $bic = '';
-
if (empty($this->context->getPaymentDetail('BIC'))) {
if (!empty($this->debtorWallet)) {
$bic = $this->debtorWallet;
diff --git a/src/Requests/PaymentStatusBluemRequest.php b/src/Requests/PaymentStatusBluemRequest.php
index 197c079..81de5e0 100644
--- a/src/Requests/PaymentStatusBluemRequest.php
+++ b/src/Requests/PaymentStatusBluemRequest.php
@@ -1,4 +1,5 @@
*
@@ -9,6 +10,7 @@
namespace Bluem\BluemPHP\Requests;
use Bluem\BluemPHP\Contexts\PaymentsContext;
+use Bluem\BluemPHP\Helpers\BluemConfiguration;
class PaymentStatusBluemRequest extends BluemRequest
{
@@ -21,14 +23,15 @@ class PaymentStatusBluemRequest extends BluemRequest
protected $xmlInterfaceName = "EPaymentInterface";
public function __construct(
- $config,
+ BluemConfiguration $config,
$transactionID,
$expected_return = "",
$entranceCode = ""
) {
parent::__construct($config, $entranceCode, $expected_return);
- if (isset($config->paymentBrandID)
+ if (
+ isset($config->paymentBrandID)
&& $config->paymentBrandID !== ""
) {
$config->setBrandID($config->paymentBrandID);
diff --git a/src/Responses/BluemResponse.php b/src/Responses/BluemResponse.php
index 589eeb4..5133aa1 100644
--- a/src/Responses/BluemResponse.php
+++ b/src/Responses/BluemResponse.php
@@ -19,10 +19,9 @@
*/
class BluemResponse extends SimpleXMLElement implements BluemResponseInterface
{
+ public static ?string $response_primary_key = null;
- public static string $response_primary_key = null;
-
- public static string $transaction_type = null;
+ public static ?string $transaction_type = null;
public static ?string $error_response_type = null;
@@ -61,7 +60,9 @@ public function GetEntranceCode(): string
$attrs = $this->{$this->getParentXmlElement()}->attributes();
if (! $attrs || ! isset($attrs['entranceCode'])) {
- throw new RuntimeException("An error occurred in reading the transaction response: no entrance code found.");
+ throw new RuntimeException(
+ "An error occurred in reading the transaction response: no entrance code found."
+ );
}
return $attrs['entranceCode'] . "";
@@ -78,7 +79,7 @@ protected function getChildXmlElement(): string
return self::$response_primary_key;
}
- protected function getParentStringVariable(string $variable) : string
+ protected function getParentStringVariable(string $variable): string
{
return ( isset($this->{$this->getParentXmlElement()}->$variable) ) ? $this->{$this->getParentXmlElement()}->$variable . '' : '';
}
diff --git a/src/Responses/ErrorBluemResponse.php b/src/Responses/ErrorBluemResponse.php
index d68d780..1c129ea 100644
--- a/src/Responses/ErrorBluemResponse.php
+++ b/src/Responses/ErrorBluemResponse.php
@@ -8,6 +8,7 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Responses;
use Bluem\BluemPHP\Interfaces\BluemResponseInterface;
diff --git a/src/Responses/IBANNameCheckBluemResponse.php b/src/Responses/IBANNameCheckBluemResponse.php
index 035a51a..65f91af 100644
--- a/src/Responses/IBANNameCheckBluemResponse.php
+++ b/src/Responses/IBANNameCheckBluemResponse.php
@@ -1,4 +1,5 @@
*
@@ -12,9 +13,9 @@
class IBANNameCheckBluemResponse extends TransactionBluemResponse
{
- public static string $transaction_type = "IBANCheckTransactionResponse";
+ public static ?string $transaction_type = "IBANCheckTransactionResponse";
- public static string $response_primary_key = "IBANCheckTransaction";
+ public static ?string $response_primary_key = "IBANCheckTransaction";
public static ?string $error_response_type = "IBANCheckResult";
@@ -29,7 +30,7 @@ private function getIBANCheckResultObject($parentObjectKey = "IBANCheckResult"):
return null;
}
- private function getKeyFromIBANCheckResult(string $key, string $parentObjectKey = null): string
+ private function getKeyFromIBANCheckResult(string $key, ?string $parentObjectKey = null): string
{
$result = $this->getIBANCheckResultObject($parentObjectKey);
diff --git a/src/Responses/IdentityStatusBluemResponse.php b/src/Responses/IdentityStatusBluemResponse.php
index ae88b5d..82f2283 100644
--- a/src/Responses/IdentityStatusBluemResponse.php
+++ b/src/Responses/IdentityStatusBluemResponse.php
@@ -1,4 +1,5 @@
*
@@ -10,9 +11,9 @@
class IdentityStatusBluemResponse extends StatusBluemResponse
{
- public static string $transaction_type = "Identity";
+ public static ?string $transaction_type = "Identity";
- public static string $response_primary_key = 'IdentityStatus';
+ public static ?string $response_primary_key = 'IdentityStatus';
public static ?string $error_response_type = 'IdentityErrorResponse';
diff --git a/src/Responses/IdentityTransactionBluemResponse.php b/src/Responses/IdentityTransactionBluemResponse.php
index cca34db..80b9f06 100644
--- a/src/Responses/IdentityTransactionBluemResponse.php
+++ b/src/Responses/IdentityTransactionBluemResponse.php
@@ -8,13 +8,14 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Responses;
class IdentityTransactionBluemResponse extends TransactionBluemResponse
{
- public static string $transaction_type = "Identity";
+ public static ?string $transaction_type = "Identity";
- public static string $response_primary_key = 'IdentityTransaction';
+ public static ?string $response_primary_key = 'IdentityTransaction';
public static ?string $error_response_type = 'IdentityErrorResponse';
}
diff --git a/src/Responses/MandateStatusBluemResponse.php b/src/Responses/MandateStatusBluemResponse.php
index da3816f..8095c86 100644
--- a/src/Responses/MandateStatusBluemResponse.php
+++ b/src/Responses/MandateStatusBluemResponse.php
@@ -1,4 +1,5 @@
*
@@ -16,9 +17,9 @@
class MandateStatusBluemResponse extends StatusBluemResponse
{
- public static string $transaction_type = "EMandate";
+ public static ?string $transaction_type = "EMandate";
- public static string $response_primary_key = 'EMandateStatus';
+ public static ?string $response_primary_key = 'EMandateStatus';
public static ?string $error_response_type = 'EMandateErrorResponse';
diff --git a/src/Responses/MandateTransactionBluemResponse.php b/src/Responses/MandateTransactionBluemResponse.php
index dd9471d..97c33ed 100644
--- a/src/Responses/MandateTransactionBluemResponse.php
+++ b/src/Responses/MandateTransactionBluemResponse.php
@@ -1,4 +1,5 @@
*
@@ -10,9 +11,9 @@
class MandateTransactionBluemResponse extends TransactionBluemResponse
{
- public static string $transaction_type = "EMandate";
+ public static ?string $transaction_type = "EMandate";
- public static string $response_primary_key = 'EMandateTransaction';
+ public static ?string $response_primary_key = 'EMandateTransaction';
public static ?string $error_response_type = 'EMandateErrorResponse';
diff --git a/src/Responses/PaymentStatusBluemResponse.php b/src/Responses/PaymentStatusBluemResponse.php
index c897fee..ff5d835 100644
--- a/src/Responses/PaymentStatusBluemResponse.php
+++ b/src/Responses/PaymentStatusBluemResponse.php
@@ -8,14 +8,14 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Responses;
class PaymentStatusBluemResponse extends StatusBluemResponse
{
- public static string $transaction_type = "Payment";
+ public static ?string $transaction_type = "Payment";
- public static string $response_primary_key = 'PaymentStatus';
+ public static ?string $response_primary_key = 'PaymentStatus';
- public static ?
- string $error_response_type = 'PaymentErrorResponse';
+ public static ?string $error_response_type = 'PaymentErrorResponse';
}
diff --git a/src/Responses/PaymentTransactionBluemResponse.php b/src/Responses/PaymentTransactionBluemResponse.php
index 798607d..8543e6f 100644
--- a/src/Responses/PaymentTransactionBluemResponse.php
+++ b/src/Responses/PaymentTransactionBluemResponse.php
@@ -8,13 +8,14 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Responses;
class PaymentTransactionBluemResponse extends TransactionBluemResponse
{
- public static string $transaction_type = "Payment";
+ public static ?string $transaction_type = "Payment";
- public static string $response_primary_key = 'PaymentTransaction';
+ public static ?string $response_primary_key = 'PaymentTransaction';
public static ?string $error_response_type = 'PaymentErrorResponse';
}
diff --git a/src/Responses/StatusBluemResponse.php b/src/Responses/StatusBluemResponse.php
index fb609a5..0541c37 100644
--- a/src/Responses/StatusBluemResponse.php
+++ b/src/Responses/StatusBluemResponse.php
@@ -8,6 +8,7 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Responses;
class StatusBluemResponse extends BluemResponse
diff --git a/src/Responses/TransactionBluemResponse.php b/src/Responses/TransactionBluemResponse.php
index 4392ab2..f4e8eaa 100644
--- a/src/Responses/TransactionBluemResponse.php
+++ b/src/Responses/TransactionBluemResponse.php
@@ -8,11 +8,11 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Responses;
class TransactionBluemResponse extends BluemResponse
{
-
public function GetTransactionURL(): string
{
return $this->getParentStringVariable("TransactionURL");
diff --git a/src/Transport/CurlHttpTransport.php b/src/Transport/CurlHttpTransport.php
new file mode 100644
index 0000000..5d9d664
--- /dev/null
+++ b/src/Transport/CurlHttpTransport.php
@@ -0,0 +1,43 @@
+ true,
+ CURLOPT_POSTFIELDS => $body,
+ CURLOPT_URL => $url,
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_RETURNTRANSFER => 1,
+ CURLOPT_SSL_VERIFYPEER => true,
+ CURLOPT_FOLLOWLOCATION => 1,
+ CURLOPT_TIMEOUT => 30,
+ ]);
+
+ $response = curl_exec($curl);
+ $statusCode = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ $errorMessage = curl_error($curl);
+
+ curl_close($curl);
+
+ if ($response === false) {
+ $message = $errorMessage !== '' ? $errorMessage : 'Unknown cURL error';
+ throw new RuntimeException($message);
+ }
+
+ return new HttpTransportResponse($statusCode, (string) $response);
+ }
+}
diff --git a/src/Transport/HttpTransportInterface.php b/src/Transport/HttpTransportInterface.php
new file mode 100644
index 0000000..648ae45
--- /dev/null
+++ b/src/Transport/HttpTransportInterface.php
@@ -0,0 +1,10 @@
+
*
@@ -8,18 +9,11 @@
namespace Bluem\BluemPHP\Validators;
+use Bluem\BluemPHP\Constants;
use Exception;
+use RuntimeException;
use Throwable;
-define("BLUEM_EXPECTED_RETURN_NONE", "none");
-define("BLUEM_EXPECTED_RETURN_SUCCESS", "success");
-define("BLUEM_EXPECTED_RETURN_CANCELLED", "cancelled");
-define("BLUEM_EXPECTED_RETURN_EXPIRED", "expired");
-define("BLUEM_EXPECTED_RETURN_FAILURE", "failure");
-define("BLUEM_EXPECTED_RETURN_OPEN", "open");
-define("BLUEM_EXPECTED_RETURN_PENDING", "pending");
-
-
class BluemConfigurationValidator
{
private ?array $errors = null;
@@ -44,7 +38,6 @@ public function validate($config)
$config = $this->_validateEMandateReason($config);
$config = $this->_validateLocalInstrumentCode($config);
$config = $this->_validateMerchantReturnURLBase($config);
-
} catch (Throwable $throwable) {
$this->errors[] = $throwable->getMessage();
@@ -56,19 +49,24 @@ public function validate($config)
private function _validateEnvironment($config)
{
- if (!isset($config->environment) || !in_array(
- $config->environment,
- [
- BLUEM_ENVIRONMENT_TESTING,
- BLUEM_ENVIRONMENT_ACCEPTANCE,
- BLUEM_ENVIRONMENT_PRODUCTION
- ],
- true
- )
+ if (
+ !isset($config->environment)
) {
throw new Exception(
- "Invalid environment setting, should be either
- 'test', 'acc' or 'prod'"
+ "environment not set; please add this to your configuration when instantiating the Bluem integration"
+ );
+ }
+
+ if (
+ !in_array(
+ $config->environment,
+ Constants::ENVIRONMENTS,
+ true
+ )
+ ) {
+ throw new Exception(
+ sprintf(sprintf("Invalid environment setting (%s), should be one of:
+ %%s", $config->environment), implode(', ', Constants::ENVIRONMENTS))
);
}
@@ -103,7 +101,8 @@ private function _validateSenderID($config)
private function _validateTest_accessToken($config)
{
- if ($config->environment === BLUEM_ENVIRONMENT_TESTING
+ if (
+ $config->environment === Constants::TESTING_ENVIRONMENT
&& ( ! isset($config->test_accessToken)
|| $config->test_accessToken === "" )
) {
@@ -120,7 +119,8 @@ private function _validateProduction_accessToken($config)
{
// only required if mode is set to PROD
// production_accessToken
- if ($config->environment === BLUEM_ENVIRONMENT_PRODUCTION
+ if (
+ $config->environment === Constants::PRODUCTION_ENVIRONMENT
&& ( ! isset($config->production_accessToken)
|| $config->production_accessToken === "" )
) {
@@ -137,7 +137,9 @@ private function _validateProduction_accessToken($config)
private function _validateBrandID($config)
{
if (! isset($config->brandID)) {
- throw new Exception("brandID not set; please add this to your configuration when instantiating the Bluem integration");
+ throw new RuntimeException(
+ "brandID not set; please add this to your configuration when instantiating the Bluem integration"
+ );
}
return $config;
@@ -149,16 +151,16 @@ private function _validateMerchantIDAndSelectAccessToken($config)
$config->merchantId = "";
}
- if ($config->environment === BLUEM_ENVIRONMENT_PRODUCTION) {
+ if ($config->environment === Constants::PRODUCTION_ENVIRONMENT) {
$config->accessToken = $config->production_accessToken;
// @todo consider throwing an exception if these tokens are missing.
- } elseif ($config->environment === BLUEM_ENVIRONMENT_TESTING) {
+ } elseif ($config->environment === Constants::TESTING_ENVIRONMENT) {
$config->accessToken = $config->test_accessToken;
// @todo consider throwing an exception if these tokens are missing.
// hardcoded merchantID in case of test.
// It is always the bluem merchant ID then.
- $config->merchantID = BLUEM_STATIC_MERCHANT_ID;
+ $config->merchantID = Constants::BLUEM_STATIC_MERCHANT_ID;
}
return $config;
@@ -179,13 +181,14 @@ private function _validateThanksPage($config)
*/
private function _validateExpectedReturnStatus($config): mixed
{
- if ($config->environment === BLUEM_ENVIRONMENT_TESTING) {
- if (! isset($config->expectedReturnStatus)
+ if ($config->environment === Constants::TESTING_ENVIRONMENT) {
+ if (
+ ! isset($config->expectedReturnStatus)
|| ( $config->expectedReturnStatus !== ""
&& !in_array($config->expectedReturnStatus, $this->getPossibleReturnStatuses(), true))
) {
// default back to success
- $config->expectedReturnStatus = BLUEM_EXPECTED_RETURN_SUCCESS;
+ $config->expectedReturnStatus = Constants::EXPECTED_RETURN_SUCCESS;
}
} else {
// no need for expectedReturnStatus when in production
@@ -201,13 +204,13 @@ private function _validateExpectedReturnStatus($config): mixed
private function getPossibleReturnStatuses(): array
{
return [
- BLUEM_EXPECTED_RETURN_NONE,
- BLUEM_EXPECTED_RETURN_SUCCESS,
- BLUEM_EXPECTED_RETURN_CANCELLED,
- BLUEM_EXPECTED_RETURN_EXPIRED,
- BLUEM_EXPECTED_RETURN_FAILURE,
- BLUEM_EXPECTED_RETURN_OPEN,
- BLUEM_EXPECTED_RETURN_PENDING
+ Constants::EXPECTED_RETURN_NONE,
+ Constants::EXPECTED_RETURN_SUCCESS,
+ Constants::EXPECTED_RETURN_CANCELLED,
+ Constants::EXPECTED_RETURN_EXPIRED,
+ Constants::EXPECTED_RETURN_FAILURE,
+ Constants::EXPECTED_RETURN_OPEN,
+ Constants::EXPECTED_RETURN_PENDING
];
}
@@ -219,7 +222,8 @@ private function _validateEMandateReason($config)
private function _validateLocalInstrumentCode($config)
{
- if (! isset($config->localInstrumentCode)
+ if (
+ ! isset($config->localInstrumentCode)
|| ! in_array(
$config->localInstrumentCode,
[ 'B2B', 'CORE' ]
diff --git a/src/Validators/BluemXMLValidator.php b/src/Validators/BluemXMLValidator.php
index 122c460..0f6911e 100644
--- a/src/Validators/BluemXMLValidator.php
+++ b/src/Validators/BluemXMLValidator.php
@@ -1,4 +1,5 @@
*
@@ -72,17 +73,35 @@ private function getKeyFileName(): string
$prefix = 'webhook_bluem_nl_';
// 2025 certificate on production from July 18th, 8:30 CET time
- if ($this->env === BLUEM_ENVIRONMENT_PRODUCTION && ( ( $current_date === "2025-07-18" && $current_time >= "08:30" ) || $current_date > "2025-07-18")) {
+ if (
+ $this->env === BLUEM_ENVIRONMENT_PRODUCTION && (
+ ( $current_date === "2025-07-18" && $current_time >= "08:30" )
+ || $current_date > "2025-07-18")
+ ) {
$timestamp = '20250717';
// 2025 certificate on testing & acceptance from July 17th, 8:30 CET time
- } elseif (($this->env === BLUEM_ENVIRONMENT_TESTING || $this->env === BLUEM_ENVIRONMENT_ACCEPTANCE)
- && (($current_date === "2024-07-17" && $current_time >= "06:30") || $current_date > "2024-07-17")) {
+ } elseif (
+ ($this->env === BLUEM_ENVIRONMENT_TESTING || $this->env === BLUEM_ENVIRONMENT_ACCEPTANCE)
+ && (($current_date === "2024-07-17"
+ && $current_time >= "06:30") || $current_date > "2024-07-17")
+ ) {
$timestamp = '20250717';
- } elseif ( ( $current_date === "2024-07-01" && $current_time >= "12:00" ) || $current_date > "2024-07-01") {
+ } elseif (
+ ( $current_date === "2024-07-01" && $current_time >= "12:00" )
+ || $current_date > "2024-07-01"
+ ) {
$timestamp = '20240701';
- } elseif ($this->env === BLUEM_ENVIRONMENT_TESTING && ( ( $current_date === "2023-06-28" && $current_time >= "08:00" ) || $current_date > "2023-06-28")) {
+ } elseif (
+ $this->env === BLUEM_ENVIRONMENT_TESTING
+ && ( ( $current_date === "2023-06-28" && $current_time >= "08:00" )
+ || $current_date > "2023-06-28")
+ ) {
$timestamp = '202306140200-202407050159';
- } elseif ($this->env === BLUEM_ENVIRONMENT_PRODUCTION && ( ( $current_date === "2023-07-04" && $current_time >= "08:00" ) || $current_date > "2023-07-04")) {
+ } elseif (
+ $this->env === BLUEM_ENVIRONMENT_PRODUCTION
+ && ( ( $current_date === "2023-07-04" && $current_time >= "08:00" )
+ || $current_date > "2023-07-04")
+ ) {
$timestamp = '202306140200-202407050159';
} else {
$timestamp = '202206090200-202307110159';
diff --git a/src/Validators/WebhookValidator.php b/src/Validators/WebhookValidator.php
index 04b852a..964b011 100644
--- a/src/Validators/WebhookValidator.php
+++ b/src/Validators/WebhookValidator.php
@@ -8,6 +8,7 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Validators;
abstract class WebhookValidator implements WebhookValidatorInterface
diff --git a/src/Validators/WebhookValidatorInterface.php b/src/Validators/WebhookValidatorInterface.php
index d5b0796..23b878f 100644
--- a/src/Validators/WebhookValidatorInterface.php
+++ b/src/Validators/WebhookValidatorInterface.php
@@ -8,6 +8,7 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Validators;
interface WebhookValidatorInterface
diff --git a/src/Validators/WebhookXMLValidator.php b/src/Validators/WebhookXMLValidator.php
index 5ca6a7d..95e369d 100644
--- a/src/Validators/WebhookXMLValidator.php
+++ b/src/Validators/WebhookXMLValidator.php
@@ -8,6 +8,7 @@
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
+
namespace Bluem\BluemPHP\Validators;
use SimpleXMLElement;
diff --git a/src/Validators/WebhookXmlValidation.php b/src/Validators/WebhookXmlValidation.php
index 5dea0da..ec18110 100644
--- a/src/Validators/WebhookXmlValidation.php
+++ b/src/Validators/WebhookXmlValidation.php
@@ -1,4 +1,5 @@
*
diff --git a/src/Webhook.php b/src/Webhook.php
index 01dd8e6..cd39953 100644
--- a/src/Webhook.php
+++ b/src/Webhook.php
@@ -1,4 +1,5 @@
*
@@ -44,8 +45,7 @@ public function __construct(
private function parse($xmlData = ''): void
{
- if (empty($xmlData))
- {
+ if (empty($xmlData)) {
if (!$this->isHttpsRequest()) {
$this->exitWithError();
return;
@@ -285,7 +285,7 @@ public function getDebtorAccountName(): ?string
return "";
}
- return $details->DebtorAccountName."" ?? "";
+ return $details->DebtorAccountName . "" ?? "";
}
public function getDebtorIBAN(): ?string
@@ -295,7 +295,7 @@ public function getDebtorIBAN(): ?string
return "";
}
- return $details->DebtorIBAN."" ?? "";
+ return $details->DebtorIBAN . "" ?? "";
}
public function getDebtorBankID(): ?string
@@ -305,7 +305,7 @@ public function getDebtorBankID(): ?string
return "";
}
- return $details->DebtorBankID."" ?? "";
+ return $details->DebtorBankID . "" ?? "";
}
@@ -360,17 +360,17 @@ public function getAcceptanceReportArray(): array
public function getRequestType(): string
{
- return $this->getPayload()->RequestType ."";
+ return $this->getPayload()->RequestType . "";
}
public function getAuthenticationAuthorityID(): string
{
- return $this->getPayload()->AuthenticationAuthorityID.'';
+ return $this->getPayload()->AuthenticationAuthorityID . '';
}
public function getAuthenticationAuthorityName(): string
{
- return $this->getPayload()->AuthenticationAuthorityName.'';
+ return $this->getPayload()->AuthenticationAuthorityName . '';
}
public function getIdentityReportArray(): array
@@ -382,29 +382,29 @@ public function getIdentityReportArray(): array
}
return [
- 'DateTime' => $report->DateTime.'',
- 'CustomerIDResponse' => $report->CustomerIDResponse.'',
+ 'DateTime' => $report->DateTime . '',
+ 'CustomerIDResponse' => $report->CustomerIDResponse . '',
'NameResponse' => [
- 'Initials'=>$report->NameResponse->Initials. '',
- 'LegalLastName'=>$report->NameResponse->LegalLastName. '',
- 'LegalLastNamePrefix'=>$report->NameResponse->LegalLastNamePrefix. '',
- 'PreferredLastName'=>$report->NameResponse->PreferredLastName. '',
- 'PreferredLastNamePrefix'=>$report->NameResponse->PreferredLastNamePrefix. '',
- 'PartnerLastName'=>$report->NameResponse->PartnerLastName. '',
- 'PartnerLastNamePrefix'=>$report->NameResponse->PartnerLastNamePrefix. '',
+ 'Initials' => $report->NameResponse->Initials . '',
+ 'LegalLastName' => $report->NameResponse->LegalLastName . '',
+ 'LegalLastNamePrefix' => $report->NameResponse->LegalLastNamePrefix . '',
+ 'PreferredLastName' => $report->NameResponse->PreferredLastName . '',
+ 'PreferredLastNamePrefix' => $report->NameResponse->PreferredLastNamePrefix . '',
+ 'PartnerLastName' => $report->NameResponse->PartnerLastName . '',
+ 'PartnerLastNamePrefix' => $report->NameResponse->PartnerLastNamePrefix . '',
],
'AddressResponse' => [
- 'Street'=>$report->AddressResponse->Street. '',
- 'HouseNumber'=>$report->AddressResponse->HouseNumber. '',
- 'HouseNumberSuffix'=>$report->AddressResponse->HouseNumberSuffix. '' ?? '',
- 'PostalCode'=>$report->AddressResponse->PostalCode. '',
- 'City'=>$report->AddressResponse->City. '',
- 'CountryCode'=>$report->AddressResponse->CountryCode. '',
+ 'Street' => $report->AddressResponse->Street . '',
+ 'HouseNumber' => $report->AddressResponse->HouseNumber . '',
+ 'HouseNumberSuffix' => $report->AddressResponse->HouseNumberSuffix . '' ?? '',
+ 'PostalCode' => $report->AddressResponse->PostalCode . '',
+ 'City' => $report->AddressResponse->City . '',
+ 'CountryCode' => $report->AddressResponse->CountryCode . '',
],
'BirthDateResponse' => $report->BirthDateResponse . '',
'GenderResponse' => $report->GenderResponse . '',
'TelephoneResponse1' => $report->TelephoneResponse1 . '',
- 'EmailResponse' => $report->EmailResponse .''
+ 'EmailResponse' => $report->EmailResponse . ''
];
}
}
diff --git a/tests/Acceptance/AcceptanceTestCase.php b/tests/Acceptance/AcceptanceTestCase.php
new file mode 100644
index 0000000..953da56
--- /dev/null
+++ b/tests/Acceptance/AcceptanceTestCase.php
@@ -0,0 +1,69 @@
+ 'test',
+ 'senderID' => 'S001',
+ 'test_accessToken' => 'INSERT_TEST_ACCESS_TOKEN_HERE',
+ 'production_accessToken' => '',
+ 'brandID' => $brandId,
+ 'merchantID' => 'INSERT_MERCHANT_ID_HERE',
+ 'merchantReturnURLBase' => 'https://example.test/return',
+ 'expectedReturnStatus' => 'success',
+ 'eMandateReason' => 'INSERT_EMANDATE_REASON_HERE',
+ 'localInstrumentCode' => 'CORE',
+ ],
+ $overrides
+ );
+
+ return new BluemConfiguration((object) $config);
+ }
+
+ /**
+ * Load a SimpleXML fixture as one of Bluem's response subclasses.
+ */
+ protected function loadXmlResponse(string $xml, string $className)
+ {
+ $response = simplexml_load_string($xml, $className);
+
+ if ($response === false) {
+ throw new \RuntimeException('Unable to parse XML fixture for ' . $className);
+ }
+
+ return $response;
+ }
+
+ /**
+ * Assert that a string contains all expected fragments.
+ */
+ protected function assertXmlContains(string $xml, string ...$fragments): void
+ {
+ foreach ($fragments as $fragment) {
+ self::assertStringContainsString($fragment, $xml);
+ }
+ }
+}
diff --git a/tests/Acceptance/IbanCheckAcceptanceTest.php b/tests/Acceptance/IbanCheckAcceptanceTest.php
new file mode 100644
index 0000000..c91d653
--- /dev/null
+++ b/tests/Acceptance/IbanCheckAcceptanceTest.php
@@ -0,0 +1,34 @@
+createConfiguration('S001Payment');
+
+ $request = new IBANBluemRequest(
+ $config,
+ 'IBAN-ENTRANCE-123',
+ 'NL66 ABNA 4097 0124 28',
+ 'D.J.M. Daan Jeroen Maarten Quackernaat ',
+ '1234'
+ );
+
+ $xml = $request->XmlString();
+
+ $this->assertXmlContains(
+ $xml,
+ 'NL66ABNA4097012428',
+ 'D.J.M. Daan Jeroen Maarten Quackernaat',
+ '1234'
+ );
+ }
+}
diff --git a/tests/Acceptance/IdentityAcceptanceTest.php b/tests/Acceptance/IdentityAcceptanceTest.php
new file mode 100644
index 0000000..172fc47
--- /dev/null
+++ b/tests/Acceptance/IdentityAcceptanceTest.php
@@ -0,0 +1,41 @@
+createConfiguration('BluemIdentity');
+
+ $request = new IdentityBluemRequest(
+ $config,
+ 'showConsumerGuiMYOWNENTRANCECODE77128',
+ 'success',
+ ['AddressRequest', 'BirthDateRequest'],
+ 'Beschrijving',
+ '1234',
+ 'http://localhost/code/etc/'
+ );
+
+ $request->selectDebtorWallet('INGBNL2A');
+ $request->enableStatusGUI();
+
+ $xml = $request->XmlString();
+
+ $this->assertXmlContains(
+ $xml,
+ '',
+ '',
+ 'Beschrijving',
+ '1234',
+ 'http://localhost/code/etc/?debtorReference=1234',
+ );
+ }
+}
diff --git a/tests/Acceptance/MandatesAcceptanceTest.php b/tests/Acceptance/MandatesAcceptanceTest.php
new file mode 100644
index 0000000..61bdd21
--- /dev/null
+++ b/tests/Acceptance/MandatesAcceptanceTest.php
@@ -0,0 +1,48 @@
+createConfiguration('BluemMandate', [
+ 'localInstrumentCode' => 'CORE',
+ 'merchantReturnURLBase' => 'https://example.test/return',
+ ]);
+
+ $request = new EmandateBluemRequest(
+ $config,
+ '56789',
+ '1234',
+ '134426345',
+ 'success'
+ );
+
+ $request->addAdditionalData('CustomerName', 'INSERT_VARIABLE_CUSTOMER_NAME_HERE');
+ $request->selectDebtorWallet('INGBNL2A');
+ $request->setBrandId('BluemMandate');
+
+ $xml = $request->XmlString();
+
+ $this->assertXmlContains(
+ $xml,
+ '134426345',
+ 'https://example.test/return?mandateID=134426345',
+ 'RCUR',
+ 'INSERT_EMANDATE_REASON_HERE',
+ '56789',
+ '56789-1234',
+ 'INSERT_VARIABLE_CUSTOMER_NAME_HERE',
+ '',
+ '',
+ 'INGBNL2A'
+ );
+ }
+}
diff --git a/tests/Acceptance/PaymentsAcceptanceTest.php b/tests/Acceptance/PaymentsAcceptanceTest.php
new file mode 100644
index 0000000..7d1e0d6
--- /dev/null
+++ b/tests/Acceptance/PaymentsAcceptanceTest.php
@@ -0,0 +1,85 @@
+createConfiguration('S001Payment', [
+ 'merchantReturnURLBase' => 'http://localhost:8000/?a=callback',
+ ]);
+
+ $request = new PaymentBluemRequest(
+ $config,
+ 'Beschrijving',
+ '1234',
+ 12.34,
+ '2026-04-12',
+ 'EUR',
+ 'TRANS123',
+ 'PAYMENT-ENTRANCE-123'
+ );
+
+ $request->selectDebtorWallet('INGBNL2A');
+ $request->setPaymentMethodToBancontact();
+
+ $xml = $request->XmlString();
+
+ $this->assertXmlContains(
+ $xml,
+ 'Beschrijving',
+ '1234',
+ 'EUR',
+ '12.34',
+ '2026-04-12T00:00:00.000Z',
+ 'http://localhost:8000/?a=callback?entranceCode=PAYMENT-ENTRANCE-123&transactionID=TRANS123',
+ '',
+ '',
+ );
+ }
+
+ public function testPaymentRequestSupportsCreditCardMethodBranch(): void
+ {
+ $config = $this->createConfiguration('S001Payment');
+
+ $request = new PaymentBluemRequest(
+ $config,
+ 'Beschrijving',
+ '1234',
+ 12.34,
+ '2026-04-12',
+ 'EUR',
+ 'TRANS456',
+ 'PAYMENT-ENTRANCE-456'
+ );
+
+ $request->setPaymentMethodToCreditCard(
+ '1234000012340000',
+ 'John Doe',
+ '123',
+ '03',
+ '2025'
+ );
+
+ self::assertTrue($request->getContext()->isCreditCard());
+
+ $xml = $request->XmlString();
+ $this->assertXmlContains(
+ $xml,
+ '',
+ '1234000012340000',
+ 'John Doe',
+ '123',
+ '',
+ '03',
+ '2025'
+ );
+ }
+}
diff --git a/tests/FakeHttpTransport.php b/tests/FakeHttpTransport.php
new file mode 100644
index 0000000..034fd7a
--- /dev/null
+++ b/tests/FakeHttpTransport.php
@@ -0,0 +1,45 @@
+
+ *
+ * This source file is subject to the license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Bluem\BluemPHP\Tests;
+
+use Bluem\BluemPHP\Transport\HttpTransportInterface;
+use Bluem\BluemPHP\Transport\HttpTransportResponse;
+
+final class FakeHttpTransport implements HttpTransportInterface
+{
+ public int $lastStatusCode = 0;
+
+ public string $lastBody = '';
+
+ /** @var string[] */
+ public array $lastHeaders = [];
+
+ public string $lastUrl = '';
+
+ private int $nextStatusCode = 200;
+
+ private string $nextBody = '';
+
+ public function setResponse(int $statusCode, string $body): void
+ {
+ $this->nextStatusCode = $statusCode;
+ $this->nextBody = $body;
+ }
+
+ public function send(string $url, array $headers, string $body): HttpTransportResponse
+ {
+ $this->lastUrl = $url;
+ $this->lastHeaders = $headers;
+ $this->lastBody = $body;
+ $this->lastStatusCode = $this->nextStatusCode;
+
+ return new HttpTransportResponse($this->nextStatusCode, $this->nextBody);
+ }
+}
diff --git a/tests/Integration/BluemGenericTestCase.php b/tests/Integration/BluemGenericTestCase.php
index ec47072..176d616 100644
--- a/tests/Integration/BluemGenericTestCase.php
+++ b/tests/Integration/BluemGenericTestCase.php
@@ -1,4 +1,5 @@
*
@@ -21,6 +22,18 @@
*/
abstract class BluemGenericTestCase extends TestCase
{
+ /**
+ * @var string[]
+ */
+ private const REQUIRED_ENVIRONMENT_VARIABLES = [
+ 'BLUEM_ENV',
+ 'BLUEM_SENDER_ID',
+ 'BLUEM_BRANDID',
+ 'BLUEM_TEST_ACCESS_TOKEN',
+ 'BLUEM_MERCHANTID',
+ 'BLUEM_MERCHANTRETURNURLBASE',
+ ];
+
/**
* The Bluem integration object
*/
@@ -31,14 +44,20 @@ abstract class BluemGenericTestCase extends TestCase
*
* @throws \Exception
*/
- protected function setUp() : void
+ protected function setUp(): void
{
- $env_file =__DIR__. '/../..';
+ $env_file = __DIR__ . '/../..';
$dotenv = Dotenv::createImmutable($env_file);
- $dotenv->load();
+ $dotenv->safeLoad();
+
+ foreach (self::REQUIRED_ENVIRONMENT_VARIABLES as $variable) {
+ if (!isset($_ENV[$variable]) || $_ENV[$variable] === '') {
+ $this->markTestSkipped(sprintf('Live Bluem integration tests require %s to be set.', $variable));
+ }
+ }
// Create a Bluem object and set the Bluem configuration details based on your .env file.
- $bluem_config = new stdClass;
+ $bluem_config = new stdClass();
$bluem_config->environment = $_ENV['BLUEM_ENV'];
$bluem_config->senderID = $_ENV['BLUEM_SENDER_ID'];
@@ -58,7 +77,7 @@ protected function setUp() : void
try {
$this->bluem = new Bluem($bluem_config);
} catch (\Exception $exception) {
- $this->fail("While initializing Bluem, ".$exception->getMessage()." occurred");
+ $this->fail("While initializing Bluem, " . $exception->getMessage() . " occurred");
}
}
@@ -87,7 +106,7 @@ public function testGetConfiguration(): void
/**
* Perform assertions based on a created BluemPHP Request object
*/
- protected function _finalizeBluemRequestAssertion(BluemRequestInterface $request) :void
+ protected function _finalizeBluemRequestAssertion(BluemRequestInterface $request): void
{
try {
// $this->assertEquals($request->getStatus(), "success");
diff --git a/tests/Integration/BluemMainTest.php b/tests/Integration/BluemMainTest.php
index 0af55b1..40f463c 100644
--- a/tests/Integration/BluemMainTest.php
+++ b/tests/Integration/BluemMainTest.php
@@ -1,4 +1,5 @@
fail("Could not create mandate: " . $exception->getMessage());
}
+ if ($response instanceof ErrorBluemResponse) {
+ $this->fail(
+ 'Got ErrorBluem response: ' . $response->Error()
+ );
+ }
+
$this->assertInstanceOf(
MandateTransactionBluemResponse::class,
$response
@@ -74,7 +80,6 @@ public function testCanMandateStatus(): void
// @todo: deal with the corresponding status if proper or improper status request
if ($response->Status()) {
-
$this->assertInstanceOf(
MandateStatusBluemResponse::class,
$response
diff --git a/tests/Integration/IPAPITest.php b/tests/Integration/IPAPITest.php
index 82dc781..5a03d94 100644
--- a/tests/Integration/IPAPITest.php
+++ b/tests/Integration/IPAPITest.php
@@ -1,4 +1,5 @@
*
@@ -9,6 +10,7 @@
namespace Bluem\BluemPHP\Tests\Integration;
use Bluem\BluemPHP\Extensions\IPAPI;
+use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
class IPAPITest extends TestCase
@@ -26,7 +28,7 @@ public function testCheckIsNetherlandsReturnsTrueIfNoIPAddressGiven(): void
$this->assertTrue($result);
}
- /** @dataProvider NetherlandsIPTestDataProvider */
+ #[DataProvider('NetherlandsIPTestDataProvider')]
public function testCheckIPAdressGivenDataProvider($ipAddress, $expectedNetherlands): void
{
$isNetherlands = $this->IPAPI->checkIsNetherlands($ipAddress);
@@ -38,8 +40,8 @@ public static function NetherlandsIPTestDataProvider(): array
{
return [
[
- 'ipAddress'=>'31.187.128.0',
- '$expectedNetherlands' => true,
+ 'ipAddress' => '31.187.128.0',
+ 'expectedNetherlands' => true,
]
// @todo: add true negative test and check for usage rate limits to prevent false negatives.
];
diff --git a/tests/Integration/IdentityRequestTest.php b/tests/Integration/IdentityRequestTest.php
index ca50772..f7421fe 100644
--- a/tests/Integration/IdentityRequestTest.php
+++ b/tests/Integration/IdentityRequestTest.php
@@ -1,4 +1,5 @@
*
@@ -8,7 +9,6 @@
namespace Bluem\BluemPHP\Tests\Integration;
-
class IdentityRequestTest extends BluemGenericTestCase
{
public function testCanCreateIdentityRequestWithWeirdCharacters()
diff --git a/tests/Integration/MandateRequestTest.php b/tests/Integration/MandateRequestTest.php
index 3fe5e91..30a418e 100644
--- a/tests/Integration/MandateRequestTest.php
+++ b/tests/Integration/MandateRequestTest.php
@@ -12,7 +12,6 @@
use Bluem\BluemPHP\Requests\EmandateBluemRequest;
-
class MandateRequestTest extends BluemGenericTestCase
{
public function testCanCreateMandateRequest()
@@ -27,7 +26,7 @@ public function testCanCreateMandateRequest()
"default"
);
} catch (\Exception $exception) {
- $this->fail("Exception while creating mandate request: ". $exception->getMessage());
+ $this->fail("Exception while creating mandate request: " . $exception->getMessage());
}
$this->assertInstanceOf(
diff --git a/tests/Integration/PaymentRequestTest.php b/tests/Integration/PaymentRequestTest.php
index 834d91d..775e99b 100644
--- a/tests/Integration/PaymentRequestTest.php
+++ b/tests/Integration/PaymentRequestTest.php
@@ -1,6 +1,6 @@
bluem->CreateEntranceCode();
$this->assertTrue(
- (is_string($entranceCode) && $entranceCode!==""),
+ (is_string($entranceCode) && $entranceCode !== ""),
"Valid entranceCode generated"
);
}
diff --git a/tests/Integration/WebhookTest.php b/tests/Integration/WebhookTest.php
index 48f3594..6692c47 100644
--- a/tests/Integration/WebhookTest.php
+++ b/tests/Integration/WebhookTest.php
@@ -1,4 +1,5 @@
*
@@ -8,7 +9,6 @@
namespace Bluem\BluemPHP\Tests\Integration;
-
class WebhookTest extends BluemGenericTestCase
{
/**
@@ -98,6 +98,6 @@ public function testCanPerformWebhookIdentity()
}
}
- $this->assertEquals('Success', $status, $fileName . ': Status not success: ' . $status);
+ $this->assertEquals('Success', $status, $fileName . ': Status not success: ' . $status);
}
}
diff --git a/tests/Unit/BluemConfigurationTest.php b/tests/Unit/BluemConfigurationTest.php
index a4d7a4f..53d34c6 100644
--- a/tests/Unit/BluemConfigurationTest.php
+++ b/tests/Unit/BluemConfigurationTest.php
@@ -74,4 +74,3 @@ private function getValidConfig(): stdClass
return $bluem_config;
}
}
-
diff --git a/tests/Unit/BluemTest.php b/tests/Unit/BluemTest.php
index f3dd97c..7a777e7 100644
--- a/tests/Unit/BluemTest.php
+++ b/tests/Unit/BluemTest.php
@@ -1,4 +1,5 @@
*
@@ -14,6 +15,7 @@
use Bluem\BluemPHP\Interfaces\BluemResponseInterface;
use Bluem\BluemPHP\Requests\BluemRequest;
use Bluem\BluemPHP\Responses\ErrorBluemResponse;
+use Bluem\BluemPHP\Tests\FakeHttpTransport;
use PHPUnit\Framework\TestCase;
use RuntimeException;
use stdClass;
@@ -21,6 +23,7 @@
class BluemTest extends TestCase
{
private Bluem $bluem;
+ private FakeHttpTransport $transport;
/**
* @throws InvalidBluemConfigurationException
@@ -29,7 +32,8 @@ protected function setUp(): void
{
// Mock the configuration as needed
$mockedConfig = $this->getConfig();
- $this->bluem = new Bluem($mockedConfig);
+ $this->transport = new FakeHttpTransport();
+ $this->bluem = new Bluem($mockedConfig, $this->transport);
}
@@ -46,13 +50,37 @@ public function testConstructorWithInvalidConfig(): void
}
- public function testMandateWithValidParameters(): void
+ public function testIdentityRequestWithValidParameters(): void
{
- // Test the Mandate method with valid parameters
- $response = $this->bluem->Mandate('customer_id', 'order_id', 'mandate_id');
+ $this->transport->setResponse(
+ 200,
+ <<<'XML'
+
+
+
+ https://test.viamijnbank.net/identity/transaction/1234abcdef
+ 1234abcdef
+ 1234
+
+
+XML
+ );
+
+ $request = $this->bluem->CreateIdentityRequest(
+ requestCategory: ['CustomerIDRequest', 'NameRequest'],
+ description: 'Identificatie test',
+ debtorReference: '1234',
+ entranceCode: '20260405095326915',
+ returnURL: 'http://localhost/code/etc/'
+ );
+
+ $response = $this->bluem->PerformRequest($request);
- // Assertions
$this->assertInstanceOf(BluemResponseInterface::class, $response);
+ $this->assertNotInstanceOf(ErrorBluemResponse::class, $response);
+ $this->assertNotSame('', $this->transport->lastUrl);
+ $this->assertStringStartsWith('xmlRequest=', $this->transport->lastBody);
+ $this->assertNotEmpty($this->transport->lastHeaders);
}
public function testMandateWithException(): void
@@ -86,7 +114,7 @@ public function testPerformRequestWithInvalidXml(): void
// helper classes
private function getConfig(): stdClass
{
- $bluem_config = new stdClass;
+ $bluem_config = new stdClass();
$bluem_config->environment = 'test';
$bluem_config->senderID = 'S12345';
diff --git a/tests/Unit/IbanResponseTest.php b/tests/Unit/IbanResponseTest.php
new file mode 100644
index 0000000..da47e19
--- /dev/null
+++ b/tests/Unit/IbanResponseTest.php
@@ -0,0 +1,76 @@
+
+
+
+
+ KNOWN
+ MATCH
+ D.J.M. Daan Jeroen Maarten Quackernaat
+ OPEN
+
+
+ PERSONAL
+ false
+ 1
+ Netherlands
+
+
+
+XML;
+
+ $response = $this->loadXmlResponse($xml, IBANNameCheckBluemResponse::class);
+
+ self::assertSame('KNOWN', $response->GetIBANResult());
+ self::assertSame('MATCH', $response->GetNameResult());
+ self::assertSame('D.J.M. Daan Jeroen Maarten Quackernaat', $response->GetSuggestedName());
+ self::assertSame('OPEN', $response->GetAccountStatus());
+ self::assertSame('PERSONAL', $response->GetAccountType());
+ self::assertSame('false', $response->GetIsJointAccount());
+ self::assertSame('1', $response->GetNumberOfAccountHolders());
+ self::assertSame('Netherlands', $response->GetCountryName());
+ }
+
+ public function testIbanResponseInvalidAndUnavailableStatesRemainParsable(): void
+ {
+ $invalidXml = <<<'XML'
+
+
+
+
+ INVALID
+
+
+
+XML;
+
+ $unavailableXml = <<<'XML'
+
+
+
+
+ SERVICE_TEMPORARILY_NOT_AVAILABLE
+
+
+
+XML;
+
+ $invalidResponse = $this->loadXmlResponse($invalidXml, IBANNameCheckBluemResponse::class);
+ $unavailableResponse = $this->loadXmlResponse($unavailableXml, IBANNameCheckBluemResponse::class);
+
+ self::assertSame('INVALID', $invalidResponse->GetIBANResult());
+ self::assertSame('SERVICE_TEMPORARILY_NOT_AVAILABLE', $unavailableResponse->GetIBANResult());
+ }
+}
diff --git a/tests/Unit/IdentityResponseTest.php b/tests/Unit/IdentityResponseTest.php
new file mode 100644
index 0000000..1c2ef17
--- /dev/null
+++ b/tests/Unit/IdentityResponseTest.php
@@ -0,0 +1,54 @@
+
+
+
+ https://test.viamijnbank.net/identity/transaction/1234abcdef
+ 1234abcdef
+ 1234
+
+
+XML;
+
+ $response = $this->loadXmlResponse($xml, IdentityTransactionBluemResponse::class);
+
+ self::assertSame('https://test.viamijnbank.net/identity/transaction/1234abcdef', $response->GetTransactionURL());
+ self::assertSame('1234abcdef', $response->GetTransactionID());
+ self::assertSame('1234', $response->GetDebtorReference());
+ }
+
+ public function testIdentityStatusResponseReadsIdentityReport(): void
+ {
+ $xml = <<<'XML'
+
+
+
+ AUTH-001
+ Success
+
+ Verified
+ INSERT_VARIABLE_CUSTOMER_NAME_HERE
+
+
+
+XML;
+
+ $response = $this->loadXmlResponse($xml, IdentityStatusBluemResponse::class);
+
+ self::assertSame('AUTH-001', $response->GetAuthenticationAuthorityID());
+ self::assertNotNull($response->GetIdentityReport());
+ self::assertSame('Verified', (string) $response->GetIdentityReport()->ReportStatus);
+ }
+}
diff --git a/tests/Unit/MandateResponseTest.php b/tests/Unit/MandateResponseTest.php
new file mode 100644
index 0000000..ffd2e9b
--- /dev/null
+++ b/tests/Unit/MandateResponseTest.php
@@ -0,0 +1,58 @@
+
+
+
+ https://test.viamijnbank.net/mandate/transaction/134426345
+ MANDATE-TX-123
+ 134426345
+
+
+XML;
+
+ $response = $this->loadXmlResponse($xml, MandateTransactionBluemResponse::class);
+
+ self::assertSame('https://test.viamijnbank.net/mandate/transaction/134426345', $response->GetTransactionURL());
+ self::assertSame('MANDATE-TX-123', $response->GetTransactionID());
+ self::assertSame('134426345', $response->GetMandateID());
+ }
+
+ public function testMandateStatusResponseReadsAcceptanceReportData(): void
+ {
+ $xml = <<<'XML'
+
+
+
+
+
+ NL66ABNA4097012428
+ ABNANL2A
+ D.J.M. Daan Jeroen Maarten Quackernaat
+ 250.00
+
+
+
+
+XML;
+
+ $response = $this->loadXmlResponse($xml, MandateStatusBluemResponse::class);
+
+ self::assertSame('NL66ABNA4097012428', $response->GetDebtorIBAN());
+ self::assertSame('ABNANL2A', $response->GetDebtorBankID());
+ self::assertSame('D.J.M. Daan Jeroen Maarten Quackernaat', $response->GetDebtorAccountName());
+ self::assertSame(250.00, $response->GetMaximumAmount()->amount);
+ self::assertSame('EUR', $response->GetMaximumAmount()->currency->code);
+ }
+}
diff --git a/tests/Unit/PaymentResponseTest.php b/tests/Unit/PaymentResponseTest.php
new file mode 100644
index 0000000..efa16f4
--- /dev/null
+++ b/tests/Unit/PaymentResponseTest.php
@@ -0,0 +1,56 @@
+
+
+
+ 2026-04-05T00:00:00Z
+ 1234134426345ae
+ 1234
+ 134426345ae
+ Success
+ 12.34
+ 12.34
+ EUR
+ IDEAL
+
+
+XML;
+
+ $response = $this->loadXmlResponse($xml, PaymentStatusBluemResponse::class);
+
+ self::assertTrue($response->Status());
+ self::assertSame('Success', $response->GetStatusCode());
+ }
+
+ public function testPaymentTransactionResponseReadsTransactionData(): void
+ {
+ $xml = <<<'XML'
+
+
+
+ https://test.viamijnbank.net/payment/transaction/TRANS123
+ TRANS123
+ 1234
+
+
+XML;
+
+ $response = $this->loadXmlResponse($xml, PaymentTransactionBluemResponse::class);
+
+ self::assertSame('https://test.viamijnbank.net/payment/transaction/TRANS123', $response->GetTransactionURL());
+ self::assertSame('TRANS123', $response->GetTransactionID());
+ self::assertSame('1234', $response->GetDebtorReference());
+ }
+}
diff --git a/tests/Unit/ResponseTestCase.php b/tests/Unit/ResponseTestCase.php
new file mode 100644
index 0000000..666b24e
--- /dev/null
+++ b/tests/Unit/ResponseTestCase.php
@@ -0,0 +1,23 @@
+