From 54525613ab38101b6f298b4e68b539cfd2a369a4 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Fri, 29 Mar 2024 13:31:12 +0100 Subject: [PATCH 1/8] [ECP-8992] Add Apple Pay billing address requirement (#2572) Co-authored-by: Can Demiralp --- view/frontend/layout/checkout_index_index.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/view/frontend/layout/checkout_index_index.xml b/view/frontend/layout/checkout_index_index.xml index 6489c4955..9c97006f6 100755 --- a/view/frontend/layout/checkout_index_index.xml +++ b/view/frontend/layout/checkout_index_index.xml @@ -299,6 +299,9 @@ true + + true + @@ -316,4 +319,4 @@ - \ No newline at end of file + From 1c082af4dcabeb985188bb0cb33b34d9580a88c6 Mon Sep 17 00:00:00 2001 From: Arinc Elhan Date: Wed, 3 Apr 2024 10:35:12 +0200 Subject: [PATCH 2/8] Update SECURITY.md (#2576) --- SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index 60fbd7c5a..6e2cde6ee 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,4 +1,4 @@ # Reporting Security Issues We welcome reports of possible vulnerabilities or issues as part of our responsible disclosure program. For more information go to -https://support.adyen.com/hc/en-us/articles/115001187330-How-do-I-report-a-possible-security-issue-to-Adyen- +[this page](https://www.adyen.com/policies-and-disclaimer/responsible-disclosure). From f4278d6ed9f9d4dd54d26ef56f586559630085a3 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Wed, 3 Apr 2024 14:55:29 +0200 Subject: [PATCH 3/8] Stop running Functional Test workflow against develop branch --- .github/workflows/mftf-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mftf-test.yml b/.github/workflows/mftf-test.yml index a413c452b..d31b61ebd 100644 --- a/.github/workflows/mftf-test.yml +++ b/.github/workflows/mftf-test.yml @@ -2,7 +2,7 @@ name: Functional Tests on: workflow_dispatch: pull_request: - branches: [main, develop] + branches: [main] jobs: build: From b43d73982704a83be2d1c91c41a1c187240f0383 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Wed, 3 Apr 2024 15:04:28 +0200 Subject: [PATCH 4/8] [ECP-8984] Remove version from data patches and setup_version from module.xml (#2567) * [ECP-8984] Remove setup_version definition * [ECP-8984] Remove unused import * [ECP-8984] Remove version constraints from data patches --------- Co-authored-by: Can Demiralp --- Setup/Patch/Data/CreateStatusAuthorized.php | 8 +------- Setup/Patch/Data/CreditCardsBecomeCards.php | 10 +--------- Setup/Patch/Data/RatepayIdConfiguration.php | 8 +------- .../Data/RemoveMinMaxOrderConfigMigration.php | 15 +-------------- Setup/Patch/Data/VaultMigration.php | 1 - etc/module.xml | 2 +- 6 files changed, 5 insertions(+), 39 deletions(-) diff --git a/Setup/Patch/Data/CreateStatusAuthorized.php b/Setup/Patch/Data/CreateStatusAuthorized.php index 9e8fef755..a30495f71 100644 --- a/Setup/Patch/Data/CreateStatusAuthorized.php +++ b/Setup/Patch/Data/CreateStatusAuthorized.php @@ -15,7 +15,6 @@ use Magento\Framework\Exception\AlreadyExistsException; 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; @@ -23,7 +22,7 @@ use Magento\Sales\Model\ResourceModel\Order\StatusFactory as StatusResourceFactory; use Magento\Sales\Model\ResourceModel\Order\Status as StatusResource; -class CreateStatusAuthorized implements DataPatchInterface, PatchVersionInterface +class CreateStatusAuthorized implements DataPatchInterface { private ModuleDataSetupInterface $moduleDataSetup; private WriterInterface $configWriter; @@ -103,9 +102,4 @@ public static function getDependencies(): array { return []; } - - public static function getVersion(): string - { - return '9.0.3'; - } } diff --git a/Setup/Patch/Data/CreditCardsBecomeCards.php b/Setup/Patch/Data/CreditCardsBecomeCards.php index 6831b7a52..413c8c2a1 100644 --- a/Setup/Patch/Data/CreditCardsBecomeCards.php +++ b/Setup/Patch/Data/CreditCardsBecomeCards.php @@ -6,7 +6,7 @@ use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Framework\Setup\Patch\PatchVersionInterface; -class CreditCardsBecomeCards implements DataPatchInterface, PatchVersionInterface +class CreditCardsBecomeCards implements DataPatchInterface { private ModuleDataSetupInterface $moduleDataSetup; @@ -65,12 +65,4 @@ public static function getDependencies() { return []; } - - /** - * @inheritDoc - */ - public static function getVersion() - { - return '9.0.5'; - } } diff --git a/Setup/Patch/Data/RatepayIdConfiguration.php b/Setup/Patch/Data/RatepayIdConfiguration.php index ef86431df..5bc7e99b6 100644 --- a/Setup/Patch/Data/RatepayIdConfiguration.php +++ b/Setup/Patch/Data/RatepayIdConfiguration.php @@ -15,9 +15,8 @@ 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 RatepayIdConfiguration implements DataPatchInterface, PatchVersionInterface +class RatepayIdConfiguration implements DataPatchInterface { private ModuleDataSetupInterface $moduleDataSetup; private WriterInterface $configWriter; @@ -96,9 +95,4 @@ public static function getDependencies(): array { return []; } - - public static function getVersion(): string - { - return '9.0.0'; - } } diff --git a/Setup/Patch/Data/RemoveMinMaxOrderConfigMigration.php b/Setup/Patch/Data/RemoveMinMaxOrderConfigMigration.php index b35ed1d60..fdce2052f 100644 --- a/Setup/Patch/Data/RemoveMinMaxOrderConfigMigration.php +++ b/Setup/Patch/Data/RemoveMinMaxOrderConfigMigration.php @@ -2,23 +2,18 @@ namespace Adyen\Payment\Setup\Patch\Data; -use Magento\Framework\App\Config\ReinitableConfigInterface; use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; -use Magento\Framework\Setup\Patch\PatchVersionInterface; -class RemoveMinMaxOrderConfigMigration implements DataPatchInterface, PatchVersionInterface +class RemoveMinMaxOrderConfigMigration implements DataPatchInterface { private ModuleDataSetupInterface $moduleDataSetup; - private ReinitableConfigInterface $reinitableConfig; public function __construct( ModuleDataSetupInterface $moduleDataSetup, - ReinitableConfigInterface $reinitableConfig ) { $this->moduleDataSetup = $moduleDataSetup; - $this->reinitableConfig = $reinitableConfig; } /** @@ -67,12 +62,4 @@ public static function getDependencies() { return []; } - - /** - * @inheritDoc - */ - public static function getVersion() - { - return '9.0.0'; - } } diff --git a/Setup/Patch/Data/VaultMigration.php b/Setup/Patch/Data/VaultMigration.php index 7d8769f36..baa668808 100644 --- a/Setup/Patch/Data/VaultMigration.php +++ b/Setup/Patch/Data/VaultMigration.php @@ -19,7 +19,6 @@ use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Vault\Api\Data\PaymentTokenFactoryInterface; -use Magento\Vault\Api\Data\PaymentTokenInterface; use Magento\Vault\Api\PaymentTokenManagementInterface; use Magento\Vault\Api\PaymentTokenRepositoryInterface; diff --git a/etc/module.xml b/etc/module.xml index b345122da..2b504dd98 100755 --- a/etc/module.xml +++ b/etc/module.xml @@ -12,7 +12,7 @@ --> - + From 03721085d5f0c3f9470e57f61b020a7db34efff4 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Thu, 4 Apr 2024 12:58:12 +0200 Subject: [PATCH 5/8] [ECP-9077] Use PaymentMethodUtil class instead of deprecated ManualCapture class (#2564) * [ECP-9077] Use PaymentMethodUtil class instead of deprecated ManualCapture class * [ECP-9077] Fix unit tests --------- Co-authored-by: Can Demiralp --- Helper/PaymentMethods.php | 9 +++------ Helper/Util/PaymentMethodUtil.php | 4 ++++ Test/Unit/Helper/PaymentMethodsTest.php | 24 ++++++++---------------- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/Helper/PaymentMethods.php b/Helper/PaymentMethods.php index aab4e4df0..2f6b7c2c9 100644 --- a/Helper/PaymentMethods.php +++ b/Helper/PaymentMethods.php @@ -14,13 +14,12 @@ use Adyen\AdyenException; use Adyen\Client; use Adyen\ConnectionException; +use Adyen\Payment\Helper\Util\PaymentMethodUtil; use Adyen\Payment\Logger\AdyenLogger; use Adyen\Payment\Model\Notification; use Adyen\Payment\Model\Ui\Adminhtml\AdyenMotoConfigProvider; use Adyen\Payment\Model\Ui\AdyenPayByLinkConfigProvider; use Adyen\Payment\Model\Ui\AdyenPosCloudConfigProvider; -use Adyen\Util\ManualCapture; -use Exception; use Magento\Framework\App\Area; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Helper\AbstractHelper; @@ -42,6 +41,7 @@ use Magento\Store\Model\Store; use Magento\Vault\Api\PaymentTokenRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; + class PaymentMethods extends AbstractHelper { const ADYEN_HPP = 'adyen_hpp'; @@ -85,7 +85,6 @@ class PaymentMethods extends AbstractHelper protected \Magento\Quote\Model\Quote $quote; private ChargedCurrency $chargedCurrency; private Config $configHelper; - private ManualCapture $manualCapture; private SerializerInterface $serializer; private PaymentTokenRepositoryInterface $paymentTokenRepository; private SearchCriteriaBuilder $searchCriteriaBuilder; @@ -105,7 +104,6 @@ public function __construct( ChargedCurrency $chargedCurrency, Config $configHelper, MagentoDataHelper $dataHelper, - ManualCapture $manualCapture, SerializerInterface $serializer, AdyenDataHelper $adyenDataHelper, PaymentTokenRepositoryInterface $paymentTokenRepository, @@ -125,7 +123,6 @@ public function __construct( $this->chargedCurrency = $chargedCurrency; $this->configHelper = $configHelper; $this->dataHelper = $dataHelper; - $this->manualCapture = $manualCapture; $this->serializer = $serializer; $this->adyenDataHelper = $adyenDataHelper; $this->paymentTokenRepository = $paymentTokenRepository; @@ -519,7 +516,7 @@ public function getCcAvailableTypesByAlt(): array public function isAutoCapture(Order $order, string $notificationPaymentMethod): bool { // validate if payment methods allows manual capture - if ($this->manualCapture->isManualCaptureSupported($notificationPaymentMethod)) { + if (PaymentMethodUtil::isManualCaptureSupported($notificationPaymentMethod)) { $captureMode = trim( (string) $this->configHelper->getConfigData( 'capture_mode', diff --git a/Helper/Util/PaymentMethodUtil.php b/Helper/Util/PaymentMethodUtil.php index 504b8dc69..f8d182889 100644 --- a/Helper/Util/PaymentMethodUtil.php +++ b/Helper/Util/PaymentMethodUtil.php @@ -30,6 +30,7 @@ class PaymentMethodUtil 'laser', 'paypal', 'sepadirectdebit', + 'ach', 'dankort', 'elo', 'hipercard', @@ -42,6 +43,9 @@ class PaymentMethodUtil 'paywithgoogle', 'mc_googlepay', 'visa_googlepay', + 'amex_googlepay', + 'discover_googlepay', + 'maestro_googlepay', 'svs', 'givex', 'valuelink', diff --git a/Test/Unit/Helper/PaymentMethodsTest.php b/Test/Unit/Helper/PaymentMethodsTest.php index 6794023ae..4206fe0ed 100644 --- a/Test/Unit/Helper/PaymentMethodsTest.php +++ b/Test/Unit/Helper/PaymentMethodsTest.php @@ -23,7 +23,6 @@ use Adyen\Payment\Model\Notification; use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; use Adyen\Service\Checkout; -use Adyen\Util\ManualCapture; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Helper\Context; use Magento\Framework\App\RequestInterface; @@ -67,7 +66,6 @@ class PaymentMethodsTest extends AbstractAdyenTestCase private ChargedCurrency $chargedCurrencyMock; private Config $configHelperMock; private MagentoDataHelper $dataHelperMock; - private ManualCapture $manualCaptureMock; private SerializerInterface $serializerMock; private AdyenDataHelper $adyenDataHelperMock; private PaymentTokenRepositoryInterface $paymentTokenRepository; @@ -94,7 +92,6 @@ protected function setUp(): void $this->chargedCurrencyMock = $this->createMock(ChargedCurrency::class); $this->configHelperMock = $this->createMock(Config::class); $this->dataHelperMock = $this->createMock(MagentoDataHelper::class); - $this->manualCaptureMock = $this->createMock(ManualCapture::class); $this->serializerMock = $this->createMock(SerializerInterface::class); $this->adyenDataHelperMock = $this->createMock(AdyenDataHelper::class); $this->paymentTokenRepository = $this->createMock(PaymentTokenRepositoryInterface::class); @@ -133,7 +130,6 @@ protected function setUp(): void $this->chargedCurrencyMock, $this->configHelperMock, $this->dataHelperMock, - $this->manualCaptureMock, $this->serializerMock, $this->adyenDataHelperMock, $this->paymentTokenRepository, @@ -998,6 +994,11 @@ public function testIsAutoCapture( $manualCapturePayPal, $expectedResult ) { + // Reset Config mock to prevent interventions with other expects() assertions. + $this->configHelperMock = $this->createMock(Config::class); + + $this->orderMock->method('getStoreId')->willReturn(1); + $this->configHelperMock->expects($this->any()) ->method('getConfigData') ->with('capture_mode', 'adyen_abstract', '1') @@ -1023,7 +1024,7 @@ public function testIsAutoCapture( ->willReturn($paymentCode); // Configure the order mock to return the payment mock - $this->orderMock->expects($this->once()) + $this->orderMock->expects($this->any()) ->method('getPayment') ->willReturn($this->orderPaymentMock); @@ -1031,11 +1032,7 @@ public function testIsAutoCapture( ->method('getAutoCaptureOpenInvoice') ->willReturn($autoCaptureOpenInvoice); - $paymentMethodsHelper = $this->objectManager->getObject(PaymentMethods::class, [ - 'configHelper' => $this->configHelperMock - ]); - - $result = $paymentMethodsHelper->isAutoCapture($this->orderMock, $paymentCode); + $result = $this->paymentMethodsHelper->isAutoCapture($this->orderMock, $paymentCode); $this->assertEquals($expectedResult, $result); } @@ -1221,10 +1218,6 @@ public function testManualCaptureAllowed(): void $this->orderMock->method('getStoreId')->willReturn($storeId); $this->orderPaymentMock->method('getMethod')->willReturn('sepadirectdebit'); $this->orderMock->method('getPayment')->willReturn($this->orderPaymentMock); - $manualCaptureMock = $this->getMockBuilder(ManualCapture::class) - ->disableOriginalConstructor() - ->getMock(); - $manualCaptureMock->expects($this->any())->method('isManualCaptureSupported')->willReturn(true); $notificationPaymentMethod = 'sepadirectdebit'; // Provide your notification payment method @@ -1251,8 +1244,7 @@ public function testManualCaptureAllowed(): void 'configHelper' => $this->configHelperMock, 'chargedCurrency' => $this->chargedCurrencyMock, 'adyenHelper' => $this->adyenHelperMock, - 'adyenLogger' => $this->adyenLoggerMock, - 'manualCapture' => $manualCaptureMock + 'adyenLogger' => $this->adyenLoggerMock ] ); From 9d964bfa6555c33cd936cdf2e15e9c9561ed1514 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Fri, 5 Apr 2024 13:05:33 +0200 Subject: [PATCH 6/8] [ECP-9113] Skip child items while building the line items array for credit memo (#2577) * [ECP-9113] Skip child items while building the line items array for credit memo * [ECP-9113] Prevent possible zero division * [ECP-9113] Update unit tests --------- Co-authored-by: Can Demiralp --- Helper/OpenInvoice.php | 4 +- Model/AdyenAmountCurrency.php | 6 ++- Test/Unit/Model/AdyenAmountCurrencyTest.php | 47 +++++++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 Test/Unit/Model/AdyenAmountCurrencyTest.php diff --git a/Helper/OpenInvoice.php b/Helper/OpenInvoice.php index 529b49746..fad9152c1 100644 --- a/Helper/OpenInvoice.php +++ b/Helper/OpenInvoice.php @@ -97,7 +97,9 @@ public function getOpenInvoiceDataForCreditMemo(Order\Creditmemo $creditMemo) $formFields = ['lineItems' => []]; foreach ($creditMemo->getItems() as $creditmemoItem) { - if ($creditmemoItem->getQty() <= 0) { + // Child items only identifies the variant data and doesn't contain line item information. + $isChildItem = $creditmemoItem->getOrderItem()->getParentItem() !== null; + if ($creditmemoItem->getQty() <= 0 || $isChildItem) { continue; } diff --git a/Model/AdyenAmountCurrency.php b/Model/AdyenAmountCurrency.php index 5322bfb36..c8040e4d2 100644 --- a/Model/AdyenAmountCurrency.php +++ b/Model/AdyenAmountCurrency.php @@ -97,6 +97,10 @@ public function getAmountWithDiscount() public function getCalculatedTaxPercentage() { - return $this->getTaxAmount() / ($this->getAmountWithDiscount()) * 100; + if ($this->getAmountWithDiscount() > 0) { + return ($this->getTaxAmount() / $this->getAmountWithDiscount()) * 100; + } else { + return 0; + } } } diff --git a/Test/Unit/Model/AdyenAmountCurrencyTest.php b/Test/Unit/Model/AdyenAmountCurrencyTest.php new file mode 100644 index 000000000..e7cff8e9b --- /dev/null +++ b/Test/Unit/Model/AdyenAmountCurrencyTest.php @@ -0,0 +1,47 @@ + + */ + +namespace Adyen\Payment\Test\Helper\Unit\Model; + +use Adyen\Payment\Model\AdyenAmountCurrency; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; + +class AdyenAmountCurrencyTest extends AbstractAdyenTestCase +{ + /** + * @dataProvider getCalculatedTaxPercentageProvider + * @return void + */ + public function testGetCalculatedTaxPercentage($amount, $currencyCode, $taxPercentage, $expectedValue) + { + $adyenAmountCurrency = new AdyenAmountCurrency($amount, $currencyCode, 0, $taxPercentage); + + $this->assertEquals($adyenAmountCurrency->getCalculatedTaxPercentage(), $expectedValue); + } + + public static function getCalculatedTaxPercentageProvider() + { + return [ + [ + 'amount' => 100.0, + 'currencyCode' => 'EUR', + 'taxAmount' => 21, + 'expectedValue' => 21 + ], + [ + 'amount' => 0, + 'currencyCode' => 'EUR', + 'taxAmount' => 21, + 'expectedValue' => 0 + ] + ]; + } +} From 7219753cd2865b1090fffaff8ca6751dba7a857a Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Fri, 5 Apr 2024 14:39:41 +0200 Subject: [PATCH 7/8] [ECP-8885] Fix invoice creation of partial shipment for capture on shipment case (#2570) * [ECP-8885] Invoice only shipped items * [ECP-8885] Write unit tests * [ECP-8885] Remove return type declaration from the deprecated method --------- Co-authored-by: Can Demiralp --- Observer/BeforeShipmentObserver.php | 61 ++++- .../Observer/BeforeShipmentObserverTest.php | 239 ++++++++++++++++++ 2 files changed, 288 insertions(+), 12 deletions(-) create mode 100644 Test/Unit/Observer/BeforeShipmentObserverTest.php diff --git a/Observer/BeforeShipmentObserver.php b/Observer/BeforeShipmentObserver.php index fdd789022..9d2795121 100644 --- a/Observer/BeforeShipmentObserver.php +++ b/Observer/BeforeShipmentObserver.php @@ -14,8 +14,8 @@ use Adyen\Payment\Helper\Data as AdyenHelper; use Adyen\Payment\Helper\Config as ConfigHelper; +use Adyen\Payment\Helper\PaymentMethods; use Adyen\Payment\Logger\AdyenLogger; -use Adyen\Payment\Observer\AdyenPaymentMethodDataAssignObserver; use Exception; use Magento\Framework\Event\Observer; use Magento\Payment\Observer\AbstractDataAssignObserver; @@ -23,57 +23,72 @@ use Magento\Sales\Model\Order\InvoiceRepository; use Magento\Sales\Model\Order\Shipment; use Psr\Log\LoggerInterface; -use Throwable; class BeforeShipmentObserver extends AbstractDataAssignObserver { const XML_ADYEN_ABSTRACT_PREFIX = "adyen_abstract"; const ONSHIPMENT_CAPTURE_OPENINVOICE = 'onshipment'; - private $adyenHelper; + /** + * @var AdyenHelper + */ + private AdyenHelper $adyenHelper; - private $configHelper; + /** + * @var ConfigHelper + */ + private ConfigHelper $configHelper; /** * @var LoggerInterface */ - private $logger; + private LoggerInterface $logger; /** * @var InvoiceRepository */ - private $invoiceRepository; + private InvoiceRepository $invoiceRepository; + + /** + * @var PaymentMethods + */ + private PaymentMethods $paymentMethodsHelper; /** * BeforeShipmentObserver constructor. * * @param AdyenHelper $adyenHelper + * @param ConfigHelper $configHelper * @param AdyenLogger $logger * @param InvoiceRepository $invoiceRepository + * @param PaymentMethods $paymentMethodsHelper */ public function __construct( AdyenHelper $adyenHelper, ConfigHelper $configHelper, AdyenLogger $logger, - InvoiceRepository $invoiceRepository + InvoiceRepository $invoiceRepository, + PaymentMethods $paymentMethodsHelper ) { $this->adyenHelper = $adyenHelper; $this->configHelper = $configHelper; $this->logger = $logger; $this->invoiceRepository = $invoiceRepository; + $this->paymentMethodsHelper = $paymentMethodsHelper; } /** * @param Observer $observer * @throws Exception */ - public function execute(Observer $observer) + public function execute(Observer $observer): void { /** @var Shipment $shipment */ $shipment = $observer->getEvent()->getData('shipment'); $order = $shipment->getOrder(); + $paymentMethod = $order->getPayment()->getMethod(); - if (!$this->isPaymentMethodAdyen($order)) { + if (!$this->paymentMethodsHelper->isAdyenPayment($paymentMethod)) { $this->logger->info( "Payment method is not from Adyen for order id {$order->getId()}", ['observer' => 'BeforeShipmentObserver'] @@ -116,26 +131,48 @@ public function execute(Observer $observer) } try { - $invoice = $order->prepareInvoice(); + $itemsToBeInvoiced = $this->itemsToBeInvoiced($shipment); + + $invoice = $order->prepareInvoice($itemsToBeInvoiced); $invoice->getOrder()->setIsInProcess(true); - // set transaction id so you can do a online refund from credit memo + // set transaction id, so you can do an online refund from credit memo $pspReference = $order->getPayment()->getAdyenPspReference(); $invoice->setTransactionId($pspReference); $invoice->setRequestedCaptureCase(Invoice::CAPTURE_ONLINE); $invoice->register(); $this->invoiceRepository->save($invoice); - } catch (Throwable $e) { + } catch (Exception $e) { $this->logger->error($e); throw new Exception('Error saving invoice. The error message is: ' . $e->getMessage()); } } /** + * @deprecated Use isAdyenPayment() method from Adyen\Payment\Helper\PaymentMethods. + * * Determine if the payment method is Adyen */ public function isPaymentMethodAdyen($order) { return strpos((string) $order->getPayment()->getMethod(), 'adyen') !== false; } + + /** + * Builds the invoice item array in the form of "ORDER_ITEM_ID => QTY" + * + * @param Shipment $shipment + * @return array + */ + private function itemsToBeInvoiced(Shipment $shipment): array + { + $shipmentItems = $shipment->getItems(); + $invoiceItems = []; + + foreach ($shipmentItems as $shipmentItem) { + $invoiceItems[$shipmentItem->getOrderItemId()] = $shipmentItem->getQty(); + } + + return $invoiceItems; + } } diff --git a/Test/Unit/Observer/BeforeShipmentObserverTest.php b/Test/Unit/Observer/BeforeShipmentObserverTest.php new file mode 100644 index 000000000..ece633c2f --- /dev/null +++ b/Test/Unit/Observer/BeforeShipmentObserverTest.php @@ -0,0 +1,239 @@ + + */ + +namespace Adyen\Payment\Test\Unit\Observer; + +use Adyen\Payment\Helper\Config; +use Adyen\Payment\Helper\Data; +use Adyen\Payment\Helper\PaymentMethods; +use Adyen\Payment\Logger\AdyenLogger; +use Adyen\Payment\Observer\AdyenPaymentMethodDataAssignObserver; +use Adyen\Payment\Observer\BeforeShipmentObserver; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Exception; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Sales\Api\Data\ShipmentItemInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\InvoiceRepository; +use Magento\Sales\Model\Order\Payment; + +class BeforeShipmentObserverTest extends AbstractAdyenTestCase +{ + # Build the test class + private $beforeShipmentObserver; + + # Define constructor arguments as mocks + private $adyenHelperMock; + private $configHelperMock; + private $adyenLoggerMock; + private $invoiceRepositoryMock; + private $paymentMethodsHelperMock; + + # Other mock objects + private $paymentMock; + private $orderMock; + private $observerMock; + private $eventMock; + private $shipmentMock; + + public function setUp(): void + { + $this->adyenHelperMock = $this->createMock(Data::class); + $this->configHelperMock = $this->createMock(Config::class); + $this->adyenLoggerMock = $this->createMock(AdyenLogger::class); + $this->invoiceRepositoryMock = $this->createMock(InvoiceRepository::class); + $this->paymentMethodsHelperMock = $this->createMock(PaymentMethods::class); + + $this->paymentMock = $this->createMock(Payment::class); + + $this->orderMock = $this->createMock(Order::class); + $this->orderMock->method('getPayment')->willReturn($this->paymentMock); + $this->orderMock->method('getStoreId')->willReturn(1); + + $this->shipmentMock = $this->createMock(Order\Shipment::class); + $this->shipmentMock->method('getOrder')->willReturn($this->orderMock); + + $this->eventMock = $this->createMock(Event::class); + $this->eventMock->method('getData')->with('shipment')->willReturn($this->shipmentMock); + + $this->observerMock = $this->createMock(Observer::class); + $this->observerMock->method('getEvent')->willReturn($this->eventMock); + + $this->beforeShipmentObserver = new BeforeShipmentObserver( + $this->adyenHelperMock, + $this->configHelperMock, + $this->adyenLoggerMock, + $this->invoiceRepositoryMock, + $this->paymentMethodsHelperMock + ); + } + + public function testNonAdyenPaymentMethod() + { + $randomPaymentMethod = 'random_payment_method'; + + $this->paymentMethodsHelperMock->method('isAdyenPayment') + ->with($randomPaymentMethod) + ->willReturn(false); + + $this->paymentMock->method('getMethod')->willReturn($randomPaymentMethod); + + $this->adyenLoggerMock->expects($this->once())->method('info'); + $this->configHelperMock->expects($this->never())->method('getConfigData'); + + $this->beforeShipmentObserver->execute($this->observerMock); + } + + public function testCaptureManual() + { + $randomPaymentMethod = 'adyen_klarna'; + + $this->paymentMethodsHelperMock->method('isAdyenPayment') + ->with($randomPaymentMethod) + ->willReturn(true); + + $this->paymentMock->method('getMethod')->willReturn($randomPaymentMethod); + + $this->configHelperMock->method('getConfigData') + ->with('capture_for_openinvoice', BeforeShipmentObserver::XML_ADYEN_ABSTRACT_PREFIX, 1) + ->willReturn('manual'); + + $this->adyenLoggerMock->expects($this->once())->method('info'); + $this->paymentMock->expects($this->never())->method('getAdditionalInformation'); + + $this->beforeShipmentObserver->execute($this->observerMock); + } + + public function testNonOpenInvoicePaymentMethod() + { + $randomPaymentMethod = 'adyen_klarna'; + + $this->paymentMethodsHelperMock->method('isAdyenPayment')->willReturn(true); + + $this->paymentMock->method('getMethod')->willReturn($randomPaymentMethod); + $this->paymentMock->method('getAdditionalInformation') + ->with(AdyenPaymentMethodDataAssignObserver::BRAND_CODE) + ->willReturn('klarna'); + + $this->configHelperMock->method('getConfigData') + ->with('capture_for_openinvoice', BeforeShipmentObserver::XML_ADYEN_ABSTRACT_PREFIX, 1) + ->willReturn(BeforeShipmentObserver::ONSHIPMENT_CAPTURE_OPENINVOICE); + + $this->adyenHelperMock->method('isPaymentMethodOpenInvoiceMethod') + ->with('klarna') + ->willReturn(false); + + $this->adyenLoggerMock->expects($this->once())->method('info'); + $this->orderMock->expects($this->never())->method('canInvoice'); + + $this->beforeShipmentObserver->execute($this->observerMock); + } + + public function testNonInvoicableOrder() + { + $randomPaymentMethod = 'adyen_klarna'; + + $this->paymentMethodsHelperMock->method('isAdyenPayment')->willReturn(true); + + $this->paymentMock->method('getMethod')->willReturn($randomPaymentMethod); + $this->paymentMock->method('getAdditionalInformation') + ->with(AdyenPaymentMethodDataAssignObserver::BRAND_CODE) + ->willReturn('klarna'); + + $this->configHelperMock->method('getConfigData') + ->with('capture_for_openinvoice', BeforeShipmentObserver::XML_ADYEN_ABSTRACT_PREFIX, 1) + ->willReturn(BeforeShipmentObserver::ONSHIPMENT_CAPTURE_OPENINVOICE); + + $this->adyenHelperMock->method('isPaymentMethodOpenInvoiceMethod') + ->with('klarna') + ->willReturn(true); + + $this->orderMock->method('canInvoice')->willReturn(false); + + $this->adyenLoggerMock->expects($this->once())->method('info'); + + $this->beforeShipmentObserver->execute($this->observerMock); + } + + public function testSuccessfulShipment() + { + $randomPaymentMethod = 'adyen_klarna'; + + $this->paymentMethodsHelperMock->method('isAdyenPayment')->willReturn(true); + + $this->paymentMock->method('getMethod')->willReturn($randomPaymentMethod); + $this->paymentMock->method('getAdditionalInformation') + ->with(AdyenPaymentMethodDataAssignObserver::BRAND_CODE) + ->willReturn('klarna'); + + $this->configHelperMock->method('getConfigData') + ->with('capture_for_openinvoice', BeforeShipmentObserver::XML_ADYEN_ABSTRACT_PREFIX, 1) + ->willReturn(BeforeShipmentObserver::ONSHIPMENT_CAPTURE_OPENINVOICE); + + $this->adyenHelperMock->method('isPaymentMethodOpenInvoiceMethod') + ->with('klarna') + ->willReturn(true); + + $invoiceMock = $this->createMock(Order\Invoice::class); + $invoiceMock->method('getOrder')->willReturn($this->orderMock); + + $this->orderMock->method('canInvoice')->willReturn(true); + $this->orderMock->method('prepareInvoice')->willReturn($invoiceMock); + + $shipmentItem = $this->createMock(ShipmentItemInterface::class); + $shipmentItem->method('getOrderItemId')->willReturn(1); + $shipmentItem->method('getQty')->willReturn(10); + $this->shipmentMock->method('getItems')->willReturn([$shipmentItem]); + + $this->invoiceRepositoryMock->expects($this->once())->method('save'); + + $this->beforeShipmentObserver->execute($this->observerMock); + } + + public function testSaveError() + { + $this->expectException(Exception::class); + + $randomPaymentMethod = 'adyen_klarna'; + + $this->paymentMethodsHelperMock->method('isAdyenPayment')->willReturn(true); + + $this->paymentMock->method('getMethod')->willReturn($randomPaymentMethod); + $this->paymentMock->method('getAdditionalInformation') + ->with(AdyenPaymentMethodDataAssignObserver::BRAND_CODE) + ->willReturn('klarna'); + + $this->configHelperMock->method('getConfigData') + ->with('capture_for_openinvoice', BeforeShipmentObserver::XML_ADYEN_ABSTRACT_PREFIX, 1) + ->willReturn(BeforeShipmentObserver::ONSHIPMENT_CAPTURE_OPENINVOICE); + + $this->adyenHelperMock->method('isPaymentMethodOpenInvoiceMethod') + ->with('klarna') + ->willReturn(true); + + $invoiceMock = $this->createMock(Order\Invoice::class); + $invoiceMock->method('getOrder')->willReturn($this->orderMock); + + $this->orderMock->method('canInvoice')->willReturn(true); + $this->orderMock->method('prepareInvoice')->willReturn($invoiceMock); + + $shipmentItem = $this->createMock(ShipmentItemInterface::class); + $shipmentItem->method('getOrderItemId')->willReturn(1); + $shipmentItem->method('getQty')->willReturn(10); + $this->shipmentMock->method('getItems')->willReturn([$shipmentItem]); + + $this->invoiceRepositoryMock->method('save')->willThrowException(new Exception()); + $this->adyenLoggerMock->expects($this->once())->method('error'); + + $this->beforeShipmentObserver->execute($this->observerMock); + } +} From 0552f0e609115f0d92f1565e9cf74284052f63f2 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Fri, 5 Apr 2024 16:47:47 +0200 Subject: [PATCH 8/8] Version bump --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 51b7683dd..6a6ef49d8 100755 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "adyen/module-payment", "description": "Official Magento2 Plugin to connect to Payment Service Provider Adyen.", "type": "magento2-module", - "version": "9.4.0", + "version": "9.4.1", "license": "MIT", "repositories": [ {