diff --git a/Helper/DataPatch.php b/Helper/DataPatch.php new file mode 100644 index 000000000..1e9f1f10d --- /dev/null +++ b/Helper/DataPatch.php @@ -0,0 +1,45 @@ + + */ + +namespace Adyen\Payment\Helper; + +use Magento\Framework\Setup\ModuleDataSetupInterface; + +class DataPatch +{ + const CONFIG_TABLE = 'core_config_data'; + + public function findConfig( + ModuleDataSetupInterface $setup, + string $path, + ?string $value + ): ?array { + $config = null; + + $configDataTable = $setup->getTable(self::CONFIG_TABLE); + $connection = $setup->getConnection(); + + $select = $connection->select()->from($configDataTable)->where('path = ?', $path); + $matchingConfigs = $connection->fetchAll($select); + + if (!empty($matchingConfigs) && is_null($value)) { + $config = reset($matchingConfigs); + } else { + foreach ($matchingConfigs as $matchingConfig) { + if ($matchingConfig['value'] === $value) { + $config = $matchingConfig; + } + } + } + + return $config; + } +} diff --git a/Helper/Webhook.php b/Helper/Webhook.php index 9a679e674..cf357f9c1 100644 --- a/Helper/Webhook.php +++ b/Helper/Webhook.php @@ -45,7 +45,7 @@ class Webhook * Indicative matrix for possible states to enter after given event */ const STATE_TRANSITION_MATRIX = [ - 'payment_pre_authorized' => [Order::STATE_NEW, Order::STATE_PROCESSING], + 'payment_pre_authorized' => [Order::STATE_NEW], 'payment_authorized' => [Order::STATE_PROCESSING] ]; diff --git a/Helper/Webhook/AuthorisationWebhookHandler.php b/Helper/Webhook/AuthorisationWebhookHandler.php index f4a3455a5..67b4e193f 100644 --- a/Helper/Webhook/AuthorisationWebhookHandler.php +++ b/Helper/Webhook/AuthorisationWebhookHandler.php @@ -123,8 +123,6 @@ private function handleSuccessfulAuthorisation(Order $order, Notification $notif $additionalData = !empty($notification->getAdditionalData()) ? $this->serializer->unserialize($notification->getAdditionalData()) : []; $requireFraudManualReview = $this->caseManagementHelper->requiresManualReview($additionalData); - $this->invoiceHelper->createInvoice($order, $notification, $isAutoCapture); - if ($isAutoCapture) { $order = $this->handleAutoCapture($order, $notification, $requireFraudManualReview); } else { @@ -219,6 +217,7 @@ private function handleFailedAuthorisation(Order $order, Notification $notificat */ private function handleAutoCapture(Order $order, Notification $notification, bool $requireFraudManualReview): Order { + $this->invoiceHelper->createInvoice($order, $notification, true); if ($requireFraudManualReview) { $order = $this->caseManagementHelper->markCaseAsPendingReview($order, $notification->getPspreference(), true); } else { diff --git a/Model/Config/Source/PreAuthorized.php b/Model/Config/Source/PreAuthorized.php index 35f1834dc..cc0596cc7 100644 --- a/Model/Config/Source/PreAuthorized.php +++ b/Model/Config/Source/PreAuthorized.php @@ -11,6 +11,8 @@ namespace Adyen\Payment\Model\Config\Source; +use Magento\Sales\Model\Order; + /** * Order Statuses source model */ @@ -20,7 +22,6 @@ class PreAuthorized extends \Magento\Sales\Model\Config\Source\Order\Status * @var string[] */ protected $_stateStatuses = [ - \Magento\Sales\Model\Order::STATE_PROCESSING, - \Magento\Sales\Model\Order::STATE_NEW + Order::STATE_NEW ]; } diff --git a/Observer/InvoiceObserver.php b/Observer/InvoiceObserver.php index 33d3fe6d4..a53ffd870 100644 --- a/Observer/InvoiceObserver.php +++ b/Observer/InvoiceObserver.php @@ -102,7 +102,6 @@ public function execute(Observer $observer) return; } - $this->logger->addAdyenDebug( 'Event sales_order_invoice_save_after for invoice {invoiceId} will be handled', array_merge($this->logger->getInvoiceContext($invoice), $this->logger->getOrderContext($order)) @@ -119,18 +118,8 @@ public function execute(Observer $observer) $this->adyenOrderPaymentHelper->updatePaymentTotalCaptured($adyenOrderPaymentObject, $linkedAmount); } - $status = $this->configHelper->getConfigData( - 'payment_pre_authorized', - Config::XML_ADYEN_ABSTRACT_PREFIX, - $order->getStoreId() - ); - - if (empty($status)) { - $status = $this->statusResolver->getOrderStatusByState($order, Order::STATE_PENDING_PAYMENT); - } - - // Set order to PROCESSING to allow further invoices to be generated - $order->setState(Order::STATE_PENDING_PAYMENT); + $status = $this->statusResolver->getOrderStatusByState($order, Order::STATE_PAYMENT_REVIEW); + $order->setState(Order::STATE_PAYMENT_REVIEW); $order->setStatus($status); $this->logger->addAdyenDebug( diff --git a/Setup/Patch/Data/CreateStatusAuthorized.php b/Setup/Patch/Data/CreateStatusAuthorized.php new file mode 100644 index 000000000..b56a16d58 --- /dev/null +++ b/Setup/Patch/Data/CreateStatusAuthorized.php @@ -0,0 +1,107 @@ + + */ +declare(strict_types=1); + +namespace Adyen\Payment\Setup\Patch\Data; + +use Adyen\Payment\Helper\DataPatch; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\Sales\Model\Order; + +class CreateStatusAuthorized implements DataPatchInterface, PatchVersionInterface +{ + private ModuleDataSetupInterface $moduleDataSetup; + private WriterInterface $configWriter; + private ReinitableConfigInterface $reinitableConfig; + private DataPatch $dataPatchHelper; + + const ADYEN_AUTHORIZED_STATUS = 'adyen_authorized'; + const ADYEN_AUTHORIZED_STATUS_LABEL = 'Authorized'; + + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + WriterInterface $configWriter, + ReinitableConfigInterface $reinitableConfig, + DataPatch $dataPatchHelper + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->configWriter = $configWriter; + $this->reinitableConfig = $reinitableConfig; + $this->dataPatchHelper = $dataPatchHelper; + } + + public function apply() + { + $setup = $this->moduleDataSetup; + + $orderStateAssignmentTable = $this->moduleDataSetup->getTable('sales_order_status_state'); + $orderStatusTable = $this->moduleDataSetup->getTable('sales_order_status'); + + $status = [ + [ + 'status' => self::ADYEN_AUTHORIZED_STATUS, + 'label' => self::ADYEN_AUTHORIZED_STATUS_LABEL + ] + ]; + + $this->moduleDataSetup->getConnection()->insertOnDuplicate($orderStatusTable, $status); + + $stateAssignment = [ + [ + 'status' => self::ADYEN_AUTHORIZED_STATUS, + 'state' => Order::STATE_NEW, + 'is_default' => 0, + 'visible_on_front' => 1 + ] + ]; + + $this->moduleDataSetup->getConnection()->insertOnDuplicate($orderStateAssignmentTable, $stateAssignment); + + $path = 'payment/adyen_abstract/payment_pre_authorized'; + + $config = $this->dataPatchHelper->findConfig($setup, $path, Order::STATE_PROCESSING); + if (isset($config)) { + $this->configWriter->save( + $path, + self::ADYEN_AUTHORIZED_STATUS, + $config['scope'], + $config['scope_id'] + ); + } + + // re-initialize otherwise it will cause errors + $this->reinitableConfig->reinit(); + } + + /** + * @inheritdoc + */ + public function getAliases(): array + { + return []; + } + + /** + * @inheritdoc + */ + public static function getDependencies(): array + { + return []; + } + + public static function getVersion(): string + { + return '9.0.3'; + } +} diff --git a/Setup/Patch/Data/PreAuthorizedSettingsMigration.php b/Setup/Patch/Data/PreAuthorizedSettingsMigration.php deleted file mode 100644 index 7d332c52b..000000000 --- a/Setup/Patch/Data/PreAuthorizedSettingsMigration.php +++ /dev/null @@ -1,119 +0,0 @@ - - */ -declare(strict_types=1); - -namespace Adyen\Payment\Setup\Patch\Data; - -use Magento\Framework\App\Config\ReinitableConfigInterface; -use Magento\Framework\App\Config\Storage\WriterInterface; -use Magento\Framework\Setup\ModuleDataSetupInterface; -use Magento\Framework\Setup\Patch\DataPatchInterface; -use Magento\Framework\Setup\Patch\PatchVersionInterface; - -class PreAuthorizedSettingsMigration implements DataPatchInterface, PatchVersionInterface -{ - private ModuleDataSetupInterface $moduleDataSetup; - private WriterInterface $configWriter; - private ReinitableConfigInterface $reinitableConfig; - - public function __construct( - ModuleDataSetupInterface $moduleDataSetup, - WriterInterface $configWriter, - ReinitableConfigInterface $reinitableConfig - ) { - $this->moduleDataSetup = $moduleDataSetup; - $this->configWriter = $configWriter; - $this->reinitableConfig = $reinitableConfig; - } - - /** - * Do Upgrade - * - * @return void - */ - public function apply(): void - { - $this->moduleDataSetup->getConnection()->startSetup(); - $this->updateSchemaVersion($this->moduleDataSetup); - $this->moduleDataSetup->getConnection()->endSetup(); - } - - /** - * Ensure that new path does not exist before updating - * - * @param ModuleDataSetupInterface $setup - */ - public function updateSchemaVersion(ModuleDataSetupInterface $setup): void - { - $path = 'payment/adyen_abstract/payment_pre_authorized'; - - $config = $this->findConfig($setup, $path, \Magento\Sales\Model\Order::STATE_PENDING_PAYMENT); - if (isset($config)) { - $this->configWriter->save( - $path, - \Magento\Sales\Model\Order::STATE_PROCESSING, - $config['scope'], - $config['scope_id'] - ); - } - // re-initialize otherwise it will cause errors - $this->reinitableConfig->reinit(); - } - - /** - * Return the config based on the passed path and value. If value is null, return the first item in array - * - * @param ModuleDataSetupInterface $setup - * @param string $path - * @param string|null $value - * @return array|null - */ - private function findConfig(ModuleDataSetupInterface $setup, string $path, ?string $value): ?array - { - $config = null; - $configDataTable = $setup->getTable('core_config_data'); - $connection = $setup->getConnection(); - $select = $connection->select()->from($configDataTable)->where('path = ?', $path); - $matchingConfigs = $connection->fetchAll($select); - - if (!empty($matchingConfigs) && is_null($value)) { - $config = reset($matchingConfigs); - } else { - foreach ($matchingConfigs as $matchingConfig) { - if ($matchingConfig['value'] === $value) { - $config = $matchingConfig; - } - } - } - - return $config; - } - - /** - * @inheritdoc - */ - public function getAliases(): array - { - return []; - } - - /** - * @inheritdoc - */ - public static function getDependencies(): array - { - return []; - } - - public static function getVersion(): string - { - return '9.0.0'; - } -} diff --git a/Test/Unit/Helper/DataPatchTest.php b/Test/Unit/Helper/DataPatchTest.php new file mode 100644 index 000000000..38be2f86b --- /dev/null +++ b/Test/Unit/Helper/DataPatchTest.php @@ -0,0 +1,53 @@ + + */ + +namespace Adyen\Payment\Test\Unit\Helper; + +use Adyen\Payment\Helper\DataPatch; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Framework\DB\Select; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; + +class DataPatchTest extends AbstractAdyenTestCase +{ + const TEST_CONFIG_PATH = 'payment/adyen_mock_payment_method/sort_order'; + const CONFIG_VALUE = '10'; + + public function testFindConfig() + { + $connectionMock = $this->createConfiguredMock(AdapterInterface::class, [ + 'fetchAll' => [ + [ + 'config_id' => 1, + 'scope' => 'default', + 'scope_id' => 0, + 'path' => self::TEST_CONFIG_PATH, + 'value' => self::CONFIG_VALUE, + 'updated_at' => '2023-10-11 11:05:11' + ] + ], + 'select' => $this->createConfiguredMock(Select::class, [ + 'from' => $this->createMock(Select::class) + ]) + ]); + + $setupMock = $this->createConfiguredMock(ModuleDataSetupInterface::class, [ + 'getConnection' => $connectionMock + ]); + + $dataPatchHelper = new DataPatch(); + + $config = $dataPatchHelper->findConfig($setupMock, self::TEST_CONFIG_PATH, self::CONFIG_VALUE); + + $this->assertEquals(self::CONFIG_VALUE, $config['value']); + } +} diff --git a/Test/Unit/Helper/Webhook/AuthorisationWebhookHandlerTest.php b/Test/Unit/Helper/Webhook/AuthorisationWebhookHandlerTest.php index eb8cc4d72..7fa6307f7 100644 --- a/Test/Unit/Helper/Webhook/AuthorisationWebhookHandlerTest.php +++ b/Test/Unit/Helper/Webhook/AuthorisationWebhookHandlerTest.php @@ -150,26 +150,15 @@ public function testHandleSuccessfulAuthorisation($isAutoCapture): void ->method('updatePaymentDetails'); $this->orderHelperMock->expects($this->once()) ->method('sendOrderMail'); - - if ($isAutoCapture){ - $this->orderHelperMock->expects($this->once()) - ->method('finalizeOrder')->willReturn($this->orderMock); - } else { - $this->orderHelperMock->expects($this->once()) - ->method('addWebhookStatusHistoryComment')->willReturn($this->orderMock); - } - + $this->orderHelperMock->expects($this->once()) + ->method('finalizeOrder')->willReturn($this->orderMock); $paymentMethodsMock = $this->createConfiguredMock( PaymentMethods::class, [ - 'isAutoCapture' => $isAutoCapture + 'isAutoCapture' => true ] ); - $this->invoiceHelperMock->expects($this->once()) - ->method('createInvoice') - ->with($this->orderMock, $this->notificationMock, $isAutoCapture); - $authorisationWebhookHandler = $this->createAuthorisationWebhookHandler( $this->adyenOrderPaymentMock, @@ -179,7 +168,7 @@ public function testHandleSuccessfulAuthorisation($isAutoCapture): void null, $mockChargedCurrency, null, - $this->invoiceHelperMock, + null, $paymentMethodsMock ); diff --git a/Test/Unit/Setup/Patch/Data/CreateStatusAuthorizedTest.php b/Test/Unit/Setup/Patch/Data/CreateStatusAuthorizedTest.php new file mode 100644 index 000000000..246751450 --- /dev/null +++ b/Test/Unit/Setup/Patch/Data/CreateStatusAuthorizedTest.php @@ -0,0 +1,77 @@ + + */ + +namespace Adyen\Payment\Test\Unit\Setup\Patch\Data; + +use Adyen\Payment\Helper\DataPatch; +use Adyen\Payment\Setup\Patch\Data\CreateStatusAuthorized; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Setup\ModuleDataSetupInterface; + +class CreateStatusAuthorizedTest extends AbstractAdyenTestCase +{ + public function testApply() + { + $createStatusAuthorized = $this->getCreateStatusAuthorized(); + $createStatusAuthorized->apply(); + + $this->assertTrue(true); + } + + public function testGetAliases() + { + $createStatusAuthorized = $this->getCreateStatusAuthorized(); + $aliases = $createStatusAuthorized->getAliases(); + + $this->assertSame([], $aliases); + } + + public function testGetDependencies() + { + $createStatusAuthorized = $this->getCreateStatusAuthorized(); + $dependencies = $createStatusAuthorized::getDependencies(); + + $this->assertSame([], $dependencies); + } + + public function getVersion() + { + $createStatusAuthorized = $this->getCreateStatusAuthorized(); + $version = $createStatusAuthorized::getVersion(); + + $this->assertNotEmpty($version); + } + + /** + * @return CreateStatusAuthorized + */ + public function getCreateStatusAuthorized(): CreateStatusAuthorized + { + $moduleDataSetupMock = $this->createConfiguredMock(ModuleDataSetupInterface::class, [ + 'getConnection' => $this->createMock(AdapterInterface::class) + ]); + $configWriteMock = $this->createMock(WriterInterface::class); + $reinitableConfigMock = $this->createMock(ReinitableConfigInterface::class); + $dataPatchHelperMock = $this->createConfiguredMock(DataPatch::class, [ + 'findConfig' => null + ]); + + return new CreateStatusAuthorized( + $moduleDataSetupMock, + $configWriteMock, + $reinitableConfigMock, + $dataPatchHelperMock + ); + } +} diff --git a/etc/config.xml b/etc/config.xml index 3eff2f45e..d3ab4830a 100755 --- a/etc/config.xml +++ b/etc/config.xml @@ -29,7 +29,7 @@ 0 cron support@adyen.com - processing + adyen_authorized processing canceled manual @@ -1906,4 +1906,4 @@ - \ No newline at end of file +