Calculating shipping rates before applying discounts

Calculating shipping rates before applying discounts

Agolab
Explorer
48 0 16

Hi there. 
Is there any way to make shopify calculate the shipping rate BEFORE applying the discount and not after?

1) On our store we offer free shipping for over 49 

2) Customers add 49 worth of goods and proceed to checkout thinking they have free shipping

3) Apply discount % and total goes to 44

4) Shopify calculate shipping rate base on 44, and customer end up spending 51. 

They spend more with a discount code than without it, because shopify erranously calculate shipping cost on the net discounted and not on the price before discount

5) We lose 50-100 sales a month for this and we receive 10-15 mails a week of people complaining about it and frustrated about it

6) Shopify does absolutely nothing about it and keeps saying it cannot be changed, while litterally would take 10 minutes of coding to add an option allowing merchants to decide this.

 

Anything we can do ? I will surely move away from it if this does not get changed before EOY

Replies 18 (18)

Dirk
Shopify Staff
2396 255 526

Hey @Agolab!

 

Happy to help out today. It sounds like you are using a percentage-based discount to account for the potential cost of shipping? The issue you will have with that is different orders come from different locations, the orders are weighted different, etc. so unless you are using flat based shipping rates (Ex. $10 flat-rate) you won't be able to use a percentage or flat rate discount to match the calculated cost of shipping.

 

In this case, you would want to create a 'Free Shipping' discount. The free shipping discount, once entered, will zero out any shipping cost to the customer as long as they meet the discount requirements that you set up. We have a guide on setting that up here.

 

Let me know if this helps. If there is anything else I can help you with, please let me know.

 

Dirk | Social Care @ Shopify 
 - Was my reply helpful? Click Like to let me know! 
 - Was your question answered? Mark it as an Accepted Solution
 - To learn more visit the Shopify Help Center or the Shopify Blog

Agolab
Explorer
48 0 16

Hi Dirk, 
This is another big issue with shopify: we cannot stack discounts. 

I would be more than glad to offer a coupon for free shipping to our customers, but they cannot apply it with another coupon based on % off the total order. They can only use one at the checkout, another thing that is really really annoying and shows how much shopify is behind its competitors. 

Anyway we ship only to Italy and our shipping rate is a flat rate of 6,50. 

The thing is WE SHOULD NOT LOOK for workarounds to this problem. This problem should by fixed by shopify, allowing merchants to calculate shipping rates before the discounts are applied, like on many many many stores worldwide. 

Is this possible at least with shopify PLUS ? I've heard you can customize more the checkout with that plan. 



Dirk
Shopify Staff
2396 255 526

Great question, using an app such as Stackable Discounts would provide you with that functionality you're looking for to stack multiple discounts together. Including both manual, and automatic discounts.

 

Additionally, the fixed value and percentage-based discounts apply to the products in an order, but don't apply to shipping costs. The free shipping discount code is intended for that. Feel free to learn more about our discount system in greater detail here.

 

With that said, Shopify Plus does provide you with the ability to make more changes to your checkout and includes a script editor tool that allows you to stack discounts as well. With that said, the systematic order in which our checkout functions (items added to cart > cart pushed to checkout > personal info > shipping rates and order total > order confirmation) remain the same. Which is standard practice in ecommerce.

 

I hope this helps! If there is anything else I can help you with, please let me know.

Dirk | Social Care @ Shopify 
 - Was my reply helpful? Click Like to let me know! 
 - Was your question answered? Mark it as an Accepted Solution
 - To learn more visit the Shopify Help Center or the Shopify Blog

SWireless
Tourist
4 0 5

Dirk, I don't think what you are proposing is a solution. The shipping rate should be based on the subtotal and not the total (after the discount is applied). It is honestly just common sense, and trying to find ways around it is just NOT a solution, and this should be addressed with your team. We are having the same issue, and WE ARE LOSING SALES on a daily basis because of this issue. We just migrated from Bigcommerce, thinking that Shopify would be a better platform. Please address with your technical team. Thank you!

DeonSmit
Shopify Partner
13 1 5

This is not a solution. We are also losing so many customers cause they go over the minimum amount for free shipping and then apply their welcome coupon for %10. Goes under the minimum value and then they have to pay shipping. 

 

Shipping fee calculation should exclude coupons. Or at least give us the option when setting up shipping to include coupons at calculation or not

 

reneemc
Visitor
1 0 0

I am sorry to say that the “work around” you explained is not a work around. It’s just doesn’t work. At all. 

I’ll explain. 

I set up a discount code for amount off order for our ship cost of $6.95 to be added automatically to all orders over $60. 
This code comes off the subtotal like we want, but then if an actual discount code is used on the order, for example 20% off, the 20% comes off and could drop the order total below $60 and then the shipping cost set up in the shipping options will add back in. See attached. 

In the shipping and delivery settings, there’s no way to adjust it properly to account for utilizing the discount code option for amount off order at a certain dollar amount because if you set to add shipping at a flat rate to all orders, thinking the discount code that is set up to add automatically at orders $60 and over, if the order total isn’t high enough and the customer uses a discount code, entered at checkout and the subtotal drops below $60, the flat rate shipping cost will be added back in. 

Theres no way to make this work. At all. 
How is it even possible that Shopify was set up this way without thinking about this when creating the options that would be offered to merchants in the shipping and delivery section in settings. It’s truly mind blowing, and also forcing us to rip off our customers. Because that’s exactly what it is, a rip off.

and now I feel terrible that there’s an undetermined amount of customers that technically over paid on their purchases with us. 

this is an even bigger deal if merchants have higher flat rate costs above $6.95, because at some point, the shipping cost becomes more than the discount amount calculated when a customer is entering a code. 
And that is an even bigger rip off because they would actually pay less for their order by not using the discount code at all, and getting the free shipping. 

So, the next question becomes;

Why would merchants ever offer discount codes to our customers when they aren’t actually getting a discount and has in turn, makes the merchants discount code announcements to entice customers and help drive sales for merchants, totally false. 
And Shopify has made all of their merchants lose sales and look like idiots to their customers, because they have no idea the merchant didn’t actually set it up that way and it’s really the poorly developed options in the platform we are using.
Even more shocking it that shop if I hasn’t fixed this already and available for all plans offered.

you should actually be really embarrassed that you suggest that Shopify plus has more options for changes and set up, because merchants shouldn’t have to pay more money giving up revenue to try to drive sales using discounts.  

You should want to provide tools your merchants can use to be as successful as possible but you are hindering all of our success, and yours. 

5260F70C-C239-43ED-8E1D-A735DDCBF3BD.jpeg

 

 

SWireless
Tourist
4 0 5

We found a temporary solution to resolve this issue: an app called discountyard. It allows you to setup free shipping based on the subtotal and not the total. Hopefully Shopify developers will eventually figure a solution and fix this bug since it is misleading to our customers to offer free shipping above a certain amount, and end up charging them a fee if a discount puts them under the threshold. Hope this app helps you! 

DeonSmit
Shopify Partner
13 1 5

Great to know . I actually wanted to come and update here as well. 

 

We are on shopify plus and managed to use their scripting options to change this. So no app. 

 

Let me know if you want the script here. It checks the values before the discount code. If the discount code then drops the order below the minimum order value for free shipping it will then just update the shipping value to 0 as it should be.

DeonSmit
Shopify Partner
13 1 5

Here is the Code. At the bottom you will see I have 2 " Campaigns" You can add more if needed. 

Free shipping cause I hit R450

 

DeonSmit_0-1675664339367.png

Still Free shipping after discount dropped me below the threshold ( Scripts Controlled this )

DeonSmit_1-1675664397934.png

 

 

class Campaign
  def initialize(condition, *qualifiers)
    @condition = (condition.to_s + '?').to_sym
    @qualifiers = PostCartAmountQualifier ? [] : [] rescue qualifiers.compact
    @Anonymous_item_selector = qualifiers.last unless @Anonymous_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) do |item|
          next false if item.nil?
          qualifier.match?(item)
        end
      else
        qualifier.match?(cart, @Anonymous_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 ShippingDiscount < Campaign
  def initialize(condition, customer_qualifier, cart_qualifier, li_match_type, line_item_qualifier, rate_selector, discount)
    super(condition, customer_qualifier, cart_qualifier, line_item_qualifier)
    @Li_match_type = (li_match_type.to_s + '?').to_sym
    @rate_selector = rate_selector
    @discount = discount
  end

  def run(rates, cart)
    raise "Campaign requires a discount" unless @discount
    return unless qualifies?(cart)
    rates.each do |rate|
      next unless @rate_selector.nil? || @rate_selector.match?(rate)
      @discount.apply(rate)
    end
  end
end

class Qualifier
  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

  def compare_amounts(compare, comparison_type, compare_to)
    case comparison_type
      when :greater_than
        return compare > compare_to
      when :greater_than_or_equal
        return compare >= compare_to
      when :less_than
        return compare < compare_to
      when :less_than_or_equal
        return compare <= compare_to
      when :equal_to
        return compare == compare_to
      else
        raise "Invalid comparison type"
    end
  end
end

class CartAmountQualifier < Qualifier
  def initialize(behaviour, comparison_type, amount)
    @behaviour = behaviour
    @comparison_type = comparison_type
    @amount = Money.new(cents: amount * 100)
  end

  def match?(cart, selector = nil)
    total = cart.subtotal_price
    if @behaviour == :item || @behaviour == :diff_item
      total = cart.line_items.reduce(Money.zero) do |total, item|
        total + (selector&.match?(item) ? item.line_price : Money.zero)
      end
    end
    case @behaviour
      when :cart, :item
        compare_amounts(total, @comparison_type, @amount)
      when :diff_cart
        compare_amounts(cart.subtotal_price_was - @amount, @comparison_type, total)
      when :diff_item
        original_line_total = cart.line_items.reduce(Money.zero) do |total, item|
          total + (selector&.match?(item) ? item.original_line_price : Money.zero)
        end
        compare_amounts(original_line_total - @amount, @comparison_type, total)
    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 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

class PercentageDiscount
  def initialize(percent, message)
    @percent = Decimal.new(percent) / 100
    @message = message
  end

  def apply(rate)
    rate.apply_discount(rate.price * @percent, { message: @message })
  end
end

CAMPAIGNS = [
  ShippingDiscount.new(
    :any,
    nil,
    CartAmountQualifier.new(
      :cart,
      :greater_than_or_equal,
      450
    ),
    :any,
    nil,
    RateNameSelector.new(
      :does,
      :match,
      ["Deliver to my door via courier."]
    ),
    PercentageDiscount.new(
      100,
      "Congratulations! Your order qualifies for free shipping."
    )
  ),
  ShippingDiscount.new(
    :any,
    nil,
    CartAmountQualifier.new(
      :cart,
      :less_than,
      450
    ),
    :any,
    nil,
    RateNameSelector.new(
      :does,
      :match,
      ["Deliver to my door via courier."]
    ),
    PercentageDiscount.new(
      0,
      "Free shipping on orders over R450 Ts & Cs apply."
    )
  )
].freeze

CAMPAIGNS.each do |campaign|
  campaign.run(Input.shipping_rates, Input.cart)
end

Output.shipping_rates = Input.shipping_rates

 

 

 

Agolab
Explorer
48 0 16

Is this only for shopify plus merchant ? Where I am supposed to put this script and do I need to change any line of code to make it work in my theme ? I guess a lot as I use euro and 49 as free shipping thresold. 

DeonSmit
Shopify Partner
13 1 5

Only Shopify plus Merchants.

 

Under Script Editor. Yes you will have to add a script to Shipping tab. 

Yes you will have to change some line in the bottom section. 

 

DeonSmit_0-1675687975744.png

 

450 is my free shipping threshold on the amount before discount codes apply.

 

["Deliver to my door via courier."] is my Shipping method name. This needs to match in my case cause my international has diff names. This must be changed to match. 

 

Percentage discount is 100 cause I am giving you free shipping if above is good. 

 

This is a short description on what to do. 

 

ShippingDiscount.new(
    :any,
    nil,
    CartAmountQualifier.new(
      :cart,
      :greater_than_or_equal,
      450
    ),
    :any,
    nil,
    RateNameSelector.new(
      :does,
      :match,
      ["Deliver to my door via courier."]
    ),
    PercentageDiscount.new(
      100,
      "Congratulations! Your order qualifies for free shipping."
    )
  )

 

karlhaller15
Shopify Partner
3 0 1

That worked perfect and the messages into the Shipping Options is a great touch.

Agolab
Explorer
48 0 16

Hei how this works ? Are you sure the app does allow your customer to calculate shipping on subtotal and not total after discount ? Cause that's a function on shopify checkout system and we cannot touch that. 

aliasmatti
Shopify Partner
2 0 9

I'm also looking for a solution on that issue. 

I would like to have an option to set the free shipping based on the original total price (technically described here). With that, the discounts wouldn't be considered when calculating the shipping costs.

@Dirk Free shipping discounts aren't a solution nor a workaround for this issue.

bruksnys
Visitor
1 0 1

Same here. We give people big discounts on black friday, lets say 30%, ant by default free shipping is anything above 70 EUR originally, at the end of the day they pay let's say 60 euros and still get a free shipping, because the value is calculated before, not after applying discounts and we end up giving 30% + let's say another 10% if the shipping cost for us is 6 eur. And 40%value we can't give as we go negative in profits. 

HG1919
Visitor
1 0 1

I'm trying to actually fix the opposite issue (in a shipping app) of having Shipping Rate rules to be applied after a discount has been applied to an order rather than before. I don't think it's deceiving at all if you're offering Free Shipping on total cart value, rather than subtotal (or individual items). I think the later is double dipping. If you visit an online store who offers Free Shipping on orders over $100 and see an item showing the price as Was $110, Now $90, you wouldn't expect Free shipping (just because it used to cost $110), so why would you with a discount code?

RMedia
Shopify Partner
80 7 10

@Agolab @bruksnys @DeonSmit
Advanced Free Shipping allows you to create shipping discounts based on checkout subtotal (that is, total after discounts are applied).
Let me know if I can help further.  (No need for scripts).

Founder @ Advanced Free Shipping, create custom free shipping rules easily.
- Set Free-Shipping by 20 parameters; products, collections, customers + more
- No need for confusing Scripts or Carrier Calculated Shipping
- To learn more about 'Advanced Free Shipping' visit our Shopify app page here.
Agolab
Explorer
48 0 16

I cannot recreate every single of the hundreds of discounts code we have in your app to have this applied. I need a script or a solution to fix it.. 4 years later still no solutions.