Skip to content

Commit

Permalink
Merge pull request #6919 from magento-l3/PR_20210609
Browse files Browse the repository at this point in the history
L3 Bugfix delivery. 2.4-develop
  • Loading branch information
dhorytskyi authored Jul 1, 2021
2 parents 912857f + 3ae8fe1 commit bf4cdad
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 30 deletions.
54 changes: 38 additions & 16 deletions app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
*/
class ReturnUrl extends Payflow implements CsrfAwareActionInterface, HttpGetActionInterface
{
private const ORDER_INCREMENT_ID = 'INVNUM';

private const SILENT_POST_HASH = 'secure_silent_post_hash';

/**
* @var array of allowed order states on frontend
*/
Expand Down Expand Up @@ -63,30 +67,48 @@ public function execute()
$this->_view->loadLayout(false);
/** @var \Magento\Checkout\Block\Onepage\Success $redirectBlock */
$redirectBlock = $this->_view->getLayout()->getBlock($this->_redirectBlockName);

if ($this->_checkoutSession->getLastRealOrderId()) {
/** @var \Magento\Sales\Model\Order $order */
$order = $this->_orderFactory->create()->loadByIncrementId($this->_checkoutSession->getLastRealOrderId());

if ($order->getIncrementId()) {
if ($this->checkOrderState($order)) {
$redirectBlock->setData('goto_success_page', true);
$order = $this->getOrderFromRequest();
if ($order) {
if ($this->checkOrderState($order)) {
$redirectBlock->setData('goto_success_page', true);
} else {
if ($this->checkPaymentMethod($order)) {
$gotoSection = $this->_cancelPayment((string)$this->getRequest()->getParam('RESPMSG'));
$redirectBlock->setData('goto_section', $gotoSection);
$redirectBlock->setData('error_msg', __('Your payment has been declined. Please try again.'));
} else {
if ($this->checkPaymentMethod($order)) {
$gotoSection = $this->_cancelPayment((string)$this->getRequest()->getParam('RESPMSG'));
$redirectBlock->setData('goto_section', $gotoSection);
$redirectBlock->setData('error_msg', __('Your payment has been declined. Please try again.'));
} else {
$redirectBlock->setData('goto_section', false);
$redirectBlock->setData('error_msg', __('Requested payment method does not match with order.'));
}
$redirectBlock->setData('goto_section', false);
$redirectBlock->setData('error_msg', __('Requested payment method does not match with order.'));
}
}
}

$this->_view->renderLayout();
}

/**
* Returns an order from request.
*
* @return Order|null
*/
private function getOrderFromRequest(): ?Order
{
$orderId = $this->getRequest()->getParam(self::ORDER_INCREMENT_ID);
if (!$orderId) {
return null;
}

$order = $this->_orderFactory->create()->loadByIncrementId($orderId);
$storedHash = (string)$order->getPayment()->getAdditionalInformation(self::SILENT_POST_HASH);
$requestHash = (string)$this->getRequest()->getParam('USER2');
if (empty($storedHash) || empty($requestHash) || !hash_equals($storedHash, $requestHash)) {
return null;
}
$this->_checkoutSession->setLastRealOrderId($orderId);

return $order;
}

/**
* Check order state
*
Expand Down
2 changes: 2 additions & 0 deletions app/code/Magento/Paypal/Plugin/TransparentSessionChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class TransparentSessionChecker
*/
private $disableSessionUrls = [
'paypal/transparent/redirect',
'paypal/payflowadvanced/returnUrl',
'paypal/payflow/returnUrl',
'paypal/hostedpro/return',
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class ReturnUrlTest extends TestCase
{
const LAST_REAL_ORDER_ID = '000000001';

const SILENT_POST_HASH = 'abcdfg';

/**
* @var ReturnUrl
*/
Expand Down Expand Up @@ -142,7 +144,7 @@ protected function setUp(): void

$this->checkoutSession = $this->getMockBuilder(Session::class)
->disableOriginalConstructor()
->setMethods(['getLastRealOrderId', 'getLastRealOrder', 'restoreQuote'])
->setMethods(['setLastRealOrderId', 'getLastRealOrder', 'restoreQuote'])
->getMock();

$this->paymentFailures = $this->getMockBuilder(PaymentFailuresInterface::class)
Expand Down Expand Up @@ -177,8 +179,15 @@ public function testExecuteAllowedOrderState($state)
$this->withLayout();
$this->withOrder(self::LAST_REAL_ORDER_ID, $state);

$this->checkoutSession->method('getLastRealOrderId')
->willReturn(self::LAST_REAL_ORDER_ID);
$this->request->method('getParam')
->willReturnMap([
['INVNUM', self::LAST_REAL_ORDER_ID],
['USER2', self::SILENT_POST_HASH],
]);

$this->checkoutSession->expects($this->once())
->method('setLastRealOrderId')
->with(self::LAST_REAL_ORDER_ID);

$this->block->method('setData')
->with('goto_success_page', true)
Expand All @@ -202,6 +211,45 @@ public function allowedOrderStateDataProvider()
];
}

/**
* Checks a test case when silent post hash validation fails.
*
* @param string $requestHash
* @param string $orderHash
* @dataProvider invalidHashVariations
*/
public function testFailedHashValidation(string $requestHash, string $orderHash)
{
$this->withLayout();
$this->withOrder(self::LAST_REAL_ORDER_ID, Order::STATE_PROCESSING, $orderHash);

$this->request->method('getParam')
->willReturnMap([
['INVNUM', self::LAST_REAL_ORDER_ID],
['USER2', $requestHash],
]);

$this->checkoutSession->expects($this->never())
->method('setLastRealOrderId')
->with(self::LAST_REAL_ORDER_ID);

$this->returnUrl->execute();
}

/**
* Gets list of allowed order states.
*
* @return array
*/
public function invalidHashVariations()
{
return [
['requestHash' => '', 'orderHash' => self::SILENT_POST_HASH],
['requestHash' => self::SILENT_POST_HASH, 'orderHash' => ''],
['requestHash' => 'abcd', 'orderHash' => 'dcba'],
];
}

/**
* Checks a test case when action processes order with not allowed state.
*
Expand All @@ -218,8 +266,11 @@ public function testExecuteNotAllowedOrderState($state, $restoreQuote, $expected
$this->withCheckoutSession(self::LAST_REAL_ORDER_ID, $restoreQuote);

$this->request->method('getParam')
->with('RESPMSG')
->willReturn($errMessage);
->willReturnMap([
['RESPMSG', $errMessage],
['INVNUM', self::LAST_REAL_ORDER_ID],
['USER2', self::SILENT_POST_HASH],
]);

$this->payment->method('getMethod')
->willReturn(Config::METHOD_PAYFLOWLINK);
Expand Down Expand Up @@ -261,8 +312,14 @@ public function testCheckRejectByPaymentMethod()
$this->withLayout();
$this->withOrder(self::LAST_REAL_ORDER_ID, Order::STATE_NEW);

$this->checkoutSession->method('getLastRealOrderId')
->willReturn(self::LAST_REAL_ORDER_ID);
$this->checkoutSession->expects($this->once())
->method('setLastRealOrderId')
->with(self::LAST_REAL_ORDER_ID);
$this->request->method('getParam')
->willReturnMap([
['INVNUM', self::LAST_REAL_ORDER_ID],
['USER2', self::SILENT_POST_HASH],
]);

$this->withBlockContent(false, 'Requested payment method does not match with order.');

Expand All @@ -285,8 +342,11 @@ public function testCheckXSSEscaped($errorMsg, $errorMsgEscaped)
$this->withCheckoutSession(self::LAST_REAL_ORDER_ID, true);

$this->request->method('getParam')
->with('RESPMSG')
->willReturn($errorMsg);
->willReturnMap([
['RESPMSG', $errorMsg],
['INVNUM', self::LAST_REAL_ORDER_ID],
['USER2', self::SILENT_POST_HASH],
]);

$this->checkoutHelper->method('cancelCurrentOrder')
->with(self::equalTo($errorMsgEscaped));
Expand Down Expand Up @@ -323,8 +383,11 @@ public function testCheckAdvancedAcceptingByPaymentMethod()
$this->withCheckoutSession(self::LAST_REAL_ORDER_ID, true);

$this->request->method('getParam')
->with('RESPMSG')
->willReturn('message');
->willReturnMap([
['RESPMSG', 'message'],
['INVNUM', self::LAST_REAL_ORDER_ID],
['USER2', self::SILENT_POST_HASH],
]);

$this->withBlockContent('paymentMethod', 'Your payment has been declined. Please try again.');

Expand All @@ -347,9 +410,10 @@ public function testCheckAdvancedAcceptingByPaymentMethod()
*
* @param string $incrementId
* @param string $state
* @param string $hash
* @return void
*/
private function withOrder($incrementId, $state)
private function withOrder($incrementId, $state, $hash = self::SILENT_POST_HASH)
{
$this->orderFactory->method('create')
->willReturn($this->order);
Expand All @@ -366,6 +430,8 @@ private function withOrder($incrementId, $state)

$this->order->method('getPayment')
->willReturn($this->payment);
$this->payment->method('getAdditionalInformation')
->willReturn($hash);
}

/**
Expand All @@ -390,8 +456,8 @@ private function withLayout()
*/
private function withCheckoutSession($orderId, $restoreQuote)
{
$this->checkoutSession->method('getLastRealOrderId')
->willReturn($orderId);
$this->checkoutSession->method('setLastRealOrderId')
->with($orderId);

$this->checkoutSession->method('getLastRealOrder')
->willReturn($this->order);
Expand Down
8 changes: 8 additions & 0 deletions app/code/Magento/Paypal/etc/csp_whitelist.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@
<values>
<value id="www_paypal" type="host">www.paypal.com</value>
<value id="www_sandbox_paypal" type="host">www.sandbox.paypal.com</value>
<value id="pilot_payflowlink_paypal_com" type="host">pilot-payflowlink.paypal.com</value>
</values>
</policy>
<policy id="form-action">
<values>
<value id="www_paypal" type="host">www.paypal.com</value>
<value id="www_sandbox_paypal" type="host">www.sandbox.paypal.com</value>
<value id="pilot_payflowlink_paypal_com" type="host">pilot-payflowlink.paypal.com</value>
</values>
</policy>
</policies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ define([
if (this.iframeIsLoaded) {
document.getElementById(this.getCode() + '-iframe')
.contentWindow.location.reload();
this.paymentReady(false);
}

this.paymentReady(true);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

use Magento\Sales\Api\Data\OrderInterfaceFactory;
use Magento\Sales\Model\Order;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
use Magento\Sales\Api\OrderManagementInterface;

Resolver::getInstance()->requireDataFixture('Magento/Sales/_files/order.php');

$objectManager = Bootstrap::getObjectManager();
/** @var Order $order */
$order = $objectManager->get(OrderInterfaceFactory::class)->create()->loadByIncrementId('100000001');
/** @var OrderManagementInterface $orderManagement */
$orderManagement = $objectManager->create(OrderManagementInterface::class);
$orderManagement->place($order);
$orderManagement->cancel($order->getEntityId());
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

use Magento\Framework\Registry;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\Data\OrderInterfaceFactory;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;

$objectManager = Bootstrap::getObjectManager();
/** @var OrderRepositoryInterface $orderRepository */
$orderRepository = $objectManager->get(OrderRepositoryInterface::class);
/** @var OrderInterface $order */
$order = $objectManager->get(OrderInterfaceFactory::class)->create()->loadByIncrementId('100000001');
/** @var Registry $registry */
$registry = $objectManager->get(Registry::class);
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', true);

$orderRepository->delete($order);

$registry->unregister('isSecureArea');
$registry->register('isSecureArea', false);

Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple_rollback.php');

0 comments on commit bf4cdad

Please sign in to comment.