There are different reasons why someone wants to specifically enable or disable specific payment gateways for some shipping methods. In this post I will show you how you can enable or disable payment gateways based on the shipping method with a code snippet.

Disable payment gateways for shipping methods

For this example I will disable the ‘BACS’ payment gateway for the ‘local delivery’ shipping method. To get started you’d need to get the ID or both the payment gateway and the shipping method. To get these IDs you can go to the settings section of the given payment gateway, the URL should end with something like &section=wc_gateway_paypal. The ID of the payment gateway here is paypal (‘&section’ value, with ‘wc_gateway_’ omitted). For the shipping rate this is very similar, the URL ends with &section=wc_shipping_local_delivery where ‘local_delivery’ is the shipping method ID.

Next we would need to add the following code snippet to remove the given payment gateway when the given shipping rate has been selected.

In the code snippet you will see on line 10 that we are checking if the local_delivery shipping method has been selected, and if that is the case then on line 13 the bacs payment gateway will be disabled. You can change these values to the IDs that you’ve retrieved before.

If you want to disable multiple payment gateways for a specific shipping method, you can copy line 13 and add those extra payment gateways.

Enable payment gateways for shipping methods

The opposite, enabling payment gateways only for specific shipping methods is very similar. We still need to get the IDs of the payment gateway/shipping method. In this example I will only enable the ‘cash on delivery’ payment gateway when the ‘local delivery’ shipping method has been selected. I know that COD also has a option available for this, but for some shipping plugins, like WooCommerce Advanced Shipping, which add multiple shipping rates under one shipping method the shipping rates cannot be selected from the drop down list in the Cash On Delivery gateway. Via this way specific shipping rates (which most likely do not have the same shipping ID as mentioned above) can also be used to enable the Cash On Delivery gateway for.

(for WooCommerce Advanced Shipping users that are looking for the shipping ID, when looking in the shipping rate in detail, you can find the numeric ID in the URL ?post=33696)

In the code snippet above you notice that there is very little difference then the ‘disable payment gateway’ code. The difference here is a exclamation mark on line 10 that makes sure that when the Cash On Delivery is always removed when ‘Local Delivery’ is not the chosen shipping method.

Closing words

These code snippets hopefully get you started on modifying your payment gateway availability. If you have any questions, don’t hesitate to ask!

I would love to hear from you if this was useful in any way, have feedback, missing something that you’d love to see or just a Thank you 🙂

  • Jeroen Sormani

    I'm a professional WordPress plugin developer on a mission to create the best plugins for my clients. I'm specialised in developing general WordPress, WooCommerce and Easy Digital Downloads plugins.

    Interested in talking about a WordPress project? Get in touch!

    Follow me on Twitter
  • 39 thoughts on “Enable/Disable payment gateways for specific shipping methods

    Lee December 8, 2015 at 3:03 am

    Hi Jeroen,
    Thanks for the helpful tip. I have an issue similar to this which need your advice.

    I had setup 2 payment method which are paypal and International Order(renamed from COD).

    I wanted the local country to be able to pay ONLY via Paypal while for other country customer, they will select international order to place an order, I will then calculate and add shipment charge backend and send them a customer invoice to pay.

    I use this function snippet:

    function payment_gateway_disable_country( $available_gateways ) {
    global $woocommerce;
    if ( isset( $available_gateways[‘paypal’] ) && $woocommerce->customer->get_country() ‘SG’ ) {
    unset( $available_gateways[‘paypal’] );
    } else if ( isset( $available_gateways[‘cod’] ) && $woocommerce->customer->get_country() == ‘SG’ ) {
    unset( $available_gateways[‘cod’] );
    }
    return $available_gateways;
    }
    add_filter( ‘woocommerce_available_payment_gateways’, ‘payment_gateway_disable_country’ );

    The above solved the problem of hiding and setting the right payment method for local SG customers and International customers when they place an order.

    However, I noticed the customer invoice only has the International Order (i.e. COD) payment method as the Paypal was unset in the above PHP code. May I know is there a way to tweak the function to enable Paypal Only on customer invoice by checking that the customer order is pending etc? I need this solution to apply to all users including guest.

    Thanks!!

    jeroen December 9, 2015 at 5:27 pm

    Hi Lee,

    I’m not sure what you’re trying to do with the PP gateway, but from what I do understand you’d probably have to dive in a bit deeper to accomplish that. You can probably do a extra check, but I haven’t done such thing before..

    Cheers,
    Jeroen

    harry January 11, 2016 at 10:50 am

    thanks bro

    Wolfgang Vogel March 12, 2016 at 9:17 pm

    Dear Jeroen,

    THANK YOU SO MUCH – you saved my day.
    Since 9 hours I was searching a solution to disable PayPal for local pickup!
    With your snippet it finally works!

    THANKS

    jeroen March 12, 2016 at 9:22 pm

    Glad to be of help 🙂

    Apostolos March 18, 2016 at 7:15 pm

    Dear Jeroen

    Could you please help me on which file/place should I add this snippet?

    Thank you in advance

    jeroen March 18, 2016 at 9:42 pm

    Hi,

    Please refer to this post I wrote to learn about code snippets 😉
    https://shopplugins.com/how-to-add-a-code-snippet-to-your-site/

    Jeroen

    Anthony Attard March 25, 2016 at 11:37 am

    The code needs better error handling. I was getting a null array error showing on the checkout page. Did a simple check if array was null and then added a dummy value to avoid the error.
    /**
    * Filter payment gateways.
    */
    function my_custom_available_payment_gateways( $gateways ) {
    $chosen_shipping_rates = WC()->session->get( ‘chosen_shipping_methods’ );
    if( empty( $chosen_shipping_rates ) ) {
    $chosen_shipping_rates = array(“Placeholder to avoid null value”);
    }

    // When ‘local delivery’ has been chosen as shipping rate
    if ( in_array( ‘local_delivery’, $chosen_shipping_rates )
    ) :
    // Remove bank transfer payment gateway
    unset( $gateways[‘cod’] );
    endif;
    return $gateways;
    }
    add_filter( ‘woocommerce_available_payment_gateways’, ‘my_custom_available_payment_gateways’ );

    Alex Furer April 30, 2016 at 1:31 am

    Hi Jeroen,

    Thank you very much for this post. It actually made my day.

    Unfortunately I had to switch the table rate plugin I was using because it had some limitations I was not able to overcome. With the new table rate shipping I can’t look for the shipping rate’s ID or class anymore, I actually have to compare the label of the shipping rate since the plugin gives ID’s to the shipping rates based on how many it displays. This changes because I disable certain shipping rates under certain circumstances.

    Since I am a copy paste programmer, qualified enough to use your script and adjust it to look for the correct shipping rate ID, I am not able to extend it to find the label of the shipping rate.

    Would that be an easy change to your script and could you point me to the right place on the internet to implement that, or would that mean the script would be entirely different?

    Alex

    jeroen April 30, 2016 at 9:10 pm

    Hi Alex,

    Hmm, strange that the rate IDs keep changing..
    Have you given my Advanced Shipping plugin a try? Works like a charm with that 😉
    http://codecanyon.net/item/woocommerce-advanced-shipping/8634573

    I haven’t done it before based on shipping label, and honestly also wouldn’t recommend it, but it should be possible though. Probably with a simple `if` check based on something like $gateways['bacs']->title (haven’t checked if that is the correct property).

    Cheers,
    Jeroen

    Alex Furer May 1, 2016 at 3:27 am

    Hi Jeroen,

    Thanks for posting back. I will check your suggestion asap and I will post my findings. Maybe it will help someone ales too.

    Yeah, changing the shipping ID based on what’s displayed is odd. But I do like the plugin a lot. How they organize the shipping rates in draggable nodes is awesome. Also that all shipping rates are on one page makes editing really fast.

    I actually only found out about your table rate shipping plugin after I bought two others and when only after I was searching for the solution you provide here… Besides that at the time I already bought the second plugin, I also have to admit that one thing that doesn’t put your plugin at the top of my list is that the I see myself opening dozens of tabs to adjust all shipping options using your plugin. Ok, that’s also because my current client has a very detailed shipping program with loads of options.

    And on top of that, and this is my very personal view, I am very turned off by codecanyon lately. Their good intentions turned into pure business and while they might provide a good platform I think they rip developers off and also they bind developers in a way I don’t like at all. And that I found the other table rate plugin, which is sold directly by them, shows that google also knows about plugins not sold on codecanyon (envato).

    Since it’s your competition, I don’t dare to post the link here 😉 Unless you tell me to.

    Alex

    Jake June 23, 2016 at 2:21 pm

    Hi, does the code still work with the new version’s Shipping Zones put in place? I have used this code for quite a while but they dont seem to work anymore now. I am wondering if others are also experiencing the same. Thanks!

    Jeroen June 23, 2016 at 9:41 pm

    Hi Jake,

    I haven’t tried the code out yet with the new Zones, but I don’t think the ‘local_delivery’ shipping rate ID exists in there. Though thats just used example shipping rate ID and should be changed to your own ID anyway.

    What are you using?

    Cheers,
    Jeroen

    Jake June 27, 2016 at 11:32 pm

    Hi Jeoren, I finally got the code to work (somewhat) with the new version of Woocommerce.

    There are only three shipping method IDs now (local_pickup, flat_rate, free_shipping). The new feature now is that it supports multiple instance of the same type. So the selected shipping id will now show as shippingmethod:instance (e.g. flat_rate:2 — See https://wordpress.org/support/topic/how-to-get-instance-id-selected-shipping-method?replies=6)

    All in all the code seems to work fine.

    However, if I wanted to throw in another condition in your code, a bug arises where I think it ignores the instance ID. im in a bit of dilemma here since i’m not really a programmer, much so a woocommerce developer, but here’s a snippet of what i’ve done so far:

    function my_custom_available_payment_gateways( $gateways ) {
    $chosen_shipping_rates = WC()->session->get( ‘chosen_shipping_methods’ );
    print_r(array_values($chosen_shipping_rates));

    // ex: If in-store pickup is selected, remove paypal
    if ( in_array( ‘local_pickup:12’, $chosen_shipping_rates ) ) :
    unset( $gateways[‘paypal’] );
    endif;

    // ex: If free delivery within the city is selected, remove bacs
    if ( in_array( ‘local_pickup:14’, $chosen_shipping_rates ) ) :
    unset( $gateways[‘bacs’] );
    endif;

    return $gateways;
    }

    Do you happen to know where I’ve done wrong in the code?

    Hope you could really help! Thanks!

    Jake June 27, 2016 at 11:59 pm

    On the other hand, do scratch off my previous reply 🙂 the value returned fine all along — there was just a small problem in the conditional statement that was giving undesirable results. Thanks!

    Kristin August 9, 2016 at 6:22 pm

    Hi Jake

    I’m doing the same, would you share what was wrong with the conditional statement?

    Peter July 22, 2016 at 12:47 pm

    Works great! Thank you both!
    I’ve lost whole day trying to set woocommerce to work like this myself x_x
    This option should be available in default installation.

    Bill December 5, 2016 at 1:18 pm

    Hi there,

    This is a great snippet but…

    (I am using Woocommerce Version 2.6.8)
    I put the snippet to my child functions.php but I can't make the snippet work. Could it have to do with the Woocommerce version I am using?

    Does your themeforest Woocommerce Advanced Shipping plugin does this job?

    Jeroen December 6, 2016 at 10:26 am

    Hi Bill,

    It should still be working for WC 2.6+, the only catch is that the IDs of the shipping rates are different with the WC Zones feature.
    Finding out the shipping rate ID is a bit harder now, but my best way is through the inspect element of your browser at the cart and see in the <input> value: https://cl.ly/1G021L0U0n2m

    Hope that helps 🙂

    Cheers,
    Jeroen

    Tomáš Cirkl December 8, 2016 at 12:11 pm

    Thanks, works perfectly!

    Stephen Mackereth December 29, 2016 at 5:38 am

    Hi Jeroen,

    I am having the same issue
    im running woocommerce 2.6.11

    Im trying to get the selected shipping_method so I can either add a packaging fee or not.

    can you please expand on your previous answer.

    regards,
    Stephen

    Jeroen December 29, 2016 at 10:41 am

    Hi Stephen,

    What previous answer? This post isn’t about adding fees based on the shipping method, so I’m not sure.

    Cheers,
    Jeroen

    Stephen Mackereth December 30, 2016 at 6:18 am

    Hi Jeroen,

    I am trying to get the selected shipping method
    the line
    $chosen_shipping_rates = WC()->session->get( ‘chosen_shipping_methods’ );
    doesn’t appear to work on the later versions of woocommerce

    do you have an updated version of this line of code.

    regards,
    Stephen

    Jeroen December 30, 2016 at 10:10 am

    Hi Stephen,

    I verified it in the source of WooCommerce, it should still be the same code. Maybe something else is going on your site..?

    Cheers,
    Jeroen

    vintik January 1, 2017 at 10:19 pm

    WooCommerce 2.6.4 The code does not work ((Please, update!! ?
    I get an error 500 when I go to the /wp-admin

    Jeroen January 1, 2017 at 11:22 pm

    Hi,

    The code should be good. If you get a 500 error then probably something went wrong with the implementation of it (e.g. a PHP opening tag too much).

    Cheers,
    Jeroen

    vintik January 2, 2017 at 7:56 am

    Without the code, everything works. Maybe I need to change it?

    Jeroen January 2, 2017 at 10:43 am

    Hi,

    The 500 error indicates to something else, I can’t see any reason why the code would cause that other then a implementation mistake somewhere. If the code was not to work it simply wouldn’t filter anything, not cause such error.

    I think the best thing you can do is contact your web developer to debug this, since I can’t debug it for you through here 😉

    Cheers,
    Jeroen

    vintik January 2, 2017 at 10:49 am

    Thank you!:))

    Lena January 2, 2017 at 4:29 pm

    hi Jeroen!

    i have a question, is that possible to remove from the order total amount, the cod fee amount when someone choose to pick up the products in store?

    thnx in advance

    Lena January 3, 2017 at 11:25 am

    Solved thnx!

    Atom January 11, 2017 at 12:01 pm

    Hello
    I Write this code:

    /**
    * Filter payment gatways
    */
    function my_custom_available_payment_gateways( $gateways ) {
    $chosen_shipping_rates = WC()->session->get( ‘chosen_shipping_methods’ );
    // When ‘local delivery’ has been chosen as shipping rate
    if ( in_array( ‘flat_rate:4’, $chosen_shipping_rates ) || in_array( ‘flat_rate:6’, $chosen_shipping_rates ) ) :
    unset( $gateways[‘paypal’] );
    unset( $gateways[‘bacs’] );
    unset( $gateways[‘transferuj’] );

    endif;
    if ( in_array( ‘flat_rate:1’, $chosen_shipping_rates ) || in_array( ‘flat_rate:2’, $chosen_shipping_rates )
    || in_array( ‘flat_rate:3’, $chosen_shipping_rates ) || in_array( ‘flat_rate:5’, $chosen_shipping_rates ) ) :

    unset( $gateways[‘cod’] );

    endif;

    return $gateways;
    }
    add_filter( ‘woocommerce_available_payment_gateways’, ‘my_custom_available_payment_gateways’ );

    Can i make it little short?

    dexter March 21, 2017 at 12:07 pm

    Hi,
    thanks for this imput but i would like to go deeper on this and just cant get it right… it works with 2 options, but more… i get lost there. 🙂

    I have Shipping options: #1, #2, #3 and #4 and i have payyment gateways: a, b, c and d

    So what i wanna do is next:

    1. when #1 shipping option is selected i wanna disable all payment gateways except “a”, i wanna leave that one on
    2. when #2 shipping option is selected i wanna leave payment “b” and remove other options
    3. when #3 is selected i wanna leave option “c”
    4. when #4 is selected i wanna leave payment “b” and “d”

    This is how far i came, after that it all gets messy and stops working…

    function my_custom_available_payment_gateways( $gateways ) {
    	$chosen_shipping_rates = WC()->session->get( 'chosen_shipping_methods' );
    	// When 'local delivery' has been chosen as shipping rate
    	if ( ! in_array( 'flat_rate:1', $chosen_shipping_rates ) ) :
    		// Remove bank transfer payment gateway
    		unset( $gateways['cod'] );
    	elseif ( ! in_array( 'flat_rate:2', $chosen_shipping_rates ) ) :
    		// Remove bank transfer payment gateway
    		unset( $gateways['bacs'] );
    	endif;
    	return $gateways;
    }
    add_filter( 'woocommerce_available_payment_gateways', 'my_custom_available_payment_gateways' );

    Can you help me with this please?

    PS: im using latest version of WC and WP.

    George P. May 31, 2017 at 1:10 pm

    I’ve been trying a couple of plugins that caused more conflicts than solutions. I am very happy that I stumbled upon your simple code.

    I combined your two examples and when customer chooses a courier CoD appears and Pay Locally disappears. If customer chooses local pickup, CoD is gone and Pay locally appears.

    Simple and effective. Thank you very much..!!!

    filazza June 22, 2017 at 12:44 pm

    Hi

    Congratulation for your very helpful post.

    Do you have an idea to oblige the payment by bank transfer if the amount reaches a maximum price defined ?

    George P. June 26, 2017 at 11:41 am

    Hello and thank you for a great post,

    I’ve been using the above code for months successfully but today, after updating WC to the latest version, I noticed that the menu editor in admin panel stopped working!

    I discovered that it had to do with the above code and in my logs I get this errors:

    #0: *2967264 FastCGI sent in stderr: “PHP message: PHP Fatal error: Uncaught Error: Call to a member function get() on null in /child-theme-path/functions.php:66
    Stack trace:
    #0 /path/wp-includes/class-wp-hook.php(298): my_custom_available_payment_gateways(Array)
    #1 /path/wp-includes/plugin.php(203): WP_Hook->apply_filters(Array, Array)
    #2 /path/wp-content/plugins/woocommerce/includes/class-wc-payment-gateways.php(165): apply_filters(‘woocommerce_ava…’, Array)
    #3 /path/wp-content/plugins/woocommerce/includes/wc-account-functions.php(115): WC_Payment_Gateways->get_available_payment_gateways()
    #4 /path/wp-content/plugins/woocommerce/includes/admin/class-wc-admin-menus.php(242): wc_get_account_menu_items()
    #5 /path/wp-admin/includes/template.php(1170): WC_Admin_Menus->nav_menu_links(NULL, Array)
    #6 /var” while reading response header from upstream

    If I comment out the code erros are gone and menu editor page displays correctly. Any ideas on this?

    stephen August 18, 2017 at 12:50 pm

    Thanks for this – managed to get it working after finding the correct 2.6 taxonomies. saved me bucks on a plug in.

    Nathalie August 23, 2017 at 10:59 am

    Hi ! I’ve a problem since the last updates :
    “Warning: in_array() expects parameter 2 to be array, null given in”
    It seems the code changes ?
    Thank you for your help.

    Jeroen August 23, 2017 at 11:07 pm

    Hi Nathalie,

    This can happen if the chosen payment gateway returns a non-array value. This can easily be solved by type casting the return value on line 7 by adding a (array) after the ‘= ‘ sign.

    Cheers,
    Jeroen

    Leave a Reply