Hi Shopify Community,
I’m currently migrating this custom Shopify Script to a Shopify Function as well, since Shopify Scripts are being deprecated. This script controls which shipping rates are displayed to customers based on specific conditions.
- Whether the shipping address is a PO Box
- Whether the order is a return/exchange (based on line item properties)
This script is essential to our shipping experience, and I’d like to help replicate this logic using Shopify Functions (likely through a Delivery Customisation Function).
What the Script Does:
1. PO Box Address Handling
- If the shipping address contains PO Box-related terms (e.g. “P.O. Box”, “Postal Box”), only specific shipping rates (e.g. USPS) are shown.
- All other shipping rates are hidden.
2. Return Shipping Logic
- If none of the cart items include a custom property like
exchange_credit, the script removes any shipping rates labeled “return shipping”.
What I Need Help With:
I want to recreate the exact logic in Shopify Functions, but I’m not sure how to:
- Check if
address1oraddress2contains PO Box triggers in a Delivery Customization Function - Show/hide shipping rates based on string matching (partial or exact) of the rate name
- Detect line item properties (e.g.
exchange_credit) in the input and use them to filter rates - Implement the conditional logic that only shows USPS if PO Box is found
# ================= Customizable Settings =================
SHOW_RATES_FOR_ZIP_PROVINCE_COUNTRY = [
{
po_box_triggers: [
"po box", "post office", "p o box", "p.o.box", "p.o. box", "p.o box", "pobox",
"post office box", "post box", "p. o. box", "po. box", "postal box", "postal code", "PO Box",
],
rate_match_type: :exact,
rate_names: ["USPS *(ONLY OPTION FOR P.O. BOX/APO/FPO/DPO)"],
},
]
# ========== Helper Classes ==========
class RateNameSelector
def initialize(match_type, rate_names)
@match_type = match_type
@comparator = match_type == :exact ? '==' : 'include?'
@rate_names = rate_names&.map { |rate_name| rate_name.downcase.strip }
end
def match?(shipping_rate)
if @match_type == :all
true
else
@rate_names.any? { |name| shipping_rate.name.downcase.send(@comparator, name) }
end
end
end
class AddressSelector
def initialize(triggers)
@triggers = triggers.map { |trigger| trigger.downcase.strip }
end
def match?(address)
address_fields = [address.address1, address.address2].map do |line|
line.nil? ? "" : line.downcase
end
address_fields = address_fields.join(" ")
@triggers.any? { |trigger| address_fields.include?(trigger) }
end
end
class ShowRatesForZipProvinceCountryCampaign
def initialize(campaigns)
@campaigns = campaigns
end
def run(cart, shipping_rates)
address = cart.shipping_address
@campaigns.each do |campaign|
rate_name_selector = RateNameSelector.new(campaign[:rate_match_type], campaign[:rate_names])
if address.nil?
full_match = false
else
country_match = AddressSelector.new(campaign[:po_box_triggers]).match?(address)
full_match = country_match
end
shipping_rates.delete_if do |shipping_rate|
rate_name_selector.match?(shipping_rate) != full_match
end
end
end
end
# ========== Run Campaigns ==========
CAMPAIGNS = [
ShowRatesForZipProvinceCountryCampaign.new(SHOW_RATES_FOR_ZIP_PROVINCE_COUNTRY),
]
CAMPAIGNS.each do |campaign|
campaign.run(Input.cart, Input.shipping_rates)
end
# ========== Narvar Return Shipping Filter ==========
is_return = Input.cart.line_items.map { |x|
x.properties.keys.include?('exchange_credit') || x.properties.keys.include?('_exchange_credit') ||
x.properties.keys.include?('narvar_exchange_credit') || x.properties.keys.include?('_narvar_exchange_credit')
}.any?
if !is_return
Input.shipping_rates.delete_if do |shipping_rate|
shipping_rate.name.downcase.include?('return shipping')
end
end
# ========== Output ==========
Output.shipping_rates = Input.shipping_rates
Would Appreciate Guidance On:
- Mapping these classes and matchers into JavaScript (Wasm) or Rust as a Shopify Function
- Best practices for rate filtering and property checking in the new framework
- Any sample templates or community examples doing similar filtering logic
Thanks in advance for your help! I’d be happy to share a working Function example once it’s built, in case it helps others too.