Cancel WooCommerce not-paid Orders automatically

One way to keep your online store clean and organized is by using the order status. Complete, Cancelled, Awaiting Payment are statuses which helps you to pay attention to the most important orders.

But when you have a lot of “Awaiting payment” orders and you don’t know until when you need to consider that order? Well, I’m writing today because of that. A client of mine gave me this problem: I want to cancel my pending orders (awaiting payment) after 4 days.

Before the solution we discuss about:

  • Should we need to consider all the payment methods?
  • The client should receive any type of email confirmation?
  • Which order status we need to consider?

But after that, I started to code. Follow me, step by step:

1. Create a query with WooCommerce orders.

$query = ( 
   array(
	'limit'   => 5,
	'orderby' => 'date',
	'order'   => 'DESC',
	'status'  => array( 'pending' )
   ) 
);

$orders = wc_get_orders( $query );

// our orders loop
foreach( $orders as $order ){		
		
}

For performance reasons, I’m getting only 5 orders each time I run my snippet. Pay attention to the “status” array, where you need to replace by the status you want to consider. We have a lot of status in WooCommerce, like “on-hold”, “completed”, “cancelled”, etc…

2. Check the number of days between order name and today

Inside the loop I wrote above, we will check the time difference and know the “age” of your order.

foreach( $orders as $order ){		
  
  // date of our order
  $date = new DateTime( $order->get_date_created() );
  // today's date
  $today = new DateTime();
  
  $interval = $date->diff($today);
  // days between order name and today	
  $datediff = $interval->format('%a');
   
  // 4: minimum of days the order can be considered
  if( $datediff >= 4 ){
    $order->update_status('cancelled', 'Cancelled for missing payment);
   }
		
	}

Right now, the code says: “If the order is pending (as we defined on the query) and it has more than 4 days, we will update the order status to “canceled“, adding a status note (Cancelled for missing payment).

3. Tell to WordPress when to run our function

Considering that you put our code into a function like that:


function autocancel_wc_orders(){
	$query = ( array(
		'limit'   => 5,
		'orderby' => 'date',
		'order'   => 'DESC',
		'status'  => array( 'pending' )
	) );
	$orders = wc_get_orders( $query );
	foreach( $orders as $order ){		

		$date     = new DateTime( $order->get_date_created() );
		$today    = new DateTime();
		$interval = $date->diff($today);
	
		$datediff = $interval->format('%a');

		if( $datediff >= 4 ){
			$order->update_status('cancelled', 'Cancelled for missing payment');
		}
		
	}

}

Now we just need to select a WordPress action to run this. Just as it is only 5 orders each time, I decided to use the simple admin_init action. We can use a lot of alternatives, like WPcron or something, but for this case, I think it was the best way: nice performance, admin security, and without dependencies need 🙂

add_action( 'admin_init', 'autocancel_wc_orders' );

That’s all. It was simple? If you want to discuss this or you have a better way to make this, please let me know in the comment area.

Cheers.

8 comments

  1. hey, very nice work.
    we need a directly cancelation of “pending orders” I have tried to set “if( $datediff >= 4 )” to “-1” => if( $datediff >= -1 ) and that is working, but is there any better solution?

    if the order is getting created, the order should get directly cancled if not payed.

    1. Hi Brian!

      If you don’t want to use the number of days interval to cancel, you only need to remove that if clause, instead of using -1 comparing.

      – if( $datediff >= 4 ){

      The best,
      Samuel

  2. Hi, this is exactly what I’ve been looking for some time now. Thank you!

    Just a question, does it compare hours or just date difference?

    I want to cancel on-hold orders after 24hrs.

    1. Thank you for your words, Axel.
      Regarding your request, you only need to change the number from this line to 1.
      > if( $datediff >= 4 ){

      1. Awesome. I already did, just wanted to make sure it actually activates after 24hrs and not when date changes.

        I also want to ask you a question and maybe make it a request/suggestion for future article:

        I’ve been searching for weeks on a way to make on-site notifications for woocommerce order status changes.

        By “on-site” what I mean is to have a bell icon (showing count of recent notifications inside a red circle) on the header (frontend, for all customers) and when clicked it displays all recent notifications, like for example: “Your order has been shipped 🎉 Today at 2:00pm”

        I imagine that would be some kind of an inbox. I know this can be done, and I just started learning code so I’m kinda lost on how to achieve this.

        Hope you have some guidance as to how to do this or what should I learn to be able to build it.

        Anyways, that’s all 😊 thanks a lot for the automatic order cancellation snippet! I tested it and it’s working.

        Have a great day Samuel!

        1. Hi Axel,

          Ok, I got you. We’re not considering 24hours on this snippet, but we’re counting and comparing day by day. To consider 24 hours, the date/time needs to compare the hour diff, instead of number of day diff.

          Regarding your suggestion, it seems great!
          The first step is searching and learning about the Ajax method on WordPress, this is the technology that provides you change the information on the HTML without a refresh.

          If you need custom code for starting or hiring me for the development, please email me: hello@samuelsilva.pt

          The best,
          Samuel

  3. Olá Samuel.

    Muito bom artigo, obrigado, mesmo o que procurava. Este código deve ser adicionado à functions.php?

    1. Olá Fernando.

      Obrigado. Sim, o snippet pode ser adicionado no functions.php, recomendo o child-theme.

Leave a comment

Your email address will not be published. Required fields are marked *