From 8b580403f02fad25cc15de1faafb5c816ce1c4a9 Mon Sep 17 00:00:00 2001 From: Marvin Besselsen Date: Thu, 3 Aug 2023 15:09:13 +0200 Subject: [PATCH 1/5] Update GitHub actions * Added PHP 8.2 syntax checker * Added Magento 2.4.6 to testings * Set minimum-stability to dev for composer to include dev branch * Removed truelayer/client version constraint to include dev branch --- .github/workflows/linting.yml | 16 +++++----------- .github/workflows/phpstan.yml | 3 +++ .github/workflows/setup-di-compile.yml | 10 +++++++++- composer.json | 2 +- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 4d7e17f..18ba3a9 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -5,20 +5,14 @@ jobs: php-74: runs-on: ubuntu-latest steps: - - uses: StephaneBour/actions-php-lint@7.4 - with: - dir: './' + - uses: prestashop/github-action-php-lint/7.4@v2.1 - php-80: + php-81: runs-on: ubuntu-latest steps: - - uses: StephaneBour/actions-php-lint@8.0 - with: - dir: './' + - uses: prestashop/github-action-php-lint/8.1@v2.1 - php-81: + php-82: runs-on: ubuntu-latest steps: - - uses: StephaneBour/actions-php-lint@8.1 - with: - dir: './' + - uses: prestashop/github-action-php-lint/8.2@v2.1 \ No newline at end of file diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 32f67d8..0a39c4c 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -22,6 +22,9 @@ jobs: - name: Upload our code into the docker container run: docker cp $(pwd) magento-project-community-edition:/data/extensions/ + - name: Set minimum-stability for composer (temp) + run: docker exec magento-project-community-edition composer config minimum-stability dev + - name: Install the extensions in Magento run: docker exec magento-project-community-edition composer require truelayer/magento2:@dev --no-plugins --with-all-dependencies diff --git a/.github/workflows/setup-di-compile.yml b/.github/workflows/setup-di-compile.yml index fe681d5..e489892 100644 --- a/.github/workflows/setup-di-compile.yml +++ b/.github/workflows/setup-di-compile.yml @@ -13,6 +13,8 @@ jobs: MAGENTO_VERSION: 2.4.4 - PHP_VERSION: php81-fpm MAGENTO_VERSION: 2.4.5 + - PHP_VERSION: php82-fpm + MAGENTO_VERSION: 2.4.6 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -23,8 +25,14 @@ jobs: - name: Create branch for Composer and remove version from composer.json run: git checkout -b continuous-integration-test-branch && sed -i '/version/d' ./composer.json + - name: Set minimum-stability for composer (temp) + run: docker exec magento-project-community-edition composer config minimum-stability dev + - name: Upload the code into the docker container - run: docker cp $(pwd) magento-project-community-edition:/data/extensions/ && docker exec magento-project-community-edition composer require truelayer/magento2:@dev --no-plugins --with-all-dependencies + run: docker cp $(pwd) magento-project-community-edition:/data/extensions/ + + - name: Install the extensions in Magento + run: docker exec magento-project-community-edition composer require truelayer/magento2:@dev --no-plugins --with-all-dependencies - name: Activate the extension and run setup:upgrade and setup:di:compile run: docker exec magento-project-community-edition ./retry "php bin/magento module:enable TrueLayer_Connect && php bin/magento setup:upgrade && php bin/magento setup:di:compile" diff --git a/composer.json b/composer.json index 0ba734d..d90394c 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "magento/module-payment": ">=100.1.0", "magento/module-checkout": ">=100.1.0", "magento/module-sales": ">=100.1.0", - "truelayer/client": ">=1.2.0", + "truelayer/client": "*", "php": ">=7.4.0" }, "require-dev": { From 56912d5553899429d2f63f31a3215f808b775afa Mon Sep 17 00:00:00 2001 From: Marvin Besselsen Date: Thu, 3 Aug 2023 15:09:50 +0200 Subject: [PATCH 2/5] [PLUG-27] Sending private key content with AJAJX request --- Controller/Adminhtml/Credentials/Check.php | 122 +++++++++++++----- .../system/config/button/credentials.phtml | 27 +++- 2 files changed, 116 insertions(+), 33 deletions(-) diff --git a/Controller/Adminhtml/Credentials/Check.php b/Controller/Adminhtml/Credentials/Check.php index 47ed4d6..699a24e 100644 --- a/Controller/Adminhtml/Credentials/Check.php +++ b/Controller/Adminhtml/Credentials/Check.php @@ -11,8 +11,13 @@ use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Filesystem\DirectoryList; +use Magento\Framework\Filesystem\Io\File; use TrueLayer\Connect\Api\Config\RepositoryInterface as ConfigRepository; use TrueLayer\Connect\Service\Api\GetClient; +use TrueLayer\Interfaces\Client\ClientInterface; /** * Credential check controller to validate API data @@ -20,6 +25,11 @@ class Check extends Action implements HttpPostActionInterface { + private const PEM_UPLOAD_FILE = '/truelayer/temp/private_key.pem'; + /** + * @var DirectoryList + */ + private $directoryList; /** * @var GetClient */ @@ -32,6 +42,10 @@ class Check extends Action implements HttpPostActionInterface * @var ConfigRepository */ private $configProvider; + /** + * @var File + */ + private $file; /** * Check constructor. @@ -40,16 +54,22 @@ class Check extends Action implements HttpPostActionInterface * @param JsonFactory $resultJsonFactory * @param GetClient $getClient * @param ConfigRepository $configProvider + * @param File $file + * @param DirectoryList $directoryList */ public function __construct( Action\Context $context, JsonFactory $resultJsonFactory, GetClient $getClient, - ConfigRepository $configProvider + ConfigRepository $configProvider, + File $file, + DirectoryList $directoryList ) { $this->getClient = $getClient; $this->resultJson = $resultJsonFactory->create(); $this->configProvider = $configProvider; + $this->file = $file; + $this->directoryList = $directoryList; parent::__construct($context); } @@ -58,38 +78,10 @@ public function __construct( */ public function execute(): Json { - $config = $this->getCredentials(); - if (!$config['credentials']['client_id'] || !$config['credentials']['client_secret']) { - return $this->resultJson->setData( - [ - 'success' => true, - 'msg' => __('Set credentials first') - ] - ); - } - - try { - $result = $this->getClient->execute( - (int)$config['store_id'], - ['credentials' => $config['credentials']] - ); - } catch (\Exception $exception) { - return $this->resultJson->setData( - ['success' => false, 'msg' => $exception->getMessage()] - ); - } - if (!$result) { - return $this->resultJson->setData( - ['success' => false, 'msg' => 'Credentials are not correct'] - ); - } try { - $result->getMerchantAccounts(); + $this->testCredentials()->getMerchantAccounts(); return $this->resultJson->setData( - [ - 'success' => true, - 'msg' => __('Credentials correct!')->render() - ] + ['success' => true, 'msg' => __('Credentials correct!')->render()] ); } catch (\Exception $exception) { return $this->resultJson->setData( @@ -98,8 +90,39 @@ public function execute(): Json } } + /** + * @throws LocalizedException + * @throws FileSystemException + */ + private function testCredentials(): ?ClientInterface + { + $config = $this->getCredentials(); + + if (!$config['credentials']['client_id']) { + throw new LocalizedException(__('No Client ID set!')); + } + + if (!$config['credentials']['client_secret']) { + throw new LocalizedException(__('No Client Secret set!')); + } + + $result = $this->getClient->execute( + (int)$config['store_id'], + ['credentials' => $config['credentials']] + ); + + $this->cleanSavedTemporaryPrivateKey(); + + if (!$result) { + throw new LocalizedException(__('Credentials are not correct.')); + } + + return $result; + } + /** * @return array + * @throws FileSystemException */ private function getCredentials(): array { @@ -125,9 +148,44 @@ private function getCredentials(): array 'credentials' => [ 'client_id' => $clientId, 'client_secret' => $clientSecret, - 'private_key' => $configCredentials['private_key'], + 'private_key' => $this->getPrivateKeyPath($configCredentials), 'key_id' => $keyId ] ]; } + + /** + * @param array $configCredentials + * @return string + * @throws FileSystemException + */ + private function getPrivateKeyPath(array $configCredentials): string + { + if ($privateKey = $this->getRequest()->getParam('private_key')) { + $path = $this->directoryList->getPath('var') . self::PEM_UPLOAD_FILE; + $fileInfo = $this->file->getPathInfo($path); + + if (!$this->file->fileExists($fileInfo['dirname'])) { + $this->file->mkdir($fileInfo['dirname']); + } + + $this->file->write($path, $privateKey); + + return $path; + } + + return $configCredentials['private_key']; + } + + /** + * @return void + * @throws FileSystemException + */ + private function cleanSavedTemporaryPrivateKey(): void + { + $path = $this->directoryList->getPath('var') . self::PEM_UPLOAD_FILE; + if ($this->file->fileExists($path)) { + $this->file->rm($path); + } + } } diff --git a/view/adminhtml/templates/system/config/button/credentials.phtml b/view/adminhtml/templates/system/config/button/credentials.phtml index 0dac913..a9b7a49 100644 --- a/view/adminhtml/templates/system/config/button/credentials.phtml +++ b/view/adminhtml/templates/system/config/button/credentials.phtml @@ -16,6 +16,29 @@ use TrueLayer\Connect\Block\Adminhtml\System\Config\Button\Credentials; 'jquery', 'prototype' ], function (jQuery) { + let private_key_sandbox = '', + private_key_production = '', + truelayer_mode = 'sandbox'; + + document.querySelector('#truelayer_general').addEventListener('change', (e) => { + // Check mode + if (e.target.getAttribute('name').includes('[mode]')) { + truelayer_mode = e.target.value; + } + + if (e.target.getAttribute('type') === 'file') { + const FR = new FileReader(); + + FR.onload = () => { + truelayer_mode === 'sandbox' + ? private_key_sandbox = FR.result + : private_key_production = FR.result; + } + + FR.readAsText(e.target.files[0], "UTF-8"); + } + }); + var resultSpan = jQuery('#result_api'); jQuery('#truelayer-button_credentials').click(function () { var params = { @@ -32,8 +55,10 @@ use TrueLayer\Connect\Block\Adminhtml\System\Config\Button\Credentials; "sandbox_key_id": jQuery("input[name='groups[general][fields][sandbox_key_id][value]']").val(), "mode": - jQuery("select[name='groups[general][fields][mode][value]']").val() + jQuery("select[name='groups[general][fields][mode][value]']").val(), + "private_key": truelayer_mode === 'sandbox' ? private_key_sandbox : private_key_production, }; + new Ajax.Request('escapeUrl($block->getApiCheckUrl()) ?>', { parameters: params, loaderArea: false, From fba98e868ed1f86974f964a7817ef0204d12635a Mon Sep 17 00:00:00 2001 From: Marvin Besselsen Date: Thu, 3 Aug 2023 15:10:49 +0200 Subject: [PATCH 3/5] Refactored pending checkout flow --- Controller/Checkout/Process.php | 2 +- .../frontend/templates/checkout/pending.phtml | 34 ++-- view/frontend/web/css/pending.css | 172 ++++++++++++++++-- view/frontend/web/js/pending.js | 54 +++--- 4 files changed, 202 insertions(+), 60 deletions(-) diff --git a/Controller/Checkout/Process.php b/Controller/Checkout/Process.php index 120fa8d..ee6b9d7 100644 --- a/Controller/Checkout/Process.php +++ b/Controller/Checkout/Process.php @@ -62,7 +62,7 @@ public function execute(): Redirect $result = $this->processReturn->execute((string)$transactionId); if ($result['success']) { $resultRedirect->setPath('checkout/onepage/success'); - } elseif ($result['status'] == 'settled' || $result['status'] == 'executed') { + } elseif (in_array($result['status'], ['settled', 'executed', 'authorized'])) { $resultRedirect->setPath('truelayer/checkout/pending', ['payment_id' => $transactionId]); } else { $this->messageManager->addErrorMessage('Something went wrong'); diff --git a/view/frontend/templates/checkout/pending.phtml b/view/frontend/templates/checkout/pending.phtml index 4803fb1..fc46efc 100644 --- a/view/frontend/templates/checkout/pending.phtml +++ b/view/frontend/templates/checkout/pending.phtml @@ -10,23 +10,31 @@ $viewModel = $block->getData('view_model'); ?>
-

+

+ escapeHtml(__('Your payment has been accepted by your bank.')) ?> +
escapeHtml(__( - 'Your payment has been accepted by TrueLayer. We are redirecting you to the order confirmation page ' . - 'but it might take an extra second.' + 'We are redirecting you to the order confirmation page but it might take an extra second.' )) ?> -

-
-
-

escapeHtml(__('Refreshing...')); ?>

-
+

+
+
+
+
-