What I ran into
The checkout create exclusion set covers fifteen names but not continue_url, expires_at or messages, so those members travel from the request into the response.
Observed against main (bd06e29)
Fresh clone, documented setup, seeded flower shop database:
POST /checkout-sessions
{"line_items": [{"item": {"id": "bouquet_roses"}, "quantity": 1}],
"continue_url": "https://example.com/client",
"expires_at": "2030-01-01T00:00:00Z"}
HTTP 201
response.continue_url = "https://example.com/client"
response.expires_at = "2030-01-01T00:00:00Z"
Both are persisted, so a later GET returns the same values. messages behaves the same way, keeping the client content with server defaults filled in.
ucp, id, totals, status and links are excluded and come back server owned, which is the negative control showing the intended handling for this class.
Expected
At v2026-04-08 checkout.json marks continue_url, expires_at and messages as ucp_request omit. The merchant owns them on the response.
Root cause
create_checkout builds checkout_data at services/checkout_service.py line 263 with an exclusion set of fifteen names: line_items, payment, ucp, currency, id, status, totals, links, fulfillment, buyer, context, signals, attribution, cart_id and discounts. continue_url, expires_at and messages are not among them, so they arrive through **checkout_data at line 366.
Where it comes from
Same pitfall as the cart report filed alongside this one, and the same class as currency in #156 and id in #167. Under extra allow, a member the schema assigns to the merchant can still arrive from the wire.
Why CI did not catch it
No test sends these members, so the path never runs. The suite is green on this commit, 222 passed locally.
What I ran into
The checkout create exclusion set covers fifteen names but not continue_url, expires_at or messages, so those members travel from the request into the response.
Observed against main (bd06e29)
Fresh clone, documented setup, seeded flower shop database:
Both are persisted, so a later GET returns the same values. messages behaves the same way, keeping the client content with server defaults filled in.
ucp, id, totals, status and links are excluded and come back server owned, which is the negative control showing the intended handling for this class.
Expected
At v2026-04-08 checkout.json marks continue_url, expires_at and messages as ucp_request omit. The merchant owns them on the response.
Root cause
create_checkout builds checkout_data at services/checkout_service.py line 263 with an exclusion set of fifteen names: line_items, payment, ucp, currency, id, status, totals, links, fulfillment, buyer, context, signals, attribution, cart_id and discounts. continue_url, expires_at and messages are not among them, so they arrive through **checkout_data at line 366.
Where it comes from
Same pitfall as the cart report filed alongside this one, and the same class as currency in #156 and id in #167. Under extra allow, a member the schema assigns to the merchant can still arrive from the wire.
Why CI did not catch it
No test sends these members, so the path never runs. The suite is green on this commit, 222 passed locally.