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:
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.
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);}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.