boboko / shipping · competitive survey

What shipping elsewhere can do that boboko can't yet

A feature-by-feature pass across Shopify, WooCommerce, and PrestaShop's shipping layer — sourced, not recalled from memory — checked against what Lunar core's ShippingManifest and the lunarphp/table-rate-shipping add-on actually support today, and what's actually wired up in boboko-core and 3dealer right now. For deciding what to design next, not a build order.

● have ◐ partial ○ missing
01

Core plumbing

The mechanism Lunar core provides for offering and applying a shipping charge — everything else in this survey is built on top of it.

Pluggable shipping option providers
have
Lunar\Base\ShippingModifier abstract class + ShippingManifest::addOption() — any package can register options onto the manifest via a pipeline of modifiers (ShippingModifiers::getModifiers()).
Shipping applied to cart totals during calculate()
have
Lunar\Pipelines\Cart\ApplyShipping — reads ShippingManifest::getShippingOption($cart) or a manual shippingOptionOverride, writes a ShippingBreakdown and shippingSubTotal onto the cart before CalculateTax runs.
Cart-level shippable check
have
Cart::isShippable() — true if any line's purchasable (e.g. ProductVariant::isShippable()) is shippable; a digital-only cart skips the shipping-address requirement entirely.
Selecting a shipping option on the cart
have
Cart::setShippingOption() → SetShippingOption action, validated by ShippingOptionValidator, triggers a recalculate. Nothing in 3dealer's storefront calls it yet — no shipping step exists in the UI.
Order-time shipping line snapshot
have
Lunar\Pipelines\Order\Creation\CreateShippingLine writes an immutable shipping-type order line from the cart's shipping breakdown at checkout — survives later rate changes.
02

Rate configuration (table-rate-shipping add-on)

lunarphp/table-rate-shipping is installed (composer.json, pinned ^1.3) and its ShippingPlugin is registered in CorePlugin::boot() — so 3dealer inherits it automatically, it doesn't need its own registration.

Geographic shipping zones (country / state / postcode)
have
ShippingZone model, type unrestricted|countries|states|postcodes; ShippingZoneResolver::get() matches a cart's address against zone scope, falling back to any unrestricted zone.
Flat-rate shipping
have
Drivers\ShippingMethods\FlatRate::resolve() — one price per cart subtotal via Pricing::for($shippingRate).
Weight- or total-tiered rates ("ship by")
have
Drivers\ShippingMethods\ShipBy::resolve() — data['charge_by'] is cart_total or weight, tiered via priceBreaks, with customer-group price overrides taking priority.
Free-shipping threshold
have
Drivers\ShippingMethods\FreeShipping::resolve() — data['minimum_spend'] (per-currency array supported), optional use_discount_amount to check against post-discount subtotal.
In-store pickup / collection
have
Drivers\ShippingMethods\Collection::resolve() — zero-price option, flagged collect: true on the ShippingOption. Single implicit "store" — no concept of which location, no per-location stock or hours.
Per-product shipping exclusions by zone
have
ShippingExclusionList + ShippingZone::shippingExclusions() — every driver checks it before resolving and returns null if any cart line's product is excluded from that zone.
Per-customer-group rate visibility
have
ShippingMethod::customerGroups() pivot carries visible, enabled, starts_at, ends_at — scheduling and audience-gating a rate is already modeled.
Filament admin UI for zones/methods/rates
have
ShippingZoneResource, ShippingMethodResource, ShippingExclusionListResource ship with the add-on — usable as soon as the Filament plugin is registered, which it is via CorePlugin.
Storefront checkout step to pick a rate
missing
No shipping views exist in 3dealer's resources/views beyond a passing mention in components/footer.blade.php — the whole backend above is unwired to any customer-facing UI.
03

Carrier integration

Real carriers quoting and printing on Lunar's behalf, rather than merchant-defined flat/tiered rates.

Real-time carrier rate shopping (USPS/UPS/FedEx/DHL)
missing
No driver in table-rate-shipping calls an external carrier API — all four shipped drivers (FlatRate, ShipBy, FreeShipping, Collection) compute from local data. Shopify's CarrierService API is the model for this: shop sends weight/dims/destination, carrier returns live rates at checkout.
Product/variant weight & dimensions for rating
partial
ProductVariant has weight_value/weight_unit (referenced in ShipBy's weight tier and docs/lunar.md) but no length/width/height fields exist in core migrations — enough for weight-tier rating, not enough for carrier-grade dimensional/volumetric quotes.
Shipping label generation & printing (staff-facing)
missing
No label concept anywhere in core or the add-on. Shopify has this built in for US merchants (USPS/UPS labels from admin or mobile); WooCommerce/PrestaShop lean on Shippo/EasyPost-style apps.
Return / exchange label generation
missing
No returns concept exists in Lunar core at all — this sits behind both "labels" and "returns," neither of which exists yet.
Shipment tracking numbers on orders
missing
OrderShippingZone pivot table records which zone an order matched, but no field anywhere stores a carrier tracking number or shipment status.
04

Fulfillment logistics

Where an order physically ships from, and whether it can ship from more than one place.

Multi-warehouse / multi-location inventory
missing
No warehouse, location, or fulfillment-center model anywhere in vendor/lunarphp/core or lunar — stock is a flat quantity on the variant. WooCommerce needs Calcurates or WooCommerce Warehouses add-ons for this; it's genuinely not a Lunar concept at all.
Split shipment (one order, multiple packages/warehouses)
missing
Downstream of multi-warehouse — with a single implicit stock pool, there's nothing to split by. CreateShippingLine writes exactly one shipping line per order.
Multiple pickup locations (choose a specific store)
missing
The Collection driver models pickup as a single yes/no rate per zone — no location entity to pick from, no per-location hours/capacity.
Local delivery (distinct from carrier shipping or pickup)
missing
No radius/zone-based "we deliver it ourselves" driver — only ShipBy/FlatRate (carrier-agnostic priced shipping) and Collection (pickup) exist as concepts.
Delivery date / time-slot selection at checkout
missing
No date/time field on ShippingOption, CartAddress, or the order shipping line. WooCommerce needs a dedicated delivery-date-picker plugin for this too — not a gap unique to Lunar, but still open here.
05

International & risk

What happens when a shipment crosses a border, or something goes wrong in transit.

Customs documentation / HS codes per product
missing
No HS-code or customs-description field found on Product/ProductVariant migrations. Every international shipment needs one per line item to clear customs — researched requirement, not yet modeled anywhere in Lunar.
Duties/taxes collected at checkout (DDP)
missing
Lunar's CalculateTax pipeline step handles sales tax/VAT on the cart itself, not import duty estimation for cross-border orders. DDP vs. DDU is the standard framing (seller-collects-upfront vs. customer-pays-on-delivery) — neither is modeled.
Country/zone-restricted shipping
have
ShippingZone type countries/states/postcodes already scopes which rates apply where — the building block international shipping would sit on top of.
Shipping insurance / package protection at checkout
missing
No insurance line-item concept in core. On Shopify this is exclusively third-party (ShipInsure, Route, Simply Shipping Protection) — not a platform-native feature there either, so the gap is normal, not distinctive.