Skip to content

Integrate with a third-party address verification API

You might want to enhance the shopper experience by streamlining the process of populating and verifying the shipping address, thereby reducing the risk of user error. You can achieve this by implementing a third-party address lookup and autocomplete APIs, such as those provided by Google Places.

This tutorial describes how to override any field in a checkout address form and extend it to integrate with this service. The implementation supports backend-configurable validation and full form submission integration.

Upon successful completion of this tutorial, a form similar to the following will be displayed:

Autocomplete shipping address

Autocomplete shipping address

Step-by-step

The following steps describe how to integrate the Google Address Validation API with the Commerce boilerplate template. You can use these steps as a reference to integrate other API providers as well.

Prerequisites

For this tutorial, you must have a valid Google API key. Use API keys describes the process to obtain and set up this key.

Identify the container and override the default field

Locate the Addresses or AddressForm container in the commerce-checkout.js block. Use the Addresses container when the customer has saved addresses. Use the AddressForm container when no addresses are saved. The same approach works for both.

To override the street field in the AddressForm container for the shipping address, pass the AddressFormInput_street slot parameter. Here’s how you remove the default field:

shippingForm = await AccountProvider.render(AddressForm, {
// Other parameters
slots: {
AddressFormInput_street: async (ctx) => {
},
},
});

Define a function to generate the custom field markup

Use a function to create and render a custom input field.

The following code generates markup for new input using the Input component from the @dropins/tools package. The callback is destructured from the context passed into the slot from the container, allowing the custom input to be fully functional and integrated with the form. As a result of these changes, the default input will be replaced by custom input that matches the general look and feel, but is not functional yet.

import { Input } from '@dropins/tools/components.js';
const generateMarkup = async (context) => {
const { inputName, handleOnChange, handleOnBlur, handleOnFocus, config } = context;
const wrapper = document.createElement('div');
const errorContainer = document.createElement('div');
errorContainer.classList.add('dropin-field__hint', 'dropin-field__hint--medium', 'dropin-field__hint--error');
errorContainer.style.display = 'none';
const inputComponent = await UI.render(Input, {
name: inputName,
onChange: handleOnChange,
onBlur: handleOnBlur,
onFocus: handleOnFocus,
floatingLabel: `${config.label} *`,
placeholder: config.label,
})(wrapper);
wrapper.appendChild(errorContainer);
ctx.appendChild(wrapper);
return { inputElement: wrapper.querySelector('input'), inputComponent, errorContainer };
};
const markupElements = await generateMarkup(ctx);

Replicate the default functionality for the custom input

Make the custom input fully functional by using the onChange callback provided by the slot context. This enables validation and integration with the form.

const handleStateChange = (next, { inputElement, inputComponent, errorContainer }) => {
const { errorMessage, errors, handleOnChange, handleOnBlur } = next;
const getNextProps = (prev, error) => ({
...prev,
error,
onChange: (e) => handleOnChange(e, errors),
onBlur: (e) => handleOnBlur(e, errors),
});
if (errorMessage) {
errorContainer.innerText = errorMessage;
errorContainer.style.display = 'block';
inputComponent.setProps((prev) => getNextProps(prev, true));
} else {
errorContainer.innerText = '';
errorContainer.style.display = 'none';
inputComponent.setProps((prev) => getNextProps(prev, false));
}
if (document.activeElement === inputElement) {
setTimeout(() => inputElement.focus(), 0);
}
};
ctx.onChange((nextState) => handleStateChange(nextState, markupElements));

Enable Google Address Lookup integration

Use the following steps to enable the integration.

  1. Add the Google Places API to the checkout page:

    const scriptUrl = 'https://maps.googleapis.com/maps/api/js?key={GOOGLE_API_KEY}&loading=async&libraries=places';
    if (!document.querySelector(`script[src="${scriptUrl}"]`)) {
    const script = document.createElement('script');
    script.src = scriptUrl;
    script.async = true;
    document.head.appendChild(script);
    }
  2. Initialize the Google Autocomplete API for the custom field:

    const initAutocomplete = (inputElement) => {
    const autocompleteEl = new google.maps.places.Autocomplete(inputElement, {
    types: ['address'],
    fields: ['address_components'],
    });
    autocompleteEl.addListener('place_changed', onPlaceChanged);
    };
    const onPlaceChanged = () => {
    const place = autocompleteEl.getPlace();
    const addressComponents = place.address_components;
    let street = '', city = '', countryCode = '';
    addressComponents.forEach((component) => {
    if (component.types.includes('route')) street = component.long_name;
    if (component.types.includes('locality') || component.types.includes('sublocality')) city = component.long_name;
    if (component.types.includes('country')) countryCode = component.short_name;
    });
    document.getElementById('country_code').value = countryCode;
    document.getElementById('street').value = street;
    document.getElementById('city').value = city;
    };
    initAutocomplete(markupElements.inputElement);

Example

See blocks/commerce-checkout-address-lookup in the demos branch of the boilerplate repository for complete JS and CSS code for the address verification flow.