fix(shipping): round weights before posting to shippo
Some checks failed
Deploy / deploy (push) Has been cancelled

Shippo's /v1/orders endpoint rejects >2 decimal places on parcel
weight and >4 on line_items[].weight. Converting ounces to kg
(oz * 0.0283495) routinely produces 6-decimal values, so the first
real label purchase 400'd with "Ensure that there are no more than
N decimal places".

Round at the payload boundary in createShippoOrder rather than in
the caller — keeps the precision of the source (Coffee.Ounces) and
only loses it when the wire format demands it.
This commit is contained in:
2026-05-12 21:12:31 +02:00
parent c22b64d6fa
commit 99f34788ea

View File

@@ -183,7 +183,7 @@ func (c *Client) createShippoOrder(ctx context.Context, to Address, email string
lines = append(lines, map[string]any{
"quantity": item.Quantity,
"title": item.Title,
"weight": item.WeightKg,
"weight": roundN(item.WeightKg, 4),
"weight_unit": "kg",
})
}
@@ -195,7 +195,7 @@ func (c *Client) createShippoOrder(ctx context.Context, to Address, email string
"to_address": toPayload,
"line_items": lines,
"placed_at": time.Now().Format(time.RFC3339),
"weight": totalKg,
"weight": roundN(totalKg, 2),
"weight_unit": "kg",
}
var resp shippoOrderResponse
@@ -304,6 +304,19 @@ func addrPayload(a Address) map[string]any {
return m
}
// roundN rounds to n decimal places. Shippo's /v1/orders rejects >2 decimals
// on parcel weight and >4 on line-item weight.
func roundN(v float64, n int) float64 {
pow := 1.0
for i := 0; i < n; i++ {
pow *= 10
}
if v >= 0 {
return float64(int64(v*pow+0.5)) / pow
}
return float64(int64(v*pow-0.5)) / pow
}
func parseAmountCents(s string) int {
f, err := strconv.ParseFloat(s, 64)
if err != nil {