I am trying to prevent access to the checkout page when there are no shipping options available. I have disabled the "proceed to checkout' button but want to prevent the customer from directly accessing the checkout page. I have tried this snippet which works in terms of preventing the access, however if a customer DOES have a valid shipping option the success message is replaced by the cart page which now of course has nothing in it and informs the customer their cart is empty so they try an re-order again.
Any help gratefully welcome.
function prevent_checkout_access_no_shipping() {
// Check that WC is enabled and loaded
if( function_exists( 'is_checkout' ) && is_checkout() ) {
// get shipping packages and their rate counts
$packages = WC()->cart->get_shipping_packages();
foreach( $packages as $key => $pkg ) {
$calculate_shipping = WC()->shipping->calculate_shipping_for_package( $pkg );
if( empty( $calculate_shipping['rates'] ) ) {
wp_redirect( esc_url( WC()->cart->get_cart_url() ) );
exit;
}
}
}
}
add_action( 'wp', 'prevent_checkout_access_no_shipping' );
You can try this below code. In your code, you redirect after the first empty shipping rate
function prevent_checkout_access_no_shipping() {
// Check that WC is enabled and loaded
if( function_exists( 'is_checkout' ) && is_checkout() ) {
$packages_valid = 0;
// get shipping packages and their rate counts
$packages = WC()->cart->get_shipping_packages();
foreach( $packages as $key => $pkg ) {
$calculate_shipping = WC()->shipping->calculate_shipping_for_package( $pkg );
if( !empty( $calculate_shipping['rates'] ) ) {
$packages_valid++;
}
}
if (!$packages_valid) {
wp_redirect( esc_url( WC()->cart->get_cart_url() ) , 302);
exit;
}
}
}
add_action( 'template_redirect', 'prevent_checkout_access_no_shipping' );
Related
I am trying to add an action that will check if there is a product in the cart on backorder and if true display a message before the checkout form. This is what i have so far but it doesn't seem to be working. Am i messing something ?
add_action( 'woocommerce_before_checkout_form', 'checkout_add_cart_notice' );
function checkout_add_cart_notice() {
$message = "Please allow 2-3 weeks for the custom order product.";
if ( check_cart_has_backorder_product() )
wc_add_notice( $message, 'error' );
}
function check_cart_has_backorder_product() {
foreach( WC()->cart->get_cart() as $cart_item_key => $values ) {
$cart_product = wc_get_product( $values['data']->get_id() );
if( $cart_product->is_on_backorder() )
return true;
}
return false;
}
The following code will display a custom message on checkout page when there is backordered items in cart:
add_action( 'woocommerce_before_checkout_form', 'backordered_items_checkout_notice' );
function backordered_items_checkout_notice() {
$found = false;
foreach( WC()->cart->get_cart() as $cart_item ) {
if( $cart_item['data']->is_on_backorder( $cart_item['quantity'] ) ) {
$found = true;
break;
}
}
if( $found ) {
wc_print_notice( __("Please allow 2-3 weeks for the custom order product.", "woocommerce"), 'notice' );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
I have this code:
add_action('template_redirect', 'woo_custom_redirect');
function woo_custom_redirect( $redirect ) {
if (
! is_user_logged_in()
&& (is_checkout())
) {
wp_redirect( home_url( '/my-account/edit-account/' ) );
return $redirect;
}
}
How can I replace the redirection condition when I make an order to purchase a product from a certain category?
For example, if a user purchases a product from a certain category, then when he attempts to place an order, he redirects to registration and back, after successful registration.
The following code will redirect to my account page the non logged user in checkout page when they have an item from a specific product category. You will have to define in the code your specific product category:
add_action('template_redirect', 'woo_custom_redirect');
function woo_custom_redirect( $redirect ) {
// HERE set your product category (can be term IDs, slugs or names)
$category = 'posters';
$found = false;
// CHECK CART ITEMS: search for items from our product category
foreach ( WC()->cart->get_cart() as $cart_item ){
if( has_term( $category, 'product_cat', $cart_item['product_id'] ) ) {
$found = true;
break;
}
}
if ( ! is_user_logged_in() && is_checkout() && $found ) {
wp_redirect( get_permalink( get_option('woocommerce_myaccount_page_id') ) );
exit();
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
I needed to do this recently and this solution worked for me. This solution checks what product categories the items are in, in the order details once the order has been received. So the redirect will take place after payment has been made.
add_action( 'template_redirect', 'woocommerce_redirect_after_checkout' );
function woocommerce_redirect_after_checkout() {
global $wp;
if ( is_checkout() && ! empty( $wp->query_vars['order-received'] ) ) {
$order_id = absint( $wp->query_vars['order-received'] );
$order = wc_get_order( $order_id );
$prod_category = [CAT ID OR SLUG];
foreach( $order->get_items() as $item ) {
if( has_term( $prod_category, 'product_cat', $item['product_id'] ) ) {
// change below to the URL that you want to send your customer to
$redirect_url = 'https://www.example.com/custom-thank-you/';
wp_redirect($redirect_url );
exit;
}
}
}
}
I use Lirox One theme on Wordpress with WooCommerce. I want make custom redirections after payment:
If a customer buy product ID 333, It will be redirected to product 444 (for example).
I have make some custom code but it doesn't works, I get an error 500 (and debug is empty).
What I am doing wrong and how can I make it work?
This is my code:
add_action( 'woocommerce_thankyou', 'check_order_product_id', 1 );
function check_order_product_id( $order_id ){
$order = new WC_Order( $order_id );
$items = $order->get_items();
foreach ( $items as $item ) {
$product_id = $item['product_id'];
//* single product id
if ( $product_id == 399 ) {
// Content Title line
$url = 'http://yoursite.com/custom-url1';
}
if ( $product_id == 358 ) {
$url = 'http://yoursite.com/custom-url2';
}
if ( $product_id == 398 ) {
$url = 'http://yoursite.com/custom-url3';
}
if ( $product_id == 357) {
$url = 'http://yoursite.com/custom-url5';
}
if ( $product_id == 356) {
$url = 'http://yoursite.com/custom-url6';
}
if ( $product_id == 335) {
$url = 'http://yoursite.com/custom-url';
}
if ( $order->status != 'failed' ) {
wp_redirect($url);
exit;
}
For redirections, use a custom function hooked in WordPress template_redirect action hook, to avoid errors 500…
I have changed your code as you will see, to match your requirements. This code is made for WooCommerce version 3+:
add_action( 'template_redirect', 'conditional_redirection_after_payment');
function conditional_redirection_after_payment(){
// When "thankyou" order-received page is reached …
if ( is_wc_endpoint_url( 'order-received' ) ) {
global $wp;
// Get the order ID from the browser url
$order_id = intval( str_replace( 'checkout/order-received/', '', $wp->request ) );
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
// If the order status is 'failed' we stop the function
if( $order->has_status( 'failed' ) ) return;
// HERE set in the array for each product ID the coresponding url final path
$product_id_url_paths = array(
'399' => '/custom-url1',
'358' => '/custom-url2',
'398' => '/custom-url3',
'357' => '/custom-url4',
'356' => '/custom-url5',
'335' => '/custom-url6',
);
// Iterating through each order items
foreach( $order->get_items() as $item_id => $item_values ){
// The Product ID
$product_id = $item_values->get_product_id();
foreach( $product_id_url_paths as $key_id => $url_path ){
if( $key_id == $product_id ){
// Product is found and ID match: we got our path url. We redirect
wp_redirect( home_url( $url_path ) );
exit(); // always exit
}
}
}
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Code is tested and works on WC 3+
Note: In your Code you have to remove the exit which you have used after the wp_redirect. Other all will work fine. Some more References are provided below in order to have better understanding of the Concept.
Explanation: Then you Provide Redirect only for the Particular Product
ID alone not For all the Product IDS since it may lead you to the
Internal Server Error as it is in the Foreach Loop.
It is Fine using wp_redirect() for the redirection purpose.
It is correct what have you checked for the Product ID. The Thing is that you have to follow another method if the URL are to be explicitly passed.
// We don't know for sure whether this is a URL for this site,
// so we use wp_safe_redirect() to avoid an open redirect.
wp_safe_redirect( $url );
// We are trying to redirect to another site, using a hard-coded URL.
wp_redirect( 'https://example.com/some/page' );
Some More Reference:
<?php wp_redirect( home_url() ); exit; ?>
Redirects can also be external, and/or use a “Moved Permanently” code :
<?php wp_redirect( 'http://www.example-one.com', 301 ); exit; ?>
The code below redirects to the parent post URL which can be used to redirect attachment pages back to the parent.
<?php wp_redirect( get_permalink( $post->post_parent ) ); exit; ?>
Reference: https://developer.wordpress.org/reference/functions/wp_redirect/
Th accepted answer works for Woocommerce 3+, for older versions (I'm using 2.6.4) it does not. For anyone else using this version, try the following code:
add_action( 'template_redirect', 'conditional_redirection_after_payment');
function conditional_redirection_after_payment(){
// When "thankyou" order-received page is reached …
if ( is_wc_endpoint_url( 'order-received' ) ) {
global $wp;
$order_id = intval( str_replace( 'checkout/order-received/', '', $wp->request ) );
// Get an instance of the WC_Order object
$order = new WC_Order( $order_id );
// If the order status is 'failed' we stop the function
if( $order->has_status( 'failed' ) ) return;
// HERE set in the array for each product ID the coresponding url final path
$product_id_url_paths = array(
'1234' => '/your-path/'
);
// Iterating through each order items
foreach( $order->get_items() as $item){
// echo $item_id;
// The Product ID
$product_id = $item['product_id'];
foreach( $product_id_url_paths as $key_id => $url_path ){
if( $key_id == $product_id ){
// Product is found and ID match: we got our path url. We redirect
wp_redirect( home_url( $url_path ) );
exit(); // always exit
}
}
}
}
}
I would like to disable all payments gateways under special situation: I've 2 special products that I don't want to be combined at checkout with any other product.
Lets say that my "special" products IDs are 496 and 484. All other are "normal" products.
if one of these "special" products is in the cart, I want to disable "paypal" for example.
if a customer has in his cart, at once, a "special" product and a "normal" product, I want to disable all the payments gateway, so he can't checkout.
This is my code:
//disable add to cart if
add_filter( 'woocommerce_available_payment_gateways', 'filter_gateways', 1);
function filter_gateways( $gateways )
{
global $woocommerce;
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
// store product IDs in array
$nonPPproducts = array(496,484);
if (in_array( $values['product_id'], $nonPPproducts ) ) {
unset($gateways['cod'], $gateways['bacs'], $gateways['cheque'], $gateways['stripe']);
} elseif ( in_array( $values['product_id'], $nonPPproducts ) && in_array( $values['product_id'] ) ) {
unset($gateways['under-review'], $gateways['cod'], $gateways['bacs'], $gateways['cheque'], $gateways['stripe']);
}
}
return $gateways;
}
But I can't figure out why the only first if statement works… In other words whatever the situation, all payment gateways are disabled except under-review payment gateway.
What I am doing wrong?
How can I achieve this?
Thanks
Updated for WooCommerce 3+
First I think that in_array( $values['product_id'] ) in your code is not working as a correct condition and so your else statement is never "true". Then as a customer can have many items in his cart, depending on customer successive choices, with your code there will be many redundant gateway unsets…
Here it is your code revisited (you will need to put the desire unset gateways in each statement):
add_filter( 'woocommerce_available_payment_gateways', 'filter_gateways', 1);
function filter_gateways( $gateways ){
// Not in backend (admin)
if( is_admin() )
return $gateways;
// Storing special product IDs in an array
$non_pp_products = array( 496, 484 );
// Needed variables
$is_non_prod = false;
$is_prod = false;
$count = 0;
foreach ( WC()->cart->get_cart() as $cart_item ) {
// count number of items if needed (optional)
$count++;
$product = $cart_item['data'];
if( ! empty($product) ){
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;
if ( in_array( $product_id, $non_pp_products ) && ! $is_non_prod )
$is_non_prod = true;
if ( !in_array( $product_id, $non_pp_products ) && !$is_prod )
$is_prod = true;
}
}
if ( $is_non_prod && ! $is_prod ) // only special products
{
// unset only paypal;
unset( $gateways['paypal'] );
}
elseif ( $is_non_prod && $is_prod ) // special and normal products mixed
{
// unset ALL GATEWAYS
unset( $gateways['cod'],
$gateways['bacs'],
$gateways['cheque'],
$gateways['paypal'],
$gateways['stripe'],
$gateways['under-review'] );
}
elseif ( ! $is_non_prod && $is_prod ) // only normal products (optional)
{
// (unset something if needed)
}
return $gateways;
}
Naturally this code goes on functions.php file of your active child theme or theme.
I'm trying to disable a couple of payment gateways based on a user's role. The function & hook I found works on the Paypal method but not Amazon Payments Advanced. Here's my code:
function wk_disable_gateways( $available_gateways ) {
global $woocommerce;
$wholesale_cust = check_user_role( array( 'wholesale', 'orig-wholesale' ) );
if ( isset( $available_gateways['paypal'] ) && $wholesale_cust ) {
unset( $available_gateways['paypal'] );
}
if ( isset( $available_gateways['amazon_payments_advanced'] ) && $wholesale_cust ) {
unset( $available_gateways['amazon_payments_advanced'] );
}
return $available_gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'wk_disable_gateways' );
The "Pay with Amazon" code is still running on the checkout page. Any ideas?
This is not the best solution, but I have not been able to find a more viable answer.
First off I did what you did and disabled all gateways expect the one I wanted the user to use. In my case I only want someone to check out with the nmigateway.
This goes inside of your theme functions.php
add_filter( 'woocommerce_available_payment_gateways', 'filter_gateways', 1);
function filter_gateways( $gateways ){
global $woocommerce;
// what products you wish to exculde
$nonPPproducts = array(1457, 1447, 479); // LIST YOUR PRODUCT IDS HERE
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
if ( in_array( $values['product_id'], $nonPPproducts ) ) {
foreach ( $gateways as $gateway_key => $gateway ) {
if ( $gateway_key !== 'nmipay' ) {
unset( $gateways[ $gateway_key ] );
}
}
}
}
return $gateways;
}
Next here is the part that makes this not the best solution editing the plugins source code.
Change the following two functions inside of the plugins/woocommerce-gateway-amazon-payments-advanced/amazon-payments-advanced.php
/**
* Checkout Button
*
* Triggered from the 'woocommerce_proceed_to_checkout' action.
*/
function checkout_button() {
global $woocommerce;
// what products you wish to exculde
$nonPPproducts = array(1457, 1447, 479); // LIST YOUR PRODUCT IDS HERE
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
if ( in_array( $values['product_id'], $nonPPproducts ) ) {
$disable_button = true;
}
}
if(!isset($disable_button) && $disable_button !== true ){
?><div id="pay_with_amazon"></div><?php
}
}
/**
* Checkout Message
*/
function checkout_message() {
global $woocommerce;
// what products you wish to exculde
$nonPPproducts = array(1457, 1447, 479); // LIST YOUR PRODUCT IDS HERE
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
if ( in_array( $values['product_id'], $nonPPproducts ) ) {
$disable_button = true;
}
}
if(!isset($disable_button) && $disable_button !== true ){
if ( empty( $this->reference_id ) ) {
echo '<div class="woocommerce-info info"><div id="pay_with_amazon"></div> ' . apply_filters( 'woocommerce_amazon_pa_checkout_message', __( 'Have an Amazon account?', 'woocommerce-gateway-amazon-payments-advanced' ) ) . '</div>';
}
}
}
Keep in mind that when the plugin is updated all of your changes will be lost.