Search code examples
phpwordpressdatetimewoocommercecart

Disable checkout based on an allowed time range in Woocommerce


I need a time related checkout option (so if people order from past 23.59 to 06.00, the checkout will be canceled and they will be redirected to an other page). I know how to build the logics but dont know the code for the time relation.

This time related checkout only needs to happen when a certain category is in the cart.

This is my code for now:

foreach( WC()->cart->get_cart() as $cart_item ){

    if( is_checkout() && has_parent_term( $cart_item['product_id']) && TIME_IS > (SATURDAY 23:59) && TIME_IS < (SUNDAY 06:00)) {
      wp_redirect( get_permalink( get_option('woocommerce_cart_page_id') ) );
      wc_add_notice( sprintf( 'unfortunatly you are not able to puchase these items at this time'), 'error');
      exit;
    }
}

The conditional function has_parent_term() is defined in this answer thread.


Solution

  • The following code will check for a defined product category in cart items and a time range. If the defined product category is found and if time it's out of the defined time range (from 06:00 to 23:59), a custom notice will be displed avoiding checkout.

    There is no need of redirection to cart when using woocommerce_check_cart_items dedicated hook.

    For the time range conditional function:
    - The formatted start and end time strings need to be like hh:mm:ss
    - You will have to define the time zone (see the List of Supported Timezones

    // Custom conditional function that checks for parent product categories from a product category slug
    function has_parent_term( $product_id, $category_slug ) {
    
        // Convert category term slug to term id
        $category_id   = get_term_by('slug', $category_slug, 'product_cat')->term_id;
        $parent_term_ids = array(); // Initializing
    
        // Loop through the current product category terms to get only parent main category term
        foreach( get_the_terms( $product_id, 'product_cat' ) as $term ){
            if( $term->parent > 0 ){
                $parent_term_ids[] = $term->parent; // Set the parent product category
                $parent_term_ids[] = $term->term_id; // (and the child)
            } else {
                $parent_term_ids[] = $term->term_id;
            }
        }
        return in_array( $category_id, array_unique($parent_term_ids) );
    }
    
    // Custom conditional function that checks from a time range
    function is_on_time( $start_time, $end_time, $time_zone = 'UTC' ) {
        // Set the default time zone (http://php.net/manual/en/timezones.php)
        date_default_timezone_set($time_zone);
    
        $from   = explode( ':', $start_time ); //
        $from_h = isset($from[0]) ? $from[0] : 0; // hours
        $from_m = isset($from[1]) ? $from[1] : 0; // minutes
        $from_s = isset($from[2]) ? $from[2] : 0; // seconds
        $start  = mktime( $from_h,  $from_m, $from_s, date("m"), date("d"), date("Y"));
    
        $to   = explode( ':', $end_time );
        $to_h = isset($to[0]) ? $to[0] : 0; // hours
        $to_m = isset($to[1]) ? $to[1] : 0; // minutes
        $to_s = isset($to[2]) ? $to[2] : 0; // seconds
        $end  = mktime( $to_h,  $to_m,  $to_s, date("m"), date("d"), date("Y"));
    
        $now   = strtotime("now");
    
        return ( $start >= $now && $end < $now ) ? true : false;
    }
    
    // Checking cart items and avoid checkout displaying an error notice
    add_action( 'woocommerce_check_cart_items', 'woocommerce_check_cart_quantities' );
    function woocommerce_check_cart_quantities() {
        $found = false; // Initializing
    
        // Loop through cart items
        foreach ( WC()->cart->get_cart() as $cart_item ) {
            // Check for product category term and parent term
            if ( has_parent_term( $cart_item['product_id'], 't-shirts' ) )
                $found = true; // category is found
        }
    
        // Checking product category and time
        if ( $found && ! is_on_time( '6:00', '23:59', 'Europe/Paris' ) ){
            // Avoiding checkout displaying a custom error notice
            wc_add_notice( __("Sorry too late, time is now off. You are not allowed to checkout", "woocommerce" ), 'error' );
        }
    }
    

    Code goes in function.php file of your active child theme (active theme). Tested and works.


    When time is over if any cart item remaining to a specific product category:

    1) In cart page:

    enter image description here

    2) In Checkout page:

    enter image description here


    Addition - Targeting days of the week in the is_on_time() conditional function.

    Replace return ( $start >= $now && $end < $now ) ? true : false; by one of the following, if you want to:

    1) Targeting 'Sundays' only (example):

    return ( $start >= $now && $end < $now && date('w') == '0' ) ? true : false;
    

    2) Targeting week end days only (example):

    return ( $start >= $now && $end < $now && in_array( date('w'), array('6','0') ) ) ? true : false;
    

    2) Targeting the working days only (example):

    return ( $start >= $now && $end < $now && ! in_array( date('w'), array('6','0') ) ) ? true : false;