Models
Shopper uses two models to manage shipping:Database Schema
Carrier Table
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
id | bigint | no | auto | Primary key |
name | string | no | - | Carrier name |
slug | string | yes | auto | URL-friendly identifier (unique) |
driver | string | yes | null | Shipping driver code (ups, fedex, usps, or null for manual) |
link_url | string | yes | null | Carrier website URL |
description | string | yes | null | Carrier description |
shipping_amount | integer | yes | null | Default shipping amount (cents) |
is_enabled | boolean | no | false | Carrier visibility |
metadata | json | yes | null | Additional custom data |
created_at | timestamp | yes | null | Creation timestamp |
updated_at | timestamp | yes | null | Last update timestamp |
CarrierOption Table
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
id | bigint | no | auto | Primary key |
name | string | no | - | Option name (unique) |
description | string(255) | yes | null | Option description |
price | integer | no | - | Shipping price (cents) |
is_enabled | boolean | no | false | Option visibility |
carrier_id | bigint | no | - | FK to carrier |
zone_id | bigint | no | - | FK to zone |
metadata | json | yes | null | Additional custom data |
created_at | timestamp | yes | null | Creation timestamp |
updated_at | timestamp | yes | null | Last update timestamp |
Shipping Drivers
Shopper uses a driver-based architecture for shipping, similar to Laravel’s Mail or Queue systems. This allows you to connect carriers directly to shipping provider APIs for real-time rate calculation, or use manual rates stored in your database.Available Drivers
| Driver | Code | Description |
|---|---|---|
| Manual | manual | Rates come from CarrierOption records in your database |
| UPS | ups | Real-time rates from UPS API |
| FedEx | fedex | Real-time rates from FedEx API |
| USPS | usps | Real-time rates from USPS API |
Driver Configuration
All driver credentials are stored in your.env file. Publish the shipping configuration file to customize drivers:
config/shopper/shipping.php:
.env file:
Using the Shipping Facade
TheShipping facade provides access to the shipping manager and all registered drivers:
Carrier Driver Integration
The Carrier model provides methods to work with shipping drivers:manual), rates come from the CarrierOption records in your database.
Calculating Shipping Rates
TheCarrierRateService provides a unified way to get shipping rates, regardless of whether the carrier uses an API driver or manual configuration.
Getting Rates for a Carrier
Getting Rates for a Zone
To get all available shipping rates for a zone across all enabled carriers:ShippingRate DTO
Both methods return a collection ofShippingRate objects:
Custom Shipping Drivers
Shopper’s shipping system is designed for extensibility. You can create custom drivers for any shipping provider not included by default, such as DHL, Canada Post, Colissimo, or regional carriers.Creating a Driver
A shipping driver must implement theShippingDriver contract. The easiest approach is to extend the abstract Driver class, which provides sensible defaults for optional methods.
Create your driver class:
The Driver Contract
TheShippingDriver contract defines the following methods:
| Method | Return | Description |
|---|---|---|
code() | string | Unique identifier for the driver |
name() | string | Human-readable name displayed in the admin |
logo() | ?string | URL to the carrier’s logo |
isConfigured() | bool | Whether required credentials are set |
supportsRealTimeRates() | bool | Whether the driver can fetch rates from an API |
supportsLabels() | bool | Whether the driver can generate shipping labels |
supportsTracking() | bool | Whether the driver can track shipments |
calculateRates() | Collection | Get shipping rates for the given addresses and packages |
createShipment() | Shipment | Create a shipment and get the label |
track() | TrackingInfo | Get tracking information for a shipment |
Driver class provides default implementations for supportsRealTimeRates(), supportsLabels(), and supportsTracking() (all return true), and throws ShippingException::notSupported() for createShipment() and track(). Override these methods as needed for your driver.
Registering the Driver
Register your custom driver in a service provider using theextend method on the Shipping facade:
ShippingDriver. Drivers are resolved lazily, meaning the closure is only called when the driver is first accessed.
Adding Configuration
Add your driver configuration toconfig/shopper/shipping.php:
Driver Capabilities
The abstractDriver class provides a normalizePackages() helper method that converts between metric and imperial units. This is useful since different carriers expect different unit systems:
'metric' for carriers expecting centimeters and kilograms, or 'imperial' for those expecting inches and pounds.
Relationships
Carrier Options
Zone (CarrierOption)
Carrier (CarrierOption)
Query Scopes
Price Handling
CarrierOption prices are stored in cents and automatically converted:Creating Carriers
Manual Carrier
For carriers where you define rates manually in the database:API-Connected Carrier
For carriers using a shipping driver to fetch real-time rates:Free Shipping Option
Retrieving Carriers
Working with Orders
Storefront Example
Checkout Shipping Selection
The origin address for shipping calculations should come from your store’s inventory (warehouse). Shopper uses theInventory model to represent physical locations where products are stored and shipped from.
If your store has multiple warehouses, you can pass the inventory ID to ship from a specific location. This is useful for selecting the nearest warehouse to the customer or the one with available stock:
Use Cases
| Carrier | Options |
|---|---|
| USPS | Priority Mail, First-Class, Media Mail |
| FedEx | Ground, Express, Overnight |
| UPS | Ground, 2-Day, Next Day Air |
| DHL | Express, Economy, Freight |
| Local | Same-Day, Pick-up |
| Scenario | Implementation |
|---|---|
| Flat rate shipping | Manual carrier with single option at fixed price |
| Free shipping threshold | Use metadata for min_order_amount |
| Zone-based pricing | Create options per zone for manual carriers |
| Real-time rates | Assign a shipping driver to the carrier |
| Hybrid approach | Manual carriers for local, API drivers for national/international |