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?