diff --git a/.github/workflows/backport-pull-request.yml b/.github/workflows/backport-pull-request.yml index 61b98b74..c79d9741 100644 --- a/.github/workflows/backport-pull-request.yml +++ b/.github/workflows/backport-pull-request.yml @@ -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/hotfix-pull-request.yml b/.github/workflows/hotfix-pull-request.yml index 54929131..227f2fde 100644 --- a/.github/workflows/hotfix-pull-request.yml +++ b/.github/workflows/hotfix-pull-request.yml @@ -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/release-publish.yml b/.github/workflows/release-publish.yml index 1b52c16b..a0c5c9f1 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -114,7 +114,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 faf5a5c2..7b77d76d 100644 --- a/.github/workflows/release-pull-request.yml +++ b/.github/workflows/release-pull-request.yml @@ -55,7 +55,7 @@ jobs: repositories: alma-installments-prestashop - 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 8fd0c21d..ad09459f 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/commitizen-tools/commitizen - rev: v3.28.0 + rev: v3.29.0 hooks: - id: commitizen name: Check commit message format @@ -39,7 +39,7 @@ repos: stages: [commit] - repo: https://github.com/returntocorp/semgrep - rev: v1.83.0 + rev: v1.87.0 hooks: - id: semgrep args: diff --git a/CHANGELOG.md b/CHANGELOG.md index c7ed1664..1df04a37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## v4.4.0 - 2024-10-07 + +### Changes + +### 🚀 New Features + +- feat: In page is now available for Credit (#575) +- feat: Remove insurance in cart when deactivated (#569) +- feat: HMAC verification on IPN callback for security (#561) + +### 🐛 Bug Fixes + +- fix: handle the height automatically of the insurance widget (#572) +- fix: install module if container of getService Ps_account is null (#565) +- fix: Inpage on prestashop 1.6 with third party module of checkout (#566) + +#### Contributors + +@Benjamin-Freoua-Alma, @FranceBe, @alma-renovate-bot, @alma-renovate-bot[bot], @github-actions and @joyet-simon + ## v4.3.0 - 2024-08-12 ### Changes diff --git a/Taskfile.yml b/Taskfile.yml index de045b81..c161f721 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -100,7 +100,7 @@ tasks: deps: - tools:install cmds: - - php ./alma/vendor/bin/phpcs -p alma --standard=PHPCompatibility -s --runtime-set testVersion 5.6-8.1 --ignore=\*/vendor/\* + - php ./alma/vendor/bin/phpcs -p alma --standard=PHPCompatibility -s --runtime-set testVersion 5.6-8.1 --ignore=\*/vendor/\*,\*/.coverage/\* crowdin: internal: true diff --git a/alma/alma.php b/alma/alma.php index 34ccaa4b..e6f7829d 100644 --- a/alma/alma.php +++ b/alma/alma.php @@ -30,7 +30,7 @@ class Alma extends PaymentModule { - const VERSION = '4.3.0'; + const VERSION = '4.4.0'; public $_path; public $local_path; @@ -80,7 +80,7 @@ public function __construct() { $this->name = 'alma'; $this->tab = 'payments_gateways'; - $this->version = '4.3.0'; + $this->version = '4.4.0'; $this->author = 'Alma'; $this->need_instance = false; $this->bootstrap = true; @@ -123,41 +123,30 @@ public function __construct() $this->tabsHelper = new \Alma\PrestaShop\Helpers\Admin\TabsHelper(); $this->toolsHelper = new \Alma\PrestaShop\Helpers\ToolsHelper(); - - $this->handlePSModule(); } /** * @return void + * + * @throws \Alma\PrestaShop\Exceptions\CompatibilityPsAccountException */ - public function handlePSModule() + public function checkCompatibilityPSModule() { + if (_PS_MODE_DEV_ === true) { + throw new \Alma\PrestaShop\Exceptions\CompatibilityPsAccountException('[Alma] Debug mode is activated'); + } + if ( - $this->checkCompatibilityPSModule() - && $this->container === null + !class_exists(\Symfony\Component\Config\ConfigCache::class) + || !class_exists(\PrestaShop\ModuleLibServiceContainer\DependencyInjection\ServiceContainer::class) ) { - $this->container = new \PrestaShop\ModuleLibServiceContainer\DependencyInjection\ServiceContainer( - $this->name, - $this->getLocalPath() - ); + throw new \Alma\PrestaShop\Exceptions\CompatibilityPsAccountException('[Alma] Classes don\'t exist for PS Account'); } - } - - /** - * @return bool - */ - public function checkCompatibilityPSModule() - { if ( $this->toolsHelper->psVersionCompare('1.6', '<') - || !class_exists(\Symfony\Component\Config\ConfigCache::class) - || !class_exists(\PrestaShop\ModuleLibServiceContainer\DependencyInjection\ServiceContainer::class) - || _PS_MODE_DEV_ === true ) { - return false; + throw new \Alma\PrestaShop\Exceptions\CompatibilityPsAccountException('[Alma] Prestashop version lower than 1.6'); } - - return true; } /** @@ -232,8 +221,12 @@ private function checkCoreInstall($coreInstall) */ public function install() { - if ($this->checkCompatibilityPSModule()) { + try { + $this->checkCompatibilityPSModule(); + $this->setContainer(); $this->getService('alma.ps_accounts_installer')->install(); + } catch (\Alma\PrestaShop\Exceptions\CompatibilityPsAccountException $e) { + \Alma\PrestaShop\Logger::instance()->info($e->getMessage()); } $coreInstall = parent::install(); @@ -266,6 +259,14 @@ public function getService($serviceName) return $this->container->getService($serviceName); } + public function setContainer() + { + $this->container = new \PrestaShop\ModuleLibServiceContainer\DependencyInjection\ServiceContainer( + $this->name, + $this->getLocalPath() + ); + } + /** * @return bool * @@ -589,6 +590,8 @@ public function getContent() try { $hasPSAccount = $this->renderPSAccount(); + } catch (\Alma\PrestaShop\Exceptions\CompatibilityPsAccountException $e) { + $hasPSAccount = false; } catch (\PrestaShop\PsAccountsInstaller\Installer\Exception\ModuleNotInstalledException $e) { $hasPSAccount = false; $suggestPSAccount = true; @@ -599,12 +602,13 @@ public function getContent() /** * @return bool + * + * @throws \Alma\PrestaShop\Exceptions\CompatibilityPsAccountException */ public function renderPSAccount() { - if (!$this->checkCompatibilityPSModule()) { - return false; - } + $this->checkCompatibilityPSModule(); + $this->setContainer(); try { $accountsFacade = $this->getService('alma.ps_accounts_facade'); diff --git a/alma/composer.json b/alma/composer.json index 94bf843a..72b1b29f 100644 --- a/alma/composer.json +++ b/alma/composer.json @@ -11,7 +11,7 @@ }, "require": { "php": "^5.6 || ~7.0 || ~7.1 || ~7.2 || ~7.3 || ~7.4 || ~8.0 || ~8.1", - "alma/alma-php-client": "^2.1.0", + "alma/alma-php-client": "^2.2.0", "ext-json": "*", "ext-openssl": "*", "prestashop/prestashop-accounts-installer": "^v1.0.4", diff --git a/alma/controllers/admin/AdminAlmaInsuranceConfiguration.php b/alma/controllers/admin/AdminAlmaInsuranceConfiguration.php index 21485aa7..5145dd38 100644 --- a/alma/controllers/admin/AdminAlmaInsuranceConfiguration.php +++ b/alma/controllers/admin/AdminAlmaInsuranceConfiguration.php @@ -26,10 +26,14 @@ } use Alma\PrestaShop\Builders\Admin\InsuranceHelperBuilder; +use Alma\PrestaShop\Builders\Services\InsuranceProductServiceBuilder; +use Alma\PrestaShop\Exceptions\InsuranceProductException; +use Alma\PrestaShop\Exceptions\WrongParamsException; use Alma\PrestaShop\Helpers\Admin\AdminInsuranceHelper; use Alma\PrestaShop\Helpers\ConfigurationHelper; use Alma\PrestaShop\Helpers\ConstantsHelper; use Alma\PrestaShop\Logger; +use Alma\PrestaShop\Services\InsuranceProductService; use Alma\PrestaShop\Traits\AjaxTrait; class AdminAlmaInsuranceConfigurationController extends ModuleAdminController @@ -44,6 +48,10 @@ class AdminAlmaInsuranceConfigurationController extends ModuleAdminController * @var ConfigurationHelper */ private $configurationHelper; + /** + * @var InsuranceProductService + */ + protected $insuranceProductService; public function __construct() { @@ -51,6 +59,8 @@ public function __construct() $insuranceHelperBuilder = new InsuranceHelperBuilder(); $this->insuranceHelper = $insuranceHelperBuilder->getInstance(); $this->configurationHelper = new ConfigurationHelper(); + $insuranceProductServiceBuilder = new InsuranceProductServiceBuilder(); + $this->insuranceProductService = $insuranceProductServiceBuilder->getInstance(); parent::__construct(); } @@ -88,20 +98,38 @@ public function ajaxProcessSaveConfigInsurance() { try { $config = Tools::getValue('config'); + $savedInsuranceStatus = $config[AdminInsuranceHelper::$fieldsDbInsuranceToIframeParamNames[ConstantsHelper::ALMA_ACTIVATE_INSURANCE]]; $this->insuranceHelper->saveConfigInsurance($config); + $this->insuranceProductService->handleInsuranceProductState($savedInsuranceStatus); + + if ($savedInsuranceStatus === 'false') { + $this->insuranceProductService->removeInsuranceProductsNotOrdered(); + } $this->ajaxRenderAndExit(json_encode([ 'success' => true, 'message' => $this->module->l('Your configuration has been saved', 'AdminAlmaInsuranceConfiguration'), ]) ); - } catch (\Exception $e) { - Logger::instance()->error('Error creating Alma configuration insurance: ' . $e->getMessage()); + } catch (InsuranceProductException $e) { + Logger::instance()->error('[Alma] Error insurance product during change configuration: ' . $e->getMessage()); + $this->ajaxRenderAndExit(json_encode([ + 'error' => [ + 'msg' => sprintf( + $this->module->l('[Alma] Error insurance product during change configuration: %1$s', 'AdminAlmaInsuranceConfiguration'), + $e->getMessage() + ), + 'code' => $e->getCode(), + ], + ]) + ); + } catch (WrongParamsException $e) { + Logger::instance()->error('[Alma] Error creating Alma configuration insurance: ' . $e->getMessage()); $this->ajaxRenderAndExit(json_encode([ 'error' => [ 'msg' => sprintf( - $this->module->l('Error creating configuration Alma insurance: %1$s', 'AdminAlmaInsuranceConfiguration'), + $this->module->l('[Alma] Error creating configuration Alma insurance: %1$s', 'AdminAlmaInsuranceConfiguration'), $e->getMessage() ), 'code' => $e->getCode(), diff --git a/alma/controllers/front/ipn.php b/alma/controllers/front/ipn.php index 496c4641..f4fd1561 100644 --- a/alma/controllers/front/ipn.php +++ b/alma/controllers/front/ipn.php @@ -23,7 +23,9 @@ */ use Alma\PrestaShop\API\MismatchException; -use Alma\PrestaShop\Exceptions\RefundException; +use Alma\PrestaShop\Builders\Validators\PaymentValidationBuilder; +use Alma\PrestaShop\Exceptions\PaymentValidationException; +use Alma\PrestaShop\Helpers\SettingsHelper; use Alma\PrestaShop\Logger; use Alma\PrestaShop\Traits\AjaxTrait; use Alma\PrestaShop\Validators\PaymentValidation; @@ -40,8 +42,21 @@ class AlmaIpnModuleFrontController extends ModuleFrontController { use AjaxTrait; + /** + * @var bool + */ public $ssl = true; + /** + * @var Context + */ + public $context; + + /** + * @var PaymentValidation + */ + protected $paymentValidation; + /** * IPN constructor * @@ -51,14 +66,15 @@ public function __construct() { parent::__construct(); $this->context = Context::getContext(); + $paymentValidationBuilder = new PaymentValidationBuilder(); + $this->paymentValidation = $paymentValidationBuilder->getInstance(); } /** * @return void * * @throws PrestaShopException - * @throws RefundException - * @throws MismatchException + * @throws Exception */ public function postProcess() { @@ -67,10 +83,19 @@ public function postProcess() header('Content-Type: application/json'); $paymentId = Tools::getValue('pid'); - $validator = new PaymentValidation($this->context, $this->module); + if (!array_key_exists('HTTP_X_ALMA_SIGNATURE', $_SERVER)) { + $msg = 'Header key X-Alma-Signature doesn\'t exist'; + Logger::instance()->error('[Alma] IPN Payment Validation Error - Message : ' . $msg); + $this->ajaxRenderAndExit(json_encode(['error' => $msg]), 500); + } try { - $validator->validatePayment($paymentId); + $this->paymentValidation->checkSignature($paymentId, SettingsHelper::getActiveAPIKey(), $_SERVER['HTTP_X_ALMA_SIGNATURE']); + $this->paymentValidation->validatePayment($paymentId); + $this->ajaxRenderAndExit(json_encode(['success' => true])); + } catch (PaymentValidationException $e) { + Logger::instance()->error('[Alma] IPN Payment Validation Error - Message : ' . $e->getMessage()); + $this->ajaxRenderAndExit(json_encode(['error' => $e->getMessage()]), 500); } catch (PaymentValidationError $e) { Logger::instance()->error('ipn payment_validation_error - Message : ' . $e->getMessage()); $this->ajaxRenderAndExit(json_encode(['error' => $e->getMessage()]), 500); @@ -78,7 +103,5 @@ public function postProcess() Logger::instance()->error('ipn payment_validation_mismatch_error - Message : ' . $e->getMessage()); $this->ajaxRenderAndExit(json_encode(['error' => $e->getMessage()]), 200); } - - $this->ajaxRenderAndExit(json_encode(['success' => true])); } } diff --git a/alma/controllers/front/payment.php b/alma/controllers/front/payment.php index 7f91dca2..ffdd2f12 100644 --- a/alma/controllers/front/payment.php +++ b/alma/controllers/front/payment.php @@ -167,7 +167,7 @@ public function postProcess() $this->ajaxErrorAndDie(); } - if ($this->paymentData->isInPage($data)) { + if ($this->paymentData->isInPage()) { $this->ajaxRenderAndExit(json_encode($payment)); } diff --git a/alma/controllers/front/validation.php b/alma/controllers/front/validation.php index ff72fa56..d04afac0 100644 --- a/alma/controllers/front/validation.php +++ b/alma/controllers/front/validation.php @@ -22,6 +22,9 @@ * @license https://opensource.org/licenses/MIT The MIT License */ +use Alma\PrestaShop\API\MismatchException; +use Alma\PrestaShop\Builders\Validators\PaymentValidationBuilder; +use Alma\PrestaShop\Exceptions\PaymentValidationException; use Alma\PrestaShop\Logger; use Alma\PrestaShop\Validators\PaymentValidation; use Alma\PrestaShop\Validators\PaymentValidationError; @@ -33,6 +36,10 @@ class AlmaValidationModuleFrontController extends ModuleFrontController { public $ssl = true; + /** + * @var PaymentValidation + */ + protected $paymentValidation; /** * @codeCoverageIgnore @@ -41,6 +48,8 @@ public function __construct() { parent::__construct(); $this->context = Context::getContext(); + $paymentValidationBuilder = new PaymentValidationBuilder(); + $this->paymentValidation = $paymentValidationBuilder->getInstance(); } private function fail($cart, $msg = null) @@ -65,14 +74,16 @@ public function postProcess() parent::postProcess(); $paymentId = Tools::getValue('pid'); - $validator = new PaymentValidation($this->context, $this->module); try { - $redirect_to = $validator->validatePayment($paymentId); + $redirect_to = $this->paymentValidation->validatePayment($paymentId); } catch (PaymentValidationError $e) { Logger::instance()->error('payment_validation_error - Message : ' . $e->getMessage()); $redirect_to = $this->fail($e->cart, $e->getMessage()); - } catch (Exception $e) { + } catch (PaymentValidationException $e) { + Logger::instance()->error('payment_validation_error - Message : ' . $e->getMessage()); + $redirect_to = $this->fail($e->cartId, $e->getMessage()); + } catch (MismatchException $e) { Logger::instance()->error('payment_error - Message : ' . $e->getMessage()); $redirect_to = $this->fail(null, $e->getMessage()); } diff --git a/alma/controllers/hook/ActionFrontControllerSetVariablesHookController.php b/alma/controllers/hook/ActionFrontControllerSetVariablesHookController.php index 61f0dd14..8044b611 100644 --- a/alma/controllers/hook/ActionFrontControllerSetVariablesHookController.php +++ b/alma/controllers/hook/ActionFrontControllerSetVariablesHookController.php @@ -68,6 +68,10 @@ public function run($params) */ private function checkIsInsuranceProduct($params) { + if (!array_key_exists('templateVars', $params)) { + return false; + } + $templateVars = $params['templateVars']; $idInsuranceProduct = $this->productRepository->getProductIdByReference(ConstantsHelper::ALMA_INSURANCE_PRODUCT_REFERENCE); diff --git a/alma/controllers/hook/DisplayPaymentHookController.php b/alma/controllers/hook/DisplayPaymentHookController.php index 3c06f78a..96159ec4 100644 --- a/alma/controllers/hook/DisplayPaymentHookController.php +++ b/alma/controllers/hook/DisplayPaymentHookController.php @@ -234,7 +234,6 @@ public function run($params) ), $installment ); - $paymentOption['isInPageEnabled'] = false; } if ($isDeferred) { diff --git a/alma/controllers/hook/DisplayProductActionsHookController.php b/alma/controllers/hook/DisplayProductActionsHookController.php index 611dc4f2..8959309f 100644 --- a/alma/controllers/hook/DisplayProductActionsHookController.php +++ b/alma/controllers/hook/DisplayProductActionsHookController.php @@ -127,10 +127,10 @@ public function run($params) $productName = isset($productParams['name']) ? $productParams['name'] : ''; $merchantId = SettingsHelper::getMerchantId(); - $settings = $this->handleSettings($merchantId); $this->context->smarty->assign([ - 'settingsInsurance' => $settings, + 'productDetails' => $this->handleProductDetails($params), + 'settingsInsurance' => $this->handleSettings($merchantId), 'iframeUrl' => sprintf( '%s%s?cms_reference=%s&product_price=%s&product_name=%s&merchant_id=%s&customer_session_id=%s&cart_id=%s', $this->adminInsuranceHelper->envUrl(), @@ -161,4 +161,20 @@ protected function handleSettings($merchantId) return json_encode($settings); } + + /** + * @param $params + * + * @return false|string + */ + protected function handleProductDetails($params) + { + $productDetails = []; + + if (isset($params['product'])) { + $productDetails = $params['product']; + } + + return json_encode($productDetails); + } } diff --git a/alma/controllers/hook/FrontHeaderHookController.php b/alma/controllers/hook/FrontHeaderHookController.php index 2caed1a6..13cbed51 100644 --- a/alma/controllers/hook/FrontHeaderHookController.php +++ b/alma/controllers/hook/FrontHeaderHookController.php @@ -31,7 +31,7 @@ use Alma\PrestaShop\Builders\Admin\InsuranceHelperBuilder as AdminInsuranceHelperBuilder; use Alma\PrestaShop\Builders\Helpers\InsuranceHelperBuilder; use Alma\PrestaShop\Builders\Helpers\SettingsHelperBuilder; -use Alma\PrestaShop\Helpers\Admin\InsuranceHelper as AdminInsuranceHelper; +use Alma\PrestaShop\Helpers\Admin\AdminInsuranceHelper; use Alma\PrestaShop\Helpers\ConstantsHelper; use Alma\PrestaShop\Helpers\InsuranceHelper; use Alma\PrestaShop\Helpers\SettingsHelper; diff --git a/alma/exceptions/CartException.php b/alma/exceptions/CartException.php new file mode 100644 index 00000000..e3b012ee --- /dev/null +++ b/alma/exceptions/CartException.php @@ -0,0 +1,33 @@ + + * @copyright 2018-2024 Alma SAS + * @license https://opensource.org/licenses/MIT The MIT License + */ + +namespace Alma\PrestaShop\Exceptions; + +if (!defined('_PS_VERSION_')) { + exit; +} + +class CartException extends AlmaException +{ +} diff --git a/alma/exceptions/CompatibilityPsAccountException.php b/alma/exceptions/CompatibilityPsAccountException.php new file mode 100644 index 00000000..8e459d87 --- /dev/null +++ b/alma/exceptions/CompatibilityPsAccountException.php @@ -0,0 +1,11 @@ + + * @copyright 2018-2024 Alma SAS + * @license https://opensource.org/licenses/MIT The MIT License + */ + +namespace Alma\PrestaShop\Exceptions; + +class InsuranceProductException extends \Exception +{ +} diff --git a/alma/exceptions/PaymentValidationException.php b/alma/exceptions/PaymentValidationException.php new file mode 100644 index 00000000..ac662060 --- /dev/null +++ b/alma/exceptions/PaymentValidationException.php @@ -0,0 +1,43 @@ + + * @copyright 2018-2024 Alma SAS + * @license https://opensource.org/licenses/MIT The MIT License + */ + +namespace Alma\PrestaShop\Exceptions; + +if (!defined('_PS_VERSION_')) { + exit; +} + +class PaymentValidationException extends AlmaException +{ + /** + * @var int + */ + public $cartId; + + public function __construct($message = '', $cartId = -1, $code = 0, $previous = null) + { + parent::__construct($message, $code, $previous); + $this->cartId = $cartId; + } +} diff --git a/alma/exceptions/ProductException.php b/alma/exceptions/ProductException.php new file mode 100644 index 00000000..ef33077a --- /dev/null +++ b/alma/exceptions/ProductException.php @@ -0,0 +1,29 @@ + + * @copyright 2018-2024 Alma SAS + * @license https://opensource.org/licenses/MIT The MIT License + */ + +namespace Alma\PrestaShop\Exceptions; + +class ProductException extends \Exception +{ +} diff --git a/alma/lib/Builders/Services/CartServiceBuilder.php b/alma/lib/Builders/Services/CartServiceBuilder.php index 59fb654a..183fc10f 100644 --- a/alma/lib/Builders/Services/CartServiceBuilder.php +++ b/alma/lib/Builders/Services/CartServiceBuilder.php @@ -49,7 +49,9 @@ public function getInstance() $this->getOpartSaveCartCartService(), $this->getInsuranceHelper(), $this->getInsuranceProductHelper(), - $this->getToolsFactory() + $this->getToolsFactory(), + $this->getCartFactory(), + $this->getProductHelper() ); } } diff --git a/alma/lib/Builders/Services/InsuranceProductServiceBuilder.php b/alma/lib/Builders/Services/InsuranceProductServiceBuilder.php index 5e37592d..1de32177 100644 --- a/alma/lib/Builders/Services/InsuranceProductServiceBuilder.php +++ b/alma/lib/Builders/Services/InsuranceProductServiceBuilder.php @@ -60,7 +60,8 @@ public function getInstance() $this->getInsuranceHelper(), $this->getToolsFactory(), $this->getImageHelper(), - $this->getToolsHelper() + $this->getToolsHelper(), + $this->getCartFactory() ); } } diff --git a/alma/lib/Builders/Validators/PaymentValidationBuilder.php b/alma/lib/Builders/Validators/PaymentValidationBuilder.php new file mode 100644 index 00000000..cab41b4a --- /dev/null +++ b/alma/lib/Builders/Validators/PaymentValidationBuilder.php @@ -0,0 +1,52 @@ + + * @copyright 2018-2024 Alma SAS + * @license https://opensource.org/licenses/MIT The MIT License + */ + +namespace Alma\PrestaShop\Builders\Validators; + +use Alma\PrestaShop\Traits\BuilderTrait; +use Alma\PrestaShop\Validators\PaymentValidation; + +if (!defined('_PS_VERSION_')) { + exit; +} + +/** + * PaymentValidationBuilder. + */ +class PaymentValidationBuilder +{ + use BuilderTrait; + + /** + * @return PaymentValidation + */ + public function getInstance() + { + return new PaymentValidation( + $this->getContextFactory(), + $this->getModuleFactory(), + $this->getClientPaymentValidator() + ); + } +} diff --git a/alma/lib/Factories/CartFactory.php b/alma/lib/Factories/CartFactory.php index 41a8b391..e8c44a57 100644 --- a/alma/lib/Factories/CartFactory.php +++ b/alma/lib/Factories/CartFactory.php @@ -33,6 +33,21 @@ */ class CartFactory { + /** + * @var \Cart + */ + protected $cart; + + /** + * @param \Cart|null $cart + */ + public function __construct($cart = null) + { + if ($cart !== null) { + $this->cart = $cart; + } + } + /** * @param int $id * diff --git a/alma/lib/Helpers/ConfigurationHelper.php b/alma/lib/Helpers/ConfigurationHelper.php index 689863f6..e765511d 100644 --- a/alma/lib/Helpers/ConfigurationHelper.php +++ b/alma/lib/Helpers/ConfigurationHelper.php @@ -124,20 +124,13 @@ public function updateValue($key, $values, $html = false, $idShopGroup = null, $ } /** - * @param int $installments * @param SettingsHelper $settingsHelper * * @return bool */ - public function isInPageEnabled($installments, $settingsHelper) + public function isInPageEnabled($settingsHelper) { - $isInPageEnabled = $settingsHelper->isInPageEnabled(); - - if ($installments > 4) { - $isInPageEnabled = false; - } - - return $isInPageEnabled; + return $settingsHelper->isInPageEnabled(); } /** diff --git a/alma/lib/Helpers/HookHelper.php b/alma/lib/Helpers/HookHelper.php index 56ea4a34..25b4f260 100644 --- a/alma/lib/Helpers/HookHelper.php +++ b/alma/lib/Helpers/HookHelper.php @@ -101,7 +101,7 @@ public function __construct() 'operand' => '>=', ], 'actionObjectProductInCartDeleteAfter' => [ - 'version' => '8.1.4', + 'version' => '1.7.1', 'operand' => '>', ], 'actionCartSave' => 'all', diff --git a/alma/lib/Helpers/PaymentOptionHelper.php b/alma/lib/Helpers/PaymentOptionHelper.php index 6ca1d51b..f580cec4 100644 --- a/alma/lib/Helpers/PaymentOptionHelper.php +++ b/alma/lib/Helpers/PaymentOptionHelper.php @@ -189,15 +189,15 @@ public function getEuCompliance($params) /** * @param int $installementCount * @param int $duration - * @param bool $isPnxPlus4 + * @param bool $isCredit * @param bool $isDeferred * @param bool $isPayNow * * @return array */ - public function getTextsByTypes($installementCount, $duration, $isPnxPlus4, $isDeferred, $isPayNow) + public function getTextsByTypes($installementCount, $duration, $isCredit, $isDeferred, $isPayNow) { - if ($isPnxPlus4) { + if ($isCredit) { return $this->getTexts( $installementCount, PaymentButtonAdminFormBuilder::ALMA_PNX_AIR_BUTTON_TITLE, @@ -247,15 +247,14 @@ public function sortPaymentsOptions($sortOptions, $paymentOptions) /** * @param PaymentOption $paymentOption * @param $template - * @param int $installments * * @return mixed */ - public function setAdditionalInformationForEuCompliance($paymentOption, $template, $installments) + public function setAdditionalInformationForEuCompliance($paymentOption, $template) { $paymentOption->setAdditionalInformation($template); - if ($this->configurationHelper->isInPageEnabled($installments, $this->settingsHelper)) { + if ($this->configurationHelper->isInPageEnabled($this->settingsHelper)) { $paymentOption->setForm($this->paymentOptionTemplateHelper->getTemplateInPage()); } diff --git a/alma/lib/Helpers/PaymentOptionTemplateHelper.php b/alma/lib/Helpers/PaymentOptionTemplateHelper.php index 77f340dd..e964a355 100644 --- a/alma/lib/Helpers/PaymentOptionTemplateHelper.php +++ b/alma/lib/Helpers/PaymentOptionTemplateHelper.php @@ -155,7 +155,6 @@ public function buildTemplateVar( 'apiMode' => strtoupper($this->settingsHelper->getModeActive()), 'merchantId' => $this->settingsHelper->getIdMerchant(), 'isInPageEnabled' => $this->configurationHelper->isInPageEnabled( - $plan->installmentsCount, $this->settingsHelper ), 'first' => $first, diff --git a/alma/lib/Helpers/PlanHelper.php b/alma/lib/Helpers/PlanHelper.php index 72172641..a09fdd43 100644 --- a/alma/lib/Helpers/PlanHelper.php +++ b/alma/lib/Helpers/PlanHelper.php @@ -94,7 +94,7 @@ public function __construct( * * @return bool */ - public function isPnxPlus4($plan) + public function isCredit($plan) { return $plan->installmentsCount > 4; } diff --git a/alma/lib/Helpers/ProductHelper.php b/alma/lib/Helpers/ProductHelper.php index 8a7d876a..06b61de1 100644 --- a/alma/lib/Helpers/ProductHelper.php +++ b/alma/lib/Helpers/ProductHelper.php @@ -25,6 +25,8 @@ namespace Alma\PrestaShop\Helpers; use Alma\PrestaShop\Builders\Helpers\InsuranceHelperBuilder; +use Alma\PrestaShop\Exceptions\ProductException; +use Alma\PrestaShop\Factories\ProductFactory; if (!defined('_PS_VERSION_')) { exit; @@ -41,11 +43,15 @@ class ProductHelper * @var InsuranceHelper */ protected $insuranceHelper; + /** + * @var ProductHelper + */ + protected $productFactory; - public function __construct() + public function __construct($insuranceHelper = null, $productFactory = null) { - $insuranceHelperBuilder = new InsuranceHelperBuilder(); - $this->insuranceHelper = $insuranceHelperBuilder->getInstance(); + $this->insuranceHelper = $insuranceHelper ?: (new InsuranceHelperBuilder())->getInstance(); + $this->productFactory = $productFactory ?: new ProductFactory(); } /** @@ -276,4 +282,23 @@ public function getCategoriesName($product) return $category; } + + /** + * @param int $productId + * @param int $languageId + * + * @return array + * + * @throws ProductException + */ + public function getAttributeCombinationsByProductId($productId, $languageId) + { + if (!$productId || !$languageId || !is_int($productId) || !is_int($languageId)) { + throw new ProductException("[Alma] Error to get attribute combination with productId {$productId} and languageId {$languageId}"); + } + + $product = $this->productFactory->create($productId); + + return $product->getAttributeCombinations($languageId); + } } diff --git a/alma/lib/Helpers/ShareOfCheckoutHelper.php b/alma/lib/Helpers/ShareOfCheckoutHelper.php index c4652c8d..d87b95d5 100644 --- a/alma/lib/Helpers/ShareOfCheckoutHelper.php +++ b/alma/lib/Helpers/ShareOfCheckoutHelper.php @@ -185,8 +185,6 @@ public function isSocActivated() || empty($shareOfCheckoutEnabledDate) || !$this->dateHelper->isValidTimeStamp($shareOfCheckoutEnabledDate) ) { - Logger::instance()->info('Share Of Checkout is disabled or invalide date'); - return false; } diff --git a/alma/lib/Model/PaymentData.php b/alma/lib/Model/PaymentData.php index 9fd45494..2568ddce 100644 --- a/alma/lib/Model/PaymentData.php +++ b/alma/lib/Model/PaymentData.php @@ -329,7 +329,7 @@ public function buildDataPayment( $dataPayment['payment']['deferred_description'] = $this->customFieldsHelper->getDescriptionPaymentTriggerByLang($this->context->language->id); } - if ($this->isInPage($dataPayment)) { + if ($this->isInPage()) { $dataPayment['payment']['origin'] = 'online_in_page'; } @@ -490,13 +490,9 @@ public function isPayNow($paymentData) * * @return bool */ - public function isInPage($dataPayment) + public function isInPage() { - return ( - $this->isPnXOnly($dataPayment) - || $this->isPayNow($dataPayment) - || $this->isPayLater($dataPayment)) - && $this->settingsHelper->isInPageEnabled(); + return $this->settingsHelper->isInPageEnabled(); } /** diff --git a/alma/lib/Repositories/AlmaInsuranceProductRepository.php b/alma/lib/Repositories/AlmaInsuranceProductRepository.php index e9b019df..4818f392 100644 --- a/alma/lib/Repositories/AlmaInsuranceProductRepository.php +++ b/alma/lib/Repositories/AlmaInsuranceProductRepository.php @@ -540,4 +540,32 @@ public function getContractByProductAndCartIdAndShopAndInsuranceProductAttribute return \Db::getInstance()->executeS($sql); } + + /** + * @throws \PrestaShopDatabaseException + */ + public function getCartsNotOrdered() + { + $sql = ' + SELECT aip.`id_cart` + FROM `' . _DB_PREFIX_ . 'alma_insurance_product` aip + WHERE aip.`id_order` IS NULL + GROUP BY aip.`id_cart`'; + + return \Db::getInstance()->executeS($sql); + } + + /** + * @param string $cartIds + * + * @return bool + */ + public function deleteAssociationsByCartIds($cartIds) + { + $sql = ' + DELETE FROM `' . _DB_PREFIX_ . 'alma_insurance_product` + WHERE `id_cart` IN (' . $cartIds . ')'; + + return \Db::getInstance()->execute($sql); + } } diff --git a/alma/lib/Services/CartService.php b/alma/lib/Services/CartService.php index 204879da..83e27aab 100644 --- a/alma/lib/Services/CartService.php +++ b/alma/lib/Services/CartService.php @@ -25,10 +25,14 @@ namespace Alma\PrestaShop\Services; use Alma\PrestaShop\Exceptions\AlmaException; +use Alma\PrestaShop\Exceptions\CartException; +use Alma\PrestaShop\Exceptions\ProductException; +use Alma\PrestaShop\Factories\CartFactory; use Alma\PrestaShop\Factories\ContextFactory; use Alma\PrestaShop\Factories\ToolsFactory; use Alma\PrestaShop\Helpers\InsuranceHelper; use Alma\PrestaShop\Helpers\InsuranceProductHelper; +use Alma\PrestaShop\Helpers\ProductHelper; use Alma\PrestaShop\Modules\OpartSaveCart\OpartSaveCartCartService; use Alma\PrestaShop\Repositories\CartProductRepository; @@ -64,6 +68,14 @@ class CartService * @var ToolsFactory */ protected $toolsFactory; + /** + * @var CartFactory + */ + protected $cartFactory; + /** + * @var ProductHelper + */ + protected $productHelper; /** * @param CartProductRepository $cartProductRepository @@ -72,15 +84,27 @@ class CartService * @param InsuranceHelper $insuranceHelper * @param InsuranceProductHelper $insuranceProductHelper * @param ToolsFactory $toolsFactory + * @param CartFactory $cartFactory + * @param ProductHelper $productHelper */ - public function __construct($cartProductRepository, $contextFactory, $opartCartSaveService, $insuranceHelper, $insuranceProductHelper, $toolsFactory) - { + public function __construct( + $cartProductRepository, + $contextFactory, + $opartCartSaveService, + $insuranceHelper, + $insuranceProductHelper, + $toolsFactory, + $cartFactory, + $productHelper + ) { $this->cartProductRepository = $cartProductRepository; $this->contextFactory = $contextFactory; $this->opartCartSaveService = $opartCartSaveService; $this->insuranceHelper = $insuranceHelper; $this->insuranceProductHelper = $insuranceProductHelper; $this->toolsFactory = $toolsFactory; + $this->cartFactory = $cartFactory; + $this->productHelper = $productHelper; } /** @@ -228,4 +252,41 @@ public function updateQty( ); } } + + /** + * @param int|false $productId + * @param int $cartId + * + * @return bool|null + * + * @throws AlmaException + * @throws CartException + */ + public function deleteProductByCartId($productId, $cartId) + { + if (!$productId || !$cartId) { + throw new CartException("[Alma] Product id and cart id are required. ProductId: {$productId}, cartId: {$cartId}"); + } + + $cart = $this->cartFactory->create($cartId); + $languageId = $this->contextFactory->getContextLanguageId(); + + try { + $insuranceProductAttributes = $this->productHelper->getAttributeCombinationsByProductId($productId, $languageId); + } catch (ProductException $e) { + throw new CartException("[Alma] Cannot get Attribute combination of productId {$productId}, with languageId {$languageId} "); + } + + try { + foreach ($insuranceProductAttributes as $insuranceProductAttribute) { + $cart->deleteProduct($productId, $insuranceProductAttribute['id_product_attribute']); + } + + return true; + } catch (\PrestaShopDatabaseException $e) { + throw new CartException("[Alma] Error Database while deleting product from cart. ProductId: {$productId}, cartId: {$cartId}"); + } catch (\PrestaShopException $e) { + throw new CartException("[Alma] Error while deleting product from cart. ProductId: {$productId}, cartId: {$cartId}"); + } + } } diff --git a/alma/lib/Services/InsuranceProductService.php b/alma/lib/Services/InsuranceProductService.php index 9c3268ae..7433f9a1 100644 --- a/alma/lib/Services/InsuranceProductService.php +++ b/alma/lib/Services/InsuranceProductService.php @@ -26,6 +26,8 @@ use Alma\PrestaShop\Exceptions\AlmaException; use Alma\PrestaShop\Exceptions\InsuranceContractException; +use Alma\PrestaShop\Exceptions\InsuranceProductException; +use Alma\PrestaShop\Factories\CartFactory; use Alma\PrestaShop\Factories\ContextFactory; use Alma\PrestaShop\Factories\LinkFactory; use Alma\PrestaShop\Factories\ProductFactory; @@ -126,9 +128,13 @@ class InsuranceProductService */ protected $toolsFactory; /** - * @var mixed + * @var Logger */ protected $logger; + /** + * @var CartFactory + */ + protected $cartFactory; /** * @param AlmaInsuranceProductRepository $almaInsuranceProductRepository @@ -146,6 +152,7 @@ class InsuranceProductService * @param InsuranceHelper $insuranceHelper * @param ImageHelper $imageHelper * @param ToolsHelper $toolsHelper + * @param CartFactory $cartFactory */ public function __construct( $productFactory, @@ -164,7 +171,8 @@ public function __construct( $insuranceHelper, $toolsFactory, $imageHelper, - $toolsHelper + $toolsHelper, + $cartFactory ) { $this->productFactory = $productFactory; $this->link = $linkFactory->create(); @@ -183,6 +191,7 @@ public function __construct( $this->toolsFactory = $toolsFactory; $this->imageHelper = $imageHelper; $this->toolsHelper = $toolsHelper; + $this->cartFactory = $cartFactory; } /** @@ -422,4 +431,52 @@ public function getItemsCartInsuranceProductAttributes($product, $cartId, $insur return $resultInsurance; } + + /** + * @throws InsuranceProductException + * @throws \PrestaShopException + */ + public function handleInsuranceProductState($state) + { + $filterState = filter_var($state, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + + if (is_null($state) || is_null($filterState)) { + throw new InsuranceProductException('[Alma] Wrong param to save insurance : ' . var_export($state, true)); + } + + $insuranceProductId = $this->productRepository->getProductIdByReference(ConstantsHelper::ALMA_INSURANCE_PRODUCT_REFERENCE, $this->context->language->id); + $insuranceProduct = $this->productFactory->create($insuranceProductId); + $insuranceProduct->active = $filterState; + $insuranceProduct->save(); + } + + /** + * @return true|void + * + * @throws InsuranceProductException + * @throws \PrestaShopDatabaseException + */ + public function removeInsuranceProductsNotOrdered() + { + $cartIds = $this->almaInsuranceProductRepository->getCartsNotOrdered(); + $insuranceProductId = $this->productRepository->getProductIdByReference(ConstantsHelper::ALMA_INSURANCE_PRODUCT_REFERENCE); + + if (empty($cartIds)) { + return true; + } + if (!$insuranceProductId) { + throw new InsuranceProductException("[Alma] Error while removing insurance product id. InsuranceProductId: {$insuranceProductId} doesn't exist"); + } + + foreach ($cartIds as $cartId) { + try { + $this->cartService->deleteProductByCartId((int) $insuranceProductId, (int) $cartId['id_cart']); + } catch (AlmaException $e) { + throw new InsuranceProductException("[Alma] Error while removing product id : {$insuranceProductId} in cart id : {$cartId['id_cart']}"); + } + } + + $arrayCartIds = array_column($cartIds, 'id_cart'); + $this->almaInsuranceProductRepository->deleteAssociationsByCartIds(implode(', ', $arrayCartIds)); + } } diff --git a/alma/lib/Services/PaymentService.php b/alma/lib/Services/PaymentService.php index a9d7fb1a..47625730 100644 --- a/alma/lib/Services/PaymentService.php +++ b/alma/lib/Services/PaymentService.php @@ -128,7 +128,7 @@ class PaymentService /** * @var bool */ - protected $isPnxPlus4; + protected $isCredit; /** * @var MediaHelper @@ -264,7 +264,7 @@ public function createPaymentOptions($params) $this->isPayNow = $this->configurationHelper->isPayNow($key); $this->isDeferred = $this->planHelper->isDeferred($plan); - $this->isPnxPlus4 = $this->planHelper->isPnxPlus4($plan); + $this->isCredit = $this->planHelper->isCredit($plan); $plans = $this->planHelper->buildDates($plans, $locale, $feePlans, $key, $this->isPayNow); $duration = $this->settingsHelper->getDuration($plan); @@ -272,7 +272,7 @@ public function createPaymentOptions($params) list($textPaymentButton, $descPaymentButton) = $this->paymentOptionHelper->getTextsByTypes( $plan->installmentsCount, $duration, - $this->isPnxPlus4, + $this->isCredit, $this->isDeferred, $this->isPayNow ); @@ -315,8 +315,7 @@ public function createPaymentOptions($params) $paymentOption = $this->paymentOptionHelper->setAdditionalInformationForEuCompliance( $paymentOption, - $template, - $plan->installmentsCount + $template ); } diff --git a/alma/lib/Traits/BuilderTrait.php b/alma/lib/Traits/BuilderTrait.php index e5b76462..96c1df6c 100644 --- a/alma/lib/Traits/BuilderTrait.php +++ b/alma/lib/Traits/BuilderTrait.php @@ -24,6 +24,7 @@ namespace Alma\PrestaShop\Traits; +use Alma\API\Lib\PaymentValidator; use Alma\PrestaShop\Factories\AddressFactory; use Alma\PrestaShop\Factories\CarrierFactory; use Alma\PrestaShop\Factories\CartFactory; @@ -1185,7 +1186,9 @@ public function getCartService($cartService = null) $this->getOpartSaveCartCartService(), $this->getInsuranceHelper(), $this->getInsuranceProductHelper(), - $this->getToolsFactory() + $this->getToolsFactory(), + $this->getCartFactory(), + $this->getProductHelper() ); } @@ -1202,4 +1205,12 @@ public function getInsuranceApiService($insuranceApiService = null) return new InsuranceApiService(); } + + /** + * @return PaymentValidator + */ + public function getClientPaymentValidator() + { + return new PaymentValidator(); + } } diff --git a/alma/lib/Validators/PaymentValidation.php b/alma/lib/Validators/PaymentValidation.php index 0ef180bb..e1139e99 100644 --- a/alma/lib/Validators/PaymentValidation.php +++ b/alma/lib/Validators/PaymentValidation.php @@ -25,11 +25,16 @@ namespace Alma\PrestaShop\Validators; use Alma\API\Entities\Payment; +use Alma\API\Lib\PaymentValidator; use Alma\API\RequestError; use Alma\PrestaShop\API\MismatchException; use Alma\PrestaShop\Builders\Helpers\PriceHelperBuilder; use Alma\PrestaShop\Builders\Helpers\SettingsHelperBuilder; use Alma\PrestaShop\Builders\Services\OrderServiceBuilder; +use Alma\PrestaShop\Exceptions\PaymentValidationException; +use Alma\PrestaShop\Exceptions\RefundException; +use Alma\PrestaShop\Factories\ContextFactory; +use Alma\PrestaShop\Factories\ModuleFactory; use Alma\PrestaShop\Helpers\ClientHelper; use Alma\PrestaShop\Helpers\PriceHelper; use Alma\PrestaShop\Helpers\RefundHelper; @@ -44,10 +49,10 @@ class PaymentValidation { - /** @var \Context */ - private $context; - /** @var \PaymentModule */ - private $module; + /** @var ContextFactory */ + protected $context; + /** @var ModuleFactory */ + protected $module; /** * @var SettingsHelper @@ -68,15 +73,24 @@ class PaymentValidation * @var OrderService */ protected $orderService; + /** + * @var PaymentValidator + */ + protected $paymentValidator; /** - * @param $context - * @param $module + * @param ContextFactory $contextFactory + * @param ModuleFactory $moduleFactory + * @param PaymentValidator $clientPaymentValidator */ - public function __construct($context, $module) - { - $this->context = $context; - $this->module = $module; + public function __construct( + $contextFactory, + $moduleFactory, + $clientPaymentValidator + ) { + $this->context = $contextFactory->getContext(); + $this->module = $moduleFactory->getModule(); + $this->paymentValidator = $clientPaymentValidator; $settingsHelperBuilder = new SettingsHelperBuilder(); $this->settingsHelper = $settingsHelperBuilder->getInstance(); @@ -120,6 +134,7 @@ private function isValidCurrency() * * @throws MismatchException * @throws PaymentValidationError + * @throws PaymentValidationException */ public function validatePayment($almaPaymentId) { @@ -210,7 +225,11 @@ public function validatePayment($almaPaymentId) $clientHelper = new ClientHelper(); $refundHelper = new RefundHelper($this->module, $cart, $payment->id, $clientHelper); - $refundHelper->mismatchFullRefund(); + try { + $refundHelper->mismatchFullRefund(); + } catch (RefundException $e) { + throw new PaymentValidationException('[Alma] Error refund from mismatch', $cart->id, 0, $e); + } } $firstInstalment = $payment->payment_plan[0]; @@ -312,6 +331,8 @@ public function validatePayment($almaPaymentId) * @param $cartId * * @return \OrderCore|null + * + * @throws PaymentValidationException */ private function getOrderByCartId($cartId) { @@ -320,7 +341,13 @@ private function getOrderByCartId($cartId) } else { $orderId = (int) \Order::getOrderByCartId((int) $cartId); - return new \Order($orderId); + try { + return new \Order($orderId); + } catch (\PrestaShopDatabaseException $e) { + throw new PaymentValidationException('[Alma] Error Prestashop database', $cartId, 0, $e); + } catch (\PrestaShopException $e) { + throw new PaymentValidationException('[Alma] Error Prestashop', $cartId, 0, $e); + } } } @@ -330,7 +357,7 @@ private function getOrderByCartId($cartId) * When calculating cart amount from an IPN call. * * @param \Cart $cart - * @param \Customer $cart + * @param \Customer $customer * * @return float */ @@ -347,4 +374,27 @@ private function getCartTotals($cart, $customer) return $cartTotals; } + + /** + * @param string $paymentId + * @param string $apiKey + * @param string $signature + * + * @throws PaymentValidationException + */ + public function checkSignature($paymentId, $apiKey, $signature) + { + if (!$paymentId) { + throw new PaymentValidationException('[Alma] Payment ID is missing'); + } + if (!$apiKey) { + throw new PaymentValidationException('[Alma] Api key is missing'); + } + if (!$signature) { + throw new PaymentValidationException('[Alma] Signature is missing'); + } + if (!$this->paymentValidator->isHmacValidated($paymentId, $apiKey, $signature)) { + throw new PaymentValidationException('[Alma] Signature is invalid'); + } + } } diff --git a/alma/lib/Validators/PaymentValidationError.php b/alma/lib/Validators/PaymentValidationError.php index 451f528c..f383fae1 100644 --- a/alma/lib/Validators/PaymentValidationError.php +++ b/alma/lib/Validators/PaymentValidationError.php @@ -28,6 +28,11 @@ exit; } +/** + * Class PaymentValidationError. + * + * @deprecated Use PaymentValidationException instead + */ class PaymentValidationError extends \Exception { public $cart; diff --git a/alma/phpunit.ci.xml b/alma/phpunit.ci.xml index e625168c..fa74652d 100644 --- a/alma/phpunit.ci.xml +++ b/alma/phpunit.ci.xml @@ -28,5 +28,7 @@ - + + + diff --git a/alma/tests/ReportConfigForHTMLReport.php b/alma/tests/ReportConfigForHTMLReport.php new file mode 100644 index 00000000..d68df4ce --- /dev/null +++ b/alma/tests/ReportConfigForHTMLReport.php @@ -0,0 +1,49 @@ +configFilePath = $configFilePath; + } + + public function isHtmlReportFromConfig() + { + $configContent = file_get_contents($this->configFilePath); + // Check & return if the coverage-html option is set + return strpos($configContent, 'coverage-html') !== false; + } + + public function setErrorReportingLevel($level) + { + error_reporting($level); + } + + public function restoreErrorReportingLevel($originalLevel) + { + register_shutdown_function(function () use ($originalLevel) { + $this->setErrorReportingLevel($originalLevel); + }); + } + + public function handleReportConfig() + { + if ($this->isHtmlReportFromConfig()) { + $originalErrorReportingLevel = error_reporting(); + $this->setErrorReportingLevel($originalErrorReportingLevel & ~E_WARNING); + $this->restoreErrorReportingLevel($originalErrorReportingLevel); + } + } +} diff --git a/alma/tests/Unit/Builders/Repositories/InsuranceProductRepositoryBuilderTest.php b/alma/tests/Unit/Builders/Repositories/InsuranceProductRepositoryBuilderTest.php index 1e199983..dc726a7c 100644 --- a/alma/tests/Unit/Builders/Repositories/InsuranceProductRepositoryBuilderTest.php +++ b/alma/tests/Unit/Builders/Repositories/InsuranceProductRepositoryBuilderTest.php @@ -29,7 +29,7 @@ use PHPUnit\Framework\TestCase; /** - * @covers \Alma\PrestaShop\Builders\InsuranceProductRepositoryBuilder + * @covers \Alma\PrestaShop\Builders\Repositories\InsuranceProductRepositoryBuilder */ class InsuranceProductRepositoryBuilderTest extends TestCase { @@ -44,7 +44,7 @@ public function setUp() } /** - * @covers \Alma\PrestaShop\Builders\InsuranceProductRepositoryBuilder::getInstance + * @covers \Alma\PrestaShop\Builders\Repositories\InsuranceProductRepositoryBuilder::getInstance * * @return void */ diff --git a/alma/tests/Unit/Controllers/Front/index.php b/alma/tests/Unit/Controllers/Front/index.php new file mode 100644 index 00000000..88355f61 --- /dev/null +++ b/alma/tests/Unit/Controllers/Front/index.php @@ -0,0 +1,11 @@ +cartFactory = new CartFactory(); + $this->cartMock = $this->createMock(\Cart::class); + $this->cartMock->id = 10; + $this->cartFactory = new CartFactory( + $this->cartMock + ); } public function testCreate() diff --git a/alma/tests/Unit/Helper/AddressHelperTest.php b/alma/tests/Unit/Helper/AddressHelperTest.php index b3677a29..223d82ab 100644 --- a/alma/tests/Unit/Helper/AddressHelperTest.php +++ b/alma/tests/Unit/Helper/AddressHelperTest.php @@ -38,6 +38,12 @@ public function setUp() $this->addressHelper = $addressHelperBuilder->getAddressHelper(); } + /** + * @return void + * + * @throws AlmaException + * @throws \PrestaShopException + */ public function testGetAddressFromCustomer() { $idCustomer = rand(10000, 20000); diff --git a/alma/tests/Unit/Helper/ConfigurationHelperTest.php b/alma/tests/Unit/Helper/ConfigurationHelperTest.php index c95c1b76..462239d3 100644 --- a/alma/tests/Unit/Helper/ConfigurationHelperTest.php +++ b/alma/tests/Unit/Helper/ConfigurationHelperTest.php @@ -50,7 +50,7 @@ public function testIsInPageEnabled($expected, $isInPageEnabled, $installments) { $settingsHelper = \Mockery::mock(SettingsHelper::class); $settingsHelper->shouldReceive('isInPageEnabled')->andReturn($isInPageEnabled); - $this->assertEquals($expected, $this->configurationHelper->isInPageEnabled($installments, $settingsHelper)); + $this->assertEquals($expected, $this->configurationHelper->isInPageEnabled($settingsHelper)); } public function provideIsInPageEnabled() @@ -82,7 +82,7 @@ public function provideIsInPageEnabled() 'installments' => 4, ], 'test inpage enable, installment 6' => [ - 'expected' => false, + 'expected' => true, 'isInpageEnabled' => true, 'installments' => 6, ], diff --git a/alma/tests/Unit/Helper/PlanHelperTest.php b/alma/tests/Unit/Helper/PlanHelperTest.php index 1d651c66..3a69d7da 100644 --- a/alma/tests/Unit/Helper/PlanHelperTest.php +++ b/alma/tests/Unit/Helper/PlanHelperTest.php @@ -37,11 +37,11 @@ protected function setUp() } /** - * @dataProvider provideIsPnxPlus4 + * @dataProvider provideisCredit * * @return void */ - public function testIsPnxPlus4($expected, $installmentsCount) + public function testisCredit($expected, $installmentsCount) { $plan = new FeePlan( [ @@ -49,10 +49,10 @@ public function testIsPnxPlus4($expected, $installmentsCount) ] ); - $this->assertEquals($expected, $this->planHelper->isPnxPlus4($plan)); + $this->assertEquals($expected, $this->planHelper->isCredit($plan)); } - public function provideIsPnxPlus4() + public function provideisCredit() { return [ 'test 4 installments' => [ diff --git a/alma/tests/Unit/Helper/ProductHelperTest.php b/alma/tests/Unit/Helper/ProductHelperTest.php index d9772ad5..e0d48f31 100644 --- a/alma/tests/Unit/Helper/ProductHelperTest.php +++ b/alma/tests/Unit/Helper/ProductHelperTest.php @@ -24,6 +24,9 @@ namespace Alma\PrestaShop\Tests\Unit\Helper; +use Alma\PrestaShop\Exceptions\ProductException; +use Alma\PrestaShop\Factories\ProductFactory; +use Alma\PrestaShop\Helpers\InsuranceHelper; use Alma\PrestaShop\Helpers\ProductHelper; use PHPUnit\Framework\TestCase; @@ -41,15 +44,30 @@ class ProductHelperTest extends TestCase * @var \PHPUnit_Framework_MockObject_MockObject|\Product|(\Product&\PHPUnit_Framework_MockObject_MockObject) */ protected $productMock; + /** + * @var InsuranceHelper + */ + protected $insuranceHelperMock; + /** + * @var ProductFactory + */ + protected $productFactoryMock; /** * @return void */ public function setUp() { - $this->productHelper = new ProductHelper(); $this->categoryMock = $this->createMock(\Category::class); - $this->productMock = $this->createMock(\Product::class); + $this->productMock = $this->getMockBuilder(\Product::class) + ->setMethods(['getAttributeCombination']) + ->getMock(); + $this->insuranceHelperMock = $this->createMock(InsuranceHelper::class); + $this->productFactoryMock = $this->createMock(ProductFactory::class); + $this->productHelper = new ProductHelper( + $this->insuranceHelperMock, + $this->productFactoryMock + ); } public function tearDown() @@ -57,6 +75,8 @@ public function tearDown() $this->productHelper = null; $this->categoryMock = null; $this->productMock = null; + $this->insuranceHelperMock = null; + $this->productFactoryMock = null; } /** @@ -142,4 +162,172 @@ public function wrongDataProvider() 'product is true' => [true], ]; } + + /** + * @dataProvider wrongParamsForGetAttributeCombinationDataProvider + * + * @return void + * + * @throws ProductException + */ + public function testGetAttributeCombinationsByProductIdWithWrongParams($productId, $languageId) + { + $this->expectException(ProductException::class); + $this->productHelper->getAttributeCombinationsByProductId($productId, $languageId); + } + + public function testGetAttributeCombinationsByProductIdWithRightParams() + { + $this->productMock->id = 2; + $attributeCombinationsReturned = [ + [ + 'id_product_attribute' => '9', + 'id_product' => (string) $this->productMock->id, + 'reference' => 'demo_3', + 'supplier_reference' => '', + 'location' => '', + 'ean13' => '', + 'isbn' => '', + 'upc' => '', + 'mpn' => '', + 'wholesale_price' => '0.000000', + 'price' => '0.000000', + 'ecotax' => '0.000000', + 'quantity' => 1200, + 'weight' => '0.000000', + 'unit_price_impact' => '0.000000', + 'default_on' => '1', + 'minimal_quantity' => '1', + 'low_stock_threshold' => null, + 'low_stock_alert' => '0', + 'available_date' => '0000-00-00', + 'id_shop' => '1', + 'id_attribute_group' => '1', + 'is_color_group' => '0', + 'group_name' => null, + 'attribute_name' => null, + 'id_attribute' => '1', + ], + [ + 'id_product_attribute' => '10', + 'id_product' => (string) $this->productMock->id, + 'reference' => 'demo_3', + 'supplier_reference' => '', + 'location' => '', + 'ean13' => '', + 'isbn' => '', + 'upc' => '', + 'mpn' => '', + 'wholesale_price' => '0.000000', + 'price' => '0.000000', + 'ecotax' => '0.000000', + 'quantity' => 300, + 'weight' => '0.000000', + 'unit_price_impact' => '0.000000', + 'default_on' => null, + 'minimal_quantity' => '1', + 'low_stock_threshold' => null, + 'low_stock_alert' => '0', + 'available_date' => '0000-00-00', + 'id_shop' => '1', + 'id_attribute_group' => '1', + 'is_color_group' => '0', + 'group_name' => null, + 'attribute_name' => null, + 'id_attribute' => '2', + ], + [ + 'id_product_attribute' => '11', + 'id_product' => (string) $this->productMock->id, + 'reference' => 'demo_3', + 'supplier_reference' => '', + 'location' => '', + 'ean13' => '', + 'isbn' => '', + 'upc' => '', + 'mpn' => '', + 'wholesale_price' => '0.000000', + 'price' => '0.000000', + 'ecotax' => '0.000000', + 'quantity' => 300, + 'weight' => '0.000000', + 'unit_price_impact' => '0.000000', + 'default_on' => null, + 'minimal_quantity' => '1', + 'low_stock_threshold' => null, + 'low_stock_alert' => '0', + 'available_date' => '0000-00-00', + 'id_shop' => '1', + 'id_attribute_group' => '1', + 'is_color_group' => '0', + 'group_name' => null, + 'attribute_name' => null, + 'id_attribute' => '3', + ], + [ + 'id_product_attribute' => '12', + 'id_product' => (string) $this->productMock->id, + 'reference' => 'demo_3', + 'supplier_reference' => '', + 'location' => '', + 'ean13' => '', + 'isbn' => '', + 'upc' => '', + 'mpn' => '', + 'wholesale_price' => '0.000000', + 'price' => '0.000000', + 'ecotax' => '0.000000', + 'quantity' => 300, + 'weight' => '0.000000', + 'unit_price_impact' => '0.000000', + 'default_on' => null, + 'minimal_quantity' => '1', + 'low_stock_threshold' => null, + 'low_stock_alert' => '0', + 'available_date' => '0000-00-00', + 'id_shop' => '1', + 'id_attribute_group' => '1', + 'is_color_group' => '0', + 'group_name' => null, + 'attribute_name' => null, + 'id_attribute' => '4', + ], + ]; + $languageId = 4; + + $this->productFactoryMock->expects($this->once()) + ->method('create') + ->with($this->productMock->id) + ->willReturn($this->productMock); + + $this->assertEquals( + $attributeCombinationsReturned, + $this->productHelper->getAttributeCombinationsByProductId($this->productMock->id, $languageId) + ); + } + + /** + * @return array + */ + public function wrongParamsForGetAttributeCombinationDataProvider() + { + return [ + 'params are null' => [ + 'productId' => null, + 'languageId' => null, + ], + 'params are string' => [ + 'productId' => 'wrong productId', + 'languageId' => 'wrongLanguageId', + ], + 'productId is int and languageId is null' => [ + 'productId' => 2, + 'languageId' => null, + ], + 'productId is null and languageId is int' => [ + 'productId' => null, + 'languageId' => 3, + ], + ]; + } } diff --git a/alma/tests/Unit/Services/CartServiceTest.php b/alma/tests/Unit/Services/CartServiceTest.php index 344aaedf..2eaa7968 100644 --- a/alma/tests/Unit/Services/CartServiceTest.php +++ b/alma/tests/Unit/Services/CartServiceTest.php @@ -24,10 +24,14 @@ namespace Alma\PrestaShop\Tests\Unit\Services; +use Alma\PrestaShop\Exceptions\AlmaException; +use Alma\PrestaShop\Exceptions\CartException; +use Alma\PrestaShop\Factories\CartFactory; use Alma\PrestaShop\Factories\ContextFactory; use Alma\PrestaShop\Factories\ToolsFactory; use Alma\PrestaShop\Helpers\InsuranceHelper; use Alma\PrestaShop\Helpers\InsuranceProductHelper; +use Alma\PrestaShop\Helpers\ProductHelper; use Alma\PrestaShop\Modules\OpartSaveCart\OpartSaveCartCartService; use Alma\PrestaShop\Repositories\CartProductRepository; use Alma\PrestaShop\Services\CartService; @@ -59,19 +63,38 @@ class CartServiceTest extends TestCase * @var ToolsFactory */ protected $toolsFactoryMock; + /** + * @var CartFactory + */ + protected $cartFactoryMock; + /** + * @var CartService + */ + protected $cartService; + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\ProductHelper|(\ProductHelper&\PHPUnit_Framework_MockObject_MockObject) + */ + protected $productHelper; + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Product|(\Product&\PHPUnit_Framework_MockObject_MockObject) + */ + protected $productMock; /** * @return void */ public function setUp() { - $this->cartMock = \Mockery::mock(\Cart::class); + $this->cartMock = $this->createMock(\Cart::class); $this->newCartMock = \Mockery::mock(\Cart::class); - $this->contextFactoryMock = \Mockery::mock(ContextFactory::class); + $this->contextFactoryMock = $this->createMock(ContextFactory::class); $this->opartCartSaveServiceSpy = \Mockery::spy(OpartSaveCartCartService::class); $this->toolsFactoryMock = \Mockery::mock(ToolsFactory::class); $this->insuranceHelperMock = \Mockery::mock(InsuranceHelper::class); $this->insuranceProductHelperSpy = \Mockery::spy(InsuranceProductHelper::class); + $this->cartFactoryMock = $this->createMock(CartFactory::class); + $this->productHelperMock = $this->createMock(ProductHelper::class); + $this->productMock = $this->createMock(\Product::class); $this->cartServiceMock = \Mockery::mock( CartService::class, [ @@ -81,8 +104,20 @@ public function setUp() $this->insuranceHelperMock, $this->insuranceProductHelperSpy, $this->toolsFactoryMock, + $this->cartFactoryMock, + $this->productHelperMock, ] )->makePartial(); + $this->cartService = new CartService( + $this->createMock(CartProductRepository::class), + $this->contextFactoryMock, + $this->opartCartSaveServiceSpy, + $this->insuranceHelperMock, + $this->insuranceProductHelperSpy, + $this->toolsFactoryMock, + $this->cartFactoryMock, + $this->productHelperMock + ); } /** @@ -130,4 +165,213 @@ public function testDuplicateAlmaInsuranceProductsIfNotExist() $this->cartServiceMock->duplicateAlmaInsuranceProductsIfNotExist($this->newCartMock, $this->cartMock); $this->insuranceProductHelperSpy->shouldHaveReceived('duplicateAlmaInsuranceProducts')->once(); } + + /** + * @throws AlmaException + * @throws CartException + */ + public function testDeleteProductWithoutProductId() + { + $this->cartMock->id = 12; + + $this->expectException(CartException::class); + $this->cartService->deleteProductByCartId(false, $this->cartMock->id); + } + + /** + * @throws AlmaException + * @throws CartException + */ + public function testDeleteProductWithoutCartId() + { + $idProduct = 23; + + $this->expectException(CartException::class); + $this->cartService->deleteProductByCartId($idProduct, $this->cartMock->id); + } + + /** + * @throws CartException + * @throws AlmaException + */ + public function testDeleteProductThrowExceptionIfGetAttributeCombinationsByProductIdReturnEmptyArray() + { + $languageId = 1; + $this->productMock->id = 10; + $this->cartMock->id = 12; + $this->cartFactoryMock->expects($this->once()) + ->method('create') + ->with($this->cartMock->id) + ->willReturn($this->cartMock); + $this->contextFactoryMock->expects($this->once()) + ->method('getContextLanguageId') + ->willReturn($languageId); + $this->productHelperMock->expects($this->once()) + ->method('getAttributeCombinationsByProductId') + ->with($this->productMock->id, $languageId) + ->willThrowException(new CartException('Error attribute combinations')); + $this->expectException(CartException::class); + $this->cartService->deleteProductByCartId($this->productMock->id, $this->cartMock->id); + } + + /** + * @throws CartException + * @throws AlmaException + */ + public function testDeleteProductWithProductIdAndCartId() + { + $languageId = 1; + $this->productMock->id = 10; + $this->cartMock->id = 12; + $this->cartMock->expects($this->exactly(4)) + ->method('deleteProduct') + ->withConsecutive( + [ + $this->productMock->id, $this->attributeCombinationsData()[0]['id_product_attribute'], + ], + [ + $this->productMock->id, $this->attributeCombinationsData()[1]['id_product_attribute'], + ], + [ + $this->productMock->id, $this->attributeCombinationsData()[2]['id_product_attribute'], + ], + [ + $this->productMock->id, $this->attributeCombinationsData()[3]['id_product_attribute'], + ] + ) + ->willReturnOnConsecutiveCalls(true, true, true, true); + $this->cartFactoryMock->expects($this->once()) + ->method('create') + ->with($this->cartMock->id) + ->willReturn($this->cartMock); + $this->contextFactoryMock->expects($this->once()) + ->method('getContextLanguageId') + ->willReturn($languageId); + $this->productHelperMock->expects($this->once()) + ->method('getAttributeCombinationsByProductId') + ->with($this->productMock->id, $languageId) + ->willReturn($this->attributeCombinationsData()); + $this->assertTrue($this->cartService->deleteProductByCartId($this->productMock->id, $this->cartMock->id)); + } + + /** + * @return array + */ + public function attributeCombinationsData() + { + return [ + [ + 'id_product_attribute' => '9', + 'id_product' => (string) $this->productMock->id, + 'reference' => 'demo_3', + 'supplier_reference' => '', + 'location' => '', + 'ean13' => '', + 'isbn' => '', + 'upc' => '', + 'mpn' => '', + 'wholesale_price' => '0.000000', + 'price' => '0.000000', + 'ecotax' => '0.000000', + 'quantity' => 1200, + 'weight' => '0.000000', + 'unit_price_impact' => '0.000000', + 'default_on' => '1', + 'minimal_quantity' => '1', + 'low_stock_threshold' => null, + 'low_stock_alert' => '0', + 'available_date' => '0000-00-00', + 'id_shop' => '1', + 'id_attribute_group' => '1', + 'is_color_group' => '0', + 'group_name' => null, + 'attribute_name' => null, + 'id_attribute' => '1', + ], + [ + 'id_product_attribute' => '10', + 'id_product' => (string) $this->productMock->id, + 'reference' => 'demo_3', + 'supplier_reference' => '', + 'location' => '', + 'ean13' => '', + 'isbn' => '', + 'upc' => '', + 'mpn' => '', + 'wholesale_price' => '0.000000', + 'price' => '0.000000', + 'ecotax' => '0.000000', + 'quantity' => 300, + 'weight' => '0.000000', + 'unit_price_impact' => '0.000000', + 'default_on' => null, + 'minimal_quantity' => '1', + 'low_stock_threshold' => null, + 'low_stock_alert' => '0', + 'available_date' => '0000-00-00', + 'id_shop' => '1', + 'id_attribute_group' => '1', + 'is_color_group' => '0', + 'group_name' => null, + 'attribute_name' => null, + 'id_attribute' => '2', + ], + [ + 'id_product_attribute' => '11', + 'id_product' => (string) $this->productMock->id, + 'reference' => 'demo_3', + 'supplier_reference' => '', + 'location' => '', + 'ean13' => '', + 'isbn' => '', + 'upc' => '', + 'mpn' => '', + 'wholesale_price' => '0.000000', + 'price' => '0.000000', + 'ecotax' => '0.000000', + 'quantity' => 300, + 'weight' => '0.000000', + 'unit_price_impact' => '0.000000', + 'default_on' => null, + 'minimal_quantity' => '1', + 'low_stock_threshold' => null, + 'low_stock_alert' => '0', + 'available_date' => '0000-00-00', + 'id_shop' => '1', + 'id_attribute_group' => '1', + 'is_color_group' => '0', + 'group_name' => null, + 'attribute_name' => null, + 'id_attribute' => '3', + ], + [ + 'id_product_attribute' => '12', + 'id_product' => (string) $this->productMock->id, + 'reference' => 'demo_3', + 'supplier_reference' => '', + 'location' => '', + 'ean13' => '', + 'isbn' => '', + 'upc' => '', + 'mpn' => '', + 'wholesale_price' => '0.000000', + 'price' => '0.000000', + 'ecotax' => '0.000000', + 'quantity' => 300, + 'weight' => '0.000000', + 'unit_price_impact' => '0.000000', + 'default_on' => null, + 'minimal_quantity' => '1', + 'low_stock_threshold' => null, + 'low_stock_alert' => '0', + 'available_date' => '0000-00-00', + 'id_shop' => '1', + 'id_attribute_group' => '1', + 'is_color_group' => '0', + 'group_name' => null, + 'attribute_name' => null, + 'id_attribute' => '4', + ], + ]; + } } diff --git a/alma/tests/Unit/Services/InsuranceProductServiceTest.php b/alma/tests/Unit/Services/InsuranceProductServiceTest.php index eb1bf04c..44b54b57 100644 --- a/alma/tests/Unit/Services/InsuranceProductServiceTest.php +++ b/alma/tests/Unit/Services/InsuranceProductServiceTest.php @@ -24,12 +24,17 @@ namespace Alma\PrestaShop\Tests\Unit\Services; +use Alma\PrestaShop\Exceptions\CartException; +use Alma\PrestaShop\Exceptions\InsuranceProductException; +use Alma\PrestaShop\Factories\CartFactory; use Alma\PrestaShop\Factories\ContextFactory; use Alma\PrestaShop\Factories\LinkFactory; use Alma\PrestaShop\Factories\ProductFactory; use Alma\PrestaShop\Factories\ToolsFactory; use Alma\PrestaShop\Helpers\Admin\AdminInsuranceHelper; +use Alma\PrestaShop\Helpers\ConstantsHelper; use Alma\PrestaShop\Helpers\ImageHelper; +use Alma\PrestaShop\Helpers\InsuranceHelper; use Alma\PrestaShop\Helpers\PriceHelper; use Alma\PrestaShop\Helpers\ProductHelper; use Alma\PrestaShop\Helpers\ToolsHelper; @@ -58,11 +63,11 @@ class InsuranceProductServiceTest extends TestCase /** * @var \Cart */ - protected $cart; + protected $cartMock1; /** * @var \Cart */ - protected $newCart; + protected $cartMock2; /** * @var ProductFactory */ @@ -103,6 +108,30 @@ class InsuranceProductServiceTest extends TestCase * @var \Link */ protected $linkMock; + /** + * @var InsuranceProductService + */ + protected $insuranceProductService; + /** + * @var \Language|(\Language&\PHPUnit_Framework_MockObject_MockObject)|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productRepositoryMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Product|(\Product&\PHPUnit_Framework_MockObject_MockObject) + */ + protected $insuranceProductMock; + /** + * @var \ObjectModel|(\ObjectModel&\PHPUnit_Framework_MockObject_MockObject)|\PHPUnit_Framework_MockObject_MockObject + */ + protected $objectModelMock; + /** + * @var CartFactory + */ + protected $cartFactoryMock; + /** + * @var CartService + */ + protected $cartServiceMock; public function setUp() { @@ -113,9 +142,13 @@ public function setUp() $this->productFactoryMock = $this->createMock(ProductFactory::class); $this->linkFactoryMock = $this->createMock(LinkFactory::class); $this->linkFactoryMock->method('create')->willReturn($this->linkMock); - $this->cart = $this->createMock(\Cart::class); - $this->newCart = $this->createMock(\Cart::class); + $this->cartMock1 = $this->createMock(\Cart::class); + $this->cartMock1->id = 15; + $this->cartMock2 = $this->createMock(\Cart::class); + $this->cartMock2->id = 17; + $this->cartFactoryMock = $this->createMock(CartFactory::class); $this->context = $this->createMock(\Context::class); + $this->productRepositoryMock = $this->createMock(ProductRepository::class); $this->languageMock = $this->createMock(\Language::class); $this->languageMock->id = 1; $this->shop = $this->createMock(\Shop::class); @@ -127,6 +160,7 @@ public function setUp() $this->imageHelperMock = $this->createMock(ImageHelper::class); $this->toolsHelperMock = $this->createMock(ToolsHelper::class); $this->priceHelperMock = $this->createMock(PriceHelper::class); + $this->cartServiceMock = $this->createMock(CartService::class); $this->insuranceProductServiceMock = \Mockery::mock(InsuranceProductService::class, [ $this->productFactoryMock, @@ -146,7 +180,31 @@ public function setUp() $this->toolsFactorySpy, $this->imageHelperMock, $this->toolsHelperMock, + $this->cartFactoryMock, ])->makePartial(); + $this->insuranceProductService = new InsuranceProductService( + $this->productFactoryMock, + $this->linkFactoryMock, + $this->almaInsuranceProductRepository, + $this->contextFactoryMock, + $this->createMock(AttributeGroupProductService::class), + $this->createMock(AttributeProductService::class), + $this->createMock(CombinationProductAttributeService::class), + $this->createMock(InsuranceService::class), + $this->cartServiceMock, + $this->productRepositoryMock, + $this->createMock(ProductHelper::class), + $this->createMock(InsuranceApiService::class), + $this->priceHelperMock, + $this->createMock(InsuranceHelper::class), + $this->toolsFactorySpy, + $this->imageHelperMock, + $this->toolsHelperMock, + $this->cartFactoryMock + ); + $this->insuranceProductMock = $this->createMock(\Product::class); + $this->insuranceProductMock->id = '10'; + $this->insuranceProductMock->active = true; } /** @@ -343,4 +401,168 @@ public function testCanHandleAddingProductInsuranceWithInputInsuranceWithInputAd $this->assertFalse($this->insuranceProductServiceMock->canHandleAddingProductInsuranceOnce()); } + + /** + * @dataProvider insuranceProductStateWithWrongParamsDataProvider + * + * @param $state + * + * @return void + * + * @throws InsuranceProductException + * @throws \PrestaShopException + */ + public function testHandleInsuranceProductStateWithWrongParam($state) + { + $this->expectException(InsuranceProductException::class); + $this->insuranceProductService->handleInsuranceProductState($state); + } + + /** + * @dataProvider insuranceProductStateWithRightParamsDataProvider + * + * @param $state + * + * @return void + * + * @throws InsuranceProductException + * @throws \PrestaShopException + */ + public function testHandleInsuranceProductStateWithRightParams($state) + { + $this->productRepositoryMock->expects($this->once()) + ->method('getProductIdByReference') + ->with(ConstantsHelper::ALMA_INSURANCE_PRODUCT_REFERENCE, $this->languageMock->id) + ->willReturn($this->insuranceProductMock->id); + $this->insuranceProductMock->expects($this->once()) + ->method('save'); + $this->productFactoryMock->expects($this->once()) + ->method('create') + ->with($this->insuranceProductMock->id) + ->willReturn($this->insuranceProductMock); + $this->insuranceProductService->handleInsuranceProductState($state); + } + + /** + * @return void + * + * @throws InsuranceProductException + * @throws \PrestaShopDatabaseException + */ + public function testRemoveInsuranceProductsNotOrderedWithNoCartsReturned() + { + $this->almaInsuranceProductRepository->expects($this->once()) + ->method('getCartsNotOrdered') + ->willReturn([]); + $this->assertTrue($this->insuranceProductService->removeInsuranceProductsNotOrdered()); + } + + /** + * @throws InsuranceProductException + * @throws \PrestaShopDatabaseException + */ + public function testRemoveInsuranceProductsNotOrderedWithCartIdsAndWithoutInsuranceProducts() + { + $this->almaInsuranceProductRepository->expects($this->once()) + ->method('getCartsNotOrdered') + ->willReturn([ + ['id_cart' => $this->cartMock1->id], + ['id_cart' => $this->cartMock2->id], + ]); + $this->productRepositoryMock->expects($this->once()) + ->method('getProductIdByReference') + ->with(ConstantsHelper::ALMA_INSURANCE_PRODUCT_REFERENCE) + ->willReturn(false); + $this->expectException(InsuranceProductException::class); + $this->insuranceProductService->removeInsuranceProductsNotOrdered(); + } + + /** + * @throws \PrestaShopDatabaseException + * @throws InsuranceProductException + */ + public function testRemoveInsuranceProductsNotOrderedThrowExceptionByDeleteProductByCartId() + { + $this->almaInsuranceProductRepository->expects($this->once()) + ->method('getCartsNotOrdered') + ->willReturn([ + ['id_cart' => $this->cartMock1->id], + ['id_cart' => $this->cartMock2->id], + ]); + $this->productRepositoryMock->expects($this->once()) + ->method('getProductIdByReference') + ->with(ConstantsHelper::ALMA_INSURANCE_PRODUCT_REFERENCE) + ->willReturn($this->insuranceProductMock->id); + $this->cartServiceMock->expects($this->exactly(2)) + ->method('deleteProductByCartId') + ->withConsecutive( + [$this->insuranceProductMock->id, $this->cartMock1->id], + [$this->insuranceProductMock->id, $this->cartMock2->id] + ) + ->willReturnOnConsecutiveCalls( + true, + $this->throwException(new CartException("Product id and cart id are required. ProductId: {$this->insuranceProductMock->id}, cartId: {$this->cartMock2->id}")) + ); + + $this->expectException(InsuranceProductException::class); + $this->insuranceProductService->removeInsuranceProductsNotOrdered(); + } + + /** + * @throws InsuranceProductException + * @throws \PrestaShopDatabaseException + */ + public function testRemoveInsuranceProductsNotOrderedWithRightData() + { + $this->almaInsuranceProductRepository->expects($this->once()) + ->method('getCartsNotOrdered') + ->willReturn([ + ['id_cart' => $this->cartMock1->id], + ['id_cart' => $this->cartMock2->id], + ['id_cart' => '34'], + ]); + $this->productRepositoryMock->expects($this->once()) + ->method('getProductIdByReference') + ->with(ConstantsHelper::ALMA_INSURANCE_PRODUCT_REFERENCE) + ->willReturn($this->insuranceProductMock->id); + $this->cartServiceMock->expects($this->exactly(3)) + ->method('deleteProductByCartId') + ->withConsecutive( + [$this->insuranceProductMock->id, $this->cartMock1->id], + [$this->insuranceProductMock->id, $this->cartMock2->id], + [$this->insuranceProductMock->id, '34'] + ) + ->willReturnOnConsecutiveCalls(true, true, false); + + $this->almaInsuranceProductRepository->expects($this->once()) + ->method('deleteAssociationsByCartIds') + ->with("{$this->cartMock1->id}, {$this->cartMock2->id}, 34"); + + $this->insuranceProductService->removeInsuranceProductsNotOrdered(); + } + + public function insuranceProductStateWithWrongParamsDataProvider() + { + return [ + 'With a string' => ['toto'], + 'With an object' => [\stdClass::class], + 'With an array' => [[]], + 'With a number' => [5], + 'With null' => [null], + ]; + } + + public function insuranceProductStateWithRightParamsDataProvider() + { + return [ + 'With true' => [true], + 'With false' => [false], + 'With a string false' => ['false'], + 'With a string true' => ['true'], + 'With a string 0' => ['0'], + 'With a string 1' => ['1'], + 'With a int 0' => [0], + 'With a int 1' => [1], + ]; + } } diff --git a/alma/tests/Unit/Validators/PaymentValidationTest.php b/alma/tests/Unit/Validators/PaymentValidationTest.php new file mode 100644 index 00000000..dea9e439 --- /dev/null +++ b/alma/tests/Unit/Validators/PaymentValidationTest.php @@ -0,0 +1,115 @@ + + * @copyright 2018-2024 Alma SAS + * @license https://opensource.org/licenses/MIT The MIT License + */ + +namespace Alma\PrestaShop\Tests\Unit\Validators; + +use Alma\API\Lib\PaymentValidator; +use Alma\PrestaShop\Exceptions\PaymentValidationException; +use Alma\PrestaShop\Factories\ContextFactory; +use Alma\PrestaShop\Factories\ModuleFactory; +use Alma\PrestaShop\Validators\PaymentValidation; +use PHPUnit\Framework\TestCase; + +class PaymentValidationTest extends TestCase +{ + const API_KEY = 'api_test_abc123'; + const PAYMENT_ID = 'payment_abc123'; + const WRONG_SIGNATURE = 'wrong_signature'; + const GOOD_SIGNATURE = 'good_signature'; + /** + * @var PaymentValidation + */ + protected $paymentValidation; + /** + * @var PaymentValidator + */ + protected $clientPaymentValidator; + + public function setUp() + { + $this->clientPaymentValidator = $this->createMock(PaymentValidator::class); + $this->paymentValidation = new PaymentValidation( + $this->createMock(ContextFactory::class), + $this->createMock(ModuleFactory::class), + $this->clientPaymentValidator + ); + } + + public function tearDown() + { + $this->paymentValidation = null; + } + + /** + * @dataProvider checkSignatureWrongParamsDataProvider + * + * @throws PaymentValidationException + */ + public function testCheckSignatureWithoutParamsReturnError($paymentId, $apiKey, $signature) + { + $this->expectException(PaymentValidationException::class); + $this->paymentValidation->checkSignature($paymentId, $apiKey, $signature); + } + + /** + * @throws PaymentValidationException + */ + public function testCheckSignatureWithBadSignatureReturnError() + { + $this->clientPaymentValidator->expects($this->once()) + ->method('isHmacValidated') + ->with(self::PAYMENT_ID, self::API_KEY, self::WRONG_SIGNATURE) + ->willReturn(false); + $this->expectException(PaymentValidationException::class); + $this->paymentValidation->checkSignature(self::PAYMENT_ID, self::API_KEY, self::WRONG_SIGNATURE); + } + + /** + * @throws PaymentValidationException + */ + public function testCheckSignatureWithGoodSignature() + { + $this->clientPaymentValidator->expects($this->once()) + ->method('isHmacValidated') + ->with(self::PAYMENT_ID, self::API_KEY, self::GOOD_SIGNATURE) + ->willReturn(true); + $this->paymentValidation->checkSignature(self::PAYMENT_ID, self::API_KEY, self::GOOD_SIGNATURE); + } + + /** + * @return array[] + */ + public function checkSignatureWrongParamsDataProvider() + { + return [ + 'Without api key' => [self::PAYMENT_ID, '', self::GOOD_SIGNATURE], + 'Without payement id' => ['', self::API_KEY, self::GOOD_SIGNATURE], + 'Without signature' => [self::PAYMENT_ID, self::API_KEY, ''], + 'With api key null' => [self::PAYMENT_ID, null, self::GOOD_SIGNATURE], + 'With payement id null' => [null, self::API_KEY, self::GOOD_SIGNATURE], + 'With signature null' => [self::PAYMENT_ID, self::API_KEY, null], + ]; + } +} diff --git a/alma/tests/bootstrap.php b/alma/tests/bootstrap.php index 35eb86bd..1a165f5a 100644 --- a/alma/tests/bootstrap.php +++ b/alma/tests/bootstrap.php @@ -24,3 +24,10 @@ require '../../config/config.inc.php'; require 'alma.php'; + +// Workaround to avoid error during generation of html report file +use Alma\PrestaShop\Tests\ReportConfigForHTMLReport; + +$configFilePath = __DIR__ . '/../phpunit.ci.xml'; +$reportConfig = new ReportConfigForHTMLReport($configFilePath); +$reportConfig->handleReportConfig(); diff --git a/alma/translations/de.php b/alma/translations/de.php index 063c136d..0c25ff59 100644 --- a/alma/translations/de.php +++ b/alma/translations/de.php @@ -31,7 +31,7 @@ $_MODULE['<{alma}prestashop>alma_7bab99339291e7eea710b4a998e663de'] = 'Alma erfordert die PHP-cURL-Erweiterung'; $_MODULE['<{alma}prestashop>alma_fbb47375d2272bef79f5722a183bf1ec'] = 'Alma erfordert die PHP-JSON-Erweiterung'; $_MODULE['<{alma}prestashop>alma_7fb83ba9cf3cc38857a2ddae98534d22'] = 'Alma erfordert OpenSSL 1.0.1 oder höher'; -$_MODULE['<{alma}prestashop>modulefactorytest_5914ca153abaf8a2af61e13e7fe7e829'] = 'Mein Wortlaut zum Übersetzen'; +$_MODULE['<{alma}prestashop>modulefactorytest_5914ca153abaf8a2af61e13e7fe7e829'] = 'My wording to translate'; $_MODULE['<{alma}prestashop>wrongcredentialsexception_cc76de47de858ac2fa2964f534ecfdfb'] = 'Could not connect to Alma using your API keys.'; $_MODULE['<{alma}prestashop>wrongcredentialsexception_733b6ddadc31fd97174b0cfe69b584c9'] = 'Please double check your keys on your %1$sAlma dashboard%2$s.'; $_MODULE['<{alma}prestashop>wrongparamsexception_8ec75b5f68c49d1d6541b1e318a8120d'] = 'Error(s) key(s): %1$s.'; @@ -253,13 +253,13 @@ $_MODULE['<{alma}prestashop>pnx_fees_5532ee2e5ea88b613ccb38e9c88f9a77'] = 'wenn Sie glauben, dass Ihr Volumen bessere Preise rechtfertigt!'; $_MODULE['<{alma}prestashop>order_refund_ps15_cbb3ab577b705e328991986e35218f50'] = 'Erstatteter Betrag'; $_MODULE['<{alma}prestashop>order_refund_ps15_96b0141273eabab320119c467cdcaf17'] = 'Insgesamt'; -$_MODULE['<{alma}prestashop>notificationconfiguration_9f246d669c560d2ac604f5e01c8dd981'] = 'Um Alma zu verwenden, gehen Sie bitte folgendermaßen vor'; -$_MODULE['<{alma}prestashop>notificationconfiguration_3f2c7ed52eb56243edc3a40def7df34b'] = 'Zugehöriges PrestaShop-Konto (gleich unten)'; -$_MODULE['<{alma}prestashop>notificationconfiguration_b6a9cb8bac15ad209cf970e3f7cc73a0'] = 'Ein Alma-Konto erstellen'; -$_MODULE['<{alma}prestashop>notificationconfiguration_e243bbd390b43c9ee6047fab4ab3cdc0'] = 'Lesen Sie unseren Leitfaden für den Einstieg'; -$_MODULE['<{alma}prestashop>notificationconfiguration_cf5ec9fb9f7214a3db82a5a39fec5b48'] = 'Geben Sie Ihren API-Schlüssel ein'; -$_MODULE['<{alma}prestashop>notificationconfiguration_055f421849c2a1c32b885c95540f827c'] = 'Finden Sie Ihren API-Live-Schlüssel auf Ihrem %1$s Alma Dashboard%2$s'; -$_MODULE['<{alma}prestashop>notificationconfiguration_53b458e8453e3694811e61c96527d638'] = 'Um den Testmodus zu verwenden, rufen Sie Ihren Test-API-Schlüssel von Ihrem %1$ssandbox dashboard%2$sab.'; +$_MODULE['<{alma}prestashop>notificationconfiguration_9f246d669c560d2ac604f5e01c8dd981'] = 'To use Alma, please follow these steps'; +$_MODULE['<{alma}prestashop>notificationconfiguration_3f2c7ed52eb56243edc3a40def7df34b'] = 'Associate PrestaShop account (just below)'; +$_MODULE['<{alma}prestashop>notificationconfiguration_b6a9cb8bac15ad209cf970e3f7cc73a0'] = 'Create an Alma account'; +$_MODULE['<{alma}prestashop>notificationconfiguration_e243bbd390b43c9ee6047fab4ab3cdc0'] = 'Consult our getting started guide'; +$_MODULE['<{alma}prestashop>notificationconfiguration_cf5ec9fb9f7214a3db82a5a39fec5b48'] = 'Enter your API key'; +$_MODULE['<{alma}prestashop>notificationconfiguration_055f421849c2a1c32b885c95540f827c'] = 'Find your API live key on your %1$s Alma dashboard%2$s'; +$_MODULE['<{alma}prestashop>notificationconfiguration_53b458e8453e3694811e61c96527d638'] = 'Um den Testmodus zu verwenden, rufen Sie Ihren Test-API-Schlüssel von Ihrem %1$ssandbox dashboard%2$s ab.'; $_MODULE['<{alma}prestashop>notificationconfiguration_41d0063d3bb7d3067f46734fac8b27c4'] = 'Wir bieten das PrestaShop Account Modul zum Download an'; $_MODULE['<{alma}prestashop>notificationconfiguration_6997dde298b14fbf6a7c95bd39fe8dc4'] = 'Das Modul %1$sfinden Sie hier%2$s'; $_MODULE['<{alma}prestashop>notificationshareofcheckout_a6b0ef107f0d278df612764187bbdece'] = ' Steigern Sie Ihre Leistung und erhalten Sie Einblicke!'; diff --git a/alma/views/css/alma-insurance.css b/alma/views/css/alma-insurance.css index 68049c3d..29bc1db0 100644 --- a/alma/views/css/alma-insurance.css +++ b/alma/views/css/alma-insurance.css @@ -90,6 +90,14 @@ box-shadow: none; } +/** +-- Product Details + */ +.alma-widget-insurance .js-product-details, +.alma-widget-insurance #product-details { + display: none; +} + /** -- Loader dot */ diff --git a/alma/views/js/alma-inpage.js b/alma/views/js/alma-inpage.js index 7859a268..4b3949a8 100644 --- a/alma/views/js/alma-inpage.js +++ b/alma/views/js/alma-inpage.js @@ -79,6 +79,7 @@ function onloadAlma() { removeLoaderButtonPayment(button); button.addEventListener('click', function (e) { e.preventDefault(); + e.stopPropagation(); let paymentOptionId = this.getAttribute('id'); let url = this.getAttribute('href'); let settingInpage = document.querySelector('#alma-inpage-' + paymentOptionId); diff --git a/alma/views/js/alma-product-insurance.js b/alma/views/js/alma-product-insurance.js index 935a215e..74082f79 100644 --- a/alma/views/js/alma-product-insurance.js +++ b/alma/views/js/alma-product-insurance.js @@ -20,11 +20,14 @@ * @copyright 2018-2024 Alma SAS * @license https://opensource.org/licenses/MIT The MIT License */ +if (!document.getElementById('alma-widget-insurance-product-page')) { + throw new Error('[Alma] Product details not found. You need to add the hook displayProductActions in your template product page.'); +} const settings = getSettingsInsurance(); let insuranceSelected = false; let selectedAlmaInsurance = null; let addToCartFlow = false; -let productDetails = JSON.parse(document.getElementById('product-details').dataset.product); +let productDetails = JSON.parse(document.querySelector('.alma-widget-insurance .js-product-details').dataset.product); let quantity = getQuantity(); let almaEligibilityAnswer = false; @@ -74,7 +77,9 @@ let almaEligibilityAnswer = false; 'updatedProduct', function () { document.querySelector('.qty [name="qty"]').value = quantity; - productDetails = JSON.parse(document.getElementById('product-details').dataset.product); + productDetails = JSON.parse(document.querySelector('.alma-widget-insurance .js-product-details').dataset.product); + productDetails.quantity_wanted = parseInt(quantity); + document.querySelector('.alma-widget-insurance .js-product-details').dataset.product = JSON.stringify(productDetails); refreshWidget(); addModalListenerToAddToCart(); } @@ -117,29 +122,27 @@ function onloadAddInsuranceInputOnProductAlma() { let currentResolve; window.addEventListener('message', (e) => { + let widgetInsurance = document.getElementById('alma-widget-insurance-product-page'); if (e.data.type === 'almaEligibilityAnswer') { almaEligibilityAnswer = e.data.eligibilityCallResponseStatus.response.eligibleProduct; btnLoaders('stop'); if (almaEligibilityAnswer) { - let heightIframe = e.data.widgetSize.height; - let stringHeightIframe = heightIframe + 'px'; - if (heightIframe <= 45) { - stringHeightIframe = '100%'; - } - - document.getElementById('alma-widget-insurance-product-page').style.height = stringHeightIframe; prestashop.emit('updateProduct', { reason:{ productUrl: window.location.href, } }); } else { + widgetInsurance.style.display = 'none'; let addToCart = document.querySelector('.add-to-cart'); if (addToCart) { addToCart.removeEventListener("click", insuranceListener) } } } + if (e.data.type === 'changeWidgetHeight') { + widgetInsurance.style.height = e.data.widgetHeight + 'px'; + } if (e.data.type === 'getSelectedInsuranceData') { if (parseInt(document.querySelector('.qty [name="qty"]').value) !== quantity) { quantity = getQuantity(); @@ -241,12 +244,25 @@ function removeInputInsurance() { function addModalListenerToAddToCart() { if (settings.isAddToCartPopupActivated === true && almaEligibilityAnswer) { - let addToCart = document.querySelector('.add-to-cart'); - // If we change the quantity the DOM is reloaded then we need to remove and add the listener again - addToCart.removeEventListener("click", insuranceListener); - addToCart.addEventListener("click", insuranceListener); + const addToCart = getAddToCartButton(); + if (addToCart) { + // If we change the quantity the DOM is reloaded then we need to remove and add the listener again + addToCart.removeEventListener("click", insuranceListener); + addToCart.addEventListener("click", insuranceListener); + } } } + +function getAddToCartButton() { + let addToCart = document.querySelector('button.add-to-cart'); + // TODO: Ravate specific to generalise with selector configuration + if (!addToCart) { + addToCart = document.querySelector('.add-to-cart a, .add-to-cart button').first(); + } + + return addToCart; +} + function insuranceListener(event) { if (!insuranceSelected) { event.preventDefault(); diff --git a/alma/views/templates/hook/displayProductActions.tpl b/alma/views/templates/hook/displayProductActions.tpl index c19d880a..41e1835b 100644 --- a/alma/views/templates/hook/displayProductActions.tpl +++ b/alma/views/templates/hook/displayProductActions.tpl @@ -20,8 +20,7 @@ * @copyright 2018-2024 Alma SAS * @license https://opensource.org/licenses/MIT The MIT License *} -{if isset($layout)} -
- -
-{/if} +
+ + +