Personalized checkout and custom promotions with Shopify Scripts
Hi
I am wanting to change the shipping rate name/message depending on variant metafields. Is it possible to use variant matafields as a condition, and if so can anyone point me in the direction of an example code?
Solved! Go to the solution
This is an accepted solution.
Hi @kathryn7, I've generated the following script for you. This example looks for a shipping rate called "This is the old name" and renames it to "This is the new name" when there is a cart item which has the line item property of "special_property" set to "yes".
########################################################################
## Create Shopify scripts without writing code at playwrightapp.com ##
########################################################################
class Campaign
def initialize(condition, *qualifiers)
@condition = (condition.to_s + '?').to_sym
@qualifiers = PostCartAmountQualifier ? [] : [] rescue qualifiers.compact
@line_item_selector = qualifiers.last unless @line_item_selector
qualifiers.compact.each do |qualifier|
is_multi_select = qualifier.instance_variable_get(:@conditions).is_a?(Array)
if is_multi_select
qualifier.instance_variable_get(:@conditions).each do |nested_q|
@post_amount_qualifier = nested_q if nested_q.is_a?(PostCartAmountQualifier)
@qualifiers << qualifier
end
else
@post_amount_qualifier = qualifier if qualifier.is_a?(PostCartAmountQualifier)
@qualifiers << qualifier
end
end if @qualifiers.empty?
end
def qualifies?(cart)
return true if @qualifiers.empty?
@unmodified_line_items = cart.line_items.map do |item|
new_item = item.dup
new_item.instance_variables.each do |var|
val = item.instance_variable_get(var)
new_item.instance_variable_set(var, val.dup) if val.respond_to?(:dup)
end
new_item
end if @post_amount_qualifier
@qualifiers.send(@condition) do |qualifier|
is_selector = false
if qualifier.is_a?(Selector) || qualifier.instance_variable_get(:@conditions).any? { |q| q.is_a?(Selector) }
is_selector = true
end rescue nil
if is_selector
raise "Missing line item match type" if @li_match_type.nil?
cart.line_items.send(@li_match_type) { |item| qualifier.match?(item) }
else
qualifier.match?(cart, @line_item_selector)
end
end
end
def run_with_hooks(cart)
before_run(cart) if respond_to?(:before_run)
run(cart)
after_run(cart)
end
def after_run(cart)
@discount.apply_final_discount if @discount && @discount.respond_to?(:apply_final_discount)
revert_changes(cart) unless @post_amount_qualifier.nil? || @post_amount_qualifier.match?(cart)
end
def revert_changes(cart)
cart.instance_variable_set(:@line_items, @unmodified_line_items)
end
end
class ChangeRateName < Campaign
def initialize(condition, customer_qualifier, cart_qualifier, li_match_type, line_item_qualifier, rate_selector, new_name)
super(condition, customer_qualifier, cart_qualifier, line_item_qualifier)
@li_match_type = (li_match_type.to_s + '?').to_sym
@rate_selector = rate_selector
@new_name = new_name
end
def run(rates, cart)
return unless qualifies?(cart) && @rate_selector
rates.each do|rate|
rate.change_name(@new_name, {message: ""}) if @rate_selector.match?(rate)
end
end
end
class Selector
def partial_match(match_type, item_info, possible_matches)
match_type = (match_type.to_s + '?').to_sym
if item_info.kind_of?(Array)
possible_matches.any? do |possibility|
item_info.any? do |search|
search.send(match_type, possibility)
end
end
else
possible_matches.any? do |possibility|
item_info.send(match_type, possibility)
end
end
end
end
class LineItemPropertiesSelector < Selector
def initialize(target_properties)
@target_properties = target_properties
end
def match?(line_item)
line_item_props = line_item.properties
@target_properties.all? do |key, value|
next unless line_item_props.has_key?(key)
true if line_item_props[key].downcase == value.downcase
end
end
end
class RateNameSelector < Selector
def initialize(match_type, match_condition, names)
@match_condition = match_condition
@invert = match_type == :does_not
@names = names.map(&:downcase)
end
def match?(shipping_rate)
name = shipping_rate.name.downcase
case @match_condition
when :match
return @invert ^ @names.include?(name)
else
return @invert ^ partial_match(@match_condition, name, @names)
end
end
end
CAMPAIGNS = [
ChangeRateName.new(
:all,
nil,
nil,
:any,
LineItemPropertiesSelector.new(
{"special_property" => "yes"}
),
RateNameSelector.new(
:does,
:match,
["This is the old name"]
),
"This is the new name"
)
].freeze
CAMPAIGNS.each do |campaign|
campaign.run(Input.shipping_rates, Input.cart)
end
Output.shipping_rates = Input.shipping_rates
Here is a screenshot of the UI so you can get a sense for how to customize the values at the bottom of the script:
Let me know if that is helpful for you,
Matthew
This is an accepted solution.
Hi @kathryn7, I've generated the following script for you. This example looks for a shipping rate called "This is the old name" and renames it to "This is the new name" when there is a cart item which has the line item property of "special_property" set to "yes".
########################################################################
## Create Shopify scripts without writing code at playwrightapp.com ##
########################################################################
class Campaign
def initialize(condition, *qualifiers)
@condition = (condition.to_s + '?').to_sym
@qualifiers = PostCartAmountQualifier ? [] : [] rescue qualifiers.compact
@line_item_selector = qualifiers.last unless @line_item_selector
qualifiers.compact.each do |qualifier|
is_multi_select = qualifier.instance_variable_get(:@conditions).is_a?(Array)
if is_multi_select
qualifier.instance_variable_get(:@conditions).each do |nested_q|
@post_amount_qualifier = nested_q if nested_q.is_a?(PostCartAmountQualifier)
@qualifiers << qualifier
end
else
@post_amount_qualifier = qualifier if qualifier.is_a?(PostCartAmountQualifier)
@qualifiers << qualifier
end
end if @qualifiers.empty?
end
def qualifies?(cart)
return true if @qualifiers.empty?
@unmodified_line_items = cart.line_items.map do |item|
new_item = item.dup
new_item.instance_variables.each do |var|
val = item.instance_variable_get(var)
new_item.instance_variable_set(var, val.dup) if val.respond_to?(:dup)
end
new_item
end if @post_amount_qualifier
@qualifiers.send(@condition) do |qualifier|
is_selector = false
if qualifier.is_a?(Selector) || qualifier.instance_variable_get(:@conditions).any? { |q| q.is_a?(Selector) }
is_selector = true
end rescue nil
if is_selector
raise "Missing line item match type" if @li_match_type.nil?
cart.line_items.send(@li_match_type) { |item| qualifier.match?(item) }
else
qualifier.match?(cart, @line_item_selector)
end
end
end
def run_with_hooks(cart)
before_run(cart) if respond_to?(:before_run)
run(cart)
after_run(cart)
end
def after_run(cart)
@discount.apply_final_discount if @discount && @discount.respond_to?(:apply_final_discount)
revert_changes(cart) unless @post_amount_qualifier.nil? || @post_amount_qualifier.match?(cart)
end
def revert_changes(cart)
cart.instance_variable_set(:@line_items, @unmodified_line_items)
end
end
class ChangeRateName < Campaign
def initialize(condition, customer_qualifier, cart_qualifier, li_match_type, line_item_qualifier, rate_selector, new_name)
super(condition, customer_qualifier, cart_qualifier, line_item_qualifier)
@li_match_type = (li_match_type.to_s + '?').to_sym
@rate_selector = rate_selector
@new_name = new_name
end
def run(rates, cart)
return unless qualifies?(cart) && @rate_selector
rates.each do|rate|
rate.change_name(@new_name, {message: ""}) if @rate_selector.match?(rate)
end
end
end
class Selector
def partial_match(match_type, item_info, possible_matches)
match_type = (match_type.to_s + '?').to_sym
if item_info.kind_of?(Array)
possible_matches.any? do |possibility|
item_info.any? do |search|
search.send(match_type, possibility)
end
end
else
possible_matches.any? do |possibility|
item_info.send(match_type, possibility)
end
end
end
end
class LineItemPropertiesSelector < Selector
def initialize(target_properties)
@target_properties = target_properties
end
def match?(line_item)
line_item_props = line_item.properties
@target_properties.all? do |key, value|
next unless line_item_props.has_key?(key)
true if line_item_props[key].downcase == value.downcase
end
end
end
class RateNameSelector < Selector
def initialize(match_type, match_condition, names)
@match_condition = match_condition
@invert = match_type == :does_not
@names = names.map(&:downcase)
end
def match?(shipping_rate)
name = shipping_rate.name.downcase
case @match_condition
when :match
return @invert ^ @names.include?(name)
else
return @invert ^ partial_match(@match_condition, name, @names)
end
end
end
CAMPAIGNS = [
ChangeRateName.new(
:all,
nil,
nil,
:any,
LineItemPropertiesSelector.new(
{"special_property" => "yes"}
),
RateNameSelector.new(
:does,
:match,
["This is the old name"]
),
"This is the new name"
)
].freeze
CAMPAIGNS.each do |campaign|
campaign.run(Input.shipping_rates, Input.cart)
end
Output.shipping_rates = Input.shipping_rates
Here is a screenshot of the UI so you can get a sense for how to customize the values at the bottom of the script:
Let me know if that is helpful for you,
Matthew
Thank you
Discover how to increase customer engagement on your store with articles from Shopify A...
By Jacqui Apr 23, 2025Hey Community 👋 Did you know that March 15th is National Everything You Think Is W...
By JasonH Apr 1, 2025Discover how to increase the efficiency of commerce operations with Shopify Academy's l...
By Jacqui Mar 26, 2025