Search by

justinholtweb / craft-freeride

Free shipping rules for Craft Commerce — offer it, waive it, block it, and tell customers how far away they are from earning it.

Maintainers

Package info

github.com/justinholtweb/craft-freeride

Type:craft-plugin

pkg:composer/justinholtweb/craft-freeride

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-08-29 12:57 UTC

This package is auto-updated.

Last update: 2026-08-29 12:58:26 UTC


README

Free shipping rules for Craft Commerce 5 — offer it, waive it, block it, and tell customers how far away they are from earning it.

Commerce can already price shipping conditionally. Free Ride is about the other half of the job: the free shipping offer itself, in one ordered list, with the conditions stores actually use and a cart that knows how to say "$12.00 more for free shipping".

Requirements

Craft CMS 5.3+, Craft Commerce 5.0+, PHP 8.2+.

Installation

composer require justinholtweb/craft-freeride
php craft plugin/install freeride

How it works

A rule is a set of conditions that all have to pass. The ordered list of rules is the "or":

  1. Blocking rules run first. If one matches, nothing else can make that order's shipping free.
  2. Then the granting rules, in order. The first one that matches wins.

So "free over $75, but never on the canoes, and free express over $200" is three rules, not one expression, and you can read the whole policy top to bottom on one screen.

The three things a rule can do

Mode What happens
Offer a free shipping method A $0 method appears at checkout when the rule matches — its own name, its own description. Optionally hides the paid methods while it applies.
Waive the cost of the chosen method Whatever the customer picked drops to $0, either for every method or for the ones you name. This is how "free express over $200" works without duplicating the express method.
Block free shipping A veto. Oversized items, excluded regions, clearance stock — anything that should disqualify an order however much it spends.

Conditions

Everything Commerce can ask an order — coupon code, item subtotal, discounted item subtotal, total quantity, total weight, purchasable, shipping zone, gateway, site — plus Free Ride's own:

  • Shipping Postal Code28105, 282*, 10000-19999, in or not in
  • Shipping Country and Shipping State / Province — without modelling a zone first
  • Cart Product Types — includes any / includes only / includes none
  • Cart Contains Something Related To — a category, a collection entry, anything relatable
  • Largest Item Dimension — for "nothing oversized"
  • Distinct Items — line items, not quantity
  • Day of Week and Time of Day — recurring windows, evaluated now, wrapping midnight

Plus customer conditions (user groups and everything else Craft knows about a user), a date window per rule, and per-site targeting. Rules belong to a store, so multi-store installs get their own policy per store.

Templating

{% set progress = craft.freeride.progress() %}

{% if progress.eligible %}
    <p class="free-shipping">{{ progress.message }}</p>
{% elseif progress.isAchievable %}
    <p>{{ progress.message }}</p>
    <progress value="{{ progress.percent }}" max="1"></progress>
{% endif %}

craft.freeride:

Call Returns
progress(order) how far the cart is from free shipping
eligible(order) whether free shipping is available right now
message(order) the one line to show the customer
explain(order) the whole decision, rule by rule and condition by condition
rules(storeId) the store's enabled rules, in evaluation order

Every argument is optional — with none, they read the current cart.

For carts that update without a page load, POST/GET to actions/freeride/cart/progress returns the same thing as JSON.

The progress API tells the truth. A cart is only "$12 away" when every other condition on that rule already passes. A cart shipping somewhere the rule excludes is not $12 away from anything, and it is told nothing rather than something wrong.

The simulator

Free Ride → Simulator takes an order or cart number and prints the decision: which rule matched, or exactly which condition failed, and what the customer would be charged. It reads the same evaluation checkout uses, so it can't disagree with it.

From the command line:

php craft freeride/simulate/order <number|reference|id>

Console

php craft freeride/rules                          # list, in evaluation order
php craft freeride/rules/export --file=rules.json # rules live in the database, not project config
php craft freeride/rules/import --file=rules.json # matching handles are updated, not duplicated

All three take --store=<handle>.

Settings

Default method label, the progress and qualifying messages, whether an estimated shipping address may be used before there is a real one, and a switch that logs every decision while you're setting rules up.

Permissions

Manage free shipping rules and Use the simulator.

License

Proprietary. See LICENSE.md.