0

I am making a woocommerce website for my supermarket which delivers only to one city(Ras Tanura). Since it is a supermarket store, I replaced Shipping with Delivery. I'm trying to restrict checking out to allow only customers how live in Ras Tanura to choose a payment method. I don't want people to pay for something that can't be delivered to them. Here is what I tried.

    add_filter( 'default_checkout_billing_country', 'change_default_checkout_country' );
add_filter( 'default_checkout_shipping_country', 'change_default_checkout_country' );
function change_default_checkout_country() {
    return 'SA'; // country code
}

// default checkout state
add_filter( 'default_checkout_billing_state', 'change_default_checkout_state' );
add_filter( 'default_checkout_shipping_state', 'change_default_checkout_state' );
function change_default_checkout_state() {
    return 'RT'; // state code
}

// Setting one state only
add_filter( 'woocommerce_states', 'custom_woocommerce_state', 10, 1 );
function custom_woocommerce_state( $states ) {
    // Returning a unique state
    return array('SA' => array('RT' => 'Ras Tanura'));
}

// Only one country at checkout
add_filter( 'woocommerce_checkout_fields', 'custom_checkout_fields', 10, 1 );
function custom_checkout_fields( $fields ) {

    $fields['billing']['billing_city']['type'] = 'select';
    $fields['billing']['billing_city']['options'] = array('Ras Tanura' => 'Ras Tanura');
    $fields['shipping']['shipping_city']['type'] = 'select';
    $fields['shipping']['shipping_city']['options'] = array('Ras Tanura' => 'Ras Tanura');

    return $fields;
}

I have added this code to my functions.php under oceanWP theme. This code does half of the job. It sure sets the default city to Ras Tanura and it can't be changed by customers, but I tried to order from another city and it accepted my order the only thing that this code has done is writing that the order is coming from Ras Tanura even though it wasn't.

how can I make my website knows the location of the user and prevent or accept checkout based on that? (accepts if the person is in Ras Tanura and prevents if he lives elsewhere)

Note that I have already set the selling location under woocommerce> setting> general to sell to specific countries and I have chosen this country to SA.

www
  • 21
  • 8
  • 1
    Kindly check that **selling location(s)** is set to SA in WooCommerce > Settings > General as shown in reference link(https://stackoverflow.com/questions/45444618/woocommerce-pre-select-and-restrict-to-one-state-and-city-on-checkout.). – anujpatel Dec 09 '19 at 12:46
  • I have the selling location set to 'sell to specific countries' and I set it to SA only. but it didn't work. The point here is not just the country but I want to allow checking out to ONLY one city in SA which is Ras Tanura. I couldn't achieve that. – www Dec 09 '19 at 16:44

1 Answers1

0

Note: this method make use of third party api "https://ip-api.com/" to request customer data based on ip address, which allow limited request (as per website documentation it allow 45 HTTP requests per minute from an IP address.)

Woocommerce provide geolocate customer setting by default,I have make use of some part of Woocommerce Geolocate code to fix issue.

What below code do : It will locate customer based on ip address and if customer is not belongs to specific location(country and city), it will not allow the customer to go to checkout page, redirect customer to cart error page.

Kindly note that in general setting of woocommerce "Default customer location" is set to Geolocate.

Keep your code as it is , and additionally put below code in your functions.php file,

/*
 * This function is used to retrieve location data based on ip address,
 * note that each service/api has different response format
*/
function geolocate_customer() {
    $ip_address = WC_Geolocation::get_external_ip_address();
    $geoipdata = get_transient('geoip_' . $ip_address);
    if (false === $geoipdata) {
        // you can add more service key : service name, value : service-endpoint 
        $geoip_services = array(
            'ip-api.com' => 'http://ip-api.com/json/%s',
        );
        $geoip_services_keys = array_keys($geoip_services);
        shuffle($geoip_services_keys);
        foreach ($geoip_services_keys as $service_name) {
            $service_endpoint = $geoip_services[$service_name];
            $response = wp_safe_remote_get(sprintf($service_endpoint, $ip_address), array('timeout' => 2));
            if (!is_wp_error($response) && $response['body']) {
                switch ($service_name) {
                    case 'ipinfo.io':
                        $geoipdata = json_decode($response['body']);
                        //this flag is used for error
                        $flag = ($geoipdata->error) ? true : false;
                        break;
                    case 'ip-api.com':
                        $geoipdata = json_decode($response['body']);
                        //this flag is used for error, each api may have different response format
                        $flag = ($geoipdata->status == 'success') ? false : true;
                        break;
                    default:
                        $geoipdata = '';
                        break;
                }

                if ($geoipdata && !$flag) {
                    break;
                }
            }
        }
        // This will store geolocation data so that frequent call to api can be reduced.
        set_transient('geoip_' . $ip_address, $geoipdata, WEEK_IN_SECONDS); 
    }
   return $geoipdata;
}

Note : replace 'PUT CITY NAME HERE' with city name you got in $geoipdata variable in below function.

/*
* This function is attached to 'woocommerce_before_checkout_form_cart_notices' hook,
* It will check country,city criteria and if criteria is not matched it will add error
* notice so checkout page is loaded with error message(load cart-errors.php template)
* 
*/
function add_cart_notice() {
    $geoipdata = geolocate_customer();
    if (!empty($geoipdata)) {
        if ($geoipdata->city != 'PUT CITY NAME HERE' && $geoipdata->countryCode != 'SA') {
            wc_add_notice(__('Add your message here', 'woocommerce'), 'error');
            add_action('woocommerce_cart_has_errors', 'print_notices');
        }
    }
}

add_action('woocommerce_before_checkout_form_cart_notices','add_cart_notice');
/*
* This function is used to show error message on cart page.
*/
function print_notices(){
    wc_print_notice('Add your message here', 'error');
}

For testing purpose, you can put below code in functions.php file to get city and other details based on ip address.City name can be accessed using $geoipdata->city. This will output data on website.

$geoipdata = geolocate_customer();
var_dump($geoipdata);
anujpatel
  • 141
  • 1
  • 6
  • Thanks for the reply. before trying this code I just wanted to ask about the $geoipdata, how could I get the city name from the variable? How to achieve that ? – www Dec 11 '19 at 15:39
  • I have updated answer.Kindly check, did you got your answer ? – anujpatel Dec 12 '19 at 06:31
  • I have added the two lines of code you gave me for testing. The weird thing is that it says that the country is United Stated!! and the City is Salt Lake city! I'm in Saudi Arabia and particularly in Ras Tanura City. – www Dec 12 '19 at 08:13
  • In the first function, can you replace second line($ip_address = WC_Geolocation::get_external_ip_address()) with $ip_address = WC_Geolocation::get_ip_address()) ? and check if that works for you. – anujpatel Dec 12 '19 at 11:35
  • Now it is partially fixed. It shows the country correctly but the city is wrong. I don't Know why it says that I'm in Riyadh not in Ras Tanura. what could be the reason – www Dec 12 '19 at 13:06
  • Kindly check following links for city you are looking for (Ras Tanura), https://ip-api.com/, https://ipinfo.io/, https://ipstack.com/, https://ipapi.co/. These are websites that give location information based on ip address. Let me know on which website you are getting correct city. – anujpatel Dec 13 '19 at 11:32
  • I tried all of them, they all give me the same result. None of them shows the correct city. It seems that the ip address location is not accurate – www Dec 14 '19 at 15:33