Skip to content

Instantly share code, notes, and snippets.

@willgorham
Last active December 29, 2023 02:06
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save willgorham/874c4ac943fc27443cd862a93764d659 to your computer and use it in GitHub Desktop.
Save willgorham/874c4ac943fc27443cd862a93764d659 to your computer and use it in GitHub Desktop.
WooCommerce: Automatically complete virtual orders
<?php
add_filter( 'woocommerce_payment_complete_order_status', 'wmg_auto_complete_virtual_orders', 10, 3 );
/**
* Automatically complete orders with only virtual products
*
* @param string $payment_complete_status Order status used after an order payment is received
* @param int $order_id ID of the order being processed
* @param WC_Order $order Order object being processed
* @return string $payment_complete_status Updated order status
*/
function wmg_auto_complete_virtual_orders( $payment_complete_status, $order_id, $order ) {
$current_status = $order->get_status();
// We only want to update the status to 'completed' if it's coming from one of the following statuses:
$allowed_current_statuses = array( 'on-hold', 'pending', 'failed' );
if ( 'processing' === $payment_complete_status && in_array( $current_status, $allowed_current_statuses ) ) {
$order_items = $order->get_items();
// Create an array of products in the order
$order_products = array_filter( array_map( function( $item ) {
// Get associated product for each line item
return $item->get_product();
}, $order_items ), function( $product ) {
// Remove non-products
return !! $product;
} );
if ( count( $order_products ) > 0 ) {
// Check if each product is 'virtual'
$is_virtual_order = array_reduce( $order_products, function( $virtual_order_so_far, $product ) {
return $virtual_order_so_far && $product->is_virtual();
}, true );
if ( $is_virtual_order ) {
$payment_complete_status = 'completed';
}
}
}
return $payment_complete_status;
}
@willgorham
Copy link
Author

Note: there's a current bug in WooCommerce subscriptions that doesn't accept the use of 3 parameters for this function. If you're using WCS, change your code like so:

add_filter( 'woocommerce_payment_complete_order_status', 'wmg_auto_complete_virtual_orders', 10, 2 ); // Note 2 instead of 3
function wmg_auto_complete_virtual_orders( $payment_complete_status, $order_id ) { // Drop the 3rd parameter
  $order = wc_get_order( $order_id ); // Get your order object here

  // The rest of the code is the same...

@crstauf
Copy link

crstauf commented Jul 30, 2018

Note: issue mentioned by @willgorham was fixed in WooCommerce Subscriptions v2.2.17.

@jonasskafte
Copy link

Hi there @willgorham,

Thanks for sharing this great snippet!

Just to let you know – I've just had a subscription order that didn't change status as expected. It just went from 'Pending payment' to 'On-Hold' and so it seems that the snippet didn't kick in 😞

Are anyone else experiencing this problem?

@warend2
Copy link

warend2 commented Apr 15, 2020

This is cool for virtual products. It would be great if you have also for physical products with autocomplete status if the shipment date matched or beyond.

Sorry for my English :)

@gbozzetti
Copy link

With PHP 7.4 it throws a warning on line 31: Warning: count(): Parameter must be an array or an object that implements Countable.

@Croolw
Copy link

Croolw commented Nov 17, 2021

Does anyone find the solution for the php 7.4 warning:
Warning: count(): Parameter must be an array or an object that implements Countable.
If so can you tell me?

@colinskuse
Copy link

im a bit of a newbe to code, Wordpress & WooCommerce. where do i need to place this code ?

@jsbls
Copy link

jsbls commented Jun 5, 2022

@colinskuse Your theme's functions.php file. Ideally, you should be using a Child Theme. You can find the functions.php file at wp-content/themes/your-theme.

If there's code there already, just paste the code at the very bottom or the very top. Make sure you remove the starting <?php tag in this script if there's already one in your file, though.

@nmbgeek
Copy link

nmbgeek commented Jun 26, 2023

Any suggestions for the error thrown below. This was working until some recent updates.

2023-06-16T01:25:05+00:00 CRITICAL Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array, bool given in /home/runcloud/webapps/bastore/wp-content/themes/oceanwp-child-theme-master/functions.php:192
Stack trace:
#0 /home/runcloud/webapps/bastore/wp-includes/class-wp-hook.php(308): auto_complete_virtual_orders()
#1 /home/runcloud/webapps/bastore/wp-includes/plugin.php(205): WP_Hook->apply_filters()
#2 /home/runcloud/webapps/bastore/wp-content/plugins/woocommerce/includes/class-wc-order.php(142): apply_filters()
#3 /home/runcloud/webapps/bastore/wp-content/plugins/woocommerce-square/includes/Framework/PaymentGateway/Payment_Gateway_Direct.php(329): WC_Order->payment_complete()
#4 /home/runcloud/webapps/bastore/wp-content/plugins/woocommerce/includes/class-wc-checkout.php(1050): WooCommerce\Square\Framework\PaymentGateway\Payment_Gateway_Direct->process_payment()
#5 /home/runcloud/webapps/bastore/wp-content/plugins/woocommerce/includes/class-wc-checkout.php(1279): WC_Checkout->process_order_payment()
#6 /home/runcloud/webapps/bastore/wp-content/plugins/woocommerce/includes/class-wc-ajax.php(485): WC_Checkout->process_checkout()
#7 /home/runcloud/webapps/bastore/wp-includes/class-wp-hook.php(308): WC_AJAX::checkout()
#8 /home/runcloud/webapps/bastore/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()
#9 /home/runcloud/webapps/bastore/wp-includes/plugin.php(517): WP_Hook->do_action()
#10 /home/runcloud/webapps/bastore/wp-content/plugins/woocommerce/includes/class-wc-ajax.php(96): do_action()
#11 /home/runcloud/webapps/bastore/wp-includes/class-wp-hook.php(308): WC_AJAX::do_wc_ajax()
#12 /home/runcloud/webapps/bastore/wp-includes/class-wp-hook.php(332): WP_Hook->apply_filters()
#13 /home/runcloud/webapps/bastore/wp-includes/plugin.php(517): WP_Hook->do_action()
#14 /home/runcloud/webapps/bastore/wp-includes/template-loader.php(13): do_action()
#15 /home/runcloud/webapps/bastore/wp-blog-header.php(19): require_once('...')
#16 /home/runcloud/webapps/bastore/index.php(17): require('...')
#17 {main}
  thrown in /home/runcloud/webapps/bastore/wp-content/themes/oceanwp-child-theme-master/functions.php on line 192

@daza110
Copy link

daza110 commented Aug 25, 2023

@Croolw @gbozzetti @nmbgeek

The line:
if ( count( $order_products > 0 ) ) {

Needs to be changed to:

if ( count( $order_products ) > 0 ) {

Note the correct placement of the close bracket on the count() function - this fixes the error you are all encountering

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment