By E-commerce 4 Internet Marketers Editorial
Explainer. Website owners and developers who ship hemp-derived goods, nicotine products, or firearms accessories need cart rules that reject illegal destinations before payment, not after a label prints. Spreadsheet bans copied into admins by hand break under volume, multi-SKU carts, and statute updates. This article explains how to design maintainable restriction tables and map them into WooCommerce, Shopify, and custom checkout logic. It is not legal advice and does not publish a 50-state legality map. Prefer primary statutes, agency pages, and counsel-approved rows over secondary “ban lists.”
Why the matrix is the product, not the plugin toggle
High-risk catalogs rarely fail because a platform lacks a checkbox. They fail because the rule data is incomplete, stale, or applied to the wrong SKU class. A maintainable approach treats restrictions as a versioned dataset with explicit product classes, geographic keys, actions (block ship, block sell, require age gate, require FFL handoff), effective dates, and a cite to the primary source that justified each row. Cart engines then become consumers of that dataset. When counsel or a state bulletin changes a rule, you update one table and redeploy, instead of hunting through shipping zones, theme snippets, and marketplace feeds.
Manual matrices that live only in Google Sheets or chat threads create three recurring defects:
- SKU bleed. A hemp seed food SKU and a CBD ingestible SKU share a “hemp” tag and inherit the same ship list even though FDA’s federal food and dietary-supplement framing differs from Farm Bill hemp status alone.
- Geo bleed. A state-level block is applied with a free-text county name that does not match checkout address codes, so restricted counties still check out.
- Silent drift. A carrier or payment rule changes, but the storefront still offers rates because nobody owned the effective-date field.
Federal floors that belong in every design (not invented state bans)
Encode federal floors as separate rule layers so state and county overlays do not overwrite them by accident.
Nicotine and tobacco products. The U.S. Food and Drug Administration (FDA) Tobacco 21 page states that since December 20, 2019 it has been illegal for a retailer to sell any tobacco product, including e-cigarettes, to anyone under 21, with no federal carveout for active-duty military or veterans ages 18 to 20. The same page states the T21 law covers cigarettes, smokeless tobacco, hookah/waterpipe tobacco, cigars, pipe tobacco, liquid nicotine, and electronic nicotine delivery systems including e-cigarettes and e-liquids, and that it is unlawful to sell a tobacco product containing nicotine from any source (including non-tobacco nicotine) to anyone under 21. FDA also states it inspects brick-and-mortar and online retailers for Tobacco 21 compliance. Cart rules for nicotine SKUs therefore need an age-verification gate and a product-class tag that cannot be bypassed by choosing “guest checkout” or a shipping-only form. This article does not invent which states add stricter sale or delivery rules. Those rows belong in counsel-maintained overlays with primary cites.
Hemp and CBD federal framing. FDA’s cannabis and CBD regulation Q&A states that the 2018 Farm Bill removed hemp (cannabis and derivatives with not more than 0.3 percent delta-9 tetrahydrocannabinol on a dry weight basis, as defined there) from Controlled Substances Act control, but explicitly preserved FDA authority under the Federal Food, Drug, and Cosmetic Act (FD&C Act) and section 351 of the Public Health Service Act. FDA treats cannabis-derived products under the same product-type rules as other FDA-regulated articles. Separately, FDA’s Q&A addresses whether THC or CBD products may be sold as dietary supplements and whether it is legal in interstate commerce to sell food to which THC or CBD has been added, pointing to sections 201(ff)(3)(B) and 301(ll) of the FD&C Act. That federal framing is about product classification and interstate food/supplement pathways. It is not a substitute for a state shipping matrix. Do not collapse “Farm Bill hemp” into a single ship-everywhere flag.
Firearms, frames/receivers, ammunition, and accessories. The Bureau of Alcohol, Tobacco, Firearms and Explosives (ATF) publishes Commerce in Firearms and Ammunition rules in 27 CFR Part 478. ATF’s definition of frame or receiver at 27 CFR 478.12 explains which parts are frames or receivers (including certain partially complete kits under the criteria in that section) and how variants and accessories relate to those definitions. Separately, 27 CFR 478.31 sets carrier delivery rules for packages containing a firearm or ammunition in interstate or foreign commerce (written notice to the carrier for shipments to persons other than licensees, prohibitions on exterior firearm labels required by carriers, carrier knowledge limits, and written acknowledgment of receipt for firearm deliveries, with stated exceptions). Ecommerce matrices must classify SKUs against firearm, ammunition, frame/receiver, and true accessory classes with counsel and, where needed, ATF classification materials, before applying state destination bans. This article does not claim that any named accessory is federally unrestricted in every context, and it does not invent state accessory bans.
Monitor ATF.gov and FDA.gov hubs for primary updates. Use exact regulation and Q&A pages as the cite fields on matrix rows.
Design the restriction table before you touch the cart UI
A practical schema for high-risk carts:
| Field | Purpose |
|---|---|
rule_id | Stable primary key |
product_class | Enum such as nicotine_ends, hemp_seed_food, cbd_ingestible, firearm, frame_receiver, ammunition, firearm_accessory |
sku_or_tag | Optional SKU list or catalog tag when class is too broad |
geo_level | country, subdivision (state/province), postal, county |
geo_key | Machine codes only (for example US, US-CA, ZIP or ZIP prefix, or 5-digit county GEOID) |
action | block_checkout, hide_rates, require_age_gate, require_licensee_transfer, warn_only (warn_only is rarely enough for hard bans) |
effective_from / effective_to | Inclusive dates in UTC or store timezone, documented |
source_url | Primary cite (statute, regulation, agency page, counsel memo ID) |
version | Monotonic integer or git SHA of the published matrix |
Use codes, not display names. Checkout forms submit ISO country codes, subdivision codes (WooCommerce state codes, Shopify provinceCode), and postal codes. County names typed by shoppers are unreliable. For U.S. county overlays, the U.S. Census Bureau documents Federal Information Processing Series (FIPS) codes and GEOIDs used to uniquely identify geographic features. A common pattern stores counsel-approved county GEOIDs, then resolves the shopper’s shipping ZIP through a maintained ZIP-to-county map before evaluation. Where platforms only expose ZIP wildcards, store ZIP lists or prefixes derived from that map and version both artifacts together.
Evaluate most-specific match wins. Order evaluation as SKU override, then county/ZIP, then state, then country, then federal floor. Document that order in code comments and in the ops runbook so two plugins cannot silently contradict each other.
Keep one source of truth. Export the matrix to JSON or CSV that CI validates (required fields, unknown product classes rejected, expired rows flagged). Admins may mirror rules into Woo zones or Shopify profiles, but the matrix remains authoritative for audits.
WooCommerce encoding patterns
WooCommerce gives several layers. Combine them intentionally.
Storewide selling and shipping countries. WooCommerce general settings let merchants set selling locations (all countries, all except listed countries, or specific countries) and shipping locations (ship to selling countries, a subset, or disable shipping). Use this only for coarse country policy. It cannot express per-SKU state bans.
Shipping zones with empty methods as hard stops. WooCommerce shipping zones match one zone per customer, first match wins, ordered from smallest geography to largest. Zones can target countries, states/provinces, or continents, and can be limited to specific ZIP/postcodes (including ranges and wildcards such as 902*). Official docs state that if a customer matches a zone with no shipping methods, they are told no shipping is available, and that a region can be excluded by adding a zone for that region with no methods while still using Rest of the world elsewhere. That pattern encodes state or ZIP blocks as geography, not as a product matrix. Use it for storewide destination bans. Do not rely on it alone when only some SKUs are restricted.
Product-aware destination rules. WooCommerce’s Conditional Shipping and Payments extension documents Shipping Destination restrictions that exclude countries or states/regions when conditions match (category, tag, shipping class, package contents, and other conditions listed in that guide). Product-level restrictions live under Product Data > Restrictions. That is the documented path for “category X cannot ship to state Y” without emptying a whole zone for unrestricted SKUs. Prefer shipping classes or categories that mirror your product_class enum so matrix exports can generate restriction rows.
Custom regions when counties are not native states. Core WooCommerce state lists are fixed per country. The Add/Modify States developer doc shows the woocommerce_states filter for adding or modifying state codes. That can surface custom region codes in zones and address fields, but it is a customization with support limits noted by WooCommerce. Many teams instead keep county logic in postcode limits or in a checkout validation callback that reads the matrix, which avoids teaching shoppers nonstandard “state” values.
REST locations for automation. WooCommerce’s shipping zone locations API documents location types postcode, state, country, and continent, which supports scripted sync from a matrix into zones when the restriction is geographic and storewide.
Shopify encoding patterns
Shopify separates product-specific rate geography from server-side checkout validation.
Custom shipping profiles for restricted SKU sets. Shopify’s shipping profiles docs state that a shipping profile is a set of shipping rules for specific products and locations, that a general profile covers the rest of the catalog, and that a product or variant can belong to only one profile at a time. Put restricted product classes into a custom profile whose zones and rates omit blocked destinations. Shopify’s shipping zones and rates help states that customers who enter an address in a region without rates see that no shipping rate is available. Profiles are strong when an entire SKU set simply must not ship to listed countries or regions. They are weaker for county-level logic if the admin UI only exposes country/region groupings your plan supports. Confirm the destination granularity available on your shop before promising county encoding via profiles alone.
Markets interaction. Shopify shipping documentation notes that selling into a country generally requires an active market as well as rates. Inactive markets can block orders even when a zone appears to list the country. Treat Markets configuration as part of the geo control plane for international rows.
Cart and checkout validation functions for hard blocks. Shopify’s developer tutorial Create checkout validation shows Functions that return validation errors exposed to Cart, themes, and checkout, blocking progress when criteria fail. The Admin GraphQL validationCreate mutation description explicitly lists blocking checkout for restricted shipping addresses among use cases, and states validations run on Shopify’s servers and cannot be bypassed by the client. Target delivery-address fields (the Functions docs illustrate targets such as $.cart.deliveryGroups[0].deliveryAddress.postalCode) when rejecting a banned subdivision or postal code. Load the same matrix JSON your Woo stack uses so multi-platform brands do not diverge.
Custom carts and headless checkouts
Headless and custom carts should evaluate restrictions in one server-side service called from cart mutation, shipping-rate quote, and place-order endpoints. Client-only hiding of states is not a control. Recommended pieces:
- Matrix service with signed versions and an audit log of who published each version.
- Address normalizer that outputs ISO country, subdivision code, postal code, and optional county GEOID.
- Evaluator that returns structured errors (
RULE_US_CA_CBD_BLOCK, human message, cite ID) for UX and support. - Order snapshot that stores
matrix_versionand matchedrule_idon the order so chargebacks and regulator inquiries can reconstruct why a shipment was allowed or denied. - Test harness with fixtures for mixed carts (restricted + unrestricted lines), APO/FPO addresses, and ZIP codes that straddle county lines.
Operations checklist for website owners and developers
- Classify SKUs first. Separate nicotine, hemp-seed foods, CBD ingestibles, cosmetics, firearms, frames/receivers, ammunition, and accessories. FDA and ATF primary materials show why those classes are not interchangeable.
- Require a primary cite on every block row. No cite, no deploy.
- Never invent a 50-state matrix from blogs. Add state or county rows only with statute, regulation, or counsel memo references.
- Prefer machine geo keys. Subdivision codes, ZIP prefixes, and Census GEOIDs beat county name strings.
- Encode federal floors separately (Tobacco 21 age gate, FDA cannabis product-type rules, ATF firearm/ammunition shipping and frame definitions).
- Map platform features to matrix actions. Woo empty zones and Conditional Shipping destinations, Shopify profiles and validation Functions, custom server evaluators.
- Regression-test mixed carts before each matrix publish.
- Log matrix version on the order.
- Schedule counsel review when FDA, ATF, or state primary sources change, not on an arbitrary “freshness” rewrite of the storefront copy.
What this article deliberately omits
- Any invented 50-state or county legality matrix for hemp, nicotine, or firearms accessories.
- Claims that a named state bans a named product without a primary cite in this article.
- A full inventory of carrier Acceptable Use policies or payment-network geo rules.
- Step-by-step counsel substitutes for ATF classifications of specific SKUs.
- Legal advice on whether a given shipment is lawful.
Those omissions keep the focus on maintainable encoding. High-risk ecommerce teams need a durable restriction data model and platform mapping patterns, verified against FDA, ATF, WooCommerce, and Shopify primary docs, without filling gaps with fake ban charts.
Sources
- FDA, Tobacco 21 (federal minimum age of sale, covered products, non-tobacco nicotine, online inspection note).
- FDA, FDA Regulation of Cannabis and Cannabis-Derived Products, Including Cannabidiol (CBD) (Farm Bill hemp definition, preservation of FDA authority, dietary supplement and food Q&As).
- ATF / eRegulations, 27 CFR 478.12 Definition of Frame or Receiver.
- ATF / eRegulations, 27 CFR 478.31 Delivery by common or contract carrier.
- ATF, ATF.gov (agency hub for firearms and related programs).
- FDA, FDA.gov (agency hub).
- WooCommerce, General settings (selling and shipping locations).
- WooCommerce, Setting up Shipping Zones.
- WooCommerce, Conditional Shipping and Payments.
- WooCommerce, Add/Modify States.
- Shopify Help, Setting up and managing shipping profiles.
- Shopify Help, Setting up shipping zones and rates.
- Shopify.dev, Create checkout validation.
- Shopify.dev, validationCreate mutation.
- U.S. Census Bureau, Understanding Geographic Identifiers (GEOIDs) (FIPS/GEOID identifiers for geographic features).