Shipping
This page shows you how Litestore decides which shipping rates a shopper sees, how it builds tracking links, and how it estimates arrival dates.
Litestore lets each shipping rate say which countries, location and sales channel it serves. Read this page when you change how rates are offered at checkout, how tracking links work, or how delivery dates are estimated.
Litestore has no shipping zones. You won't find a zone model, field or route anywhere in the codebase.
In most systems, a zone adds an extra step. Rates belong to zones, zones contain countries, and answering any question about a rate takes two lookups. In Litestore, each rate lists the countries it serves itself.
The downside is that two rates for the same twenty countries each repeat the list. The upside is that you can read a rate and see right away where it applies.
Where a rate applies
A rate has a list of country codes, where an empty list means every country. It can also name one location and one channel.
These three settings filter independently, and each one has a clear meaning when you leave it empty:
- an empty country list serves every country
- a null location means the rate is not tied to a particular warehouse
- a null channel means the rate is global and every channel can use it
A rate can also have a minOrderAmount and a maxOrderAmount. You can leave either one unset.
Free shipping is not a separate feature. It is a rate with a price of zero and a minimum order amount.
There is nothing more to it. You don't need a free-shipping flag that has to match a threshold, and no second code path removes the charge. The cart qualifies for the rate, and the rate costs nothing.
Fulfilment methods
A rate's type is one of these values:
delivery
pickup
digitalThe type is a string, not a database enum. You can add a new method, such as lockers or a courier, without a migration. The list of valid values is in lib/db-enums.ts and the matching zod enum.
Each rate has an explicit position. The shopper sees rates at checkout in the order the operator arranged them, not in the order they happened to be created.
Currency
Each rate has its own currency. Litestore only shows rates whose currency matches the checkout session's currency.
This is required, not just tidier. Stripe rejects a session if any of its shipping_options uses a different currency from the session. For a store that sells in several currencies, one rate in the wrong currency would stop checkout from working at all.
Litestore also stores each price as an amount and its currency together. No code can read the number without the currency that goes with it.
A rate's currency decides where it shows up
A rate priced in a currency the session isn't using is not offered as a cheaper or rough option. Shoppers don't see it at all. If a market shows no delivery options, check the rate currencies before you check the country lists.
Tracking
Litestore builds tracking URLs automatically for four carriers:
usps
ups
fedex
dhlThese four make up the carrier dropdown in the editor. Because the list is short and known, Litestore can build the customer's "Track your package" link from the carrier and the tracking number. The operator doesn't have to paste a full URL for every shipment.
For any other carrier, Litestore saves the tracking number without a link.
buildTrackingUrl returns null when the carrier is unknown or internal, and also when the tracking number is missing. The caller then uses a URL that the operator entered by hand, or leaves the link empty. A shipment with an unknown carrier still saves its tracking number and still shows it.
Arrival estimates
Litestore estimates arrival dates from a setting that lists working days and holiday closures. It doesn't ask a carrier's API, and nothing calls out to a carrier to ask when a parcel will arrive.
Litestore calculates each estimate when it is needed and never stores it:
handling (processing) time
+
the rate's transit days
↓
counted in the store's working daysThe rate provides minDays and maxDays. Either one can be null when the rate doesn't give that number, and then that end of the estimate is null too. Litestore reports a missing number as unknown instead of guessing.
Litestore doesn't assume which days are working days. The store's fulfilment calendar decides, by listing the weekdays the store ships and its own holiday closures. By default the store ships Monday to Friday. That is only a default, not a fixed rule. A store with a Friday and Saturday weekend, a dropshipper that ships seven days a week, and a maker who only ships on Mondays, Wednesdays and Fridays all get estimates that match how they really work.
The calculation exists in one place only. So the product page, the cart and the order page always show the same arrival date.
Where a new shipping rule goes
If the rule is about where a rate applies, add it as a setting on the rate, next to countries, location and channel. Don't add a new branch to checkout.
If the rule is about when a parcel arrives, put it in the estimate function or the fulfilment calendar. Then every page that shows a date shows the same one.
If the rule depends on a carrier's own systems, be careful about what you promise. Litestore builds a tracking link from a number the operator typed in. It doesn't know the parcel's actual status.
Before you add a rule, ask yourself:
Is this something the store configured, or something only the carrier knows, and which of the two am I about to show the customer?
Related
Tax
See how Litestore works out a tax rate, why Stripe Tax falls back to the flat channel rate, and what is left out on purpose.
Checkout
See how Litestore turns a cart into a Stripe payment and holds stock for the shopper while they pay.
Inventory
See how Litestore adds up stock across locations, how the database limits stock changes, and how it handles checkouts that happen at the same time.