Change The Orders Default Status By Gateway in WooCommerce

Hey guys, today i’d like to share a little method that could be interesting for many of you. I’d like to show you how to change the WooCommerce orders default status by payment gateways. For example, when you make a purchase and use the BACS payment (bank transfer), the default order status is “on hold”, but you could choose to automatically mark the status as “processing” instead. The idea is to not modify cores files (and basically I really recommend to never modify core files, never), but to create a little class and use a filter.

The function that lists default payment gateways in WooCommerce is core_gateways(). This function is hooked to a filter called woocommerce_payment_gateways. So, the first step is to remove that filter and add our own. I will work only in the functions.php file within the theme folder (remember? Never modify core files). To do so, we’ll use the remove_filter() and the add_filter() functions:

Now that we have removed the filter, you can see that in the add_filter() function we have a callback named my_core_gateways. This callback is the name of a function that will replace the default core_gateways() function. This function is the one that list WooCommerce default payment gateways. I will change the content of that function and replace the call to the WC_Gateway_BACS class. This class is the bank transfer default class. Here is the code of that new function:

As you can see the only change I made is that I replaced WC_Gateway_BACS by WC_Gateway_BACS_custom.

Are you still with me huh? Well, to summarize, I need to remove the filter that calls the default payment gateways, and use a custom function. In this custom function, I replace the call to the BACS class, and now i need to create this new BACS class. To do so, use that code:

In this snippet, I only changed the default status from “on-hold” to “processing”…. and boom the magic appears! Now each order paid using the BACS payment gateway will be marked as processing, not as on hold.

21 responses to “Change The Orders Default Status By Gateway in WooCommerce”

  1. rahy

    A good alternative to automatically complete order to all order.
    This is usefull for my need.
    Now, to accompany this change, is it possible to make the coupon code mandatory? So every purchase will need a coupon code.
    Thanks

  2. orgspring

    This is awesome, thanks Remi.
    Does having the order listed as “pending” or “processing” allow the user to cancel through his or her own account page.

    I think “On hold” doesn’t make that available. Is that correct or wrong?

    Thanks.

    Craig

  3. ewoud5

    Hi Remi,
    thank you for this!
    I had a little question: I think it is weird that cancelled orders in woocommerce can’t be paid afterwards when a customer changes his mind. I mean, an e-commerce suite is about sales after all… It saves the cart contents but doesn’t allow for payment….

    Do you have any idea where this is handled/assigned? All I am looking for is to give the cancelled orders the same payment status as the pending orders for basket retrieval by the customer.

    Thanks in advance,
    e

  4. LarsJ3

    Thanks for a great little workaround. I, unfortunately, get the error that a customer is redirected to an empty cart, right after ordering. And therefore the customer are not taken to the reciept for the buy.
    Do you have any solution to that?

  5. MauriceW

    Hi Remi,

    Great work, however I also get the empty cart like LarsJ3 after the order has been submitted. The order itself is registered in the backend and the status is also set to ‘processing’. Can you please tell us how to fix this?

    Maurice

  6. josefwb

    Really interesting stuff.
    I give that a try.
    I have a special issue in the area. In the bacs class there is an area which defines the bank account information in the emails. and will be called with woocommerce_email_before_order_table I try to customize it in following way:

    change the list in table format and add account_name and bank_name but I failed.
    I’m not very familiar with php code.

    Following I tried to use in function.php:

    ` // Change Bacs for email
    add_action( ‘woocommerce_email_before_order_table’, array( $this, ’email_instructions’ ), 10, 3 );
    }

    function email_instructions( $order, $sent_to_admin, $plain_text = false ) {

    if ( $sent_to_admin || $order->status !== ‘on-hold’ || $order->payment_method !== ‘bacs’ ) {
    return;
    }

    if ( $this->instructions ) {
    echo wpautop( wptexturize( $this->instructions ) ) . PHP_EOL;
    }

    $this->bank_details( $order->id );
    }

    /**
    * Get bank details and place into a list format
    */
    function bank_details( $order_id = ” ) {
    if ( empty( $this->account_details ) ) {
    return;
    }

    echo ” . __( ‘Our Bank Details’, ‘woocommerce’ ) . ” . PHP_EOL;

    $bacs_accounts = apply_filters( ‘woocommerce_bacs_accounts’, $this->account_details );

    if ( ! empty( $bacs_accounts ) ) {
    foreach ( $bacs_accounts as $bacs_account ) {

    $bacs_account = (object) $bacs_account;

    // BACS account fields shown on the thanks page and in emails
    $account_fields = apply_filters( ‘woocommerce_bacs_account_fields’, array(
    ‘account_name’=> array(
    ‘label’ => __( ‘Account Name’, ‘woocommerce’ ),
    ‘value’ => $bacs_account->account_name
    ),
    ‘bank_name’=> array(
    ‘label’ => __( ‘Bank’, ‘woocommerce’ ),
    ‘value’ => $bacs_account->bank_name
    ),
    ‘account_number’=> array(
    ‘label’ => __( ‘Account Number’, ‘woocommerce’ ),
    ‘value’ => $bacs_account->account_number
    ),
    ‘sort_code’ => array(
    ‘label’ => __( ‘Sort Code’, ‘woocommerce’ ),
    ‘value’ => $bacs_account->sort_code
    ),
    ‘iban’ => array(
    ‘label’ => __( ‘IBAN’, ‘woocommerce’ ),
    ‘value’ => $bacs_account->iban
    ),
    ‘bic’ => array(
    ‘label’ => __( ‘BIC’, ‘woocommerce’ ),
    ‘value’ => $bacs_account->bic
    )
    ), $order_id );

    echo ”

    foreach ( $account_fields as $field_key => $field ) {
    if ( ! empty( $field[‘value’] ) ) {
    echo ”
    echo ” . esc_attr( $field[‘label’] ) . ‘: ‘;
    echo ‘‘ . wptexturize( $field[‘value’] ) . ‘‘;
    echo ”
    }
    }

    echo ”;
    }
    }
    }
    `

    Do you have any idea?

    Thanks Josef

  7. josefwb

    Hi Remi,

    I played a little around and came a step nearer to my target, but the table is not shown.
    I have put the code in childtheme\woocommerce\templates\emails\customer-processing-order.php instead of

    Can you help here? See the code below

    account_details );

    if ( ! empty( $bacs_accounts ) ) {
    foreach ( $bacs_accounts as $bacs_account ) {
    echo '' . PHP_EOL;

    $bacs_account = (object) $bacs_account;

    // BACS account fields shown on the thanks page and in emails
    $account_fields = apply_filters( 'woocommerce_bacs_account_fields', array(
    'account_name'=> array(
    'label' => __( 'Account Name', 'woocommerce' ),
    'value' => $bfw_bacs_account->account_name
    ),
    'bank_name'=> array(
    'label' => __( 'Bank Name', 'woocommerce' ),
    'value' => $bfw_bacs_account->bank_name
    ),
    'account_number'=> array(
    'label' => __( 'Account Number', 'woocommerce' ),
    'value' => $bacs_account->account_number
    ),
    'sort_code' => array(
    'label' => __( 'Sort Code', 'woocommerce' ),
    'value' => $bacs_account->sort_code
    ),
    'iban' => array(
    'label' => __( 'IBAN', 'woocommerce' ),
    'value' => $bacs_account->iban
    ),
    'bic' => array(
    'label' => __( 'BIC', 'woocommerce' ),
    'value' => $bacs_account->bic
    )
    ), $order_id );

    foreach ( $bfw_account_fields as $bfw_field_key => $bfw_field ) {
    if ( ! empty( $bfw_field['value'] ) ) {
    echo '';
    echo '' . esc_attr( $bfw_field['label'] ) . ': ';
    echo '' . wptexturize( $bfw_field['value'] ) . '';
    echo '' . PHP_EOL;
    }
    }

    echo '';
    }
    }
    ?>

    Thanks Josef

  8. josefwb

    Sorry the code was not shown correct:

    I have put the code in childtheme\woocommerce\templates\emails\customer-processing-order.php instead of

    Changed code in the email template:

    account_details );

    if ( ! empty( $bacs_accounts ) ) {
    foreach ( $bacs_accounts as $bacs_account ) {
    echo '' . PHP_EOL;

    $bacs_account = (object) $bacs_account;

    // BACS account fields shown on the thanks page and in emails
    $account_fields = apply_filters( 'woocommerce_bacs_account_fields', array(
    'account_name'=> array(
    'label' => __( 'Account Name', 'woocommerce' ),
    'value' => $bfw_bacs_account->account_name
    ),
    'bank_name'=> array(
    'label' => __( 'Bank Name', 'woocommerce' ),
    'value' => $bfw_bacs_account->bank_name
    ),
    'account_number'=> array(
    'label' => __( 'Account Number', 'woocommerce' ),
    'value' => $bacs_account->account_number
    ),
    'sort_code' => array(
    'label' => __( 'Sort Code', 'woocommerce' ),
    'value' => $bacs_account->sort_code
    ),
    'iban' => array(
    'label' => __( 'IBAN', 'woocommerce' ),
    'value' => $bacs_account->iban
    ),
    'bic' => array(
    'label' => __( 'BIC', 'woocommerce' ),
    'value' => $bacs_account->bic
    )
    ), $order_id );

    foreach ( $bfw_account_fields as $bfw_field_key => $bfw_field ) {
    if ( ! empty( $bfw_field['value'] ) ) {
    echo '';
    echo '' . esc_attr( $bfw_field['label'] ) . ': ';
    echo '' . wptexturize( $bfw_field['value'] ) . '';
    echo '' . PHP_EOL;
    }
    }

    echo '';
    }
    }
    ?>

    Thanks Josef

    1. josefwb

      childtheme\woocommerce\templates\emails\customer-processing-order.php instead of

      Can you help here? See the code below

      account_details );

      if ( ! empty( $bacs_accounts ) ) {
      foreach ( $bacs_accounts as $bacs_account ) {
      echo ” . PHP_EOL;

      $bacs_account = (object) $bacs_account;

      // BACS account fields shown on the thanks page and in emails
      $account_fields = apply_filters( ‘woocommerce_bacs_account_fields’, array(
      ‘account_name’=> array(
      ‘label’ => __( ‘Account Name’, ‘woocommerce’ ),
      ‘value’ => $bfw_bacs_account->account_name
      ),
      ‘bank_name’=> array(
      ‘label’ => __( ‘Bank Name’, ‘woocommerce’ ),
      ‘value’ => $bfw_bacs_account->bank_name
      ),
      ‘account_number’=> array(
      ‘label’ => __( ‘Account Number’, ‘woocommerce’ ),
      ‘value’ => $bacs_account->account_number
      ),
      ‘sort_code’ => array(
      ‘label’ => __( ‘Sort Code’, ‘woocommerce’ ),
      ‘value’ => $bacs_account->sort_code
      ),
      ‘iban’ => array(
      ‘label’ => __( ‘IBAN’, ‘woocommerce’ ),
      ‘value’ => $bacs_account->iban
      ),
      ‘bic’ => array(
      ‘label’ => __( ‘BIC’, ‘woocommerce’ ),
      ‘value’ => $bacs_account->bic
      )
      ), $order_id );

      foreach ( $bfw_account_fields as $bfw_field_key => $bfw_field ) {
      if ( ! empty( $bfw_field[‘value’] ) ) {
      echo ”;
      echo ” . esc_attr( $bfw_field[‘label’] ) . ‘: ‘;
      echo ‘‘ . wptexturize( $bfw_field[‘value’] ) . ‘‘;
      echo ” . PHP_EOL;
      }
      }

      echo ”;
      }
      }
      ?>

  9. josefwb

    I’m not able to put the code correct in the post.
    here the Github link:

    Thanks

  10. GreenPeakLabs

    I’m looking to do something similar but slightly different.

    In my case, I am using the Bookings + Force Sell + Pre-Order plugins to pre-authorize a deposit (separate product that is forced) on a bookable product. Everything works great from that perspective, but the final piece is that I need to send text reminders 24 hours before the booked time to encourage people to tell us if they have to cancel.

    In order to do that, I’ve installed Twilio and set up a custom text reminder on one custom order status. My question is: what is the best way to programmatically change the order status based on info capture within a bookable product? If I can run a script that says “If status = pre-order & booking book time date < 24, update order status to textReminderSent" a couple times a day, I'd be golden …

  11. alby54

    Hi, can this script be applied to custom order statuses as well? Since WC does not allow users to cancel their own orders, I’m using a plugin (WooCommerce Custom Order Statuses & Actions) that creates custom statuses and adds a cancellation option.

    1. Yes that will work as well!

  12. LarsJ3

    Hi Remi,

    I am still having the problem, that the customers are shown an empty cart, instead of the ‘thank you’ page. The workaround works perfect, except for the mentioned little problem.

  13. tabboy

    I’m getting an error saying “Class ‘WC_Gateway_COD’ not found”. I’m trying to use this code in my plugin so what classes from WooCommerce do I need to include in my plugin and how do I code this in PHP ?

  14. defunktnu_stijn

    As a reply to josefwb, you need to check for another staus on email_instructions, you also need add the bank_details function, as it’s a private function in the parent class: https://gist.github.com/dendeffe/653b2c3277cbb1cda07d

  15. paul

    Remi,

    thanks for this post – it’s just what I needed. I did however notice that instead of sending the order confirmation email which contains all my BACS details, it just sends out what I assume is the default order confirmation email (ie. without all the BACS information).

    Anyway I might edit the function to change this?

    Thanks very much for any guidance.

  16. paulb

    Hey, Great Code Snippet.

    Was using it fine until Woocommerce change the permalink to the order received page and replaced it with an Endpoint.

    For anyone having problem need to update the redirect returned.

    Replace: get_permalink(woocommerce_get_page_id(‘thanks’))
    With: get_checkout_order_received_url()

    In:

    class WC_Gateway_BACS_custom extends WC_Gateway_BACS {
    function process_payment( $order_id ) {
    ........

    // Return thankyou redirect
    return array(
    'result' => 'success',
    'redirect' => add_query_arg('key', $order->order_key, add_query_arg('order', $order->id, $order->get_checkout_order_received_url()))
    );

    Cheers

    Paul

  17. upendrasharma221

    Hi Remi,

    I used what you mentioned in your post. I pasted the code in funtions.php file but still when payment is done using paypal, order status is not changing from pending to processing, it is stuck to pending. I pasted this code into functions.php file to the bottom :

    remove_filter( ‘woocommerce_payment_gateways’, ‘core_gateways’ );
    add_filter( ‘woocommerce_payment_gateways’, ‘my_core_gateways’ );

    /**
    * core_gateways function modified.
    *
    * @access public
    * @param mixed $methods
    * @return void
    */
    function my_core_gateways( $methods ) {
    $methods[] = ‘WC_Gateway_BACS_custom’;
    $methods[] = ‘WC_Gateway_Cheque’;
    $methods[] = ‘WC_Gateway_COD’;
    $methods[] = ‘WC_Gateway_Paypal’;
    return $methods;
    }

    class WC_Gateway_BACS_custom extends WC_Gateway_BACS {

    /**
    * Process the payment and return the result
    *
    * @access public
    * @param int $order_id
    * @return array
    */
    function process_payment( $order_id ) {
    global $woocommerce;

    $order = new WC_Order( $order_id );

    // Mark as processing (that’s what we want to change!)
    $order->update_status(‘processing’, __( ‘Awaiting BACS payment’, ‘woocommerce’ ));

    // Reduce stock levels
    $order->reduce_order_stock();

    // Remove cart
    $woocommerce->cart->empty_cart();

    // Return thankyou redirect
    return array(
    ‘result’ => ‘success’,
    ‘redirect’ => add_query_arg(‘key’, $order->order_key, add_query_arg(‘order’, $order->id, get_permalink(woocommerce_get_page_id(‘thanks’))))
    );
    }

    }

    can you tell me where i went gone wrong or how to get the status change from pending to processing ?

Leave a Reply