How to Hide Payment Methods based on Postal Code

In this tutorial I will show you how you can disable or enable payment gateways in WooCommerce depending on customer shipping or billing postcode with the help of woocommerce_available_payment_gateways filter hook.

If you need custom coding help with WooCommerce payment gateways – contact me.

Here is our goal:

Hide or show WooCommerce payment methods based on postal code
Let’s say that we have the payment methods on our website – Credit Card, PayPal and Cash on Delivery and we want Cash of Delivery to be available for certain zip codes, and PayPal to be available for any other zip codes.
add_filter( 'woocommerce_available_payment_gateways', 'rudr_gateway_by_postcode' );

function rudr_gateway_by_postcode( $gateways ) {
	
	if( is_admin() ) {
		return $gateways;
	}
	
	$zip_codes = array( // array of postal codes to check
		'X0A',
		'X0B',
		'X0C',
	);
	
	if( is_wc_endpoint_url( 'order-pay' ) ) { // Pay for order page

		$order = wc_get_order( wc_get_order_id_by_order_key( $_GET[ 'key' ] ) );
		$zip_code = $order->get_shipping_postcode();

	} else { // Cart/Checkout page

		$zip_code = WC()->customer->get_shipping_postcode();

	}
	
	if ( in_array( strtoupper( $zip_code ), $zip_codes ) ) {
		// if customer shipping postcode is in array, deactivate PayPal payment method
		if ( isset( $gateways[ 'paypal' ] ) ) {
			unset( $gateways[ 'paypal' ] );
		}
	} else {
		// for everyone else deactivate Cash on Delivery
		if ( isset( $gateways[ 'cod' ] ) ) {
			unset( $gateways[ 'cod' ] );
		}
	}

	return $gateways;
}

Trigger Checkout Update on Postal Code Change

One more thing.

By default WooCommerce doesn’t update checkout when we type in another postal code. Like it does for the state/country fields for example. If you google it, you can find a recommendation to add update_totals_on_change CSS-class, but it doesn’t work for text fields.

So let’s add a small piece of jQuery code as well.

jQuery( function( $ ) {
	$( '#billing_postcode, #shipping_postcode' ).blur( function() {
		$( 'body' ).trigger( 'update_checkout' );
	} );
} );

It is very basic, if you wish, you can use keyup event and setTimeout().

// Delay function
// Change 500ms to any other delay value, e.g. 1000 (1s)
function rudr_delay( callback ) {
	let timer = 0
	return function( ...args ) {
		clearTimeout( timer )
		timer = setTimeout( callback.bind(this, ...args ), 500 || 0)
	}
}

jQuery( function( $ ) {
	$( '#billing_postcode, #shipping_postcode' ).keyup( rudr_delay( function (e) {
	  $( 'body' ).trigger( 'update_checkout' );
	} ) );
} );

JavaScript code can be inserted into a separate .js file or to the functions.php as well inside wp_head or wp_footer action hook.

Misha Rudrastyh

Misha Rudrastyh

I develop websites since 2008, so it is total of 13 years of experience, oh my gosh. Most of all I love love love to create websites with WordPress and Gutenberg, some ideas and thoughts I share throughout my blog.

Need some developer help? Contact me

Follow me on Twitter