Member shipping technical details

Offering free shipping on member purchases is a popular membership program benefit and entices members to make additional purchases and increase conversion rates.

In this guide, we’ll walk through one way to implement this benefit using Shopify Scripts.

🚧

Before you start

  • This is a supplemental resource for Recharge's Membership benefits: Free or discounted shipping guide. Refer to Membership benefits: Free or discounted shipping for step by step instructions for configuration.
  • This build requires advanced knowledge of Shopify liquid and some basic knowledge of Ruby. This is not part of Recharge's standard turnkey solution.
  • This build utilizes the Shopify Script Editor which is only available to Shopify Plus merchants.
  • This solution relies on the membership tags created by the Recharge membership program.
  • We recommend applying changes in a duplicate copy of your current theme to avoid causing issues with your live store.

Create a Shopify Shipping Script

To ensure your discounted shipping is applied at checkout, you must install a Shopify Script to automatically apply your shipping benefit during checkout. The benefit will be applied to members and customers who have the membership product in their cart. This can be customized based on which shipping rate(s) you’d like to set to free.

  1. Install the Shopify Script Editor if you haven’t already. Refer to Shopify's documentation for more information on how to use the app.
  2. In the Script Editor app, click Create Script > select Shipping rates > select Modify shipping rate price > click Create script.
  3. Set a descriptive title and customize the code based on your specific shipping rules. Below is a sample script you can leverage to meet your needs:
# ================================ Script Overview ================================
# ================================================================
# v1.3
# Shipping Discounts For Members
#
# Given a Customer with a Membership tag, apply any shipping discounts
#
# Example tags from the Recharge Membership benefit configuration:
#
# - Customer tag:
#   rc-member-total-vip-active
#
# - Product tag:
# - rc-member-total-vip|shipping:shipping-discount|sub:10:percent|otp:10:percent|exclude:50
#
​
# ================================ Customizable Settings ================================
# ================================================================
#
#   - MEMBER_SHIPPING_MESSAGE is the message to be displayed to
#     your customers during the shipping rate selection to indicate
#     why there was a discount applied.
#
# =====
​
MEMBER_SHIPPING_MESSAGE = "Free shipping for members"
​
MEMBERSHIP_ENROLLMENT_PRODUCTS = [
    {
        membership_product_id: 6825939140123,
        membership_tag: "rc-member-default-program-name"
    },
    {
        membership_product_id: 6606423163456,
        membership_tag: "rc-member-default-program-name"
    }
]
​
# ================================ Script Code (do not edit) ================================
# ================================================================
​
​
SECTION_DELIMITER = "|"
VALUE_DELIMITER = ":"
​
class CustomerTagSelector
    def initialize()
    end
​
    def tag_match(customer_tags, product_tags)
        (customer_tags & product_tags)
    end
end
​
class MembershipProductSelector
    def initialize(membership_product_id)
        @product_id = membership_product_id
    end
​
    def match?(line_items)
        product_ids = line_items.map { |line_item| line_item.variant.product.id }
        product_ids.include? @product_id
    end
end
​
class ShippingDiscountForMember
    def initialize()
​
    end
​
    def run (cart, shipping_rates)
        customer = cart.customer
        anon_customer = customer.nil?
        anon_tag = ""
        customer_tags = cart.customer&.tags&.map { |t| t }
​
        membership_product_found = false
​
        # determine if the customer is already tagged, or has the enrollment product in the cart
        process_product_tags = (!customer_tags.nil? && !customer_tags.empty?) && customer_tags.any? { |s| s.include?('rc-member-') }
        if !process_product_tags
            MEMBERSHIP_ENROLLMENT_PRODUCTS.each do |e|
                membership_product_selector = MembershipProductSelector.new(e[:membership_product_id])
​
                if membership_product_selector.match?(cart.line_items)
                    membership_product_found = true
    
                    if anon_customer
                        anon_tag = e[:membership_tag] + '-active'
                    else
                        customer_tags << e[:membership_tag].downcase + '-active'
                    end
                end
            end
        end
        return unless membership_product_found
​
        # extract the potential discounts from the cart
        shipping_discounts = []
​
        # determine the cart type, "sub", "otp", or "mixed"
        cart_type = nil
        cart.line_items.each do |line_item|
            if !line_item.selling_plan_id.nil?
                if cart_type == "otp"
                    cart_type = "mixed"
                    break
                end
                cart_type = "sub"
            else
                if cart_type == "sub"
                    cart_type = "mixed"
                    break
                end
                cart_type = "otp"
            end
        end
        # for each tag on the first line item
        first_line_item_tags = cart.line_items[0].variant.product.tags.each do |tag|
​
            split_tags = tag.split(SECTION_DELIMITER)
            full_benefit = ""
            benefit_type = ""
            program_name = split_tags[0].downcase
            if program_name.include?("rc-member-")
                full_benefit = split_tags[1]
                benefit = full_benefit.split(VALUE_DELIMITER)
                benefit_type = benefit[1]
                if benefit_type == "shipping-discount"
                    disc_benefit = split_tags.select{ |e| e.include?(cart_type+VALUE_DELIMITER)}
                    max_shipping_value = nil
                    exclude = split_tags.select{ |e| e.include?("exclude"+VALUE_DELIMITER)}
                    unless exclude.empty?
                        max_shipping_value = exclude[0].split(VALUE_DELIMITER)[1]
                    end
                    unless disc_benefit.empty?
                        disc_benefit = disc_benefit[0].split(VALUE_DELIMITER)
​
                        value = disc_benefit[1]
                        type = disc_benefit[2]
​
                        shipping_discount = {
                            program: program_name+'-active',
                            value: value,
                            type: type,
                            exclude: max_shipping_value
                        }
                        shipping_discounts.push(shipping_discount)
                    end
                end
            end
        end
​
        if !anon_customer
            customer_map = customer_tags.map { |tag| tag.downcase.strip }
        elsif anon_tag.length > 0
            customer_map = [anon_tag]
        end
        discount_map = shipping_discounts.map{|x| x[:program.downcase]}
        matched_tag = CustomerTagSelector.new().tag_match(discount_map, customer_map)
​
        unless matched_tag.empty?
            benefit_data = shipping_discounts.find { |x| x[:program.downcase] == matched_tag[0] }
            shipping_rates.each do |rate|
                if !benefit_data[:exclude].nil? and Money.new(cents: 100) * benefit_data[:exclude] < rate.price
                    next
                end
                if benefit_data[:type] == "free" and cart.subtotal_price_was > Money.new(cents: 100) * benefit_data[:value].to_f
                    rate.apply_discount(rate.price, message: MEMBER_SHIPPING_MESSAGE)
                end
                if benefit_data[:type] == "fixed"
                    rate.apply_discount(Money.new(cents:100) * benefit_data[:value].to_f, message: MEMBER_SHIPPING_MESSAGE)
                end
                if benefit_data[:type] == "percentage"
                    price_ratio = benefit_data[:value].to_f * 0.01
                    rate.apply_discount(rate.price * price_ratio, message: MEMBER_SHIPPING_MESSAGE)
                end
                if benefit_data[:type] == "flat"
                    flat_rate = Money.new(cents:100) * benefit_data[:value].to_f
                    begin
                        rate.apply_discount(rate.price - flat_rate, message: MEMBER_SHIPPING_MESSAGE)
                    rescue Exception => e
                        puts "could not apply flat rate shipping"
                    end
                end
            end
        end
    end
end
​
discounter = ShippingDiscountForMember.new()
discounter.run(Input.cart, Input.shipping_rates)
Output.shipping_rates = Input.shipping_rates

Test the free shipping functionality

Because this is custom functionality, it's important to test thoroughly to ensure it works as expected. We recommend going through the checkout process as both a non-member and logged-in test member to ensure the free shipping rate is displayed and applied correctly.


Need Help? Contact Us