Addresses

Addresses are now a native part of Craft! We recommend reviewing the main documentation on addresses before digging in on Commerce-specifics.

Commerce manages shipping and billing information using Craft’s craft\elements\Address (opens new window) element type.

In the control panel, you’ll encounter addresses within the context of orders and users. A Store Location address may also be entered at CommerceStore SettingsStoreStore Location.

Customer’s addresses are managed from their user account, if you’ve added the native Addresses field to Users’ field layout. Commerce also inserts a Commerce Settings field into the address field layout) with primary shipping and billing controls.

# How Addresses are Used

Your customers will work with addresses directly, or via the cart.

Your primary source for information about working with Addresses is the main Craft documentation!

Every order may have a shipping and billing address, and customers with accounts can save and re-use addresses when placing new orders. How you collect and validate addresses on the front end is up to you—but Craft and Commerce provide tools that help streamline address management:

# Store Address

The store address (set via CommerceStore SettingsStore) is available via the Store service (opens new window):

{% set storeAddress = craft.commerce
  .getStore()
  .getStore()
  .getLocationAddress() %}

That getStore().getStore() is not a typo! We’re getting the craft\commerce\services\Store (opens new window) service with the first method and getting the craft\commerce\models\Store (opens new window) model with the second.

# Cart Addresses

# Fetching Cart Addresses

Once you have a cart object, you can access the attached addresses via cart.shippingAddress and cart.billingAddress:

{% if cart.shippingAddress %}
  {{ cart.shippingAddress.firstName }}
  {# ... #}
{% endif %}

{% if cart.billingAddress %}
  {{ cart.billingAddress.firstName }}
  {# ... #}
{% endif %}

It’s important to code defensively, here! If the customer hasn’t set an address yet, you’ll get back null—otherwise, it’ll be an Address (opens new window) object.

You don’t need to add your own logic to handle shippingAddressSameAsBilling or billingAddressSameAsShipping; Commerce will return the correct address taking those options into account.

# Updating Cart Addresses

Any post to the commerce/cart/update-cart action can include parameters for modifying shipping and billing address information in two formats:

  1. A shippingAddress and/or billingAddress array with details to be added (guests and logged-in users)
  2. A shippingAddressId and/or billingAddressId parameter for choosing an existing address by ID (logged-in users only)

# Synchronizing Shipping and Billing Addresses

With either approach, you can leverage the shippingAddressSameAsBilling or billingAddressSameAsShipping parameters to synchronize addresses and avoid having to send the same information twice.

If you provide a shippingAddress or shippingAddressId, for example, and the order’s billing address should be identical, you can simply pass a non-empty value under billingAddressSameAsShipping rather than supplying the same billingAddress or billingAddressId.

If you provide shippingAddress fields and shippingAddressId, the latter takes precedence.

Commerce doesn’t require an order to have a shipping address, but providing one is important for the tax and shipping engine to calculate more accurate options and costs.

# Address Fields

The specific properties and fields supported when updating an order’s shippingAddress or billingAddress in this way are determined by the Address (opens new window) element, regional differences based on the provided countryCode, and any custom fields assigned to it.

<form method="post">
  {# ... #}

  {{ input('text', 'shippingAddress[fullName]', cart.shippingAddress.fullName) }}
</form>

# Address Ownership

Logged-in users can directly manage their addresses via the front-end, and pick from them during checkout. However, addresses are only ever “owned” by one element—let’s look at some examples of how Commerce handles this:

  • When an address is selected by updating a cart with a shippingAddressId or billingAddressId, the order keeps track of where the address came from via sourceShippingAddressId and sourceBillingAddressId properties, but clones the actual address element. This means that shippingAddressId and sourceShippingAddressId will never be the same!
  • Addresses provided by sending individual fields under the shippingAddress[...] and billingAddress[...] keys are created and owned by the order.
  • Similarly, sending individual field values for an order’s shipping or billing address (regardless of how it was originally populated) will only update the order address, and breaks any association to the user’s address book via sourceShippingAddressId or sourceBillingAddressId.

If you want to make it clear that your customer has selected a preexisting address, compare order.sourceShippingAddressId or order.sourceBillingAddressId with the IDs of the addresses in their address book. We have an example of this, below.

# Cart Address Examples

Let’s look at some approaches to updating order addresses during checkout.

With either of these strategies, you can apply changes to shipping and billing addresses, simultaneously. Read more about synchronizing addresses.

# Submit New Addresses

Customers—especially guests—will probably need to enter an address at checkout. To set address information directly on the order, pass individual properties under a shippingAddress or billingAddress key, depending on what you want to update:

<form method="post">
  {{ csrfInput() }}
  {{ actionInput('commerce/cart/update-cart') }}

  {{ input('text', 'shippingAddress[fullName]', shippingAddress.fullName) }}

  <select name="shippingAddress[countryCode]">
    {% for code, name in craft.commerce.getStore().getStore().getCountriesList() %}
      {{ tag('option', {
        value: code,
        text: name,
        selected: code == shippingAddress.countryCode,
      }) }}
    {% endfor %}
  </select>

  {# ... #}

  <button>Save Shipping Address</button>
</form>

If your request also includes a non-empty shippingAddressId or `billingAddressId param, the corresponding individual address fields are ignored and Commerce attempts to fill from an existing address.

# Auto-fill from Address Book

You can allow your customers to populate order addresses with a previously-saved one by sending a shippingAddressId and/or billingAddressId param when updating the cart.

{% set cart = craft.commerce.carts.cart %}
{% set customerAddresses = currentUser ? currentUser.addresses : [] %}

<form method="post">
  {{ csrfInput() }}
  {{ actionInput('commerce/cart/update-cart') }}

  {# Display saved addresses as options if we have them #}
  {% if customerAddresses | length %}
    <div class="shipping-address">
      {% for address in customerAddresses %}
        <label>
          {{ input('radio', 'shippingAddressId', address.id, {
            checked: cart.sourceShippingAddressId == address.id,
          }) }}
          {{ address|address }}
        </label>
      {% endfor %}
    </div>

    {# ... same process for `billingAddressId` ... #}
  {% else %}
    {# No existing addresses! See examples above to learn about sending a new address. #}
  {% endif %}

  <button>Save Addresses</button>
</form>

You may need to create custom routes to allow customers to manage these addresses, or introduce logic in the browser to hide and show new address forms based on the type(s) of addresses you need.

# Update an Existing Address

An address on the cart may be updated in-place by passing individual address properties.

{% set cart = craft.commerce.carts.getCart() %}
{% set address = cart.shippingAddress %}

<form method="post">
  {{ csrfInput() }}
  {{ actionInput('commerce/cart/update-cart') }}

  {{ input('text', 'shippingAddress[fullName]', address.fullName) }}
  {{ input('text', 'shippingAddress[addressLine1]', address.addressLine1) }}

  {# ... #}

  <button>Save Shipping Info</button>
</form>

Any field(s) updated on an order address filled from the customer’s address book will not propagate back to the source, and will break the association to it. Sending shippingAddressId and billingAddressId are only intended to populate an order address with existing information—not keep them synchronized.

# Estimate Cart Addresses

It’s common to provide a shipping or tax cost estimate before a customer has entered full address details. To help with this, the cart can use “estimated” shipping and billing addresses for calculations, before complete addresses are available.

Estimated addresses are craft\elements\Address (opens new window) elements, just like shipping and billing addresses.

# Adding a Shipping Estimate Address to the Cart

You can add or update an estimated addresses on the order with the same commerce/cart/update-cart form action.

In this example we’ll first check for existing estimate addresses with the estimatedShippingAddressId and estimatedBillingAddressId attributes on the cart (opens new window) object, displaying a form to collect only the shipping country, state, and postal code if we don’t already have them:

{% set cart = craft.commerce.carts.cart %}
{% set store = craft.commerce.getStore().getStore() %}

<form method="post">
  {{ csrfInput() }}
  {{ actionInput('commerce/cart/update-cart') }}
  {{ hiddenInput('estimatedBillingAddressSameAsShipping', '1') }}

  {% if not cart.estimatedShippingAddressId %}
    {# Display country selection dropdown #}
    <select name="estimatedShippingAddress[countryCode]">
      {% for code, option in store.getCountriesList() %}
        <option value="{{ code }}">{{ option }}</option>
      {% endfor %}
    </select>

    {# Display state selection dropdown #}
    <select name="estimatedShippingAddress[administrativeArea]">
      {% for states in store.getAdministrativeAreasListByCountryCode() %}
        {% for key, option in states %}
          <option value="{{ key }}">{{ option }}</option>
        {% endfor %}
      {% endfor %}
    </select>

    {# Display a postal code input #}
    <input type="text" name="estimatedShippingAddress[postalCode]" value="">
  {% endif %}

  {% if cart.availableShippingMethodOptions|length and cart.estimatedShippingAddressId %}
    {# Display name+price selection for each available shipping method #}
    {% for handle, method in cart.availableShippingMethodOptions %}
      {% set price = method.priceForOrder(cart)|commerceCurrency(cart.currency) %}
      <label>
        <input type="radio"
          name="shippingMethodHandle"
          value="{{ handle }}"
          {% if handle == cart.shippingMethodHandle %}checked{% endif %}
        />
        {{ method.name }} - {{ price }}
      </label>
    {% endfor %}
  {% endif %}

  <button>Submit</button>
</form>

Tax adjusters (opens new window) and shipping adjusters (opens new window) will include an isEstimated attribute when their calculations were based on estimated address data.

A full example of this can be seen in the example templates on the cart page.

# Address Book

When logged in, your customers can manage their addresses independently of the cart. Refer to the main addresses documentation for more information and examples.

In addition to the natively supported params, Commerce will look for isPrimaryShipping and isPrimaryBilling. These values determine which addresses get attached to a fresh cart when the autoSetNewCartAddresses option is enabled.