> ## Documentation Index
> Fetch the complete documentation index at: https://docs.loqate.com/llms.txt
> Use this file to discover all available pages before exploring further.

# commercetools Integration

> Reference implementation for integrating Address Capture, Address Verify, Email and Phone Validation with commercetools Frontend checkout, My Account and registration.

export const IntegrationHero = ({worksWith, services = [], images = [], highlights = [], imageLayout = "landscape", ctaLabel = "Integrate in 3 steps", ctaHref = "#integrate-in-3-steps"}) => {
  const CARD_WIDTH = 520;
  const CARD_GAP = 32;
  const [index, setIndex] = useState(0);
  const [step, setStep] = useState(CARD_WIDTH + CARD_GAP);
  const trackRef = useRef(null);
  useEffect(() => {
    const track = trackRef.current;
    if (!track) return undefined;
    const measure = () => {
      const card = track.firstElementChild;
      if (!card) return;
      const gap = parseFloat(window.getComputedStyle(track).columnGap) || 0;
      setStep(card.getBoundingClientRect().width + gap);
    };
    measure();
    const observer = new ResizeObserver(measure);
    observer.observe(track);
    return () => observer.disconnect();
  }, [images.length]);
  const lastIndex = Math.max(images.length - 1, 0);
  const styles = `
    .integration-hero {
      display: grid;
      grid-template-columns: 320px minmax(0, 1fr);
      gap: 3rem;
      margin: 2rem 0 4rem 0;
      align-items: start;
    }
    .integration-hero h3 {
      font-size: 1rem;
      font-weight: 700;
      margin: 0 0 0.5rem 0;
    }
    .integration-hero-services {
      list-style-type: disc;
      list-style-position: inside;
      padding-left: 0;
      margin: 0 0 4rem 0;
    }
    .integration-hero-services li {
      margin-bottom: 0.25rem;
      font-size: 0.95rem;
    }
    .integration-hero-services li::before {
      display: none !important;
      content: none !important;
    }
    .integration-hero-cta {
      display: inline-block;
      padding: 0.75rem 1.5rem;
      background-color: #000;
      color: #fff !important;
      text-decoration: none !important;
      border: none !important;
      border-radius: 0.5rem;
      font-weight: 600;
      font-size: 0.95rem;
      text-align: center;
    }
    .integration-hero-cta:hover {
      background-color: #333;
    }
    html.dark .integration-hero-cta {
      background-color: #fff;
      color: #000 !important;
    }
    html.dark .integration-hero-cta:hover {
      background-color: #e0e0e0;
    }
    .integration-hero-media {
      position: relative;
      height: 400px;
    }
    .integration-hero-viewport {
      overflow: hidden;
      height: 100%;
      padding: 10px 15px;
      -webkit-mask-image: linear-gradient(to right, #000 85%, transparent 100%);
      mask-image: linear-gradient(to right, #000 85%, transparent 100%);
    }
    .integration-hero-track {
      display: flex;
      gap: ${CARD_GAP}px;
      height: 100%;
      transition: transform 0.5s ease;
    }
    .integration-hero-card {
      flex: 0 0 ${CARD_WIDTH}px;
      height: 100%;
      border-radius: 20px;
      overflow: hidden;
      background: #fff;
      border: 1px solid rgba(0, 0, 0, 0.08);
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .integration-hero-card img {
      width: 100%;
      height: 100%;
      object-fit: contain;
      margin: 0 !important;
      border-radius: 0 !important;
    }
    .integration-hero-media.portrait {
      height: 500px;
    }
    .integration-hero-media.portrait .integration-hero-card {
      flex-basis: 380px;
    }
    .integration-hero-media.portrait .integration-hero-card img {
      object-fit: cover;
    }
    .integration-hero-nav {
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
      width: 48px;
      height: 48px;
      border-radius: 50%;
      border: 1px solid rgba(0, 0, 0, 0.1);
      background: rgba(255, 255, 255, 0.95);
      color: #333;
      font-size: 24px;
      line-height: 1;
      cursor: pointer;
      z-index: 10;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
      display: flex;
      align-items: center;
      justify-content: center;
      transition: opacity 0.3s ease, box-shadow 0.2s ease;
    }
    .integration-hero-nav:hover {
      background: #fff;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
    }
    .integration-hero-nav:disabled {
      opacity: 0.3;
      cursor: default;
    }
    .integration-hero-nav.prev {
      left: calc(1rem + 15px);
    }
    .integration-hero-nav.next {
      right: calc(1rem + 15px);
    }
    .integration-hero-highlights {
      display: grid;
      gap: 1rem;
    }
    .integration-hero-highlight {
      padding: 1.25rem 1.5rem;
      border-radius: 16px;
      border: 1px solid rgba(0, 0, 0, 0.1);
    }
    html.dark .integration-hero-highlight {
      border-color: rgba(255, 255, 255, 0.12);
    }
    .integration-hero-highlight p {
      margin: 0;
      font-size: 0.95rem;
      opacity: 0.85;
    }
    @media (max-width: 1024px) {
      .integration-hero {
        grid-template-columns: minmax(0, 1fr);
        gap: 2rem;
      }
      .integration-hero-services {
        margin-bottom: 2rem;
      }
      .integration-hero-media {
        height: 280px;
      }
      .integration-hero-card {
        flex-basis: min(${CARD_WIDTH}px, 85%);
      }
      .integration-hero-media.portrait {
        height: 440px;
      }
      .integration-hero-media.portrait .integration-hero-card {
        flex-basis: min(380px, 85%);
      }
    }
  `;
  const go = next => setIndex(Math.min(Math.max(next, 0), lastIndex));
  return <>
      <style>{styles}</style>
      <div className="integration-overview-page"></div>
      <div className="integration-hero">
        <div>
          <h3>Works with:</h3>
          <p style={{
    margin: "0 0 2rem 0",
    fontSize: "0.95rem"
  }}>{worksWith}</p>

          <h3>Supported Loqate Services:</h3>
          <ul className="integration-hero-services">
            {services.map(service => <li key={service}>{service}</li>)}
          </ul>

          <a href={ctaHref} className="integration-hero-cta">
            {ctaLabel}
          </a>
        </div>

        {images.length > 0 ? <div className={`integration-hero-media ${imageLayout === "portrait" ? "portrait" : ""}`}>
            <div className="integration-hero-viewport">
              <div className="integration-hero-track" ref={trackRef} style={{
    transform: `translateX(-${index * step}px)`
  }}>
                {images.map(image => <div className="integration-hero-card" key={image.src}>
                    <img src={image.src} alt={image.alt} noZoom />
                  </div>)}
              </div>
            </div>
            {images.length > 1 && <>
                <button type="button" aria-label="Previous screenshot" className="integration-hero-nav prev" disabled={index === 0} onClick={() => go(index - 1)}>
                  ‹
                </button>
                <button type="button" aria-label="Next screenshot" className="integration-hero-nav next" disabled={index === lastIndex} onClick={() => go(index + 1)}>
                  ›
                </button>
              </>}
          </div> : <div className="integration-hero-highlights">
            {highlights.map(highlight => <div className="integration-hero-highlight" key={highlight.title}>
                <h3>{highlight.title}</h3>
                <p>{highlight.description}</p>
              </div>)}
          </div>}
      </div>
    </>;
};

<IntegrationHero
  worksWith="commercetools Frontend (B2C template)"
  services={["Address Capture", "Address Verify", "Email Validation", "Phone Validation"]}
  images={[
{ src: "/images/commercetools-studio-settings.png", alt: "Loqate settings in commercetools Frontend Studio" },
]}
/>

<div className="section-divider" />

## About this integration

The Loqate commercetools plugin ([`loqate-commercetools`](https://github.com/loqate/loqate-commercetools)) integrates [the commercetools platform](https://commercetools.com/) with the Loqate API to provide Address Capture, Address Verify, Email Validation and Phone Validation in the **Checkout**, **My Account** and **Registration** flows.

The modifications are applied in two areas:

* **Backend-for-frontend (BFF):** commercetools extensions that make the Loqate API calls and handle the responses
* **Frontend:** React components that display the Loqate features to your customers

<Warning>
  The plugin is a **reference implementation** built on the [commercetools Frontend B2C template](https://docs.commercetools.com/frontend-development/b2c-store-launchpad-overview) (latest as of 3 June 2024). It is not intended to be cloned and used as-is in production. Copy the relevant parts into your own implementation instead. See [Disclaimers](#disclaimers) for details.
</Warning>

## Supported Loqate Services

<div style={{marginBottom: '-1rem'}} />

Each service can be toggled on or off through project settings. The configuration options below are set in [Studio settings](/integrations/commercetools-studio-settings).

<Tabs>
  <Tab title="Address Capture">
    ### Type-Ahead Address Lookup

    Address Capture suggests possible addresses based on user input.

    **How it works:**

    * Calls to the [Find](/api-reference/address-capture/find) endpoint are made continuously until an address of type `Address` is found
    * If the type is `Container`, the system continues making calls to the Find endpoint
    * Once an address of type `Address` is found, the [Retrieve](/api-reference/address-capture/retrieve) endpoint is called to capture the full address details

    **IP to Country:** determines the user's country based on their IP address, and auto-populates the country field with the result.

    **Configuration options:**

    * `LOQATE_IP_TO_COUNTRY_ENABLED` - toggle for IP to Country functionality
    * `LOQATE_ADDRESS_LOOKUP_CHECKOUT` - toggle for address lookup during checkout
    * `LOQATE_ADDRESS_LOOKUP_MY_ACCOUNT` - toggle for address lookup on the My Account page
    * `CHECKOUT_ADDRESS_REQUEST_LIMIT` - debounce time for address capture requests
    * `RESTRICTED_COUNTRIES` - list of restricted country ISO2 codes to exclude from address capture suggestions

    **Integration areas:** Checkout, My Account

    See the [Address Capture API documentation](/api-reference/address-capture/apis) or [learn more about Address Capture](/our-services/address-capture/overview).
  </Tab>

  <Tab title="Address Verify">
    ### Address Verification

    Address Verify evaluates the validity of the address on form submission, using fields like country, city, postal code and street address. Based on the response, the address is scored as `Good`, `Questionable` or `Invalid` using the [Address Verification Code](/report-codes/address-verification-code/):

    * **Good:** no issues detected
    * **Questionable:** the user is prompted to accept a corrected address suggested by Address Verify. The user can either edit the address manually or proceed with the initial input
    * **Invalid:** a suggestion is provided to update the address. The user can either update it or proceed with the original input

    **Configuration options:**

    * `LOQATE_ADDRESS_VERIFICATION_CHECKOUT` - toggle for address verification during checkout
    * `LOQATE_ADDRESS_VERIFICATION_MY_ACCOUNT` - toggle for address verification on the My Account page
    * `LOQATE_AVC` - minimum matchscore threshold for an address to be considered `Good`

    **Integration areas:** Checkout, My Account

    See the [Address Verify API documentation](/api-reference/address-verify/international-batch-cleanse) or [learn more about Address Verify](/our-services/address-verify/overview).
  </Tab>

  <Tab title="Email Validation">
    ### Email Address Validation

    Email Validation verifies the user's email address during input, ensuring the format is correct and that the address is valid.

    **Configuration options:**

    * `LOQATE_EMAIL_VALIDATION_CHECKOUT` - toggle for email validation during checkout
    * `LOQATE_EMAIL_VALIDATION_MY_ACCOUNT` - toggle for email validation on the My Account page
    * `LOQATE_EMAIL_VALIDATION_REGISTRATION` - toggle for email validation during registration
    * `LOQATE_EMAIL_VALIDATION_TIMEOUT_MILLISECONDS` - timeout duration for email validation requests
    * `LOQATE_INCLUDE_VALID_CATCHALL_EMAILS` - determines whether `Valid_CatchAll` responses are considered valid (see the `ResponseCode` response field in the API documentation)

    **Integration areas:** Checkout, My Account, Registration

    See the [Email Validation API documentation](/api-reference/email-validation/apis) to learn more about the service.
  </Tab>

  <Tab title="Phone Validation">
    ### Phone Number Validation

    Phone Validation checks the correctness of the phone number the user enters, ensuring it is properly formatted and valid.

    **Configuration options:**

    * `LOQATE_PHONE_VALIDATION_CHECKOUT` - toggle for phone validation during checkout
    * `LOQATE_PHONE_VALIDATION_MY_ACCOUNT` - toggle for phone validation on the My Account page
    * `LOQATE_PHONE_VALIDATION_REGISTRATION` - toggle for phone validation during registration
    * `LOQATE_INCLUDE_MAYBE_PHONE_NUMBERS` - determines whether the `Maybe` validation state is treated as valid (see the `IsValid` response field in the API documentation)

    **Integration areas:** Checkout, My Account, Registration

    See the [Phone Validation API documentation](/api-reference/phone-validation/individual-validate) to learn more about the service.
  </Tab>
</Tabs>

<br />

<CardGroup cols={2}>
  <Card title="Setup guide" href="/integrations/commercetools-setup-guide">
    Install the plugin and apply the backend-for-frontend and frontend changes
  </Card>

  <Card title="Studio settings" href="/integrations/commercetools-studio-settings">
    Configure the plugin in commercetools Frontend Studio
  </Card>
</CardGroup>

<div className="section-divider" />

## Integrate in 3 steps

<p>1) Get your API key 2) Clone the repository 3) Configure and integrate</p>

<Card>
  <div style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '3rem', flexWrap: 'wrap', padding: '1rem 0.5rem'}}>
    <div style={{flex: '1', minWidth: '280px'}}>
      <div style={{marginTop: 0, marginBottom: '0.75rem', fontSize: '1.875rem', lineHeight: '1.2', fontWeight: '700'}}>
        Get your API key, then start from the reference implementation
      </div>

      <p style={{marginBottom: 0, fontSize: '1rem', lineHeight: '1.6', opacity: '0.85'}}>
        Clone the repository, configure the project in Studio, and carry the changes over to your own project.
      </p>
    </div>

    <div style={{display: 'flex', flexDirection: 'column', gap: '1rem', minWidth: '220px', alignItems: 'stretch'}}>
      <a href="https://account.loqate.com/" class="btn-primary" style={{textAlign: 'center', whiteSpace: 'nowrap'}}>
        1. Get Your API Key
      </a>

      <a href="https://github.com/loqate/loqate-commercetools" class="btn-secondary" style={{textAlign: 'center', whiteSpace: 'nowrap'}}>
        2. Clone the Repository
      </a>

      <a href="/integrations/commercetools-setup-guide" class="btn-secondary" style={{textAlign: 'center', whiteSpace: 'nowrap'}}>
        3. Configure and Integrate
      </a>
    </div>
  </div>
</Card>

<div className="section-divider" />

## Additional Resources

<div style={{display: 'flex', flexDirection: 'column', gap: '1rem'}}>
  <div style={{display: 'flex', alignItems: 'center', gap: '0.75rem'}}>
    [loqate-commercetools on GitHub](https://github.com/loqate/loqate-commercetools)
  </div>

  <div style={{display: 'flex', alignItems: 'center', gap: '0.75rem'}}>
    [commercetools Frontend development docs](https://docs.commercetools.com/frontend-development/cli)
  </div>
</div>

<div className="section-divider" />

## Disclaimers

The loqate-commercetools repository demonstrates the integration of Loqate Address Capture, Address Verify, Email Validation and Phone Validation features with commercetools. It is to be used as a reference implementation for your own integration - it is not intended to be used as is in a production environment. Please refer to the [Setup Guide](/integrations/commercetools-setup-guide) for instructions on the changes required to carry over the Loqate commercetools integration into your own project.

It uses the [commercetools Frontend B2C template](https://docs.commercetools.com/frontend-development/b2c-store-launchpad-overview) as a base, specifically the latest as of 3 June 2024.

This plugin:

* Does not aim to keep up with the latest changes in the commercetools frontend template, but rather to provide a reference implementation
* Should not be cloned and used as is in a production environment. It is recommended to use it as a reference and adapt it to your own needs by copying the relevant parts of the code and applying them to the merchant’s implementation
* Does not aim to fix any bugs or issues in the commercetools frontend template. If you encounter any issues related with the B2C Frontend Template, please refer to the commercetools documentation
* Is not responsible for any bugs or issues coming from the commercetools B2C Frontend Template, nor any customizations made to it and does not aim to fix them. You should refer to the commercetools documentation for support for any issues related to the specific version of the commercetools B2C Frontend Template that you are using
* Does not take an opinion of the flow you should support in your store - it applies changes to the out of the box checkout, my account and registration flows

<div className="section-divider" />

## Pricing

<Card title="View Pricing Plans" icon="credit-card" href="https://www.loqate.com/pricing">
  Flexible plans starting with a 45-day free trial. Choose the plan that fits your business needs.
</Card>

<div className="section-divider" />

## Frequently Asked Questions

<br />

<div className="faq-small-text">
  <AccordionGroup>
    <Accordion title="Can I use the plugin as-is in production?">
      <span style={{fontSize: '0.875rem', display: 'block'}}>
        No. The plugin is a reference implementation. Get the latest version of the commercetools B2C Frontend template and apply the backend-for-frontend and frontend changes described in the [setup guide](/integrations/commercetools-setup-guide) on top of your existing implementation.
      </span>
    </Accordion>

    <Accordion title="What do I need before I start?">
      <span style={{fontSize: '0.875rem', display: 'block'}}>
        Node.js 18.16.0 or higher, Yarn 1.22.19 or higher, a commercetools project, commercetools Frontend Studio, and a [Loqate API key](/loqate-basics/create-an-api-key).
      </span>
    </Accordion>

    <Accordion title="Which parts of my store does it change?">
      <span style={{fontSize: '0.875rem', display: 'block'}}>
        The plugin modifies the out-of-the-box Checkout, My Account and Registration flows. It doesn't take an opinion on which flows you should support in your store.
      </span>
    </Accordion>

    <Accordion title="Can I turn individual features on and off?">
      <span style={{fontSize: '0.875rem', display: 'block'}}>
        Yes. Each feature can be toggled per area through the project settings. See [Studio settings](/integrations/commercetools-studio-settings) for the full list.
      </span>
    </Accordion>
  </AccordionGroup>
</div>

<p>
  Still have questions or need support? <a href="mailto:LoqateSupport@gbgplc.com">Send us an email</a>
</p>

<div className="section-divider" />

## Integration details

<div style={{marginTop: '2rem'}}>
  <div style={{display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '2rem'}}>
    <div>
      <h3 style={{fontSize: '0.875rem', fontWeight: '700', marginBottom: '0.5rem', opacity: '0.7'}}>Resources</h3>
      <a href="https://www.loqate.com/en-us/legal/privacy-policy/" style={{textDecoration: 'none'}}>Privacy policy</a>
    </div>

    <div>
      <h3 style={{fontSize: '0.875rem', fontWeight: '700', marginBottom: '0.5rem', opacity: '0.7'}}>Developer</h3>
      <p style={{marginBottom: '0.25rem', fontWeight: '600'}}>GB GROUP PLC</p>
      <a href="https://www.loqate.com/" style={{textDecoration: 'none', display: 'block', marginBottom: '0.25rem'}}>Website</a>
      <p style={{fontSize: '0.875rem', opacity: '0.8', margin: 0}}>128 Queen Victoria Street, London, ENG, EC4V 4BJ, GB</p>
    </div>

    <div>
      <h3 style={{fontSize: '0.875rem', fontWeight: '700', marginBottom: '0.5rem', opacity: '0.7'}}>Platform</h3>
      <p style={{margin: 0}}>commercetools Composable Commerce</p>
      <a href="https://github.com/loqate/loqate-commercetools" style={{fontSize: '0.875rem', textDecoration: 'none'}}>View on GitHub</a>
    </div>
  </div>
</div>
