shipping-method-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Shipping Method Development

配送方式开发

Overview

概述

Creating custom shipping methods in Bagisto allows you to tailor delivery options to meet your specific business needs. Whether you need special handling for fragile items, express delivery options, or region-specific shipping rules, custom shipping methods provide the flexibility your e-commerce store requires.
For our tutorial, we'll create a Custom Express Shipping method that demonstrates all the essential concepts you need to build any type of shipping solution.
在Bagisto中创建自定义配送方式,可让您量身定制配送选项以满足特定业务需求。无论您需要对易碎物品进行特殊处理、提供加急配送选项,还是设置特定地区的配送规则,自定义配送方式都能为您的电商店铺提供所需的灵活性。
在本教程中,我们将创建一个自定义加急配送方式,演示构建任何类型配送解决方案所需的所有核心概念。

When to Apply

适用场景

Activate this skill when:
  • Creating new shipping methods
  • Integrating shipping carriers (FedEx, UPS, DHL, USPS, etc.)
  • Adding shipping options to checkout
  • Modifying existing shipping configurations
  • Creating admin configuration for shipping methods
  • Implementing rate calculation logic
在以下场景中启用本技能:
  • 创建新的配送方式
  • 集成配送承运商(FedEx、UPS、DHL、USPS等)
  • 在结账环节添加配送选项
  • 修改现有配送配置
  • 为配送方式创建后台管理配置
  • 实现费率计算逻辑

Bagisto Shipping Architecture

Bagisto配送架构

Bagisto's shipping system is built around a flexible carrier-based architecture that separates configuration from business logic.
Bagisto的配送系统基于灵活的承运商架构构建,将配置与业务逻辑分离。

Core Components

核心组件

ComponentPurposeLocation
Carriers ConfigurationDefines shipping method properties
Config/carriers.php
Carrier ClassesContains rate calculation logic
Carriers/ClassName.php
System ConfigurationAdmin interface forms
Config/system.php
Service ProviderRegisters shipping method
Providers/ServiceProvider.php
Shipping FacadeCollects and manages rates
Webkul\Shipping\Shipping
组件用途位置
承运商配置定义配送方式属性
Config/carriers.php
承运商类包含费率计算逻辑
Carriers/ClassName.php
系统配置后台管理界面表单
Config/system.php
服务提供者注册配送方式
Providers/ServiceProvider.php
Shipping Facade收集和管理费率
Webkul\Shipping\Shipping

Key Features

关键特性

  • Flexible Rate Calculation: Support for per-unit, per-order, weight-based, or custom pricing.
  • Configuration Management: Admin-friendly settings interface.
  • Multi-channel Support: Different rates and settings per sales channel.
  • Localization Ready: Full translation support.
  • Extensible Architecture: Easy integration with third-party APIs.
  • 灵活的费率计算:支持按单位、按订单、按重量或自定义定价。
  • 配置管理:便于后台操作的设置界面。
  • 多渠道支持:每个销售渠道可设置不同的费率和配置。
  • 本地化就绪:完整的翻译支持。
  • 可扩展架构:轻松集成第三方API。

Step-by-Step Guide

分步指南

Step 1: Create Package Directory Structure

步骤1:创建包目录结构

bash
mkdir -p packages/Webkul/CustomExpressShipping/src/{Carriers,Config,Providers}
bash
mkdir -p packages/Webkul/CustomExpressShipping/src/{Carriers,Config,Providers}

Step 2: Create Carrier Configuration

步骤2:创建承运商配置

File:
packages/Webkul/CustomExpressShipping/src/Config/carriers.php
php
<?php

return [
    'custom_express_shipping' => [
        'code'         => 'custom_express_shipping',
        'title'        => 'Express Delivery (1-2 Days)',
        'description'  => 'Premium express shipping with tracking',
        'active'       => true,
        'default_rate' => '19.99',
        'type'         => 'per_order',
        'class'        => 'Webkul\CustomExpressShipping\Carriers\CustomExpressShipping',
    ],
];
文件位置:
packages/Webkul/CustomExpressShipping/src/Config/carriers.php
php
<?php

return [
    'custom_express_shipping' => [
        'code'         => 'custom_express_shipping',
        'title'        => 'Express Delivery (1-2 Days)',
        'description'  => 'Premium express shipping with tracking',
        'active'       => true,
        'default_rate' => '19.99',
        'type'         => 'per_order',
        'class'        => 'Webkul\CustomExpressShipping\Carriers\CustomExpressShipping',
    ],
];

Configuration Properties Explained

配置属性说明

PropertyTypePurposeDescription
code
StringUnique identifierMust match the array key and
$code
property in carrier class.
title
StringDefault display nameShown to customers during checkout (can be overridden in admin).
description
StringMethod descriptionBrief explanation of the shipping service.
active
BooleanDefault statusWhether the shipping method is enabled by default.
default_rate
String/FloatBase shipping costBase shipping cost before calculations.
type
StringPricing model
per_order
(flat rate) or
per_unit
(per item).
class
StringCarrier class namespaceFull path to your carrier class.
Note: The array key (
custom_express_shipping
) must match the
code
property in your carrier class, system configuration key path, and should be consistent throughout.
属性类型用途描述
code
字符串唯一标识符必须与数组键以及承运商类中的
$code
属性匹配。
title
字符串默认显示名称在结账环节向客户展示(可在后台管理中修改)。
description
字符串方式描述配送服务的简要说明。
active
布尔值默认状态配送方式是否默认启用。
default_rate
字符串/浮点数基础配送成本计算前的基础配送费用。
type
字符串定价模式
per_order
(固定费率)或
per_unit
(按件计费)。
class
字符串承运商类命名空间承运商类的完整路径。
注意: 数组键(
custom_express_shipping
)必须与承运商类中的
$code
属性、系统配置键路径保持一致,并在整个开发过程中统一。

Step 3: Create Carrier Class

步骤3:创建承运商类

File:
packages/Webkul/CustomExpressShipping/src/Carriers/CustomExpressShipping.php
php
<?php

namespace Webkul\CustomExpressShipping\Carriers;

use Webkul\Shipping\Carriers\AbstractShipping;
use Webkul\Checkout\Models\CartShippingRate;
use Webkul\Checkout\Facades\Cart;

class CustomExpressShipping extends AbstractShipping
{
    /**
     * Shipping method code - must match carriers.php key.
     *
     * @var string
     */
    protected $code = 'custom_express_shipping';

    /**
     * Shipping method code.
     *
     * @var string
     */
    protected $method = 'custom_express_shipping_custom_express_shipping';

    /**
     * Calculate shipping rate for the current cart.
     *
     * @return \Webkul\Checkout\Models\CartShippingRate|false
     */
    public function calculate()
    {
        if (! $this->isAvailable()) {
            return false;
        }

        return $this->getRate();
    }

    /**
     * Get shipping rate.
     *
     * @return \Webkul\Checkout\Models\CartShippingRate
     */
    public function getRate(): CartShippingRate
    {
        $cart = Cart::getCart();

        $cartShippingRate = new CartShippingRate;

        $cartShippingRate->carrier = $this->getCode();
        $cartShippingRate->carrier_title = $this->getConfigData('title');
        $cartShippingRate->method = $this->getMethod();
        $cartShippingRate->method_title = $this->getConfigData('title');
        $cartShippingRate->method_description = $this->getConfigData('description');
        $cartShippingRate->price = 0;
        $cartShippingRate->base_price = 0;

        $baseRate = (float) $this->getConfigData('default_rate');

        if ($this->getConfigData('type') == 'per_unit') {
            foreach ($cart->items as $item) {
                if ($item->getTypeInstance()->isStockable()) {
                    $cartShippingRate->price += core()->convertPrice($baseRate) * $item->quantity;
                    $cartShippingRate->base_price += $baseRate * $item->quantity;
                }
            }
        } else {
            $cartShippingRate->price = core()->convertPrice($baseRate);
            $cartShippingRate->base_price = $baseRate;
        }

        return $cartShippingRate;
    }
}
文件位置:
packages/Webkul/CustomExpressShipping/src/Carriers/CustomExpressShipping.php
php
<?php

namespace Webkul\CustomExpressShipping\Carriers;

use Webkul\Shipping\Carriers\AbstractShipping;
use Webkul\Checkout\Models\CartShippingRate;
use Webkul\Checkout\Facades\Cart;

class CustomExpressShipping extends AbstractShipping
{
    /**
     * Shipping method code - must match carriers.php key.
     *
     * @var string
     */
    protected $code = 'custom_express_shipping';

    /**
     * Shipping method code.
     *
     * @var string
     */
    protected $method = 'custom_express_shipping_custom_express_shipping';

    /**
     * Calculate shipping rate for the current cart.
     *
     * @return \Webkul\Checkout\Models\CartShippingRate|false
     */
    public function calculate()
    {
        if (! $this->isAvailable()) {
            return false;
        }

        return $this->getRate();
    }

    /**
     * Get shipping rate.
     *
     * @return \Webkul\Checkout\Models\CartShippingRate
     */
    public function getRate(): CartShippingRate
    {
        $cart = Cart::getCart();

        $cartShippingRate = new CartShippingRate;

        $cartShippingRate->carrier = $this->getCode();
        $cartShippingRate->carrier_title = $this->getConfigData('title');
        $cartShippingRate->method = $this->getMethod();
        $cartShippingRate->method_title = $this->getConfigData('title');
        $cartShippingRate->method_description = $this->getConfigData('description');
        $cartShippingRate->price = 0;
        $cartShippingRate->base_price = 0;

        $baseRate = (float) $this->getConfigData('default_rate');

        if ($this->getConfigData('type') == 'per_unit') {
            foreach ($cart->items as $item) {
                if ($item->getTypeInstance()->isStockable()) {
                    $cartShippingRate->price += core()->convertPrice($baseRate) * $item->quantity;
                    $cartShippingRate->base_price += $baseRate * $item->quantity;
                }
            }
        } else {
            $cartShippingRate->price = core()->convertPrice($baseRate);
            $cartShippingRate->base_price = $baseRate;
        }

        return $cartShippingRate;
    }
}

Step 4: Create System Configuration

步骤4:创建系统配置

File:
packages/Webkul/CustomExpressShipping/src/Config/system.php
php
<?php

return [
    [
        'key'    => 'sales.carriers.custom_express_shipping',
        'name'   => 'Custom Express Shipping',
        'info'   => 'Configure the Custom Express Shipping method settings.',
        'sort'   => 1,
        'fields' => [
            [
                'name'          => 'title',
                'title'         => 'Method Title',
                'type'          => 'text',
                'validation'    => 'required',
                'channel_based' => true,
                'locale_based'  => true,
            ],
            [
                'name'          => 'description',
                'title'         => 'Description',
                'type'          => 'textarea',
                'channel_based' => true,
                'locale_based'  => false,
            ],
            [
                'name'          => 'default_rate',
                'title'         => 'Base Rate',
                'type'          => 'text',
                'validation'    => 'required|numeric|min:0',
                'channel_based' => true,
                'locale_based'  => false,
            ],
            [
                'name'          => 'type',
                'title'         => 'Pricing Type',
                'type'          => 'select',
                'options'       => [
                    [
                        'title' => 'Per Order (Flat Rate)',
                        'value' => 'per_order',
                    ],
                    [
                        'title' => 'Per Item',
                        'value' => 'per_unit',
                    ],
                ],
                'channel_based' => true,
                'locale_based'  => false,
            ],
            [
                'name'          => 'active',
                'title'         => 'Enabled',
                'type'          => 'boolean',
                'validation'    => 'required',
                'channel_based' => true,
                'locale_based'  => false,
            ],
        ],
    ],
];
文件位置:
packages/Webkul/CustomExpressShipping/src/Config/system.php
php
<?php

return [
    [
        'key'    => 'sales.carriers.custom_express_shipping',
        'name'   => 'Custom Express Shipping',
        'info'   => 'Configure the Custom Express Shipping method settings.',
        'sort'   => 1,
        'fields' => [
            [
                'name'          => 'title',
                'title'         => 'Method Title',
                'type'          => 'text',
                'validation'    => 'required',
                'channel_based' => true,
                'locale_based'  => true,
            ],
            [
                'name'          => 'description',
                'title'         => 'Description',
                'type'          => 'textarea',
                'channel_based' => true,
                'locale_based'  => false,
            ],
            [
                'name'          => 'default_rate',
                'title'         => 'Base Rate',
                'type'          => 'text',
                'validation'    => 'required|numeric|min:0',
                'channel_based' => true,
                'locale_based'  => false,
            ],
            [
                'name'          => 'type',
                'title'         => 'Pricing Type',
                'type'          => 'select',
                'options'       => [
                    [
                        'title' => 'Per Order (Flat Rate)',
                        'value' => 'per_order',
                    ],
                    [
                        'title' => 'Per Item',
                        'value' => 'per_unit',
                    ],
                ],
                'channel_based' => true,
                'locale_based'  => false,
            ],
            [
                'name'          => 'active',
                'title'         => 'Enabled',
                'type'          => 'boolean',
                'validation'    => 'required',
                'channel_based' => true,
                'locale_based'  => false,
            ],
        ],
    ],
];

System Configuration Field Properties

系统配置字段属性

PropertyPurposeDescription
name
Field identifierUsed to store and retrieve configuration values.
title
Field labelLabel displayed in the admin form.
type
Input type
text
,
textarea
,
boolean
,
select
,
password
, etc.
default_value
Default settingInitial value when first configured.
channel_based
Multi-store supportDifferent values per sales channel.
locale_based
Multi-language supportTranslatable content per language.
validation
Field validationRules like
required
,
numeric
,
email
.
属性用途描述
name
字段标识符用于存储和检索配置值。
title
字段标签在后台表单中显示的标签。
type
输入类型
text
textarea
boolean
select
password
等。
default_value
默认设置首次配置时的初始值。
channel_based
多店铺支持每个销售渠道可设置不同的值。
locale_based
多语言支持每种语言可设置不同的可翻译内容。
validation
字段验证验证规则,如
required
numeric
email

Step 5: Create Service Provider

步骤5:创建服务提供者

File:
packages/Webkul/CustomExpressShipping/src/Providers/CustomExpressShippingServiceProvider.php
php
<?php

namespace Webkul\CustomExpressShipping\Providers;

use Illuminate\Support\ServiceProvider;

class CustomExpressShippingServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register(): void
    {
        $this->mergeConfigFrom(
            dirname(__DIR__) . '/Config/carriers.php',
            'carriers'
        );

        $this->mergeConfigFrom(
            dirname(__DIR__) . '/Config/system.php',
            'core'
        );
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot(): void
    {
        //
    }
}
文件位置:
packages/Webkul/CustomExpressShipping/src/Providers/CustomExpressShippingServiceProvider.php
php
<?php

namespace Webkul\CustomExpressShipping\Providers;

use Illuminate\Support\ServiceProvider;

class CustomExpressShippingServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register(): void
    {
        $this->mergeConfigFrom(
            dirname(__DIR__) . '/Config/carriers.php',
            'carriers'
        );

        $this->mergeConfigFrom(
            dirname(__DIR__) . '/Config/system.php',
            'core'
        );
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot(): void
    {
        //
    }
}

Step 6: Register Your Package

步骤6:注册您的包

  1. Add to composer.json (in Bagisto root directory):
json
{
    "autoload": {
        "psr-4": {
            "Webkul\\CustomExpressShipping\\": "packages/Webkul/CustomExpressShipping/src"
        }
    }
}
  1. Update autoloader:
bash
composer dump-autoload
  1. Register service provider in
    bootstrap/providers.php
    :
php
<?php

return [
    App\Providers\AppServiceProvider::class,

    // ... other providers ...

    Webkul\CustomExpressShipping\Providers\CustomExpressShippingServiceProvider::class,
];
  1. Clear caches:
bash
php artisan optimize:clear
  1. 添加到composer.json(Bagisto根目录下):
json
{
    "autoload": {
        "psr-4": {
            "Webkul\\CustomExpressShipping\\": "packages/Webkul/CustomExpressShipping/src"
        }
    }
}
  1. 更新自动加载器:
bash
composer dump-autoload
  1. bootstrap/providers.php
    中注册服务提供者:
php
<?php

return [
    App\Providers\AppServiceProvider::class,

    // ... 其他服务提供者 ...

    Webkul\CustomExpressShipping\Providers\CustomExpressShippingServiceProvider::class,
];
  1. 清除缓存:
bash
php artisan optimize:clear

Base Carrier Class Reference

基础承运商类参考

Location:
packages/Webkul/Shipping/src/Carriers/AbstractShipping.php
All shipping methods extend
Webkul\Shipping\Carriers\AbstractShipping
:
php
<?php

namespace Webkul\Shipping\Carriers;

use Webkul\Shipping\Exceptions\CarrierCodeException;

abstract class AbstractShipping
{
    /**
     * Shipping method carrier code.
     *
     * @var string
     */
    protected $code;

    /**
     * Shipping method code.
     *
     * @var string
     */
    protected $method;

    abstract public function calculate();

    /**
     * Checks if shipping method is available.
     *
     * @return array
     */
    public function isAvailable()
    {
        return $this->getConfigData('active');
    }

    /**
     * Returns shipping method carrier code.
     *
     * @return string
     */
    public function getCode()
    {
        if (empty($this->code)) {
            throw new CarrierCodeException('Carrier code should be initialized.');
        }

        return $this->code;
    }

    /**
     * Return shipping method code.
     *
     * @return string
     */
    public function getMethod()
    {
        if (empty($this->method)) {
            $code = $this->getCode();

            return $code . '_' . $code;
        }

        return $this->method;
    }

    /**
     * Returns shipping method title.
     *
     * @return array
     */
    public function getTitle()
    {
        return $this->getConfigData('title');
    }

    /**
     * Returns shipping method description.
     *
     * @return array
     */
    public function getDescription()
    {
        return $this->getConfigData('description');
    }

    /**
     * Retrieve information from shipping configuration.
     *
     * @param  string  $field
     * @return mixed
     */
    public function getConfigData($field)
    {
        return core()->getConfigData('sales.carriers.' . $this->getCode() . '.' . $field);
    }
}
位置:
packages/Webkul/Shipping/src/Carriers/AbstractShipping.php
所有配送方式都继承自
Webkul\Shipping\Carriers\AbstractShipping
php
<?php

namespace Webkul\Shipping\Carriers;

use Webkul\Shipping\Exceptions\CarrierCodeException;

abstract class AbstractShipping
{
    /**
     * Shipping method carrier code.
     *
     * @var string
     */
    protected $code;

    /**
     * Shipping method code.
     *
     * @var string
     */
    protected $method;

    abstract public function calculate();

    /**
     * Checks if shipping method is available.
     *
     * @return array
     */
    public function isAvailable()
    {
        return $this->getConfigData('active');
    }

    /**
     * Returns shipping method carrier code.
     *
     * @return string
     */
    public function getCode()
    {
        if (empty($this->code)) {
            throw new CarrierCodeException('Carrier code should be initialized.');
        }

        return $this->code;
    }

    /**
     * Return shipping method code.
     *
     * @return string
     */
    public function getMethod()
    {
        if (empty($this->method)) {
            $code = $this->getCode();

            return $code . '_' . $code;
        }

        return $this->method;
    }

    /**
     * Returns shipping method title.
     *
     * @return array
     */
    public function getTitle()
    {
        return $this->getConfigData('title');
    }

    /**
     * Returns shipping method description.
     *
     * @return array
     */
    public function getDescription()
    {
        return $this->getConfigData('description');
    }

    /**
     * Retrieve information from shipping configuration.
     *
     * @param  string  $field
     * @return mixed
     */
    public function getConfigData($field)
    {
        return core()->getConfigData('sales.carriers.' . $this->getCode() . '.' . $field);
    }
}

CartShippingRate Model

CartShippingRate模型

Location:
packages/Webkul/Checkout/src/Models/CartShippingRate.php
php
<?php

namespace Webkul\Checkout\Models;

use Illuminate\Database\Eloquent\Model;
use Webkul\Checkout\Contracts\CartShippingRate as CartShippingRateContract;

class CartShippingRate extends Model implements CartShippingRateContract
{
    protected $fillable = [
        'carrier',
        'carrier_title',
        'method',
        'method_title',
        'method_description',
        'price',
        'base_price',
        'discount_amount',
        'base_discount_amount',
        'tax_percent',
        'tax_amount',
        'base_tax_amount',
        'price_incl_tax',
        'base_price_incl_tax',
        'applied_tax_rate',
    ];
}
位置:
packages/Webkul/Checkout/src/Models/CartShippingRate.php
php
<?php

namespace Webkul\Checkout\Models;

use Illuminate\Database\Eloquent\Model;
use Webkul\Checkout\Contracts\CartShippingRate as CartShippingRateContract;

class CartShippingRate extends Model implements CartShippingRateContract
{
    protected $fillable = [
        'carrier',
        'carrier_title',
        'method',
        'method_title',
        'method_description',
        'price',
        'base_price',
        'discount_amount',
        'base_discount_amount',
        'tax_percent',
        'tax_amount',
        'base_tax_amount',
        'price_incl_tax',
        'base_price_incl_tax',
        'applied_tax_rate',
    ];
}

Key Methods to Implement

需实现的关键方法

MethodPurposeRequired
calculate()
Calculate and return shipping rateYes (abstract)
getRate()
Build CartShippingRate objectNo (can be inline)
isAvailable()
Override for custom availabilityNo (uses default)
方法用途是否必填
calculate()
计算并返回配送费率是(抽象方法)
getRate()
构建CartShippingRate对象否(可内联实现)
isAvailable()
自定义可用性判断(可重写)否(使用默认实现)

Built-in Shipping Methods

内置配送方式

  • FlatRate:
    packages/Webkul/Shipping/src/Carriers/FlatRate.php
  • Free:
    packages/Webkul/Shipping/src/Carriers/Free.php
  • FlatRate
    packages/Webkul/Shipping/src/Carriers/FlatRate.php
  • Free
    packages/Webkul/Shipping/src/Carriers/Free.php

Pricing Examples

定价示例

Fixed Rate Shipping

固定费率配送

php
public function calculate()
{
    if (! $this->isAvailable()) {
        return false;
    }

    $cartShippingRate = new CartShippingRate;
    $cartShippingRate->carrier = $this->getCode();
    $cartShippingRate->carrier_title = $this->getConfigData('title');
    $cartShippingRate->method = $this->getMethod();
    $cartShippingRate->method_title = $this->getConfigData('title');
    $cartShippingRate->method_description = $this->getConfigData('description');
    $cartShippingRate->price = 15.99;
    $cartShippingRate->base_price = 15.99;

    return $cartShippingRate;
}
php
public function calculate()
{
    if (! $this->isAvailable()) {
        return false;
    }

    $cartShippingRate = new CartShippingRate;
    $cartShippingRate->carrier = $this->getCode();
    $cartShippingRate->carrier_title = $this->getConfigData('title');
    $cartShippingRate->method = $this->getMethod();
    $cartShippingRate->method_title = $this->getConfigData('title');
    $cartShippingRate->method_description = $this->getConfigData('description');
    $cartShippingRate->price = 15.99;
    $cartShippingRate->base_price = 15.99;

    return $cartShippingRate;
}

Weight-Based Pricing

按重量定价

php
public function calculate()
{
    if (! $this->isAvailable()) {
        return false;
    }

    $cart = Cart::getCart();
    $baseRate = 5.00;
    $perKg = 2.50;

    $price = $baseRate + ($cart->weight * $perKg);

    $cartShippingRate = new CartShippingRate;
    $cartShippingRate->carrier = $this->getCode();
    $cartShippingRate->carrier_title = $this->getConfigData('title');
    $cartShippingRate->method = $this->getMethod();
    $cartShippingRate->method_title = $this->getConfigData('title');
    $cartShippingRate->price = core()->convertPrice($price);
    $cartShippingRate->base_price = $price;

    return $cartShippingRate;
}
php
public function calculate()
{
    if (! $this->isAvailable()) {
        return false;
    }

    $cart = Cart::getCart();
    $baseRate = 5.00;
    $perKg = 2.50;

    $price = $baseRate + ($cart->weight * $perKg);

    $cartShippingRate = new CartShippingRate;
    $cartShippingRate->carrier = $this->getCode();
    $cartShippingRate->carrier_title = $this->getConfigData('title');
    $cartShippingRate->method = $this->getMethod();
    $cartShippingRate->method_title = $this->getConfigData('title');
    $cartShippingRate->price = core()->convertPrice($price);
    $cartShippingRate->base_price = $price;

    return $cartShippingRate;
}

Free Shipping Above Threshold

满额免配送费

php
public function calculate()
{
    if (! $this->isAvailable()) {
        return false;
    }

    $cart = Cart::getCart();
    $threshold = (float) $this->getConfigData('free_shipping_threshold');
    $price = $cart->sub_total >= $threshold ? 0 : (float) $this->getConfigData('default_rate');

    $cartShippingRate = new CartShippingRate;
    $cartShippingRate->carrier = $this->getCode();
    $cartShippingRate->carrier_title = $this->getConfigData('title');
    $cartShippingRate->method = $this->getMethod();
    $cartShippingRate->method_title = $this->getConfigData('title');
    $cartShippingRate->price = core()->convertPrice($price);
    $cartShippingRate->base_price = $price;

    return $cartShippingRate;
}
php
public function calculate()
{
    if (! $this->isAvailable()) {
        return false;
    }

    $cart = Cart::getCart();
    $threshold = (float) $this->getConfigData('free_shipping_threshold');
    $price = $cart->sub_total >= $threshold ? 0 : (float) $this->getConfigData('default_rate');

    $cartShippingRate = new CartShippingRate;
    $cartShippingRate->carrier = $this->getCode();
    $cartShippingRate->carrier_title = $this->getConfigData('title');
    $cartShippingRate->method = $this->getMethod();
    $cartShippingRate->method_title = $this->getConfigData('title');
    $cartShippingRate->price = core()->convertPrice($price);
    $cartShippingRate->base_price = $price;

    return $cartShippingRate;
}

Shipping Facade

Shipping Facade

Location:
packages/Webkul/Shipping/src/Shipping.php
The Shipping facade manages rate collection and processing:
php
class Shipping
{
    public function collectRates()
    {
        // Iterates through all carriers and calls calculate()
        // Returns grouped shipping methods with rates
    }

    public function getGroupedAllShippingRates()
    {
        // Returns rates grouped by carrier
    }

    public function getShippingMethods()
    {
        // Returns available shipping methods
    }
}
位置:
packages/Webkul/Shipping/src/Shipping.php
Shipping Facade负责管理费率的收集和处理:
php
class Shipping
{
    public function collectRates()
    {
        // Iterates through all carriers and calls calculate()
        // Returns grouped shipping methods with rates
    }

    public function getGroupedAllShippingRates()
    {
        // Returns rates grouped by carrier
    }

    public function getShippingMethods()
    {
        // Returns available shipping methods
    }
}

Package Structure

包结构

packages
└── Webkul
    └── CustomExpressShipping
        └── src
            ├── Carriers
            │   └── CustomExpressShipping.php         # Rate calculation logic
            ├── Config
            │   ├── carriers.php                     # Shipping method definition
            │   └── system.php                        # Admin configuration
            └── Providers
                └── CustomExpressShippingServiceProvider.php  # Registration
packages
└── Webkul
    └── CustomExpressShipping
        └── src
            ├── Carriers
            │   └── CustomExpressShipping.php         # 费率计算逻辑
            ├── Config
            │   ├── carriers.php                     # 配送方式定义
            │   └── system.php                        # 后台管理配置
            └── Providers
                └── CustomExpressShippingServiceProvider.php  # 注册类

Testing

测试

Shipping methods can be tested through the checkout flow. Test:
  • Method appears in checkout when enabled
  • Rate calculation is correct
  • Admin configuration saves properly
  • Method respects enabled/disabled status
可通过结账流程测试配送方式,需测试:
  • 启用后,配送方式是否在结账环节显示
  • 费率计算是否正确
  • 后台配置是否能正确保存
  • 配送方式是否遵循启用/禁用状态

Key Files Reference

关键文件参考

FilePurpose
packages/Webkul/Shipping/src/Carriers/AbstractShipping.php
Base abstract class
packages/Webkul/Shipping/src/Carriers/FlatRate.php
Flat rate shipping example
packages/Webkul/Shipping/src/Carriers/Free.php
Free shipping example
packages/Webkul/Shipping/src/Config/carriers.php
Default carriers config
packages/Webkul/Shipping/src/Shipping.php
Shipping facade
packages/Webkul/Checkout/src/Models/CartShippingRate.php
Shipping rate model
packages/Webkul/Admin/src/Config/system.php
Admin config (carrier sections)
文件用途
packages/Webkul/Shipping/src/Carriers/AbstractShipping.php
基础抽象类
packages/Webkul/Shipping/src/Carriers/FlatRate.php
固定费率配送示例
packages/Webkul/Shipping/src/Carriers/Free.php
免费配送示例
packages/Webkul/Shipping/src/Config/carriers.php
默认承运商配置
packages/Webkul/Shipping/src/Shipping.php
Shipping Facade
packages/Webkul/Checkout/src/Models/CartShippingRate.php
配送费率模型
packages/Webkul/Admin/src/Config/system.php
后台配置(承运商板块)

Common Pitfalls

常见误区

  • Forgetting to merge config in service provider
  • Not matching
    $code
    property with config array key
  • Not registering service provider in
    bootstrap/providers.php
  • Forgetting to run
    composer dump-autoload
    after adding package
  • Not clearing cache after configuration changes
  • Not using
    core()->convertPrice()
    for multi-currency support
  • Not checking
    isStockable()
    for per-item calculations
  • Not following PHPDoc conventions with proper punctuation
  • 忘记在服务提供者中合并配置
  • $code
    属性与配置数组键不匹配
  • 未在
    bootstrap/providers.php
    中注册服务提供者
  • 添加包后忘记运行
    composer dump-autoload
  • 修改配置后忘记清除缓存
  • 未使用
    core()->convertPrice()
    支持多币种
  • 按件计算时未检查
    isStockable()
  • 未遵循PHPDoc规范,标点符号使用不当