Merging Customer Tag and Product Tag Discount Scripts in Shopify

Need Help Combining Two Shopify Discount Scripts

I’m trying to merge two different Shopify scripts to create a targeted discount system. The goal is to give discounts only to customers who have a specific tag AND only on products that also have a certain tag.

Basically, I want the discount to work like this: if a customer has the tag “premium-member” and they’re buying products tagged with “sale-eligible”, then apply the discount. If either condition isn’t met, no discount should be given.

I’ve been working on this for a while but keep running into issues. Here’s my current attempt at combining the scripts:

# ================================ Configuration ================================
CUSTOMER_PRODUCT_DISCOUNTS = [
  {
    buyer_tag_match_type: :include,
    buyer_tags: ["premium-member"],
    item_selector_match_type: :include,
    item_selector_type: :tag,
    item_selectors: ["sale-eligible"],
    discount_type: :percent,
    discount_amount: 15,
    discount_message: "Premium member discount applied!",
  },
]

# ================================ Customer Tag Checker ================================
class BuyerTagChecker
  def initialize(match_type, tags)
    @comparator = match_type == :include ? 'any?' : 'none?'
    @tags = tags.map { |tag| tag.downcase.strip }
  end

  def qualified?(buyer)
    buyer_tags = buyer.tags.map { |tag| tag.downcase.strip }
    (@tags & buyer_tags).send(@comparator)
  end
end

# ================================ Item Selector ================================
class ItemSelector
  def initialize(match_type, selector_type, selectors)
    @match_type = match_type
    @comparator = match_type == :include ? 'any?' : 'none?'
    @selector_type = selector_type
    @selectors = selectors
  end

  def qualified?(cart_item)
    if self.respond_to?(@selector_type)
      self.send(@selector_type, cart_item)
    else
      raise RuntimeError.new('Invalid item selector type')
    end
  end

  def tag(cart_item)
    item_tags = cart_item.variant.product.tags.map { |tag| tag.downcase.strip }
    @selectors = @selectors.map { |selector| selector.downcase.strip }
    (@selectors & item_tags).send(@comparator)
  end
end

# ================================ Discount Handler ================================
class DiscountHandler
  def initialize(discount_type, discount_amount, discount_message)
    @discount_type = discount_type
    @discount_message = discount_message
    @discount_amount = discount_type == :percent ? 1 - (discount_amount * 0.01) : Money.new(cents: 100) * discount_amount
  end

  def process(cart_item)
    new_price = @discount_type == :percent ? cart_item.line_price * @discount_amount : [cart_item.line_price - (@discount_amount * cart_item.quantity), Money.zero].max
    cart_item.change_line_price(new_price, message: @discount_message)
  end
end

# ================================ Main Campaign ================================
class CombinedDiscountCampaign
  def initialize(discounts)
    @discounts = discounts
  end

  def execute(shopping_cart)
    return unless shopping_cart.customer&.tags

    @discounts.each do |discount|
      buyer_checker = BuyerTagChecker.new(discount[:buyer_tag_match_type], discount[:buyer_tags])
      next unless buyer_checker.qualified?(shopping_cart.customer)

      item_selector = ItemSelector.new(
        discount[:item_selector_match_type],
        discount[:item_selector_type],
        discount[:item_selectors]
      )

      discount_handler = DiscountHandler.new(
        discount[:discount_type],
        discount[:discount_amount],
        discount[:discount_message]
      )

      shopping_cart.line_items.each do |cart_item|
        discount_handler.process(cart_item) if item_selector.qualified?(cart_item)
      end
    end
  end
end

active_campaigns = [CombinedDiscountCampaign.new(CUSTOMER_PRODUCT_DISCOUNTS)]
active_campaigns.each { |campaign| campaign.execute(Input.cart) }
Output.cart = Input.cart

The script runs but the discount isn’t being applied correctly. What am I missing here?

Looking at your script, the main issue is in the execute method where you’re applying the discount to all line items regardless of whether they meet the tag criteria. You’re checking item_selector.qualified?(cart_item) but only after creating the discount handler, then applying it unconditionally.

Move the qualification check inside the line items loop and wrap the discount application in that condition. Also, your buyer tag checker logic seems flawed - using send(@comparator) on an array intersection won’t work as expected since arrays don’t have any? or none? methods.

I had similar issues when combining customer and product targeting. The key is ensuring both conditions are met before any discount processing happens. Try restructuring your execute method to check item qualification first, then only process the discount for qualifying items. Also consider adding some debugging output to see which conditions are actually being met during execution.