Maybe I overlooked it, but as much as I searched, I could not find which action to hook when a subscription changes price or frequency in Woocommerce Subscriptions.
The documentation says that to support price changes in your payment gateway you have to list subscription_amount_changes, but nowhere it says which function will be called when the amount actually changes..
Also in the Action reference I was unable to find an action hook which is called when the subscription amount or frequency changes. If anyone has a clue which hook to use or how to implement this particular feature, please tell me.
Edit
Ok thanks for the comments and answer by #Reigel, so if I understand correctly the change of a subscription in the admin menu (which I indeed refer to), has to be handled by the save_post action. Could you provide a small example how to hook this action and check if it is a subscription and get the $order_id (I guess this is the same as post_id?) to use in the subscription management calls?
Thank you very much already!
This should be considered an addon to the answer by #Reigel. If you upvote this, upvote his answer too.
Here's an example of hooking the pre_post_update action. It occurs a little before the save_post action. Both actions are triggered in the wp_insert_post() function in post.php.
function post_save_subscription_check( $post_ID, $data )
{
if( $data['post_type'] == 'product' ) {
if (!empty($_POST['_subscription_price']) && get_post_meta($post_ID, '_subscription_price', true) != $_POST['_subscription_price']) {
/* do stuff here */
}
if (!empty($_POST['_subscription_period']) && get_post_meta($post_ID, '_subscription_period', true) != $_POST['_subscription_period']) {
/* do stuff here */
}
}
}
add_action('pre_post_update', 'post_save_subscription_check', 10, 2 );
In the logic we are checking for the old value, obtained with get_post_meta() and the new value, held in the $_POST variable and comparing them.
This code only executes when a post updates, not for a new post
The code gets placed in your theme functions.php or custom plugin code.
In live code I would recommend cleaning any $_POST data before using it. I haven't bothered here.
I will try to explain about supports.
subscription_amount_changes is just a support and will not fire anything. You can use it for conditional statements, like:
if ( !$chosen_gateway->supports( 'subscription_amount_changes' )) {
echo 'Please be considerate and do not change the price for the chosen gateway does not support it.';
}
now, other plugins can then check if the chosen gateway supports subscription_amount_changes and do their appropriate actions.
action hook which is called when the subscription amount or frequency
changes
subscription is just a product type. Which means this is just a post with a post_type of product. The amount and frequency are just post meta. all are handled on save_post action. add_action( 'save_post', __CLASS__ . '::save_subscription_meta', 11 );. This is on the post_type=product. You have to check also for save_post on post_type=shop_order as it's more appropriate for checking support. Because there's already a gateway chosen.
Related
I have a plugin I developed that connects a WooCommerce order to HubSpot. The issue i'm running into though is that while it works, the hook im using right now is sending order info to HubSpot before its technically complete. So this means that stuff like Failed orders get sent in as Pending, and coupon codes are ommitted.
So i'm wondering what the right hook to use is.
My goal: Everytime a WooCommerce order is created & completed and a WooCommerce order is updated, send the data to HubSpot.
What I have so far:
add_action('save_post_shop_order', 'printout', 10, 3);
function printout($post_ID, $post, $update)
{
if (!is_admin()){
return;
}
if($update){
$msg = $post_ID;
$order = get_woocommerce_order($msg);
mainplugin($msg, $order);
}
}
add_action('woocommerce_new_order', 'neworder_delegator', 10, 2);
function neworder_delegator($order_id, $order){
mainplugin($order_id, $order);
}
So I guess i'm just looking for the right hook to get me what I want.
Thanks!
Here is your answer:
Each WooCommerce Order transition has one or more dynamic hooks that trigger when the status transition happens.
They start with 'woocommerce_order_status_' and the remaining part of the action is either the new status that the order has transitioned to, or the to and from statuses involved in the format '<status_transition_from>to<status_transition_to>'
Examples
You can hook your function to
add_action( 'woocommerce_order_status_completed', 'your_order_completed_function');
To trigger your function only when the order transitioned to completed, and not when refunded, cancelled, on-hold, etc. as those would run on other actions such as
woocommerce_order_status_refunded
woocommerce_order_status_cancelled
woocommerce_order_status_on-hold
woocommerce_order_status_failed
woocommerce_order_status_processing
Edited to add link to official WooCommerce docs:
https://woocommerce.github.io/code-reference/hooks/hooks.html
My PHP skills are not good but I learn by code example because I am not familiar with the syntax, but in this case I can not find a code example that works.
I want to set a Product Option Radio Button Field's selected value during a gform_pre_submission add_action based on another field's value. I've tried something like this (there are other functions and variables are defined elsewhere in advance, this is an abstract) but this approach isn't working for me and I am wondering what I am missing:
add_action( 'gform_pre_submission_FORMIDhere', 'select_radio_button' );
function select_radio_button( $form) {
foreach( $form['fields'] as &$field ) {
if( 53 === $field->id ) {
foreach( $field->choices as &$choice ) {
echo($aag_kg_nummorn.' / /'.$choice['value']);
if( $aag_kg_nummorn == $choice['value'] ) {
echo('Bingo!');
$choice['isSelected'] = true; // <- THIS line doesn't work for me
}
}
}
}
return $form;
}
Thank you for helping me in advance.
There are a couple of things I want to point out before answering
You should be modifying $_POST instead of the field, I can see you already figured this out.
gform_pre_submission is an action, not a filter, so returning the form here doesn't do anything.
The Answer
Say you have a product option field that has the ID of 2, and it has 3 options
First Option|1
Second Option|2
Third Option|3
Then the code will look like
add_action( 'gform_pre_submission_FORMIDhere', 'select_radio_button' );
function select_radio_button( $form ) {
$_POST['input_2'] = 'New Option|6';
}
I assume you are using this with a payment form that utilizes one of the gravityforms payment add-ons like stripe or Paypal, some of these add-ons (like PayPal Checkout) sends the total value to the payment gateway using JS before this action is fired, so the total value sent to the payment gateway will be the value of the option the user has already selected, but the total value on the entry will use the value you set in the code.
If you need help on how to override this from the JS side as well so you can send the overridden value to the payment gateway, I can write that in a separate answer.
I need the Woocommerce cart to be cleaned in case I send more than four items to it via url.
I came up with this code, but it only cleans the cart if there are already five items in it.
//in functions.php
add_filter( 'woocommerce_add_to_cart_validation', 'remove_cart_item_before_add_to_cart', 20, 3 );
function remove_cart_item_before_add_to_cart( $passed, $product_id, $quantity ) {
if( WC()->cart->get_cart_contents_count() >= 5 )
WC()->cart->empty_cart();
return $passed;
}
Another problem is that, even the code above that is not useful to me, does not work if I add multiple courses via url, as in:
https://exemple.net/cart?fill_cart=100,101,102,103,104,105,106
That is, the code in functions.php only works from the website, and not by url.
All I need is clear the cart when sending more than 4 items by url.
I prefer a solution in PHP, but a JS solution will do. Thanks for who can help me.
I found a solution using the free plugin Cart links for WooCommerce, and making a few change in its code.
I will describe below what I did:
1) To start install the plugin, this will allow you to add products using the fill_cart parameter in url (as described in my question)
2) edit the plugin file soft79-cart-links-for-woocommerce.php found at: yoursite/wp-content/plugins/soft79-cart-links-for-woocommerce/
3) go to line 141 or find the declaration of the method fill_cart
Inside the fill_cart method uncomment (or add) the code to clean the cart:
public function fill_cart( $fill_string ) {
$original_notices = wc_get_notices();
wc_clear_notices();
////Clear the cart
//WC()->cart->empty_cart(); <-- uncomment here!
Save the file. Now any new items that arrive via url must clean the cart first.
I want to add my own custom payment error text (we use Account Funds and want to add an extra prompt)
How can I change, the following which shows when there is not enough funds, from:
Sorry, it seems that there are no available payment methods for your
state. Please contact us if you require assistance or wish to make
alternate arrangements.
to an error message with a link like below:
You do not have sufficent funds to process this order, please top up or upgrade. Thank you.
The text seems to be stored in templates/checkout/payment.php
https://github.com/woocommerce/woocommerce/blob/ef05bfccfc01bb2c621ef1293e61f7c57950670f/templates/checkout/payment.php
How can I change this without it getting wiped out by a Woocommerce version upgrade?
In WordPress, filters are functions that can be hooked to an event (called hooks). During the execution when the event is triggered the filter is applied to the data output generated by the event hook. It is important to remember that filters perform their actions on data they receive and then return that data before it is displayed in the browser.
In the file you attached (payment.php) you have
apply_filters( 'woocommerce_no_available_payment_methods_message' ....
So you can use the filter "woocommerce_no_available_payment_methods_message" to change the text
Create a custom function and add it to functions file or a small plugin.
First we hook our own function with woocommerce event
add_filter( 'woocommerce_no_available_payment_methods_message', 'your_custom_function_name_here' );
Now we define what our function would do.
function your_custom_function_name_here( $content ) {
//your changes here
$content = "bla bla";
// Returns the content.
return $content;
}
You just have to add a filter and apply your change in your child-theme's functions.php file:
add_filter( 'woocommerce_no_available_payment_methods_message', function( $no_gateways_message ) {
return 'You do not have sufficent funds to process this order, please top up or upgrade. Thank you.';
});
Let me know if that worked.
add_filter( 'woocommerce_no_available_payment_methods_message', 'change_payment_message', 10, 2);
function change_payment_message( $value, $arg2 ) {
$message = WC()->customer->get_billing_country()?'You do not have sufficent funds to process this order, pleasetop up or upgrade. Thank you':'Please fill in your details above to see available payment methods.';
return $message;
}
Is there a simple way or a plugin to retain checkout information entered by the client after he/she leaves and comes back?
This plugin retains "fields information for customers when they navigate back and forth" however it has quite a lot of recent bad reviews so I don't think I'll use that for production. Any alternative suggestion?
---- Update ----
The code below is working, but only if data is submitted!
The only possible ways are javascript/jQuery form event detection on checkout fields and worpress Ajax:
Using ajax connected to some session transients function (as in code below).
Using (javascript) web Storage: localStorage, sessionStorage…
I have found some real interesting code in this thread that is using sessions transients to store checkout data.
// this function sets the checkout form data as session transients whenever the checkout page validates
function set_persitent_checkout ( $a ) {
$arr = array();
foreach ( $a as $key => $value )
if ( ! empty($value) )
$arr[$key] = $value;
WC()->session->set( 'form_data', $arr );
return $a;
}
add_action( 'woocommerce_after_checkout_validation', 'set_persitent_checkout' );
// this function hooks into woocommerce_checkout_get_value to substitute standard values with session values if present
function get_persistent_checkout ( $value, $index ) {
$data = WC()->session->get('form_data');
if ( ! $data || empty($data[$index]) )
return $value;
return is_bool($data[$index]) ? (int) $data[$index] : $data[$index];
}
add_filter( 'woocommerce_checkout_get_value', 'get_persistent_checkout', 10, 2 );
// This is a fix for the ship_to_different_address field which gets it value differently if there is no POST data on the checkout
function get_persitent_ship_to_different ( $value ) {
$data = WC()->session->get('form_data');
if ( ! $data || empty($data['ship_to_different_address']) )
return $value;
return is_bool($data['ship_to_different_address']) ? (int) $data['ship_to_different_address'] : $data['ship_to_different_address'];
}
add_action( 'woocommerce_ship_to_different_address_checked', 'get_persitent_ship_to_different' );
Add this code to the functions.php file located in your active child theme or theme.
Explanations from the author:
1. Save the form data:
The first function set_persitent_checkout hooks into woocommerce_after_checkout_validation.
Whenever that hook is fired, any current form data is saved as a WordPress transient via the WC_Session_Handler class (which was recently updated in version 2.5 to be a lot more efficient).
2. Check the saved data on reload:
Next we hook woocommerce_checkout_get_value with get_persitent_checkout. As the name suggests, here we check the session transients and return any matches for the current field if found.
3. Make ship_to_different_address work:
The only difficult was the ship_to_different_address field, which gets its value through a different method.
To get around this the final function was added. This works exactly the same as the previous function, but hooks into woocommerce_ship_to_different_address_checked.
There you have it. It would be nice if the data was saved after every field update on checkout, but the woocommerce_after_checkout_validation hook fires enough to capture the data at all the important points.
Functions.php snipped posted by LoicTheAztec didn't work for me.
I found this plugin which remembers everything I type or select in Woocommerce checkout, including shipping fields and my custom additions to the template:
Save Abandoned Carts – WooCommerce Live Checkout Field Capture
Account passwords, if creating during checkout, are naturally not remembered.