WooCommerce successful order hook - php

I am trying to send an email on success order so I was using until now the woocommerce_thankyou hook which seems to work perfect. But I found out that it is triggerred even when a customer tries to pay to external payment gateway (credit card payment), even though the payment is not accepted by the bank.
Which hook can I use in order to cover all these cases?
Bank Transfer, COD, Credit cart (only in succesfull payment)?

On successful paid orders for all payment gateways others than Bank wire, cheque or Cash on delivery, you can use dedicated woocommerce_payment_complete hook located in WC_Order payment_complete() method instead of more generic hook woocommerce_thankyou, like:
add_action( 'woocommerce_payment_complete', 'action_payment_complete', 10, 2 );
function action_payment_complete( $order_id, $order ) {
// Here add your code
}
Note that you can use defined $order_id and $order function arguments. Also this hook is only triggered once, avoiding repetitions.
For Bank wire (bacs), Cheque (cheque) or Cash on delivery (cod) payment methods, As the shop manager confirm manually that order is paid by changing order status, you can use the dedicated hook woocommerce_order_status_changed as follows.
add_action( 'woocommerce_order_status_changed', 'bacs_cheque_cod_payment_complete', 10, 4 );
function bacs_cheque_cod_payment_complete( $order_id, $old_status, $new_status, $order ) {
// 1. For Bank wire and cheque payments
if( in_array( $order->get_payment_method(), array('bacs', 'cheque')
&& in_array( $new_status, array('processing', 'completed')
&& ! $order->get_date_paid('edit') ) {
// Do something
}
// 2. For Cash on delivery payments
if( 'cod' === $order->get_payment_method() && 'completed' === $new_status ) {
// Do something
}
}
Note that you can use defined $order_id and $order function arguments. Also this hook will be triggered once, on order status change, avoiding repetitions.
Related: After a successful payment, What hook is triggered in Woocommerce

Related

WooCommerce: Order status based on Payment methods

On Woocommerce we have the option for BACS payments. Some orders are coming through as "Paid" and some aren't. I can't understand why as they are using the exact same payment method. The two images below will show you this:
Paid:
Not Paid:
We are using a function to automatically change these payments from "On Hold" to "Processing", in case this may have something to do with the issue. The code is below:
add_action( 'woocommerce_thankyou', 'bacs_order_payment_processing_order_status', 10, 1 );
function bacs_order_payment_processing_order_status( $order_id ) {
if ( ! $order_id ) {
return;
}
// Get an instance of the WC_Order object
$order = new WC_Order( $order_id );
if ( ( get_post_meta($order->id, '_payment_method', true) == 'bacs' || 'cod' ) && ('on-hold' == $order->status || 'pending' == $order->status) ) {
$order->update_status('processing');
}
else {
return;
}
}
// change COD payment method order status from processing to on-hold
add_action('woocommerce_thankyou_cod', 'action_woocommerce_thankyou_cod', 10, 1);
function action_woocommerce_thankyou_cod($order_id)
{
$order = wc_get_order($order_id);
$order->update_status('processing');
}
Any help would be massively appreciated!
Your code is very outdated, confused with mistakes…
Instead, to change order status for payments like:
Cash on delivery cod use (default status is "processing"):
Change COD default order status to "On Hold" instead of "Processing" in Woocommerce
Cash on Cheque cheque use *(default status is "on-hold"):
Change default WooCommerce order status to processing for cheque and bacs payments
bank wire bacs use *(default status is "on-hold"):
WooCommerce change order status BACS processing
Change default WooCommerce order status to processing for cheque and bacs payments
For other payment gateways use (default order status is related to each payment gateway and if there are shippable items or not):
WooCommerce: Auto complete paid orders
Auto process paid orders instead of auto complete in WooCommerce

Auto complete orders when a renewal successful payment is made on a subscription in WooCommerce

I am using WooCommerce Subscriptions plugin for subscriptions and it is recurring the order on subscription renewal and what I need is that when the order recurring order is created after successful subscription renewal payment the order status should changed to completed.
I have tried to use unsuccessfully the following hooks:
woocommerce_renewal_order_payment_complete
woocommerce_order_status_changed
woocommerce_payment_complete
For WooCommerce Subscriptions you need to use woocommerce_subscription_payment_complete action hook, that is triggered when a renewal payment is made on a subscription.
You can try the following that will update the current order status to completed:
add_action('woocommerce_subscription_payment_complete', 'subscription_payment_complete_hook_callback', 10, 1);
function subscription_payment_complete_hook_callback( $subscription ) {
// Get the current order
$current_order = $subscription->get_last_order( 'all', 'any' );
// For Paypal recurring orders
if( $current_order->get_payment_method() === 'paypal' ) {
// Update status to completed
$current_order->update_status('completed');
}
}
Related documentation: WooCommerce Subscriptions action hooks
I believe for renewals the hook that should be used is: woocommerce_subscription_renewal_payment_complete
This also accepts the $subscription parameter
In 2021, as WooCommerce Subscription API has changed, this seems to work:
/*Auto Complete all WooCommerce orders.*/
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_complete_order' );
function custom_woocommerce_auto_complete_order( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
$order->update_status( 'completed' );
}
/*Auto Complete all WooCommerce Renewal Subscription orders.*/
add_action('woocommerce_subscription_renewal_payment_complete', 'subscription_renewal_payment_complete_callback', 10, 2);
function subscription_renewal_payment_complete_callback( $subscription, $order ) {
$order->update_status('completed');
}

Automark ONLY Paid orders to "Completed" status in WooCommerce 3+

I would like to automark just success paid orders to "Completed" status. I have searched a lot on Stack and Google, and found this answer code:
WooCommerce: Auto complete paid Orders (depending on Payment methods)
But problem is that the code mark all placed orders to "Completed" status not depending if order was success placed or not.
What do I need to change in the code to automark ONLY Paid orders to "Completed" status?
New enhanced and simplified code version replacement (March 2019):
See: WooCommerce: Auto complete paid orders
Original answer:
For Paypal and other third party gateways, the "paid" order status to target is "processing" (and "completed"), so you can lightly change the code to:
add_action( 'woocommerce_thankyou', 'wc_auto_complete_paid_order', 20, 1 );
function wc_auto_complete_paid_order( $order_id ) {
if ( ! $order_id )
return;
// Get an instance of the WC_Product object
$order = wc_get_order( $order_id );
// No updated status for orders delivered with Bank wire, Cash on delivery and Cheque payment methods.
if ( in_array( $order->get_payment_method(), array( 'bacs', 'cod', 'cheque', '' ) ) ) {
return;
// Updated status to "completed" for paid Orders with all others payment methods
} elseif ( in_array( $order->get_status(), array('on-hold', 'processing') ) ) {
$order->update_status( 'completed' );
}
}
Code goes in function.php file of the active child theme (or active theme). tested and works.
This way you will avoid "failed", "Cancelled" or "Pending" orders.

Set WooCommerce order status when order is created from processing to pending

When a woocommerce order is created the status of the order is "processing". I need to change the default order-status to "pending".
How can I achieve this?
The default order status is set by the payment method or the payment gateway.
You could try to use this custom hooked function, but it will not work (as this hook is fired before payment methods and payment gateways):
add_action( 'woocommerce_checkout_order_processed', 'changing_order_status_before_payment', 10, 3 );
function changing_order_status_before_payment( $order_id, $posted_data, $order ){
$order->update_status( 'pending' );
}
Apparently each payment method (and payment gateways) are setting the order status (depending on the transaction response for payment gateways)…
For Cash on delivery payment method, this can be tweaked using a dedicated filter hook, see:
Change Cash on delivery default order status to "On Hold" instead of "Processing" in Woocommerce
Now instead you can update the order status using woocommerce_thankyou hook:
add_action( 'woocommerce_thankyou', 'woocommerce_thankyou_change_order_status', 10, 1 );
function woocommerce_thankyou_change_order_status( $order_id ){
if( ! $order_id ) return;
$order = wc_get_order( $order_id );
if( $order->get_status() == 'processing' )
$order->update_status( 'pending' );
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Tested and works
Note: The hook woocommerce_thankyou is fired each time the order received page is loaded and need to be used with care for that reason...
Now the function above will update the order status only the first time. If customer reload the page, the condition in the IF statement will not match anymore and nothing else will happen.
Related thread: WooCommerce: Auto complete paid Orders (depending on Payment methods)
Nowadays, if the payment gateway that you use properly sets the order status using WC_Order->payment_complete(), you can use the woocommerce_payment_complete_order_status filter.
This is better than using woocommerce_thankyou hook since we are setting the order status immediately, rather than applying it after it has been already set.
function h9dx3_override_order_status($status, $order_id, $order) {
if ($status === 'processing') {
$status = 'pending';
}
return $status;
}
add_filter('woocommerce_payment_complete_order_status', 'h9dx3_override_order_status', 10, 3);
Again, this will only work if the payment gateway uses the proper payment_complete wrapper method rather than setting status directly with set_status. You can just search the gateway code for 'payment_complete(' and 'set_status(' to see what it does.
If you develop a plugin for everyone, you will be better off with using woocommerce_thankyou, or you could use a combined approach and use woocommerce_thankyou as the fallback if the order status was not updated.
The hook woocommerce_thankyou suffers from the problem that you can pay for an order and then close the browser or go somewhere else and therefore never click "Return to Merchant" (which is displayed after paying via paypal) to get to the thankyou page. And then the code will never be executed.
// Rename order status 'Processing' to 'Order Completed' in admin main view - different hook, different value than the other places
add_filter( 'wc_order_statuses', 'wc_renaming_order_status' );
function wc_renaming_order_status( $order_statuses ) {
foreach ( $order_statuses as $key => $status ) {
if ( 'wc-processing' === $key )
$order_statuses['wc-processing'] = _x( 'Order Completed', 'Order status', 'woocommerce' );
}
return $order_statuses;
}

WooCommerce: Auto complete paid orders

Normally wooCommerce should autocomplete orders for virtual products. But it doesn't and this is a real problem, even a BUG like.
So at this point you can find somme helpful things(but not really convenient):
1) A snippet code (that you can find in wooCommerce docs):
/**
* Auto Complete all WooCommerce orders.
*/
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_complete_order');
function custom_woocommerce_auto_complete_order( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
$order->update_status( 'completed' );
}
But this snippet does not work for BACS*, Pay on delivery and Cheque payment methods. It's ok for Paypal and Credit Card gateways payment methods.
*BACS is a Direct Bank transfer payment method
And …
2) A plugin: WooCommerce Autocomplete Orders
This plugin works for all payment methods, but not for other Credit Card gateways payment methods.
My question:
Using (as a base) the wooCommerce snippet in point 1:
How can I implement conditional code based on woocommerce payment methods?
I mean something like: if the payment methods aren't "BACS", "Pay on delivery" and "Cheque" then apply the snippet code (update status to "completed" for paid orders concerning virtual products).
Some help will be very nice.
The most accurate, effective and lightweight solution (For WooCommerce 3 and above) - 2019
This filter hook is located in:
WC_Order Class inside payment_complete() method which is used by all payment methods when a payment is required in checkout.
WC_Order_Data_Store_CPT Class inside update() method.
As you can see, by default the allowed paid order statuses are "processing" and "completed".
###Explanations:
Lightweight and effective:
As it is a filter hook, woocommerce_payment_complete_order_status is only triggered when an online payment is required (not for "cheque", "bacs" or "cod" payment methods). Here we just change the allowed paid order statuses.
So no need to add conditions for the payment gateways or anything else.
Accurate (avoid multiple notifications):
This is the only way to avoid sending 2 different customer notifications at the same time:
• One for "processing" orders status
• And one for "completed" orders status.
So customer is only notified once on "completed" order status.
Using the code below, will just change the paid order status (that is set by the payment gateway for paid orders) to "completed":
add_action( 'woocommerce_payment_complete_order_status', 'wc_auto_complete_paid_order', 10, 3 );
function wc_auto_complete_paid_order( $status, $order_id, $order ) {
return 'completed';
}
Code goes in function.php file of the active child theme (or active theme).
Related: WooCommerce: autocomplete paid orders based on shipping method
2018 - Improved version (For WooCommerce 3 and above)
Based on Woocommerce official hook, I found a solution to this problem *(Works with WC 3+).
In Woocommerce for all other payment gateways others than bacs (Bank Wire), cheque and cod (Cash on delivery), the paid order statuses are "processing" and "completed".
So I target "processing" order status for all payment gateways like Paypal or credit card payment, updating the order status to complete.
The code:
add_action( 'woocommerce_thankyou', 'wc_auto_complete_paid_order', 20, 1 );
function wc_auto_complete_paid_order( $order_id ) {
if ( ! $order_id )
return;
// Get an instance of the WC_Product object
$order = wc_get_order( $order_id );
// No updated status for orders delivered with Bank wire, Cash on delivery and Cheque payment methods.
if ( in_array( $order->get_payment_method(), array( 'bacs', 'cod', 'cheque', '' ) ) ) {
return;
}
// For paid Orders with all others payment methods (paid order status "processing")
elseif( $order->has_status('processing') ) {
$order->update_status( 'completed' );
}
}
Code goes in function.php file of the active child theme (or active theme).
Original answer (For all woocommerce versions):
The code:
/**
* AUTO COMPLETE PAID ORDERS IN WOOCOMMERCE
*/
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_complete_paid_order', 10, 1 );
function custom_woocommerce_auto_complete_paid_order( $order_id ) {
if ( ! $order_id )
return;
$order = wc_get_order( $order_id );
// No updated status for orders delivered with Bank wire, Cash on delivery and Cheque payment methods.
if ( ( 'bacs' == get_post_meta($order_id, '_payment_method', true) ) || ( 'cod' == get_post_meta($order_id, '_payment_method', true) ) || ( 'cheque' == get_post_meta($order_id, '_payment_method', true) ) ) {
return;
}
// For paid Orders with all others payment methods (with paid status "processing")
elseif( $order->get_status() === 'processing' ) {
$order->update_status( 'completed' );
}
}
Code goes in function.php file of the active child theme (or active theme).
With the help of this post: How to check payment method on a WooCommerce order by id?
with this : get_post_meta( $order_id, '_payment_method', true ); from helgatheviking
"Bank wire" (bacs), "Cash on delivery" (cod) and "Cheque" (cheque) payment methods are ignored and keep their original order status.
Updated the code for compatibility with WC 3.0+ (2017-06-10)
For me this hook was called even if the payment did not go through or failed, and this resulted to completed failed payments. After some research I changed it to use 'woocommerce_payment_complete' because it's called only when payment is complete and it covers the issue that #LoicTheAztec mentions above –
add_action( 'woocommerce_payment_complete', 'wc_auto_complete_paid_order', 20, 1 );
function wc_auto_complete_paid_order( $order_id ) {
if ( ! $order_id )
return;
// Get an instance of the WC_Product object
$order = wc_get_order( $order_id );
// No updated status for orders delivered with Bank wire, Cash on delivery and Cheque payment methods.
if ( in_array( $order->get_payment_method(), array( 'bacs', 'cod', 'cheque', '' ) ) ) {
return;
// Updated status to "completed" for paid Orders with all others payment methods
} else {
$order->update_status( 'completed' );
}
}
For me, the simplest hook to modify order status when payment is completed is 'woocommerce_order_item_needs_processing' as this filter hook is meant to modify the target order status when payment completes.
This is what the final snippet will look alike:
add_filter('woocommerce_order_item_needs_processing', '__return_false',999);
It also compatible with the other plugins on sites.
For me, on a testing system with PayPal Sandbox (WooCommerce PayPal Payments plugin) the LoicTheAztec solution (2019 update) worked only when I added the $order->update_status( 'completed' ); code line. The return 'completed'; has not an effect in my case, I left it just because it's a filter.
add_filter( 'woocommerce_payment_complete_order_status', function( $status, $order_id, $order ) {
$order->update_status( 'completed' );
return 'completed';
}, 10, 3 );
If you are looking for autocompletion of virtual orders (like courses, ebooks, downloadables etc), this might be useful.
* Auto Complete all WooCommerce virtual orders.
*
* #param int $order_id The order ID to check
* #return void
*/
function custom_woocommerce_auto_complete_virtual_orders( $order_id ) {
// if there is no order id, exit
if ( ! $order_id ) {
return;
}
// No updated status for orders delivered with Bank wire, Cash on delivery and Cheque payment methods.
if ( in_array( $order->get_payment_method(), array( 'bacs', 'cod', 'cheque', '' ) ) ) {
return;
}
// get the order and its exit
$order = wc_get_order( $order_id );
$items = $order->get_items();
// if there are no items, exit
if ( 0 >= count( $items ) ) {
return;
}
// go through each item
foreach ( $items as $item ) {
// if it is a variation
if ( '0' != $item['variation_id'] ) {
// make a product based upon variation
$product = new WC_Product( $item['variation_id'] );
} else {
// else make a product off of the product id
$product = new WC_Product( $item['product_id'] );
}
// if the product isn't virtual, exit
if ( ! $product->is_virtual() ) {
return;
}
}
/*
* If we made it this far, then all of our items are virual
* We set the order to completed.
*/
$order->update_status( 'completed' );
}
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_complete_virtual_orders' );
Adapted from https://gist.github.com/jessepearson/33f383bb3ea33069822817cfb1da4258

Categories