magento2: Paypal some orders fail with main.CRITICAL: Wrong order ID:

When using paypal express integration as the payment method on Magento 2.2.2 a small number of orders fail after the payment has been taken by paypal. No order is registered in Magento, but paypal takes payment and sends a confirmation to both customer and store owner.

Looking in the logs the error which seems to be associated with these orders failing is: main.CRITICAL: Wrong order ID: "000000222". {"exception":"[object] (Exception(code: 0): Wrong order ID: \"000000222\". at .../vendor/magento/module-paypal/Model/Ipn.php:140)"}

Preconditions

  1. Magento 2.2.2
  2. Paypal Express Checkout set as Payment method.

Steps to reproduce

  1. Customer Places Order
  2. Directed to PayPal page and payment taken
  3. Redirected to site, order fails and is not logged in Magento.

Expected result

  1. Once payment has been taken and the customer has been redirected back to the site order is registered with magento.

Actual result

  1. Payment is taken.
  2. Paypal redirects to site and sends confirmation email with correct order number.
  3. Order is not registered in magento.
  4. Order ID skips failed order (next order number is incremented from failed order, orders list in backend has a gap where failed order would have been).

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 14
  • Comments: 104 (14 by maintainers)

Most upvoted comments

After my comment above I have gathered some additional log data which explains how (in my case) we end up with a valid paypal transaction with no order existing in Magento.

  • We have seen a deadlock when creating the sales_invoice which caused the order to be rolled back which reverts the valid sales_order entry.
  • The customer then ends up on paypal/express/review with the session message We can't place the order where they can attempt to press Place Order again.
  • However this will fail as the paypal transaction has already been registered as complete by Paypal and the PayPal gateway has rejected request. A successful transaction has already been completed for this token exception will be logged.

Explanation of how I found this, and my mitigation.

Finding the error

Step 1 - Capture errors and log them

I added some additional logging to the core paypal controller by using cweagans/composer-patches. (This log will catch a lot of false positives as address related issues etc will be caught.)

--- Controller/Express/AbstractExpress/PlaceOrder.php    2018-11-23 15:32:32.000000000 +0000
+++ Controller/Express/AbstractExpress/PlaceOrder.php    2018-11-23 15:31:53.000000000 +0000
@@ -27,6 +27,17 @@
     private $paymentFailures;

     /**
+     * Logger added to debug JIRA-TICKET-123
+     *
+     * Paypal order auth and captured with no corresponding order in magento.
+     *
+     * @var \Psr\Log\LoggerInterface
+     *
+     * @author Luke Rodgers
+     */
+    protected $logger;
+
+    /**
      * @param \Magento\Framework\App\Action\Context $context
      * @param \Magento\Customer\Model\Session $customerSession
      * @param \Magento\Checkout\Model\Session $checkoutSession
@@ -49,9 +60,11 @@
         \Magento\Framework\Url\Helper\Data $urlHelper,
         \Magento\Customer\Model\Url $customerUrl,
         \Magento\Checkout\Api\AgreementsValidatorInterface $agreementValidator,
+        \Psr\Log\LoggerInterface $logger,
         PaymentFailuresInterface $paymentFailures = null
     ) {
         $this->agreementsValidator = $agreementValidator;
+        $this->logger = $logger;
         $this->paymentFailures = $paymentFailures ? : ObjectManager::getInstance()
             ->get(PaymentFailuresInterface::class);

@@ -126,8 +139,12 @@
             $this->_redirect('checkout/onepage/success');
             return;
         } catch (ApiProcessableException $e) {
+            $this->logger->critical("Logging for JIRA-TICKET-123");
+            $this->logger->critical($e);
             $this->_processPaypalApiError($e);
         } catch (\Exception $e) {
+            $this->logger->critical("Logging for JIRA-TICKET-123");
+            $this->logger->critical($e);
             $this->messageManager->addExceptionMessage(
                 $e,
                 __('We can\'t place the order.')

Step 2 - Wait until an orphaned order is identified

I would like to try and find a way of doing this programmatically, but as the data doesn’t exist in Magento it is difficult.

Paypal will have a record of the intended order increment_id which we need to proceed.

Step 3 - Find the associated quote

select entity_id, created_at, updated_at, remote_ip, reserved_order_id from quote 
where reserved_order_id='XX123456789';

This will give you the updated_at time, this and the remote_ip can also be used to cross reference with your apache logs to confirm the user journey and that the customer ended up on paypal/express/review.

Step 4 - Get the error log

The log can be found in var/log/support_report.log, easily identified by searching for “Logging for JIRA-TICKET-123” at around the same timestamp as the quote updated_at field.

Step 5 - What we saw

SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction, query was: INSERT INTO `sales_invoice

[2018-12-29 00:01:04] report.CRITICAL: Logging for JIRA-TICKET-123 [] []
[2018-12-29 00:01:04] report.CRITICAL: Exception message: SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction, query was: INSERT INTO `sales_invoice` (`store_id`, `base_grand_total`, `shipping_tax_amount`, `tax_amount`, `base_tax_amount`, `store_to_order_rate`, `base_shipping_tax_amount`, `base_discount_amount`, `base_to_order_rate`, `grand_total`, `shipping_amount`, `subtotal_incl_tax`, `base_subtotal_incl_tax`, `store_to_base_rate`, `base_shipping_amount`, `total_qty`, `base_to_global_rate`, `subtotal`, `base_subtotal`, `discount_amount`, `billing_address_id`, `order_id`, `state`, `shipping_address_id`, `store_currency_code`, `transaction_id`, `order_currency_code`, `base_currency_code`, `global_currency_code`, `increment_id`, `discount_tax_compensation_amount`, `base_discount_tax_compensation_amount`, `shipping_discount_tax_compensation_amount`, `base_shipping_discount_tax_compensation_amnt`, `discount_description`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Trace: #0 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/DB/Statement/Pdo/Mysql.php(95): Zend_Db_Statement_Pdo->_execute(Array)
#1 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/zendframework1/library/Zend/Db/Statement.php(303): Magento\Framework\DB\Statement\Pdo\Mysql->_execute(Array)
#2 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/zendframework1/library/Zend/Db/Adapter/Abstract.php(480): Zend_Db_Statement->execute(Array)
#3 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/zendframework1/library/Zend/Db/Adapter/Pdo/Abstract.php(238): Zend_Db_Adapter_Abstract->query('INSERT INTO `sa...', Array)
#4 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php(465): Zend_Db_Adapter_Pdo_Abstract->query('INSERT INTO `sa...', Array)
#5 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php(520): Magento\Framework\DB\Adapter\Pdo\Mysql->_query('INSERT INTO `sa...', Array)
#6 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/zendframework1/library/Zend/Db/Adapter/Abstract.php(576): Magento\Framework\DB\Adapter\Pdo\Mysql->query('INSERT INTO `sa...', Array)
#7 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-sales/Model/ResourceModel/EntityAbstract.php(185): Zend_Db_Adapter_Abstract->insert('sales_invoice', Array)
#8 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Model/ResourceModel/Db/AbstractDb.php(405): Magento\Sales\Model\ResourceModel\EntityAbstract->saveNewObject(Object(Magento\Sales\Model\Order\Invoice))
#9 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Model/AbstractModel.php(636): Magento\Framework\Model\ResourceModel\Db\AbstractDb->save(Object(Magento\Sales\Model\Order\Invoice))
#10 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-sales/Model/ResourceModel/Order/Relation.php(95): Magento\Framework\Model\AbstractModel->save()
#11 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Model/ResourceModel/Db/VersionControl/RelationComposite.php(48): Magento\Sales\Model\ResourceModel\Order\Relation->processRelation(Object(Magento\Sales\Model\Order\Interceptor))
#12 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Model/ResourceModel/Db/VersionControl/AbstractDb.php(57): Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite->processRelations(Object(Magento\Sales\Model\Order\Interceptor))
#13 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Model/ResourceModel/Db/AbstractDb.php(408): Magento\Framework\Model\ResourceModel\Db\VersionControl\AbstractDb->processAfterSaves(Object(Magento\Sales\Model\Order\Interceptor))
#14 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-sales/Model/ResourceModel/Order.php(178): Magento\Framework\Model\ResourceModel\Db\AbstractDb->save(Object(Magento\Sales\Model\Order\Interceptor))
#15 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-sales/Model/OrderRepository.php(161): Magento\Sales\Model\ResourceModel\Order->save(Object(Magento\Sales\Model\Order\Interceptor))
#16 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Interceptor.php(146): Magento\Sales\Model\OrderRepository->save(Object(Magento\Sales\Model\Order\Interceptor))
#17 /var/www/vhosts/example.com/production/releases/1545033608/var/generation/Magento/Sales/Model/OrderRepository/Interceptor.php(52): Magento\Sales\Model\OrderRepository\Interceptor->___callPlugins('save', Array, Array)
#18 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-sales/Model/Service/OrderService.php(202): Magento\Sales\Model\OrderRepository\Interceptor->save(Object(Magento\Sales\Model\Order\Interceptor))
#19 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-quote/Model/QuoteManagement.php(499): Magento\Sales\Model\Service\OrderService->place(Object(Magento\Sales\Model\Order\Interceptor))
#20 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-quote/Model/QuoteManagement.php(395): Magento\Quote\Model\QuoteManagement->submitQuote(Object(Magento\Quote\Model\Quote\Interceptor), Array)
#21 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-paypal/Model/Express/Checkout.php(799): Magento\Quote\Model\QuoteManagement->submit(Object(Magento\Quote\Model\Quote\Interceptor))
#22 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-paypal/Controller/Express/AbstractExpress/PlaceOrder.php(107): Magento\Paypal\Model\Express\Checkout->place('AA-11A11111A111...')
#23 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/App/Action/Action.php(102): Magento\Paypal\Controller\Express\AbstractExpress\PlaceOrder->execute()
#24 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Interceptor.php(74): Magento\Framework\App\Action\Action->dispatch(Object(Magento\Framework\App\Request\Http))
#25 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(70): Magento\Paypal\Controller\Express\PlaceOrder\Interceptor->___callParent('dispatch', Array)
#26 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Paypal\\...', 'dispatch', Object(Magento\Paypal\Controller\Express\PlaceOrder\Interceptor), Array, 'contextPlugin')
#27 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-store/App/Action/Plugin/Context.php(106): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#28 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(67): Magento\Store\App\Action\Plugin\Context->aroundDispatch(Object(Magento\Paypal\Controller\Express\PlaceOrder\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#29 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Paypal\\...', 'dispatch', Object(Magento\Paypal\Controller\Express\PlaceOrder\Interceptor), Array, 'customer-segmen...')
#30 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-customer-segment/Model/App/Action/ContextPlugin.php(81): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#31 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(67): Magento\CustomerSegment\Model\App\Action\ContextPlugin->aroundDispatch(Object(Magento\Paypal\Controller\Express\PlaceOrder\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#32 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Paypal\\...', 'dispatch', Object(Magento\Paypal\Controller\Express\PlaceOrder\Interceptor), Array, 'customer-app-ac...')
#33 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-customer/Model/App/Action/ContextPlugin.php(61): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#34 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(67): Magento\Customer\Model\App\Action\ContextPlugin->aroundDispatch(Object(Magento\Paypal\Controller\Express\PlaceOrder\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#35 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Paypal\\...', 'dispatch', Object(Magento\Paypal\Controller\Express\PlaceOrder\Interceptor), Array, 'storeCheck')
#36 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-store/App/Action/Plugin/StoreCheck.php(44): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#37 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(67): Magento\Store\App\Action\Plugin\StoreCheck->aroundDispatch(Object(Magento\Paypal\Controller\Express\PlaceOrder\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#38 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Paypal\\...', 'dispatch', Object(Magento\Paypal\Controller\Express\PlaceOrder\Interceptor), Array, 'weee-app-action...')
#39 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-weee/Model/App/Action/ContextPlugin.php(112): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#40 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(67): Magento\Weee\Model\App\Action\ContextPlugin->aroundDispatch(Object(Magento\Paypal\Controller\Express\PlaceOrder\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#41 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Paypal\\...', 'dispatch', Object(Magento\Paypal\Controller\Express\PlaceOrder\Interceptor), Array, 'tax-app-action-...')
#42 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-tax/Model/App/Action/ContextPlugin.php(91): Magento\Paypal\Controller\Express\PlaceOrder\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
#43 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Interceptor.php(142): Magento\Tax\Model\App\Action\ContextPlugin->aroundDispatch(Object(Magento\Paypal\Controller\Express\PlaceOrder\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#44 /var/www/vhosts/example.com/production/releases/1545033608/var/generation/Magento/Paypal/Controller/Express/PlaceOrder/Interceptor.php(26): Magento\Paypal\Controller\Express\PlaceOrder\Interceptor->___callPlugins('dispatch', Array, Array)
#45 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/App/FrontController.php(55): Magento\Paypal\Controller\Express\PlaceOrder\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))
#46 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Interceptor.php(74): Magento\Framework\App\FrontController->dispatch(Object(Magento\Framework\App\Request\Http))
#47 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(70): Magento\Framework\App\FrontController\Interceptor->___callParent('dispatch', Array)
#48 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Framewo...', 'dispatch', Object(Magento\Framework\App\FrontController\Interceptor), Array, 'requestPreproce...')
#49 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-store/App/FrontController/Plugin/RequestPreprocessor.php(94): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#50 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(67): Magento\Store\App\FrontController\Plugin\RequestPreprocessor->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#51 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Framewo...', 'dispatch', Object(Magento\Framework\App\FrontController\Interceptor), Array, 'install')
#52 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Module/Plugin/DbStatusValidator.php(69): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#53 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(67): Magento\Framework\Module\Plugin\DbStatusValidator->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#54 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Framewo...', 'dispatch', Object(Magento\Framework\App\FrontController\Interceptor), Array, 'front-controlle...')
#55 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-page-cache/Model/App/FrontController/VarnishPlugin.php(55): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#56 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Chain/Chain.php(67): Magento\PageCache\Model\App\FrontController\VarnishPlugin->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#57 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Framewo...', 'dispatch', Object(Magento\Framework\App\FrontController\Interceptor), Array, 'front-controlle...')
#58 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/module-page-cache/Model/App/FrontController/BuiltinPlugin.php(69): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
#59 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/Interception/Interceptor.php(142): Magento\PageCache\Model\App\FrontController\BuiltinPlugin->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#60 /var/www/vhosts/example.com/production/releases/1545033608/var/generation/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\Framework\App\FrontController\Interceptor->___callPlugins('dispatch', Array, Array)
#61 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/App/Http.php(135): Magento\Framework\App\FrontController\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))
#62 /var/www/vhosts/example.com/production/releases/1545033608/vendor/magento/framework/App/Bootstrap.php(258): Magento\Framework\App\Http->launch()
#63 /var/www/vhosts/example.com/production/releases/1545033608/pub/index.php(37): Magento\Framework\App\Bootstrap->run(Object(Magento\Framework\App\Http))
#64 {main} [] []

Emulating the issue locally

I was able to spoof similar behaviour (lock wait timeout, not a deadlock) locally using the following methodology, this should never be done on any environment other than your local!

  1. Ensure that payment/paypal_express/payment_action is set to Sale so that invoices are created when the payment is processed.

  2. Go through your checkout locally on your chosen store view, do a successful paypal order to ensure the auto increment ids are clean and easily calculable.

  3. Clear your cookies / session / etc. Go through the checkout on the same store view and get as far as https://www.sandbox.paypal.com.

  4. Prepare to lock the sales_invoice table by getting the next values

    # Get the next order entity_id
    $ magerun2 db:query 'select max(entity_id)+1 as next_order_id from sales_order'
    next_order_id
    10
    
    # Get the next invoice increment_id
    $ magerun2 db:query 'select increment_id+1 as next_increment_id from sales_invoice order by entity_id desc limit 1'
    next_increment_id
    2000006607
    
    # Get the id for your given store view
    $ magerun2 sys:store:list | grep uk
    | 2  | uk     |
    
  5. Open a database terminal (magerun2 db:cons) and run the following commands (updating the values based on your output). Leave the window and session open so that the lock persists.

BEGIN;
insert into sales_order(entity_id, store_id) values (10, 2);
insert into sales_invoice(order_id, increment_id, store_id) values (10, '2000006607', 2);
  1. Proceed through the paypal checkout process. The callback to Magento should fail and take you to paypal/express/review where you cannot progress from. This order is broken.

  2. Close your database terminal to release the lock without writing.

Mitigating the issue

To mitigate this error and turn our handling of it from being reactive to proactive I have written an observer.

  • Hooks into sales_model_service_quote_submit_failure
  • Only operates for orders where the exception message contains the string try restarting transaction
  • Write log data to confirm a deadlocked order occurred.
  • Use Magento\Sales\Model\Service\PaymentFailuresService to send an email highlighting that a successful order was rolled back because of deadlock. Allowing for prompt investigation and contacting of the customer.

The above conditions should allow us to remove all false alerts and ensure this only fires during this scenario.

https://gist.github.com/convenient/ea1411d650e8765278338adc36deba93

Next steps

Can anyone from Magento advise on what could be causing the deadlock on the sales_invoice table? How to solve this issue properly?

I got the same issue on 2.4.2, anyone know how to fix it?

Everything we’ve done so far which have eliminated deadlocks occuring during orders and greatly reduced deadlocks overall:

In Magento: Asynchronous grid = on Flat products and categories = on Async indexing = on Production mode enabled always Install Cadence_DeadlockRetry

In my.cnf transaction-isolation = READ-COMMITTED (along with other optimizations)

Beef up your server’s CPU and disk speed (IOPS) as much as possible. This is very important, even for low traffic sites. Fast SSD’s on dedicated hardware/instances are a must.

Switched to authorize.net for CC orders (mainly due to double auth fees with the payflow integration) - see https://github.com/magento/magento2/issues/6542 - no more missing CC orders either.

At this point we aren’t seeing any order related mysql deadlocks, but we are still seeing very occasional “wrong order ID” in the system log and a corresponding missing paypal order. What gives?

We determined this remaining issue is poor CURL error handling in the payflow module. A VERY occasional connection issue with paypal with no attempt to retry and no traceback thrown. If this happens during order processing after the first API call (several API calls are made) you are left with a missing/incomplete order. You’ll have a payment, a quote with valid order ID, but no corresponding order.

We wrote a command line module to programatically complete these orders. We could even take it one step further and grep the exception log for "Wrong order ID: “$n” and automate it.

Ideally, there should at least be N retries when CURL exceptions occur (similar to what the Cadence_DeadlockRetry module does for mysql deadlocks). We briefly attempted this but found magento’s payflow integration too dense and co-dependant with other core modules to override completely.

;TLDR

This entire issue has at least two causes. MySQL deadlocks which are a way of life with magento, and poor exception handling in the payflow module. Neither of these issues are trivial to expose in testing, so the devs are not going to spend time with this.

The payflow integration in it’s current state across all magento2 versions is a steaming pile. Switch to something else for payments (at least for CC’s), if you can.

I have confirmed that this is happening on Magento 2.2.6. A small number of orders have captured funds, but the order has not been created. I get the following in exception.log:

[2018-10-26 11:56:00] report.CRITICAL: Wrong order ID: "XXXXXXXXXX". {"exception":"[object] (Exception(code: 0): Wrong order ID: \"XXXXXXXXXX\". at /path/to/vendor/magento/module-paypal/Model/Ipn.php:140)"} []

This issue comes from your third-party module. Kindly re-check all your module which customized the order flow. I fixed it with condition check Order Object in Observer. if ( $order )

@engcom-backlog-nazar Thank you for verifying the issue. Based on the provided information internal tickets MAGETWO-97869, MAGETWO-97870 were created

Hi @djamps Would you mind sharing your command line module to complete the missing orders? Would be greatly appreciated!

We wrote a command line module to programatically complete these orders. We could even take it one step further and grep the exception log for "Wrong order ID: “$n” and automate it.

Wow. @magento-engcom-team I know this is hard to reproduce but didn’t my steps at least provide something for you to work with ?

Having the same issue on the latest Magento version 2.4.5-p1. I recently upgraded magento from 2.4.1 to 2.4.5-p1, and made some test orders, then the issue happened occasionally. Unfortunately I can not reproduce it, but I can provide some details for solving this problem. The system log as following: issue-log Magento system is writing the same error for the order ID 000000434 again and again, but actually it has created the order ID 000000435 for this transaction. In my papal account, the order ID for the transaction is 000000434, magento 000000435 for the same transaction number instead. So the problem is that order ids in magento and paypal for the transaction should be same, but different instead. I think wrong order ID passed to paypal system, then it sends the IPN with wrong order ID again and again. At the same time, the issue seems be related to the paypal express review step, when you make the payment using paypal express checkout on product or shopping cart page, you login your paypal account, click continue, it should be redirected to the paypal/express/review page, but for the order with this error, I remember, the order was placed directly, your are redirected to the suscess page without review step, and the shopping cart was not cleared after purchase. Since I’m not a professional programmer, hope this will help for the issue.

It even happening in Magento 2.4.3.

I have confirmed that this is happening on Magento 2.3.7!

Having same issue in 2.3.2

hi @convenient Thank you for your detailed explanation.