I am currently trying to build an eCommerce site. The functionalities are quite basic; there are products with limited quantities and customers can purchase those products. I've decided to use Drupal 10 as my backend CMS headless to handle the products and orders. The frontend communicates with Drupal through REST APIs. This includes order creation. When a user checks out, an order gets created in the backend, and the quantity of the item purchased is decreased.
I have a potential issue arising with the above order creation. The order creation can be seen below... The code is a bit simplified but the point is there:
public function post($data) {
if (!$this->loggedUser->hasPermission('access content')) {
throw new AccessDeniedHttpException();
}
$productVariationStorage = \Drupal::entityTypeManager()->getStorage('commerce_product_variation');
$productVariation = $productVariationStorage->load($data['id']);
// potential check to make sure the product variation is available (quantity > 0)
// Throw error message if not available
$orderItems = array();
$orderItem = OrderItem::create([
'type' => 'example_order_item',
'purchased_entity' => $data['id'],
'quantity' => 1,
]);
$orderItem->save();
array_push($orderItems, $orderItem);
$order = Order::create([
'type' => 'example_order',
'uid' => $this->loggedUser->id(),
'store_id' => 1,
'order_items' => $orderItems,
'placed' => \Drupal::time()->getCurrentTime(),
'state' => 'draft'
]);
$order->save();
$productVariation->field_quantity->value -= 1;
$productVariation->save();
$resultArray = array();
$resultArray['order_id'] = $order->uuid();
return new ResourceResponse($resultArray);
}
The purchased product ID is sent from the frontend in the request body. Drupal then loads the product variation and checks if the quantity is greater than zero (if it is available, this can only be seen written in comments). If it is available, it proceeds to create an order item type and an order, etc. The quantity is then reduced by one, and the product variation is saved.
Let's say customer one clicks checkout on the frontend and the order creation API is called resulting in the above PHP code running. However, very shortly after customer one clicks checkout, customer two also clicks checkout with the same product (quantity is one for the product). A problem arises if the order of execution is the following for the two requests:
- Customer one check if available->it's available, continue
- Customer two check if available->it's available, continue
- Customer one create order, decrease quantity, save, etc.
- Customer two create order, decrease quantity, save, etc.
Both customers are able to purchase a single product, and I need to refund someone. Can this scenario happen? If so, the solution would be to somehow lock the product variation entity until it is up to date. While it is locked nobody can read/write. Can this be done from PHP?
I appreciate the help in advance, and if anything is unclear, let me know, and I'll edit the post!