diff --git a/.github/workflows/backport-pull-request.yml b/.github/workflows/backport-pull-request.yml index 61b98b7..da73910 100644 --- a/.github/workflows/backport-pull-request.yml +++ b/.github/workflows/backport-pull-request.yml @@ -13,7 +13,7 @@ jobs: create-backport-pull-request: if: ${{ (github.event.pull_request.merged == true) && (contains(github.event.pull_request.labels.*.name, 'release')) }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -28,7 +28,7 @@ jobs: git reset --hard main - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v7 with: commit-message: 'chore: backport main to develop' title: Backport main to develop diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae22886..fe2d47a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ on: jobs: ci: name: Linting, tests and coverage - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false diff --git a/.github/workflows/hotfix-pull-request.yml b/.github/workflows/hotfix-pull-request.yml index 02eb960..9325fc7 100644 --- a/.github/workflows/hotfix-pull-request.yml +++ b/.github/workflows/hotfix-pull-request.yml @@ -11,7 +11,7 @@ on: jobs: create-hotfix-pull-request: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -49,7 +49,7 @@ jobs: ./scripts/update-files-with-release-version.sh ${{ steps.release-drafter.outputs.tag_name }} - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v7 with: commit-message: 'chore: update version' title: Release ${{ steps.release-drafter.outputs.tag_name }} diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 62015b2..d3f2d9e 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -9,7 +9,7 @@ jobs: contents: read pull-requests: write - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: TimonVS/pr-labeler-action@v5 env: diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index b172312..fa12879 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -11,7 +11,7 @@ jobs: release: if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release') - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -113,7 +113,7 @@ jobs: cc <@france.berut> <@khadija.cherif> - name: Send changelog to Slack - uses: slackapi/slack-github-action@v1.26.0 + uses: slackapi/slack-github-action@v1.27.0 with: channel-id: CR9C57YM6 slack-message: ${{ steps.slack-markdown-release-notes.outputs.text }} diff --git a/.github/workflows/release-pull-request.yml b/.github/workflows/release-pull-request.yml index 70ee17e..76f6e59 100644 --- a/.github/workflows/release-pull-request.yml +++ b/.github/workflows/release-pull-request.yml @@ -6,7 +6,7 @@ on: jobs: create-release-pull-request: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: @@ -55,7 +55,7 @@ jobs: repositories: alma-php-client - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v7 with: token: ${{ steps.github-token.outputs.token }} commit-message: 'chore: update version' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1c838fa..bc207b6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: check-added-large-files args: ["--maxkb=1024"] @@ -35,7 +35,7 @@ repos: stages: [commit] - repo: https://github.com/returntocorp/semgrep - rev: v1.27.0 + rev: v1.92.0 hooks: - id: semgrep args: diff --git a/CHANGELOG.md b/CHANGELOG.md index b75e4d0..e884bb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # CHANGELOG +## v2.3.0 - 2024-11-04 + +### Changes + +- chore(deps): update pre-commit hook returntocorp/semgrep to v1.92.0 (#116) + +### 🚀 New Features + +- feat: replace payment validator hmac verification by request hmac val… (#147) +- Change CmsInfo themes to theme_name and theme_version (#146) +- Add isUrlRefreshRequired function (#145) +- Make nullable for all attributes in CmsInfo and CmsFeatures (#143) +- Create endpoint and formatter for gather cms data (#142) + +#### Contributors + +@Benjamin-Freoua-Alma, @Francois-Gomis, @alma-renovate-bot, @alma-renovate-bot[bot], @github-actions, @hyahiaoui, @joyet-simon and @remi-zuffinetti + ## v2.2.0 - 2024-09-05 ### Changes @@ -162,6 +180,7 @@ } + ``` * Add fields and docs to the Payment entity * Add a Refund entity and extract refunds data within the Payment entity constructor diff --git a/composer.json b/composer.json index f9c14f5..24fe5ee 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.2.0", + "version": "2.3.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/src/Client.php b/src/Client.php index 5176acb..010c328 100644 --- a/src/Client.php +++ b/src/Client.php @@ -30,7 +30,7 @@ class Client { - const VERSION = '2.2.0'; + const VERSION = '2.3.0'; const LIVE_MODE = 'live'; const TEST_MODE = 'test'; diff --git a/src/Endpoints/Merchants.php b/src/Endpoints/Merchants.php index f23b8af..baa8c79 100644 --- a/src/Endpoints/Merchants.php +++ b/src/Endpoints/Merchants.php @@ -27,11 +27,12 @@ use Alma\API\Entities\FeePlan; use Alma\API\Entities\Merchant; +use Alma\API\Entities\MerchantData\MerchantData; +use Alma\API\Exceptions\RequestException; use Alma\API\RequestError; class Merchants extends Base { - const MERCHANTS_PATH = '/v1/merchants'; const ME_PATH = '/v1/me'; /** @@ -81,4 +82,20 @@ public function feePlans($kind = FeePlan::KIND_GENERAL, $installmentsCounts = "a return new FeePlan($val); }, $res->json); } + + /** + * @param string $url The URL to send to Alma for integrations configuration + * @throws RequestException + * @throws RequestError + */ + public function sendIntegrationsConfigurationsUrl($url) + { + $res = $this->request(self::ME_PATH . "/configuration")->setRequestBody(array( + "endpoint_url" => $url + ))->post(); + + if ($res->isError()) { + throw new RequestException($res->errorMessage, null, $res); + } + } } diff --git a/src/Entities/MerchantData/CmsFeatures.php b/src/Entities/MerchantData/CmsFeatures.php new file mode 100644 index 0000000..5b73b0a --- /dev/null +++ b/src/Entities/MerchantData/CmsFeatures.php @@ -0,0 +1,113 @@ +|null + */ + private $specificFeatures; + + /** + * @var string[]|null + */ + private $countryRestriction; + + /** + * @var bool | null + */ + private $isMultisite; + + /** + * @var bool | null + */ + private $customWidgetCss; + + /** + * CmsFeatures constructor. + * @param array $cmsFeaturesDataArray + */ + public function __construct($cmsFeaturesDataArray) + { + // Ensure values are properly initialized + $this->almaEnabled = isset($cmsFeaturesDataArray['alma_enabled']) ? $cmsFeaturesDataArray['alma_enabled'] : null; + $this->widgetCartActivated = isset($cmsFeaturesDataArray['widget_cart_activated']) ? $cmsFeaturesDataArray['widget_cart_activated'] : null; + $this->widgetProductActivated = isset($cmsFeaturesDataArray['widget_product_activated']) ? $cmsFeaturesDataArray['widget_product_activated'] : null; + $this->usedFeePlans = isset($cmsFeaturesDataArray['used_fee_plans']) ? $cmsFeaturesDataArray['used_fee_plans'] : null; + $this->inPageActivated = isset($cmsFeaturesDataArray['in_page_activated']) ? $cmsFeaturesDataArray['in_page_activated'] : null; + $this->logActivated = isset($cmsFeaturesDataArray['log_activated']) ? $cmsFeaturesDataArray['log_activated'] : null; + $this->excludedCategories = isset($cmsFeaturesDataArray['excluded_categories']) ? $cmsFeaturesDataArray['excluded_categories'] : null; + $this->paymentMethodPosition = isset($cmsFeaturesDataArray['payment_method_position']) ? $cmsFeaturesDataArray['payment_method_position'] : null; + $this->specificFeatures = isset($cmsFeaturesDataArray['specific_features']) ? $cmsFeaturesDataArray['specific_features'] : null; + $this->countryRestriction = isset($cmsFeaturesDataArray['country_restriction']) ? $cmsFeaturesDataArray['country_restriction'] : null; + $this->isMultisite = isset($cmsFeaturesDataArray['is_multisite']) ? $cmsFeaturesDataArray['is_multisite'] : null; + $this->customWidgetCss = isset($cmsFeaturesDataArray['custom_widget_css']) ? $cmsFeaturesDataArray['custom_widget_css'] : null; + } + + /** + * @return array + */ + public function getProperties() + { + // Use array_filter with ARRAY_FILTER_USE_BOTH to remove null or empty values + return array_filter([ + 'alma_enabled' => $this->almaEnabled, + 'widget_cart_activated' => $this->widgetCartActivated, + 'widget_product_activated' => $this->widgetProductActivated, + 'used_fee_plans' => $this->usedFeePlans, + 'in_page_activated' => $this->inPageActivated, + 'log_activated' => $this->logActivated, + 'excluded_categories' => $this->excludedCategories, + 'payment_method_position' => $this->paymentMethodPosition, + 'specific_features' => $this->specificFeatures, + 'country_restriction' => $this->countryRestriction, + 'is_multisite' => $this->isMultisite, + 'custom_widget_css' => $this->customWidgetCss, + ], function ($value) { + // Keep only values that are not null and not empty + // But keep false or 0 values + return !is_null($value) && $value !== ''; + }); + } +} \ No newline at end of file diff --git a/src/Entities/MerchantData/CmsInfo.php b/src/Entities/MerchantData/CmsInfo.php new file mode 100644 index 0000000..25b4e38 --- /dev/null +++ b/src/Entities/MerchantData/CmsInfo.php @@ -0,0 +1,99 @@ +|null + */ + private $thirdPartiesPlugins; + + /** + * @var string + */ + private $languageName; + + /** + * @var string + */ + private $languageVersion; + + /** + * @var string + */ + private $almaPluginVersion; + + /** + * @var string + */ + private $almaSdkVersion; + + /** + * @var string + */ + private $almaSdkName; + + /** + * @var string | null + */ + private $themeName; + + /** + * @var null|string + */ + private $themeVersion; + + /** + * CmsInfo constructor. + * @param array $cmsInfoDataArray + */ + public function __construct($cmsInfoDataArray) + { + // Initialize values or set them to null if not available + $this->cmsName = isset($cmsInfoDataArray['cms_name']) ? $cmsInfoDataArray['cms_name'] : ''; + $this->cmsVersion = isset($cmsInfoDataArray['cms_version']) ? $cmsInfoDataArray['cms_version'] : ''; + $this->thirdPartiesPlugins = isset($cmsInfoDataArray['third_parties_plugins']) ? $cmsInfoDataArray['third_parties_plugins'] : null; + $this->themeName = isset($cmsInfoDataArray['theme_name']) ? $cmsInfoDataArray['theme_name'] : ''; + $this->themeVersion = isset($cmsInfoDataArray['theme_version']) ? $cmsInfoDataArray['theme_version'] : ''; + $this->languageName = isset($cmsInfoDataArray['language_name']) ? $cmsInfoDataArray['language_name'] : ''; + $this->languageVersion = isset($cmsInfoDataArray['language_version']) ? $cmsInfoDataArray['language_version'] : ''; + $this->almaPluginVersion = isset($cmsInfoDataArray['alma_plugin_version']) ? $cmsInfoDataArray['alma_plugin_version'] : ''; + $this->almaSdkVersion = isset($cmsInfoDataArray['alma_sdk_version']) ? $cmsInfoDataArray['alma_sdk_version'] : ''; + $this->almaSdkName = isset($cmsInfoDataArray['alma_sdk_name']) ? $cmsInfoDataArray['alma_sdk_name'] : ''; + } + + /** + * @return array + */ + public function getProperties() + { + // Use array_filter with ARRAY_FILTER_USE_BOTH to remove null or empty values + return array_filter([ + 'cms_name' => $this->cmsName, + 'cms_version' => $this->cmsVersion, + 'third_parties_plugins' => $this->thirdPartiesPlugins, + 'theme_name' => $this->themeName, + 'theme_version' => $this->themeVersion, + 'language_name' => $this->languageName, + 'language_version' => $this->languageVersion, + 'alma_plugin_version' => $this->almaPluginVersion, + 'alma_sdk_version' => $this->almaSdkVersion, + 'alma_sdk_name' => $this->almaSdkName, + ], function ($value) { + // Keep only values that are not null and not empty + // But keep false or 0 values + return !is_null($value) && $value !== ''; + }); + } +} \ No newline at end of file diff --git a/src/Lib/IntegrationsConfigurationsUtils.php b/src/Lib/IntegrationsConfigurationsUtils.php new file mode 100644 index 0000000..dee516c --- /dev/null +++ b/src/Lib/IntegrationsConfigurationsUtils.php @@ -0,0 +1,16 @@ + $oneMonthInSeconds; + } +} \ No newline at end of file diff --git a/src/Lib/PayloadFormatter.php b/src/Lib/PayloadFormatter.php new file mode 100644 index 0000000..e7d7127 --- /dev/null +++ b/src/Lib/PayloadFormatter.php @@ -0,0 +1,23 @@ + $cmsInfo->getProperties(), + "cms_features" => $cmsFeatures->getProperties(), + ]; + } + +} \ No newline at end of file diff --git a/src/Lib/PaymentValidator.php b/src/Lib/PaymentValidator.php index cc4f0f0..5fe9f90 100644 --- a/src/Lib/PaymentValidator.php +++ b/src/Lib/PaymentValidator.php @@ -58,15 +58,16 @@ public static function checkPurchaseAmount($data) } /** + * Validate the HMAC signature of the request + * * @param string $data * @param string $apiKey * @param string $signature + * @deprecated Use RequestUtils::isHmacValidated instead * @return bool */ public function isHmacValidated($data, $apiKey, $signature) { - return is_string($data) && - is_string($apiKey) && - hash_hmac('sha256', $data, $apiKey) === $signature; + return RequestUtils::isHmacValidated($data, $apiKey, $signature); } } diff --git a/src/Lib/RequestUtils.php b/src/Lib/RequestUtils.php new file mode 100644 index 0000000..3b82601 --- /dev/null +++ b/src/Lib/RequestUtils.php @@ -0,0 +1,22 @@ +merchantEndpoint = Mockery::mock(Merchants::class)->makePartial(); + $this->responseMock = Mockery::mock(Response::class); + $this->requestObject = Mockery::mock(Request::class); + } + + public function tearDown(): void + { + $this->merchantEndpoint = null; + $this->responseMock = null; + $this->requestObject = null; + Mockery::close(); + } + + public function testSendIntegrationsConfigurationsUrlIsOk(){ + $this->responseMock->shouldReceive('isError')->once()->andReturn(false); + $this->requestObject->shouldReceive('setRequestBody') + ->with(['endpoint_url' => self::URL]) + ->andReturn($this->requestObject); + + $this->merchantEndpoint->shouldReceive('request') + ->with(Merchants::ME_PATH . "/configuration") + ->once() + ->andReturn($this->requestObject); + $this->requestObject->shouldReceive('post')->once()->andReturn($this->responseMock); + + $url = self::URL; + $this->assertNull($this->merchantEndpoint->sendIntegrationsConfigurationsUrl($url)); + } + + public function testSendIntegrationsConfigurationsUrlThrowRequestException(){ + $this->responseMock->shouldReceive('isError')->once()->andReturn(true); + $this->requestObject->shouldReceive('setRequestBody') + ->with(['endpoint_url' => self::URL]) + ->andReturn($this->requestObject); + + $this->merchantEndpoint->shouldReceive('request') + ->with(Merchants::ME_PATH . "/configuration") + ->once() + ->andReturn($this->requestObject); + $this->requestObject->shouldReceive('post')->once()->andReturn($this->responseMock); + + $url = self::URL; + $this->expectException(RequestException::class); + $this->merchantEndpoint->sendIntegrationsConfigurationsUrl($url); + + } +} \ No newline at end of file diff --git a/tests/Unit/Entities/CmsFeaturesTest.php b/tests/Unit/Entities/CmsFeaturesTest.php new file mode 100644 index 0000000..814594f --- /dev/null +++ b/tests/Unit/Entities/CmsFeaturesTest.php @@ -0,0 +1,128 @@ + true, + 'widget_cart_activated' => true, + 'widget_product_activated' => false, + 'used_fee_plans' => ['Plan A'], + 'payment_method_position' => 1, + 'in_page_activated' => true, + 'log_activated' => false, + 'excluded_categories' => ['category1', 'category2'], + 'specific_features' => ['feature1', 'feature2'], + 'country_restriction' => ['FR', 'US'], + 'is_multisite' => false, + 'custom_widget_css' => true, + ]; + + $cmsFeatures = new CmsFeatures($data); + + $this->assertTrue($cmsFeatures->getProperties()['alma_enabled']); + $this->assertTrue($cmsFeatures->getProperties()['widget_cart_activated']); + $this->assertFalse($cmsFeatures->getProperties()['widget_product_activated']); + $this->assertEquals(['Plan A'], $cmsFeatures->getProperties()['used_fee_plans']); + $this->assertEquals(1, $cmsFeatures->getProperties()['payment_method_position']); + $this->assertTrue($cmsFeatures->getProperties()['in_page_activated']); + $this->assertFalse($cmsFeatures->getProperties()['log_activated']); + $this->assertEquals(['category1', 'category2'], $cmsFeatures->getProperties()['excluded_categories']); + $this->assertEquals(['feature1', 'feature2'], $cmsFeatures->getProperties()['specific_features']); + $this->assertEquals(['FR', 'US'], $cmsFeatures->getProperties()['country_restriction']); + $this->assertFalse($cmsFeatures->getProperties()['is_multisite']); + $this->assertTrue($cmsFeatures->getProperties()['custom_widget_css']); + } + + public function testConstructorHandlesNullValuesCorrectly() + { + $data = [ + 'alma_enabled' => null, + 'widget_cart_activated' => null, + 'widget_product_activated' => null, + 'used_fee_plans' => null, + 'payment_method_position' => null, + 'in_page_activated' => null, + 'log_activated' => null, + 'excluded_categories' => null, + 'specific_features' => null, + 'country_restriction' => null, + 'is_multisite' => null, + 'custom_widget_css' => null, + ]; + + $cmsFeatures = new CmsFeatures($data); + $properties = $cmsFeatures->getProperties(); + + $this->assertArrayNotHasKey('alma_enabled', $properties); + $this->assertArrayNotHasKey('widget_cart_activated', $properties); + $this->assertArrayNotHasKey('widget_product_activated', $properties); + $this->assertArrayNotHasKey('used_fee_plans', $properties); + $this->assertArrayNotHasKey('payment_method_position', $properties); + $this->assertArrayNotHasKey('in_page_activated', $properties); + $this->assertArrayNotHasKey('excluded_categories', $properties); + $this->assertArrayNotHasKey('specific_features', $properties); + $this->assertArrayNotHasKey('country_restriction', $properties); + $this->assertArrayNotHasKey('is_multisite', $properties); + $this->assertArrayNotHasKey('custom_widget_css', $properties); + } + + public function testGetPropertiesFiltersOutNullAndEmptyValues() + { + $data = [ + 'alma_enabled' => true, + 'widget_cart_activated' => null, + 'widget_product_activated' => null, + 'used_fee_plans' => 'Plan B', + 'payment_method_position' => null, + 'in_page_activated' => false, + 'log_activated' => null, + 'excluded_categories' => ['category3'], + 'specific_features' => [], + 'country_restriction' => [], + 'is_multisite' => false, + 'custom_widget_css' => null, + ]; + + $cmsFeatures = new CmsFeatures($data); + $properties = $cmsFeatures->getProperties(); + + $this->assertArrayHasKey('alma_enabled', $properties); + $this->assertArrayNotHasKey('widget_cart_activated', $properties); // Should be filtered out (empty string) + $this->assertArrayNotHasKey('widget_product_activated', $properties); // Should be filtered out (null) + $this->assertArrayHasKey('used_fee_plans', $properties); + $this->assertArrayNotHasKey('payment_method_position', $properties); // Should be filtered out (null) + $this->assertArrayHasKey('in_page_activated', $properties); + $this->assertArrayHasKey('excluded_categories', $properties); + $this->assertArrayHasKey('specific_features', $properties); + $this->assertArrayHasKey('country_restriction', $properties); + $this->assertArrayHasKey('is_multisite', $properties); + $this->assertArrayNotHasKey('custom_widget_css', $properties); // Should be filtered out (null) + } + + public function testGetPropertiesFiltersOutWithEmptyData() + { + $data = []; + + $cmsFeatures = new CmsFeatures($data); + $properties = $cmsFeatures->getProperties(); + + $this->assertArrayNotHasKey('alma_enabled', $properties); + $this->assertArrayNotHasKey('widget_cart_activated', $properties); + $this->assertArrayNotHasKey('widget_product_activated', $properties); + $this->assertArrayNotHasKey('used_fee_plans', $properties); + $this->assertArrayNotHasKey('payment_method_position', $properties); + $this->assertArrayNotHasKey('in_page_activated', $properties); + $this->assertArrayNotHasKey('excluded_categories', $properties); + $this->assertArrayNotHasKey('specific_features', $properties); + $this->assertArrayNotHasKey('country_restriction', $properties); + $this->assertArrayNotHasKey('is_multisite', $properties); + $this->assertArrayNotHasKey('custom_widget_css', $properties); + } +} \ No newline at end of file diff --git a/tests/Unit/Entities/CmsInfoTest.php b/tests/Unit/Entities/CmsInfoTest.php new file mode 100644 index 0000000..80a9710 --- /dev/null +++ b/tests/Unit/Entities/CmsInfoTest.php @@ -0,0 +1,117 @@ + 'WordPress', + 'cms_version' => '5.8', + 'third_parties_plugins' => ['plugin1', 'plugin2'], + 'theme_name' => 'theme1', + 'theme_version' => '1.1.0', + 'language_name' => 'PHP', + 'language_version' => '7.4', + 'alma_plugin_version' => '1.0.0', + 'alma_sdk_version' => self::SDK_VERSION, + 'alma_sdk_name' => self::SDK_NAME + ]; + + $cmsInfo = new CmsInfo($data); + + $this->assertEquals('WordPress', $cmsInfo->getProperties()['cms_name']); + $this->assertEquals('5.8', $cmsInfo->getProperties()['cms_version']); + $this->assertEquals(['plugin1', 'plugin2'], $cmsInfo->getProperties()['third_parties_plugins']); + $this->assertEquals('theme1', $cmsInfo->getProperties()['theme_name']); + $this->assertEquals('1.1.0', $cmsInfo->getProperties()['theme_version']); + $this->assertEquals('PHP', $cmsInfo->getProperties()['language_name']); + $this->assertEquals('7.4', $cmsInfo->getProperties()['language_version']); + $this->assertEquals('1.0.0', $cmsInfo->getProperties()['alma_plugin_version']); + $this->assertEquals(self::SDK_VERSION, $cmsInfo->getProperties()['alma_sdk_version']); + $this->assertEquals(self::SDK_NAME, $cmsInfo->getProperties()['alma_sdk_name']); + } + + public function testConstructorHandlesNullValuesCorrectly() + { + $data = [ + 'cms_name' => null, + 'cms_version' => null, + 'third_parties_plugins' => null, + 'theme_name' => null, + 'theme_version' => null, + 'language_name' => null, + 'language_version' => null, + 'alma_plugin_version' => null, + 'alma_sdk_version' => null, + 'alma_sdk_name' => null + ]; + + $cmsInfo = new CmsInfo($data); + + $this->assertArrayNotHasKey('cms_name', $cmsInfo->getProperties()); + $this->assertArrayNotHasKey('cms_version', $cmsInfo->getProperties()); + $this->assertArrayNotHasKey('language_name', $cmsInfo->getProperties()); + $this->assertArrayNotHasKey('language_version', $cmsInfo->getProperties()); + $this->assertArrayNotHasKey('alma_plugin_version', $cmsInfo->getProperties()); + $this->assertArrayNotHasKey('alma_sdk_version', $cmsInfo->getProperties()); + $this->assertArrayNotHasKey('alma_sdk_name', $cmsInfo->getProperties()); + $this->assertArrayNotHasKey('third_parties_plugins', $cmsInfo->getProperties()); + $this->assertArrayNotHasKey('theme_name', $cmsInfo->getProperties()); + $this->assertArrayNotHasKey('theme_version', $cmsInfo->getProperties()); + } + + public function testGetPropertiesFiltersOutNullAndEmptyValues() + { + $data = [ + 'cms_name' => 'WordPress', + 'cms_version' => '', + 'third_parties_plugins' => ['plugin1'], + 'theme_name' => '', + 'theme_version' => '', + 'language_name' => null, + 'language_version' => '', + 'alma_plugin_version' => null, + 'alma_sdk_version' => self::SDK_VERSION, + 'alma_sdk_name' => self::SDK_NAME + ]; + + $cmsInfo = new CmsInfo($data); + $properties = $cmsInfo->getProperties(); + + $this->assertArrayHasKey('cms_name', $properties); + $this->assertArrayNotHasKey('cms_version', $properties); // Should be filtered out (empty string) + $this->assertArrayHasKey('third_parties_plugins', $properties); + $this->assertArrayNotHasKey('language_name', $properties); // Should be filtered out (null) + $this->assertArrayNotHasKey('language_version', $properties); // Should be filtered out (empty string) + $this->assertArrayHasKey('alma_sdk_version', $properties); + $this->assertArrayHasKey('alma_sdk_name', $properties); + $this->assertArrayNotHasKey('theme_name', $properties); // Should be filtered out (empty string) + $this->assertArrayNotHasKey('theme_version', $properties); // Should be filtered out (empty string) + } + + public function testGetPropertiesFiltersOutWithEmptyData() + { + $data = []; + + $cmsInfo = new CmsInfo($data); + $properties = $cmsInfo->getProperties(); + + $this->assertArrayNotHasKey('cms_name', $properties); + $this->assertArrayNotHasKey('cms_version', $properties); // Should be filtered out (empty string) + $this->assertArrayNotHasKey('third_parties_plugins', $properties); + $this->assertArrayNotHasKey('theme_name', $properties); + $this->assertArrayNotHasKey('theme_version', $properties); + $this->assertArrayNotHasKey('language_name', $properties); // Should be filtered out (null) + $this->assertArrayNotHasKey('language_version', $properties); // Should be filtered out (empty string) + $this->assertArrayNotHasKey('alma_sdk_version', $properties); + $this->assertArrayNotHasKey('alma_sdk_name', $properties); + } +} \ No newline at end of file diff --git a/tests/Unit/Lib/IntegrationsConfigurationsUtilsTest.php b/tests/Unit/Lib/IntegrationsConfigurationsUtilsTest.php new file mode 100644 index 0000000..dcf5dae --- /dev/null +++ b/tests/Unit/Lib/IntegrationsConfigurationsUtilsTest.php @@ -0,0 +1,27 @@ +assertFalse(IntegrationsConfigurationsUtils::isUrlRefreshRequired($timestamp)); + } + public function testNewSendIsNecessary() + { + $oneMonthInSecondsMoreTen = 30 * 24 * 60 * 60 + 10; // 30 jours en secondes +10 sec + $timestamp = time() - $oneMonthInSecondsMoreTen; + $this->assertTrue(IntegrationsConfigurationsUtils::isUrlRefreshRequired($timestamp)); + } + + public function testNewSendIsNecessaryWithValueNull() + { + $this->assertTrue(IntegrationsConfigurationsUtils::isUrlRefreshRequired(null)); + } +} \ No newline at end of file diff --git a/tests/Unit/Lib/PayloadFormatterTest.php b/tests/Unit/Lib/PayloadFormatterTest.php new file mode 100644 index 0000000..955d8a7 --- /dev/null +++ b/tests/Unit/Lib/PayloadFormatterTest.php @@ -0,0 +1,112 @@ +payloadFormatter = new PayloadFormatter(); + } + + public function testFormatIntegrationConfigurationPayload() + { + // Simulated input data for CmsInfo + $cmsInfoData = [ + 'cms_name' => 'WordPress', + 'cms_version' => '5.8', + 'third_parties_plugins' => [['name' => 'plugin1', 'version' => '1.0']], + 'themes' => [['name' => 'theme1', 'version' => '2.0']], + 'language_name' => 'PHP', + 'language_version' => '7.4', + 'alma_plugin_version' => '1.0.0', + 'alma_sdk_version' => '2.0.0', + 'alma_sdk_name' => 'Alma SDK' + ]; + $cmsInfo = new CmsInfo($cmsInfoData); + + // Simulated input data for CmsFeatures + $cmsFeaturesData = [ + 'alma_enabled' => true, + 'widget_cart_activated' => true, + 'widget_product_activated' => false, + 'used_fee_plans' => '{"plan":"A"}', + 'payment_method_position' => 1, + 'in_page_activated' => true, + 'log_activated' => false, + 'excluded_categories' => ['category1'], + 'excluded_categories_activated' => true, + 'specific_features' => [['name' => 'feature1']], + 'country_restriction' => ['FR', 'US'], + 'is_multisite' => false, + 'custom_widget_css' => true, + ]; + $cmsFeatures = new CmsFeatures($cmsFeaturesData); + + // Call the method to be tested + $result = $this->payloadFormatter->formatConfigurationPayload($cmsInfo, $cmsFeatures); + + // Expected result in JSON format + $expectedPayload = [ + 'cms_info' => $cmsInfo->getProperties(), + 'cms_features' => $cmsFeatures->getProperties(), + ]; + + // Assertion: Check if the output matches the expected JSON payload + $this->assertEquals($expectedPayload, $result); + } + + public function testFormatIntegrationConfigurationPayloadWithEmptyValues() + { + // CmsInfo with null or empty values + $cmsInfoData = [ + 'cms_name' => null, + 'cms_version' => '', + 'third_parties_plugins' => [], + 'themes' => [], + 'language_name' => null, + 'language_version' => '', + 'alma_plugin_version' => null, + 'alma_sdk_version' => null, + 'alma_sdk_name' => null + ]; + $cmsInfo = new CmsInfo($cmsInfoData); + + // CmsFeatures with null or empty values + $cmsFeaturesData = [ + 'alma_enabled' => null, + 'widget_cart_activated' => null, + 'widget_product_activated' => null, + 'used_fee_plans' => '', + 'payment_method_position' => null, + 'in_page_activated' => null, + 'log_activated' => null, + 'excluded_categories' => [], + 'excluded_categories_activated' => null, + 'specific_features' => [], + 'country_restriction' => [], + 'is_multisite' => null, + 'custom_widget_css' => null, + ]; + $cmsFeatures = new CmsFeatures($cmsFeaturesData); + + // Call the method to be tested + $result = $this->payloadFormatter->formatConfigurationPayload($cmsInfo, $cmsFeatures); + + // Expected result in JSON format (should not include keys with null or empty values) + $expectedPayload = [ + 'cms_info' => $cmsInfo->getProperties(), + 'cms_features' => $cmsFeatures->getProperties(), + ]; + + // Assertion: Check if the output matches the expected JSON payload + $this->assertEquals($expectedPayload, $result); + } +} diff --git a/tests/Unit/Lib/RequestUtilsTest.php b/tests/Unit/Lib/RequestUtilsTest.php new file mode 100644 index 0000000..e5c0ddc --- /dev/null +++ b/tests/Unit/Lib/RequestUtilsTest.php @@ -0,0 +1,118 @@ +assertTrue(RequestUtils::isHmacValidated($data, $apiKey, $signature)); + } + /** + * @dataProvider checkHmacInvalidDataProvider + * @param $data + * @param $apiKey + * @param $signature + * @return void + */ + public function testHmacDataDifferentFromSignature($data, $apiKey, $signature) + { + $this->assertFalse(RequestUtils::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 + ] + + ]; + } + +}