shopwaree-commercephpshippinglogistics

Custom Shopware Shipping Calculation: When Rules Are Not Enough

| 7 min read | Huzaifa Mustafa

Shopware 6’s Rule Builder handles shipping for most shops without any custom code. Weight-based pricing, order value thresholds, country restrictions, free shipping above a certain amount. It covers the basics well, and for straightforward logistics it is genuinely all you need.

But I recently built a plugin for a client whose products ranged from 1-liter containers to 65-liter drums, shipped across five different package types, each with its own cost structure. Their shipping was not about weight or cart value. It was about physical space, and no configuration UI can express that. This post walks through how a custom shipping cost calculation engine actually looks in production Shopware 6.

What Shopware Gives You Out of the Box

Credit where it is due: Shopware’s shipping system is flexible. The Rule Builder lets you define conditions based on weight, cart price, item count, customer group, shipping country, and more. Price matrices map those conditions to shipping costs. You can configure multiple shipping methods, each with its own set of rules.

The limitation is architectural, not a missing feature. These tools model shipping as a lookup problem: match a set of conditions, return a price. They cannot model shipping as an optimization problem: given N items of varying physical sizes, find the cheapest combination of packages that fits everything.

When your products have radically different physical dimensions and need to be packed across multiple package types, you have left the territory of configuration and entered the territory of algorithms.

The Shopware Shipping Problem That Broke the Rules

The client sold physical goods in containers ranging from 1 liter to 65 liters, with some products available in oversized variants of the same volume. They shipped using five package types: small parcels (Paket), half palettes, Euro palettes, industrial palettes, and XXL palettes. Each package type had its own carrier cost.

The constraints were physical, not commercial. A 50-liter container physically cannot fit in a small parcel. A customer ordering three 1-liter bottles should not have their shipment placed on a Euro palette. A mixed cart with both small and large items needs optimal allocation across package types to minimize total shipping cost.

On top of that: Germany, Austria, and international destinations each had different pricing rules. German island zip codes were blocked entirely because carriers will not deliver palettes to those locations. Free shipping promotions were restricted to domestic orders only.

No amount of Rule Builder configuration can express: “Pack these 14 items across the fewest packages, choosing from 5 types, while respecting physical constraints and minimizing total cost.”

Space Usage as a Percentage Problem

The key insight was to stop thinking about products in terms of weight or flat-rate categories, and instead model them by what percentage of each package type they consume.

A 1-liter container uses 6.67% of a small parcel but only 0.83% of a Euro palette. A 50-liter container uses 0% of a small parcel (it does not fit at all), 100% of a half palette, and 50% of a Euro palette. Each of the 17 product sizes maps to a specific space consumption percentage across all five package types.

This turns shipping calculation into a clear optimization: fill packages to 100% capacity using the cheapest combination of package types.

The oversized variants added another dimension. Two products with the same volume but different physical footprints consume different amounts of space. A standard 15-liter container and its oversized variant both hold 15 liters, but the oversized version takes up significantly more room in every package type.

This is a bin-packing problem. It is NP-hard in the general case, but with a bounded set of package types and a greedy heuristic, you can get optimal-enough results in milliseconds.

The Bin-Packing Algorithm Inside Shopware’s Cart Pipeline

The calculation runs on every cart update and follows a greedy strategy:

  • Sort products largest-first. The biggest items are the hardest to fit, so placing them first minimizes wasted space in later rounds.
  • Check existing packages before opening new ones. If a partially-filled Euro palette has 50% capacity remaining and the next item uses 12.5%, put it there instead of opening a new package.
  • Pick the package type that minimizes total packages needed. For each product batch, the algorithm evaluates all five package types and selects the one requiring the fewest total packages.
  • Escalate automatically. If the parcel count exceeds 10, the algorithm switches to palettes. This is not an arbitrary threshold. It is a real-world carrier constraint where 10+ individual parcels cost more than a single palette shipment.
  • Track remaining capacity as a percentage. Each package maintains its fill level, and subsequent items are slotted into remaining space before new packages are allocated.

The plugin implements Shopware’s CartProcessorInterface, hooking into the cart pipeline after products are resolved but before the order is finalized. Registration is a single service tag in the DI container:

<service id="MyPlugin\Core\Cart\ShippingProcessor">
    <argument type="service" id="MyPlugin\Service\DeliveryCalculationService"/>
    <argument type="service" id="monolog.logger"/>
    <tag name="shopware.cart.processor" priority="4500"/>
</service>

The calculated cost is set as a manual shipping cost override on the cart delivery, replacing Shopware’s native calculation entirely. The full package breakdown (how many of each type) is stored as a cart extension so it persists into the order record. The warehouse team uses this breakdown to know exactly which packages to prepare.

Geographic Rules and Edge Cases

Shipping logic is never just about packaging. Geography adds its own layer of complexity.

German island zip codes are loaded from a curated dataset at calculation time. If the customer’s shipping address matches one of these zip codes, the order is blocked with a clear error message. The carrier simply will not deliver palettes to islands like Sylt or Helgoland, and attempting it would result in failed deliveries and return shipping costs.

International orders are forced into parcel-only mode, falling back to Shopware’s native shipping price configuration. The space optimization only runs for domestic German shipments where the full range of package types is available.

Free shipping promotions are restricted to domestic orders. Products flagged as free-shipping still count toward the package calculation (the warehouse still needs to pack and ship them), but their cost is excluded from the total. Attempting to order free-shipping products internationally triggers a blocking error rather than silently applying a promotion that would eat into margins.

Every shipping plugin I have built has at least one rule that sounds absurd until you understand the logistics behind it.

When to Build a Custom Shopware Shipping Engine

Not every shop needs a custom shipping engine. Here is the decision framework I use:

  • If your shipping logic can be expressed as “if condition, then price,” use the Rule Builder. It is what it is designed for, and maintaining configuration is cheaper than maintaining code.
  • If your logic involves optimization (minimize packages), physical constraints (item X cannot fit in container Y), or algorithmic decisions (escalate when threshold exceeded), you need custom code.

The good news is that Shopware’s architecture supports this cleanly. CartProcessorInterface exists for exactly this purpose. Custom entities for package types and their costs mean the client can adjust pricing through the admin panel without touching code. The framework is not fighting you. You are just extending it at the exact point where configuration runs out.

I wrote about the broader architecture rules I follow in my previous post on Shopware plugin development. The same engine powers the automated dropshipping pipeline I built for the same client, where the package breakdown feeds directly into supplier order routing. More examples of this kind of work are in my case studies.

The best Shopware plugins do not replace the framework. They extend it precisely where configuration runs out. If your shipping logic has outgrown what the admin panel can express, let’s talk.

Share this article

Found this useful? Share it with your network

Huzaifa Mustafa

Huzaifa Mustafa

Shopware 6 certified developer with 162+ custom plugins delivered and 95+ clients across the DACH region. I write about Shopware architecture, e-commerce performance, and lessons from real projects.

Need help with Shopware?

Let's discuss how I can help with your e-commerce project.