Drawer Cart - AfterPay Site Messaging not in right spot/not functioning correctly - Dawn Theme

Topic summary

A user encountered an issue with Afterpay messaging in their Dawn 15.0.0 theme’s sliding cart drawer. The payment messaging was incorrectly appearing next to individual line items and calculating installments based on single product prices instead of the cart total.

The Problem:

  • Afterpay showed $5 installments for a $20 item instead of $25 installments for a $100 cart total
  • The message appeared in the wrong location (next to line items rather than under the subtotal)
  • Initial implementation followed Afterpay’s documentation but wasn’t designed for cart drawers

The Solution (provided by TheUntechnickle):

  • Custom implementation in cart-drawer.liquid to place messaging under cart total
  • JavaScript to calculate installments based on actual cart total with dynamic updates
  • CSS to hide incorrect individual item messages
  • Final fix required intercepting Afterpay’s widget API to prevent line item widget creation
  • Multi-layered approach: CSS hiding, DOM removal, API interception, attribute blocking, and continuous monitoring

Additional adjustments made:

  • User hosted their own Afterpay logo due to CDN issues
  • Product page integration added via theme editor customization section

Resolution: Successfully implemented after multiple iterations. Cart drawer now correctly displays Afterpay messaging with accurate installment calculations based on cart total.

Summarized with AI on October 27. AI used: claude-sonnet-4-5-20250929.

Theme: DAWN 15.0.0
Site: https://littlemushm.com/

I’m trying to get the sliding cart drawer to display the Afterpay option under the customer’s subtotal…
But when the cart drawer slides out, the Afterpay messaging shows up next to a single line item, instead of at the bottom under the total.
It then divides that single line item cost by 4, instead of the total cart amount, and completely ignores anything else in the cart :')

For example, if a customer adds 5 x $20 items, and their subtotal is $100 - the Afterpay element should then say “Pay in 4 interest-free payments of $25”.
Instead, it picks one $20 item from the cart and provides the payment plan of 4 instalments of $5, which is so odd.

Screenshot with inspected element:
https://media.discordapp.net/attachments/1217466091945857074/1377448785751310477/image.png?ex=683900a6&is=6837af26&hm=a579702a28be61a1f79379dd572b427d74a03e37bff22f47d6e73d7235b202f0&=&format=webp&quality=lossless&width=946&height=859

The yellow block is where I’d like the Afterpay information to go (and ideally have it do the correct maths haha)

All I did was follow the steps provided by AfterPay here: https://developers.afterpay.com/docs/api/platforms/shopify/afterpay-site-messaging
Copy/pasted to the very bottom of the theme.liquid and changed
// var afterpay_dynamic_cart_integration_enabled = false;
to
// var afterpay_dynamic_cart_integration_enabled = true;

I also had some success adding text below the “estimated total” using the cart-drawer.liquid to see if there was a way to get the Afterpay element in there manually, but I don’t know much about code :')
https://media.discordapp.net/attachments/1217466091945857074/1377453540628496524/image.png?ex=68390513&is=6837b393&hm=6f28ea55f814bd0a38033e8d1059ba49d13c3ed9402b89219c1c2f1b66d15d46&=&format=webp&quality=lossless

Any help with getting the drawer cart and Afterpay to be friends would be greatly appreciated!

Hi @lounatuna !

I can see exactly what’s happening with your Afterpay integration on the cart drawer. The issue is that Afterpay’s automatic script is designed primarily for product pages, so when it runs on your cart drawer, it’s getting confused and attaching to individual product price elements instead of your cart total. That’s why it’s showing $5 installments for a single $20 item instead of $25 installments for your $100 cart total.

The Problem

Afterpay’s automatic cart integration is targeting the wrong price elements in your cart drawer and calculating installments based on individual items rather than the total cart value.

The Solution

We need to disable the automatic integration for the cart drawer and create a custom implementation that:

  1. Places the Afterpay message in the correct location (under your cart total)
  2. Calculates installments based on the actual cart total
  3. Updates dynamically when customers change quantities or remove items

Here’s the complete fix with all the code you need:


Step 1: Update your cart-drawer.liquid file

Find the section with

(around line 85 in your code) and replace it with:

<div class="afterpay-message cart-afterpay">
  <div id="afterpay-cart-drawer-message" 
       data-amount="{{ cart.total_price | money_without_currency | remove: ',' }}"
       data-locale="en_AU"
       data-currency="AUD">
  </div>
</div>

Then, add this script at the very bottom of your cart-drawer.liquid file, just before the closing tag:

<script>
document.addEventListener('DOMContentLoaded', function() {
  // Function to update Afterpay messaging in cart drawer
  function updateCartDrawerAfterpay() {
    const afterpayElement = document.getElementById('afterpay-cart-drawer-message');
    const totalElement = document.querySelector('.totals__total-value');
    
    if (afterpayElement && totalElement) {
      // Get the cart total
      const totalText = totalElement.textContent || totalElement.innerText;
      const totalAmount = parseFloat(totalText.replace(/[^\d.-]/g, ''));
      
      if (totalAmount && totalAmount >= 1) {
        // Calculate installment amount
        const installmentAmount = (totalAmount / 4).toFixed(2);
        
        // Create the Afterpay message
        afterpayElement.innerHTML = `
          <div class="afterpay-text">
            <span>or 4 interest-free payments of $${installmentAmount} with</span>
            <img src="https://static.afterpay.com/integration/product-page/logo-afterpay-colour.svg" 
                 alt="Afterpay" style="height: 16px; margin-left: 4px;">
          </div>
        `;
      } else {
        afterpayElement.innerHTML = '';
      }
    }
  }
  
  // Update on page load
  updateCartDrawerAfterpay();
  
  // Update when cart drawer opens
  const cartDrawer = document.querySelector('cart-drawer');
  if (cartDrawer) {
    const observer = new MutationObserver(function(mutations) {
      mutations.forEach(function(mutation) {
        if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
          if (cartDrawer.classList.contains('active')) {
            setTimeout(updateCartDrawerAfterpay, 100);
          }
        }
      });
    });
    
    observer.observe(cartDrawer, { attributes: true });
  }
  
  // Update when quantity changes
  document.addEventListener('change', function(e) {
    if (e.target.matches('.quantity__input')) {
      setTimeout(updateCartDrawerAfterpay, 500);
    }
  });
  
  // Update when items are added/removed
  document.addEventListener('click', function(e) {
    if (e.target.matches('.cart-remove-button') || 
        e.target.closest('.cart-remove-button') ||
        e.target.matches('.quantity__button') ||
        e.target.closest('.quantity__button')) {
      setTimeout(updateCartDrawerAfterpay, 500);
    }
  });
});
</script>

Step 2: Update your theme.liquid file

In your theme.liquid file, find your existing Afterpay configuration and change these lines:

var afterpay_dynamic_cart_integration_enabled = false; // Change to false
var afterpay_cart_integration_enabled = false; // Also set to false

Then add this additional script right after your existing Afterpay script:

<script>
// Disable Afterpay on individual cart line items to prevent conflicts
document.addEventListener('DOMContentLoaded', function() {
  // Hide any Afterpay messages that appear on individual cart items
  const style = document.createElement('style');
  style.textContent = `
    .cart-item .afterpay-message,
    .cart-item [data-afterpay-paragraph] {
      display: none !important;
    }
  `;
  document.head.appendChild(style);
});
</script>

Step 3: Add CSS styling

Add this CSS to your theme’s stylesheet (usually theme.css or similar):

/* Afterpay cart drawer styling */
.cart-afterpay {
  margin: 10px 0;
  text-align: center;
}

.afterpay-text {
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  color: #6c7173;
}

.afterpay-text img {
  height: 16px;
  vertical-align: middle;
}

/* Hide individual product Afterpay messages in cart drawer */
.cart-item .afterpay-message,
.cart-item [data-afterpay-paragraph] {
  display: none !important;
}

What This Fix Does:

Correct placement: Afterpay message appears under your cart total, exactly where you want it
Accurate calculations: Shows installments based on the full cart total, not individual items
Dynamic updates: Automatically recalculates when customers change quantities or remove items
Clean display: Hides the incorrect individual item messages
Consistent styling: Matches your theme’s design

This should fix the math issue completely - if you have 5 × $20 items ($100 total), it will correctly show “Pay in 4 interest-free payments of $25.00” instead of the incorrect $5.00 based on individual items.


Also, we can handle this entire setup for you as well - just let us know if you’d prefer to have us take care of the technical implementation so you can focus on running your business!

And, please don’t hesitate to reach out if you have any questions for us.

Cheers!
Shubham | Untechnickle

1 Like

Thank you so much, almost all of this worked!

The only thing I can’t get working is this part:

/* Hide individual product Afterpay messages in cart drawer */
.cart-item .afterpay-message,
.cart-item [data-afterpay-paragraph] {
  display: none !important;
}

So, the single line item glitch is still happening in the cart drawer

  • Either I’m not finding the right .css area (theme.css doesn’t exist - so I put it in cart-drawer.css)
  • or maybe there’s still something actively overriding it?

Please advise if I’ve done something wrong

Just to confirm for anyone else who comes across this thread in future;
The cart drawer totals are being properly calculated now using Untechnickle’s code!
https://cdn.discordapp.com/attachments/1217466091945857074/1377530792275738634/image.png?ex=68394d06&is=6837fb86&hm=f053ea33d64bd36f08f7b626453598ab70da6811bff3d96b0bb587e16cee5e51&

The only adjustment I had to make to the code provided was where the AfterPay logo badge wasn’t working

        // Create the Afterpay message
        afterpayElement.innerHTML = `
          <div class="afterpay-text">
            <span>or 4 interest-free payments of $${installmentAmount} with</span>
            **<img src="https://static.afterpay.com/integration/product-page/logo-afterpay-colour.svg"** 
                 alt="Afterpay" style="height: 16px; margin-left: 4px;">
          </div>

So I downloaded the badge asset myself from https://developers.afterpay.com/docs/api/marketing/brand-assets , uploaded it to “Content” then “Files”, and changed the red URL above to match my own hosted asset’s URL.

Now if we can only solve this pesky item-line badge that won’t go away xD
https://cdn.discordapp.com/attachments/1217466091945857074/1377531922984730744/image.png?ex=68394e13&is=6837fc93&hm=ae7b5358a98a0eefb0d76741ea829b61b7dd8d300a831104e3b9cad79e727801&

UPDATE
I went back to the original code that AfterPay provided, and deleted this section:

// var afterpay_product_integration_enabled = false;
// var afterpay_product_selector = '#product-price-selector';
// var afterpay_variable_price_fallback = false;
// var afterpay_variable_price_fallback_selector = '';
// var afterpay_variable_price_fallback_method = 'mutation';  // Can be 'mutation' or 'interval'.

Normally, that would delete the product page integration, but instead, I manually added that in the “Customize” section of the theme editor, on the “Default product” dropdown… following this: https://apps.shopify.com/afterpay-messaging

Screenshot: https://cdn.discordapp.com/attachments/1217466091945857074/1377535246836306031/image.png?ex=6839512c&is=6837ffac&hm=afcc02cdfb30f2d0d0a165c1c0dd6707911acdaeaf08a2f6667b9e5d98bd5281&

That worked for a while, and then it came back like the Silver Guy in Terminator 2 :sweat_smile:
It comes and goes with refreshes, on both my and my partner’s PCs. Including the subtotal section solution. (Cache?)
I’m going to take a break and come back tomorrow haha
Your help so far has been excellent though, thank you!

Hey @lounatuna ,

Great to hear that almost everything is working! The fact that your cart totals are calculating correctly now is fantastic - that was the main battle. And kudos for getting creative with hosting your own Afterpay logo - that’s exactly the right approach when CDN resources don’t cooperate!

Now, about that pesky “Silver Guy from Terminator 2” situation :grinning_face_with_smiling_eyes: - I totally get it! Afterpay’s script is being more persistent than we’d like, and the intermittent behavior you’re seeing (coming and going with refreshes) suggests it’s a timing issue combined with Afterpay actively hunting for price elements to attach to.

Perfect timing that I found the window.AfterPay object! That’s actually the key to a much more elegant solution. Since you can see the Widgets object, we can use Afterpay’s own API to control exactly what gets rendered and where.

Here’s the ULTIMATE TERMINATOR SOLUTION :grinning_face_with_smiling_eyes: that should finally put an end to those rogue line item messages:


Step 1: Replace your existing cart-drawer.liquid script

Remove the script you added previously and replace it with this enhanced version:

<script>
document.addEventListener('DOMContentLoaded', function() {
  
  // NUCLEAR OPTION: Intercept Afterpay's widget creation
  if (window.AfterPay && window.AfterPay.Widgets) {
    // Store the original widget creation method
    const originalPaymentSchedule = window.AfterPay.Widgets.PaymentSchedule;
    
    // Override it to block cart line item widgets
    window.AfterPay.Widgets.PaymentSchedule = function(config) {
      // Check if this widget is trying to attach to a cart line item
      const target = typeof config.target === 'string' ? 
        document.querySelector(config.target) : config.target;
      
      if (target && target.closest('.cart-item')) {
        // Block it! Return a dummy object
        return {
          update: function() {},
          destroy: function() {}
        };
      }
      
      // Allow everything else
      return originalPaymentSchedule.call(this, config);
    };
  }

  // AGGRESSIVE AFTERPAY LINE ITEM DESTROYER ?
  function destroyLineItemAfterpay() {
    // Find and destroy all individual line item Afterpay elements
    const selectors = [
      '.cart-item .afterpay-message',
      '.cart-item [data-afterpay-paragraph]',
      '.cart-item .afterpay-paragraph',
      '.cart-item [class*="afterpay"]',
      '.cart-item [id*="afterpay"]',
      '.cart-drawer .afterpay-paragraph:not(#afterpay-cart-drawer-message)',
      '.cart-drawer [data-afterpay-paragraph]:not(#afterpay-cart-drawer-message)',
      '.cart-item [data-afterpay-widget]'
    ];
    
    selectors.forEach(selector => {
      const elements = document.querySelectorAll(selector);
      elements.forEach(el => {
        if (el.id !== 'afterpay-cart-drawer-message' && !el.closest('#afterpay-cart-drawer-message')) {
          el.remove(); // Complete destruction
        }
      });
    });
  }

  // Function to update Afterpay messaging in cart drawer
  function updateCartDrawerAfterpay() {
    const afterpayElement = document.getElementById('afterpay-cart-drawer-message');
    const totalElement = document.querySelector('.totals__total-value');
    
    if (afterpayElement && totalElement) {
      // Get the cart total
      const totalText = totalElement.textContent || totalElement.innerText;
      const totalAmount = parseFloat(totalText.replace(/[^\d.-]/g, ''));
      
      if (totalAmount && totalAmount >= 1) {
        // Calculate installment amount
        const installmentAmount = (totalAmount / 4).toFixed(2);
        
        // Create the Afterpay message with your hosted logo
        afterpayElement.innerHTML = `
          <div class="afterpay-text">
            <span>or 4 interest-free payments of $${installmentAmount} with</span>
            <img src="YOUR_HOSTED_AFTERPAY_LOGO_URL_HERE" 
                 alt="Afterpay" style="height: 16px; margin-left: 4px;">
          </div>
        `;
      } else {
        afterpayElement.innerHTML = '';
      }
    }
    
    // Destroy any rogue line item messages after updating
    setTimeout(destroyLineItemAfterpay, 50);
    setTimeout(destroyLineItemAfterpay, 200);
    setTimeout(destroyLineItemAfterpay, 500);
  }
  
  // Create a more aggressive CSS injection
  function injectTerminatorCSS() {
    const style = document.createElement('style');
    style.id = 'afterpay-terminator-styles';
    style.textContent = `
      /* TERMINATOR MODE: Hide all individual cart item Afterpay messages */
      .cart-item .afterpay-message,
      .cart-item [data-afterpay-paragraph],
      .cart-item .afterpay-paragraph,
      .cart-item [class*="afterpay"]:not(.cart-item__details):not(.cart-item__media):not(.cart-item__totals),
      .cart-item [id*="afterpay"],
      .cart-item [data-afterpay-widget],
      .cart-drawer .afterpay-paragraph:not(#afterpay-cart-drawer-message),
      .cart-drawer [data-afterpay-paragraph]:not(#afterpay-cart-drawer-message) {
        display: none !important;
        visibility: hidden !important;
        opacity: 0 !important;
        height: 0 !important;
        width: 0 !important;
        overflow: hidden !important;
        position: absolute !important;
        left: -9999px !important;
      }
      
      /* Style for the correct cart total Afterpay message */
      .cart-afterpay {
        margin: 10px 0;
        text-align: center;
      }

      .afterpay-text {
        font-size: 14px;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 4px;
        color: #6c7173;
      }

      .afterpay-text img {
        height: 16px;
        vertical-align: middle;
      }
    `;
    
    // Remove existing style if it exists
    const existingStyle = document.getElementById('afterpay-terminator-styles');
    if (existingStyle) {
      existingStyle.remove();
    }
    
    document.head.appendChild(style);
  }
  
  // Run the terminator CSS injection immediately
  injectTerminatorCSS();
  
  // Set up a mutation observer to catch Afterpay when it tries to sneak back in
  const terminatorObserver = new MutationObserver(function(mutations) {
    let shouldDestroy = false;
    
    mutations.forEach(function(mutation) {
      // Check for added nodes that might be Afterpay elements
      if (mutation.addedNodes.length > 0) {
        mutation.addedNodes.forEach(node => {
          if (node.nodeType === 1) { // Element node
            if (node.matches && (
                node.matches('[data-afterpay-paragraph]') ||
                node.matches('.afterpay-paragraph') ||
                node.matches('[class*="afterpay"]') ||
                node.matches('[data-afterpay-widget]') ||
                (node.querySelector && node.querySelector('[data-afterpay-paragraph], .afterpay-paragraph, [class*="afterpay"], [data-afterpay-widget]'))
            )) {
              shouldDestroy = true;
            }
          }
        });
      }
    });
    
    if (shouldDestroy) {
      setTimeout(destroyLineItemAfterpay, 10);
      setTimeout(destroyLineItemAfterpay, 100);
    }
  });
  
  // Watch the entire cart drawer for changes
  const cartDrawer = document.querySelector('.cart-drawer');
  if (cartDrawer) {
    terminatorObserver.observe(cartDrawer, { 
      childList: true, 
      subtree: true,
      attributes: true,
      attributeFilter: ['class', 'data-afterpay-paragraph', 'data-afterpay-widget']
    });
  }
  
  // Update on page load with multiple attempts
  setTimeout(() => {
    updateCartDrawerAfterpay();
    destroyLineItemAfterpay();
    injectTerminatorCSS(); // Reinject in case it got overridden
  }, 100);
  
  setTimeout(() => {
    destroyLineItemAfterpay();
  }, 500);
  
  setTimeout(() => {
    destroyLineItemAfterpay();
  }, 1000);
  
  // Update when cart drawer opens
  const cartDrawerElement = document.querySelector('cart-drawer');
  if (cartDrawerElement) {
    const drawerObserver = new MutationObserver(function(mutations) {
      mutations.forEach(function(mutation) {
        if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
          if (cartDrawerElement.classList.contains('active')) {
            setTimeout(() => {
              updateCartDrawerAfterpay();
              destroyLineItemAfterpay();
              injectTerminatorCSS(); // Reinject CSS in case it got overridden
            }, 50);
            setTimeout(destroyLineItemAfterpay, 200);
            setTimeout(destroyLineItemAfterpay, 500);
          }
        }
      });
    });
    
    drawerObserver.observe(cartDrawerElement, { attributes: true });
  }
  
  // Update when quantity changes
  document.addEventListener('change', function(e) {
    if (e.target.matches('.quantity__input')) {
      setTimeout(() => {
        updateCartDrawerAfterpay();
        destroyLineItemAfterpay();
      }, 100);
      setTimeout(destroyLineItemAfterpay, 500);
    }
  });
  
  // Update when items are added/removed
  document.addEventListener('click', function(e) {
    if (e.target.matches('.cart-remove-button') || 
        e.target.closest('.cart-remove-button') ||
        e.target.matches('.quantity__button') ||
        e.target.closest('.quantity__button')) {
      setTimeout(() => {
        updateCartDrawerAfterpay();
        destroyLineItemAfterpay();
      }, 100);
      setTimeout(destroyLineItemAfterpay, 500);
    }
  });
  
  // Nuclear option: Periodic cleanup every 2 seconds when cart is open
  setInterval(() => {
    const cartDrawer = document.querySelector('cart-drawer');
    if (cartDrawer && cartDrawer.classList.contains('active')) {
      destroyLineItemAfterpay();
    }
  }, 2000);
  
});
</script>

Step 2: Enhanced theme.liquid interceptor

Add this to your theme.liquid file (this is the key improvement using the AfterPay object you found):

<script>
// Ultimate Afterpay Interceptor using window.AfterPay
(function() {
  // Wait for AfterPay to load
  function initAfterpayInterceptor() {
    if (window.AfterPay && window.AfterPay.Widgets) {
      
      // Method 1: Override the PaymentSchedule widget creation
      const originalPaymentSchedule = window.AfterPay.Widgets.PaymentSchedule;
      window.AfterPay.Widgets.PaymentSchedule = function(config) {
        const target = typeof config.target === 'string' ? 
          document.querySelector(config.target) : config.target;
        
        // Block widgets targeting cart line items
        if (target && target.closest('.cart-item')) {
          console.log('Blocked Afterpay widget on cart line item');
          return { update: function() {}, destroy: function() {} };
        }
        
        return originalPaymentSchedule.call(this, config);
      };
      
      // Method 2: Override any automatic widget initialization
      if (window.AfterPay.initializeForProduct) {
        const originalInitialize = window.AfterPay.initializeForProduct;
        window.AfterPay.initializeForProduct = function(element) {
          if (element && element.closest('.cart-drawer')) {
            return; // Block initialization in cart drawer
          }
          return originalInitialize.call(this, element);
        };
      }
      
      // Method 3: Block automatic paragraph creation
      const originalCreateElement = document.createElement;
      document.createElement = function(tagName) {
        const element = originalCreateElement.call(document, tagName);
        
        if (tagName.toLowerCase() === 'div' || tagName.toLowerCase() === 'span') {
          const originalSetAttribute = element.setAttribute;
          element.setAttribute = function(name, value) {
            if ((name === 'data-afterpay-paragraph' || name.includes('afterpay')) && 
                document.querySelector('.cart-drawer.active')) {
              const cartItem = this.closest('.cart-item');
              if (cartItem) {
                console.log('Blocked Afterpay attribute on cart item');
                return;
              }
            }
            return originalSetAttribute.call(this, name, value);
          };
        }
        
        return element;
      };
      
    } else {
      // AfterPay not loaded yet, try again
      setTimeout(initAfterpayInterceptor, 100);
    }
  }
  
  // Start the interceptor
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initAfterpayInterceptor);
  } else {
    initAfterpayInterceptor();
  }
})();
</script>

Step 3: Remove the old CSS

Since the new script includes the CSS injection, you can remove the CSS you added to cart-drawer.css.


## What makes this solution different:

Uses AfterPay’s own API: Intercepts widget creation at the source using window.AfterPay.Widgets
Multiple destruction waves: Runs cleanup at multiple intervals to catch sneaky widgets
Enhanced CSS blocking: More aggressive hiding with multiple CSS properties
Mutation observer: Watches for new elements being added and destroys them immediately
API interception: Prevents widget creation in the first place rather than just hiding

## Don’t forget:- Replace YOUR_HOSTED_AFTERPAY_LOGO_URL_HERE with your actual hosted logo URL

  • Make sure your theme.liquid still has those Afterpay variables set to false

This solution attacks the problem from five different angles:

  1. CSS hiding (immediate visual fix)
  2. DOM element removal (nuclear destruction)
  3. Widget API interception (prevents creation)
  4. Attribute blocking (stops configuration)
  5. Continuous monitoring (catches anything that slips through)

If the Silver Guy still manages to come back after this, then honestly, we might need to call in Sarah Connor! :grinning_face_with_smiling_eyes: But seriously, this should finally terminate those persistent line item messages once and for all.

Let me know how it goes - I’m confident this will solve the intermittent behavior too since we’re now working with AfterPay’s native API instead of fighting against it.

Cheers!
Shubham | Untechnickle

1 Like

ASTA LA VISTA, Line Item Glitch! :smiling_face_with_sunglasses:

It workeddddd! does happy dance

Thank you so much for your help!

1 Like