Shipping Plugins
This guide explains how to build a custom CartThrob shipping plugin using the CartThrob 9.x plugin API. It assumes basic PHP knowledge: classes, methods, arrays, and returning values.
If you need ExpressionEngine add-on basics first, see the ExpressionEngine Docs and the Add-on Development Overview.
CartThrob ships with several working shipping plugins. Start by copying one of those and changing it, rather than writing everything from scratch.
Good starter examples:
Cartthrob_shipping_single_flat_rate.phpfor a simple fixed rateCartthrob_shipping_flat_rates.phpfor customer-selectable ratesCartthrob_shipping_by_weight_global_rate.phpfor a calculation based on cart weight
Start Here
A shipping plugin is a PHP class that:
- Lives in CartThrob’s shipping plugins folder
- Extends
CartThrob\Plugins\Shipping\ShippingPlugin - Declares a
$title - Implements
get_shipping()and returns aMoneyobject
At a minimum, CartThrob needs that get_shipping() method so it can calculate shipping for the cart.
If customers should choose among multiple shipping methods, also:
- Implement
CartThrob\Plugins\Shipping\OptionsInterface - Add
plugin_shipping_options() - Optionally add
default_shipping_option()
File Location and Naming
Store shipping plugins here:
system/user/addons/cartthrob/cartthrob/plugins/shipping/
Rules:
- The filename and classname must match.
- The classname must begin with
Cartthrob_shipping_. - The file must begin with
Cartthrob_shipping_. - Do not start your own PHP session. CartThrob already manages session and cart state.
Example:
Filename: Cartthrob_shipping_my_rate.php
Class: Cartthrob_shipping_my_rate
Minimum Working Plugin
This is the smallest useful shipping plugin: one fixed rate from a settings field.
<?php
use CartThrob\Dependency\Money\Money;
use CartThrob\Plugins\Shipping\ShippingPlugin;
if (!defined('CARTTHROB_PATH')) {
Cartthrob_core::core_error('No direct script access allowed');
}
class Cartthrob_shipping_my_rate extends ShippingPlugin
{
public $title = 'My Flat Rate';
public $settings = [
[
'name' => 'Rate',
'short_name' => 'rate',
'type' => 'text',
'default' => '10.00',
],
];
protected array $rules = [
'rate' => 'required|numeric',
];
public function get_shipping(): Money
{
return ee('cartthrob:MoneyService')->toMoney(
$this->plugin_settings('rate')
);
}
}
What this does:
- Shows a Rate field in CartThrob shipping settings
- Reads that setting with
plugin_settings('rate') - Converts the number into a CartThrob
Moneyobject - Returns that as the cart shipping cost
After adding the file, select the plugin in CartThrob’s shipping settings and save.
Plugin Information
These public properties describe the plugin in the control panel.
public $title = 'My Flat Rate';
public $note = 'Optional note shown with the plugin settings.';
public $overview = 'Optional longer overview text.';
$titleis required.$noteand$overvieware optional.- You can use plain text, or a language key that CartThrob will look up.
Language Files
Language keys keep settings labels cleaner and make text easier to reuse.
Built-in CartThrob shipping plugins store many labels in:
system/user/addons/cartthrob/language/english/cartthrob_lang.php
You can also place a plugin-specific language file next to the plugin, named like:
cartthrob_shipping_my_rate_lang.php
Example language entries:
<?php
$lang = [
'my_flat_rate_title' => 'My Flat Rate',
'my_flat_rate_note' => 'Charges one fixed shipping amount for the whole order.',
'rate' => 'Shipping Rate',
'rate_example' => 'Example: 10.00',
];
Then reference those keys in the plugin:
public $title = 'my_flat_rate_title';
public $note = 'my_flat_rate_note';
public $settings = [
[
'name' => 'rate',
'note' => 'rate_example',
'short_name' => 'rate',
'type' => 'text',
],
];
If CartThrob finds the language key, it shows the translated text. If not, it shows the key itself.
Plugin Settings
Each plugin can define its own settings array. Every setting needs at least:
nametypeshort_name
Optional keys:
defaultoptionsnotesettingsfor matrix rows
Setting Keys
name
Required. Label shown in the control panel. Use plain text or a language key.
type
Required. Supported types:
texttextarearadioselectcheckboxmatrixheader
text, textarea, and header store string-like values. The others usually store arrays or selected option values.
short_name
Required. The key you use in PHP with plugin_settings('short_name').
Use letters, numbers, underscores, and hyphens only.
default
Optional. Default value for the setting.
Matrix fields can have defaults on individual columns. You generally cannot preset a full matrix row with one top-level default.
options
Usually required for radio, select, and checkbox.
'options' => [
'yes' => 'Yes',
'no' => 'No',
],
The array key is the stored value. The array value is the label shown to the admin.
note
Optional help text shown with the setting.
settings
Required for matrix settings. Contains the column definitions for each matrix row.
CartThrob shipping plugins do not support a matrix inside another matrix.
Settings Example
public $settings = [
[
'name' => 'Mode',
'short_name' => 'mode',
'type' => 'radio',
'default' => 'flat',
'options' => [
'flat' => 'Flat amount',
'per_item' => 'Per item',
],
],
[
'name' => 'Rate',
'short_name' => 'rate',
'type' => 'text',
'default' => '5.00',
'note' => 'Enter the shipping amount.',
],
[
'name' => 'Thresholds',
'short_name' => 'thresholds',
'type' => 'matrix',
'settings' => [
[
'name' => 'Threshold',
'short_name' => 'threshold',
'type' => 'text',
],
[
'name' => 'Rate',
'short_name' => 'rate',
'type' => 'text',
],
],
],
];
Read settings in your plugin like this:
$mode = $this->plugin_settings('mode');
$rate = $this->plugin_settings('rate');
$thresholds = $this->plugin_settings('thresholds', []);
Required and Optional Methods
get_shipping()
Required for every shipping plugin.
Parameters: none
Returns: Money
This is the method CartThrob calls when calculating cart shipping.
public function get_shipping(): Money
{
$amount = $this->plugin_settings('rate');
return ee('cartthrob:MoneyService')->toMoney($amount);
}
Important:
- Do not return a raw float.
- Convert numbers with
ee('cartthrob:MoneyService')->toMoney(...). - Return zero shipping with
ee('cartthrob:MoneyService')->fresh().
Example of returning no shipping charge:
if ($this->core->cart->count() <= 0) {
return ee('cartthrob:MoneyService')->fresh();
}
initialize()
Optional.
Use this when you want to prepare plugin-wide values once, such as building rate arrays from settings.
Parameters: $params = [], $defaults = []
Returns: usually $this or void
public function initialize($params = [], $defaults = [])
{
foreach ($this->plugin_settings('rates', []) as $rate) {
$this->rates[$rate['short_name']] = $rate['rate'];
}
return $this;
}
plugin_shipping_options()
Optional, but needed when customers choose among shipping methods.
Parameters: none
Returns: array of option arrays
Each option should include:
rate_short_namerate_titlepricerate_price
public function plugin_shipping_options()
{
$options = [];
foreach ($this->rates as $rate_short_name => $price) {
$options[] = [
'rate_short_name' => $rate_short_name,
'price' => $price,
'rate_price' => $price,
'rate_title' => $this->rate_titles[$rate_short_name],
];
}
return $options;
}
These options are used by {exp:cartthrob:get_shipping_options}.
default_shipping_option()
Optional companion to plugin_shipping_options().
Parameters: none
Returns: string short name of the default option
public function default_shipping_option()
{
return $this->default_shipping_option;
}
Useful Cart Helpers
These are the helpers you will use most often inside get_shipping().
Cart totals and weight
$this->core->cart->shippable_weight();
$this->core->cart->shippable_subtotal();
$this->core->cart->count();
shippable_weight(): total weight of shippable itemsshippable_subtotal(): cost of shippable items before shipping and taxcount(): number of items in the cart
Selected shipping option and customer data
$this->core->cart->shipping_info('shipping_option');
$this->core->cart->customer_info();
$this->core->cart->customer_info('country_code');
$this->core->cart->customer_info('shipping_zip');
Plugin settings
$this->plugin_settings('rate');
$this->plugin_settings('thresholds', []);
The second argument is the fallback if the setting is missing.
Cart items
foreach ($this->core->cart->items() as $item) {
$item->quantity();
$item->price();
$item->weight();
$item->product_id();
}
Built-in shipping helpers
The base shipping plugin also provides:
$this->shipping_data('zip');
$this->get_thresholds();
$this->threshold($number, $thresholds);
$this->cart_hash($shipping);
shipping_data()looks up shipping destination data, falling back through shipping fields, billing fields, settings, custom data, and defaultsget_thresholds()turns a thresholds matrix into[threshold => rate]threshold()finds the matching rate for a numbercart_hash()is useful when caching live rate responses against the current cart contents
Customer Selectable Rates
If the customer should pick a shipping method, do all of the following:
implements OptionsInterface- Build the available rates in
initialize()orget_shipping() - Return those rates from
plugin_shipping_options() - Read the selected option from:
$this->core->cart->shipping_info('shipping_option');
The included Flat Rates plugin is the best reference:
Cartthrob_shipping_flat_rates.php
In templates, output the choices with:
{exp:cartthrob:get_shipping_options}
That tag builds a dropdown named shipping_option, or parses your custom tagdata for each option.
Offsite Rate Helpers
For live carrier quotes, CartThrob still provides a shipping plugins library:
ee()->load->library('cartthrob_shipping_plugins');
Useful methods:
curl_transaction()
Sends a request to an external shipping API.
$result = ee()->cartthrob_shipping_plugins->curl_transaction($url, $data);
customer_location_defaults()
Reads a customer location field and optionally supplies a fallback.
$city = ee()->cartthrob_shipping_plugins->customer_location_defaults('city', 'Springfield');
Country code conversion
For 2-character country codes, use the countries helper:
$country = alpha2_country_code('USA'); // US
Validation Rules
Many current shipping plugins validate settings with a $rules array:
protected array $rules = [
'rate' => 'required|numeric',
];
This helps catch empty or non-numeric admin settings before they cause bad shipping calculations.
Examples
Example 1: Fixed rate
Same pattern as Cartthrob_shipping_single_flat_rate.php.
<?php
use CartThrob\Dependency\Money\Money;
use CartThrob\Plugins\Shipping\ShippingPlugin;
if (!defined('CARTTHROB_PATH')) {
Cartthrob_core::core_error('No direct script access allowed');
}
class Cartthrob_shipping_single_flat_rate extends ShippingPlugin
{
public $title = 'single_flat_rate';
public $settings = [
[
'name' => 'rate',
'short_name' => 'rate',
'type' => 'text',
],
];
protected array $rules = [
'rate' => 'required|numeric',
];
public function get_shipping(): Money
{
return ee('cartthrob:MoneyService')->toMoney(
$this->plugin_settings('rate')
);
}
}
Example 2: Rate based on cart weight
Same pattern as Cartthrob_shipping_by_weight_global_rate.php.
public function get_shipping(): Money
{
$shipping = $this->core->cart->shippable_weight()
* $this->plugin_settings('rate');
return ee('cartthrob:MoneyService')->toMoney($shipping);
}
Example 3: Customer selectable rates
Use Flat Rates as your model when you need multiple named options, a default option, free-shipping thresholds, and {exp:cartthrob:get_shipping_options} support.
Key pieces from that plugin:
use CartThrob\Plugins\Shipping\OptionsInterface;
use CartThrob\Plugins\Shipping\ShippingPlugin;
class Cartthrob_shipping_flat_rates extends ShippingPlugin implements OptionsInterface
{
public function initialize($params = [], $defaults = [])
{
// Build $this->rates and $this->default_shipping_option from settings
}
public function get_shipping(): Money
{
// Look up shipping_info('shipping_option') and return that rate as Money
}
public function default_shipping_option()
{
return $this->default_shipping_option;
}
public function plugin_shipping_options()
{
// Return rate_short_name / rate_title / price / rate_price rows
}
}
Checklist
Before testing your plugin:
- File is in
system/user/addons/cartthrob/cartthrob/plugins/shipping/ - Filename and classname match
- Classname starts with
Cartthrob_shipping_ - Class extends
CartThrob\Plugins\Shipping\ShippingPlugin get_shipping()exists and returnsMoney- Settings use valid
name,type, andshort_namekeys - If customers choose rates, the class implements
OptionsInterfaceand providesplugin_shipping_options() - Plugin is selected and saved in CartThrob shipping settings
- Test with an empty cart, a normal cart, and any free-shipping or threshold conditions you support
If something does not appear in the control panel, check the classname prefix and file location first. If shipping calculates as zero or errors, check that get_shipping() returns a Money object instead of a plain number.