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.
- Install the Shopify Script Editor if you haven’t already. Refer to Shopify's documentation for more information on how to use the app.
- In the Script Editor app, click Create Script > select Shipping rates > select Modify shipping rate price > click Create script.
- 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.
Updated 6 days ago