Fees, Zones & Rates
Shipping costs in yStore are built from three components:
- Shipping Zones — postal-code-based geographic areas that group destinations
- Table Rates — weight-based cost rows matched to a zone group
- 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
| Field | Required | Description |
|---|---|---|
| Name | Yes | Unique internal identifier (e.g., "SK — Bratislava") |
| Country code | Yes | ISO 3166-1 alpha-2 (e.g., SK, CZ, HU) |
| Group | No | Groups related zones together so one table rate can cover multiple ZIP entries |
| ZIP | Yes (exact mode) | Single postal code |
| ZIP from / ZIP to | Yes (range mode) | Inclusive postal code range |
| Range mode | — | Toggle between exact ZIP and ZIP range |
| Enabled | — | Disabled zones are excluded from rate matching |
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
- Go to
yStore → Shipping → Zones. - Click New Zone.
- Enter a name and the country code.
- Optionally assign a group (select an existing one, or type a new group name).
- Enable range mode if you want to cover a ZIP range; otherwise enter a single ZIP.
- 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:
| Mode | When to use |
|---|---|
| Exact weight | Applies when the cart weight equals a specific value; use * to match any weight |
| Weight range | Applies when cart weight falls between weight_from and weight_to |
| Self rate | Inherits the weight definition from another table rate — reuse weight brackets across multiple prices without duplicating them |
Table rate fields
| Field | Required | Description |
|---|---|---|
| Name | Yes | Internal label |
| Zone group | No | Zone group this rate applies to; leave as NOT IN GROUP to apply outside any group |
| Price | Yes | Shipping cost in the store currency |
| Weight | Yes (exact mode) | Weight value in kg; * matches any weight |
| Weight from / Weight to | Yes (range mode) | Weight range in kg |
| Reference rate | Yes (self rate mode) | Another table rate whose weight definition is inherited |
| Excluded shipping methods | No | Methods that will not use this rate |
| Default | — | Fallback when no other rate matches; only one rate can be the default |
| Enabled | — | Disabled rates are ignored at checkout |
Creating a table rate
- Go to
yStore → Shipping → Table Rates. - Click New Table Rate.
- Enter a name and select the zone group.
- Enter the price.
- Choose a weight mode and fill in the weight value(s).
- Optionally exclude shipping methods that should not use this rate.
- Save.
Managing table rates
| Action | How |
|---|---|
| Reorder | Drag rows to change evaluation order |
| Set default | Mark one rate as the fallback for unmatched carts (clears the previous default automatically) |
| Duplicate | Copy a rate — the copy gets a (Copy) name suffix |
| Bulk enable/disable | Select rows and use the status toggle |
| Delete | Select rows and click Delete |
Example: tiered domestic rates (Slovakia)
| Name | Zone group | Mode | Weight | Price |
|---|---|---|---|---|
| SK 0–2 kg | SK-All | Weight range | 0 – 2 kg | €3.50 |
| SK 2–5 kg | SK-All | Weight range | 2 – 5 kg | €4.90 |
| SK 5–20 kg | SK-All | Weight range | 5 – 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:
| Name | Zone group | Mode | References | Price |
|---|---|---|---|---|
| SK 0–2 kg | SK-All | Weight range | — | €3.50 |
| SK Bratislava 0–2 kg | SK-Bratislava | Self rate | SK 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
| Field | Required | Description |
|---|---|---|
| Name | Yes | Unique internal name |
| Label | No | Display name shown to the customer at checkout |
| Code | No | Unique machine identifier; auto-generated from the name if left blank |
| Instructions | No | Optional text displayed alongside the fee at checkout |
| Fee adjustment | Yes | append — added to the rate price; override — replaces the rate price |
| Zone data | No | Per-zone pricing rows (see below) |
| Required | — | Always applied, cannot be removed by the customer |
| Enabled | — | Disabled fees are not applied |
Fee adjustment modes
| Mode | Effect |
|---|---|
append | Fee amount is added on top of the matched table rate |
override | Fee 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:
| Key | Description |
|---|---|
label | Human-readable zone name |
code | Machine identifier; auto-generated from label if omitted |
value | Fee amount for this zone |
free | If 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
- Go to
yStore → Shipping → Fees. - Click New Fee.
- Enter a name; the code is generated automatically.
- Choose the fee adjustment mode (
appendoroverride). - Optionally add zone data rows for per-zone pricing.
- Save.
- 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
| Field | Required | Description |
|---|---|---|
| Name | Yes | Unique internal label |
| Shipping methods | Yes | One or more methods this rule applies to |
| Price | Yes | Override price charged when conditions pass; use 0 for free shipping |
| Conditions | No | Craft native conditions — all must pass for the rule to activate |
| Enabled | — | Disabled rules are never evaluated |
Adding a rule
- Go to
yStore → Shipping → Rules. - Click New Rule.
- Select the Shipping methods this rule should affect.
- Enter the override Price (
0for free shipping). - Add conditions using Craft's condition builder.
- Save.
When a rule's conditions pass, its price replaces the method's calculated shipping price for the affected methods.
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
- Create zones — one record per country + ZIP (or ZIP range); assign a group label to zones you want to bundle.
- Create table rates — one row per weight bracket per zone group; use self rates to reuse weight brackets across multiple groups.
- Set a default rate — ensures a price always appears even when no zone group matches.
- Create fees if needed — configure adjustment mode and optional per-zone data; enable
salesShippingFeesEnabledin plugin settings. - Link fees to methods — attach fees in
yStore → Shipping → Methods → [Method] → Fees. - Create rules for condition-based price overrides — e.g., free shipping for specific customer segments.
- 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:
- Zone matching — the customer's
country_codeand postal code are compared against all enabled zones. Range zones match whenzip_from ≤ customer_zip ≤ zip_to; exact zones match whenzip === customer_zip. - Table rate selection — all enabled table rates whose
zone_groupequals the matched zone's group are candidates. The candidate whose weight bracket covers the cart's total weight is selected. A rate withweight = '*'(stored as the literal string*in the database;getFormattedWeight()resolves it to0.0for 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 flaggedis_defaultis used directly. - Fee application — enabled fees attached to the chosen shipping method are applied in order:
appendadds to the rate price;overridereplaces it. - 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:
| Property | Type | Description |
|---|---|---|
name | string | Unique rule label |
condition_id | ?int | Craft condition set ID (null = no conditions, always matches) |
methods | array | Shipping method handles this rule applies to |
price | float | Override price; 0.0 for free shipping |
enabled | bool | Whether 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).