I am trying to change the PayPal address that Woocommerce uses, depending on what page they are on. I only have 5 products at the moment, and all 5 need to use a different PayPal address.
I found this hook that can change the Paypal address, although not too sure how to add it in exactly (code is 3 years old apparently).
$paypal_args = apply_filters( 'woocommerce_paypal_args', $paypal_args );
add_filter( 'woocommerce_paypal_args' , 'custom_override_paypal_email' );
function custom_override_paypal_email( $paypal_args ) {
$paypal_args['business'] = 'paypalemail#domain.com';
print_r( $paypal_args['business'] );
return $paypal_args;
}
How can I use this hook to change the Paypal address depending on which page/product the user is on?
I've checked and found out that woocommerce_paypal_args has two arguments, the settings and the order. So based on the order, we can check what product it has and use the appropriate email.
add_filter( 'woocommerce_paypal_args' , 'custom_override_paypal_email', 10, 2 );
function custom_override_paypal_email( $paypal_args, $order ) {
foreach ( $order->get_items() as $item ) {
switch( $item['product_id'] ) {
case 561:
$paypal_args['business'] = 'paypalemail1#domain.com';
break;
case 562:
$paypal_args['business'] = 'paypalemail2#domain.com';
break;
}
}
return $paypal_args;
}
please take note that you have to make sure that there can only be one item on the cart. If there are more than 1 product in the cart, this will use the last found product in the foreach loop. The code above is just for guidance, please change accordingly.
Related
I've got the following code in my functions.php file that is supposed to check if certain products are in the cart and, if they are, remove a field from the billing set of fields at checkout. However, it's not removing the field, or any other field I try. I'm wondering why that is. Other tutorials I'm following are consistent with this code and say it should work, but for me it's not. What did I get wrong?
/**
* Check if a specific product ID is in the cart
*/
function dz_product_is_in_the_cart() {
// Add your special product IDs here
$ids = array( '10771', '10773', '10774', '10943', '10944', '10945', '10946', '10947', '10948', '10949', '10950', '10951', '10952', '10953', '10943', '10943', '10943', '10943');;
// Products currently in the cart
$cart_ids = array();
// Find each product in the cart and add it to the $cart_ids array
foreach( WC()->cart->get_cart() as $cart_item_key => $values ) {
$cart_product = $values['data'];
$cart_ids[] = $cart_product->id;
}
// If one of the special products are in the cart, return true.
if ( ! empty( array_intersect( $ids, $cart_ids ) ) ) {
return true;
} else {
return false;
}
}
/**
* Conditionally remove a checkout field based on products in the cart
*/
function dz_remove_checkout_field( $fields ) {
if ( dz_product_is_in_the_cart() ) {
unset( $fields['billing']['billing_first_name'] );
}
return $fields;
}
add_filter( 'woocommerce_checkout_fields' , 'dz_remove_checkout_field' );
The billing_first_name field is a required field. So do I need to do something to unrequire it before I try to unset it?
The problem was a plugin that was inserting this field was overriding my code that tried to unrequire and remove it. I opted to disable the plugin and add and conditionally control my own field(s) with the code here: https://woocommerce.com/document/tutorial-customising-checkout-fields-using-actions-and-filters/#section-7
I'm selling multiple products, each with 2 variations that will each need to have a custom bit of text (with with URLs embedded) in the Completed email. Lots of custom emails: per product and variation. I've found many options for functions.php but they are all from many years and woo versions ago.
The very popular "Woo Custom Emails Per Product" plugin does not have a per-variation function. I do not want to make each variation its own product (and could therefore use that plugin) since I want a single product page for each, where the patron can select the variation they want.
So I decided the best way to add the info for each variation is in the "Description" field for the variation.
Here's where I would like it go, above what I believe is the woocommerce_email_order_items_table:
screen grab of email showing where text should go
I tried adding this to functions.php but it's from 2015 and is for "processing" not "completed" emails:
add_filter( 'woocommerce_product_variation_title_include_attributes', '__return_false' );
function render_product_description($item_id, $item, $order){
$_product = $order->get_product_from_item( $item );
echo "<br>" . $_product->post->post_content;
}
add_action('woocommerce_order_item_meta_end', 'render_product_description',10,3);
I've tried this, but it's from years ago and did not accomplish what I need:
Add the product description to WooCommerce email notifications
This is close: Add Custom Product Field in WooCommerce 'Order Completed' Emails but not quite what I want, because there is not a custom field specific to each variation; it seems the only thing custom to each variation is "Description."
I'd like to find a way to edit the email template, as I think that would be the ideal way to go here. If it can simply list the Description contents for each item's variation ordered, I can format that text to be self-explanatory for the patron, and then the order summary box (with Product/Quantity/Price) will stay clean and just list the items.
This is a test page I set up: https://www.chambermusicpittsburgh.org/concerts-and-tickets-new-store/. Only the Escher and Dover product has the variables, with a tester phrase and URL in the Description for the weblink (which will pop down if you choose that option, but which I will eventually hide here with CSS but I've left it for testing).
I feel like adding the variation Description to the email should be super straightforward/simple, and maybe I'm not experienced enough or not looking in the right place, but that particular piece of data seems extremely hard to hook into and display in the Order Confirmed email.
Thanks!
To add custom content to the email template at this point:
You can use the woocommerce_email_before_order_table hook. See here for more information.
The following function will get the descriptions of each ordered product (if the description is not empty).
The description is obtained from the product variation NOT the
variable product.
// adds the product description before the order table in the completed order email
add_action( 'woocommerce_email_before_order_table', 'add_content_before_order_table', 99, 4 );
function add_content_before_order_table( $order, $sent_to_admin, $plain_text, $email ) {
// only for the email of the completed order
if ( ! $email->id == 'customer_completed_order' ) {
return;
}
// initializes the array with all product descriptions
$descriptions = array();
// for each product ordered
foreach ( $order->get_items() as $order_item ) {
$product = $order_item->get_product();
// get the product description
if ( $product->get_description() ) {
$descriptions[] = $product->get_description();
}
}
// if the $descriptions is not empty it shows it
if ( ! empty( $descriptions ) ) {
foreach ( $descriptions as $content ) {
echo '<p>' . $content . '</p>';
}
}
}
The code has been tested and works. Add it to your active theme's functions.php.
This works but added the description in both the processing and the completed mail and admin mails. There seems to something wrong with the status check $email->id == 'customer_completed_order'. I added $status = $order->get_status(); and changed it to look for $status == "completed" instead. It works for me, but please add your thoughts or comments if this is a good way.
add_action( 'woocommerce_email_before_order_table', 'add_content_before_order_table', 99, 4 );
function add_content_before_order_table( $order, $sent_to_admin, $plain_text, $email ) {
// checking if it's the order status we want
$status = $order->get_status();
if ( $status == "completed" ) {
// initializes the array with all product descriptions
$descriptions = array();
// for each product ordered
foreach ( $order->get_items() as $order_item ) {
$product = $order_item->get_product();
// get the product description
if ( $product->get_description() ) {
$descriptions[] = $product->get_description();
}
}
// if the $descriptions is not empty it shows it
if ( ! empty( $descriptions ) ) {
foreach ( $descriptions as $content ) {
echo '<p>' . $content . '</p>';
}
}
}
}
I need to hide a shipping method depending on if a discount code is used or not.
But I don't find how to hide a shipping method in the order confirmation page.
I saw that answer but it does nothing, I tried this code to verify:
add_filter( 'woocommerce_package_rates', 'custom_shipping_rates', 100, 2 );
function custom_shipping_rates( $rates, $package ) {
exit();
}
Which should block page display but the page is well loaded.
EDIT: The filter is never called because wordpress uses stored rates in class-wc-shipping.php:
if ( ! is_array( $stored_rates ) || $package_hash !== $stored_rates['package_hash'] || 'yes' === get_option( 'woocommerce_shipping_debug_mode', 'no' ) )
{
...
// Filter the calculated rates.
$package['rates'] = apply_filters( 'woocommerce_package_rates',
$package['rates'], $package );
...
}
else
{
$package['rates'] = $stored_rates['rates'];
}
That code should actually work unless you have an old wc version. You can verify if that hook exist by searching for it in /plugins/woocommerce code base. If you don't find it, then something is wrong with the updates.
If you find it and your code still doesn't work, then the only other reason is that you placed that snipped of code in a point that never get fired or you simple come too late.
Could you please add where and how you placed that code so that we can see better what is going on?
Edit:
if you look above the snipped you copied you will see:
// Get rates stored in the WC session data for this package.
$wc_session_key = 'shipping_for_package_' . $package_key ;
$stored_rates = WC()->session->get( $wc_session_key );
which makes me think that is a session-related thing. So in order to fully test you need to either open the website with another browser, empty your browser cache + cookie or use an anonymous browser session
There's a trick to disable the usage of cache, I limited it to the "update order review" action that way:
if (preg_match('#update_order_review#i',$_SERVER['REQUEST_URI']))
{
add_filter('transient_shipping-transient-version', function($value, $name)
{
return false;
}, 10, 2);
}
add_filter( 'woocommerce_package_rates', 'custom_shipping_rates', 100, 2 );
function custom_shipping_rates( $rates, $package )
{
//get coupons
$reduc=WC()->cart->get_coupons();
$hidemr=isset($reduc['thediscountcode']);
if ($hidemr)
{
foreach($rates as $k=>$v)
{
if (preg_match('#MethodLabelToHide#i',$v->get_label()))
unset($rates[$k]);
}
}
return $rates;
}
I have a Woocommerce site that I want to detect if a user changes any of their billing details. I've had a look around and found this snippet:
add_filter( 'insert_user_meta', function( $meta, $user, $update ) {
if( true !== $update ) return $meta;
$old_meta = get_user_meta( $user->ID );
if( $old_meta[ 'first_name' ] !== $meta[ 'first_name' ] ) {
//* Do something
}
return $meta;
}, 10, 3 );
Will this fire everytime an update is made to the user meta? How can I add in all the following fields to see if any change:
Company
Billing address line 1
Billing address line 2
City
Postcode
Country
County
Or alternatively, is there a woocommerce hook for this?
Would something like this work?
function my_profile_update( $user_id ) {
if ( ! isset( $_POST['pass1'] ) || '' == $_POST['pass1'] ) {
return;
}
// password changed...
}
add_action( 'profile_update', 'my_profile_update' );
If a user edit address from woocommerce my account page in frontend, there is a hook available for edit address through which you can check and achieve your work. The hook as follow -
do_action( 'woocommerce_customer_save_address', $user_id, $load_address );
where $load_address return 'billing' whenever a user edit billing fields.
I would say that if the hook works, it is triggered for all Wordpress users, not only customers.
But I would recommend not to compare on data that a user can enter. In your case if he made a typo in his first name (like uppercase first letter), comparision fails and one can never correct that.
What you are looking for is one of the account page hooks. But unfortunately I can only point out a direction.
I need to programmatically and dynamically change the price of an item in the cart.
I’ve tried varying combinations of Woocommerce action hooks, the cart and session objects, but nothing quite seems to do the trick. I thought this wouldn’t be so challenging.
add_action( 'woocommerce_before_calculate_totals', 'change_cart_item_price' );
function change_cart_item_price( $cart_object ) {
foreach ( $cart_object->cart_contents as $key => $value ) {
if( 123 == $value['data']->id ) {
$new_price = function_to_get_new_price( $value, get_current_user_id( ) );
$value['data']->price = $new_price;
}
}
}
The above code changes the price for each item only on the checkout page, or when updating the cart (ie: the hook is called when removing an item from the cart), but not indefinitely.
I'm using the Woocommerce Gravity Forms add-on. I have one product in particular, which will be ordered multiple times by a given user. The user will be allowed 5x free with only shipping fees, and each above 5 will be $20. I have this much coded and functional with Gravity Forms hooks that dynamically populate fields. Shipping is specific to fields within the gravity form, therefore I am leaving that calculation to Gravity Forms.
My issue is that if a user reduces the quantity of this product from their order (removes one of the items from the cart), it should re-calculate the price of each item of the same product within the cart, otherwise they could be over-charged (an item that used to be the 6th is now the 4th, but the price remains the same, which it shouldn't)
Therefore, I would like to re-calculate the price of each item in the cart, based on the quantity of this particular product, every time something is removed from the cart.
--- EDIT ---
The above code works, but I'm realizing the issue must be a custom loop I'm using to display the prices...
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = $cart_item['data'];
if( 123 == $_product->post->ID ) {
$price_not_updated = $cart_item['data']->price;
}
}
I figured it out... I looked at the woocommerce cart docs and essentially realized that the prices I was getting had yet to be calculated. So, before running the loop, I had to do the action that I was initially hooking into to change the prices.
Thanks for your help!
function getUpdatedCartPrices() {
do_action( 'woocommerce_before_calculate_totals', WC()->cart );
$horray_updated_prices_works = array();
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = $cart_item['data'];
if( 123 == $_product->post->ID ) {
$horray_updated_prices_works[] = $cart_item['data']->price;
}
}
}
I changed the function to have a second parameter so that you can use it dynamically with any product as long as you call the proper id. I also set the function return value to either a success or error message. This way you can know if it was changed, and if so, to what price. Hope this helps.
by the way, having this function called again when the cart is updated should solve the recalculation issue. It would be great if i could see the code for your $function_to_get_new_price() function.
add_action( 'woocommerce_before_calculate_totals', 'change_cart_item_price' );
function change_cart_item_price($id, $cart_object ) {
foreach ( $cart_object->cart_contents as $key => $value ) {
if( $id == $value['data']->id ) {
$new_price = function_to_get_new_price( $value, get_current_user_id( ) );
$value['data']->price = $new_price;
$successMsg = "The price of item $value['data']->id was set to $value['data']->price".;
return $successMsg;
}else{
$errorMsg = "Price was not changed!";
return $errorMsg;
}
}
}