WooCommerce: Disable Payment Method If Product Category @ Cart

Today we take a look at the WooCommerce Checkout and specifically at how to disable a payment gateway (e.g. PayPal) if a specific product category is in the Cart.

There are two tasks to code in this case: (1) based on all the products in the Cart, calculate the list of product categories in the Cart and (2) disable a specific payment ID if the target product category is in the Cart.

May sound difficult but don’t worry – solution is right below. Enjoy!

WooCommerce: Disable Payment Method for Specific Category
WooCommerce: Disable Payment Method for Specific Category. In this example, let’s imagine that “Sale Product” belongs to category “SALE”; well, if “Sale” product category is in the Cart, I wish to hide “COD” (cash on delivery) at Checkout. Makes sense!

PHP Snippet: Disable Payment Method for Specific Category @ WooCommerce Checkout

/**
 * @snippet       Disable Payment Method for Specific Category
 * @how-to        Get CustomizeWoo.com FREE
 * @author        Rodolfo Melogli
 * @compatible    WooCommerce 7
 * @community     https://businessbloomer.com/club/
 */
 
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;
    if ( ! is_checkout() ) return $available_gateways;
    $unset = false;
    $category_id = 8; // TARGET CATEGORY
    foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
        $terms = get_the_terms( $values['product_id'], 'product_cat' );    
        foreach ( $terms as $term ) {        
            if ( $term->term_id == $category_id ) {
                $unset = true; // CATEGORY IS IN THE CART
                break;
            }
        }
    }
    if ( $unset == true ) unset( $available_gateways['cod'] ); // DISABLE COD IF CATEGORY IS IN THE CART
    return $available_gateways;
}

Mini-Plugin: Business Bloomer WooCommerce Toggle Payments By Category

You don’t feel confident with coding? You need more control over your payment/category exclusions? You don’t want to purchase yet another bloated, expensive plugin? Great!

Business Bloomer WooCommerce Toggle Payments By Category is a mini WooCommerce plugin, without the usual hassles. One feature. Lifetime license. No annoying subscriptions. 1 plugin file. A few lines of code. No banners. No up-sells. No WP notifications. Use it on as many websites as you like. Lifetime support. 1-page documentation. A single and easy admin dashboard.

Screenshot of the settings? Here you go:

Quick demo? Here it is:

As you can see the settings are pretty straight forward. Select a payment method you wish to hide/show from the left, and the category that should trigger that from the right. Add more rules if needed. Simple!

Advanced Plugin: WooCommerce Conditional Payment Gateways

If you require something more advanced, I decided to look for a reliable plugin that achieves the same result of this snippet (and much 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, categories, tags, 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 custom code?

You should place custom PHP in functions.php and custom CSS in style.css of your child theme: where to place WooCommerce customization?

This code still works, unless you report otherwise. To exclude conflicts, temporarily switch to the Storefront theme, disable all plugins except WooCommerce, and test the snippet again: WooCommerce troubleshooting 101

Related content

  • WooCommerce: Cart and Checkout on the Same Page
    This is your ultimate guide – complete with shortcodes, snippets and workarounds – to completely skip the Cart page and have both cart table and checkout form on the same (Checkout) page. But first… why’d you want to do this? Well, if you sell high ticket products (i.e. on average, you sell no more than […]
  • WooCommerce: Add Privacy Policy Checkbox @ Checkout
    Here’s a snippet regarding the checkout page. If you’ve been affected by GDPR, you will know you now need users to give you Privacy Policy consent. Or, you might need customer to acknowledge special shipping requirements for example. So, how do we display an additional tick box on the Checkout page (together with the existing […]
  • WooCommerce: Redirect to Custom Thank you Page
    How can you redirect customers to a beautifully looking, custom, thank you page? Thankfully you can add some PHP code to your functions.php or install a simple plugin and define a redirect to a custom WordPress page (as opposed to the default order-received endpoint). This is a great way for you to add specific up-sells, […]
  • WooCommerce: Disable Payment Gateway by Country
    You might want to disable PayPal for non-local customers or enable a specific gateway for only one country… Either way, this is a very common requirement for all of those who trade internationally. Here’s a simple snippet you can further customize to achieve your objective. Simply pick the payment gateway “slug” you want to disable/enable […]
  • WooCommerce: Disable Payment Gateway for Specific User Role
    You may want to disable payment gateways depending on the logged in user role. For example, you may want to disable PayPal for user role “subscriber” or enable a specific gateway for user role “customer”. All you need is to paste the following code in your functions.php or to install a super simple plugin. Enjoy!

Rodolfo Melogli

Business Bloomer Founder

Author, WooCommerce expert and WordCamp speaker, Rodolfo has worked as an independent 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. Follow @rmelogli

172 thoughts on “WooCommerce: Disable Payment Method If Product Category @ Cart

  1. Ok. I asked my question too fast. I found out how to do it. Posting the modified code here in case it helps someone:

    function bbloomer_unset_gateway_by_category( $available_gateways ) {
        if ( is_admin() ) return $available_gateways;
        if ( ! is_checkout() ) return $available_gateways;
        $unset = false;
        $category_ids = array( 5607, 5608, 5609, 5610, 5611, 5612, 5613, 5614 ); //ADD YOUR CATEGORY IDs
        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 )) { //$category_id ) {
                    $unset = true; // CATEGORY IS IN THE CART
                    break;
                }
            }
        }
        if ( $unset == true ) unset( $available_gateways['bacs'] ); // DISABLE BACS IF CATEGORY IS IN THE CART
        return $available_gateways;
    }
    
  2. Hey there!
    As of today (30/11/2022) the snippet works just fine with one category found in the cart 🙂
    Thanks a lot!

  3. Hi,
    Love the plugin though the setting are not saved when the parent plugin (not sure if it’s Woocommerce or Afterpay) is updated. The fields in the Payment Settings are intact (ie; if I have two rows, both remain) though the dropdowns are no longer set (ie; “Select…”, “Select…”) and need to be re-selected and saved.

    1. Sorry for the dalay, Matt, and sorry about this. In order for me to understand the bug, could you tell me what you mean by “when the parent plugin is updated”? Do you mean when you upgrade the plugin to a newest version?

  4. Hi, I installed the plugin and ran it. Works great right now So how do I do something like this: I will add a payment method and with this method, only one category will be shopped. So it’s the opposite of the current plugin.

    1. Hello Tayfun! Thanks for your email – I’m not sure I fully understand what you’re trying to do here. Do you want to make sure a target payment method is only enabled if a certain category is in the cart?

  5. Hey ,

    thanks for the great work and the snippet. Just wanted to let you know about one incident where the snippet won’t work. It is a customer hack actually lol. I use this snippet to hide COD for a category. But if for some reason the credit card payment fails for customer, then the Woo shows the alternative options for payment which includes COD. https://i.postimg.cc/ZqTXN8PT/Screen-Shot-2022-10-18-at-10-41-18-AM.png . Let me know if there’s a way we can remove the payment method even after failed payments.

    Thanks again for the wonderful work.

    1. Hello Joice, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  6. I bought a plug-in but it seems it broke the other plugin which is a “Conditional Checkout Fields for WooCommerce”, if I disable your plugin… it works fine.. Any ways we could make both plugins? The “Conditional Checkout Fields for WooCommerce” has nothing to do with the payment options.

    1. Hello Samuel, and sorry about that! If you could describe what you mean by “broke” and send me the other plugin, I can see if it’s doable. Thank you!

  7. Hi Rodolfo –

    I have a subscription site (powered by Paid Member Subscriptions) – WooCommerce is on there too with regular products and Stripe Gateway. Scenario – subscribers choose their monthly box once a month, handled as a WooCommerce product – I want the order placed but no Payment Gateway offered, the order pushed direct to ‘Processing’ after ‘Place Order’ button pressed during the checkout. So unset all Payment Gateways. Can any of your plugins do this and allow the order to go through? Thanks so much!

    1. Hi Tom, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  8. Wow, an excellent and unique resource! Is there a way to do the opposite? Like enabling certain payment or shipping methods if someone chooses a certain product or category?

  9. In the latest version of woocommerce it has as the first gateway a message to install “Woocommerce payments”, when you click on the select gateway dropbox in your plugin, it shows the html code of the message. How can we avoid it?
    Just download the latest version of wordpress and woocommerce and you will see it at the top of the payments gateway

    1. Hey Rafa, I can’t seem to enable this WooCommerce Payments option. I use the latest WC/WP

      1. I just installed it like a week ago. Is there an email is there a way to send you a screen capture so you can see it?
        Thanks Rodolfo

  10. Hello Roberto, thanks for the great job.
    I tried the code and nothing happens, do i need to buy the plugin for it to work?
    Thanks

    1. I just bought the plugin and it works well.
      Now i want to include just the code without the plugin.
      is there a version that does not include the plugin part?
      Thanks

      1. Sorry for so many comments. I also want to include in a different page, not in the payment methods section.
        How can we do this?

        1. After installation, when i click on add a new row, it whows this error:
          Warning: Undefined array key “gtw” in C:\xampp\htdocs\sandbox1\wp-content\plugins\cofidis-payment\cofidis_payment_categorias.php on line 61

          Warning: Undefined array key “ct” in C:\xampp\htdocs\sandbox1\wp-content\plugins\cofidis-payment\cofidis_payment_categorias.php on line 62

          1. Hi Rafa! Lots of questions, here is my feedback:

            I tried the code and nothing happens, do i need to buy the plugin for it to work?

            Purchasing the plugin was not necessary. Though, the plugin adds a nice backend panel from where you can select gateways and categories.

            Now i want to include just the code without the plugin.
            is there a version that does not include the plugin part?

            Yes. Remove the plugin entirely. Then, copy/paste the “raw” version code into your child theme’s functions.php.

            Sorry for so many comments. I also want to include in a different page, not in the payment methods section.

            Yes, this is possible, but requires a bit of customization. Where exactly would you like to place it?

            Warning: Undefined array key “gtw” in C:\xampp\htdocs\sandbox1\wp-content\plugins\cofidis-payment\cofidis_payment_categorias.php on line 61

            Warnings are not fatal errors, so the plugin should still work. What’s on line 61 and 62 of that Cofidis plugin?

            1. this is what causes the warning and yes it is still working
              $gateway_id = $this_row[‘gtw’] ? $this_row[‘gtw’] : 0;
              $category_slug = $this_row[‘ct’] ? $this_row[‘ct’] : ”;
              We would like to use it for the is_product() page, to show a warning if the product is in a category that can not use a certain payment method

              1. this is what causes the warning and yes it is still working
                $gateway_id = $this_row[‘gtw’] ? $this_row[‘gtw’] : 0;
                $category_slug = $this_row[‘ct’] ? $this_row[‘ct’] : ”;

                Ah, perfect. That happens as I’m not checking if the variable is set. This should fix it:

                $gateway_id = isset( $this_row['gtw'] ) ? $this_row['gtw'] : 0;
                $category_slug = isset( $this_row['ct'] ) ? $this_row['ct'] : '';
                

                Finally,

                We would like to use it for the is_product() page, to show a warning if the product is in a category that can not use a certain payment method

                That’s a different thing than I initially understood from your request – and no, the plugin does not this out of the box. You can totally throw a notice in the single product page that checks the product category against the exclusions database, and return a message in case.

                Hope this helps!

                1. Thanks for the fix, it works as a charm. is it posible to have the selector in a different page?For instance, if we want it to be in one of our plugins page instead the payment gate screen.
                  do you have an example of the code to check if a product is in a banned category?
                  Thanks again, you make our lifes much easier

                  1. Thanks for the fix, it works as a charm. is it posible to have the selector in a different page?For instance, if we want it to be in one of our plugins page instead the payment gate screen.

                    Of course, but you’ll need to DIY or I can help you, but that’s custom work.

                    do you have an example of the code to check if a product is in a banned category?

                    No, but you can reuse some of the code, it’s already written inside the plugin

                    Thanks again, you make our lifes much easier

                    Perfect!

                    1. Thanks for the answer.
                      In the latest version of woocommerce it has as the first gateway a message to install “Woocommerce payments”, when you click on the select gateway dropbox in your plugin, it shows the html code of the message. How can we avoid it?
                      Thanks

                    2. Uhm, unfortunately I can’t see this WooCommerce Payment message on my test website. How can I display it?

  11. Hi Rodolfo

    I paid for your plugin and it works well for hiding Woocommerce payment gateway for one of my product categories. However, in the choose payment gateway to hide section, the MyCred gateway isn’t showing and therefore I cannot disable it for a certain category. Any ideas? Thanks in advance.

    Kind regards

    Benjamin

    1. Sure, checking now. I will keep you posted!

      1. Hey Benjamin, I found the issue, it’s only one line. If you don’t want to wait for the updated files, simply replace inside the bbloomer_unset_gateway_by_category_premium_admin() function:

        $gateways = WC()->payment_gateways->get_available_payment_gateways();

        with

        $gateways = WC()->payment_gateways->payment_gateways();
  12. Just want to say thanks for this great snippet, works perfectly 🙂

  13. Hi @ all,

    iam very sorry but its not working by me.

    I use the theme flatsome and its not working. Paypal is always showing :-(.

    Please need help.

    1. Need more info – share your code please?

      1. Not working for me too!
        This is my code

        /**
         * @snippet       Disable Payment Method for Specific Category
         * @how-to        Get CustomizeWoo.com FREE
         * @author        Rodolfo Melogli
         * @compatible    WooCommerce 5
         * @community     https://businessbloomer.com/club/
         */
          
        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;
            if ( ! is_checkout() ) return $available_gateways;
            $unset = false;
            $category_id = 34; // TARGET CATEGORY - CARNET
            foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
                $terms = get_the_terms( $values['product_id'], 'product_cat' );    
                foreach ( $terms as $term ) {        
                    if ( $term->term_id == $category_id ) {
                        $unset = true; // CATEGORY IS IN THE CART
                        break;
                    }
                }
            }
            if ( $unset == true ) unset( $available_gateways['klarna_payments'] ); // DISABLE KLARNA IF CATEGORY IS IN THE CART
            return $available_gateways;
        }
        

        WooCommerce version 5.9.0
        Cache Cleared

        Thank you.

        1. Hey Simone, could be anything. Snippet works perfectly for me. Are you on the default checkout page (not the checkout block)? Is “klarna_payments” the correct ID? Is “34” correct?

  14. Hello, I’m new to this. how to achieve this on a product level? need to disable a gateway to some product IDs. Also, how to get and display my list of gateway IDs?

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

  16. Hi,

    I want to only disable GoCardless (Direct Debit) as a payment method for a certain category.
    Is this possible and how would I do it?

    Thanks in advance,
    Danny

  17. Thanks So Much very Useful codesnipt

    1. Welcome!

  18. Hi Rodolfo and thank you for this useful snippet.

    I was getting “Invalid argument supplied for foreach()” warnings for the following foreach loop, only when there are any uncategorized products in cart (no term is returned).

            foreach ( $terms as $term ) {        
                if ( in_array( $term->term_id, $category_ids ) ) {
                    $unset = true;
                    break;
                }
            }
    

    Putting this loop inside an if statement like

    if ($terms) {  . . .  }

    or

    if ( is_array( $terms ) ) {  . . .  }

    seems to solve the problem. What do you think, is this the correct solution? Do you think there is any difference between the two approaches?

    Thanks!

    1. I prefer the if ( $terms ) one

  19. Hi, can I ask for for a quick sanity check of the below code please.

    It seems to work as is but if i flip the and and else if statements round it doesnt seem to so I just want to check I am not being a wally.

    The idea is that this way round we can stop Cash On Delivery for product id’s 410 and 538. We will then only enable stripe for these.

    1. Hi Ben, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  20. Hi Rodolfo, after perfectly working for several months your script no longer works. Could it be that it no longer works with woocommerce 3.9.1 or do I have to look for another type of error?

    1. Disable all other plugins and theme and see if it works again 🙂 WooCommerce changed nothing in regard to this as long as I know

  21. Hi Rodolfo,

    many thanks for the snippet – it works great. With only one caveat though. It only works on the checkout page, but if I send order invoice through WC admin panel manually, then the visitor gets all the payment gateways displayed. How could this be adjusted to also apply to manually issued order invoices?

    1. Hey Andrius, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

      1. Great piece of coding this… really helped speed up the solution.

        I managed to do this with the following:

        /**
         * @snippet       Disable Payment Method for Specific Category
         * @how-to        Get CustomizeWoo.com FREE
         * @author        Rodolfo Melogli
         * @compatible    WC 3.6.5
         * @community     https://businessbloomer.com/club/
         */
          
        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;
            if ( ! is_checkout() ) return $available_gateways;
            $unset = false;
            $category_ids = array( 213 );
            
            if ( is_wc_endpoint_url( 'order-pay' ) ) {
                $order = wc_get_order( absint( get_query_var('order-pay') ) );
                foreach ($order->get_items() as $key => $values ) {
                    $terms = get_the_terms( $values['product_id'], 'product_cat' );    
            		if ($terms){
                   	 foreach ( $terms as $term ) {        
                   	     if ( in_array( $term->term_id, $category_ids ) ) {
                   	         $unset = true;
                    	        break;
                       	 }
                     }
            		}
                }
            } else
            {
                foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
                    $terms = get_the_terms( $values['product_id'], 'product_cat' );    
            		if ($terms){
                   	 foreach ( $terms as $term ) {        
                   	     if ( in_array( $term->term_id, $category_ids ) ) {
                   	         $unset = true;
                    	        break;
                       	 }
                     }
            		}
                }
            }
            if ( $unset == true ) unset( $available_gateways['sumup'] );
            return $available_gateways;
        }
        

        Hope it helps someone

  22. You rock! Thousands of points to your karma 🙂 Thank you!

    1. Yes!

  23. How can we disable the payment method for products with specific meta value?

    Let’s say we want to disable cash on delivery for pre-ordered products.

    1. Hey Jawad, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  24. This is nicely done, and easy to work with.

    I have a question though… if you had methods payment methods… say… A, B and C…

    If products of a specified category are in the cart, then payment methods A and B are unset and only method C would be available. Easy… got that bit and it works great.

    Now, what if the specified category *isn’t* in the cart, and you want only payment methods A and B t display, and unset payment method C.

    Can’t quite seem to get that tho.

    Any help would be greatly appreciated. 🙂

    1. Hi Del, thanks so much for your comment! Yes, this is definitely possible (you would need to use the “else” statement), but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  25. Ciao Rodolfo,
    I have successfully used your code, eliminating Paypal from 2 categories.
    But I can’t get rid of the fast paypal ceckout even if I enter [‘ppec_paypal’] instead of [‘paypal’].
    It would be great to be able to eliminate the fast ceckout as a payment system and even greater to eliminate both always for 2 categories.
    Wordpress version 5.2.4
    Version Woocommerce 3.7.1

    1. Renato, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  26. This is great – you are amazing – thank you! Had a problem with paypal subscriptions for our subscription product but still wanted paypal for our regular store items.

    So I used this to hide the paypal method.

    I just changed this one line of code.

        
    if ( $unset == true ) unset( $available_gateways['paypal'] );
        return $available_gateways;
    

    You rock!

    1. Nice!

  27. Works great! Thanks!

    1. Welcome 🙂

      1. OK. I got the basic premise working fine….if product category 64 is in the cart, disable square_credit_card.
        But now I am trying to make it more dependent on another category. There are two….64 and 154. What I am trying to do is set it so that depending on the combination of categories in the cart, different payment methods will appear….and I have two payments…COD and credit card.
        Opt 1: if NO Cat 64 items are in the cart, ONLY credit card is permitted and thus COD is unset.
        Opt 2: if ONLY Cat 64 items are in the cart, ONLY COD is permitted and credit card is unset.
        Opt 3: if Cat 64 AND cat 154 are in the cart then BOTH cod and credit card are unset. (I don’t want orders to be placed mixing product types. I can deal with this in an error message or instruction.)

        BUT….I can’t get it to work. I think I’m missing something stupid!

        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;
            if ( ! is_checkout() ) return $available_gateways;
            $unset == false;
            $category_ids = array( 64, 154 );
            foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
                $terms = get_the_terms(  $values['product_id'], 'product_cat' );    
                
            $in_cart_64 = false;
                $in_cart_154 = false;
                
            
         
        foreach ($terms as $term) {        
          
         if($term->term_id == 64){
          $in_cart_64 = true;
         } if($term->term_id == 154){
          $in_cart_154 = true;
         
         }
         
        }
         
        if ( $in_cart_64 = false ) {
         unset( $available_gateways['cod'] );
        }
        elseif ( $in_cart_64 = true && $in_cart_154 = false ) {
         unset( $available_gateways['square_credit_card'] );
        }
         else  {
         unset( $available_gateways['square_credit_card'] );
         unset( $available_gateways['cod'] );
        
        }
        }
            return $available_gateways;
        
        }
        
        1. Yes, mistakes are here e.g.:

          if ( $in_cart_64 = false ) {
          

          Instead it should be:

          if ( $in_cart_64 == false ) {
          
        2. Hi there,
          I tried to adapt this code to another (but similar) situation.
          I have variations of the same product (Books) and I have a special category for EBooks (282) and a category Books (185) created to let the code know that it is a printed book. Most of books are in both categories, and people select one variation or another (Ebook, Hardcover, Softcover).
          Toward Kim T code, I need that:
          If the product is on 282 AND 182 OR just in 282, then cod is disabled (payment-gateway is enabled).
          If the product IS just in 182, then both are enabled.
          But if product is in 282 AND the variation is Hardcover or Softcover, I don’t know to activate the both payment methods.
          Can you help me here?

  28. Can you add more than one payment gateway to this code?

    I have tried and I cant seem to make it work – the code works perfectly for one gateway.

    1. Yes you can! Feel free to share your attempt here and I’ll take a quick look

      1. Hi I tried your code and it’s not doing anything. I’m not getting errors either this is what I have (I want it to only show the echeck option but it’s still showing both):

        /**
         * @snippet       Disable Payment Method for Specific Category
         * @how-to        Get CustomizeWoo.com FREE
         * @author        Rodolfo Melogli
         * @compatible    WC 3.6.5
         * @community     https://businessbloomer.com/club/
         */
          
        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;
            if ( ! is_checkout() ) return $available_gateways;
            $unset = false;
            $category_ids = array( 519, 520, 521 );
            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['payment_method_authorize_net_cim_credit_card'] );
            return $available_gateways;
        }
        
        1. Hey Jason, the only thing that looks weird to me is this “payment_method_authorize_net_cim_credit_card”. Sure that’s correct?

  29. Hey Rodolfo,

    some time ago I used your snippet and it worked just great.
    Today I noticed some errors in the log and followed them back. One of them was concerning the snippet and then I noticed that you made update. So I updated the snippet. But it throws an error and “breaks” the page. This is the error message:

    PHP Warning: Invalid argument supplied for foreach() in /…/wp-content/themes/clevercourse/functions.php on line 267
    PHP Fatal error: Uncaught Error: Call to a member function get_cart_contents() on null in /…/wp-content/themes/clevercourse/functions.php:265
    Stack trace:
    #0 /…/wp-includes/class-wp-hook.php(286): bbloomer_unset_gateway_by_category(Array)
    #1 /…/wp-includes/plugin.php(208): WP_Hook->apply_filters(Array, Array)
    #2 /…/wp-content/plugins/woocommerce/includes/class-wc-payment-gateways.php(160): apply_filters(‘woocommerce_ava…’, Array)
    #3 /…/wp-content/plugins/woocommerce-multilingual/classes/multi-currency/payment-gateways/class-wcml-currencies-payment-gateways.php(182): WC_Payment_Gateways->get_available_payment_gateways()
    #4 /…/wp-content/plugins/woocommerce-multilingual/classes/multi-currency/payment-gateways/class-wcml-curre in /…/wp-content/themes/clevercourse/functions.php on line 265
    PHP Warning: Invalid argument supplied for foreach() in /…/wp-content/themes/clevercourse/functions.php on line 267

    And thats the code I’m using:

    function bbloomer_unset_gateway_by_category($available_gateways) {
    		//global $woocommerce;
    		$unset = FALSE;
    		$category_ids = array(131, 132);
    		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['ppec_paypal']);
    		return $available_gateways;
    

    I’ve tried using the global, but thats no solution either. Maybe it’s because not all products have a category? How do I handle a product without?

    Thanks in advance

    1. Hey Dennis, thanks for your feedback. Are you on the latest version of WooCommerce?

      1. Hi Rodolfo,

        yea I’m using the latest WooCommerce.
        All my plugins are up to date.

        Sorry for my late answer!

        1. I’ve now added a new line (“! is_checkout()”) to the snippet, let me know if that does the trick

          1. Thank you very much, that just made the trick!! Working perfect.

  30. I use the wirecard with a payment method, and it has the option of payment of card and Billet.
    is it possible to disable only one of these functions?

      1. Hello Antony, ask WireCard please 🙂

  31. Subscribing to this so I can get updates if code is updated – Thanks

    1. Cool. I should think of a “subscribe” button sometime 🙂

  32. Hi,
    Above snippet was helpful but I need reverse checking!
    I want to have conditional shipping based on payment method have chosen.
    I don’t know how to check chosen payment method in checkout page to restrict shipping method.
    Please help me to address this problem.
    Thank you.

    1. Hi Ardeshir, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  33. 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 🙂

  34. 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 🙂

  35. 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

  36. 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

  37. 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

  38. 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

  39. 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!

  40. 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!

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

    Thanks

    1. Works perfectly on 3.5.1 🙂

  42. 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

  43. 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

  44. 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

  45. AWEESOOME!! Great! Really wonderfull…

    1. 🙂

  46. 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 ) {
      
  47. 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.

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

  49. 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

  50. I use WordPress 4.9.2 with Avada theme and your code still works 🙂 thanks

  51. 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

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

    1. Wow, thank you so much Victor!

  53. 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 🙂

  54. 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

  55. 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

  56. 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

  57. 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.

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

  59. 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        Get CustomizeWoo.com FREE
     * @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.

  60. 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.

  61. 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,

  62. 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

  63. 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;
      }
      
  64. 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'] );
      
  65. 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!

      1. 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 !

        1. No problem, thanks for the follow up 🙂

  66. 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?

  67. 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 🙂

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

        1. You’re welcome Kimberley 🙂

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

    1. Thank you so much Fecso!!! Your feedback is much appreciated 🙂

Questions? Feedback? Customization? Leave your comment now!
_____

If you are writing code, please wrap it like so: [php]code_here[/php]. Failure to complying with this, as well as going off topic or not using the English language will result in comment disapproval. You should expect a reply in about 2 weeks - this is a popular blog but I need to get paid work done first. Please consider joining the Business Bloomer Club to get quick WooCommerce support. Thank you!

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