Skip to main content
Version: 2.0.0

Fees, Zones & Rates

Shipping costs in yStore are built from three components:

  1. Shipping Zones — postal-code-based geographic areas that group destinations
  2. Table Rates — weight-based cost rows matched to a zone group
  3. Shipping Fees — optional surcharges layered on top of a rate

Each component has its own CP section under yStore → Shipping.

Shipping zones

Navigate to yStore → Shipping → Zones.

The index has two tabs:

  • ZIP list — all individual zone records
  • Zone groups — zones aggregated by their group label

A zone is a single country + postal code (or postal code range) entry. Multiple zones can share a group label; table rates are then assigned to that group rather than individual zones.

Zone fields

FieldRequiredDescription
NameYesUnique internal identifier (e.g., "SK — Bratislava")
Country codeYesISO 3166-1 alpha-2 (e.g., SK, CZ, HU)
GroupNoGroups related zones together so one table rate can cover multiple ZIP entries
ZIPYes (exact mode)Single postal code
ZIP from / ZIP toYes (range mode)Inclusive postal code range
Range modeToggle between exact ZIP and ZIP range
EnabledDisabled zones are excluded from rate matching
Zone groups

Create a group named SK-Standard and add every Slovak postal zone to it. A single table rate assigned to SK-Standard then applies to all matching zones, without duplicating rows.

Adding a zone

  1. Go to yStore → Shipping → Zones.
  2. Click New Zone.
  3. Enter a name and the country code.
  4. Optionally assign a group (select an existing one, or type a new group name).
  5. Enable range mode if you want to cover a ZIP range; otherwise enter a single ZIP.
  6. Save.

Table rates

Navigate to yStore → Shipping → Table Rates.

A table rate defines a shipping price for a specific weight threshold, optionally scoped to a zone group. At checkout yStore selects the matching rate by comparing the cart's total weight against all enabled rates for the customer's zone group.

Rate modes

Each table rate operates in one of three modes:

ModeWhen to use
Exact weightApplies when the cart weight equals a specific value; use * to match any weight
Weight rangeApplies when cart weight falls between weight_from and weight_to
Self rateInherits the weight definition from another table rate — reuse weight brackets across multiple prices without duplicating them

Table rate fields

FieldRequiredDescription
NameYesInternal label
Zone groupNoZone group this rate applies to; leave as NOT IN GROUP to apply outside any group
PriceYesShipping cost in the store currency
WeightYes (exact mode)Weight value in kg; * matches any weight
Weight from / Weight toYes (range mode)Weight range in kg
Reference rateYes (self rate mode)Another table rate whose weight definition is inherited
Excluded shipping methodsNoMethods that will not use this rate
DefaultFallback when no other rate matches; only one rate can be the default
EnabledDisabled rates are ignored at checkout

Creating a table rate

  1. Go to yStore → Shipping → Table Rates.
  2. Click New Table Rate.
  3. Enter a name and select the zone group.
  4. Enter the price.
  5. Choose a weight mode and fill in the weight value(s).
  6. Optionally exclude shipping methods that should not use this rate.
  7. Save.

Managing table rates

ActionHow
ReorderDrag rows to change evaluation order
Set defaultMark one rate as the fallback for unmatched carts (clears the previous default automatically)
DuplicateCopy a rate — the copy gets a (Copy) name suffix
Bulk enable/disableSelect rows and use the status toggle
DeleteSelect rows and click Delete

Example: tiered domestic rates (Slovakia)

NameZone groupModeWeightPrice
SK 0–2 kgSK-AllWeight range0 – 2 kg€3.50
SK 2–5 kgSK-AllWeight range2 – 5 kg€4.90
SK 5–20 kgSK-AllWeight range5 – 20 kg€6.90
Fallback— (default)Exact*€9.90

Self rate example

To charge a lower price for Bratislava using the same weight brackets as the standard SK rates, create self rates that reference the standard rows:

NameZone groupModeReferencesPrice
SK 0–2 kgSK-AllWeight range€3.50
SK Bratislava 0–2 kgSK-BratislavaSelf rateSK 0–2 kg€2.90

Shipping fees

Navigate to yStore → Shipping → Fees.

Fees are optional surcharges that can be added to or can replace a rate. The Fees section only appears when salesShippingFeesEnabled is active in plugin settings.

Fee fields

FieldRequiredDescription
NameYesUnique internal name
LabelNoDisplay name shown to the customer at checkout
CodeNoUnique machine identifier; auto-generated from the name if left blank
InstructionsNoOptional text displayed alongside the fee at checkout
Fee adjustmentYesappend — added to the rate price; override — replaces the rate price
Zone dataNoPer-zone pricing rows (see below)
RequiredAlways applied, cannot be removed by the customer
EnabledDisabled fees are not applied

Fee adjustment modes

ModeEffect
appendFee amount is added on top of the matched table rate
overrideFee amount replaces the matched table rate entirely

Use override for fixed-price scenarios, for example a cash-on-delivery fee that sets a flat shipping total regardless of weight.

Zone-based pricing (data)

A fee can carry a per-zone pricing table (data). Each row maps a zone label to an amount:

KeyDescription
labelHuman-readable zone name
codeMachine identifier; auto-generated from label if omitted
valueFee amount for this zone
freeIf true, waives the fee when free shipping is available

This allows one fee record to charge different amounts per zone without creating multiple fee records.

Adding a fee

  1. Go to yStore → Shipping → Fees.
  2. Click New Fee.
  3. Enter a name; the code is generated automatically.
  4. Choose the fee adjustment mode (append or override).
  5. Optionally add zone data rows for per-zone pricing.
  6. Save.
  7. Attach the fee to the relevant shipping method: yStore → Shipping → Methods → [Method] → Fees.

Shipping rules

Navigate to yStore → Shipping → Rules.

Shipping rules override the calculated shipping price for selected methods when a set of conditions is met. They use Craft's native condition builder and are evaluated at checkout after the base rate and fees have been determined.

Rule fields

FieldRequiredDescription
NameYesUnique internal label
Shipping methodsYesOne or more methods this rule applies to
PriceYesOverride price charged when conditions pass; use 0 for free shipping
ConditionsNoCraft native conditions — all must pass for the rule to activate
EnabledDisabled rules are never evaluated

Adding a rule

  1. Go to yStore → Shipping → Rules.
  2. Click New Rule.
  3. Select the Shipping methods this rule should affect.
  4. Enter the override Price (0 for free shipping).
  5. Add conditions using Craft's condition builder.
  6. Save.

When a rule's conditions pass, its price replaces the method's calculated shipping price for the affected methods.

Use cases

Rules are ideal for promotional scenarios that don't fit into static table rates: free shipping for members, holiday discount rates, or cart-value-based overrides scoped to specific methods.

How zones, rates, fees, and rules connect

Customer's shipping address
└── matched to Zone (country_code + ZIP)
└── Zone belongs to Zone Group (e.g., "SK-All")
└── Table Rate selected by Zone Group + cart weight
└── Shipping Fee applied (append or override)
└── Shipping Rule evaluated — overrides price if conditions pass

Concrete example:

Zone: SK — Bratislava 800–829  (group: SK-Bratislava)
└── Table Rate: SK Bratislava 0–2 kg → €2.90
└── Fee: Handling surcharge → +€0.50 (append) = €3.40
└── Rule: "Free shipping for members" → €0.00 (if conditions pass)

Setup checklist

  1. Create zones — one record per country + ZIP (or ZIP range); assign a group label to zones you want to bundle.
  2. Create table rates — one row per weight bracket per zone group; use self rates to reuse weight brackets across multiple groups.
  3. Set a default rate — ensures a price always appears even when no zone group matches.
  4. Create fees if needed — configure adjustment mode and optional per-zone data; enable salesShippingFeesEnabled in plugin settings.
  5. Link fees to methods — attach fees in yStore → Shipping → Methods → [Method] → Fees.
  6. Create rules for condition-based price overrides — e.g., free shipping for specific customer segments.
  7. Test — place test orders with addresses in each zone group and verify the correct rate, fee, and rule outcome appear at checkout.

Developer / integrator reference

Rate resolution at checkout

yStore resolves the shipping cost for a cart in three steps:

  1. Zone matching — the customer's country_code and postal code are compared against all enabled zones. Range zones match when zip_from ≤ customer_zip ≤ zip_to; exact zones match when zip === customer_zip.
  2. Table rate selection — all enabled table rates whose zone_group equals the matched zone's group are candidates. The candidate whose weight bracket covers the cart's total weight is selected. A rate with weight = '*' (stored as the literal string * in the database; getFormattedWeight() resolves it to 0.0 for numeric comparisons) matches any weight and is typically used as the fallback when no bracket applies. If no zone matched in step 1, the table rate flagged is_default is used directly.
  3. Fee application — enabled fees attached to the chosen shipping method are applied in order: append adds to the rate price; override replaces it.
  4. Rule evaluation — enabled shipping rules whose conditions all pass can override the final price for the affected methods.

Self rates do not carry their own weight bracket — they inherit it from the referenced table rate via table_rate_id. getFormattedWeight() on a self rate resolves the parent's bracket.

PHP service API

Access services via the plugin instance. Services are request-scoped and cache their results in memory; call the clearing variant (getAllActive*) if you need a fresh query.

ZoneService

use yui\craft\Plugin;

$zones = Plugin::getInstance()->getShippingZone();

// All zones (cached)
$zones->getAllShippingZones(): ZoneModel[]

// Enabled zones only (always queries)
$zones->getAllActiveShippingZones(): ZoneModel[]

// Retrieve by ID
$zones->getShippingZoneById(int $id): ?ZoneModel

// First zone with a matching group
$zones->getShippingZoneByGroup(string $group): ?ZoneModel

// All zones with a matching group
$zones->getShippingZonesByGroup(string $group): ZoneModel[]

// Group summary: [['group' => 'SK-All', 'count' => 12], ...]
$zones->getAllShippingZoneGroups(): array

TableRateService

$rates = Plugin::getInstance()->getShippingTableRate();

// All rates ordered by sort_order (cached)
$rates->getAllShippingTableRates(): TableRateModel[]

// Enabled rates only (always queries)
$rates->getAllActiveShippingTableRates(): TableRateModel[]

// Retrieve by ID
$rates->getShippingTableRateById(int $id): ?TableRateModel

// Retrieve multiple by IDs
$rates->getShippingTableRatesByIds(array $ids): TableRateModel[]

// Weight-group option list for self-rate dropdowns
// ['label' => '0kg - 2kg', 'value' => 7]
$rates->getShippingTableRateWeightGroups(bool $labeled = false): array

FeeService

$fees = Plugin::getInstance()->getShippingFee();

// Check whether the fees module is active
$fees->getEnabled(): bool // reads salesShippingFeesEnabled from plugin settings

// All fees (cached)
$fees->getAllShippingFees(): FeeModel[]

// Enabled fees only (always queries)
$fees->getAllActiveShippingFees(): FeeModel[]

// Retrieve by ID or code
$fees->getShippingFeeById(int $id): ?FeeModel
$fees->getShippingFeeByCode(string $code): ?FeeModel

RuleService

$rules = Plugin::getInstance()->getShippingRule();

// All rules (cached)
$rules->getAllShippingRules(): RuleModel[]

// Enabled rules only (always queries)
$rules->getAllActiveShippingRules(): RuleModel[]

// Retrieve by ID
$rules->getShippingRuleById(int $id): ?RuleModel

RuleModel key properties:

PropertyTypeDescription
namestringUnique rule label
condition_id?intCraft condition set ID (null = no conditions, always matches)
methodsarrayShipping method handles this rule applies to
pricefloatOverride price; 0.0 for free shipping
enabledboolWhether the rule is active

Enabling the fees module

Shipping fees are an optional feature. They must be activated in the plugin settings before the Fees tab appears in the CP and before FeeService::getEnabled() returns true:

yStore → Settings → Sales → Enable shipping fees

Setting key: salesShippingFeesEnabled (boolean).