Description
Some payment methods in cancel payment action try to cancel current order and restore quote and as the result product qty is increased by 2.
Affected payment methods
- Paypal Hosted Pro (CE, EE)
- Paypal Payflow (CE, EE)
- Worldpay (EE)
- Eway (EE)
- PayEx (Third party payment gateway)
Preconditions
- Magento 2.1.* CE and EE
- Configured payment methods
Steps to reproduce
- Suppose you have product with stock quantity: 1
- Add product to cart and Checkout
- Select affected payment method
- The order is placed and stock is reduced to 0
- Click cancel on Payment gateway (e.g. press "Cancel and return to store" button on PayPal page)
Expected result
- Order was canceled
- Product stock quantity: 1
Actual result
- Order was canceled
- Product stock quantity: 2 (increase to 2)
Additional information
For example Paypal Hosted Pro Cancel action
\Magento\Paypal\Controller\Hostedpro\Cancel
/**
* Customer canceled payment on gateway side.
*
* @return void
*/
public function execute()
{
$this->checkoutHelper->cancelCurrentOrder('');
$this->checkoutHelper->restoreQuote();
$this->_redirect('checkout', ['_fragment' => 'payment']);
}
Method cancelCurrentOrder
will execute $order->registerCancellation($comment)->save();
that will cancel all order items and dispatch 'sales_order_item_cancel' event for each one. \Magento\CatalogInventory\Observer\CancelOrderItemObserver
observer listens this event and increases product qty.
Next \Magento\Checkout\Model\Session::restoreQuote()
will be executed to restore customer quote. That method will update and save a quote and dispatch restore_quote
event.
\Magento\CatalogInventory\Observer\RevertQuoteInventoryObserver
listens to this event and product qty is increased the second time.
The problem is that both statements increase quantity of product.
The same issue in Payflow cancel action \Magento\Paypal\Controller\Payflow
:
/**
* Cancel order, return quote to customer
*
* @param string $errorMsg
* @return false|string
*/
protected function _cancelPayment($errorMsg = '')
{
$errorMsg = trim(strip_tags($errorMsg));
$gotoSection = false;
$this->_checkoutHelper->cancelCurrentOrder($errorMsg);
if ($this->_checkoutSession->restoreQuote()) {
//Redirect to payment step
$gotoSection = 'paymentMethod';
}
return $gotoSection;
}