How to Set WooCommerce Virtual Order Status to Complete After Payment
WooCommerce virtual orders can be automatically marked as ‘completed’ after payment with a little bit of code added to a custom plugin, or your themes functions.php file. By default WooCommerce will mark virtual-downloadable orders as ‘completed’ after successful payment, which makes sense, but some store owners will want to be able to automatically mark even a virtual order as complete upon payment, for instance in the case of a site which takes donations where no further action is required. To do so, use the following code, which is based on the core virtual-downloadable completed order status:
add_filter( 'woocommerce_payment_complete_order_status', 'virtual_order_payment_complete_order_status', 10, 2 );
function virtual_order_payment_complete_order_status( $order_status, $order_id ) {
$order = new WC_Order( $order_id );
if ( 'processing' == $order_status &&
( 'on-hold' == $order->status || 'pending' == $order->status || 'failed' == $order->status ) ) {
$virtual_order = null;
if ( count( $order->get_items() ) > 0 ) {
foreach( $order->get_items() as $item ) {
if ( 'line_item' == $item['type'] ) {
$_product = $order->get_product_from_item( $item );
if ( ! $_product->is_virtual() ) {
// once we've found one non-virtual product we know we're done, break out of the loop
$virtual_order = false;
break;
} else {
$virtual_order = true;
}
}
}
}
// virtual order, mark as completed
if ( $virtual_order ) {
return 'completed';
}
}
// non-virtual order, return original status
return $order_status;
}
Note: This article has been updated to work with WooCommerce 2.0+ and the sample code above will no longer work with WC 1.6.6 and previous
Remember, this functionality is now available as a convenient plugin with the release of the premium WooCommerce Order Status Control
I am bookmarking this one — thanks again Justin.
Thanks a lot for sharing, it’s exactly what I needed!
Could it be that the code doesn’t work with the latest update? If so, do you have an idea what to change? Thanks in advance, Hendrik
Hey Hendrik, sorry to hear it’s not working for you. I just gave this solution a try in the most recent WooCommerce and it worked just fine for me, so not sure why it wouldn’t be working for you. One thing to note is that this will complete an order only if the entire order consists of virtual products.
Thanks Justin,
If I understand. I have to put the code in my “fonctions.php” of my “inspire-commerce theme” and that’s it?
I sell a membership for the website so it’s not a physical product and not a downloadable product too. The orders status stay on “Processing” until I put the orders on “Complete”. So the customers can’t reach the membership content until I put the order on “Complete”.
I hope this will fix my problem.
Thanks.
Yep, you got it, just make sure you’re setting these products as “virtual”
Thanks Justin!
It’s working now! Thanks for sharing with us this solution. It’s very appreciate!
Hi,
I’ve pasted that code into my theme’s functions.php at the end, but before the ?>. I’ve set the product to Virtual and it’s still not auto-confirming the order?
Great site, thanks in advance!
hmm, I just took another look at the relevant WooCommerce core code, and I can’t see anything that would cause this not to work. I’ve been unable to reproduce whatever the issue is, so I’m not able to determine why it would not work for you, it should just work assuming that the entire order consists of virtual products. I’m afraid you’d have to do a little debugging to track down the problem, if this is something you’re able to tackle, I do have an introduction to debugging WordPress which you can read through. You’d want to add some
error_log()statements: first to make sure thevirtual_order_payment_complete_order_status()is being called, and then to figure out whether it’s returning the correct status “completed” when you desired. Hope this helpsDo you think I could use this for a regular product as well? I have a customer who never bothers going into their admin panel so the orders always sit as ‘processing’ and I would like them to automatically be marked as completed. I fairly new to WC so I am having some trouble figuring out how to do this.
Sure, that’s a simpler, more general case than what I did here. Get rid of the check for whether the order is virtual, and just return ‘completed’
So would that look like this?
add_action( ‘woocommerce_payment_complete_order_status’, ‘virtual_order_payment_complete_order_status’, 10, 2 );
function virtual_order_payment_complete_order_status( $order_status, $order_id ) {
$order = new WC_Order( $order_id );
if ( $order_status == ‘processing’ &&
( $order->status == ‘on-hold’ || $order->status == ‘pending’ || $order->status == ‘failed’ ) ) {
$order = true;
if ( count( $order->get_items() ) > 0 ) {
foreach( $order->get_items() as $item ) {
if ( $item['id'] > 0 ) {
$_product = $order->get_product_from_item( $item );
}
}
}
// virtual order, mark as completed
if ( $order ) {
return ‘completed’;
}
}
}
Well, no, the function should be even simpler, maybe something just like:
function virtual_order_payment_complete_order_status( $order_status, $order_id ) {
$order = new WC_Order( $order_id );
if ( $order_status == 'processing' &&
( $order->status == 'on-hold' || $order->status == ‘pending’ || $order->status == ‘failed’ ) ) return 'completed';
return $order_status;
}
Hi Justin,
I had a question about your choice of conditionals….
if ( $order_status == 'processing' &&
( $order->status == 'on-hold' || $order->status == 'pending' || $order->status == 'failed' ) )
What is the point of checking that original $order->status ? Also, nitpicking, but shouldn’t it be add_filter()? Anyway, I appreciate your sharing this! Cheers!
Hey Kathy, thanks for pointing out my action/filter misstype, wordpress is so loose with that sort of thing that I think it still works anyways, but I do prefer using the correct one.
As for checking the original order status, I based this function off of
WC_Order::payment_complete(), and they make the same check there and so I maintained it.Thanks!
Hi Justin,
One more thing: I presume you are adding this to your theme’s functions.php?? I ask, because I’ve tried putting this in my plugin’s class and my function that is attached to ‘woocommerce_payment_complete_order_status’ never seems to fire.
thanks,
-kathy
it was a facepalm!!! of the highest order. hangs head in shame. thanks again for this post.
Ha ha, no problem, glad if it was of help to you!
Hi Justin,
Looks like you have another useful piece of code I want to use! I am trying to adapt this code so that any orders I get with the status ” On Hold” or “Pending” are updated to “Processing”.
Simple I thought, but unfortunately I can’t get it to work. I have taken your code and changed it to this:
function virtual_order_payment_complete_order_status( $order_status, $order_id ) {$order = new WC_Order( $order_id );
if ( $order_status == 'on-hold' &&
( $order->status == 'pending' || $order->status == ‘failed’ ) ) return 'processing';
return $order_status;
}
Nothing changes on the backend with the above code. What am I doing wrong?
Many thanks in advance for your help.
Jamie
Hey Jaime, in this filter
$order_statuswill be the status that the order is being set to, while$order->statuswill be the current (soon to be previous) order status. So to automatically set on-hold/pending orders to processing, you could simply try replacing your if statement with:if ( $order_status == 'on-hold' || $order_status == 'pending' ) return 'processing';Where here we don’t even care what the previous order status was, we’re just making all on-hold/pending status’ change to ‘processing’.
Hi Justin,
Many thanks for the explanation. I understand where I went wrong now! I will give the new code a try and post back with my results.
Little by little I am beginning to understand, it’s a slow process for me though!
Jamie
OK, I tried the new code, but nothing seems to happen. I have placed a couple of orders and they are still at the “On Hold” status.
Here’s my code below that I’m adding to the theme’s function.php file:
function virtual_order_payment_complete_order_status( $order_status,$order_id ) {
$order = new WC_Order( $order_id );
if ( $order_status == 'on-hold' || $order_status == 'pending' )
return 'processing';
return $order_status;
}
I also tried the same code without the return $order_status at the end:
function virtual_order_payment_complete_order_status( $order_status,$order_id ) {
$order = new WC_Order( $order_id );
if ( $order_status == 'on-hold' || $order_status == 'pending' )
return 'processing';
}
Any ideas where I’m going wrong?
Many thanks,
Jamie
am using a theme called Bounce by ghostpool.com, am wanting to add this code to the theme functions… just trying to understand why the redirect is to
themes.php?page=theme-options.php
Redirect to Theme Options after Activation
Does this mean the user upon successful sale is redirected to the theme options page?
No, you’re probably talking about the WP Admin theme options page that can be displayed by certain themes when you activate them. This will have nothing to do with the thank-you page a customer sees after checking out
Think I might be doing something wrong? Every time I put the code in my ‘functions.php’ it just makes the code appear at the top of my website for everyone to see.
Just make sure that the code comes after the PHP opening tag: <?php
Great! worked perfectly for me
really nice work!!
Awesome!
This process seems to have broken after an update to WooCommerce 2.0. It doesn’t seem to be passing $order_id. I’m digging under the hood now, but wanted to see if you have run in to this as well and if you had a solution.
Thanks.
Hey John, I just took a look at the WooCommerce code and it’s definitely still passing the order id; must be something else going on in your environment? Did you get this working?
Yes, I’m in the same boat as John. Marking orders as complete is no longer working for me. I’m still looking for a solution as this functionality was great to have. Doh!
Yeah, sorry about this, this was long overdue, I’ve updated the sample code to be WooCommerce 2.0+ compatible (and no longer compatible with WC 1.6.6 and earlier)
Also not working for me on WooCommerce 2.0. In my case I use payment method “COD” as its a free download, I just want them to go through the checkout process and get download immediately. It’s Virtual and Downloadable order. The order remains in status “On Hold”. Should I perhaps try another payment method like Bank Transfer?
Hey Marc, my code will only work for a payment gateway that processes payment. You’d need a different implementation to make this work with one of the “fake” gateways like COD or bank transfer
Hello there,
Im trying to get your code to work for me. im using variables products though, so i cant seem to get this to work?
Thanks in advanges
Hey Mark, I have updated the code to be WC 2.0 compatible, and tested with a variable virtual product and it is now working. Cheers!
Hi Justin,
I have applied this a while ago and it worked great until today. I haven’t updated the woocommerce plugin but i have updated the Follow Up Emails for woocommerce plugin because welcome emails were not going out, could this have effected it?
If not, how would I go about diagnosing why it is not working.
Thanks again!
Hey, it definitely was broken for WC 2.0, I know you said you didn’t upgrade, but I guess I’d recommend upgrading now if you haven’t already and then trying out the updated code that I just posted
Hi Justin,
I recently upgraded to WC 2.0 and have found that this function has stopped working as before. All my orders (virtual or not) are being marked as completed now. I have looked at the code and can’t see any obvious reasons why this has stopped working. (FYI, this seems to be impacting variable products.)
Any help would be really appreciated.
Cheers.
Joe
Joe
Hey Joe, I have updated the code and this should now be working with WC 2.0
THANK YOU kind sir. I was able to easily modify this code to work for my specific needs.
I used the same hook to submit data to a CRM to manage my customers better.
Thanks again!
Hope you can give me an idea on where to start troubleshooting this — the code works – virtual orders are being marked as complete.
However, I’m testing a Simple product which is *not* marked as virtual, and it too is being marked complete upon successful purchase. Hrm.
If I remove the code, then virtual products and simple products return to default functionality, and show up as pending.
Any idea on where to start looking?
Somehow the code is returning that all purchase are virtual no matter what.
I can’t see where I can put in another conditional to bail out of the script if the product is not virtual… as that test is already right there.
Stumped. I’ve checked and double checked that the product is just a regular old physical product, no downloads no nothing — shipping rates show up, tax shows up… ????
Face –> Palm.
Just read the last comment before the one I posted and see that I had old version of the code from somewhere else.
New version = working.
Thank you !!!
ha ha ok cool, I just tested the code in my dev environment and it worked fine so I was about to ask whether you had the latest. You beat me to it!
Will this help fix my issue?, I can seem to go through the whole process of adding a virtual product…in my case a mp3 file, but it wont show the download links on the order page or even in the email woocommerce sends out. I managed to get it working for a pretend user I created but I had to manually go in as admin to the order page and grant access to that file (product) so it shows up under the “My Account” page when I log back in as a user. I am very confused. I have been searching google and ended up here. any help would be great!
I think it’s basically not auto approving it somehow. I am just paying myself between 2 of my pay pal accounts and the transactions are working. Just the available download links are not working.
Many thanks.
Jeramiah
Hey Jeramiah, sounds like the orders aren’t being completed properly. You’ll want to make sure PayPal is sending the payment notifications correctly by looking at the order notes for the order. If you don’t see “PayPal IPN Completed”, then you’ll need to look into that.