Update Cart’s address ID from plugin I want to try and stop Commerce 3 creating a new customer address if it’s a duplicate of an existing one.

As an example, a store selling digital products has a checkout form that simply asks for the customer’s country, no other address info is required. All we need is one select field for the country (pre-checked if we already have that info). We could add radio buttons to select any previously saved addresses as well as a field to select a (new) country. However, even if we do that, there’s still a chance the user opts to enter a new address and actually submits a duplicate.

I’ve created a plugin that catches a duplicate address and then tries to set the cart’s billingAddressId so a new address isn’t created. What I have so far though doesn’t work. It doesn’t successfully update the billingAddressId. If I use Craft::$app-&gt;getElements()-&gt;saveElement($cart) it results in a loop in this case.

How can I complete this to update the cart, or perhaps the post data in this case, with the matched billingAddressId?

Event::on(Address::class, Address::EVENT_BEFORE_VALIDATE, function (Event $e) {

    $sentCountryId = $e-&gt;sender-&gt;countryId;
    $currentUser = Craft::$app-&gt;user;
    $cart = craft\commerce\Plugin::getInstance()-&gt;getCarts()-&gt;getCart();
    $addresses = $cart-&gt;customer-&gt;addresses;
    $match = 0;

    // Only check against saved addresses if currentUser has any
    if ($currentUser and $addresses) {

        // If true, loop saved address to check for a country match
        foreach($addresses as $address) {

            // Loop until a match is found
            if ($match == 0) {

                $savedCountryId = $address-&gt;countryId;

                if ($sentCountryId == $savedCountryId) {
                    $match = $address-&gt;id;
                }

            }

        };

        // If there’s a match set the cart’s billingAddressId
        // to stop a new address being created:
        if ($match) {

            $cart-&gt;billingAddressId = $match; // This doesn’t work on it’s own
            // Craft::$app-&gt;getElements()-&gt;saveElement($cart); // Results in a loop
            $e-&gt;handled = true;

        }

    }

});
If I am reading you right you are wanting to update the shipping address on the order rather than create a new one. 

You should be able to do that right now:

{% set cart = craft.commerce.carts.cart %}

{% set flashNotice = craft.app.session.getFlash('notice') %}

{% if flashNotice %}
    &lt;div class="bg-blue-500 text-white"&gt;
        &lt;div class="flash container mx-auto px-6 py-4"&gt;
            {{ flashNotice }}
        &lt;/div&gt;
    &lt;/div&gt;
{% endif %}

{% set flashError = craft.app.session.getFlash('error') %}

{% if flashError %}
&lt;div class="bg-red-500 text-white"&gt;
  &lt;div class="flash container mx-auto px-6 py-4"&gt;
    {{ flashError }}
  &lt;/div&gt;
&lt;/div&gt;
{% endif %}


//meat and potatoes:


{{ cart.number }}&lt;br&gt;

Shipping Address ID: {{ cart.shippingAddressId }}

&lt;form method="post"&gt;
  &lt;input type="hidden" name="action" value="commerce/cart/update-cart"/&gt;
  &lt;input type="hidden" name="cartUpdatedNotice" value="The address was updated."&gt;
  {{ redirectInput('shop/address') }}
  {{ csrfInput() }}

  &lt;!-- THIS IS WHAT WILL UPDATE THE ADDRESS VS ADDING A NEW ONE --&gt;
  {% if cart.shippingAddress.id %}
  &lt;input type="hidden" name="shippingAddress[id]" value="{{cart.shippingAddress.id}}"/&gt;
  {% endif %}

  First Name:
  &lt;input name="shippingAddress[firstName]" value="{{cart.shippingAddress.firstName ?? ''}}"/&gt;

  &lt;button type="submit"&gt;Update&lt;/button&gt;
&lt;/form&gt;


As demonstrated above, using an input with name="shippingAddress[id]" with the address ID set as the value will update the address with that ID on the commerce/cart/update-cart controller action just fine.

You would only use name="shippingAddressId" if you wanted to let the user select an existing one.