Prevent access to checkout without shipping method in WooCommerce - php

I am trying to remove the proceed to checkout button and restrict access to the checkout page until a customer fills out the 'Calculate shipping' option on the basket page.
I created a local shipping method that's restricted to multiple post/zipcodes. I then went and added this to my functions.php file.
function disable_checkout_button_no_shipping() {
$package_counts = array();
// get shipping packages and their rate counts
$packages = WC()->shipping->get_packages();
foreach( $packages as $key => $pkg )
$package_counts[ $key ] = count( $pkg[ 'rates' ] );
// remove button if any packages are missing shipping options
if( in_array( 0, $package_counts ) )
remove_action( 'woocommerce_proceed_to_checkout', 'woocommerce_button_proceed_to_checkout', 20 );
}
add_action( 'woocommerce_proceed_to_checkout', 'disable_checkout_button_no_shipping', 1 );
However when I test the site in different browsers and in incognito modes the 'Proceed to checkout' button is there.
If I click on the 'Calculate shipping' link and not fill out the form but update it the button disappears. I basically want the button to appear when a customer fills out the 'Calculate shipping' form on the cart page (and have one of the postcodes in my shipping method) before being able to proceed to the checkout page.

You should better try with the "Chosen shipping method" from WooCommerce session like:
function disable_checkout_button_no_shipping() {
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
// remove button if there is no chosen shipping method
if( empty( $chosen_shipping_methods ) ) {
remove_action( 'woocommerce_proceed_to_checkout', 'woocommerce_button_proceed_to_checkout', 20 );
}
}
add_action( 'woocommerce_proceed_to_checkout', 'disable_checkout_button_no_shipping', 1 );
Or in a different way using woocommerce_check_cart_items action hook:
add_action( 'woocommerce_check_cart_items', 'required_chosen_shipping_methods' );
function required_chosen_shipping_methods() {
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
if( is_array( $chosen_shipping_methods ) && count( $chosen_shipping_methods ) > 0 ) {
// Display an error message
wc_add_notice( __("A shipping method is required in order to proceed to checkout."), 'error' );
}
}
It should better work.

Related

Conditional remove "proceed to checkout" button in Braintree for WooCommerce plugin

I'm using in the wc_braintree_cart_payment_gateways filter hook, this code to remove the "proceed to checkout" button in WooCommerce:
add_filter( 'wc_braintree_cart_payment_gateways', function ( $gateways ) {
$categories = [aaa,bbb];
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
// Check for product categories
if ( ! has_term($categories, 'product_cat', $cart_item['product_id'] ) ) {
$gateways = [];
break;
}else{
remove_action( 'woocommerce_proceed_to_checkout', 'woocommerce_button_proceed_to_checkout', 20 );
}
}
return $gateways;
} );
The first condition works as intented. The remove_action doesn't take affect. I'm not sure it's correctly placed? Any advice?
I don't use the plugin you are talking about but adding a (lower) priorty number to the hook can provide the solution.
function filter_wc_braintree_cart_payment_gateways ( $gateways ) {
// Remove proceed to checkout button
remove_action( 'woocommerce_proceed_to_checkout', 'woocommerce_button_proceed_to_checkout', 20 );
return $gateways;
}
add_filter( 'wc_braintree_cart_payment_gateways', 'filter_wc_braintree_cart_payment_gateways', 10, 1 );
If this works, you can extend your code

Hide WooCommerce Payment Method Function Disables WordPress nav-menus.php

I have made hidden the other payment methods if selected COD method (which is flat_rate:5 in my case). I. am using Show hide payment methods based on selected shipping method in Woocommerce answer where I have made some minor edits.
The code works exactly as it should be however, I can't edit menus in "Appearance -> Menus" screen. It seems no problem in front-end.
function payment_gateways_based_on_chosen_shipping_method( $gateways ) {
// Get chosen shipping methods
$chosen_shipping_methods = (array) WC()->session->get( 'chosen_shipping_methods' );
if ( in_array( 'flat_rate:5', $chosen_shipping_methods ) )
{
unset( $gateways['bacs'] );
unset( $gateways['iyzico'] );
}
return $gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'payment_gateways_based_on_chosen_shipping_method' );
I am totally confused where is the bug and why it only appears on "Appearence -> Menus" page.
Also this guy from WordPress support forum had encounter similar issue with different code but in same filter (woocommerce_available_payment_gateways).
Thank you.
That's because WC()->session is null on backoffice. So you can't call method "get".
You need to check if you are in admin area before execute your code:
function payment_gateways_based_on_chosen_shipping_method( $gateways ) {
if(is_admin()){
return $gateways;
}
// Get chosen shipping methods
$chosen_shipping_methods = (array) WC()->session->get( 'chosen_shipping_methods' );
if ( in_array( 'flat_rate:5', $chosen_shipping_methods ) ){
unset( $gateways['bacs'] );
unset( $gateways['iyzico'] );
}
return $gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'payment_gateways_based_on_chosen_shipping_method' );

Add a fee based on chosen shipping method in WooCommerce

In WooCommerce, I need to add a fee when user select specific shipping methods.
I have found "Add a fee based on shipping method and payment method in Woocommerce" answer, that looks like what I need.
I have tried the code and removed the pieces about payment method which I don't need.
The problem is that when I change the shipping method the fee is not added, looks like that I just keep the old value in the session.
This is my code:
// Add a conditional fee
add_action( 'woocommerce_cart_calculate_fees', 'add_cod_fee', 20, 1 );
function add_cod_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$chosen_shipping_method_id = WC()->session->get( 'chosen_shipping_methods' )[0];
var_dump($chosen_shipping_method_id); die;
switch($chosen_shipping_method_id){
case 'flat_rate:3': {
$fee_text = __( "Spese per ritiro", "woocommerce" );
$cart->add_fee( $fee_text, 12, false );
break;
}
case 'flat_rate:4': {
$fee_text = __( "Spese per consegna a domicilio", "woocommerce" );
$cart->add_fee( $fee_text, 24, false );
break;
}
}
}
// Refresh checkout on payment method change
add_action( 'wp_footer', 'refresh_checkout_script' );
function refresh_checkout_script() {
// Only on checkout page
if( is_checkout() && ! is_wc_endpoint_url('order-received') ) :
?>
<script type="text/javascript">
jQuery(function($){
// On payment method change
$('form.woocommerce-checkout').on( 'change', '.shipping_method', function(){
// Refresh checkout
$('body').trigger('update_checkout');
});
})
</script>
<?php
endif;
}
It just keep the old value in $chosen_shipping_method_id
Your code works nicely if you comment var_dump($chosen_shipping_method_id); die;. Also the jQuery script is not needed as it was for payment methods that doesn't update checkout by default.
So there is something else that is making trouble in your case.
Now I have revisited a bit your code (it will work too):
// Add a conditional fee
add_action( 'woocommerce_cart_calculate_fees', 'flat_rate_based_fee', 20, 1 );
function flat_rate_based_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
if( in_array( 'flat_rate:3', $chosen_shipping_methods ) ) {
$fee = array( 'text' => __( "Spese per ritiro", "woocommerce" ), 'amount' => 12 );
} elseif ( in_array( 'flat_rate:4', $chosen_shipping_methods ) ) {
$fee = array( 'text' => __( "Spese per consegna a domicilio", "woocommerce" ), 'amount' => 24 );
}
if( isset($fee) ) {
$cart->add_fee( $fee['text'], $fee['amount'], false );
}
}
Code goes in functions.php file of your active child theme (or active theme).
Tested and work (on Woocommerce 3.5.x and 3.6.x). See it working live on this test server.

Reset previous chosen shipping method in Woocommerce checkout page

Currently, I am clearing the default shipping selection method using this filter:
add_filter( 'woocommerce_shipping_chosen_method', '__return_false', 99);
However, this only clears it during the initial session of the customer. Once the customer chooses an option even once, it remembers the selection for the future.
I am trying to get the checkout to force the customer to pick a shipping option every time they visit the checkout even in the same session. Is it possible to run this filter every time the checkout page is loaded?
You can reset the last chosen shipping method using in checkout page (for logged in customers):
delete_user_meta( get_current_user_id(), 'shipping_method' );
And also remove the chosen shipping method from session data:
WC()->session->__unset( 'chosen_shipping_methods' );
In a hooked function like:
add_action( 'template_redirect', 'reset_previous_chosen_shipping_method' );
function reset_previous_chosen_shipping_method() {
if( is_checkout() && ! is_wc_endpoint_url() && is_user_logged_in()
&& get_user_meta( get_current_user_id(), 'shipping_method', true ) ) {
delete_user_meta( get_current_user_id(), 'shipping_method' );
WC()->session->__unset( 'chosen_shipping_methods' );
}
}
Or you can also set a default shipping method for everybody in checkout page:
add_action( 'template_redirect', 'reset_previous_chosen_shipping_method' );
function reset_previous_chosen_shipping_method() {
if( is_checkout() && ! is_wc_endpoint_url() && is_user_logged_in() ) {
WC()->session->set( 'chosen_shipping_methods', array('flat_rate:14') );
}
}
To find out the shipping methods rate Ids to be used, you can inspect the shipping method radio buttons in cart or checkout pages, with your browser inspector like:
Code goes on function.php file of your active child theme (or active theme). It should works.
Found this solution, it might be a bit hacky.
Just unsets currently selected shipping method and then checks if it's present while validating the checkout.
add_action(
'woocommerce_before_checkout_form',
'reset_previous_chosen_shipping_method'
);
function reset_previous_chosen_shipping_method()
{
if (is_checkout() && !is_wc_endpoint_url()) {
unset(WC()->session->chosen_shipping_methods);
}
}
add_action('woocommerce_after_checkout_validation', 'validate', 10, 2);
function validate($data, $errors)
{
if (!$data['shipping_method']) {
$errors->add('validation', __('Please select shipping method'));
}
}

Disable a shipping method for a specific payment method in Woocommerce

On Woocommerce, I have enabled 2 shipping methods: Free shipping or Flat rate. I have enabled 2 payment methods: Bank transfer (bacs) and PayPal (paypal).
What I want to achieve:
If a customer selects PayPal as payment type he should be forced to select "Flat rate" as shipping method. "Free shipping" should be either hidden or greyed out or something like that.
If bank transfer is chosen then both shipping methods should be available.
Any help is appreciated.
If anyone is interested, I found a solution:
function alter_payment_gateways( $list ){
// Retrieve chosen shipping options from all possible packages
$chosen_rates = ( isset( WC()->session ) ) ? WC()->session->get( 'chosen_shipping_methods' ) : array();
if( in_array( 'free_shipping:1', $chosen_rates ) ) {
$array_diff = array('WC_Gateway_Paypal');
$list = array_diff( $list, $array_diff );
}
return $list;
}
add_action('woocommerce_payment_gateways', 'alter_payment_gateways');
This code will deactivate PayPal if a customer selects free shipping.
Update 2: The following code will disable "free_shipping" shipping method (method ID) when "paypal" is the chosen payment method:
add_filter( 'woocommerce_package_rates', 'shipping_methods_based_on_chosen_payment', 100, 2 );
function shipping_methods_based_on_chosen_payment( $rates, $package ) {
// Checking if "paypal" is the chosen payment method
if ( WC()->session->get( 'chosen_payment_method' ) === 'paypal' ) {
// Loop through shipping methods rates
foreach( $rates as $rate_key => $rate ){
if ( 'free_shipping' === $rate->method_id ) {
unset($rates[$rate_key]); // Remove 'Free shipping'shipping method
}
}
}
return $rates;
}
// Enabling, disabling and refreshing session shipping methods data
add_action( 'woocommerce_checkout_update_order_review', 'refresh_shipping_methods', 10, 1 );
function refresh_shipping_methods( $post_data ){
$bool = true;
if ( WC()->session->get('chosen_payment_method' ) ) $bool = false;
// Mandatory to make it work with shipping methods
foreach ( WC()->cart->get_shipping_packages() as $package_key => $package ){
WC()->session->set( 'shipping_for_package_' . $package_key, $bool );
}
WC()->cart->calculate_shipping();
}
// Jquery script for checkout page
add_action('wp_footer', 'refresh_checkout_on_payment_method_change' );
function refresh_checkout_on_payment_method_change() {
// Only checkout page
if( is_checkout() && ! is_wc_endpoint_url() ):
?>
<script type="text/javascript">
jQuery(function($){
// On shipping method change
$('form.checkout').on( 'change', 'input[name^="payment_method"]', function(){
$('body').trigger('update_checkout'); // Trigger Ajax checkout refresh
});
})
</script>
<?php
endif;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
To get the related shipping methods rate IDs, something like flat_rate:12, inspect with your browser code inspector each related radio button attribute name like:
Note: Since WooCommerce new versions changes, sorry, the code doesn't work anymore.

Categories