How to Set WooCommerce Virtual Order Status to Complete After Payment

Don’t feel comfortable editing your functions.php? Worried about maintaining compatibility with new versions of WooCommerce? Try the WooCommerce Order Status Control extension to automatically complete virtual (or all!) orders! Learn More…

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