Soft-focus sunset behind a house roof and tree silhouette, standing in for Shopify prices and fees that change once a buyer’s destination comes into view

Shopify Product Fees by Destination: Markets vs Functions

Shopify Product Fees by Destination: Markets vs Functions

Oct 22, 2026

Octavian Contis 10 minutes

Share

A merchandiser adds an environmental recycling fee so a California checkout picks up a state levy on one SKU. She types the shipping address in hosted checkout and the extra line appears, then the same cart paid with Apple Pay drops the fee.

Shopify documents the break: Checkout UI Extensions cannot apply attribute changes when the buyer uses an accelerated wallet such as Apple Pay or Google Pay. The extension wrote the shipping state into a cart attribute, the Discount Function read that attribute, and the wallet never stored the write.

The next job is a sheet you can hand to commerce and tech. List every SKU that needs a destination-specific fee or price, choose a Markets catalog, a checkout Function that reads delivery address, or a separate fee product, then complete a hosted checkout and an Apple Pay checkout for each rule.

Introduction

This article teaches that inventory and the mechanism choice. Walking every live market for duties, currency, shipping profiles, and catalog gaps before paid international traffic belongs in Shopify Markets Black Friday Traps: Duties, Currency, and Catalog Gaps. Deciding when a Plus checkout needs a UI extension for display and a Function for logic belongs in When to Use Checkout UI Extensions vs Shopify Functions. Confirming UK VAT on EU orders after the September 2026 tax-product change belongs in Shopify Tax Checklist: Confirm UK VAT on EU Orders Over £135.

Merchant and developer threads this month keep returning to one cluster: a destination fee that holds in hosted checkout and vanishes on Apple Pay, a Discount Function that waits on a cart attribute the wallet never wrote, and a team that tries to fake country prices with a discount instead of a Markets catalog. The shared work is to name the SKU, pick the surface that owns the amount, and prove both checkout paths.

Treat the walk as a settings and Function checklist. Confirm plan eligibility before you commission a custom app.

What merchants are running into

The first pattern is a state or province levy on a named product: a recycling fee, an environmental surcharge, or a destination handling line that must appear as its own amount. Teams pre-fill the shipping address in a checkout UI extension, write the province into a cart attribute, and let a Discount Function change the fee variant from that attribute.

The second pattern is a country price that should have lived in a Markets catalog. The store still shows one base price, then a Function or an automatic discount tries to rewrite the amount after the buyer reaches checkout. Express wallets and Shop Pay then see a different number from the product page.

The third pattern is a fee SKU that merchandising added so finance can see a taxable line, while checkout still relies on the attribute handoff to decide whether that SKU belongs in the cart. Hosted checkout can look complete while the wallet cart omits the same line.

Lanoguard is an auto and moto rust-protection brand where we shipped checkout UI extensions and Shopify Functions on a Shopify 2.0 theme, with internationalisation as a storefront feature. Destination rules that change inside one country belong in that Functions layer, with the delivery address as the input. Medik8 is a beauty and skincare brand we migrated to Shopify 2.0 with a multi-store architecture for regional merchandising. Country-level price and catalogue differences can live in Markets catalogs or in separate shops. A levy that changes by state on one shop still needs a Function or a fee product.

Why this happens

Shopify Markets assigns a buyer context from conditions you configure: location, company location, retail location, or channel. A catalog on that market publishes products and attaches a price list, and Shopify can convert the store price, apply a percentage adjustment, or store a fixed local-currency price on the variant. Nested markets inherit parent customisations and the most specific match wins, so a market that only matches a country will price California and Texas the same.

Checkout UI Extensions can write cart attributes through applyAttributeChange. Shopify’s Attributes API lists a hard limit: attribute changes are unavailable on accelerated checkout methods such as Apple Pay and Google Pay. A Function that keys off that attribute is reading a field the wallet path never updated.

The current Discount Function input can include delivery groups with a deliveryAddress. Shopify’s own Function examples treat city and other address fields as optional. Billing address on the cart is also nullable, so a wallet that fills shipping first can still leave billing empty on an early Function run. Legacy product-discount and order-discount Function APIs are a poor home for this job because delivery groups are the field you need.

Cart Transform can add or reshape lines, including a fee component, and line-update operations are limited to development stores and Shopify Plus. That API still needs a destination signal, and the delivery address on the Function input is the signal Shopify already owns.

Destination fee path

SKU needs a destinationfee or priceWhat must change?Country or configuredmarket priceState levy or extra lineMarkets catalog price listFunction readsdeliveryAddressHosted checkout andApple PayDestination fee path
Destination fee path

How to diagnose it

Open a sheet with one row per SKU that merchandising, finance, or compliance says must change with destination. Record the product, the rule (country price, state levy, B2B company price), the surface that currently owns the amount, and whether Apple Pay is on.

Walk the current path in admin before you write code. For a country price, open Markets, open the catalog on that market, and confirm a price list or a percentage adjustment exists for the variant. For a state levy, search the checkout editor and the custom app list for a UI extension that calls applyAttributeChange, then open the Function that reads the matching attribute key.

Place two hosted checkouts for that SKU, one destination that should charge the extra amount and one that should not, then repeat both carts with Apple Pay or Google Pay. If the hosted cart is correct and the wallet cart is wrong, the attribute handoff is the first suspect; if both carts are wrong, the catalog or the Function input is the first suspect.

Open the Function run log, or replay the run with the CLI, for the wallet order. Confirm whether deliveryAddress.provinceCode was present, whether billingAddress was empty, and whether the cart attribute you expected was missing. Shopify’s Function examples already guard address fields with optional reads, and your Function should do the same.

Confirm the plan row on the same sheet: Markets catalog prices can ship without a custom Function, while checkout UI Extensions and custom Functions that change checkout logic need Shopify Plus, except on a development store where Shopify documents Function line-update support. What Shopify Plan Should I Choose? is the plan conversation, and this sheet stays on the SKU and the surface.

Checklist rowWhat you recordPass criterionOwner
SKU and ruleProduct, variant, country price or state levy, amount sourceEvery destination-specific SKU is namedCommerce / finance
Current surfaceMarkets catalog, Function, fee product, cart attribute, or appOne named owner per amountTech
Market conditionsCountry only, nested region, B2B company locationThe market can actually match the destination you sell intoCommerce
Hosted checkoutOne cart that should charge, one that should notThe expected line and amount appearCommerce
Apple Pay or Google PaySame two carts through the walletThe wallet cart matches the hosted cartTech
Function logdeliveryAddress, billingAddress, attribute keys, API targetThe Function read Shopify’s address, not a missing attributeTech

How to fix it

Pick one owner per SKU. Do not leave a catalog price, a discount, and an attribute write all trying to set the same amount.

Markets catalog is the fix when the amount is a country or a market you have already configured. Create or open the market, attach a catalog, and set a percentage adjustment or a fixed local-currency price on the variant. Shopify Help says customers can view, pay, and receive refunds in local currency when you sell to markets, and that you can control prices with rounding, percentage adjustments, and product prices by country or region. Full local-currency checkout needs Shopify Payments and one-page checkout. Add a country selector so the buyer can confirm the market. Read the market price from Shopify. Do not compute it from the base variant in your own code.

A checkout Function that reads delivery address is the fix when the amount changes inside one country, or when you need a levy Shopify will not store on a catalog. Use the current Discount Function API so cart.deliveryGroups can carry deliveryAddress.provinceCode, treat the address as optional, and fall back only when a second, documented field is present. Stop writing the province into a cart attribute as the only source of truth. A UI extension can still explain the levy on hosted checkout. The Function must keep working when that extension never runs.

A separate fee product is the fix when finance needs a taxable, reportable line with its own SKU. Keep that product in the catalogue. Let Cart Transform or the Discount Function add or price it from the delivery address. Line-update operations on Cart Transform stay on development stores and Shopify Plus. Give the fee a title the buyer will recognise on the order, and keep it out of collection grids so shoppers cannot add it from the storefront.

After you pick the path, freeze the other writers. Remove the attribute write if the Function now reads delivery address. Remove the discount that was faking a country price if the catalog now owns that variant. Automatic discounts and Discount Functions can both change an amount at peak, which is why Shopify Automatic Discounts vs Functions for Black Friday Promos belongs on the same desk when a promo and a levy share a cart.

Complete the two hosted checkouts and the two wallet checkouts again, screenshot the order summary, and store the Function version and the market name on the sheet. If the shipping step is empty while add-to-cart still looks healthy, finish this fee inventory first, then use How to Fix Shopify Checkout Abandonment When Your Funnel Looks Healthy.

When to get help

A single country price on one catalog is a Markets configuration task. A state levy that must survive Apple Pay, share the cart with a promo Function, and post as its own taxable SKU is a checkout architecture task.

Bring in a technical partner when more than one app already writes cart lines, when the Function run log still shows an empty delivery address on a paid wallet order, or when B2B company-location catalogs and region markets both claim the same variant. Shopify UI Extensions and Functions is the offering that maps those writers. Theme section edits will not attach a province levy.

Book a stack assessment if the sheet has more than one owner per SKU and the wallet cart still disagrees with hosted checkout after you removed the attribute handoff.

Conclusion

You can now list every SKU that needs a destination-specific fee or price, choose a Markets catalog, a checkout Function that reads delivery address, or a separate fee product, and prove the rule on hosted checkout and Apple Pay.

The catalog owns country and configured-market prices, while the Function owns levies that change inside one country and reads the address Shopify already has. Keep a fee product only when finance needs a reportable line, and treat cart attributes as a weak link on accelerated wallets.

If that sheet still has two writers on one amount, talk to us about checkout Functions or book a discovery call. The work is a named owner and two test checkouts, not another theme section.

Frequently Asked Questions

Use a Shopify Markets catalog with a price list for that market. Shopify can convert the store price at the market exchange rate, apply a percentage adjustment, or store a fixed local-currency price on the variant. Load the market price from Shopify rather than computing it from the base variant. Full local-currency checkout needs Shopify Payments and one-page checkout. A country selector should be on the storefront so the buyer can confirm the market Shopify assigned.

A Markets catalog follows the buyer context you configured. If that market only matches a country, every state in that country sees the same catalog price. Nested markets can be more specific when your conditions include the province or state you care about. A recycling or environmental fee that changes inside one country usually needs a checkout Function that reads deliveryAddress.provinceCode, or a separate fee product the Function adds, unless you have already created a market whose conditions match that state.

Shopify’s Checkout UI Extensions Attributes API states that attribute changes are unavailable when the buyer uses an accelerated checkout method such as Apple Pay or Google Pay. A UI extension that writes the shipping state into a cart attribute, then a Discount Function that reads that attribute, will miss the write on those wallets. Point the Function at the delivery group’s deliveryAddress on the current Discount Function API, treat the address as optional, and complete an Apple Pay test order before you trust the fee.

Use a Discount Function when you already have a fee line or a priced variant and you need to change the amount from the delivery address. Use Cart Transform when you must add, expand, or present a fee line that is not already in the cart. Line-update operations on Cart Transform are limited to development stores and Shopify Plus. Pair the Function with a checkout UI extension only when the buyer needs an explanation on the page. Display and logic stay separate, as in the checkout extensions guide.

Country-level catalog prices through Shopify Markets are available without a custom Function. Checkout UI Extensions and custom Shopify Functions that change checkout logic require Shopify Plus, except on development stores where Shopify documents Function line-update support. If the fee only applies inside one country and Markets cannot match that province, Plus (or an app that already ships the Function) is the path that can read delivery address at checkout. Confirm the plan before you scope a custom app.

Place one hosted checkout to a destination that should charge the fee and one that should not, using a product from the SKU list. Repeat both carts with Apple Pay or Google Pay, and with Shop Pay if that wallet is on. Screenshot the order summary, the fee title, and the address Shopify stored. Open the Function run log or replay the run when the wallet cart disagrees with the hosted cart. Keep the vendor or Function version on the same sheet as the screenshots.

Bring in a technical partner when a Markets catalog, a Discount Function, and a fee SKU all write to the same checkout, or when Apple Pay still drops the line after you stopped using cart attributes. A checkout Functions review maps which surface owns the amount before you add another app. Theme edits will not attach a state levy. If purchases stop after the fee appears, use the checkout abandonment guide once the fee inventory is done.

Related Articles