CartThrob 9 documentation for ExpressionEngine commerce teams Visit CartThrob.com
CartThrob.com

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.php for a simple fixed rate
  • Cartthrob_shipping_flat_rates.php for customer-selectable rates
  • Cartthrob_shipping_by_weight_global_rate.php for a calculation based on cart weight

Start Here

A shipping plugin is a PHP class that:

  1. Lives in CartThrob’s shipping plugins folder
  2. Extends CartThrob\Plugins\Shipping\ShippingPlugin
  3. Declares a $title
  4. Implements get_shipping() and returns a Money object

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:

  1. Implement CartThrob\Plugins\Shipping\OptionsInterface
  2. Add plugin_shipping_options()
  3. Optionally add default_shipping_option()

File Location and Naming

Store shipping plugins here:

system/user/addons/cartthrob/cartthrob/plugins/shipping/

Rules:

  1. The filename and classname must match.
  2. The classname must begin with Cartthrob_shipping_.
  3. The file must begin with Cartthrob_shipping_.
  4. 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 Money object
  • 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.';
  • $title is required.
  • $note and $overview are 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:

  • name
  • type
  • short_name

Optional keys:

  • default
  • options
  • note
  • settings for matrix rows

Setting Keys

name

Required. Label shown in the control panel. Use plain text or a language key.

type

Required. Supported types:

  1. text
  2. textarea
  3. radio
  4. select
  5. checkbox
  6. matrix
  7. header

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_name
  • rate_title
  • price
  • rate_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 items
  • shippable_subtotal(): cost of shippable items before shipping and tax
  • count(): 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 defaults
  • get_thresholds() turns a thresholds matrix into [threshold => rate]
  • threshold() finds the matching rate for a number
  • cart_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:

  1. implements OptionsInterface
  2. Build the available rates in initialize() or get_shipping()
  3. Return those rates from plugin_shipping_options()
  4. 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:

  1. File is in system/user/addons/cartthrob/cartthrob/plugins/shipping/
  2. Filename and classname match
  3. Classname starts with Cartthrob_shipping_
  4. Class extends CartThrob\Plugins\Shipping\ShippingPlugin
  5. get_shipping() exists and returns Money
  6. Settings use valid name, type, and short_name keys
  7. If customers choose rates, the class implements OptionsInterface and provides plugin_shipping_options()
  8. Plugin is selected and saved in CartThrob shipping settings
  9. 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.

ExpressionEngine References