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.

24 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.

  4. Great work! Would be awesome if you can create a plugin from this where some of the variables could be set.

    I now do all this by hand, and always send them an email explaining why their order was cancelled (and that they are welcome to order again).

    If you need help (I’m pro PHP dev, but rookie in WordPress), let me know!

    1. Hi Ivor! Actually, I was starting the plugin, but I didn’t finish it yet. If you’re interested into take a look on the code, just let me know 🙂

  5. Hi. Thanks for sharing this.

    I tried to use the codes in my child theme’s functions.php but don’t seem to work. I want to make it cancel the order immediately after the customer fails to complete the payment. By default on my site, the order will be made and the status is pending payment. I would like this pending to change to cancelled immediately so that I don’t have to deal with it.

    I removed both
    if( $datediff >= 4 ){

    But I received this error when saving.
    syntax error, unexpected ‘orderby’ (T_STRING), expecting ‘)’

    Any advice? Thanks!

    1. Hi Josh,

      Thank you for reaching out and paste your questions here in the comments. It can be a general issue and somebody else can have the same solution.

      Before anything, please check if you cleaned the “)” from the IF you removed.
      Regarding your error, I think it’s because of the parenthesis on the query, before the array(…). Please remove them – the result will be something like: $query = array(…).

      Hope this helps!

        1. I catch it! Please remove the “{” after your comment “Immediate Cancellation”, that’s a sintax error.

  6. Hello! Can I know what is the maximum number that I can put for
    ‘limit’ => 5,

    I am checking out test orders and realize the 6th order onwards will be back to ‘processing’ instead of ‘cancelled’

    1. Hi! Technically, there isn’t a maximum number on the query.
      Maybe you have another service running that back the orders to “processing” status, this snippet compares the date and decide to cancel the order or not, according to the snippet info.

  7. Olá Samuel, muito obrigado pelo tutorial, já estava com este pedido a quase 2 meses e realmente pelo painel do woocommerce não é possível resolver. Ví que está desenvolvendo um plugin, como está o processo? andando ou parado?

    1. Cesar, if you need additional help with this, get in touch with me using the contact form.
      Cheers,
      Samuel

  8. Hello Samuel I have tried the snippet and it worked in my small website. I have cancelled 2 orders. But I have another ecommerce, I tried the snippet but still showing 1373 orders as pending, looks like it changes just few orders.
    Older orders are not changing, What can I do? Is that limit you put as five (5) the problem?

    1. Hi César! Each time you access the wp-admin, 5 orders will be processed, for performance reasons.
      You can also integrate this snippet in a WP Cron Job, and for example, we can process 5 orders for every 5 minutes.
      Hope this helps!

      1. Thank you Samuel, I tried using WP Control plugin to schedule but it didn’t work. Now I put 100 as limit on function.php and updated but nothing happened.

  9. Hi,
    I wanted to compare if the order is pending for more than 60 minutes. How can that be achieved?

  10. Hi… I have two questions.

    1) I pasted the code exactly as below in the function.php and nothing happened (note: I have several requests like “Pagamento Pending” in Portuguese, I don’t know if it changes something because of the woocomerce sling.

    2) Considering that this function works inside functions.php… I would like to create a unique php file to be executed by cronjob, as would be the formatting in the file and the leader for cronjob

    ———–
    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 >= 7 ){
    $order->update_status(‘cancelled’, ‘Cancelled for missing payment’);
    }

    }

    }

    add_action( ‘admin_init’, ‘autocancel_wc_orders’ );

Leave a comment

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