WordPress Woocommerce plugin to disable payment methods based on zip or postal codes

Hello!

Woocommerce is a great easy-to-implement and versatile e-commerce platform. With the robust development community, expanding the core functionality can be relatively straight forward with the availability of a wide assortment of 3rd party plugins for Woocommerce.

One of the things that we felt was missing, but a simple requirement, was the ability to manipulate the payment methods available based on the zip or postal code of the customer.

This means that under certain conditions, the end-user will have a catered list of payment methods available to them. The system would need to have the ability to “Remember” the user, and subsequently the available payment methods, even if they came back later to purchase with a different postal or zip code.

Why is this necessary? There could be many different justifications for this type of behavior with Woocommerce. If you are offering products and services to customers on a national or international scale, then the ability to mitigate fraud or problematic sales may be necessary. In the scenario we found ourselves in, our customer requested the ability to remove credit card as a payment option based on a list of postal codes that generated problems with respect to fraud and/or problem customers for other reasons.

As a result of this requirement, we decided to build a plugin : Shift8 Woocommerce Postal Blocker. By developing our own plugin to accomplish this, we were giving ourselves full flexibility to ensure all the needs and expectations were met without. In many scenarios this path is ideal with clients.

Build a list of postal codes for woocommerce to block

Shift8 Woocommerce Postal Code Blocker

We created this plugin with a simple user interface. The first objective of the interface is to allow the administrator to choose with (activated) payment gateway to remove when a postal code matches. The second objective is to allow the administrator to build a list of postal codes or zip codes to block. The list is a large text area where the administrator can simply input a new postal code, one per line.

Remove Woocommerce payment gateway if postal code matches

The woocommerce checkout form is a unique input form. The data entered into the form generates an ajax request that dynamically updates the form. One of the reasons for this behavior is so things like shipping costs and total cost of the order can be dynamically updated when you enter or change your shipping address or enter a coupon code. This makes the checkout process much smoother and easier.

What we want is to use this process to dynamically update the checkout form to remove a payment method if it matches anything in the list of banned postal codes. For this we want to hook into the woocommerce_available_payment_gateways filter. This filer is how Woocommerce builds the list of available payment gateways.

add_filter( 'woocommerce_available_payment_gateways', 'shift8_wooblock_payment_gateways_process' );
function shift8_wooblock_payment_gateways_process( $available_gateways ) {

    global $woocommerce;
     $encryption_key = wp_salt('auth');

    if ( is_admin() ) {
        return $available_gateways;
    }

    // Get settings prior to any processing
    if (shift8_wooblock_check_options()) {

        // Pull the settings for processing
        $gateway_remove = esc_attr( get_option('wc_settings_tab_shift8_wooblock_gateway') );
        $postal_codes = explode(" ", esc_attr( shift8_wooblock_sanitize(get_option('wc_settings_tab_shift8_wooblock_postals') )));
        $user_postal = !empty($woocommerce->customer->get_shipping_postcode()) ? shift8_wooblock_sanitize($woocommerce->customer->get_shipping_postcode()) : shift8_wooblock_sanitize($woocommerce->customer->get_billing_postcode());
        // If postal code matches
        if (in_array($user_postal, $postal_codes) && isset($available_gateways[$gateway_remove])) {
            unset( $available_gateways[$gateway_remove]);
            $cookie_data = shift8_wooblock_encrypt($encryption_key, $user_postal . '_' . $woocommerce->customer->get_email());
            setcookie('shift8_wb', $cookie_data, strtotime('+30 day'), '/');
        } else if (isset($_COOKIE['shift8_wb'])) {
            $cookie_data = explode('_', shift8_wooblock_decrypt($encryption_key, $_COOKIE['shift8_wb']));
            if (esc_attr($cookie_data[1]) != 'error') {
                unset( $available_gateways[$gateway_remove]);
            }
        }
    }
    return $available_gateways;
}

There’s a few things going on in the above function. First, if you’re logged in as an admin, then show all the payment gateways. Then what we want to do is pull the list of postal / zip codes provided in the administration area, sanitize the input, and match it against what was entered in the checkout form. This function would be called any time that postal code changes.

If a match happens, we remove the available payment gateway that was selected. Then we are setting a cookie with the data encrypted of their postal code matched. The cookie will have an expiration of 30 days in the future.

If a match doesn’t happen with the first check, but a previously set cookie is found that was a match, then the payment gateway is still removed. Why does this happen? Well the idea behind this plugin is a blacklist of sorts, so having a strict control over when a specific payment method will be available is key to ensuring that malicious or abusive end-users will not be able to easily circumvent the system.

Obviously they can clear their browser cookies and try another postal code! The point is that this mitigation is intended to address the majority of scenarios, not all.

How to encrypt or decrypt cookie data in WordPress

We have covered this topic in a few other blog posts, however it should be noted that the idea behind encrypting the data in a session cookie is mainly so that it cannot be easily manipulated. Manipulating cookie data can, in theory, be used as an attack vector.

I hope this post has been useful! Future updates to the plugin are planned to be able to dynamically set the cookie expiration time as well as a few other options.