6

If two users simultaneously attempt to purchase the last item from an online store, we either have an opportunity for a race condition, or an opportunity for a DOS attack on the store.

Specifically, I am using Magento and Worldpay payment engine, but I see the problem as a more general one where a third party payment gateway is used.

Consider the two scenarios

  1. Stock is deducted when an order is placed OR...
  2. Stock is deducted when a payment is received.

In scenario 1, we have the following problem. If the payment is not completed, the item still goes out of stock. This allows an attacker to order everything without paying for it - of course the item will not be dispatched, but stock control will prevent any legitimate users from ordering the item. The problem has to be fixed manually, when it is noticed, during office hours.

There are settings in Magento to limit the number of items that can be ordered, but this isn't really useful for items where there are only a few in stock - and we might want to sell all of them to legitimate purchaser (ie not say "you can only have 1")

It doesn't necessarily have to be a deliberate attack either. If the user has trouble navigating the payment gateway's website due to a browser issue they may give up - or they may start the process again further depleting stock.

So the other option, scenario 2 leads us to a race condition. If two users both get to the checkout with the last item, they can both order it and carry onto the payment gateway website, because at that stage the stock has not been deducted. And then they both pay, but there is only one item available.

As far as I can make out, there is only a payment notification protocol, not a "is this item still in stock" protocol.

So I was wondering how people generally deal with this problem, and in particular what Magento / Worldpay configurations could be used to alleviate the issues.

The best fit I could think of is some sort of timeout on payment, which puts the item back in stock. This would require a payment timeout both at the Magento end and at the Worldpay end, but I can't see any method of doing that at either end.

4
  • I cancel off any pending payment orders via the magento API two hours after the order is placed. I also send the user an email to say the order had been canceled because no payment was found so they can contact us or rebuild their cart and try again. In conjunction with this we use the API to do full stock updates from our back office at regular intervals. We do not really got stock level issues selling thousands of products to hundreds of customers a day and we sell via the phone as well and eBay. All done automatically with no user interjection at all.
    – Ashley Swatton
    Nov 1, 2013 at 17:22
  • Please identify what version of Magento you are using. Specifically, 1.8/1.13 provide better indexing that might fix most of this problem.
    – parhamr
    Dec 9, 2013 at 23:37
  • We are using 1.7. I wonder how big of a job an upgrade would be, and what benefit it would be. I'm kinda coming to the conclusion that when using a 3rd party payment engine, this problem can never be eliminated completely, but maybe I'm missing something.
    – Rodney
    Dec 11, 2013 at 12:56
  • You’re right—it’s a common problem that is easy enough to reduce and really hard to solve. Check out the upgrade tag for estimates of upgrade complexities.
    – parhamr
    Dec 11, 2013 at 18:51

3 Answers 3

3

In the various bits of contracting and consulting I've done, the solution is not to solve this problem. The general thinking is

  • It happens less often than you'd think

  • When it does happen, it's easier to take both orders and fix things on a business level

Fixing this on a business level means waiting to fulfill the second order until more stock comes in, or if more stock isn't coming you cancel the customer's order and send an apologetic email with an optional coupon or some other low cost fringe benefit so they don't feel bad.

2

I can't speak specifically for Worldpay, but we use a SagePay server integration method and the module we use (by Ebizmarts) resolves this in what I see to be the best workable solution. Depending on API support etc, this approach may not be applicable.

We have 1 item in stock and two people on the checkout, Magento of course has no problem with this. Customer A and Customer B both hit the final stage of checkout which pops open the SagePay form in a fancy box leaving the customer to complete their payment details. After the customer has submitted their payment details to SagePay (and to their bank in the case of 3D Secure schemes) SagePay will make a post to a notification URL on our server. Let's assume both customers enter correct details and that Customer A did so first. SagePay will notify Magento of a successful payment for Customer A. Magento will convert Customer A's quote to an order and invoice immediately (there's various configuration options for DEFER etc, but we don't use them) before outputting a success message in response to the notification. SagePay will then re-direct the customer to the Magento checkout success page.

At this point Customer A has completed and Customer B just submitted the final stage of payment. SagePay will then make a post to Magento saying successful payment for Customer B. Magento attempts to convert Customer B's quote to an order, but this will fail since the quote_item is now out of stock. The module catches this exception and outputs a failure response to the notification, with Customer B's quote still intact, and no order. SagePay will then VOID the payment and redirect the customer back to the failure page (OPC) where Customer B will receive the message "Order could not be completed, one or more items are out of stock".

So many other payment modules I've looked at create orders before receiving any kind of payment information, this drives me mad because of the shear number of orders that never complete. Clients are often confused why they have so many orders in the system which aren't actually orders. But perhaps that's just my take on things.

3
  • Thanks, it's good to know Magento can possibly be made to work this way - depending on the payment engine. What version of Magento are you using? I wonder if the Worldpay module can void a payment, and if it can't whether that's a deficiency in it, or in the Worldpay API in general. Possibly I need to speak to Worldpay technical support about this.
    – Rodney
    Dec 12, 2013 at 22:46
  • Current CE 1.7.0.2, but we've been using the module since 1.4.2. Earlier versions of the module did have some issue with this process, but the current version seems to have ironed those out. Dec 13, 2013 at 8:47
  • Coincidentally, I'm current looking into ways to resolve a somewhat related stock issue. We export orders to a 3rd party system in batches, they process them and send back stock levels for us to import (sort of redundant since Magento tracks this, but it allows them to move stock between warehouses, remove lost stock etc). Unfortunately if we sell anything between the time of exporting, them processing and importing the resultant file, we can put the item back in stock even though it's technically sold out. Dec 13, 2013 at 8:51
1

Here is my suggestions,

  * Decrease stock count, while “Add to cart”
  * Set expire time, if cart is abandoned then set back the ‘stock’ count
  * Again check “Stock” availability at the time of checkout.
  * If Payment Success then decrease the ‘Stock’
  * Otherwise set timeout then revert back the ‘Stock Count’

By this way, you have the chances of checking the 'stock' at two places and it helps to avoid this situation by 99.9%. Thanks,

1
  • I don't think Magento supports this out the box. One thing I can see here is people could DOS you by filling their cart, deleting their cookies, filling their cart, deleting their cookies....
    – Rodney
    Oct 3, 2018 at 12:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.