missing attributes data in orders

Topic summary

A Shopify store owner is experiencing inconsistent cart attribute data (specifically specialist_ref and share_cart) not transferring from cart.json to completed orders. The attributes are captured from URL parameters or sessionStorage with a default value of “0000” for specialist_ref, but some orders arrive without these critical business values despite appearing correctly in cart.json during testing.

Key Technical Details:

  • Script runs in theme.liquid using DOMContentLoaded
  • Updates cart attributes via /cart/update.js endpoint
  • Values verified in console but don’t consistently reach checkout

Proposed Solutions:

  1. Initial suggestion involved checkout customization (requires Shopify Plus)
  2. Revised approach for standard Shopify plans:
    • Store values in both sessionStorage and localStorage for redundancy
    • Add values to cart notes as backup (notes transfer more reliably than attributes)
    • Implement cart form submission listener to ensure data persists

Current Status:

  • User adapted the solution to include cart notes alongside attributes
  • Added localStorage cleanup to prevent value persistence across different customer sessions
  • Testing in progress with mixed results: 2 orders successful, 1 order missing both notes and attributes (possibly from pre-update cart)
  • Awaiting feedback on whether notes provide higher success rate than attributes alone
Summarized with AI on October 30. AI used: claude-sonnet-4-5-20250929.

I have a weird issue with the attribute data. I have read similar cases, but I haven’t found the correct answer. I have this code that adds a value fetched from the URL params and stores it in sessionStorage (just in case) and in cart.json, so I can have this value in my orders and generate reports based on this data. Recently, I added a “default” value to one of these fields so that if it is not loaded in the params or session, I get a “default value.” This way, if for any reason I need to modify this value in the orders, I have this “key=value” available and don’t have to depend on a third party to allow me to add it. Does that make sense?

After adding this “default value,” we noticed that several orders are arriving without this value, which concerns us because our business model depends on having reliable information in all cases. Am I executing the code incorrectly? I’ve tested multiple cases, and in all of them, the value from cart.json is coming through correctly (verified through console.log), but it is not being sent with the order when it passes through the checkout.

Here is my script, and I have it in the theme.liquid.

<script>
document.addEventListener("DOMContentLoaded", async function () {
    console.log("? Script cargado en:", window.location.href);

    // Obtener 'sref' y 'ml-shared-cart-id' de la URL
    const urlParams = new URLSearchParams(window.location.search);
    let specialistRef = urlParams.get('sref') || sessionStorage.getItem('sref') || "0000";
    let shareCart = urlParams.get('ml-shared-cart-id') || sessionStorage.getItem('shareCart') || "";

    console.log("? 'sref' en URL o SessionStorage:", specialistRef);
    console.log("? 'ml-shared-cart-id' en URL o SessionStorage:", shareCart);

    // Guardar valores en sessionStorage si vienen en la URL
    if (urlParams.get('sref')) {
        sessionStorage.setItem('sref', specialistRef);
    }
    if (urlParams.get('ml-shared-cart-id')) {
        sessionStorage.setItem('shareCart', shareCart);
    }

    // Obtener datos actuales del carrito
    const cartResponse = await fetch('/cart.js');
    const cartData = await cartResponse.json();
    let currentSpecialistRef = cartData.attributes?.specialist_ref || "";

    console.log("? 'specialist_ref' actual en el carrito:", currentSpecialistRef);

    // Lógica de actualización:
    // 1. Si el carrito tiene un 'specialist_ref' diferente de 0000 y viene otro diferente de 0000 → ACTUALIZAR
    // 2. Si el carrito tiene '0000' o está vacío → ACTUALIZAR con el mejor valor disponible
    // 3. Si el carrito ya tiene un valor válido y el nuevo es '0000' → NO MODIFICAR
    if (
        (currentSpecialistRef !== "0000" && specialistRef !== "0000" && currentSpecialistRef !== specialistRef) ||
        (currentSpecialistRef === "" || currentSpecialistRef === "0000")
    ) {
        fetch('/cart/update.js', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                attributes: {
                    specialist_ref: specialistRef,
                    share_cart: shareCart || ""
                }
            }),
        })
        .then(response => response.json())
        .then(data => {
            console.log("✅ Notas del carrito actualizadas:", data);
        })
        .catch(error => console.error("⚠️ Error actualizando notas del carrito:", error));
    } else {
        console.log("✅ 'specialist_ref' ya es válido y no se modifica.");
    }
});

</script>
  
    

I added two data attributes where in one, the specialist_ref is present, and in the other, it is not (both orders were placed on the same day, one following the other)

thanks for any help!

Hey @vitahuber ,

The core issue seems to be that while your cart attributes (specialist_ref and share_cart) are being properly set in the cart.json, they’re not consistently passing through to your orders. This is a common challenge in Shopify when working with cart attributes.

Here’s what’s likely happening:

  1. Your script correctly updates the cart.json with the attributes
  2. However, during checkout, these attributes might not be automatically transferred to the order
  3. The inconsistency (some orders having the value, others not) suggests a race condition or timing issue

Here’s a solution approach:

  1. Ensure attributes persist through checkout by making a modification to your checkout process:
// Add this code to your theme.liquid file, after your existing script
{% if first_time_accessed %}
  <script>
    if (Shopify && Shopify.Checkout && Shopify.Checkout.step === "contact_information") {
      // Get values from sessionStorage
      const specialistRef = sessionStorage.getItem('sref') || "0000";
      const shareCart = sessionStorage.getItem('shareCart') || "";
      
      // Add hidden fields to the checkout form to persist cart attributes
      document.addEventListener('DOMContentLoaded', function() {
        const form = document.querySelector('form');
        if (form) {
          // Create and append hidden input for specialist_ref
          const specialistRefInput = document.createElement('input');
          specialistRefInput.type = 'hidden';
          specialistRefInput.name = 'checkout[attributes][specialist_ref]';
          specialistRefInput.value = specialistRef;
          form.appendChild(specialistRefInput);
          
          // Create and append hidden input for share_cart
          const shareCartInput = document.createElement('input');
          shareCartInput.type = 'hidden';
          shareCartInput.name = 'checkout[attributes][share_cart]';
          shareCartInput.value = shareCart;
          form.appendChild(shareCartInput);
          
          console.log("Added hidden fields to checkout form:", specialistRef, shareCart);
        }
      });
    }
  </script>
{% endif %}
  1. Modify your current script to ensure it always executes before checkout:
// Change the timing of your existing script
document.addEventListener("DOMContentLoaded", async function () {
    // Your existing code...
    
    // Add a more aggressive update approach
    try {
        const updateResponse = await fetch('/cart/update.js', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                attributes: {
                    specialist_ref: specialistRef,
                    share_cart: shareCart || ""
                }
            }),
        });
        const updateData = await updateResponse.json();
        console.log("✅ Cart attributes updated:", updateData.attributes);
        
        // Store values in localStorage (more persistent than sessionStorage)
        localStorage.setItem('sref', specialistRef);
        localStorage.setItem('shareCart', shareCart || "");
    } catch (error) {
        console.error("⚠️ Error updating cart attributes:", error);
    }
});
  1. Add a fallback mechanism using Shopify’s order note feature:
// Add this to your theme.liquid after your main script
{% if template contains 'cart' %}
<script>
  // Capture values on the cart page to ensure they're available at checkout
  document.querySelector('form[action="/cart"]').addEventListener('submit', function() {
    // Get current values
    const specialistRef = sessionStorage.getItem('sref') || localStorage.getItem('sref') || "0000";
    const shareCart = sessionStorage.getItem('shareCart') || localStorage.getItem('shareCart') || "";
    
    // Add to note field as fallback
    const noteField = document.querySelector('[name="note"]');
    if (noteField) {
      const noteText = noteField.value || "";
      noteField.value = noteText + "\nspecialist_ref: " + specialistRef + "\nshare_cart: " + shareCart;
    }
  });
</script>
{% endif %}

This three-pronged approach should ensure your attributes consistently pass through to orders:

  1. Your original script updates cart.json
  2. The checkout form receives hidden fields with the same attributes
  3. As a last resort, the values are added to the order notes

Based on your description, the issue is likely in the handoff between cart and checkout. These changes should resolve the inconsistency you’re seeing.

Cheers!
Shubham | Untechnickle

Can this solution work if I don’t have Shopify Plus in my store? I understand that on basic plans we don’t have access to edit the checkout. Is that correct?

You’re absolutely right about Shopify Plus - without it, you can’t customize the checkout directly. Let me adjust my solution for a standard Shopify plan.

First, let’s strengthen your existing script to make it more reliable:

document.addEventListener(“DOMContentLoaded”, async function () {
// Get values from multiple sources for redundancy
const urlParams = new URLSearchParams(window.location.search);
let specialistRef = urlParams.get(‘sref’) || sessionStorage.getItem(‘sref’) || localStorage.getItem(‘sref’) || “0000”;
let shareCart = urlParams.get(‘ml-shared-cart-id’) || sessionStorage.getItem(‘shareCart’) || localStorage.getItem(‘shareCart’) || “”;

// Store in both sessionStorage and localStorage (belt and suspenders approach)
sessionStorage.setItem(‘sref’, specialistRef);
localStorage.setItem(‘sref’, specialistRef);
sessionStorage.setItem(‘shareCart’, shareCart);
localStorage.setItem(‘shareCart’, shareCart);

console.log("

Values being applied - sref:", specialistRef, “share_cart:”, shareCart);

// Always update cart attributes on every page to ensure consistency
try {
const updateResponse = await fetch(‘/cart/update.js’, {
method: ‘POST’,
headers: { ‘Content-Type’: ‘application/json’ },
body: JSON.stringify({
attributes: {
specialist_ref: specialistRef,
share_cart: shareCart
}
}),
});
const updateData = await updateResponse.json();
*console.log(“*Cart attributes updated:", updateData.attributes);
} catch (error) {
*console.error(”*Error updating cart attributes:", error);
}
});

Then, add this reliable fallback to your cart page:

{% if template contains ‘cart’ %}

{% endif %}

This approach has two safety nets:

  1. Your cart attributes should pass through to orders most of the time
  2. When they don’t, the values will be in your order notes as a backup

The inconsistency you’re seeing is a known limitation with standard Shopify plans. This solution won’t be as clean as what you could do with Shopify Plus, but it should ensure you never lose that critical data.

Let me know if you need any clarification or have other questions!

Cheers!
Shubham | Untechnickle

1 Like

Hi, I tried adapting this solution (we don’t have notes on the cart page, so I pushed them like the attributes. I also added the deletion of data from localStorage so that the next purchase made by the same customer, or at least the same browser, doesn’t get assigned the same values, as that’s not what we want). I am testing to see if notes have a higher success rate than attributes. So far, I have two orders with both values loaded, and one where neither notes nor attributes appear (I can assume this cart “started” before I uploaded the change and didn’t have the updated script, so I have some doubt about that, but feel free to comment if there are any changes). My script ended up like this; I think it’s fine, but any advice is more than welcome. (In the console, when I fetch the cart.json, both notes and attributes appear).