Medusa & ArchitectureArticle

Shipping depends on weight, destination and currency. How do you make the rate predictable before checkout?

The client’s shipping price was not one flat number. It changed by zone, country, package weight and market currency. We built those rules as an operable Medusa module, with a calculator the team could test before customers reached checkout.

We Are Souk article cover: predictable shipping rates by weight, destination and currency
Souk EngineeringCommerce architectureAug 2026·8 min read
Key takeaways
  • Model the dimensions the business actually prices: destination, weight band and currency.
  • Keep zone membership separate from the price table so geography can evolve cleanly.
  • Define boundaries precisely; one gram should not fall between two bands or match both.
  • Give operators a calculator that explains the selected rule before it reaches a real order.
  • Use the same resolver for storefront checkout, draft orders and later order edits.

The client problem: a flat rate could not represent the delivery promise

Shipping looks simple when every parcel leaves one warehouse and every customer pays the same amount. The client’s reality combined several destinations, package weights and currencies. A light parcel to one country could use one price; a heavier parcel or another zone needed another. The storefront had to quote the right amount, but the operations team also needed to understand and maintain the table.

Hardcoding conditions into checkout would have produced the correct answer only until the next tariff change. A spreadsheet would have been easier to edit but disconnected from the order. The requirement was therefore larger than a calculation: represent the shipping policy inside commerce, let operators change it safely and make the selected result explainable.

Turn the carrier spreadsheet into an explicit business model

01
Zone

A commercial grouping such as domestic, neighbouring markets or a wider region. A zone is a policy container, not a price by itself.

02
Country

The actual destination selects the eligible zone. Country membership needs one authoritative rule so checkout and Admin agree.

03
Weight band

Each band has clear inclusive and exclusive boundaries. Gaps and overlaps are rejected before publication.

04
Currency price

The rate belongs to the market currency being quoted. Converting an arbitrary base price at checkout is a separate business choice, not an invisible default.

Make rate resolution deterministic

At calculation time the resolver receives destination, package weight and commercial currency. It finds the applicable zone, selects exactly one weight band and returns the configured price. If no rule exists, the system should return a named configuration gap rather than quietly choosing zero or the nearest number.

Boundary semantics matter. If one band ends at five kilograms and the next begins at five, the contract must say which owns exactly five. The same unit must be used when product weights are aggregated. Missing weights need an explicit policy because a single unweighted product can make the parcel total meaningless.

The calculation should also return an explanation suitable for support and Admin: destination matched zone A, total weight matched band B, market currency selected price C. That trace turns a surprising shipping amount into a question the team can answer.

See the original shipping-rate module

Original project demonstration: operators configure zones and weight bands, then test a destination and parcel weight in Medusa Admin.

The Admin calculator is not decoration. It is the safety mechanism.

Configuration interfaces often let teams save data but give them no way to ask what the data will do. We added a calculator where an operator enters a country and package weight and sees the returned price. This shortens the path between editing a table and understanding its effect.

The calculator should use the same resolver as checkout. A separate preview formula would create false confidence. Useful test cases include every boundary, the heaviest supported parcel, countries at the edge of each zone, unsupported destinations and each active currency. When a tariff changes, the merchant can verify representative journeys before customers encounter them.

Treat rate tables as commercial data with a lifecycle

Carrier prices change. Countries move between commercial zones. The business may negotiate a new table that starts next month while current orders still need the old promise. Editing rows in place without dates or history makes it impossible to explain why two otherwise similar orders received different prices.

A mature module can version tariff sets with an effective period and a draft state. Operators prepare the next table, validate representative scenarios and publish it deliberately. Existing orders retain the amount and rule reference used when they were confirmed; new calculations use the active version. Rollback means reactivating a known table rather than reconstructing yesterday’s cells from memory.

Imports can accelerate large updates, but they need the same validation as the Admin form: recognised countries and currencies, valid numeric weights, non-negative prices, no overlapping bands and no uncovered interval inside a zone that claims continuity. A preview should summarise additions, changes and removals before the new table becomes active.

Separate the quoted rate from the fulfilment promise

A price table answers what the merchant will charge. It does not by itself prove that a carrier serves the address, that the parcel fits the service or that delivery will take a particular number of days. Combining those questions into one amount makes the checkout look certain while hiding important operational constraints.

The eligibility layer should first identify viable fulfilment options for the destination and parcel. The rate resolver then prices the selected commercial option. Delivery estimates, surcharges and restricted goods can participate through explicit rules where the client needs them. This layered journey lets the interface explain both availability and price.

After launch, compare quoted amounts with actual carrier costs and exceptions. Large differences may reveal an outdated tariff, missing product weight or a packaging assumption that does not hold. That feedback should improve the policy without silently changing historical orders. Shipping architecture is complete only when the price can be operated after the first successful checkout.

Use one shipping contract across every way an order is created

Storefront checkout is only one order-entry path. B2B teams create draft orders, account managers edit orders and support may change quantities after a conversation. If those paths do not invoke the same shipping policy, the amount becomes dependent on who created the order rather than what is being shipped.

We therefore treat shipping resolution as a reusable commerce capability. Product changes trigger a new package weight; destination or market changes trigger a new eligibility check; the returned amount is applied through the relevant order workflow. Operators see the consequence while they are editing, not after the customer receives a revised invoice.

Why Medusa made the client-specific rule practical

The client did not need an app marketplace approximation of its tariff table. It needed its own shipping policy to participate in orders alongside Medusa regions, carts and fulfilment options. A custom module gave the rules a clear home, while workflows and Admin extensions connected them to daily operations.

This is why platform selection is an architecture decision. Medusa supplied the commerce engine and the extension points; we added the missing business capability without creating a detached calculator. The result is a rate the storefront can quote, an operator can test and support can explain from the same underlying contract.

FAQ

Common questions

Why not use one base rate and convert it at checkout?

Some merchants choose that policy, but carrier pricing and commercial rounding can differ by market. Explicit currency prices make the promise intentional and reviewable.

What should happen when no rule matches?

Return a clear unavailable or configuration-error state. A silent zero rate or arbitrary fallback can create a loss-making delivery promise.

Should draft orders use the same calculator?

Yes. Storefront, draft-order and order-edit journeys should resolve shipping from the same contract so the price does not depend on the interface used.

Keep the useful ideas coming

One practical commerce field note at a time.

Join the WeAreSouk journal for grounded stories about Medusa, Shopify, AI, integrations and the systems behind serious commerce.

Working on a similar problem?Bring us the business constraint. We’ll help map the system behind it.Talk to Souk →
Souk AI · online now

Turn the article into an implementation plan.

Ask how this applies to your store, your stack, or your current bottleneck.

01 Describe your current setup.02 Name the workflow or signal that feels unreliable.03 Get a practical first architecture back.
I can help map this article to your stack. Tell me what you sell, what platform you use, and where the medusa & architecture question hurts.
Start a conversation

Tell us what commerce needs to do for your business.

No scheduling maze. Send the context, the constraint or the idea. We will read it and come back to you directly.