Skip to content

Commit 8cc27f8

Browse files
Merge remote-tracking branch 'adobe-commerce-tier-4/ACP2E-3143' into Tier4-PR-2024-08-05
2 parents a6131a2 + 287593c commit 8cc27f8

File tree

2 files changed

+69
-4
lines changed
  • app/code/Magento/Paypal/Model
  • dev/tests/integration/testsuite/Magento/Paypal/Model

2 files changed

+69
-4
lines changed

app/code/Magento/Paypal/Model/Ipn.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
namespace Magento\Paypal\Model;
88

99
use Exception;
10+
use Magento\Framework\App\ObjectManager;
1011
use Magento\Framework\Exception\LocalizedException;
1112
use Magento\Sales\Model\Order;
1213
use Magento\Sales\Model\Order\Email\Sender\CreditmemoSender;
1314
use Magento\Sales\Model\Order\Email\Sender\OrderSender;
15+
use Magento\Sales\Model\OrderMutexInterface;
1416

1517
/**
1618
* PayPal Instant Payment Notification processor model
@@ -46,13 +48,19 @@ class Ipn extends \Magento\Paypal\Model\AbstractIpn implements IpnInterface
4648
protected $creditmemoSender;
4749

4850
/**
49-
* @param \Magento\Paypal\Model\ConfigFactory $configFactory
51+
* @var OrderMutexInterface|null
52+
*/
53+
private ?OrderMutexInterface $orderMutex;
54+
55+
/**
56+
* @param ConfigFactory $configFactory
5057
* @param \Psr\Log\LoggerInterface $logger
5158
* @param \Magento\Framework\HTTP\Adapter\CurlFactory $curlFactory
5259
* @param \Magento\Sales\Model\OrderFactory $orderFactory
5360
* @param Info $paypalInfo
5461
* @param OrderSender $orderSender
5562
* @param CreditmemoSender $creditmemoSender
63+
* @param OrderMutexInterface|null $orderMutex
5664
* @param array $data
5765
*/
5866
public function __construct(
@@ -63,13 +71,15 @@ public function __construct(
6371
Info $paypalInfo,
6472
OrderSender $orderSender,
6573
CreditmemoSender $creditmemoSender,
74+
?OrderMutexInterface $orderMutex = null,
6675
array $data = []
6776
) {
6877
parent::__construct($configFactory, $logger, $curlFactory, $data);
6978
$this->_orderFactory = $orderFactory;
7079
$this->_paypalInfo = $paypalInfo;
7180
$this->orderSender = $orderSender;
7281
$this->creditmemoSender = $creditmemoSender;
82+
$this->orderMutex = $orderMutex ?: ObjectManager::getInstance()->get(OrderMutexInterface::class);
7383
}
7484

7585
/**
@@ -466,6 +476,21 @@ protected function _registerPaymentReversal()
466476
* @return void
467477
*/
468478
protected function _registerPaymentRefund()
479+
{
480+
return $this->orderMutex->execute(
481+
(int) $this->_order->getEntityId(),
482+
\Closure::fromCallable([$this, 'processRefund'])
483+
);
484+
}
485+
486+
/**
487+
* Process a refund
488+
*
489+
* @return void
490+
* @throws Exception
491+
* @SuppressWarnings(PHPMD.UnusedPrivateMethod) This method is used in closure callback
492+
*/
493+
private function processRefund()
469494
{
470495
$this->_importPaymentInformation();
471496
$reason = $this->getRequestData('reason_code');

dev/tests/integration/testsuite/Magento/Paypal/Model/IpnTest.php

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,46 @@ public function testProcessIpnRequestFullRefund()
8080
$this->assertEmpty($order->getTotalOfflineRefunded());
8181
}
8282

83+
/**
84+
* Refund full order amount by Paypal Express IPN message service with concurrent requests.
85+
*
86+
* @magentoDataFixture Magento/Paypal/_files/order_express_with_invoice_and_shipping.php
87+
* @magentoConfigFixture current_store payment/paypal_express/active 1
88+
* @magentoConfigFixture current_store paypal/general/merchant_country US
89+
*/
90+
public function testProcessIpnRequestFullRefundConcurrent()
91+
{
92+
$ipnData = require __DIR__ . '/../_files/ipn_refund.php';
93+
$ipnFactory = $this->_objectManager->create(IpnFactory::class);
94+
$ipnModel = $ipnFactory->create(
95+
[
96+
'data' => $ipnData,
97+
'curlFactory' => $this->_createMockedHttpAdapter()
98+
]
99+
);
100+
101+
$ipnModel->processIpnRequest();
102+
$ipnModel->processIpnRequest();
103+
104+
$order = $this->_objectManager->create(Order::class);
105+
$order->loadByIncrementId('100000001');
106+
107+
$creditmemoItems = $order->getCreditmemosCollection()->getItems();
108+
$creditmemo = current($creditmemoItems);
109+
110+
$this->assertEquals(Order::STATE_CLOSED, $order->getState()) ;
111+
$this->assertCount(1, $creditmemoItems);
112+
$this->assertEquals(Creditmemo::STATE_REFUNDED, $creditmemo->getState());
113+
$this->assertEquals(10, $order->getSubtotalRefunded());
114+
$this->assertEquals(10, $order->getBaseSubtotalRefunded());
115+
$this->assertEquals(20, $order->getShippingRefunded());
116+
$this->assertEquals(20, $order->getBaseShippingRefunded());
117+
$this->assertEquals(30, $order->getTotalRefunded());
118+
$this->assertEquals(30, $order->getBaseTotalRefunded());
119+
$this->assertEquals(30, $order->getTotalOnlineRefunded());
120+
$this->assertEmpty($order->getTotalOfflineRefunded());
121+
}
122+
83123
/**
84124
* Partial refund of order amount by Paypal Express IPN message service.
85125
*
@@ -253,11 +293,11 @@ protected function _createMockedHttpAdapter()
253293
$factory = $this->createPartialMock(\Magento\Framework\HTTP\Adapter\CurlFactory::class, ['create']);
254294
$adapter = $this->createPartialMock(\Magento\Framework\HTTP\Adapter\Curl::class, ['read', 'write']);
255295

256-
$adapter->expects($this->once())->method('read')->with()->willReturn("\nVERIFIED");
296+
$adapter->expects($this->any())->method('read')->with()->willReturn("\nVERIFIED");
257297

258-
$adapter->expects($this->once())->method('write');
298+
$adapter->expects($this->any())->method('write');
259299

260-
$factory->expects($this->once())->method('create')->with()->willReturn($adapter);
300+
$factory->expects($this->any())->method('create')->with()->willReturn($adapter);
261301
return $factory;
262302
}
263303

0 commit comments

Comments
 (0)