diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea652382..ae228866 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,7 +60,11 @@ jobs: grep -r -l "$string" tests/ | xargs sed -i "s/$string//g" - name: PHPUnit - run: composer exec phpunit -- --configuration phpunit.ci.xml --coverage-xml ./.coverage + run: | + case ${{ matrix.php }} in + 8.1|8.2|8.3 ) composer exec phpunit -- --configuration phpunit.ci.8.xml --coverage-xml ./.coverage;; + *) composer exec phpunit -- --configuration phpunit.ci.xml --coverage-xml ./.coverage;; + esac env: XDEBUG_MODE: coverage diff --git a/.gitignore b/.gitignore index dd74e98b..ee982aca 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ phpunit.xml .phpunit.cache/ composer.lock .DS_Store -.env \ No newline at end of file +.env +.coverage/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 75370ae0..b75e4d03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # CHANGELOG +## v2.2.0 - 2024-09-05 + +### Changes + +### 🚀 New Features + +- Add function for HMAC verification (#132) + +#### Contributors + +@Benjamin-Freoua-Alma, @CamilleFljt, @Francois-Gomis and @carine-bonnafous + ## v2.1.0 - 2024-07-29 ### Changes @@ -149,6 +161,7 @@ // Handle errors } + ``` * Add fields and docs to the Payment entity * Add a Refund entity and extract refunds data within the Payment entity constructor @@ -268,10 +281,15 @@ Getting more serious with a 1.0.0 release! 🎉 ## v0.0.1 * Initial "pre-release" of the API Client + * Includes two main endpoints: Payments and Merchants + * Provides what's necessary for a typical payment flow: + * `Payments.createPayment` and `Payments.eligibility` * `Merchants.me` * Base `Alma\API\Client` class can be configured with API key and live/test mode + * TLS is automatically forced to TLS 1.2 to meet Alma's security requirements, but configurable + diff --git a/Dockerfile b/Dockerfile index 12545aa0..8796849a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,7 @@ ARG COMPOSER_VERSION FROM composer:${COMPOSER_VERSION} as composer FROM php:${PHP_VERSION}-fpm +ARG PHP_VERSION ENV DEBIAN_FRONTEND noninteractive @@ -16,6 +17,11 @@ RUN apt update && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* +RUN case ${PHP_VERSION} in \ + 8.0|8.1|8.2|8.3 ) pecl install xdebug-3.3.2 && docker-php-ext-enable xdebug;; \ + *) pecl install xdebug-2.9.8 && docker-php-ext-enable xdebug;; \ + esac + RUN usermod -u 1000 www-data RUN groupmod -g 1000 www-data diff --git a/Dockerfile.legacy b/Dockerfile.legacy index 4223fa91..1692a16f 100644 --- a/Dockerfile.legacy +++ b/Dockerfile.legacy @@ -20,6 +20,10 @@ RUN apt update && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* +# Install xdebug for code coverage +RUN pecl install xdebug-2.5.5 \ +&& docker-php-ext-enable xdebug + RUN usermod -u 1000 www-data RUN groupmod -g 1000 www-data diff --git a/Taskfile.php.yml b/Taskfile.php.yml index 9c03f34f..65fc9160 100644 --- a/Taskfile.php.yml +++ b/Taskfile.php.yml @@ -27,9 +27,9 @@ tasks: cmds: - >- {{ if eq .PHP_VERSION "5.6" "7.0" }} - docker compose run {{ .COMPOSE_SERVICE }} ./tests/legacy_tests.sh + docker compose run --rm {{ .COMPOSE_SERVICE }} ./tests/legacy_tests.sh {{ else }} - docker compose run {{ .COMPOSE_SERVICE }} composer exec phpunit --verbose -- --configuration phpunit.dist.xml --testsuite "Alma PHP Client Unit Test Suite" + docker compose run --rm {{ .COMPOSE_SERVICE }} composer exec phpunit --verbose -- --configuration {{ .PHPUNIT_FILE }} --testsuite "Alma PHP Client Unit Test Suite" --coverage-xml ./.coverage {{ end }} tests:integration: @@ -39,9 +39,9 @@ tasks: cmds: - >- {{ if eq .PHP_VERSION "5.6" "7.0" }} - docker compose run {{ .COMPOSE_SERVICE }} ./tests/legacy_integration_tests.sh + docker compose run --rm {{ .COMPOSE_SERVICE }} ./tests/legacy_integration_tests.sh {{ else }} - docker compose run {{ .COMPOSE_SERVICE }} composer exec phpunit --verbose -- --configuration phpunit.dist.xml --testsuite "Alma PHP Client Integration Test Suite" + docker compose run --rm {{ .COMPOSE_SERVICE }} composer exec phpunit --verbose -- --configuration {{ .PHPUNIT_FILE }} --testsuite "Alma PHP Client Integration Test Suite" {{ end }} shell: @@ -49,5 +49,5 @@ tasks: deps: - docker:build cmds: - - sed 's/{MYVERSION}/{{ .PHPUNIT_VERSION }}/g' phpunit.dist.xml > phpunit.xml - - docker compose run {{ .COMPOSE_SERVICE }} bash + - sed 's/{MYVERSION}/{{ .PHPUNIT_VERSION }}/g' {{ .PHPUNIT_FILE }} > phpunit.xml + - docker compose run --rm {{ .COMPOSE_SERVICE }} bash diff --git a/Taskfile.yml b/Taskfile.yml index 74ede9f8..2e09d806 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -44,14 +44,17 @@ includes: taskfile: Taskfile.php.yml vars: PHP_VERSION: "8.1" + PHPUNIT_FILE: phpunit.dist.8.xml "8.2": taskfile: Taskfile.php.yml vars: PHP_VERSION: "8.2" + PHPUNIT_FILE: phpunit.dist.8.xml "8.3": taskfile: Taskfile.php.yml vars: PHP_VERSION: "8.3" + PHPUNIT_FILE: phpunit.dist.8.xml tasks: default: diff --git a/compose.yml b/compose.yml index b73909d7..7c3bd0c7 100644 --- a/compose.yml +++ b/compose.yml @@ -12,6 +12,8 @@ services: volumes: - ./:/app - /app/vendor + environment: + XDEBUG_MODE: coverage php-legacy: user: ${UID:-1000}:${GID:-1000} diff --git a/composer.json b/composer.json index 6e02ebf4..f9c14f5e 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "alma/alma-php-client", "description": "PHP API client for the Alma payments API", - "version": "2.1.0", + "version": "2.2.0", "type": "library", "require": { "php": "^5.6 || ~7.0 || ~7.1 || ~7.2 || ~7.3 || ~7.4 || ~8.0 || ~8.1 || ~8.2 || ~8.3", diff --git a/phpunit.ci.8.xml b/phpunit.ci.8.xml new file mode 100644 index 00000000..851e6fb7 --- /dev/null +++ b/phpunit.ci.8.xml @@ -0,0 +1,32 @@ + + + + + + + tests/Unit + + + + + + + + + ./src/* + + + + diff --git a/phpunit.dist.8.xml b/phpunit.dist.8.xml new file mode 100644 index 00000000..550e8793 --- /dev/null +++ b/phpunit.dist.8.xml @@ -0,0 +1,40 @@ + + + + + + tests/Unit + + + tests/Integration + + + + + + + + + ./src/* + + + + + + + + diff --git a/phpunit.dist.xml b/phpunit.dist.xml index 72837d2a..e3ff90a3 100644 --- a/phpunit.dist.xml +++ b/phpunit.dist.xml @@ -1,5 +1,10 @@ - + tests/Unit @@ -8,6 +13,11 @@ tests/Integration + + + ./src/* + + diff --git a/src/Client.php b/src/Client.php index ed371ea6..5176acb3 100644 --- a/src/Client.php +++ b/src/Client.php @@ -30,7 +30,7 @@ class Client { - const VERSION = '2.1.0'; + const VERSION = '2.2.0'; const LIVE_MODE = 'live'; const TEST_MODE = 'test'; diff --git a/src/Lib/PaymentValidator.php b/src/Lib/PaymentValidator.php index 8bdda9e5..cc4f0f0c 100644 --- a/src/Lib/PaymentValidator.php +++ b/src/Lib/PaymentValidator.php @@ -34,6 +34,9 @@ */ class PaymentValidator { + + const HEADER_SIGNATURE_KEY = 'X-Alma-Signature'; + /** * Ensure that the purchase amount is an integer * @@ -53,4 +56,17 @@ public static function checkPurchaseAmount($data) )); } } -} \ No newline at end of file + + /** + * @param string $data + * @param string $apiKey + * @param string $signature + * @return bool + */ + public function isHmacValidated($data, $apiKey, $signature) + { + return is_string($data) && + is_string($apiKey) && + hash_hmac('sha256', $data, $apiKey) === $signature; + } +} diff --git a/tests/Unit/Lib/PaymentValidatorTest.php b/tests/Unit/Lib/PaymentValidatorTest.php new file mode 100644 index 00000000..284288f4 --- /dev/null +++ b/tests/Unit/Lib/PaymentValidatorTest.php @@ -0,0 +1,128 @@ +paymentValidator = new PaymentValidator(); + } + + /** + * @dataProvider checkHmacInvalidDataProvider + * @param $data + * @param $apiKey + * @param $signature + * @return void + */ + public function testHmacDataDifferentFromSignature($data, $apiKey, $signature) + { + $this->assertFalse($this->paymentValidator->isHmacValidated($data, $apiKey, $signature)); + } + + public function testHmacDataEqualsSignature() + { + $data = 'payment_id_test'; + $apiKey = 'api_key_test'; + $signature = '4545854d3b8704d4b21cf88bc8b5da5680c46b2ab9d45c8cffe6278d8a8b1860'; + + $this->assertTrue($this->paymentValidator->isHmacValidated($data, $apiKey, $signature)); + } + + public static function checkHmacInvalidDataProvider() + { + return [ + 'String data' => [ + 'data' => 'payment_id_test', + 'apiKey' => 'api_key_test', + 'signature' => 'wrong_signature' + ], + 'Empty array data' => [ + 'data' => [], + 'apiKey' => 'api_key_test', + 'signature' => 'wrong_signature' + ], + 'Empty array apiKey' => [ + 'data' => 'payment_id_test', + 'apiKey' => [], + 'signature' => 'wrong_signature' + ], + 'Empty array signature' => [ + 'data' => 'payment_id_test', + 'apiKey' => 'api_key_test', + 'signature' => [] + ], + 'Empty string data' => [ + 'data' => '', + 'apiKey' => 'api_key_test', + 'signature' => 'wrong_signature' + ], + 'Empty string apiKey' => [ + 'data' => 'payment_id_test', + 'apiKey' => '', + 'signature' => 'wrong_signature' + ], + 'Empty string signature' => [ + 'data' => 'payment_id_test', + 'apiKey' => 'api_key_test', + 'signature' => '' + ], + 'Object data' => [ + 'data' => new stdClass(), + 'apiKey' => 'api_key_test', + 'signature' => 'wrong_signature' + ], + 'Object apiKey' => [ + 'data' => 'payment_id_test', + 'apiKey' => new stdClass(), + 'signature' => 'wrong_signature' + ], + 'Object signature' => [ + 'data' => 'payment_id_test', + 'apiKey' => 'api_key_test', + 'signature' => new stdClass() + ], + 'Boolean data' => [ + 'data' => false, + 'apiKey' => 'api_key_test', + 'signature' => 'wrong_signature' + ], + 'Boolean apiKey' => [ + 'data' => 'payment_id_test', + 'apiKey' => true, + 'signature' => 'wrong_signature' + ], + 'Boolean signature' => [ + 'data' => 'payment_id_test', + 'apiKey' => 'api_key_test', + 'signature' => true + ], + 'Int data' => [ + 'data' => 1, + 'apiKey' => 'api_key_test', + 'signature' => 'wrong_signature' + ], + 'Int apiKey' => [ + 'data' => 'payment_id_test', + 'apiKey' => 2, + 'signature' => 'wrong_signature' + ], + 'Int signature' => [ + 'data' => 'payment_id_test', + 'apiKey' => 'api_key_test', + 'signature' => 3 + ] + + ]; + } +} diff --git a/tests/legacy_tests.sh b/tests/legacy_tests.sh index b21daaf9..a3eca141 100755 --- a/tests/legacy_tests.sh +++ b/tests/legacy_tests.sh @@ -11,4 +11,4 @@ string=': void' grep -r -l "$string" tests/ | xargs sed -i "s/$string//g" # Run tests -composer exec phpunit --verbose -- --configuration phpunit.dist.xml --testsuite "Alma PHP Client Unit Test Suite" +composer exec phpunit --verbose -- --configuration phpunit.dist.xml --testsuite "Alma PHP Client Unit Test Suite" --coverage-xml ./.coverage