Personalized checkout and custom promotions with Shopify Scripts
Hey!
Our store is trying to implement a tiered discount based on spend and found the below script which executes fairly well for us. The problem that we're running into is that it allows a user to enter in a discount code at checkout that stacks upon the existing discount. I'm wondering if anyone has an example script where a single discount code is required in order to execute the below. The other alternative would be for us to disable entering in a coupon code altogether when we run a promo like this, however we would prefer to track this + allow users from other channels (e.g. ambassadors) to be able to use codes that have a higher % off:
TyTyTy for any help / tips here!
# ================================ Customizable Settings ================================
# ================================================================
# Tiered Discounts by Spend Threshold
#
# If the cart total is greater than (or equal to) an entered
# threshold, the associated discount is applied to each item.
#
# - 'threshold' is the spend amount needed to qualify
# - 'discount_type' is the type of discount to provide. Can be
# either:
# - ':percent'
# - ':dollar'
# - 'discount_amount' is the percentage/dollar discount to
# apply (per item)
# - 'discount_message' is the message to show when a discount
# is applied
# ================================================================
SPENDING_THRESHOLDS = [
{
threshold: 30,
discount_type: :percent,
discount_amount: 10,
discount_message: 'Spend $30 and get 10% off!',
},
{
threshold: 50,
discount_type: :percent,
discount_amount: 15,
discount_message: 'Spend $50 and get 15% off!',
},
{
threshold: 100,
discount_type: :percent,
discount_amount: 20,
discount_message: 'Spend $100 and get 20% off!',
},
]
# ================================ Script Code (do not edit) ================================
# ================================================================
# DiscountApplicator
#
# Applies the entered discount to the supplied line item.
# ================================================================
class DiscountApplicator
def initialize(discount_type, discount_amount, discount_message)
@discount_type = discount_type
@discount_message = discount_message
@discount_amount = if discount_type == :percent
1 - (discount_amount * 0.01)
else
Money.new(cents: 100) * discount_amount
end
end
def apply(line_item)
new_line_price = if @discount_type == :percent
line_item.line_price * @discount_amount
else
[line_item.line_price - (@discount_amount * line_item.quantity), Money.zero].max
end
line_item.change_line_price(new_line_price, message: @discount_message)
end
end
# ================================================================
# TieredDiscountBySpendCampaign
#
# If the cart total is greater than (or equal to) an entered
# threshold, the associated discount is applied to each item.
# ================================================================
class TieredDiscountBySpendCampaign
def initialize(tiers)
@Tiers = tiers.sort_by { |tier| tier[:threshold] }.reverse
end
def run(cart)
applicable_tier = @Tiers.find { |tier| cart.subtotal_price >= (Money.new(cents: 100) * tier[:threshold]) }
return if applicable_tier.nil?
discount_applicator = DiscountApplicator.new(
applicable_tier[:discount_type],
applicable_tier[:discount_amount],
applicable_tier[:discount_message]
)
cart.line_items.each do |line_item|
next if line_item.variant.product.gift_card?
discount_applicator.apply(line_item)
end
end
end
CAMPAIGNS = [
TieredDiscountBySpendCampaign.new(SPENDING_THRESHOLDS),
]
CAMPAIGNS.each do |campaign|
campaign.run(Input.cart)
end
Output.cart = Input.cart
Solved! Go to the solution
This is an accepted solution.
Hi Sam,
Those codes are for 0% off. The script takes care of the actual discounting. So you just need one code per tiered pricing scheme.
And for extra clarification, this script supports two different codes (currently STANDARD_CODE and AMBASSADOR_CODE). You can change those codes to match the ones you create in the Shopify Admin > Discounts area. Here is a screen shots of my discount codes:
However, if I wanted to change the codes, I would edit this area of the code I supplied you.
DISCOUNT_CODES = {
'STANDARD_CODE' => SPENDING_THRESHOLDS_STANDARD,
'AMBASSADOR_CODE' => SPENDING_THRESHOLDS_AMBASSADOR
}
Instead I might use a new code for the standard discount called "TIERED_SPECIAL". If so, my code would look like this:
DISCOUNT_CODES = {
'TIERED_SPECIAL' => SPENDING_THRESHOLDS_STANDARD,
'AMBASSADOR_CODE' => SPENDING_THRESHOLDS_AMBASSADOR
}
And then I would need to add a new 0% off discount code to match:
In any case, the discount code is only used to activate the portion of the script that creates the actual tiered discounts.
Hopefully that works for you. If so, feel free to mark this as the accepted solution.
Thanks!
Matthew
Hi @Rastaclat,
I created an app that lets you create scripts like this without having to write code . It has a "tiered discount" feature, so I borrowed some ideas from that.
Here is an updated script that should allow you to create two different discount codes and point them to different discount tiers.
# ================================ Customizable Settings ================================
# ================================================================
# Tiered Discounts by Spend Threshold
#
# If the cart total is greater than (or equal to) an entered
# threshold, the associated discount is applied to each item.
#
# - 'threshold' is the spend amount needed to qualify
# - 'discount_type' is the type of discount to provide. Can be
# either:
# - ':percent'
# - ':dollar'
# - 'discount_amount' is the percentage/dollar discount to
# apply (per item)
# - 'discount_message' is the message to show when a discount
# is applied
# ================================================================
SPENDING_THRESHOLDS_STANDARD = [
{
threshold: 30,
discount_type: :percent,
discount_amount: 10,
discount_message: 'Spend $30 and get 10% off!',
},
{
threshold: 50,
discount_type: :percent,
discount_amount: 15,
discount_message: 'Spend $50 and get 15% off!',
},
{
threshold: 100,
discount_type: :percent,
discount_amount: 20,
discount_message: 'Spend $100 and get 20% off!',
},
]
SPENDING_THRESHOLDS_AMBASSADOR = [
{
threshold: 30,
discount_type: :percent,
discount_amount: 12.5,
discount_message: 'Spend $30 and get 12.5% off!',
},
{
threshold: 50,
discount_type: :percent,
discount_amount: 17.5,
discount_message: 'Spend $50 and get 17.5% off!',
},
{
threshold: 100,
discount_type: :percent,
discount_amount: 22.5,
discount_message: 'Spend $100 and get 22.5% off!',
},
]
DISCOUNT_CODES = {
'STANDARD_CODE' => SPENDING_THRESHOLDS_STANDARD,
'AMBASSADOR_CODE' => SPENDING_THRESHOLDS_AMBASSADOR
}
# ================================ Script Code (do not edit) ================================
# ================================================================
# DiscountApplicator
#
# Applies the entered discount to the supplied line item.
# ================================================================
class DiscountApplicator
def initialize(discount_type, discount_amount, discount_message)
@discount_type = discount_type
@discount_message = discount_message
@discount_amount = if discount_type == :percent
1 - (discount_amount * 0.01)
else
Money.new(cents: 100) * discount_amount
end
end
def apply(line_item)
new_line_price = if @discount_type == :percent
line_item.line_price * @discount_amount
else
[line_item.line_price - (@discount_amount * line_item.quantity), Money.zero].max
end
line_item.change_line_price(new_line_price, message: @discount_message)
end
end
# ================================================================
# TieredDiscountBySpendCampaign
#
# If the cart total is greater than (or equal to) an entered
# threshold, the associated discount is applied to each item.
# ================================================================
class TieredDiscountBySpendCampaign
def initialize(tiers)
@tiers = tiers.sort_by { |tier| tier[:threshold] }.reverse
end
def run(cart)
applicable_tier = @tiers.find { |tier| cart.subtotal_price >= (Money.new(cents: 100) * tier[:threshold]) }
return if applicable_tier.nil?
discount_applicator = DiscountApplicator.new(
applicable_tier[:discount_type],
applicable_tier[:discount_amount],
applicable_tier[:discount_message]
)
cart.line_items.each do |line_item|
next if line_item.variant.product.gift_card?
discount_applicator.apply(line_item)
end
end
end
def discount_code_present(cart, code)
!cart.discount_code.nil? and (cart.discount_code.code.upcase == code.upcase)
end
CAMPAIGNS = []
DISCOUNT_CODES.each do |discount_code|
if discount_code_present(Input.cart, discount_code[0])
CAMPAIGNS = [
TieredDiscountBySpendCampaign.new(discount_code[1]),
]
end
end
CAMPAIGNS.each do |campaign|
campaign.run(Input.cart)
end
Output.cart = Input.cart
Here is a screen shot of it working for me:
Don't forget to create the actual discount codes in the Shopify Admin > Discounts screen.
Hope that helps!
Matthew
Hey Matthew,
Thank you for your help here! The below script worked amazing as far as requiring a discount code to be entered vs. automatically applying. I just have a question about having the code apply the correct % off depending on the $ threshold. If I create this coupon in the backend, I'm only able to assign one % off, as Shopify doesn't allow me to create multiple codes with the same discount code name for each % off. Do you have a workaround for this?
Thanks again!
Sam
This is an accepted solution.
Hi Sam,
Those codes are for 0% off. The script takes care of the actual discounting. So you just need one code per tiered pricing scheme.
And for extra clarification, this script supports two different codes (currently STANDARD_CODE and AMBASSADOR_CODE). You can change those codes to match the ones you create in the Shopify Admin > Discounts area. Here is a screen shots of my discount codes:
However, if I wanted to change the codes, I would edit this area of the code I supplied you.
DISCOUNT_CODES = {
'STANDARD_CODE' => SPENDING_THRESHOLDS_STANDARD,
'AMBASSADOR_CODE' => SPENDING_THRESHOLDS_AMBASSADOR
}
Instead I might use a new code for the standard discount called "TIERED_SPECIAL". If so, my code would look like this:
DISCOUNT_CODES = {
'TIERED_SPECIAL' => SPENDING_THRESHOLDS_STANDARD,
'AMBASSADOR_CODE' => SPENDING_THRESHOLDS_AMBASSADOR
}
And then I would need to add a new 0% off discount code to match:
In any case, the discount code is only used to activate the portion of the script that creates the actual tiered discounts.
Hopefully that works for you. If so, feel free to mark this as the accepted solution.
Thanks!
Matthew
Im not sure why this is happening? Did I miss something?
# ================================ Customizable Settings ================================
# ================================================================
# Tiered Discounts by Spend Threshold
#
# If the cart total is greater than (or equal to) an entered
# threshold, the associated discount is applied to each item.
#
# - 'threshold' is the spend amount needed to qualify
# - 'discount_type' is the type of discount to provide. Can be
# either:
# - ':percent'
# - ':dollar'
# - 'discount_amount' is the percentage/dollar discount to
# apply (per item)
# - 'discount_message' is the message to show when a discount
# is applied
# ================================================================
SPENDING_THRESHOLDS_STANDARD = [
{
threshold: 100,
discount_type: :dollar,
discount_amount: 15,
discount_message: 'Spend $100 and get $15 off!',
},
{
threshold: 200,
discount_type: :dollar,
discount_amount: 40,
discount_message: 'Spend $200 and get $40 off!',
},
{
threshold: 400,
discount_type: :dollar,
discount_amount: 100,
discount_message: 'Spend $400 and get $100 off!',
},
]
SPENDING_THRESHOLDS_AMBASSADOR = [
{
threshold: 30,
discount_type: :percent,
discount_amount: 12.5,
discount_message: 'Spend $30 and get 12.5% off!',
},
{
threshold: 50,
discount_type: :percent,
discount_amount: 17.5,
discount_message: 'Spend $50 and get 17.5% off!',
},
{
threshold: 100,
discount_type: :percent,
discount_amount: 22.5,
discount_message: 'Spend $100 and get 22.5% off!',
},
]
DISCOUNT_CODES = {
'CYBER' => SPENDING_THRESHOLDS_STANDARD,
'AMBASSADOR_CODE' => SPENDING_THRESHOLDS_AMBASSADOR
}
# ================================ Script Code (do not edit) ================================
# ================================================================
# DiscountApplicator
#
# Applies the entered discount to the supplied line item.
# ================================================================
class DiscountApplicator
def initialize(discount_type, discount_amount, discount_message)
@discount_type = discount_type
@discount_message = discount_message
@discount_amount = if discount_type == :percent
1 - (discount_amount * 0.01)
else
Money.new(cents: 100) * discount_amount
end
end
def apply(line_item)
new_line_price = if @discount_type == :percent
line_item.line_price * @discount_amount
else
[line_item.line_price - (@discount_amount * line_item.quantity), Money.zero].max
end
line_item.change_line_price(new_line_price, message: @discount_message)
end
end
# ================================================================
# TieredDiscountBySpendCampaign
#
# If the cart total is greater than (or equal to) an entered
# threshold, the associated discount is applied to each item.
# ================================================================
class TieredDiscountBySpendCampaign
def initialize(tiers)
@tiers = tiers.sort_by { |tier| tier[:threshold] }.reverse
end
def run(cart)
applicable_tier = @tiers.find { |tier| cart.subtotal_price >= (Money.new(cents: 100) * tier[:threshold]) }
return if applicable_tier.nil?
discount_applicator = DiscountApplicator.new(
applicable_tier[:discount_type],
applicable_tier[:discount_amount],
applicable_tier[:discount_message]
)
cart.line_items.each do |line_item|
next if line_item.variant.product.gift_card?
discount_applicator.apply(line_item)
end
end
end
def discount_code_present(cart, code)
!cart.discount_code.nil? and (cart.discount_code.code.upcase == code.upcase)
end
CAMPAIGNS = []
DISCOUNT_CODES.each do |discount_code|
if discount_code_present(Input.cart, discount_code[0])
CAMPAIGNS = [
TieredDiscountBySpendCampaign.new(discount_code[1]),
]
end
end
CAMPAIGNS.each do |campaign|
campaign.run(Input.cart)
end
Output.cart = Input.cart
I am looking for a code that works on the cart subtotal not line item price.
For example I used the code provided but if the cart reaches $75 it is giving $10 off each item in the cart instead of just $10 off the order
We also have the same question – Is there a way to discount the cart subtotal, rather than each individual line item if the cart threshold is met? Specifically, this script doesn't work if we wanted to take $10 off $100, as it would discount $10 for each line item. Thank you!
@CRMW @japple I know it's not a PLUS script, but our app can apply tiered discounts on the subtotal, rather than on each line_item. Example here.
The app works alongside scripts that use discount codes, which means you can stack a tiered discount applied with our app with a discount applied via Script.
I'm having the same issue where the script works but it shows Discount under the subtotal as $0, which I think will confuse customers. Has anyone figured out how to fix this?
I was looking into this code and was wondering if it could be modified to restrict the eligible items? Similar to this solution https://community.shopify.com/c/shopify-scripts/script-tiered-discounts-w-collection-condition/m-p/6... but with your code? Thanks!
Hi @playwright-mike
Hoping you can help. I am trying to use this code, but it does nothing to the cart. Any idea what is going on? I even called into tech support and they could not figure it out. They did say something about it being older, and there may be an update to the code necessary for it to work.
# ================================ Customizable Settings ================================
# ================================================================
# Tiered Discounts by Spend Threshold
#
# If the cart total is greater than (or equal to) an entered
# threshold, the associated discount is applied to each item.
#
# - 'threshold' is the spend amount needed to qualify
# - 'discount_type' is the type of discount to provide. Can be
# either:
# - ':percent'
# - ':dollar'
# - 'discount_amount' is the percentage/dollar discount to
# apply (per item)
# - 'discount_message' is the message to show when a discount
# is applied
# ================================================================
SPENDING_THRESHOLDS_150 = [
{
threshold: 150,
discount_type: :percent,
discount_amount: 15,
discount_message: '15% off!',
},
{
threshold: 200,
discount_type: :percent,
discount_amount: 20,
discount_message: '20% off!',
},
{
threshold: 250,
discount_type: :percent,
discount_amount: 25,
discount_message: '25% off!',
},
]
SPENDING_THRESHOLDS_200 = [
{
threshold: 200,
discount_type: :percent,
discount_amount: 20,
discount_message: '20% off!',
},
]
SPENDING_THRESHOLDS_250 = [
{
threshold: 250,
discount_type: :percent,
discount_amount: 25,
discount_message: '25% off!',
},
]
DISCOUNT_CODES = {
'CMTEST15' => SPENDING_THRESHOLDS_150,
'CMTEST20' => SPENDING_THRESHOLDS_200,
'CMTEST25' => SPENDING_THRESHOLDS_250,
}
# ================================ Script Code (do not edit) ================================
# ================================================================
# DiscountApplicator
#
# Applies the entered discount to the supplied line item.
# ================================================================
class DiscountApplicator
def initialize(discount_type, discount_amount, discount_message)
@discount_type = discount_type
@discount_message = discount_message
@discount_amount = if discount_type == :percent
1 - (discount_amount * 0.01)
else
Money.new(cents: 100) * discount_amount
end
end
def apply(line_item)
new_line_price = if @discount_type == :percent
line_item.line_price * @discount_amount
else
[line_item.line_price - (@discount_amount * line_item.quantity), Money.zero].max
end
line_item.change_line_price(new_line_price, message: @discount_message)
end
end
# ================================================================
# TieredDiscountBySpendCampaign
#
# If the cart total is greater than (or equal to) an entered
# threshold, the associated discount is applied to each item.
# ================================================================
class TieredDiscountBySpendCampaign
def initialize(tiers)
@tiers = tiers.sort_by { |tier| tier[:threshold] }.reverse
end
def run(cart)
applicable_tier = @tiers.find { |tier| cart.subtotal_price >= (Money.new(cents: 100) * tier[:threshold]) }
return if applicable_tier.nil?
discount_applicator = DiscountApplicator.new(
applicable_tier[:discount_type],
applicable_tier[:discount_amount],
applicable_tier[:discount_message]
)
cart.line_items.each do |line_item|
next if line_item.variant.product.gift_card?
discount_applicator.apply(line_item)
end
end
end
def discount_code_present(cart, code)
!cart.discount_code.nil? and (cart.discount_code.code.upcase == code.upcase)
end
CAMPAIGNS = []
DISCOUNT_CODES.each do |discount_code|
if discount_code_present(Input.cart, discount_code[0])
CAMPAIGNS = [
TieredDiscountBySpendCampaign.new(discount_code[1]),
]
end
end
CAMPAIGNS.each do |campaign|
campaign.run(Input.cart)
end
Output.cart = Input.cart
If you also wanted to add Free Shipping using a code that applies the discount tiers, how would you adjust that script?
Hey Matt,
Can you point me in the right direction on where to paste this script. I have tried the theme.liquid and the cart.liquid but no luck.
Thank you,
Jerry
Hi Jerry - Shopify Scripts like these are added via the Script Editor app, which is exclusive to Plus clients. If you are a Plus client, and you don’t have the now ‘offline’ Script Editor app installed already, there are some ways to still get it.
Note though that scripts like these are being deprecated in 2025, as Shopify wants checkout customizations to be made using Functions, authorized Checkout Apps, etc.
Cheers.
WhiteWater Web
Hey Community! As the holiday season unfolds, we want to extend heartfelt thanks to a...
By JasonH Dec 6, 2024Dropshipping, a high-growth, $226 billion-dollar industry, remains a highly dynamic bus...
By JasonH Nov 27, 2024Hey Community! It’s time to share some appreciation and celebrate what we have accomplis...
By JasonH Nov 14, 2024