Skip to content

[checkout] demo.shipping.amount and demo.order.amount telemetry always drop the cents #3742

Description

@bhuvan-somisetty

Ran into this while poking around the checkout service's telemetry attributes.

PlaceOrder and prepareOrderItemsAndShippingQuoteFromCart in src/checkout/main.go both convert a Money proto (units + nanos) into a float64 for the demo.shipping.amount / demo.order.amount span attributes and log fields, using this pattern:

shippingCostFloat, _ := strconv.ParseFloat(fmt.Sprintf("%d.%02d", shippingPrice.GetUnits(), shippingPrice.GetNanos()/1000000000), 64)

Three call sites do this: src/checkout/main.go:383, :384 (same line, but for the order total) and :455.

The divisor is wrong. Per pb/demo.proto, Money.nanos is documented as being in the range -999,999,999 to 999,999,999 - it can never reach 1,000,000,000. So nanos / 1000000000 is integer division of a value that's always less than 1e9 by 1e9, which is always 0 in Go. The %02d after the decimal point then always prints 00, no matter what the actual cents are.

Concrete example: a shipping cost of $5.99 comes through as Money{units: 5, nanos: 990000000}. 990000000 / 1000000000 is 0, so the code formats it as "5.00" and reports demo.shipping.amount = 5.0 instead of 5.99. Same thing happens to demo.order.amount.

This isn't just a hunch either, the telemetry schema documents both attributes with non-integer examples: telemetry-schema/attributes/shipping.yaml lists [5.99, 15.00] for demo.shipping.amount, and telemetry-schema/attributes/order.yaml lists [99.99, 150.50] for demo.order.amount. So the schema's own examples don't match what actually gets emitted.

To reproduce:

  1. Place an order where the shipping cost or order total has a non-zero cents component (i.e. basically any real order).
  2. Look at the demo.shipping.amount / demo.order.amount attributes on the checkout span, or the same fields in the "order placed" log line.
  3. Compare against the actual amount shown in the frontend/cart - the telemetry value is always rounded down to a whole dollar.

The fix is just the divisor. nanos is billionths of a unit, so to get a 2-decimal-place value you need nanos / 10_000_000 (1 cent = 10,000,000 nanos), not nanos / 1_000_000_000. src/shipping/src/shipping_service.rs:18 already gets this right (const NANOS_MULTIPLE: u32 = 10000000u32;), so this is really just bringing main.go in line with what the Rust side already does correctly.

Component(s)

checkout, telemetry

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions