Search code examples
phpwoocommerceproductstockorders

Change order reserved stock time for specific payment method in WooCommerce


I am using WooCommerce for selling some digital products. Due to the natural of our business, we do not want the item stock to go lower than 0 (-1, -2, etc.)

We are currently accepting different payment methods, including Cryptocurrency and Stripe. As the transaction speed of Crypto is low, sometimes takes up to 1 hour, so we are facing issue like this:

  1. When a user (User A) purchase an item and pay with Crypto, the item stock gets hold (going from 1 to 0). As the blockchain is slow, it takes 2 hours to complete the payment. Since it's so slow, the item stock goes back from 0 to 1.
  2. While the blockchain payment is still pending, a different user (User B) comes to buy the same product. At this time, the item is not being held anymore. Thus, this user gets the product. Item stock goes from 1 to 0.
  3. The blockchain payment is finally confirmed. Woocommerce also gives the product to this user. Item stock goes from 0 to -1.

So 2 users get the same product.

How to prevent this issue?

I haven't found any solution yet.


Solution

  • The following should change the time to hold the stock (60 minutes by default) for an order passed with a specific payment method (untested).

    See How to get the ID of a payment method in WooCommerce?.

    Below, inside the function, you will define the desired payment method(s) ID(s) (for your cryptocurrency payment option(s)) and the desired time in minutes to hold the stock:

    add_action( 'woocommerce_checkout_order_created', 'wc_reserve_stock_for_crypto', 5 );
    function wc_reserve_stock_for_crypto( $order ) {
        // HERE below define the desired payment ID(s)
        $targeted_payment_ids = array('coinbase', 'coinpayments'); 
        // HERE below define the time in minutes to hold stock
        $hold_stock_minutes   = 150; 
    
        if ( in_array( $order->get_payment_method(), $targeted_payment_ids ) ) {
            // Remove the default functionality to hold stock for 60 minutes
            remove_action( 'woocommerce_checkout_order_created', 'wc_reserve_stock_for_order' );
    
            if ( ! apply_filters( 'woocommerce_hold_stock_for_checkout', wc_string_to_bool( get_option( 'woocommerce_manage_stock', 'yes' ) ) ) ) {
                return;
            }
    
            $order = $order instanceof WC_Order ? $order : wc_get_order( $order );
    
            if ( $order ) {
                // Add back the functionality with a custom delay to hold the stock
                ( new \Automattic\WooCommerce\Checkout\Helpers\ReserveStock() )->reserve_stock_for_order( $order, $hold_stock_minutes );
            }
        }
    }
    

    Code goes in functions.php file of your child theme (or in a plugin). It should work.