From 6c33a35bddbc21b406230d14ca4aa0d0293c6826 Mon Sep 17 00:00:00 2001 From: khushboos Date: Mon, 9 Dec 2024 12:31:59 +0530 Subject: [PATCH 01/12] Fixing refund response handler if an exception is thrown --- Gateway/Http/Client/TransactionRefund.php | 2 + .../Http/Client/TransactionRefundTest.php | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/Gateway/Http/Client/TransactionRefund.php b/Gateway/Http/Client/TransactionRefund.php index 3484c030d0..238f18fb79 100644 --- a/Gateway/Http/Client/TransactionRefund.php +++ b/Gateway/Http/Client/TransactionRefund.php @@ -88,6 +88,8 @@ public function placeRequest(TransferInterface $transferObject): array $this->adyenHelper->logResponse($responseData); } catch (AdyenException $e) { $this->adyenHelper->logAdyenException($e); + $responseData['error'] = $e->getMessage(); + $responseData['errorCode'] = $e->getAdyenErrorCode(); } $responses[] = $responseData; } diff --git a/Test/Unit/Gateway/Http/Client/TransactionRefundTest.php b/Test/Unit/Gateway/Http/Client/TransactionRefundTest.php index 3d98825999..21b2730451 100644 --- a/Test/Unit/Gateway/Http/Client/TransactionRefundTest.php +++ b/Test/Unit/Gateway/Http/Client/TransactionRefundTest.php @@ -21,6 +21,7 @@ use Adyen\Service\Checkout\ModificationsApi; use Magento\Payment\Gateway\Http\TransferInterface; use PHPUnit\Framework\MockObject\MockObject; +use Adyen\AdyenException; class TransactionRefundTest extends AbstractAdyenTestCase { @@ -102,4 +103,46 @@ public function testPlaceRequestIncludesHeadersInRequest() $this->assertCount(1, $responses); $this->assertArrayHasKey('pspReference', $responses[0]); } + + public function testPlaceRequestHandlesException() + { + $requestBody = [ + 'amount' => ['value' => 1000, 'currency' => 'EUR'], + 'paymentPspReference' => '123456789' + ]; + + $headers = ['idempotencyExtraData' => ['order_id' => '1001']]; + + $transferObjectMock = $this->createConfiguredMock(TransferInterface::class, [ + 'getBody' => [$requestBody], + 'getHeaders' => $headers, + 'getClientConfig' => [] + ]); + + $serviceMock = $this->createMock(ModificationsApi::class); + $adyenClientMock = $this->createMock(Client::class); + + $this->adyenHelperMock->method('initializeAdyenClientWithClientConfig')->willReturn($adyenClientMock); + $this->adyenHelperMock->method('initializeModificationsApi')->willReturn($serviceMock); + $this->adyenHelperMock->method('buildRequestHeaders')->willReturn(['custom-header' => 'value']); + + $this->idempotencyHelperMock->expects($this->once()) + ->method('generateIdempotencyKey') + ->with($requestBody, $headers['idempotencyExtraData']) + ->willReturn('generated_idempotency_key'); + + $serviceMock->expects($this->once()) + ->method('refundCapturedPayment') + ->willThrowException(new AdyenException()); + + $this->adyenHelperMock->expects($this->once()) + ->method('logAdyenException') + ->with($this->isInstanceOf(AdyenException::class)); + + $responses = $this->transactionRefund->placeRequest($transferObjectMock); + $this->assertIsArray($responses); + $this->assertCount(1, $responses); + $this->assertArrayHasKey('error', $responses[0]); + $this->assertArrayHasKey('errorCode', $responses[0]); + } } From 8e6a869c4b7fff007c4908afe6b70ba8f1891b70 Mon Sep 17 00:00:00 2001 From: Pieter Zandbergen Date: Fri, 13 Dec 2024 15:06:21 +0100 Subject: [PATCH 02/12] fix: Handle errors on placeOrder mutations (#2799) The PlaceOrder resolver does not necessary return an order, in case of errors. See https://github.com/magento/magento2/blob/2.4-develop/app/code/Magento/QuoteGraphQl/Model/Resolver/PlaceOrder.php#L106 Co-authored-by: Can Demiralp --- Plugin/GraphQlPlaceOrderAddCartId.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Plugin/GraphQlPlaceOrderAddCartId.php b/Plugin/GraphQlPlaceOrderAddCartId.php index ee360eb04f..bf88af05b8 100644 --- a/Plugin/GraphQlPlaceOrderAddCartId.php +++ b/Plugin/GraphQlPlaceOrderAddCartId.php @@ -59,6 +59,10 @@ public function __construct( */ public function afterResolve(PlaceOrder $placeOrder, array $result): array { + if (!isset($result['order'])) { + return $result; + } + try { $cart = $this->quoteHelper->getQuoteByOrderIncrementId($result['order']['order_number']); $maskedId = $this->quoteIdToMaskedQuoteId->execute($cart->getId()); From ea6e8fc242761ce02e6c07bb03c4e1aa19ff3413 Mon Sep 17 00:00:00 2001 From: Pieter Zandbergen Date: Fri, 13 Dec 2024 15:06:58 +0100 Subject: [PATCH 03/12] fix: Sort Search Result when fetching Order by Quote ID (#2824) * fix: Sort Search Result when fetching Order by Quote ID Multiple orders might exist for the same Quote. We should be using the most recent Order when dealing with the Payment. * fix: Updated Unit Test for OrderRepository * fix: Missing create call on Sort Order Builder --------- Co-authored-by: Khushboo Co-authored-by: Can Demiralp --- Model/Sales/OrderRepository.php | 9 +++++++++ Test/Unit/Model/Sales/OrderRepositoryTest.php | 20 +++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Model/Sales/OrderRepository.php b/Model/Sales/OrderRepository.php index d51680b268..0919294075 100644 --- a/Model/Sales/OrderRepository.php +++ b/Model/Sales/OrderRepository.php @@ -16,6 +16,7 @@ use Magento\Framework\Api\Search\FilterGroupBuilder; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Api\SortOrderBuilder; use Magento\Framework\Serialize\Serializer\Json as JsonSerializer; use Magento\Payment\Api\Data\PaymentAdditionalInfoInterfaceFactory; use Magento\Sales\Api\Data\OrderExtensionFactory; @@ -28,6 +29,7 @@ class OrderRepository extends SalesOrderRepository { private SearchCriteriaBuilder $searchCriteriaBuilder; + private SortOrderBuilder $sortOrderBuilder; private FilterBuilder $filterBuilder; private FilterGroupBuilder $filterGroupBuilder; @@ -35,6 +37,7 @@ public function __construct( SearchCriteriaBuilder $searchCriteriaBuilder, FilterBuilder $filterBuilder, FilterGroupBuilder $filterGroupBuilder, + SortOrderBuilder $sortOrderBuilder, Metadata $metadata, SearchResultFactory $searchResultFactory, CollectionProcessorInterface $collectionProcessor = null, @@ -56,6 +59,7 @@ public function __construct( ); $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->sortOrderBuilder = $sortOrderBuilder; $this->filterBuilder = $filterBuilder; $this->filterGroupBuilder = $filterGroupBuilder; } @@ -68,9 +72,14 @@ public function getOrderByQuoteId(int $quoteId): OrderInterface|false ->create(); $quoteIdFilterGroup = $this->filterGroupBuilder->setFilters([$quoteIdFilter])->create(); + $sortOrder = $this->sortOrderBuilder->setField('entity_id') + ->setDescendingDirection() + ->create(); $searchCriteria = $this->searchCriteriaBuilder ->setFilterGroups([$quoteIdFilterGroup]) + ->setSortOrders([$sortOrder]) + ->setPageSize(1) ->create(); $orders = $this->getList($searchCriteria)->getItems(); diff --git a/Test/Unit/Model/Sales/OrderRepositoryTest.php b/Test/Unit/Model/Sales/OrderRepositoryTest.php index 261640f068..a0e9714810 100644 --- a/Test/Unit/Model/Sales/OrderRepositoryTest.php +++ b/Test/Unit/Model/Sales/OrderRepositoryTest.php @@ -19,6 +19,8 @@ use Magento\Framework\Api\Search\FilterGroupBuilder; use Magento\Framework\Api\SearchCriteria; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Api\SortOrder; +use Magento\Framework\Api\SortOrderBuilder; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\Data\OrderSearchResultInterface; use ReflectionClass; @@ -41,10 +43,15 @@ public function testGetOrderByQuoteId() $filterGroupBuilderMock->method('create') ->willReturn($this->createMock(FilterGroup::class)); + $sortOrderBuilderMock = $this->createPartialMock(SortOrderBuilder::class, ['create']); + $sortOrderBuilderMock->method('create') + ->willReturn($this->createMock(SortOrder::class)); + $orderRepository = $this->buildOrderRepositoryClass( $searchCriteriaBuilderMock, $filterBuilderMock, - $filterGroupBuilderMock + $filterGroupBuilderMock, + $sortOrderBuilderMock ); $order = $orderRepository->getOrderByQuoteId($quoteId); @@ -54,7 +61,8 @@ public function testGetOrderByQuoteId() public function buildOrderRepositoryClass( $searchCriteriaBuilderMock = null, $filterBuilderMock = null, - $filterGroupBuilderMock = null + $filterGroupBuilderMock = null, + $sortOrderBuilderMock = null ): OrderRepository { if (is_null($searchCriteriaBuilderMock)) { $searchCriteriaBuilderMock = $this->createMock(SearchCriteriaBuilder::class); @@ -68,6 +76,10 @@ public function buildOrderRepositoryClass( $filterGroupBuilderMock = $this->createMock(FilterGroupBuilder::class); } + if (is_null($sortOrderBuilderMock)) { + $sortOrderBuilderMock = $this->createMock(SortOrderBuilder::class); + } + $orderRepositoryPartialMock = $this->getMockBuilder(OrderRepository::class) ->setMethods(['getList']) ->disableOriginalConstructor() @@ -87,6 +99,10 @@ public function buildOrderRepositoryClass( $filterGroupBuilderProperty->setAccessible(true); $filterGroupBuilderProperty->setValue($orderRepositoryPartialMock, $filterGroupBuilderMock); + $sortOrderBuilderProperty = $reflection->getProperty('sortOrderBuilder'); + $sortOrderBuilderProperty->setAccessible(true); + $sortOrderBuilderProperty->setValue($orderRepositoryPartialMock, $sortOrderBuilderMock); + $orderSearchResultMock = $this->createConfiguredMock(OrderSearchResultInterface::class, [ 'getItems' => [$this->createMock(OrderInterface::class)] ]); From 218c5918add2377e855c0c856e4d93e20712c5d7 Mon Sep 17 00:00:00 2001 From: khushboos Date: Mon, 16 Dec 2024 16:40:36 +0530 Subject: [PATCH 04/12] Fixing errors from security point of view --- view/frontend/web/template/payment/cc-form.html | 2 +- view/frontend/web/template/payment/cc-vault-form.html | 4 ++-- view/frontend/web/template/payment/pm-vault-form.html | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/view/frontend/web/template/payment/cc-form.html b/view/frontend/web/template/payment/cc-form.html index b93b74ee04..a0df9a907b 100755 --- a/view/frontend/web/template/payment/cc-form.html +++ b/view/frontend/web/template/payment/cc-form.html @@ -63,7 +63,7 @@ -
+
diff --git a/view/frontend/web/template/payment/cc-vault-form.html b/view/frontend/web/template/payment/cc-vault-form.html index ddc824d3c9..515ed6ba91 100644 --- a/view/frontend/web/template/payment/cc-vault-form.html +++ b/view/frontend/web/template/payment/cc-vault-form.html @@ -21,7 +21,7 @@ value: getId(), click: selectPaymentMethod, checked: isChecked, - visible: isRadioButtonVisible()"/> + visible: isRadioButtonVisible()">