WooCommerce Subscriptions: Is there a hook for a manual payment retry? - php

I have Woocommerce orders that are pending payment. At the moment when an order hits the automatic retry payment schedule it pulls the payment information from the subscription, which works great as the customer may have corrected their wrong details by then.
But in some cases, we need to manually retry the payment. When this is done, it doesn't pull the payment information from the subscription (as the customer may have corrected their details for it to go through).
Is there a hook/action that I can use to fire the following code? woocommerce_subscriptions_before_payment_retry doesn't seem to work.
add_action('woocommerce_subscriptions_before_payment_retry', 'remove_payment_details', 10, 2 );
function remove_payment_details( $order_id ){
$order = wc_get_order( $order_id ); // Order Object
$subscriptions = wcs_get_subscriptions_for_order( $order_id, array( 'order_type' => 'any' ) ); // Array of subscriptions Objects
foreach( $subscriptions as $subscription_id => $subscription ){
$stripe_cust_id = $subscription->get_meta( '_stripe_customer_id');
$stripe_src_id = $subscription->get_meta( '_stripe_source_id' );
$order->update_meta_data( '_stripe_customer_id', $stripe_cust_id );
$order->update_meta_data( '_stripe_source_id', $stripe_src_id );
$order->save();
}
}

Please check woocommerce_order_action_wcs_retry_renewal_payment hook.
The hook is for "Retry Renewal Payment" handling, and you can use it.
add_action( 'woocommerce_order_action_wcs_retry_renewal_payment', 'custom_process_retry_renewal_payment_action_request', 20, 1 );
function custom_process_retry_renewal_payment_action_request( $order ) {
// your code is here
}

Related

Which Woocommerce hook when order is purchased for an external delivery service

Sometimes the function woocommerce_thankyou is not called, but sometimes works fine.
Our code is:
add_action(‘woocommerce_thankyou’, ‘send_order_information_for_delivery’, 999, 1);
function send_order_information_for_delivery($order_id)
{
$order = wc_get_order($order_id);
$order_items = $order->get_items();
// … …
}
Any idea why sometimes doesn’t work?
The main objective of this method is to obtain the information of the purchase order and its items and to send them to another database through an API.
The strange thing is that in some orders the method is not called.
Instead, you could try to use the following hooked function, that will work for defined order statuses only once, after the delivery data is processed.
add_action( 'woocommerce_order_status_changed', 'delivery_information_process', 20, 4 );
function delivery_information_process( $order_id, $old_status, $new_status, $order ){
// Define the order statuses to check
$statuses_to_check = array( 'on-hold', 'processing' );
// Only "On hold" order status and "Free Shipping" Shipping Method
if ( $order->get_meta( '_delivery_check', true ) && in_array( $new_status, $statuses_to_check ) )
{
// Getting all WC_emails objects
foreach($order->get_items() as $item_id => $item ){
$product = $item->get_product();
$sku = $product->get_sku();
}
## ==> Process delivery data step
// Once delivery information is send or processed ==> update '_delivery_check' to avoid repetitions
$order->update_meta_data( '_delivery_check', true );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.

WooCommerce: autocomplete paid orders based on shipping method

I have a product that people can print directly (shipping method 1) or choose to get it via shipping service (shipping method 2). So the order should auto complete if they choose to print it directly (shipping method 2) ONLY.
Is it possible to extend that code snippet from WooCommerce?
From docs I found
this
/**
* 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' );
}
Here is the working solution. BIG THANKS TO LoicTheAztec:
add_action( 'woocommerce_thankyou',
'wc_auto_complete_paid_order_based_on_shipping_method', 20, 1 );
function wc_auto_complete_paid_order_based_on_shipping_method( $order_id ) {
if ( ! $order_id ) return;
// HERE define the allowed shipping methods IDs (can be names or slugs changing the code a bit)
$allowed_shipping_methods = array( '5' );
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
// Get the shipping related data for this order:
$shipping_item = $order->get_items('shipping');
$item = reset($shipping_item);
$item_data = $item->get_data();
// Get the shipping method name, rate ID and type slug
$method_rate_id = $item_data['instance_id']; // Shipping method ID
// No updated status for orders delivered with Bank wire, Cash on
delivery and Cheque payment methods.
$avoided_statuses = array( 'bacs', 'cod', 'cheque');
if ( in_array( $order->get_payment_method(), $avoided_statuses ) ){
return;
}
// update status to "completed" for paid Orders and a defined shipping
method ID
elseif ( in_array( $method_rate_id, $allowed_shipping_methods ) ){
$order->update_status( 'completed' );
}
}
First, get_post_meta($order_id, '_payment_method', true ) or $order->get_payment_method() are completely similar and do the same thing.
The difference is that the first one use a Wordpress function to access this data from wp_postmeta table and the 2nd one is a WC_Order method that will also access the same data from wp_postmeta table…
No one is better than the other.
New enhanced revisited code (see this thread for explanations).
What is the instance ID:
For example if the Shipping method rate Id is flat_rate:14, the instance Id is 14 (unique ID)
The new version code:
add_action( 'woocommerce_payment_complete_order_status', 'auto_complete_paid_order_based_on_shipping_method', 10, 3 );
function auto_complete_paid_order_based_on_shipping_method( $status, $order_id, $order ) {
// HERE define the allowed shipping methods instance IDs
$allowed_shipping_methods_instance_ids = array( '14', '19' );
// Loop through order "shipping" items
foreach ( $order->get_shipping_methods() as $shipping_method ) {
if( in_array( $shipping_method->get_instance_id(), $allowed_shipping_methods_instance_ids ) ) {
return 'completed';
}
}
return $status;
}
Code goes in function.php file of the active child theme (or active theme). Tested and works.
Original answer:
The answer below comes from this similar answer I have made some time ago:
WooCommerce: Auto complete paid orders
add_action( 'woocommerce_thankyou', 'auto_complete_paid_order_based_on_shipping_method', 10, 1 );
function auto_complete_paid_order_based_on_shipping_method( $order_id ) {
if ( ! $order_id ) return;
// HERE define the allowed shipping methods IDs (can be names or slugs changing the code a bit)
$allowed_shipping_methods = array( 'flat_rate:14', 'flat_rate:19' );
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
// Get the shipping related data for this order:
$shipping_item = $order->get_items('shipping');
$item = reset($shipping_item);
$item_data = $item->get_data();
// Get the shipping method name, rate ID and type slug
$shipping_name = $item_data['name']; // Shipping method name
$method_rate_id = $item_data['method_id']; // Shipping method ID
$method_arr = explode( ':', $method_rate_id );
$method_type = $method_arr[0]; // Shipping method type slug
// No updated status for orders delivered with Bank wire, Cash on delivery and Cheque payment methods.
$avoided_statuses = array( 'bacs', 'cod', 'cheque');
if ( in_array( $order->get_payment_method(), $avoided_statuses ) ){
return;
}
// update status to "completed" for paid Orders and a defined shipping method ID
elseif ( in_array( $method_rate_id, $allowed_shipping_methods ) ){
$order->update_status( 'completed' );
}
}
Code goes in function.php file of the active child theme (or active theme).
Tested and works.
I think what you're looking for is $order->has_shipping_method('name_of_method')
From: https://docs.woocommerce.com/wc-apidocs/class-WC_Abstract_Order.html#_has_shipping_method)
That is my working solution (Thanks to LoicTheAztec):
add_action( 'woocommerce_thankyou',
'wc_auto_complete_paid_order_based_on_shipping_method', 20, 1 );
function wc_auto_complete_paid_order_based_on_shipping_method( $order_id ) {
if ( ! $order_id ) return;
// HERE define the allowed shipping methods IDs (can be names or slugs changing the code a bit)
$allowed_shipping_methods = array( '5' );
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
// Get the shipping related data for this order:
$shipping_item = $order->get_items('shipping');
$item = reset($shipping_item);
$item_data = $item->get_data();
// Get the shipping method name, rate ID and type slug
$method_rate_id = $item_data['instance_id']; // Shipping method ID
// No updated status for orders delivered with Bank wire, Cash on delivery and Cheque payment methods.
$avoided_statuses = array( 'bacs', 'cod', 'cheque');
if ( in_array( $order->get_payment_method(), $avoided_statuses ) ){
return;
}
// update status to "completed" for paid Orders and a defined shipping method ID
elseif ( in_array( $method_rate_id, $allowed_shipping_methods ) ){
$order->update_status( 'completed' );
}
}

WooCommerce auto restore stock on order cancel

I'm looking for a solution to automatically restore stock when order is canceled. I'm using PayU gateway on my client's site and it works by setting all orders on hold when waiting for payment and cancels them after 24 hours if there's no valid payment made. However, this means that WooCommerce built-in mechanism for freezing stock and then restocking after given time doesn't work (as the payment gateway sets the status to on hold).
There was a plugin called WooCommerce Auto Restore Stock by Gerhard Potgieter but it's an oldie and I was unable to find a similar solution in WordPress plugin repository or on Codecanyon.
Perhaps someone stumbled upon a solution to auto restore stock for cancelled orders that works with WC 3.0+?
According to woocommerce github issue here, they say that (24 hours hold stock & cancel without restock) is correct behavior. Orders can be cancelled for a variety of reasons - incorrect stock levels, faulty products, user choice etc etc therefore the re-increment of stock should be an entirely (manual) admin decision.
So, i try to override the function in my child theme`s functions.php and it works!
Here's the code :
remove_filter( 'woocommerce_cancel_unpaid_orders', 'wc_cancel_unpaid_orders' );
add_filter( 'woocommerce_cancel_unpaid_orders', 'override_cancel_unpaid_orders' );
function override_cancel_unpaid_orders() {
$held_duration = get_option( 'woocommerce_hold_stock_minutes' );
if ( $held_duration < 1 || 'yes' !== get_option( 'woocommerce_manage_stock' ) ) {
return;
}
$data_store = WC_Data_Store::load( 'order' );
$unpaid_orders = $data_store->get_unpaid_orders( strtotime( '-' . absint( $held_duration ) . ' MINUTES', current_time( 'timestamp' ) ) );
if ( $unpaid_orders ) {
foreach ( $unpaid_orders as $unpaid_order ) {
$order = wc_get_order( $unpaid_order );
if ( apply_filters( 'woocommerce_cancel_unpaid_order', 'checkout' === $order->get_created_via(), $order ) ) {
//Cancel Order
$order->update_status( 'cancelled', __( 'Unpaid order cancelled - time limit reached.', 'woocommerce' ) );
//Restock
foreach ($order->get_items() as $item_id => $item) {
// Get an instance of corresponding the WC_Product object
$product = $item->get_product();
$qty = $item->get_quantity(); // Get the item quantity
wc_update_product_stock($product, $qty, 'increase');
}
}
}
}
wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' );
wp_schedule_single_event( time() + ( absint( $held_duration ) * 60 ), 'woocommerce_cancel_unpaid_orders' );
}
Hope it helps.

Change orders status made with cheque payment method to "processing" status

I need to make WooCommerce push payments made by check into the "processing" status rather than the "on hold" status. I tried the snippet below however it doesn't seem to have an effect.
Here is my code:
add_filter( 'woocommerce_payment_complete_order_status', 'sf_wc_autocomplete_paid_orders' );
function sf_wc_autocomplete_paid_orders( $order_status, $order_id ) {
$order = wc_get_order( $order_id );
if ($order->status == 'on-hold') {
return 'processing';
}
return $order_status;
}
How can I achieve this?
Thanks.
Here is the function you are looking at hooked in woocommerce_thankyou hook:
add_action( 'woocommerce_thankyou', 'cheque_payment_method_order_status_to_processing', 10, 1 );
function cheque_payment_method_order_status_to_processing( $order_id ) {
if ( ! $order_id )
return;
$order = wc_get_order( $order_id );
// Updating order status to processing for orders delivered with Cheque payment methods.
if ( get_post_meta($order->id, '_payment_method', true) == 'cheque' )
$order->update_status( 'processing' );
}
This code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This is tested and works.
Related thread: WooCommerce: Auto complete paid Orders (depending on Payment methods)
I didn't want to use the Thank You filter in case the order still got set to On Hold at a previous step, before changing it to my desired status in the filter (a custom status in my case, or Processing in yours). So I used the filter in the Cheque gateway:
add_filter( 'woocommerce_cheque_process_payment_order_status', 'myplugin_change_order_to_agent_processing', 10, 1 );
function myplugin_change_order_to_agent_processing($status){
return 'agent-processing';
}
I hope this helps someone else out there to know that there is another option.
Previous answer from LoicTheAztec is outdated and gives error about accessing object fields directly on the order object.
Correct code should be
add_action( 'woocommerce_thankyou', 'cheque_payment_method_order_status_to_processing', 10, 1 );
function cheque_payment_method_order_status_to_processing( $order_id ) {
if ( ! $order_id )
return;
$order = wc_get_order( $order_id );
// Updating order status to processing for orders delivered with Cheque payment methods.
if ( get_post_meta($order->get_id(), '_payment_method', true) == 'cheque' )
$order->update_status( 'processing' );
}

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