From 741494fec0d3c51b26ca64a28535c6982eeea46a Mon Sep 17 00:00:00 2001 From: cusell Date: Tue, 21 Apr 2026 11:45:19 +0200 Subject: [PATCH 1/5] fix: remove deprecated item metadata fields --- .../client/flower_shop/simple_happy_path_client.py | 14 +++++++------- rest/python/server/integration_test.py | 2 +- rest/python/server/services/checkout_service.py | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/rest/python/client/flower_shop/simple_happy_path_client.py b/rest/python/client/flower_shop/simple_happy_path_client.py index 5626855..55df41a 100644 --- a/rest/python/client/flower_shop/simple_happy_path_client.py +++ b/rest/python/client/flower_shop/simple_happy_path_client.py @@ -257,7 +257,7 @@ def main() -> None: # We start with one item: "Red Rose" item1 = item_create_req.ItemCreateRequest( - id="bouquet_roses", title="Red Rose" + id="bouquet_roses" ) line_item1 = line_item_create_req.LineItemCreateRequest( @@ -353,7 +353,7 @@ def main() -> None: # Update Item 1 (Roses) - Keep quantity 1 item1_update = item_update_req.ItemUpdateRequest( - id="bouquet_roses", title="Red Rose" + id="bouquet_roses" ) line_item1_update = line_item_update_req.LineItemUpdateRequest( @@ -365,7 +365,7 @@ def main() -> None: # Add Item 2 (Ceramic Pot) - Quantity 2 item2_update = item_update_req.ItemUpdateRequest( - id="pot_ceramic", title="Ceramic Pot" + id="pot_ceramic" ) line_item2_update = line_item_update_req.LineItemUpdateRequest( @@ -460,7 +460,7 @@ def main() -> None: ) item1_update = item_update_req.ItemUpdateRequest( - id="bouquet_roses", title="Red Rose" + id="bouquet_roses" ) line_item1_update = line_item_update_req.LineItemUpdateRequest( @@ -470,7 +470,7 @@ def main() -> None: ) item2_update = item_update_req.ItemUpdateRequest( - id="pot_ceramic", title="Ceramic Pot" + id="pot_ceramic" ) line_item2_update = line_item_update_req.LineItemUpdateRequest( @@ -571,7 +571,7 @@ def main() -> None: ) item1_update = item_update_req.ItemUpdateRequest( - id="bouquet_roses", title="Red Rose" + id="bouquet_roses" ) line_item1_update = line_item_update_req.LineItemUpdateRequest( @@ -581,7 +581,7 @@ def main() -> None: ) item2_update = item_update_req.ItemUpdateRequest( - id="pot_ceramic", title="Ceramic Pot" + id="pot_ceramic" ) line_item2_update = line_item_update_req.LineItemUpdateRequest( diff --git a/rest/python/server/integration_test.py b/rest/python/server/integration_test.py index 23a65e1..635c95b 100644 --- a/rest/python/server/integration_test.py +++ b/rest/python/server/integration_test.py @@ -228,7 +228,7 @@ def _create_checkout_payload( line_items = [] for item_id, item_title, item_price, quantity in items: item = item_create_req.ItemCreateRequest( - id=item_id, title=item_title, price=item_price + id=item_id ) line_item = line_item_create_req.LineItemCreateRequest( quantity=quantity, item=item diff --git a/rest/python/server/services/checkout_service.py b/rest/python/server/services/checkout_service.py index 4c38dbd..4539214 100644 --- a/rest/python/server/services/checkout_service.py +++ b/rest/python/server/services/checkout_service.py @@ -178,7 +178,7 @@ async def create_checkout( id=str(uuid.uuid4()), item=ItemResponse( id=li_req.item.id, - title=li_req.item.title, + title="", price=0, # Will be set by recalculate_totals ), quantity=li_req.quantity, @@ -424,7 +424,7 @@ async def update_checkout( id=li_req.id or str(uuid.uuid4()), item=ItemResponse( id=li_req.item.id, - title=li_req.item.title, + title="", price=0, ), quantity=li_req.quantity, From ad39c64a387b574f7fca4a102dbe572305541f40 Mon Sep 17 00:00:00 2001 From: cusell Date: Tue, 21 Apr 2026 12:27:38 +0200 Subject: [PATCH 2/5] style: fix ruff linting and formatting errors --- .../flower_shop/simple_happy_path_client.py | 28 +++++-------------- rest/python/server/integration_test.py | 6 ++-- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/rest/python/client/flower_shop/simple_happy_path_client.py b/rest/python/client/flower_shop/simple_happy_path_client.py index 55df41a..ec72a03 100644 --- a/rest/python/client/flower_shop/simple_happy_path_client.py +++ b/rest/python/client/flower_shop/simple_happy_path_client.py @@ -256,9 +256,7 @@ def main() -> None: # We start with one item: "Red Rose" - item1 = item_create_req.ItemCreateRequest( - id="bouquet_roses" - ) + item1 = item_create_req.ItemCreateRequest(id="bouquet_roses") line_item1 = line_item_create_req.LineItemCreateRequest( quantity=1, item=item1 @@ -352,9 +350,7 @@ def main() -> None: # Update Item 1 (Roses) - Keep quantity 1 - item1_update = item_update_req.ItemUpdateRequest( - id="bouquet_roses" - ) + item1_update = item_update_req.ItemUpdateRequest(id="bouquet_roses") line_item1_update = line_item_update_req.LineItemUpdateRequest( id=checkout_data["line_items"][0]["id"], @@ -364,9 +360,7 @@ def main() -> None: # Add Item 2 (Ceramic Pot) - Quantity 2 - item2_update = item_update_req.ItemUpdateRequest( - id="pot_ceramic" - ) + item2_update = item_update_req.ItemUpdateRequest(id="pot_ceramic") line_item2_update = line_item_update_req.LineItemUpdateRequest( quantity=2, @@ -459,9 +453,7 @@ def main() -> None: if li["item"]["id"] == "pot_ceramic" ) - item1_update = item_update_req.ItemUpdateRequest( - id="bouquet_roses" - ) + item1_update = item_update_req.ItemUpdateRequest(id="bouquet_roses") line_item1_update = line_item_update_req.LineItemUpdateRequest( id=li_1["id"], @@ -469,9 +461,7 @@ def main() -> None: item=item1_update, ) - item2_update = item_update_req.ItemUpdateRequest( - id="pot_ceramic" - ) + item2_update = item_update_req.ItemUpdateRequest(id="pot_ceramic") line_item2_update = line_item_update_req.LineItemUpdateRequest( id=li_2["id"], @@ -570,9 +560,7 @@ def main() -> None: if li["item"]["id"] == "pot_ceramic" ) - item1_update = item_update_req.ItemUpdateRequest( - id="bouquet_roses" - ) + item1_update = item_update_req.ItemUpdateRequest(id="bouquet_roses") line_item1_update = line_item_update_req.LineItemUpdateRequest( id=li_1["id"], @@ -580,9 +568,7 @@ def main() -> None: item=item1_update, ) - item2_update = item_update_req.ItemUpdateRequest( - id="pot_ceramic" - ) + item2_update = item_update_req.ItemUpdateRequest(id="pot_ceramic") line_item2_update = line_item_update_req.LineItemUpdateRequest( id=li_2["id"], diff --git a/rest/python/server/integration_test.py b/rest/python/server/integration_test.py index 635c95b..5c18abc 100644 --- a/rest/python/server/integration_test.py +++ b/rest/python/server/integration_test.py @@ -226,10 +226,8 @@ def _create_checkout_payload( ) -> checkout_create_req.CheckoutCreateRequest: """Create a checkout payload using SDK models.""" line_items = [] - for item_id, item_title, item_price, quantity in items: - item = item_create_req.ItemCreateRequest( - id=item_id - ) + for item_id, _item_title, _item_price, quantity in items: + item = item_create_req.ItemCreateRequest(id=item_id) line_item = line_item_create_req.LineItemCreateRequest( quantity=quantity, item=item ) From 05b61e56eb577312a17ff261e19693cd288fcb61 Mon Sep 17 00:00:00 2001 From: cusell-google Date: Thu, 23 Apr 2026 11:34:51 +0200 Subject: [PATCH 3/5] Fix Pydantic schema validation errors and request suffix in sample imports --- .../flower_shop/simple_happy_path_client.py | 79 ++++++++++--------- rest/python/server/integration_test.py | 3 +- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/rest/python/client/flower_shop/simple_happy_path_client.py b/rest/python/client/flower_shop/simple_happy_path_client.py index ec72a03..3f5fc4c 100644 --- a/rest/python/client/flower_shop/simple_happy_path_client.py +++ b/rest/python/client/flower_shop/simple_happy_path_client.py @@ -34,14 +34,14 @@ from pathlib import Path import uuid import httpx -from ucp_sdk.models.schemas.shopping import checkout_create_req -from ucp_sdk.models.schemas.shopping import checkout_update_req -from ucp_sdk.models.schemas.shopping import payment_create_req -from ucp_sdk.models.schemas.shopping.types import buyer -from ucp_sdk.models.schemas.shopping.types import item_create_req -from ucp_sdk.models.schemas.shopping.types import item_update_req -from ucp_sdk.models.schemas.shopping.types import line_item_create_req -from ucp_sdk.models.schemas.shopping.types import line_item_update_req +from ucp_sdk.models.schemas.shopping import checkout_create_request +from ucp_sdk.models.schemas.shopping import checkout_update_request +from ucp_sdk.models.schemas.shopping import payment_create_request +from ucp_sdk.models.schemas.shopping.types import buyer_create_request +from ucp_sdk.models.schemas.shopping.types import item_create_request +from ucp_sdk.models.schemas.shopping.types import item_update_request +from ucp_sdk.models.schemas.shopping.types import line_item_create_request +from ucp_sdk.models.schemas.shopping.types import line_item_update_request def get_headers() -> dict[str, str]: @@ -256,9 +256,9 @@ def main() -> None: # We start with one item: "Red Rose" - item1 = item_create_req.ItemCreateRequest(id="bouquet_roses") + item1 = item_create_request.ItemCreateRequest(id="bouquet_roses") - line_item1 = line_item_create_req.LineItemCreateRequest( + line_item1 = line_item_create_request.LineItemCreateRequest( quantity=1, item=item1 ) @@ -266,7 +266,7 @@ def main() -> None: # We do NOT select an instrument yet (selected_instrument_id=None). - payment_req = payment_create_req.PaymentCreateRequest( + payment_request = payment_create_request.PaymentCreateRequest( instruments=[], selected_instrument_id=None, handlers=supported_handlers, # Pass back what we found (or a subset) @@ -274,13 +274,13 @@ def main() -> None: # We include the buyer to trigger address lookup on the server - buyer_req = buyer.Buyer(full_name="John Doe", email="john.doe@example.com") + buyer_request = buyer_create_request.BuyerCreateRequest(full_name="John Doe", email="john.doe@example.com") - create_payload = checkout_create_req.CheckoutCreateRequest( + create_payload = checkout_create_request.CheckoutCreateRequest( currency="USD", line_items=[line_item1], - payment=payment_req, - buyer=buyer_req, + payment=payment_request, + buyer=buyer_request, ) headers = get_headers() @@ -350,9 +350,9 @@ def main() -> None: # Update Item 1 (Roses) - Keep quantity 1 - item1_update = item_update_req.ItemUpdateRequest(id="bouquet_roses") + item1_update = item_update_request.ItemUpdateRequest(id="bouquet_roses") - line_item1_update = line_item_update_req.LineItemUpdateRequest( + line_item1_update = line_item_update_request.LineItemUpdateRequest( id=checkout_data["line_items"][0]["id"], quantity=1, item=item1_update, @@ -360,16 +360,17 @@ def main() -> None: # Add Item 2 (Ceramic Pot) - Quantity 2 - item2_update = item_update_req.ItemUpdateRequest(id="pot_ceramic") + item2_update = item_update_request.ItemUpdateRequest(id="pot_ceramic") - line_item2_update = line_item_update_req.LineItemUpdateRequest( + line_item2_update = line_item_update_request.LineItemUpdateRequest( + id=str(uuid.uuid4()), quantity=2, item=item2_update, ) # Construct the Update Payload - update_payload = checkout_update_req.CheckoutUpdateRequest( + update_payload = checkout_update_request.CheckoutUpdateRequest( id=checkout_id, line_items=[line_item1_update, line_item2_update], currency=checkout_data["currency"], @@ -453,17 +454,17 @@ def main() -> None: if li["item"]["id"] == "pot_ceramic" ) - item1_update = item_update_req.ItemUpdateRequest(id="bouquet_roses") + item1_update = item_update_request.ItemUpdateRequest(id="bouquet_roses") - line_item1_update = line_item_update_req.LineItemUpdateRequest( + line_item1_update = line_item_update_request.LineItemUpdateRequest( id=li_1["id"], quantity=1, item=item1_update, ) - item2_update = item_update_req.ItemUpdateRequest(id="pot_ceramic") + item2_update = item_update_request.ItemUpdateRequest(id="pot_ceramic") - line_item2_update = line_item_update_req.LineItemUpdateRequest( + line_item2_update = line_item_update_request.LineItemUpdateRequest( id=li_2["id"], quantity=2, item=item2_update, @@ -471,7 +472,7 @@ def main() -> None: # Construct the Update Payload - update_payload = checkout_update_req.CheckoutUpdateRequest( + update_payload = checkout_update_request.CheckoutUpdateRequest( id=checkout_id, line_items=[line_item1_update, line_item2_update], currency=checkout_data["currency"], @@ -560,17 +561,17 @@ def main() -> None: if li["item"]["id"] == "pot_ceramic" ) - item1_update = item_update_req.ItemUpdateRequest(id="bouquet_roses") + item1_update = item_update_request.ItemUpdateRequest(id="bouquet_roses") - line_item1_update = line_item_update_req.LineItemUpdateRequest( + line_item1_update = line_item_update_request.LineItemUpdateRequest( id=li_1["id"], quantity=1, item=item1_update, ) - item2_update = item_update_req.ItemUpdateRequest(id="pot_ceramic") + item2_update = item_update_request.ItemUpdateRequest(id="pot_ceramic") - line_item2_update = line_item_update_req.LineItemUpdateRequest( + line_item2_update = line_item_update_request.LineItemUpdateRequest( id=li_2["id"], quantity=2, item=item2_update, @@ -578,15 +579,15 @@ def main() -> None: # Construct full update payload - trigger_req = checkout_update_req.CheckoutUpdateRequest( + trigger_request = checkout_update_request.CheckoutUpdateRequest( id=checkout_id, line_items=[line_item1_update, line_item2_update], currency=checkout_data["currency"], payment=checkout_data["payment"], - fulfillment={"methods": [{"type": "shipping"}]}, + fulfillment={"methods": [{"id": "method_1", "type": "shipping", "line_item_ids": [li_1["id"], li_2["id"]]}]}, ) - trigger_payload = trigger_req.model_dump( + trigger_payload = trigger_request.model_dump( mode="json", by_alias=True, exclude_none=True ) @@ -662,11 +663,11 @@ def main() -> None: # We must send full payload again - trigger_req.fulfillment = { - "methods": [{"type": "shipping", "selected_destination_id": dest_id}] + trigger_request.fulfillment = { + "methods": [{"id": "method_1", "type": "shipping", "line_item_ids": [li_1["id"], li_2["id"]], "selected_destination_id": dest_id}] } - payload = trigger_req.model_dump( + payload = trigger_request.model_dump( mode="json", by_alias=True, exclude_none=True ) @@ -708,17 +709,19 @@ def main() -> None: logger.info("STEP 6: Selecting option: %s", option_id) - trigger_req.fulfillment = { + trigger_request.fulfillment = { "methods": [ { + "id": "method_1", "type": "shipping", + "line_item_ids": [li_1["id"], li_2["id"]], "selected_destination_id": dest_id, - "groups": [{"selected_option_id": option_id}], + "groups": [{"id": "group_1", "line_item_ids": [li_1["id"], li_2["id"]], "selected_option_id": option_id}], } ] } - payload = trigger_req.model_dump( + payload = trigger_request.model_dump( mode="json", by_alias=True, exclude_none=True ) diff --git a/rest/python/server/integration_test.py b/rest/python/server/integration_test.py index 5c18abc..b8d668d 100644 --- a/rest/python/server/integration_test.py +++ b/rest/python/server/integration_test.py @@ -26,7 +26,7 @@ import db import dependencies from fastapi.testclient import TestClient -from server import app +from server.server import app from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import create_async_engine from sqlalchemy.orm import sessionmaker @@ -90,6 +90,7 @@ class IntegrationTest(absltest.TestCase): """Integration tests for the UCP server application.""" def setUp(self) -> None: + flags.FLAGS(["test"]) """Set up the test environment, including temporary DBs and dependencies.""" super().setUp() # Create a temporary directory for test databases From e81601e177b0cadeff02a7a019d962a42afc6c94 Mon Sep 17 00:00:00 2001 From: cusell-google Date: Thu, 23 Apr 2026 11:39:20 +0200 Subject: [PATCH 4/5] Auto-format and correct missing docstring to pass linting --- .../flower_shop/simple_happy_path_client.py | 31 ++++++++++++++++--- rest/python/server/integration_test.py | 2 +- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/rest/python/client/flower_shop/simple_happy_path_client.py b/rest/python/client/flower_shop/simple_happy_path_client.py index 3f5fc4c..d3abafe 100644 --- a/rest/python/client/flower_shop/simple_happy_path_client.py +++ b/rest/python/client/flower_shop/simple_happy_path_client.py @@ -274,7 +274,9 @@ def main() -> None: # We include the buyer to trigger address lookup on the server - buyer_request = buyer_create_request.BuyerCreateRequest(full_name="John Doe", email="john.doe@example.com") + buyer_request = buyer_create_request.BuyerCreateRequest( + full_name="John Doe", email="john.doe@example.com" + ) create_payload = checkout_create_request.CheckoutCreateRequest( currency="USD", @@ -584,7 +586,15 @@ def main() -> None: line_items=[line_item1_update, line_item2_update], currency=checkout_data["currency"], payment=checkout_data["payment"], - fulfillment={"methods": [{"id": "method_1", "type": "shipping", "line_item_ids": [li_1["id"], li_2["id"]]}]}, + fulfillment={ + "methods": [ + { + "id": "method_1", + "type": "shipping", + "line_item_ids": [li_1["id"], li_2["id"]], + } + ] + }, ) trigger_payload = trigger_request.model_dump( @@ -664,7 +674,14 @@ def main() -> None: # We must send full payload again trigger_request.fulfillment = { - "methods": [{"id": "method_1", "type": "shipping", "line_item_ids": [li_1["id"], li_2["id"]], "selected_destination_id": dest_id}] + "methods": [ + { + "id": "method_1", + "type": "shipping", + "line_item_ids": [li_1["id"], li_2["id"]], + "selected_destination_id": dest_id, + } + ] } payload = trigger_request.model_dump( @@ -716,7 +733,13 @@ def main() -> None: "type": "shipping", "line_item_ids": [li_1["id"], li_2["id"]], "selected_destination_id": dest_id, - "groups": [{"id": "group_1", "line_item_ids": [li_1["id"], li_2["id"]], "selected_option_id": option_id}], + "groups": [ + { + "id": "group_1", + "line_item_ids": [li_1["id"], li_2["id"]], + "selected_option_id": option_id, + } + ], } ] } diff --git a/rest/python/server/integration_test.py b/rest/python/server/integration_test.py index b8d668d..c4f3186 100644 --- a/rest/python/server/integration_test.py +++ b/rest/python/server/integration_test.py @@ -90,8 +90,8 @@ class IntegrationTest(absltest.TestCase): """Integration tests for the UCP server application.""" def setUp(self) -> None: - flags.FLAGS(["test"]) """Set up the test environment, including temporary DBs and dependencies.""" + flags.FLAGS(["test"]) super().setUp() # Create a temporary directory for test databases self.test_dir = Path(tempfile.mkdtemp()) From 9ff702388687b453eda13c22fab59acaa8b8c9c1 Mon Sep 17 00:00:00 2001 From: cusell-google Date: Thu, 23 Apr 2026 11:43:54 +0200 Subject: [PATCH 5/5] Fix discovery payment_handlers parsing to resolve Bug #87 Point 1 --- rest/python/client/flower_shop/simple_happy_path_client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rest/python/client/flower_shop/simple_happy_path_client.py b/rest/python/client/flower_shop/simple_happy_path_client.py index d3abafe..6859332 100644 --- a/rest/python/client/flower_shop/simple_happy_path_client.py +++ b/rest/python/client/flower_shop/simple_happy_path_client.py @@ -237,7 +237,9 @@ def main() -> None: discovery_data = response.json() - supported_handlers = discovery_data.get("payment", {}).get("handlers", []) + supported_handlers = [] + for handlers in discovery_data.get("payment_handlers", {}).values(): + supported_handlers.extend(handlers) logger.info( "Merchant supports %d payment handlers:", len(supported_handlers)