# retail.shipping-rate Looks up a delivery charge on a rate card: pick the service and zone, find the cheapest weight band the parcel fits on the order date, and waive the charge when the basket reaches the free-delivery threshold. ## The rate card is an argument Delivery prices are the merchant's own commercial terms, negotiated with a carrier and changed whenever the merchant likes, not published rules. So the table is passed in as `bands`, not shipped as registry data; each row still carries `validFrom` and `validTo`, so one card can hold last year's prices and this year's and the order date picks between them. The table below, used by the vectors, is **illustrative only**: it is not any carrier's price list. | service | zone | up to | longest side | divisor | step | price | free over | from | to | |---|---|---|---|---|---|---|---|---|---| | standard | UK | 2 kg | 450 mm | none | 1 g | £3.95 | £50 | 2026-01-01 | | | standard | UK | 10 kg | 1000 mm | 5000 | 500 g | £6.95 | £50 | 2026-01-01 | | | standard | UK | 30 kg | 1500 mm | 5000 | 1 kg | £12.95 | never | 2026-01-01 | | | express | UK | 10 kg | 1000 mm | 5000 | 500 g | £9.95 | never | 2026-01-01 | | | standard | EU | 2 kg | 450 mm | none | 1 g | £9.95 | never | 2026-01-01 | | | standard | UK | 2 kg | 450 mm | none | 1 g | £3.50 | £40 | 2025-01-01 | 2025-12-31 | ## How a band is chosen A band matches when its service and zone are the ones asked for, the order date is within its dates (both ends inclusive), the parcel's longest side is within `maxLengthMm`, and the parcel's chargeable weight in that band is within `maxGrams`. Chargeable weight depends on the band: with a `volumetricDivisor` it is the greater of actual and volumetric weight (`retail.volumetric-weight`), rounded up to the band's step; without one it is the actual weight rounded up to the step. Of the matching bands the one with the smallest `maxGrams` wins, then the cheaper, then the first listed. No matching band is an error that names the service, zone and date. That is deliberate: a checkout that quietly charges nothing for a parcel it cannot ship is worse than one that refuses it. ## Free delivery When the band has `freeOver` and `basketTotal` is at least that amount, the price is zero and `free` is true; `standardPrice` still says what it would have cost, for "you saved £3.95" messages. The threshold is compared exactly, so £49.99 is not £50. Which total counts (before or after coupons, with or without VAT) is the merchant's rule: pass that total. 1.0.1 fixes Python accepting a trailing newline or non-ASCII digits in onDate; adds tests.