Loading...
Loading...
Shipping method development in Bagisto. Activates when creating shipping methods, integrating shipping carriers like FedEx, UPS, DHL, or any third-party shipping provider; or when the user mentions shipping, shipping method, shipping carrier, delivery, or needs to add a new shipping option to checkout.
npx skill4agent add bagisto/agent-skills shipping-method-development| Component | Purpose | Location |
|---|---|---|
| Carriers Configuration | Defines shipping method properties | |
| Carrier Classes | Contains rate calculation logic | |
| System Configuration | Admin interface forms | |
| Service Provider | Registers shipping method | |
| Shipping Facade | Collects and manages rates | |
mkdir -p packages/Webkul/CustomExpressShipping/src/{Carriers,Config,Providers}packages/Webkul/CustomExpressShipping/src/Config/carriers.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',
],
];| Property | Type | Purpose | Description |
|---|---|---|---|
| String | Unique identifier | Must match the array key and |
| String | Default display name | Shown to customers during checkout (can be overridden in admin). |
| String | Method description | Brief explanation of the shipping service. |
| Boolean | Default status | Whether the shipping method is enabled by default. |
| String/Float | Base shipping cost | Base shipping cost before calculations. |
| String | Pricing model | |
| String | Carrier class namespace | Full path to your carrier class. |
Note: The array key () must match thecustom_express_shippingproperty in your carrier class, system configuration key path, and should be consistent throughout.code
packages/Webkul/CustomExpressShipping/src/Carriers/CustomExpressShipping.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/Config/system.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,
],
],
],
];| Property | Purpose | Description |
|---|---|---|
| Field identifier | Used to store and retrieve configuration values. |
| Field label | Label displayed in the admin form. |
| Input type | |
| Default setting | Initial value when first configured. |
| Multi-store support | Different values per sales channel. |
| Multi-language support | Translatable content per language. |
| Field validation | Rules like |
packages/Webkul/CustomExpressShipping/src/Providers/CustomExpressShippingServiceProvider.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
{
//
}
}{
"autoload": {
"psr-4": {
"Webkul\\CustomExpressShipping\\": "packages/Webkul/CustomExpressShipping/src"
}
}
}composer dump-autoloadbootstrap/providers.php<?php
return [
App\Providers\AppServiceProvider::class,
// ... other providers ...
Webkul\CustomExpressShipping\Providers\CustomExpressShippingServiceProvider::class,
];php artisan optimize:clearpackages/Webkul/Shipping/src/Carriers/AbstractShipping.phpWebkul\Shipping\Carriers\AbstractShipping<?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/Checkout/src/Models/CartShippingRate.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',
];
}| Method | Purpose | Required |
|---|---|---|
| Calculate and return shipping rate | Yes (abstract) |
| Build CartShippingRate object | No (can be inline) |
| Override for custom availability | No (uses default) |
packages/Webkul/Shipping/src/Carriers/FlatRate.phppackages/Webkul/Shipping/src/Carriers/Free.phppublic 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;
}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;
}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;
}packages/Webkul/Shipping/src/Shipping.phpclass 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
└── CustomExpressShipping
└── src
├── Carriers
│ └── CustomExpressShipping.php # Rate calculation logic
├── Config
│ ├── carriers.php # Shipping method definition
│ └── system.php # Admin configuration
└── Providers
└── CustomExpressShippingServiceProvider.php # Registration| File | Purpose |
|---|---|
| Base abstract class |
| Flat rate shipping example |
| Free shipping example |
| Default carriers config |
| Shipping facade |
| Shipping rate model |
| Admin config (carrier sections) |
$codebootstrap/providers.phpcomposer dump-autoloadcore()->convertPrice()isStockable()