WooCommerce: Disable Payment Method for Specific Category

Today we take a look at the WooCommerce Checkout and specifically at how to disable a payment gateway (e.g. PayPal) if certain product categories are in the cart.

WooCommerce: Disable Payment Method for Specific Category
WooCommerce: Disable Payment Method for Specific Category

Disable Payment Method for Specific Category

/**
 * @snippet       Disable Payment Method for Specific Category
 * @how-to        Watch tutorial @ https://businessbloomer.com/?p=19055
 * @author        Rodolfo Melogli
 * @compatible    WC 3.6.2
 * @donate $9     https://businessbloomer.com/bloomer-armada/
 */
 
add_filter( 'woocommerce_available_payment_gateways', 'bbloomer_unset_gateway_by_category' );
 
function bbloomer_unset_gateway_by_category( $available_gateways ) {
if ( is_admin() ) return $available_gateways;
$unset = false;
$category_ids = array( 8, 37 );
foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
    $terms = get_the_terms( $values['product_id'], 'product_cat' );    
    foreach ( $terms as $term ) {        
        if ( in_array( $term->term_id, $category_ids ) ) {
            $unset = true;
            break;
        }
    }
}
if ( $unset == true ) unset( $available_gateways['cheque'] );
return $available_gateways;
}

Is There a WooCommerce “Payment Gateways by Category” Plugin?

If you don’t feel 100% confident with coding, I decided to look for a reliable plugin that achieves the same result of this snippet (and more).

In this case, I found the WooCommerce Conditional Payment Gateways plugin to be the most complete when you need to enable/disable payment gateways based on certain criteria. You can create unlimited “rules” and use, for example, cart totals, billing country, shipping country, user role and much more to define which payment gateway shows and which not.

But in case you don’t want to use plugins and wish to code (or wish to try that), then keep reading πŸ™‚

Where to add this code?

You can place PHP snippets at the bottom of your child theme functions.php file (before "?>" if you have it). CSS, on the other hand, goes in your child theme style.css file. Make sure you know what you are doing when editing such delicate files - if you need more guidance, please take a look at my free WooCommerce Customization video tutorial.

Does this snippet still work?

Please let me know in the comments if everything worked as expected. I would be happy to revise the snippet if you report otherwise (please provide screenshots). I have tested this code with Storefront theme, the WooCommerce version listed above and a WordPress-friendly hosting on PHP 7.

If you think this code saved you time & money, please join other Business Bloomer supporters and avail of 365 days of WooCommerce benefits. Thank you in advance :)

Need Help with WooCommerce?

Check out these free videos, tutorials and tips!

  • how-to-edit-woocommerce-with-php-snippets
  • woocommerce-hooks-add_action-list-visual
  • woocommerce-customize-single-product-page-PHP
  • woocommerce-customize-shop-page-PHP
  • woocommerce-advanced-customization
  • how-to-edit-woocommerce-cart
  • woocommerce-customize-checkout-page-PHP
  • woocommerce-email-customization
  • woocommerce-conditional-logic

Rodolfo Melogli

Author, WooCommerce expert and WordCamp speaker, Rodolfo has worked as a WooCommerce freelancer since 2011. His goal is to help entrepreneurs and developers overcome their WooCommerce nightmares. Rodolfo loves travelling, chasing tennis & soccer balls and, of course, wood fired oven pizza.

84 thoughts on “WooCommerce: Disable Payment Method for Specific Category

  1. Thank you for the snippet.

    It works fine but if I use it it breaks wp-admin/nav-menus.php page, menu items do not show, please help.

    I have WP 5.1.1 and Woocommerce 3.5.7

    Here is my code (same as yours but changed category ID)

    add_filter( 'woocommerce_available_payment_gateways', 'bbloomer_unset_gateway_by_category' );
      
    function bbloomer_unset_gateway_by_category( $available_gateways ) {
    $unset = false;
    $category_ids = array( 22 );
    foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
        $terms = get_the_terms( $values['product_id'], 'product_cat' );    
        foreach ( $terms as $term ) {        
            if ( in_array( $term->term_id, $category_ids ) ) {
                $unset = true;
                break;
            }
        }
    }
        if ( $unset == true ) unset( $available_gateways['cod'] );
        return $available_gateways;
    }
    1. Thank you Damir – snippet now updated with the bug fix πŸ™‚

  2. Hi Rodoflo,

    I’m trying to disable a specific Payment method for specific category. It looks like its working fine on the Frontend, but in the backend I get this error message:

    Warning: Invalid argument supplied for foreach() in /srv/users/*****/functions.php on line 78.
    Line 78 is
    foreach ( $woocommerce->cart->cart_contents as $key => $values ) {

    Thank you
    Kolya

    1. Snippet updated πŸ™‚

  3. Hello Rodolfo,
    It’s possibile to setup the limit for product instead categories?
    How could I setup for 3 product, for example?

    Thanks in advance,
    Mario

    PS: parli anche italiano?

    1. Ciao Mario, thanks so much for your comment! Yes, this is possible – unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding! ~R

  4. Hi Rodolfo,

    Thanks for this! Really helpful. Just so I’m clear, to customize this to my site, which parts do I need replace, say, if I’m just trying to NOT have cod payment gateway for category id 32. I’m not sure what other information I need to use or replace based on all of the terms in the code you provided.

    Sorry! Big newbie.

    1. Hey there πŸ™‚ Yes, just the IDS ($category_ids) and the gateway name ($available_gateways). Hope this helps

  5. Hi Rodolfo,
    this is useful cause i want to hide ‘paypal’ and ‘bacs’ for a specific product, but keeping ‘cheque’.

    But what if I want to hide ‘cheque’ for all other products and keep ‘paypal’ and ‘bacs’ instaed?

    I’d really appreciate your help with this,

    thank you.

    1. Hello Luigi, thanks so much for your comment! Yes, this is possible – unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding! ~R

  6. hello, how can I edit this by members?

    1. Hello Gokhan, thanks so much for your comment! Yes, this is possible – unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  7. Rodolfo this is great! Thank you for making this solution accessible.

    My site allows customers to pay by CC or by invoice for physical goods, but I’ll soon be offering virtual products (e-learning courses) as well so I need to prevent customers from using these courses without securing payment first.

    If the logic depends on an attribute or tag value it seems that it would more flexible and operationally scalable for me to manage. Would it possible for me to modify this snippet to disable a payment method based on a product attribute or tag rather than a category?

    1. Thank you Carey πŸ™‚ Yes of course you can!

  8. Thanks Rodolfo! I update your snippet because I need unset only when all products in cart has the category.

     foreach ( $woocommerce->cart->cart_contents as $key => $values ) {
            $terms = get_the_terms( $values['product_id'], 'product_cat' );    
            foreach ( $terms as $term ) {        
                if ( in_array( $term->term_id, $category_ids ) ) {
                    $unset = true;
                    break;
                }
    	    else
    	        $unset = false;
            }
    	if ( $unset == false ) break;
        }
    

    keep up the great work!

    1. Thanks!

  9. Hi
    This is not working post latest woo-commerce update. Pl check .

    Thanks

    1. Works perfectly on 3.5.1 πŸ™‚

  10. Hello, i love your website, it’s very usefull for me, thank’s. i have a question, i search snippet code for see payment method in dashboard woocommerce, can you help me ? Thank you. Adrien

    1. Hey Adrien – thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding! ~R

  11. Hi,
    I want to add conditional logic to a specific payment gateway. Like — Only when a person clicks on Direct Bank Transfer, I want a specific checkout field to be displayed in my checkout form on WooCommerce

    1. Hello Khush, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding! ~R

  12. Hi Rodolfo Melogli,

    Successfully added and its working. I have two quick questions. I want to add a single line message that ‘This product is not available on COD’ something like this. And, what if two products are added to the cart, one from non-cod category and other from cod. It will checkout successfully. How to stop it from happening?

    1. Hello Muhammad, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding! ~R

  13. AWEESOOME!! Great! Really wonderfull…

    1. πŸ™‚

  14. Hello Rodolfo!

    thank you so much for this code snippet. When I use it on my site though I get the following message in the debug.log:

    [10-Apr-2018 17:30:05 UTC] PHP Notice: Trying to get property of non-object in /XXXXXXX/functions.php on line 163
    [10-Apr-2018 17:30:05 UTC] PHP Warning: Invalid argument supplied for foreach() in /XXXXXXX/functions.php on line 163

    Line 163 is:
    foreach ( $woocommerce->cart->cart_contents as $key => $values ) {

    Any idea what causes this warning? It seems it works fine on the frontend, but still what to clean up the log-file.

    Thanks for your help

    1. Hey Alfons, thanks so much for your comment! I can’t replicate this I’m afraid, maybe you’re using out of date PHP? As a test, try to assign the array to a variable and use the variable name in the foreach instead:

      $cart_items = $woocommerce->cart->cart_contents;
      foreach ( $cart_items as $key => $values ) {
      
  15. Hello! I have a silly question: how do I find the category ID? I looked in Products -> Categories -> Edit category and I dind’t so any number.

    Also, how this code works: if category 20 has a payment disabled and it the cart is added a product from category 15 which has no payment restriction, then in the checkout page that payment method is not displayed? I hope I made myslef clear.

    1. Thank you! It worked like a charm.

  16. Installed and working on WordPress 4.7.9 with WooCommerce 2.6, Organic Web Shop theme by Colibri Interactive.
    Thank you very much!

  17. Hi,

    it’s possible disable COD for specific products?

    1. Domenico, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  18. I use WordPress 4.9.2 with Avada theme and your code still works πŸ™‚ thanks

  19. This might seem like an odd question, but when this function is active in my child-theme’s functions.php file, it prevents me from editing the site’s menu (Appearance -> Menu)
    Customizr Pro is the theme. I have been going through the theme files but cannot figure out why this is happening.

    1. Cole, thanks for your comment! I just tested this again with Storefront theme and it works perfectly. Maybe your theme (or another plugin) is messing/conflicting with my snippet?

      To troubleshoot, take a look at this tutorial to see how to troubleshoot: https://businessbloomer.com/woocommerce-troubleshooting-mistakes-to-avoid/

      Finally, can you try switching temporarily to “Twentyseventeen” or “Storefront” theme and let me know if it works?

      Hope this helps!

      R

  20. You my friend will go straight to heaven :).
    This worked perfectly. Thanks for the help.

    1. Wow, thank you so much Victor!

  21. Hola Rodolfo,

    I tried your snippet and at the front end it works flawlessly.
    Unfortunately at the back end woocommerce-settings-checkout-checkout options I get the next two messages:

    “Notice: Trying to get property of non-object in /homepages/38/d696347667/htdocs/clickandbuilds/badboybox/wp-content/themes/wowmall-child/functions.php on line 138”

    “Warning: Invalid argument supplied for foreach() in /homepages/38/d696347667/htdocs/clickandbuilds/badboybox/wp-content/themes/wowmall-child/functions.php on line 138”

    I tried your advice to another user:
    “Could you please try with $woocommerce->cart->get_cart() instead?”

    It did not work for him and me.

    Please advice.

    1. Hola Luis! I just retested the snippet on 3.2.5 and I got no warnings. I also revised it a little. Try the new version and let me know πŸ™‚

  22. Hi Rodolfo,
    I can see I’ll be taking a really good look through your site – these tips are great.
    This is SO close to exactly what I’m after, I was just wondering if you could steer me in the right direction?
    I have a single (virtual) product which I’d like to disable Direct Bank Transfer for.
    Product Category could suffice, but Product ID would be better. Is that possible?
    And where would I look to find the right payment gateway code to add to exclude direct bank transfer?
    Any help at all would be really appreciated πŸ™‚

    1. Hey Jason, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  23. Hi! Your posts are grate. Is there any way to Disable Payment Method (cash on delivery) if Cart has a specific Shipping Class?
    Thank you in advance.

    1. Anos, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  24. Hey there, great tutorial. However, I was looking for something else. I want to disable payment gateways based to specific currency. As in my store I have PayPal and Instamojo for USD & INR. I want if the customer switches currency to USD, only PayPal shows up and when one switches to INR only Instamojo shows up. Would be great if you could help me with that.

    1. Hay Ajay, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  25. Great article, it has been helpful. Though things have changed with the website I use this with. Is there a way to adjust the code so that instead of based on category. It is based on whether the user is a member. Like if they are a member then they can use checks. If not then cheque is not allowed.

  26. OMG, This code is perfect, thumbs up boss, i really appreciate this.

  27. Hi Thanks so much for the lessons really helpful. We are trying to use this snippet with ‘twenty seventeen child theme’, WooCommerce Password Protected Categories and ‘Extended Widget Options’ to facilitate a wholesale shop along-side the regular shop. I want to hide ‘cod’ on products in the retail category. The ID for retail is ‘334’ all retail items are in the subcategory of retail. We have added the code, below to functions.php but it has no effect. Do I need to list each category ID in the retail category? Testing for this is with local installation of MAMP but the live site, without the code below is http://www.crumbleandcore.com
    Also Would it be possible to use your snippets to our complete our wholesale solution. In addition to the above can we hide retail products in the trade category and hide trade category products in the retail category. Note each product has two versions, ‘trade’ and ‘retail’

     
    /**
     * @snippet       Disable Payment Method for Specific Category
     * @how-to        Watch tutorial @ https://businessbloomer.com/?p=19055
     * @sourcecode    https://businessbloomer.com/?p=19892
     * @author        Rodolfo Melogli
     * @testedwith    WooCommerce 2.5.2
     */
     
    add_filter('woocommerce_available_payment_gateways','bbloomer_unset_gateway_by_category');
     
    function bbloomer_unset_gateway_by_category($available_gateways){
    global $woocommerce;
    $category_IDs = array(334);
    foreach ($woocommerce->cart->cart_contents as $key => $values ) {
    $terms = get_the_terms( $values['product_id'], 'product_cat' );    
    foreach ($terms as $term) {        
    if(in_array($term->term_id, $category_IDs)){
        unset( $available_gateways['cod'] );
                break;
            }
        break;
        }
     }
        return $available_gateways;
    }
    
    1. Hi Thanks, I changed the array to each individual category ID seperated by commas and it works. Happy me, brilliant you.

  28. can we disable any other payment gateway other than bacs, cheque, Cod, paypal under woocommerce/include/gateway folder ? Pl advice.

    1. Kumar, thanks for your comment! Yes, you’ll simply need to know what the “gateway ID” is, and then enter that in the code.

  29. Really nice!!!! I am search for something like that for a very long time.

    But could you help me with some changes?

    I would like the cod method display only for one product (and only method), for all the other products the only method displays is stripe.

    1. Hey Leandro, thanks for your comment! If you’d like to get a quote, feel free to contact me here. Thanks a lot! ~R

    2. Thank you, but i solved it just including this:

       else{
            unset( $available_gateways['cod'] );  
          }
      

      Silly of me πŸ™‚

      Thank you,

  30. Hi there Rodolfo,

    Firstly, thanks for providing so many useful articles to the WP community! I really appreciate it.

    I was wondering, do you know if it’s possible to disable a payment method when a text based custom checkout field is not filled in? I’ve looked and looked and I can’t find a solution to this.

    Alternatively, if the above isn’t doable, do you think it would be possible to only show a required checkout field when a certain payment method has been chosen, and in that way only make it required when shown?

    I’m trying to enable bank transfer payments, but only to customers that have filled in a custom field with their institution number.

    Hope you can help! Thanks!

    1. Jonas, thanks so much for your comment! Yes, this is definitely possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  31. Hello!
    I think that this is not working, if not the direct categorie id is in the script.
    So if I only put the main-categorie-id in the script it is not working. Only if I add the direct categorie ID it is effective.

    1. Hey Rene, thanks for your comment! If I understand correctly, this snippet does not work with subcategories – which is possible πŸ™‚

    2. Hey there,
      yes but only the subcategories are working, not the maincategorie.
      Is there a possibility to make it work?

      Thanks for the quick answer :-)!

    3. Hey there,
      I now added following script so for every subcategorie itΒ΄s searching the maincategorie. ItΒ΄s maybe not that efficient and fast..

      add_filter('woocommerce_available_payment_gateways','bbloomer_unset_gateway_by_category');
      function bbloomer_unset_gateway_by_category($available_gateways)
      {
      	global $woocommerce;
      	$super_final_cat = '';
      
      	$category_IDs = array(58);
      	foreach ($woocommerce->cart->cart_contents as $key => $values )
      	{
      		$terms = get_the_terms( $values['product_id'], 'product_cat' );    
      		foreach ($terms as $term)
      		{
      	    	$product_cat_id = $term->term_id;
      	    	$product_parent_categories_all_hierachy = get_ancestors( $product_cat_id, 'product_cat' );  
      	    	$last_parent_cat = array_slice($product_parent_categories_all_hierachy, -1, 1, true);
      	    	foreach($last_parent_cat as $last_parent_cat_value)
      			{
      	        	$super_final_cat = $last_parent_cat_value;
          		}
      
      			if((in_array($term->term_id, $category_IDs)) OR ($term->term_id = $super_final_cat))
      			{
        		  		unset( $available_gateways['paypal_plus'] );
          			unset( $available_gateways['paypal'] );
        		      	break;
         		 	}
         			break;
          	}
      	}
      	return $available_gateways;
      }
      
  32. Greetings Rodolfo – the snippet is helping me, very useful! A question: how would you suggest I add an additional payment gateway to be disabled? E.g I want to disable ‘cod’ *and* ‘cheque’ … thoughts?

    1. Hey Ezekial, thanks for your comment! Just add another “unset” line:

      unset( $available_gateways['cheque'] );
      
  33. Hello Rodolfo, thanks for your snippet, it helps me a ton !

    I’m using it and It disables correctly the paiement method for the selected ID’s.
    However, I have a php warning appearing in my wordpress dashboard stating “Warning: Invalid argument supplied for foreach() in …” on the line of the first foreach.

    Do you happen to know what would be the cause of this error ?
    Again, thanks a lot for your work.

    1. Hey Afka, thanks for your message! Could you please try with $woocommerce->cart->get_cart() instead? Let me know!

    2. Hi Rodolfo,
      Thanks for your answer, much appreciated !

      I tried your solution, so instead of having :

       foreach ($woocommerce->cart->cart_contents as $key => $values ){} 

      I did :

       foreach ($woocommerce->cart->get_cart() as $key => $values ){} 

      Unfortunately, this time it completely breaks the code and gives me a fatal error :
      “Fatal error: Call to a member function get_cart() on null in …”.
      This also only affects the dashboard, the website itself works correctly and the payment method is still disabled.

      Thanks again for taking the time to help me !

    3. No problem, thanks for the follow up πŸ™‚

  34. I want my privileged customers to book the products without paying (Clicking on add to cart will just book the product without payment). We will take the payment through an invoice.

    1. Hello Pritam, thanks for your comment! I guess with “privileged customers” you mean “WordPress users who have a custom user role e.g. wholesalers”. Is that correct?

      If yes, you can just use the “COD” payment gateway and rename it “Pre-book Online”, it’s as simple as that. And you would show “COD” only to a specific user role, e.g. “wholesaler”.

      Does this make sense?

  35. Hello!
    Thanks for the snippet, very helpful. I am trying to do something similar but need a slight tweak and wondering if there was any chance you would be able to help me?

    So like your snippet above, if category ID 40 is in cart, then disable all payment methods except for cheque.

    But the tweak I am after is if category 40 AND category 39 are both in the cart, then all the payment methods should still be displayed. So basically only disable payment methods if only the one category ID of 40 is in the cart.

    I understand if this cannot be done! But thought it was worth asking.

    keep up the great work!

    1. Hey Kimberley, thanks for your comment πŸ™‚ Your request is actually not that difficult… let me try for a minute:

      
      // change this part in the snippet above
      
      $in_cart_40 = false;
      $in_cart_39 = false;
      
      foreach ($terms as $term) {        
       
       if($term->term_id == 40)){
        $in_cart_40 = true;
       } elseif($term->term_id == 39)){
        $in_cart_39 = true;
       }
      
      }
      
      if ( $in_cart_40 = true && $in_cart_39 = false ) {
       unset( $available_gateways['cod'] );
      }
      
      }
      
      

      Basically there is an additional IF to make sure ID 39 and ID 40 are not in the cart. I haven’t tested it but give it a go πŸ™‚

    2. Hi Rodolfo,
      Knew there would need to be a sneaky if somewhere. You have gone above and beyond! Thanks very much, much appreciated!

    3. You’re welcome Kimberley πŸ™‚

  36. Thanks Rodolfo! It is a very useful snippet.

    1. Thank you so much Fecso!!! Your feedback is much appreciated πŸ™‚

Questions? Feedback? Support? Leave your Comment Now!
_____

If you are writing code, please wrap it between: [php]code_here[/php]. Failure to complying with this (as well as going off topic) will result in comment deletion. You should expect a reply in about a week - this is a popular blog but I need to get paid work done first. Please consider joining #BloomerArmada to ask me 1-to-1 WooCommerce questions. Thank you :)

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.