My affiliate script tracks a conversion after an order is placed. it runs inside the woocommerce_thankyou action hook:
function affiliate_tracking_code( $order_id ) {
// get the order info for the script
?>
<script>
// affiliate script here
</script>
<?php
}
add_action( 'woocommerce_thankyou', 'affiliate_tracking_code', 10, 1 );
I do not want this script to fire if the order has failed or is pending. Only if it is successful. I can not find in the documentation whether or not the woocommerce_thankyou action hook fires for anything but successful orders.
If it does then what is the best way to make sure that my script only tracks conversions for successful orders and not failed ones.?
One way I have tested is to wrap my script in an if and check if ( $order->get_status() == 'processing' ) : // run the script however I am not sure if there are hidden loopholes.
Yes, it will fire or failed orders as well.
add_action('woocommerce_before_thankyou', 'woocommerce_before_thankyou_failed_order')
function woocommerce_before_thankyou_failed_order( $order_id ) {
$order = wc_get_order( $order_id );
if ( !$order->has_status( 'failed' ) ) {
// if order not failed
}
}
See the hook under wp-content/plugins/woocommerce/templates/checkout/thankyou.php
Related
I would like to trigger an action hook (or something similar) on specific order status change. The code should be run only once from order status "processing" to "completed".
Here is m code attempt:
function payment_complete( $order_id, $old_status, $new_status ){
if( $new_status == "completed" && $old_status == "processing") {
// $this->generate_order_file($order_id);
echo '<script>alert("Working now, but not once:()")</script>';
}
}
add_action( 'woocommerce_order_status_changed', 'payment_complete', 99, 3 );
But it seems that my code runs multiple times. I am stuck for instance. Any help will be appreciated.
You can use the following to make the order status change from "processing" to "complete" to be triggered only once for your code:
add_action( 'woocommerce_order_status_processing_to_completed', 'order_processing_to_completed', 100, 2 );
function order_processing_to_completed( $order_id, $order ) {
// Avoid hook to be triggered multiple times at once
if ( did_action( 'woocommerce_order_status_processing_to_completed' ) > 1 ) {
return;
}
// Check that this action hook has not been triggered before
if ( ! $order->get_meta( '_processing_to_completed' ) ) {
// Grab the action in WordPress error logs (for testing)
error_log('"processing_to_completed" Run once only.');
// Add a custom meta data to flag the action as triggered
$order->update_meta_data( '_processing_to_completed', 'yes' );
$order->save(); // Save
// Here add your code to be run once
}
}
Note: You should not use payment_complete function name as it could be used by another plugin.
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
The code inside the if statement will get triggered only once.
I'm using this little function here to detect if an order is set into pending. This happens between the payment page and the payment provider notification:
add_action( 'woocommerce_order_status_pending', 'status_pending' );
function status_pending( $related_job ) {
error_log('Triggered');
}
The problem is that I don't get any error log which shows me that the function work. But it becomes crazier. When I update the status via the dashboard from completed to pending, the log appears. So I've absolutely no idea why it's not working during the checkout process. Any recommendations or ideas what could be the problem?
The "pending" order status is the default status for orders before customer get on a payment gateway, just after order creation.
So the best way is to use a hook once the order is created, before payment method process:
1) try first the woocommerce_checkout_order_processed action hook (3 args):
add_action( 'woocommerce_checkout_order_processed', 'order_processed_with_pending_status', 10, 3 );
function order_processed_with_pending_status( $order_id, $posted_data, $order ) {
error_log('Triggered');
}
2) Alternatively try the woocommerce_checkout_update_order_meta action hook (2 args):
add_action( 'woocommerce_checkout_update_order_meta', 'order_processed_with_pending_status', 10, 2 );
function order_processed_with_pending_status( $order_id, $data ) {
error_log('Triggered');
}
Both should work…
That's because the hook is only triggering on order status change not on order creation, there is another hook that you can use to detect new orders, you can use the order ID to get order object which you can use to find out the order status:
add_action( 'woocommerce_new_order', 'prefix_new_wc_order', 1, 1 );
function prefix_new_wc_order( $order_id ) {
$order = new WC_Order( $order_id );
}
The hook above is only triggered in checkout process, so creating orders on backend won't trigger it.
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;
}
I've a situation to add some data to the database tables on order status completed button.
I can see the url in class-wc-admin-post-types.php
Can someone help me for any hook? Or how the admin-ajax.php works? I have to add status to some of mine custom database tables.
this code will fire a customer's order is set to completed..
add_action( 'woocommerce_order_status_completed', 'custom_task' );
function custom_task( $order_id ) {
// Only continue if have $order_id
if ( ! $order_id ) {
return;
}
// Get order
$order = wc_get_order( $order_id );
// Do your thing
}
I am in the middle of creating an additional plugin which is a custom payment gateway using the Woocommerce plugin and a child theme to style it.
This is working and I see the payment form correctly and items which is great.
The issue I have is with the payment process option.
The checkout page has an i-frame to the payment solution and is supposed to show only when an order is created and an ID is present. And to make sure we have all the persons details etc.
However the process payment take you to the thanks you page instead.
It also dumps the cart session.
even if I redirect the URL back to the cart page by playing around with it, it still kills the cart.
This would be fine in normal circumstances as I'd expect it to go to a payment page, checkout and be done. But I do not want to do this because this site has to mirror one that is currently live and how that works.
The function in my extended class is this.
public function process_payment( $order_id ) {
global $woocommerce;
#$order = wc_create_order();
///
$order = new WC_Order( $order_id );
// Mark as on-hold (we're awaiting the cheque)
$order->update_status('on-hold', __( 'Awaiting Confirmation of Funds', 'woocommerce' ));
// Reduce stock levels
///$order->reduce_order_stock();
// Remove cart
//$woocommerce->cart->empty_cart();
// Return thankyou redirect
return array(
'refresh' => true,
'reload' => false,
'result' => 'success',
'redirect' => $this->get_return_url( $order )
);
///return $order_id;
}
As you can see I commented out the bit where to empty the cart and the also the endpoint brings in the thank you template instead of just staying on the same page.
So far I have tried:
Replicating the code on my checkout to the thank you page and that
results in an undefined checkout, because the process removes it by
then
Changing the end-point reference to the same as checkout
Creating another page and putting the same shortcode on it
Nothing works, and I have been through the plugin and can see a few things I could do.
Copy the process_payment function into my extended class and
effectively re-write it
or
Find a filter similar to below that could do what I need
add_action( 'woocommerce_thankyou', function(){
global $woocommerce;
$order = new WC_Order();
if ( $order->status != 'failed' ) {
wp_redirect( home_url() ); exit; // or whatever url you want
}
});
What I need is it to stay on the same page (refresh) the same way it does when it checks the details to refresh the totals.
Create an order with those current details in and return an order number
Will not kill the cart session at that point, so if I was to refresh the browser for arguments sake it would stay live. (I will work out a way to kill the cart when the person navigates away and a unique session has been created at that point).
Its just this bit I have been fighting with for the past couple of days and not sure the best way.
Any help or pointers would be greatly appreciated.
thanks
Andi
Ok figured this one out.
There is a header action that clears the cart.
We needed to add some more statuses to the array so that it ignores it.
1st remove the action and then add your own method.
function st_wc_clear_cart_after_payment( $methods ) {
global $wp, $woocommerce;
if ( ! empty( $wp->query_vars['order-received'] ) ) {
$order_id = absint( $wp->query_vars['order-received'] );
if ( isset( $_GET['key'] ) )
$order_key = $_GET['key'];
else
$order_key = '';
if ( $order_id > 0 ) {
$order = wc_get_order( $order_id );
if ( $order->order_key == $order_key ) {
WC()->cart->empty_cart();
}
}
}
if ( WC()->session->order_awaiting_payment > 0 ) {
$order = wc_get_order( WC()->session->order_awaiting_payment );
if ( $order->id > 0 ) {
// If the order has not failed, or is not pending, the order must have gone through
if ( ! $order->has_status( array( 'failed', 'pending','pending-st-cleared-funds','on-hold' ) ) ) { ///// <- add your custom status here....
WC()->cart->empty_cart();
}
}
}
}
function override_wc_clear_cart_after_payment() {
remove_filter('get_header', 'wc_clear_cart_after_payment' );
add_action('get_header', 'st_wc_clear_cart_after_payment' );
}
add_action('init', 'override_wc_clear_cart_after_payment');