To get the coordinates of the customer's shipping address, we will be using the Google Maps API. They require an account with billing account connected for access there though, so you'll need to sign up and create an account if you haven't already. They provide a $200 free allowance per month, which should allow for 40,000 requests per month before any billing would kick in.
Geocoding API
.In order to dictate what the supported address zone is, we need to generate a polygon, where each point of the polygon is a set of coordinates. We can use Google's My Maps functionality to build out that polygon visually, and export it to get the coordinates.
coordinates
section of the XML, it'll look like this:<Polygon> <outerBoundaryIs> <LinearRing> <tessellate>1</tessellate> <coordinates> -73.9546793,40.8478817,0 -74.0205973,40.741837,0 -74.0549296,40.6304119,0 -74.0247172,40.5647194,0 -73.9230936,40.5699355,0 -73.8297098,40.6481274,0 -73.8599223,40.799039,0 -73.9546793,40.8478817,0 </coordinates> </LinearRing> </outerBoundaryIs> </Polygon>
coordinates
node (of your file, not the example code above), and paste it into a new file.[-73.9546793,40.8478817,0], [-74.0205973,40.741837,0], [-74.0549296,40.6304119,0], [-74.0247172,40.5647194,0], [-73.9230936,40.5699355,0], [-73.8297098,40.6481274,0], [-73.8599223,40.799039,0], [-73.9546793,40.8478817,0]
Now for the fun part - adding the custom shipping code to your store!
// Google Maps API key const google_maps_api_key = ''; // Shipping area polygon const shipping_polygon = [ ]; // Get address details const country = cart['_embedded']['fx:shipment']['country']; const region = cart['_embedded']['fx:shipment']['region']; const city = cart['_embedded']['fx:shipment']['city']; const postal_code = cart['_embedded']['fx:shipment']['postal_code']; let address1 = ""; for (let c in cart['_embedded']['fx:custom_fields']) { if (cart['_embedded']['fx:custom_fields'][c]['name'] == "shipping_address1") { address1 = cart['_embedded']['fx:custom_fields'][c]['value']; break; } } // Get address coordinates from Google Maps const maps_url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' + address1 + ', ' + city + ', ' + region + ', ' + country + ', ' + postal_code + '&key=' + google_maps_api_key; let coordinates = []; await request(maps_url, function (error, response, body) { let payload = JSON.parse(body); if (response && response.statusCode == 200 && payload.status == "OK") { let location = payload.results[0].geometry.location; coordinates = [location.lng, location.lat]; } }); if (coordinates.length == 2 && checkGeoFence(coordinates, shipping_polygon)) { // Shipping is allowed! rates.add(10000, 15, '', 'Standard Delivery'); } else { // Shipping is not allowed rates.error("Sorry, we're unable to deliver to your address"); } function checkGeoFence(point, polygon) { if(polygon[0] != polygon[polygon.length-1]) polygon[polygon.length] = polygon[0]; let j = 0; let oddNodes = false; let x = point[1]; let y = point[0]; let n = polygon.length; for (let i = 0; i < n; i++) { j++; if (j == n) { j = 0; } if (((polygon[i][0] < y) && (polygon[j][0] >= y)) || ((polygon[j][0] < y) && (polygon[i][0] >= y))) { if (polygon[i][1] + (y - polygon[i][0]) / (polygon[j][0] - polygon[i][0]) * (polygon[j][1] - polygon[i][1]) < x) { oddNodes = !oddNodes; } } } return oddNodes; }
const google_maps_api_key = '';
to use your Google Maps API key you generated above, pasting it within the quotations.shipping_polygon
array. On the blank line in this code, paste in the coordinates array you generated in the last section:const shipping_polygon = [ ];
// Google Maps API key const google_maps_api_key = 'abCd12eFGh__zyXw34vUT'; // Shipping area polygon const shipping_polygon = [ [-73.9546793,40.8478817,0], [-74.0205973,40.741837,0], [-74.0549296,40.6304119,0], [-74.0247172,40.5647194,0], [-73.9230936,40.5699355,0], [-73.8297098,40.6481274,0], [-73.8599223,40.799039,0], [-73.9546793,40.8478817,0], ];
if (coordinates.length == 2 && checkGeoFence(coordinates, shipping_polygon)) { // Shipping is allowed! rates.add(10000, 15, '', 'Standard Delivery'); } else { // Shipping is not allowed rates.error("Sorry, we're unable to deliver to your address"); }
As a last step, you'll just need to add in some custom code to your store. The lookup relies on the customers address line 1, which isn't currently sent to the shipping rate endpoint natively. We'll add some custom code to make it pass over, and also hide the shipping estimate on the cart, so it's only calculated after the customer has entered their full address.
<style type="text/css"> #fc .fc-sidebar--cart .fc-address-entry { display: none; } </style>
{% if context == "checkout" %} <script> FC.client.on("render.done", function() { $("[name^='shipping_address1']").attr("data-fc-shipping-custom-field", true); }); FC.client.on("checkout-shipping-options-update", function() { return $('[data-fc-shipping-custom-field]:visible').filter(function() { return this.value == ""; }).length == 0; }); </script> {% endif %}
At this stage - you've completed all the configuration! You should now be able to add a product to your cart, proceed to the checkout, and enter shipping addresses that are inside and outside of your shippable zone to test it out.