Product Category Shipping for WooCommerce
WooCommerce shipping plugin that charges per product category using flat-rate or tiered-quantity rules. Selected user roles can be offered a cart-wide Free Local Pickup option alongside the configured shipping rates.
By TechTank
- Details
- FAQ
- Installation
- Development
Description
Product Category Shipping lets WooCommerce store owners define shipping charges per product category instead of per order or per product. Two rule types are available: a simple Flat Rate, or Tiered Quantity brackets that unlock lower rates as cart quantities grow.
How it works for regular customers: the cart is split into one shipping package per managed category. Each package gets its own rate, so customers see exactly one shipping line per category — additive, transparent, and fair.
How it works for selected roles (e.g. Distributors, Wholesale accounts): the cart is not split. Customers see a single cart-wide Shipping total alongside a Free Local Pickup option. One decision covers the entire order. The total is identical to split mode — just a cleaner checkout experience for trade accounts.
Core features
- Per-category shipping rules — Flat Rate or Tiered Quantity, independently configured per category
- Tiered rules support free-shipping thresholds (e.g. “free when quantity ≥ 12”)
- Split-cart mode: one shipping line per category, additive charges
- Combined-cart mode for selected roles: Shipping + Free Local Pickup as two cart-wide choices
- Role diagnostics table in wp-admin showing exact role keys and user counts
- Rate guard prevents third-party “hide other methods when free shipping applies” filters from accidentally removing the Local Pickup option
- All calculations run locally at checkout — no server round-trips, no external API calls
- No dependencies beyond WooCommerce 8.0+
Requirements
- WordPress 6.4+
- PHP 7.4+
- WooCommerce 8.0+ (plugin must be active)
FAQ
What rule types are available?
Flat Rate — a base charge for the first item, plus an optional per-additional-item charge.
Tiered Quantity — unlimited min/max quantity brackets, each with its own price or a “free” flag. Example: 1–5 items = $8, 6–11 items = $5, 12+ items = free.
Can different categories use different rule types?
Yes. Each category is configured independently — one can use Flat Rate, another Tiered Quantity. No conflicts.
How does tiered quantity work?
Quantities are counted per category, not per order. Exact-match tiers (e.g. “exactly 6”) take priority over range tiers. You define as many brackets as you need.
What happens if a customer’s cart quantity falls outside all defined tiers?
In split mode (default): checkout is blocked for that category until the customer adjusts quantity or you extend the top tier. In combined mode (trade roles): that category contributes $0 to the total, undercharging. In both cases, the fix is to set your top tier’s Max Qty to 9999.
How do I enable Free Local Pickup for specific roles?
Go to WooCommerce → Category Shipping → Local Pickup & User Roles and add the desired role key(s) to the Pickup Roles field. Role keys are case-sensitive — use Distributor, not distributor. The diagnostics table shows you the exact keys currently in use on your store.
Why aren’t my trade customers seeing Free Local Pickup?
Most likely a case-sensitivity issue with the role key. The diagnostics table at the bottom of the Local Pickup admin screen shows every matching role, its exact key, and how many users are on it. Check that the key you entered matches exactly.
Does the plugin work with WPML or Polylang?
The plugin loads its own text domain. Place .mo translation files in wp-content/languages/plugins/bt-shipping-{locale}.mo.
Does this work alongside other shipping plugins?
Yes — the plugin submits its own rates alongside other shipping methods. Known incompatibility: plugins that re-package the cart into new packages will cause each extra package to receive its own Shipping + Local Pickup pair. Avoid combining with cart-repackaging plugins.
Will my shipping rules still work if I deactivate the plugin?
No. Rates are only calculated and presented while the plugin is active. Deactivating never deletes your saved rules or settings — they reappear the moment you reactivate.
Installation
- Upload the
product-category-shippingfolder to/wp-content/plugins/— or install via the WordPress Plugins screen. - Activate the plugin through the Plugins menu in WordPress.
- Go to WooCommerce → Settings → Shipping → Category Shipping and add rules for each category you manage.
- Optionally, go to WooCommerce → Category Shipping → Local Pickup & User Roles to configure which roles see Free Local Pickup and set custom labels.
Contributors
Braxton Moody / TechTank Holdings, Inc.
Support
Got something to say? Need help?
When reaching out, include your WordPress and WooCommerce versions, a description of what you expected vs. what happened, and screenshots of your rule configuration if relevant. Typical response: within 48 hours on business days.
Changelog
1.4.5
- Migration bug fixed: the
plugins_loadedmigration hook was checkingpcsfw_shipping_rules === false && pcsfw_shipping_rules !== false— mutually exclusive conditions that always evaluated to false, so no data was ever migrated. - Corrected to check
pcsfw_shipping_rules === false && bt_shipping_rules !== false(old key as source). - Three
$_POSTaccesses insave_rules()(pcsfw_settings,rules,role_rules) suppressed with// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized— data is sanitised bysanitize_rule_set()before use.$_POST['pcsfw_nonce']nonce sanitisation corrected tosanitize_text_field(wp_unslash(...)). $clear_hookrenamed$pcsfw_clear_hookto avoid global namespace pollution.- Text domain reverted to
product-category-shipping(the
short directory name that WordPress.org’s automated Plugin Check tool expects as the canonical domain, distinct from the plugin slug). All__()calls and header updated accordingly.
1.4.4
- Admin layout reorder: Local Pickup & User Roles card moved from the top of the Category Shipping admin page to below the Selected-Role Override Rules section (better UX flow).
PCSFW_URLandPCSFW_VERSIONconstants added — were undefined, causing critical PHP fatal error on admin page load.- All
bt_shipping_*option references in the main plugin file and
shipping method class renamed topcsfw_shipping_*for consistency. - Added
plugins_loadedmigration hook to auto-copy saved data from oldbt_shipping_*keys topcsfw_shipping_*keys
on activation, preventing data loss when upgrading from prior versions.
1.4.3
- HPOS compatibility declared via
FeaturesUtil::declare_compatibility('custom_order_tables')
onbefore_woocommerce_inithook, silencing WooCommerce “incompatible plugins” notice on multisites with HPOS enabled. - Text domain corrected to
product-category-shipping(dots/numbers in domain violate WP conventions — reverted fromv1.4.3andproduct-category-shipping-v1.4.2attempts) BT_SHIPPING_VERSIONbumped to1.4.3.- readme.txt stable tag updated. 25 stray
/* phpcs:ignore WordPress.WP.I18n.TextDomainMismatch */comments removed — they were inside HTML template sections and rendering as visible text in the browser. - Class files renamed
class-bt-shipping-*.php→class-pcsfw-*.php. All__()calls updated to useproduct-category-shippingdomain throughout. Plugin verified working on WordPress multisite
with WooCommerce 7.1.2 + HPOS enabled.
1.4.2
- Fixed duplicate “Category Shipping” submenu entry under WooCommerce.
- Fixed admin settings page rendering raw HTML as plain text (
esc_html()on buffered output). - Removed stale root-level duplicate PHP files (
class-bt-shipping-*.phpat trunk root). - Added
phpcs.xmlruleset + per-filephpcs:ignorefor TextDomainMismatch false positives
(Plugin Check infers domain from versioned folder name, not the declared header). - Added singleton
get_instance()guard to admin class.
1.4.1
- Post-review fixes: removed
Tested up tofrom PHP header (now readme.txt only) - replaced inline
<style>/<script>withwp_enqueue_style/wp_enqueue_script+ externaladmin.css/admin.jsat plugin root - changed text domain to
product-category-shipping-for-woocommerce
(26 occurrences) - added
Requires Plugins: woocommerceheader; removed all WP.org asset files from zip.
1.4.0
- Rebranded to “Product Category Shipping for WooCommerce”
- Updated header fields for WordPress.org compatibility (Tested up to 7.1, WC 9.3)
- Added
Requires at least,Requires PHP,WC requires at least,WC tested up toheader fields - Added translation infrastructure (
load_plugin_textdomain, languages directory) - Internal: renamed main plugin file; no functional changes
1.3.0
- Added Selected-Role Override Rules — a second, independent rule set that fully replaces the default rules for a category when the customer’s role is in
pickup_roles.
1.2.1
- Fixed role keys being lowercased by
sanitize_key(), which silently dropped custom roles on every save. - Added role diagnostics table to the admin screen.
- Added rate guard at priority 99999 to re-insert both combined-mode rates so a third-party “hide other methods when free shipping exists” filter cannot remove one.
1.2.0
- Role-gated free Local Pickup with combined cart-wide rate for selected roles.
- Fixed double-counting for products assigned to two managed categories.
- Made shipping rate IDs unique across all categories.
1.0.4
- Per-category rules, cart split, admin settings page.
Version | 1.4.5 |
Requires WordPress | 6.4+ |
Requires PHP | 7.4+ |
Requires WooCommerce | 8.0+ |
Tested up to WooCommerce | 9.3 |
