From bbf422aa5fd5790630a837c0e6ad018b9290dbe7 Mon Sep 17 00:00:00 2001 From: "Patrick R. Jordan" Date: Thu, 16 Apr 2026 11:05:07 -0700 Subject: [PATCH] feat: update models to UCP release 2026-04-08 Regenerate Pydantic models from UCP specification release 2026-04-08 using `./generate_models.sh 2026-04-08`. This is a significant update from the previous 2026-01-23 release. Key changes include: - New Cart capability models (cart, cart_create/update_request) - New Catalog models (catalog_lookup, catalog_search) - New Order request variants (order_create/update_request) - Restructured ap2_mandate, buyer_consent, discount as packages - Updated type models: amount, signed_amount, variant, product, etc. - Error handling models (error_code, error_response) - Signals, pagination, media, and other new type models --- src/ucp_sdk/models/schemas/__init__.py | 1 + src/ucp_sdk/models/schemas/capability.py | 132 ++++++- src/ucp_sdk/models/schemas/payment_handler.py | 26 ++ src/ucp_sdk/models/schemas/service.py | 28 +- .../models/schemas/shopping/__init__.py | 1 + .../__init__.py} | 34 -- .../shopping/ap2_mandate/dev/__init__.py | 18 + .../shopping/ap2_mandate/dev/ucp/__init__.py | 18 + .../shopping/ap2_mandate/dev/ucp/shopping.py | 56 +++ .../__init__.py} | 17 +- .../shopping/buyer_consent/dev/__init__.py | 18 + .../buyer_consent/dev/ucp/__init__.py | 18 + .../buyer_consent/dev/ucp/shopping.py | 38 ++ src/ucp_sdk/models/schemas/shopping/cart.py | 95 +++++ .../schemas/shopping/cart_create_request.py | 66 ++++ .../schemas/shopping/cart_update_request.py | 70 ++++ .../models/schemas/shopping/catalog_lookup.py | 171 +++++++++ .../models/schemas/shopping/catalog_search.py | 68 ++++ .../models/schemas/shopping/checkout.py | 7 +- .../shopping/checkout_complete_request.py | 2 + .../shopping/checkout_create_request.py | 2 + .../shopping/checkout_update_request.py | 6 +- .../{discount.py => discount/__init__.py} | 32 +- .../schemas/shopping/discount/dev/__init__.py | 18 + .../shopping/discount/dev/ucp/__init__.py | 18 + .../shopping/discount/dev/ucp/shopping.py | 47 +++ .../schemas/shopping/fulfillment/__init__.py | 21 +- .../shopping/fulfillment/dev/__init__.py | 1 + .../shopping/fulfillment/dev/ucp/__init__.py | 1 + .../shopping/fulfillment/dev/ucp/shopping.py | 17 + src/ucp_sdk/models/schemas/shopping/order.py | 23 +- .../schemas/shopping/order_create_request.py | 117 ++++++ .../schemas/shopping/order_update_request.py | 117 ++++++ .../models/schemas/shopping/types/__init__.py | 1 + .../schemas/shopping/types/adjustment.py | 14 +- .../types/adjustment_create_request.py | 77 ++++ .../types/adjustment_update_request.py | 77 ++++ .../models/schemas/shopping/types/amount.py | 31 ++ .../types/available_payment_instrument.py | 41 ++ .../shopping/types/card_payment_instrument.py | 25 +- .../models/schemas/shopping/types/category.py | 39 ++ .../models/schemas/shopping/types/context.py | 20 +- .../shopping/types/context_create_request.py | 23 +- .../shopping/types/context_update_request.py | 23 +- .../schemas/shopping/types/description.py | 43 +++ .../shopping/types/detail_option_value.py | 41 ++ .../schemas/shopping/types/error_code.py | 42 +++ .../schemas/shopping/types/error_response.py | 46 +++ .../types/expectation_create_request.py | 73 ++++ .../types/expectation_update_request.py | 73 ++++ .../types/fulfillment_event_create_request.py | 77 ++++ .../types/fulfillment_event_update_request.py | 77 ++++ .../fulfillment_method_update_request.py | 6 + .../shopping/types/input_correlation.py | 39 ++ .../models/schemas/shopping/types/item.py | 10 +- .../shopping/types/item_create_request.py | 2 +- .../shopping/types/item_update_request.py | 2 +- .../models/schemas/shopping/types/media.py | 51 +++ .../shopping/types/message_create_request.py | 43 +++ .../schemas/shopping/types/message_error.py | 14 +- .../shopping/types/message_update_request.py | 43 +++ .../schemas/shopping/types/message_warning.py | 14 +- .../schemas/shopping/types/option_value.py | 39 ++ .../shopping/types/order_confirmation.py | 4 + .../schemas/shopping/types/order_line_item.py | 16 +- .../types/order_line_item_create_request.py | 77 ++++ .../types/order_line_item_update_request.py | 77 ++++ .../schemas/shopping/types/pagination.py | 71 ++++ .../models/schemas/shopping/types/price.py | 41 ++ .../schemas/shopping/types/price_filter.py | 41 ++ .../schemas/shopping/types/price_range.py | 41 ++ .../models/schemas/shopping/types/product.py | 97 +++++ .../schemas/shopping/types/product_option.py | 41 ++ .../models/schemas/shopping/types/rating.py | 47 +++ .../shopping/types/reverse_domain_name.py | 35 ++ .../reverse_domain_name_create_request.py | 35 ++ .../reverse_domain_name_update_request.py | 35 ++ .../schemas/shopping/types/search_filters.py | 38 ++ .../schemas/shopping/types/selected_option.py | 43 +++ .../models/schemas/shopping/types/signals.py | 39 ++ .../types/signals_complete_request.py | 39 ++ .../shopping/types/signals_create_request.py | 39 ++ .../shopping/types/signals_update_request.py | 39 ++ .../schemas/shopping/types/signed_amount.py | 31 ++ .../models/schemas/shopping/types/total.py | 25 +- .../shopping/types/total_create_request.py | 4 + .../shopping/types/total_update_request.py | 4 + .../models/schemas/shopping/types/totals.py | 63 ++++ .../shopping/types/totals_create_request.py | 77 ++++ .../shopping/types/totals_update_request.py | 77 ++++ .../models/schemas/shopping/types/variant.py | 226 +++++++++++ .../models/schemas/transports/__init__.py | 1 + .../schemas/transports/embedded_config.py | 10 +- src/ucp_sdk/models/schemas/ucp.py | 184 +++++++-- .../models/schemas/ucp_create_request.py | 357 ++++++++++++++++++ .../models/schemas/ucp_update_request.py | 357 ++++++++++++++++++ 96 files changed, 4523 insertions(+), 198 deletions(-) rename src/ucp_sdk/models/schemas/shopping/{ap2_mandate.py => ap2_mandate/__init__.py} (82%) create mode 100644 src/ucp_sdk/models/schemas/shopping/ap2_mandate/dev/__init__.py create mode 100644 src/ucp_sdk/models/schemas/shopping/ap2_mandate/dev/ucp/__init__.py create mode 100644 src/ucp_sdk/models/schemas/shopping/ap2_mandate/dev/ucp/shopping.py rename src/ucp_sdk/models/schemas/shopping/{buyer_consent.py => buyer_consent/__init__.py} (84%) create mode 100644 src/ucp_sdk/models/schemas/shopping/buyer_consent/dev/__init__.py create mode 100644 src/ucp_sdk/models/schemas/shopping/buyer_consent/dev/ucp/__init__.py create mode 100644 src/ucp_sdk/models/schemas/shopping/buyer_consent/dev/ucp/shopping.py create mode 100644 src/ucp_sdk/models/schemas/shopping/cart.py create mode 100644 src/ucp_sdk/models/schemas/shopping/cart_create_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/cart_update_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/catalog_lookup.py create mode 100644 src/ucp_sdk/models/schemas/shopping/catalog_search.py rename src/ucp_sdk/models/schemas/shopping/{discount.py => discount/__init__.py} (78%) create mode 100644 src/ucp_sdk/models/schemas/shopping/discount/dev/__init__.py create mode 100644 src/ucp_sdk/models/schemas/shopping/discount/dev/ucp/__init__.py create mode 100644 src/ucp_sdk/models/schemas/shopping/discount/dev/ucp/shopping.py create mode 100644 src/ucp_sdk/models/schemas/shopping/order_create_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/order_update_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/adjustment_create_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/adjustment_update_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/amount.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/available_payment_instrument.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/category.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/description.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/detail_option_value.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/error_code.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/error_response.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/expectation_create_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/expectation_update_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/fulfillment_event_create_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/fulfillment_event_update_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/input_correlation.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/media.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/message_create_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/message_update_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/option_value.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/order_line_item_create_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/order_line_item_update_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/pagination.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/price.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/price_filter.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/price_range.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/product.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/product_option.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/rating.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/reverse_domain_name.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/reverse_domain_name_create_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/reverse_domain_name_update_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/search_filters.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/selected_option.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/signals.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/signals_complete_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/signals_create_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/signals_update_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/signed_amount.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/totals.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/totals_create_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/totals_update_request.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/variant.py create mode 100644 src/ucp_sdk/models/schemas/ucp_create_request.py create mode 100644 src/ucp_sdk/models/schemas/ucp_update_request.py diff --git a/src/ucp_sdk/models/schemas/__init__.py b/src/ucp_sdk/models/schemas/__init__.py index 1252d6b..421dc21 100644 --- a/src/ucp_sdk/models/schemas/__init__.py +++ b/src/ucp_sdk/models/schemas/__init__.py @@ -15,3 +15,4 @@ # generated by datamodel-codegen # pylint: disable=all # pyformat: disable + diff --git a/src/ucp_sdk/models/schemas/capability.py b/src/ucp_sdk/models/schemas/capability.py index 3e326ff..06c378c 100644 --- a/src/ucp_sdk/models/schemas/capability.py +++ b/src/ucp_sdk/models/schemas/capability.py @@ -33,6 +33,114 @@ class UcpCapability(RootModel[Any]): """ +class Extends(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field(..., pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$") + """ + Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions. + """ + + +class Extends1Item(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field(..., pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$") + + +class Extends1(RootModel[list[Extends1Item]]): + model_config = ConfigDict( + frozen=True, + ) + root: list[Extends1Item] = Field(..., min_length=1) + """ + Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions. + """ + + +class Extends2(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field(..., pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$") + """ + Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions. + """ + + +class Extends3Item(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field(..., pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$") + + +class Extends3(RootModel[list[Extends3Item]]): + model_config = ConfigDict( + frozen=True, + ) + root: list[Extends3Item] = Field(..., min_length=1) + """ + Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions. + """ + + +class Extends4(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field(..., pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$") + """ + Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions. + """ + + +class Extends5Item(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field(..., pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$") + + +class Extends5(RootModel[list[Extends5Item]]): + model_config = ConfigDict( + frozen=True, + ) + root: list[Extends5Item] = Field(..., min_length=1) + """ + Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions. + """ + + +class Extends6(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field(..., pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$") + """ + Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions. + """ + + +class Extends7Item(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field(..., pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$") + + +class Extends7(RootModel[list[Extends7Item]]): + model_config = ConfigDict( + frozen=True, + ) + root: list[Extends7Item] = Field(..., min_length=1) + """ + Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions. + """ + + class Version(RootModel[Any]): model_config = ConfigDict( frozen=True, @@ -64,11 +172,9 @@ class Base(BaseModel): """ Entity-specific configuration. Structure defined by each entity's schema. """ - extends: str | None = Field( - None, pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$" - ) + extends: Extends | Extends1 | None = None """ - Parent capability this extends. Present for extensions, absent for root capabilities. + Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions. """ @@ -100,11 +206,9 @@ class PlatformSchema(BaseModel): """ Entity-specific configuration. Structure defined by each entity's schema. """ - extends: str | None = Field( - None, pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$" - ) + extends: Extends2 | Extends3 | None = None """ - Parent capability this extends. Present for extensions, absent for root capabilities. + Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions. """ @@ -136,11 +240,9 @@ class BusinessSchema(BaseModel): """ Entity-specific configuration. Structure defined by each entity's schema. """ - extends: str | None = Field( - None, pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$" - ) + extends: Extends4 | Extends5 | None = None """ - Parent capability this extends. Present for extensions, absent for root capabilities. + Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions. """ @@ -172,9 +274,7 @@ class ResponseSchema(BaseModel): """ Entity-specific configuration. Structure defined by each entity's schema. """ - extends: str | None = Field( - None, pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$" - ) + extends: Extends6 | Extends7 | None = None """ - Parent capability this extends. Present for extensions, absent for root capabilities. + Parent capability(s) this extends. Present for extensions, absent for root capabilities. Use array for multi-parent extensions. """ diff --git a/src/ucp_sdk/models/schemas/payment_handler.py b/src/ucp_sdk/models/schemas/payment_handler.py index c51161c..1e3d4e5 100644 --- a/src/ucp_sdk/models/schemas/payment_handler.py +++ b/src/ucp_sdk/models/schemas/payment_handler.py @@ -22,6 +22,8 @@ from pydantic import AnyUrl, BaseModel, ConfigDict, Field, RootModel +from .shopping.types import available_payment_instrument + class PaymentHandler(RootModel[Any]): model_config = ConfigDict( @@ -64,6 +66,12 @@ class Base(BaseModel): """ Entity-specific configuration. Structure defined by each entity's schema. """ + available_instruments: ( + list[available_payment_instrument.AvailablePaymentInstrument] | None + ) = Field(None, min_length=1) + """ + Instrument types this handler supports, with optional constraints. When absent, every instrument should be considered available. + """ class PlatformSchema(BaseModel): @@ -94,6 +102,12 @@ class PlatformSchema(BaseModel): """ Entity-specific configuration. Structure defined by each entity's schema. """ + available_instruments: ( + list[available_payment_instrument.AvailablePaymentInstrument] | None + ) = Field(None, min_length=1) + """ + Instrument types this handler supports, with optional constraints. When absent, every instrument should be considered available. + """ class BusinessSchema(BaseModel): @@ -124,6 +138,12 @@ class BusinessSchema(BaseModel): """ Entity-specific configuration. Structure defined by each entity's schema. """ + available_instruments: ( + list[available_payment_instrument.AvailablePaymentInstrument] | None + ) = Field(None, min_length=1) + """ + Instrument types this handler supports, with optional constraints. When absent, every instrument should be considered available. + """ class ResponseSchema(BaseModel): @@ -154,3 +174,9 @@ class ResponseSchema(BaseModel): """ Entity-specific configuration. Structure defined by each entity's schema. """ + available_instruments: ( + list[available_payment_instrument.AvailablePaymentInstrument] | None + ) = Field(None, min_length=1) + """ + Instrument types this handler supports, with optional constraints. When absent, every instrument should be considered available. + """ diff --git a/src/ucp_sdk/models/schemas/service.py b/src/ucp_sdk/models/schemas/service.py index 2a91c36..b5810ad 100644 --- a/src/ucp_sdk/models/schemas/service.py +++ b/src/ucp_sdk/models/schemas/service.py @@ -43,7 +43,11 @@ class Config(BaseModel): ) delegate: list[str] | None = None """ - Delegations the business allows. At service-level, declares available delegations. In checkout responses, confirms accepted delegations for this session. + Delegations the business allows. At service-level, declares available delegations. In UCP responses, confirms accepted delegations for this session. + """ + color_scheme: list[Literal["light", "dark"]] | None = None + """ + Color schemes the business supports. Hosts use ec_color_scheme query parameter to request a scheme from this list. """ @@ -90,7 +94,7 @@ class Base(BaseModel): class PlatformSchema(BaseModel): """ - Full service declaration for platform-level discovery. Different transports require different fields. + Full service declaration for platform-level discovery. All transports require `version`, `spec`, and `transport`. REST and MCP additionally require `schema` and `endpoint`; A2A requires `endpoint`; embedded requires `schema`. """ model_config = ConfigDict( @@ -126,9 +130,9 @@ class PlatformSchema(BaseModel): """ -class PlatformSchema5(BaseModel): +class PlatformSchema7(BaseModel): """ - Full service declaration for platform-level discovery. Different transports require different fields. + Full service declaration for platform-level discovery. All transports require `version`, `spec`, and `transport`. REST and MCP additionally require `schema` and `endpoint`; A2A requires `endpoint`; embedded requires `schema`. """ model_config = ConfigDict( @@ -164,9 +168,9 @@ class PlatformSchema5(BaseModel): """ -class PlatformSchema6(BaseModel): +class PlatformSchema8(BaseModel): """ - Full service declaration for platform-level discovery. Different transports require different fields. + Full service declaration for platform-level discovery. All transports require `version`, `spec`, and `transport`. REST and MCP additionally require `schema` and `endpoint`; A2A requires `endpoint`; embedded requires `schema`. """ model_config = ConfigDict( @@ -202,9 +206,9 @@ class PlatformSchema6(BaseModel): """ -class PlatformSchema7(BaseModel): +class PlatformSchema9(BaseModel): """ - Full service declaration for platform-level discovery. Different transports require different fields. + Full service declaration for platform-level discovery. All transports require `version`, `spec`, and `transport`. REST and MCP additionally require `schema` and `endpoint`; A2A requires `endpoint`; embedded requires `schema`. """ model_config = ConfigDict( @@ -240,19 +244,19 @@ class PlatformSchema7(BaseModel): """ -class PlatformSchema3( +class PlatformSchema5( RootModel[ - PlatformSchema | PlatformSchema5 | PlatformSchema6 | PlatformSchema7 + PlatformSchema | PlatformSchema7 | PlatformSchema8 | PlatformSchema9 ] ): model_config = ConfigDict( frozen=True, ) root: ( - PlatformSchema | PlatformSchema5 | PlatformSchema6 | PlatformSchema7 + PlatformSchema | PlatformSchema7 | PlatformSchema8 | PlatformSchema9 ) = Field(..., title="Service (Platform Schema)") """ - Full service declaration for platform-level discovery. Different transports require different fields. + Full service declaration for platform-level discovery. All transports require `version`, `spec`, and `transport`. REST and MCP additionally require `schema` and `endpoint`; A2A requires `endpoint`; embedded requires `schema`. """ diff --git a/src/ucp_sdk/models/schemas/shopping/__init__.py b/src/ucp_sdk/models/schemas/shopping/__init__.py index 1252d6b..421dc21 100644 --- a/src/ucp_sdk/models/schemas/shopping/__init__.py +++ b/src/ucp_sdk/models/schemas/shopping/__init__.py @@ -15,3 +15,4 @@ # generated by datamodel-codegen # pylint: disable=all # pyformat: disable + diff --git a/src/ucp_sdk/models/schemas/shopping/ap2_mandate.py b/src/ucp_sdk/models/schemas/shopping/ap2_mandate/__init__.py similarity index 82% rename from src/ucp_sdk/models/schemas/shopping/ap2_mandate.py rename to src/ucp_sdk/models/schemas/shopping/ap2_mandate/__init__.py index a76c1d8..5a87c76 100644 --- a/src/ucp_sdk/models/schemas/shopping/ap2_mandate.py +++ b/src/ucp_sdk/models/schemas/shopping/ap2_mandate/__init__.py @@ -22,8 +22,6 @@ from pydantic import BaseModel, ConfigDict, Field, RootModel -from .checkout import Checkout as Checkout_1 - class Ap2MandateExtension(RootModel[Any]): model_config = ConfigDict( @@ -91,24 +89,6 @@ class Ap2WithCheckoutMandate(BaseModel): """ -class Ap2(BaseModel): - """ - AP2 extension data including merchant authorization. - """ - - model_config = ConfigDict( - extra="allow", - ) - merchant_authorization: MerchantAuthorization | None = None - """ - Merchant's signature proving checkout terms are authentic. - """ - checkout_mandate: CheckoutMandate | None = None - """ - SD-JWT+kb proving user authorized this checkout. - """ - - class ErrorCode( RootModel[ Literal[ @@ -137,17 +117,3 @@ class ErrorCode( """ Error codes specific to AP2 mandate verification. """ - - -class Checkout(Checkout_1): - """ - Checkout extended with AP2 mandate support. - """ - - model_config = ConfigDict( - extra="allow", - ) - ap2: Ap2 | None = None - """ - AP2 extension data including merchant authorization. - """ diff --git a/src/ucp_sdk/models/schemas/shopping/ap2_mandate/dev/__init__.py b/src/ucp_sdk/models/schemas/shopping/ap2_mandate/dev/__init__.py new file mode 100644 index 0000000..421dc21 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/ap2_mandate/dev/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + diff --git a/src/ucp_sdk/models/schemas/shopping/ap2_mandate/dev/ucp/__init__.py b/src/ucp_sdk/models/schemas/shopping/ap2_mandate/dev/ucp/__init__.py new file mode 100644 index 0000000..421dc21 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/ap2_mandate/dev/ucp/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + diff --git a/src/ucp_sdk/models/schemas/shopping/ap2_mandate/dev/ucp/shopping.py b/src/ucp_sdk/models/schemas/shopping/ap2_mandate/dev/ucp/shopping.py new file mode 100644 index 0000000..312a17a --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/ap2_mandate/dev/ucp/shopping.py @@ -0,0 +1,56 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict + +from ....checkout import Checkout as Checkout_1 +from ... import CheckoutMandate, MerchantAuthorization + + +class Ap2(BaseModel): + """ + AP2 extension data including merchant authorization. + """ + + model_config = ConfigDict( + extra="allow", + ) + merchant_authorization: MerchantAuthorization | None = None + """ + Merchant's signature proving checkout terms are authentic. + """ + checkout_mandate: CheckoutMandate | None = None + """ + SD-JWT+kb proving user authorized this checkout. + """ + + +class Checkout(Checkout_1): + """ + Checkout extended with AP2 mandate support. + """ + + model_config = ConfigDict( + extra="allow", + ) + ap2: Ap2 | None = None + """ + AP2 extension data including merchant authorization. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/buyer_consent.py b/src/ucp_sdk/models/schemas/shopping/buyer_consent/__init__.py similarity index 84% rename from src/ucp_sdk/models/schemas/shopping/buyer_consent.py rename to src/ucp_sdk/models/schemas/shopping/buyer_consent/__init__.py index 4162d93..c23994e 100644 --- a/src/ucp_sdk/models/schemas/shopping/buyer_consent.py +++ b/src/ucp_sdk/models/schemas/shopping/buyer_consent/__init__.py @@ -22,8 +22,7 @@ from pydantic import BaseModel, ConfigDict, Field, RootModel -from .checkout import Checkout as Checkout_1 -from .types.buyer import Buyer as Buyer_1 +from ..types.buyer import Buyer as Buyer_1 class BuyerConsentExtension(RootModel[Any]): @@ -74,17 +73,3 @@ class Buyer(Buyer_1): """ Consent tracking fields. """ - - -class Checkout(Checkout_1): - """ - Checkout extended with consent tracking via buyer object. - """ - - model_config = ConfigDict( - extra="allow", - ) - buyer: Buyer | None = None - """ - Buyer with consent tracking. - """ diff --git a/src/ucp_sdk/models/schemas/shopping/buyer_consent/dev/__init__.py b/src/ucp_sdk/models/schemas/shopping/buyer_consent/dev/__init__.py new file mode 100644 index 0000000..421dc21 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/buyer_consent/dev/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + diff --git a/src/ucp_sdk/models/schemas/shopping/buyer_consent/dev/ucp/__init__.py b/src/ucp_sdk/models/schemas/shopping/buyer_consent/dev/ucp/__init__.py new file mode 100644 index 0000000..421dc21 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/buyer_consent/dev/ucp/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + diff --git a/src/ucp_sdk/models/schemas/shopping/buyer_consent/dev/ucp/shopping.py b/src/ucp_sdk/models/schemas/shopping/buyer_consent/dev/ucp/shopping.py new file mode 100644 index 0000000..6b59298 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/buyer_consent/dev/ucp/shopping.py @@ -0,0 +1,38 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import ConfigDict + +from ....checkout import Checkout as Checkout_1 +from ... import Buyer + + +class Checkout(Checkout_1): + """ + Checkout extended with consent tracking via buyer object. + """ + + model_config = ConfigDict( + extra="allow", + ) + buyer: Buyer | None = None + """ + Buyer with consent tracking. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/cart.py b/src/ucp_sdk/models/schemas/shopping/cart.py new file mode 100644 index 0000000..c7e5267 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/cart.py @@ -0,0 +1,95 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict + +from .. import ucp as ucp_1 +from .checkout import Checkout as Checkout_1 +from .types import buyer as buyer_1 +from .types import context as context_1 +from .types import line_item, link, message +from .types import signals as signals_1 +from .types import totals as totals_1 + + +class Cart(BaseModel): + """ + Shopping cart with estimated pricing before checkout. Lightweight pre-purchase exploration with no payment info or complex status states. + """ + + model_config = ConfigDict( + extra="allow", + ) + ucp: ucp_1.UcpMetadata + id: str + """ + Unique cart identifier. + """ + line_items: list[line_item.LineItem] + """ + Cart line items. Same structure as checkout. Full replacement on update. + """ + context: context_1.Context | None = None + """ + Buyer signals for localization (country, region, postal_code). Merchant uses for pricing, availability, currency. Falls back to geo-IP if omitted. + """ + signals: signals_1.Signals | None = None + buyer: buyer_1.Buyer | None = None + """ + Optional buyer information for personalized estimates. + """ + currency: str + """ + ISO 4217 currency code. Determined by merchant based on context or geo-IP. + """ + totals: totals_1.Totals + """ + Estimated cost breakdown. May be partial if shipping/tax not yet calculable. + """ + messages: list[message.Message] | None = None + """ + Validation messages, warnings, or informational notices. + """ + links: list[link.Link] | None = None + """ + Optional merchant links (policies, FAQs). + """ + continue_url: AnyUrl | None = None + """ + URL for cart handoff and session recovery. Enables sharing and human-in-the-loop flows. + """ + expires_at: AwareDatetime | None = None + """ + Cart expiry timestamp (RFC 3339). Optional. + """ + + +class Checkout(Checkout_1): + """ + Checkout extended with cart capability. Adds cart_id to create_checkout for cart-to-checkout conversion. + """ + + model_config = ConfigDict( + extra="allow", + ) + cart_id: str | None = None + """ + Cart ID to convert to checkout. Business MUST use cart contents (line_items, context, buyer) and MUST ignore overlapping fields in checkout payload. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/cart_create_request.py b/src/ucp_sdk/models/schemas/shopping/cart_create_request.py new file mode 100644 index 0000000..10cd5db --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/cart_create_request.py @@ -0,0 +1,66 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict + +from .checkout import Checkout as Checkout_1 +from .types import ( + buyer_create_request, + context_create_request, + line_item_create_request, + signals_create_request, +) + + +class CartCreateRequest(BaseModel): + """ + Shopping cart with estimated pricing before checkout. Lightweight pre-purchase exploration with no payment info or complex status states. + """ + + model_config = ConfigDict( + extra="allow", + ) + line_items: list[line_item_create_request.LineItemCreateRequest] + """ + Cart line items. Same structure as checkout. Full replacement on update. + """ + context: context_create_request.ContextCreateRequest | None = None + """ + Buyer signals for localization (country, region, postal_code). Merchant uses for pricing, availability, currency. Falls back to geo-IP if omitted. + """ + signals: signals_create_request.SignalsCreateRequest | None = None + buyer: buyer_create_request.BuyerCreateRequest | None = None + """ + Optional buyer information for personalized estimates. + """ + + +class Checkout(Checkout_1): + """ + Checkout extended with cart capability. Adds cart_id to create_checkout for cart-to-checkout conversion. + """ + + model_config = ConfigDict( + extra="allow", + ) + cart_id: str | None = None + """ + Cart ID to convert to checkout. Business MUST use cart contents (line_items, context, buyer) and MUST ignore overlapping fields in checkout payload. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/cart_update_request.py b/src/ucp_sdk/models/schemas/shopping/cart_update_request.py new file mode 100644 index 0000000..938639d --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/cart_update_request.py @@ -0,0 +1,70 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict + +from .checkout import Checkout as Checkout_1 +from .types import ( + buyer_update_request, + context_update_request, + line_item_update_request, + signals_update_request, +) + + +class CartUpdateRequest(BaseModel): + """ + Shopping cart with estimated pricing before checkout. Lightweight pre-purchase exploration with no payment info or complex status states. + """ + + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Unique cart identifier. + """ + line_items: list[line_item_update_request.LineItemUpdateRequest] + """ + Cart line items. Same structure as checkout. Full replacement on update. + """ + context: context_update_request.ContextUpdateRequest | None = None + """ + Buyer signals for localization (country, region, postal_code). Merchant uses for pricing, availability, currency. Falls back to geo-IP if omitted. + """ + signals: signals_update_request.SignalsUpdateRequest | None = None + buyer: buyer_update_request.BuyerUpdateRequest | None = None + """ + Optional buyer information for personalized estimates. + """ + + +class Checkout(Checkout_1): + """ + Checkout extended with cart capability. Adds cart_id to create_checkout for cart-to-checkout conversion. + """ + + model_config = ConfigDict( + extra="allow", + ) + cart_id: str | None = None + """ + Cart ID to convert to checkout. Business MUST use cart contents (line_items, context, buyer) and MUST ignore overlapping fields in checkout payload. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/catalog_lookup.py b/src/ucp_sdk/models/schemas/shopping/catalog_lookup.py new file mode 100644 index 0000000..307480a --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/catalog_lookup.py @@ -0,0 +1,171 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field + +from .. import ucp as ucp_1 +from .types import context as context_1 +from .types import ( + detail_option_value, + input_correlation, + message, + search_filters, + selected_option, +) +from .types import signals as signals_1 +from .types.product import Product as Product_1 +from .types.variant import Variant + + +class CatalogLookup(BaseModel): + """ + Product/variant lookup by identifier. Supports batch retrieval (lookup_catalog) and single-product detail (get_product). + """ + + model_config = ConfigDict( + extra="allow", + ) + + +class LookupVariant(Variant): + """ + Variant with required correlation metadata for lookup responses. + """ + + model_config = ConfigDict( + extra="allow", + ) + inputs: list[input_correlation.InputCorrelation] = Field(..., min_length=1) + """ + Which request identifiers resolved to this variant, and how. Each entry maps a request ID to its match type. + """ + + +class LookupRequest(BaseModel): + """ + Request body for catalog lookup. + """ + + model_config = ConfigDict( + extra="allow", + ) + ids: list[str] = Field(..., min_length=1) + """ + Identifiers to lookup. Implementations MUST support product ID and variant ID; MAY support secondary identifiers (SKU, handle, etc.). + """ + filters: search_filters.SearchFilters | None = None + """ + Filter criteria to narrow returned products and variants. All specified filters combine with AND logic. + """ + context: context_1.Context | None = None + signals: signals_1.Signals | None = None + + +class Product(Product_1): + model_config = ConfigDict( + extra="allow", + ) + variants: list[LookupVariant] | None = None + + +class LookupResponse(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + ucp: ucp_1.ResponseCatalogSchema + products: list[Product] + """ + Products matching the requested identifiers. May contain fewer items if some identifiers not found, or more if identifiers match multiple products. + """ + messages: list[message.Message] | None = None + """ + Errors, warnings, or informational messages about the requested items. + """ + + +class GetProductRequest(BaseModel): + """ + Request body for single-product retrieval. Supports interactive variant narrowing via selected and preferences. + """ + + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Product or variant identifier. Implementations MUST support product ID and variant ID. + """ + selected: list[selected_option.SelectedOption] | None = None + """ + Partial or full option selections for interactive variant narrowing. When provided, response option values include availability signals (available, exists) relative to these selections. + """ + preferences: list[str] | None = None + """ + Option names in relaxation priority order. When no exact variant matches all selections, the server drops options from the end of this list first. E.g., ['Color', 'Size'] keeps Color and relaxes Size. + """ + filters: search_filters.SearchFilters | None = None + """ + Filter criteria to narrow returned variants. All specified filters combine with AND logic. + """ + context: context_1.Context | None = None + signals: signals_1.Signals | None = None + + +class Option(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + name: str + values: list[detail_option_value.DetailOptionValue] = Field( + ..., min_length=1 + ) + + +class DetailProduct(Product_1): + """ + A product in a get_product response, extended with effective selections and availability signals on option values. + """ + + model_config = ConfigDict( + extra="allow", + ) + selected: list[selected_option.SelectedOption] | None = None + """ + Effective option selections that anchor the featured variant and availability signals. Required when the product has configurable options; may be empty or omitted for products with no option axes. + """ + options: list[Option] | None = None + """ + Product options with availability signals relative to the effective selections. + """ + + +class GetProductResponse(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + ucp: ucp_1.ResponseCatalogSchema + product: DetailProduct + """ + The requested product with full detail. Singular — this is a single-resource operation. + """ + messages: list[message.Message] | None = None + """ + Warnings or informational messages about the product (e.g., price recently changed, limited availability). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/catalog_search.py b/src/ucp_sdk/models/schemas/shopping/catalog_search.py new file mode 100644 index 0000000..6e65867 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/catalog_search.py @@ -0,0 +1,68 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict + +from .. import ucp as ucp_1 +from .types import context as context_1 +from .types import message +from .types import pagination as pagination_1 +from .types import product, search_filters +from .types import signals as signals_1 + + +class CatalogSearch(BaseModel): + """ + Product catalog search capability. + """ + + model_config = ConfigDict( + extra="allow", + ) + + +class SearchRequest(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + query: str | None = None + """ + Free-text search query. + """ + context: context_1.Context | None = None + signals: signals_1.Signals | None = None + filters: search_filters.SearchFilters | None = None + pagination: pagination_1.Request | None = None + + +class SearchResponse(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + ucp: ucp_1.ResponseCatalogSchema + products: list[product.Product] + """ + Products matching the search criteria. + """ + pagination: pagination_1.Response | None = None + messages: list[message.Message] | None = None + """ + Errors, warnings, or informational messages about the search results. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/checkout.py b/src/ucp_sdk/models/schemas/shopping/checkout.py index e2c95a2..2b2c938 100644 --- a/src/ucp_sdk/models/schemas/shopping/checkout.py +++ b/src/ucp_sdk/models/schemas/shopping/checkout.py @@ -26,7 +26,9 @@ from . import payment as payment_1 from .types import buyer as buyer_1 from .types import context as context_1 -from .types import line_item, link, message, order_confirmation, total +from .types import line_item, link, message, order_confirmation +from .types import signals as signals_1 +from .types import totals as totals_1 class Checkout(BaseModel): @@ -51,6 +53,7 @@ class Checkout(BaseModel): Representation of the buyer. """ context: context_1.Context | None = None + signals: signals_1.Signals | None = None status: Literal[ "incomplete", "requires_escalation", @@ -66,7 +69,7 @@ class Checkout(BaseModel): """ ISO 4217 currency code reflecting the merchant's market determination. Derived from address, context, and geo IP—buyers provide signals, merchants determine currency. """ - totals: list[total.Total] + totals: totals_1.Totals """ Different cart totals. """ diff --git a/src/ucp_sdk/models/schemas/shopping/checkout_complete_request.py b/src/ucp_sdk/models/schemas/shopping/checkout_complete_request.py index 84dab14..c7010c5 100644 --- a/src/ucp_sdk/models/schemas/shopping/checkout_complete_request.py +++ b/src/ucp_sdk/models/schemas/shopping/checkout_complete_request.py @@ -21,6 +21,7 @@ from pydantic import BaseModel, ConfigDict from . import payment_complete_request +from .types import signals_complete_request class CheckoutCompleteRequest(BaseModel): @@ -31,4 +32,5 @@ class CheckoutCompleteRequest(BaseModel): model_config = ConfigDict( extra="allow", ) + signals: signals_complete_request.SignalsCompleteRequest | None = None payment: payment_complete_request.PaymentCompleteRequest diff --git a/src/ucp_sdk/models/schemas/shopping/checkout_create_request.py b/src/ucp_sdk/models/schemas/shopping/checkout_create_request.py index 1747b92..e10e36a 100644 --- a/src/ucp_sdk/models/schemas/shopping/checkout_create_request.py +++ b/src/ucp_sdk/models/schemas/shopping/checkout_create_request.py @@ -25,6 +25,7 @@ buyer_create_request, context_create_request, line_item_create_request, + signals_create_request, ) @@ -45,4 +46,5 @@ class CheckoutCreateRequest(BaseModel): Representation of the buyer. """ context: context_create_request.ContextCreateRequest | None = None + signals: signals_create_request.SignalsCreateRequest | None = None payment: payment_create_request.PaymentCreateRequest | None = None diff --git a/src/ucp_sdk/models/schemas/shopping/checkout_update_request.py b/src/ucp_sdk/models/schemas/shopping/checkout_update_request.py index 4bc4d90..4b2fd71 100644 --- a/src/ucp_sdk/models/schemas/shopping/checkout_update_request.py +++ b/src/ucp_sdk/models/schemas/shopping/checkout_update_request.py @@ -25,6 +25,7 @@ buyer_update_request, context_update_request, line_item_update_request, + signals_update_request, ) @@ -36,10 +37,6 @@ class CheckoutUpdateRequest(BaseModel): model_config = ConfigDict( extra="allow", ) - id: str - """ - Unique identifier of the checkout session. - """ line_items: list[line_item_update_request.LineItemUpdateRequest] """ List of line items being checked out. @@ -49,4 +46,5 @@ class CheckoutUpdateRequest(BaseModel): Representation of the buyer. """ context: context_update_request.ContextUpdateRequest | None = None + signals: signals_update_request.SignalsUpdateRequest | None = None payment: payment_update_request.PaymentUpdateRequest | None = None diff --git a/src/ucp_sdk/models/schemas/shopping/discount.py b/src/ucp_sdk/models/schemas/shopping/discount/__init__.py similarity index 78% rename from src/ucp_sdk/models/schemas/shopping/discount.py rename to src/ucp_sdk/models/schemas/shopping/discount/__init__.py index 14e429b..8b2f9f3 100644 --- a/src/ucp_sdk/models/schemas/shopping/discount.py +++ b/src/ucp_sdk/models/schemas/shopping/discount/__init__.py @@ -22,7 +22,8 @@ from pydantic import BaseModel, ConfigDict, Field, RootModel -from .checkout import Checkout as Checkout_1 +from ..types import amount as amount_1 +from ..types import reverse_domain_name class DiscountExtension(RootModel[Any]): @@ -31,7 +32,7 @@ class DiscountExtension(RootModel[Any]): ) root: Any = Field(..., title="Discount Extension") """ - Extends Checkout with discount code support, enabling agents to apply promotional, loyalty, referral, and other discount codes. + Extends Cart and Checkout with discount support, including discount codes, automatic discounts, and eligibility-triggered provisional discounts. """ @@ -47,9 +48,9 @@ class Allocation(BaseModel): """ JSONPath to the allocation target (e.g., '$.line_items[0]', '$.totals.shipping'). """ - amount: int = Field(..., ge=0) + amount: amount_1.Amount """ - Amount allocated to this target in minor (cents) currency units. + Amount allocated to this target in ISO 4217 minor units. """ @@ -69,9 +70,9 @@ class AppliedDiscount(BaseModel): """ Human-readable discount name (e.g., 'Summer Sale 20% Off'). """ - amount: int = Field(..., ge=0) + amount: amount_1.Amount """ - Total discount amount in minor (cents) currency units. + Total discount amount in ISO 4217 minor units. """ automatic: bool | None = False """ @@ -85,6 +86,14 @@ class AppliedDiscount(BaseModel): """ Stacking order for discount calculation. Lower numbers applied first (1 = first). """ + provisional: bool | None = False + """ + True if this discount requires additional verification. + """ + eligibility: reverse_domain_name.ReverseDomainName | None = None + """ + The eligibility claim accepted by the Business for this discount. Corresponds to a value from context.eligibility. Omitted for code-based and non-eligibility automatic discounts. + """ allocations: list[Allocation] | None = None """ Breakdown of where this discount was allocated. Sum of allocation amounts equals total amount. @@ -107,14 +116,3 @@ class DiscountsObject(BaseModel): """ Discounts successfully applied (code-based and automatic). """ - - -class Checkout(Checkout_1): - """ - Checkout extended with discount capability. - """ - - model_config = ConfigDict( - extra="allow", - ) - discounts: DiscountsObject | None = None diff --git a/src/ucp_sdk/models/schemas/shopping/discount/dev/__init__.py b/src/ucp_sdk/models/schemas/shopping/discount/dev/__init__.py new file mode 100644 index 0000000..421dc21 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/discount/dev/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + diff --git a/src/ucp_sdk/models/schemas/shopping/discount/dev/ucp/__init__.py b/src/ucp_sdk/models/schemas/shopping/discount/dev/ucp/__init__.py new file mode 100644 index 0000000..421dc21 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/discount/dev/ucp/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + diff --git a/src/ucp_sdk/models/schemas/shopping/discount/dev/ucp/shopping.py b/src/ucp_sdk/models/schemas/shopping/discount/dev/ucp/shopping.py new file mode 100644 index 0000000..23d4fcf --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/discount/dev/ucp/shopping.py @@ -0,0 +1,47 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import ConfigDict + +from ....cart import Cart as Cart_1 +from ....checkout import Checkout as Checkout_1 +from ... import DiscountsObject + + +class Cart(Cart_1): + """ + Cart extended with discount capability. + """ + + model_config = ConfigDict( + extra="allow", + ) + discounts: DiscountsObject | None = None + + +class Checkout(Checkout_1): + """ + Checkout extended with discount capability. + """ + + model_config = ConfigDict( + extra="allow", + ) + discounts: DiscountsObject | None = None diff --git a/src/ucp_sdk/models/schemas/shopping/fulfillment/__init__.py b/src/ucp_sdk/models/schemas/shopping/fulfillment/__init__.py index f926b0b..46fba84 100644 --- a/src/ucp_sdk/models/schemas/shopping/fulfillment/__init__.py +++ b/src/ucp_sdk/models/schemas/shopping/fulfillment/__init__.py @@ -22,9 +22,8 @@ from pydantic import ConfigDict, Field, RootModel -from ..checkout import Checkout as Checkout_1 -from ..types import fulfillment as fulfillment_1 from ..types import ( + fulfillment, fulfillment_available_method, fulfillment_group, fulfillment_method, @@ -72,22 +71,8 @@ class FulfillmentMethod(RootModel[fulfillment_method.FulfillmentMethod]): root: fulfillment_method.FulfillmentMethod -class Fulfillment(RootModel[fulfillment_1.Fulfillment]): +class Fulfillment(RootModel[fulfillment.Fulfillment]): model_config = ConfigDict( frozen=True, ) - root: fulfillment_1.Fulfillment - - -class Checkout(Checkout_1): - """ - Checkout extended with hierarchical fulfillment. - """ - - model_config = ConfigDict( - extra="allow", - ) - fulfillment: Fulfillment | None = None - """ - Fulfillment details. - """ + root: fulfillment.Fulfillment diff --git a/src/ucp_sdk/models/schemas/shopping/fulfillment/dev/__init__.py b/src/ucp_sdk/models/schemas/shopping/fulfillment/dev/__init__.py index 1252d6b..421dc21 100644 --- a/src/ucp_sdk/models/schemas/shopping/fulfillment/dev/__init__.py +++ b/src/ucp_sdk/models/schemas/shopping/fulfillment/dev/__init__.py @@ -15,3 +15,4 @@ # generated by datamodel-codegen # pylint: disable=all # pyformat: disable + diff --git a/src/ucp_sdk/models/schemas/shopping/fulfillment/dev/ucp/__init__.py b/src/ucp_sdk/models/schemas/shopping/fulfillment/dev/ucp/__init__.py index 1252d6b..421dc21 100644 --- a/src/ucp_sdk/models/schemas/shopping/fulfillment/dev/ucp/__init__.py +++ b/src/ucp_sdk/models/schemas/shopping/fulfillment/dev/ucp/__init__.py @@ -15,3 +15,4 @@ # generated by datamodel-codegen # pylint: disable=all # pyformat: disable + diff --git a/src/ucp_sdk/models/schemas/shopping/fulfillment/dev/ucp/shopping.py b/src/ucp_sdk/models/schemas/shopping/fulfillment/dev/ucp/shopping.py index 5a804e5..2d58bb8 100644 --- a/src/ucp_sdk/models/schemas/shopping/fulfillment/dev/ucp/shopping.py +++ b/src/ucp_sdk/models/schemas/shopping/fulfillment/dev/ucp/shopping.py @@ -22,9 +22,26 @@ from pydantic import ConfigDict, RootModel +from ....checkout import Checkout as Checkout_1 +from ... import Fulfillment as Fulfillment_1 + class Fulfillment(RootModel[Any]): model_config = ConfigDict( frozen=True, ) root: Any + + +class Checkout(Checkout_1): + """ + Checkout extended with hierarchical fulfillment. + """ + + model_config = ConfigDict( + extra="allow", + ) + fulfillment: Fulfillment_1 | None = None + """ + Fulfillment details. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/order.py b/src/ucp_sdk/models/schemas/shopping/order.py index 2e60bb1..27535f1 100644 --- a/src/ucp_sdk/models/schemas/shopping/order.py +++ b/src/ucp_sdk/models/schemas/shopping/order.py @@ -25,9 +25,10 @@ adjustment, expectation, fulfillment_event, + message, order_line_item, - total, ) +from .types import totals as totals_1 class PlatformSchema(BaseModel): @@ -64,7 +65,7 @@ class Fulfillment(BaseModel): class Order(BaseModel): """ - Order schema with immutable line items, buyer-facing fulfillment expectations, and append-only event logs. + Order schema with line items, buyer-facing fulfillment expectations, and event logs. """ model_config = ConfigDict( @@ -75,6 +76,10 @@ class Order(BaseModel): """ Unique order identifier. """ + label: str | None = None + """ + Human-readable label for identifying the order. MUST only be provided by the business. + """ checkout_id: str """ Associated checkout ID for reconciliation. @@ -85,7 +90,7 @@ class Order(BaseModel): """ line_items: list[order_line_item.OrderLineItem] """ - Immutable line items — source of truth for what was ordered. + Line items representing what was purchased — can change post-order via edits or exchanges. """ fulfillment: Fulfillment """ @@ -93,9 +98,17 @@ class Order(BaseModel): """ adjustments: list[adjustment.Adjustment] | None = None """ - Append-only event log of money movements (refunds, returns, credits, disputes, cancellations, etc.) that exist independently of fulfillment. + Post-order events (refunds, returns, credits, disputes, cancellations, etc.) that exist independently of fulfillment. + """ + currency: str """ - totals: list[total.Total] + ISO 4217 currency code. MUST match the currency from the originating checkout session. + """ + totals: totals_1.Totals """ Different totals for the order. """ + messages: list[message.Message] | None = None + """ + Business outcome messages (errors, warnings, informational). Present when the business needs to communicate status or issues to the platform. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/order_create_request.py b/src/ucp_sdk/models/schemas/shopping/order_create_request.py new file mode 100644 index 0000000..a31c315 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/order_create_request.py @@ -0,0 +1,117 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import AnyUrl, BaseModel, ConfigDict + +from .. import ucp_create_request +from .types import ( + adjustment_create_request, + expectation_create_request, + fulfillment_event_create_request, + message_create_request, + order_line_item_create_request, + totals_create_request, +) + + +class PlatformSchema(BaseModel): + """ + Platform's order capability configuration. + """ + + model_config = ConfigDict( + extra="allow", + ) + webhook_url: AnyUrl + """ + URL where merchant sends order lifecycle events (webhooks). + """ + + +class Fulfillment(BaseModel): + """ + Fulfillment data: buyer expectations and what actually happened. + """ + + model_config = ConfigDict( + extra="allow", + ) + expectations: ( + list[expectation_create_request.ExpectationCreateRequest] | None + ) = None + """ + Buyer-facing groups representing when/how items will be delivered. Can be split, merged, or adjusted post-order. + """ + events: ( + list[fulfillment_event_create_request.FulfillmentEventCreateRequest] + | None + ) = None + """ + Append-only event log of actual shipments. Each event references line items by ID. + """ + + +class OrderCreateRequest(BaseModel): + """ + Order schema with line items, buyer-facing fulfillment expectations, and event logs. + """ + + model_config = ConfigDict( + extra="allow", + ) + ucp: ucp_create_request.UcpMetadataCreateRequest + id: str + """ + Unique order identifier. + """ + label: str | None = None + """ + Human-readable label for identifying the order. MUST only be provided by the business. + """ + checkout_id: str + """ + Associated checkout ID for reconciliation. + """ + permalink_url: AnyUrl + """ + Permalink to access the order on merchant site. + """ + line_items: list[order_line_item_create_request.OrderLineItemCreateRequest] + """ + Line items representing what was purchased — can change post-order via edits or exchanges. + """ + fulfillment: Fulfillment + """ + Fulfillment data: buyer expectations and what actually happened. + """ + adjustments: ( + list[adjustment_create_request.AdjustmentCreateRequest] | None + ) = None + """ + Post-order events (refunds, returns, credits, disputes, cancellations, etc.) that exist independently of fulfillment. + """ + totals: totals_create_request.TotalsCreateRequest + """ + Different totals for the order. + """ + messages: list[message_create_request.MessageCreateRequest] | None = None + """ + Business outcome messages (errors, warnings, informational). Present when the business needs to communicate status or issues to the platform. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/order_update_request.py b/src/ucp_sdk/models/schemas/shopping/order_update_request.py new file mode 100644 index 0000000..fd4a285 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/order_update_request.py @@ -0,0 +1,117 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import AnyUrl, BaseModel, ConfigDict + +from .. import ucp_update_request +from .types import ( + adjustment_update_request, + expectation_update_request, + fulfillment_event_update_request, + message_update_request, + order_line_item_update_request, + totals_update_request, +) + + +class PlatformSchema(BaseModel): + """ + Platform's order capability configuration. + """ + + model_config = ConfigDict( + extra="allow", + ) + webhook_url: AnyUrl + """ + URL where merchant sends order lifecycle events (webhooks). + """ + + +class Fulfillment(BaseModel): + """ + Fulfillment data: buyer expectations and what actually happened. + """ + + model_config = ConfigDict( + extra="allow", + ) + expectations: ( + list[expectation_update_request.ExpectationUpdateRequest] | None + ) = None + """ + Buyer-facing groups representing when/how items will be delivered. Can be split, merged, or adjusted post-order. + """ + events: ( + list[fulfillment_event_update_request.FulfillmentEventUpdateRequest] + | None + ) = None + """ + Append-only event log of actual shipments. Each event references line items by ID. + """ + + +class OrderUpdateRequest(BaseModel): + """ + Order schema with line items, buyer-facing fulfillment expectations, and event logs. + """ + + model_config = ConfigDict( + extra="allow", + ) + ucp: ucp_update_request.UcpMetadataUpdateRequest + id: str + """ + Unique order identifier. + """ + label: str | None = None + """ + Human-readable label for identifying the order. MUST only be provided by the business. + """ + checkout_id: str + """ + Associated checkout ID for reconciliation. + """ + permalink_url: AnyUrl + """ + Permalink to access the order on merchant site. + """ + line_items: list[order_line_item_update_request.OrderLineItemUpdateRequest] + """ + Line items representing what was purchased — can change post-order via edits or exchanges. + """ + fulfillment: Fulfillment + """ + Fulfillment data: buyer expectations and what actually happened. + """ + adjustments: ( + list[adjustment_update_request.AdjustmentUpdateRequest] | None + ) = None + """ + Post-order events (refunds, returns, credits, disputes, cancellations, etc.) that exist independently of fulfillment. + """ + totals: totals_update_request.TotalsUpdateRequest + """ + Different totals for the order. + """ + messages: list[message_update_request.MessageUpdateRequest] | None = None + """ + Business outcome messages (errors, warnings, informational). Present when the business needs to communicate status or issues to the platform. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/__init__.py b/src/ucp_sdk/models/schemas/shopping/types/__init__.py index 1252d6b..421dc21 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/__init__.py +++ b/src/ucp_sdk/models/schemas/shopping/types/__init__.py @@ -15,3 +15,4 @@ # generated by datamodel-codegen # pylint: disable=all # pyformat: disable + diff --git a/src/ucp_sdk/models/schemas/shopping/types/adjustment.py b/src/ucp_sdk/models/schemas/shopping/types/adjustment.py index f28c0ea..99cdfe4 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/adjustment.py +++ b/src/ucp_sdk/models/schemas/shopping/types/adjustment.py @@ -20,7 +20,9 @@ from typing import Literal -from pydantic import AwareDatetime, BaseModel, ConfigDict, Field +from pydantic import AwareDatetime, BaseModel, ConfigDict + +from . import total class LineItem(BaseModel): @@ -31,15 +33,15 @@ class LineItem(BaseModel): """ Line item ID reference. """ - quantity: int = Field(..., ge=1) + quantity: int """ - Quantity affected by this adjustment. + Signed quantity affected by this adjustment. Negative values represent reductions (e.g. returns); positive values represent additions (e.g. exchanges). """ class Adjustment(BaseModel): """ - Append-only event that exists independently of fulfillment. Typically represents money movements but can be any post-order change. Polymorphic type that can optionally reference line items. + Post-order event that exists independently of fulfillment. Typically represents money movements but can be any post-order change. Polymorphic type that can optionally reference line items. """ model_config = ConfigDict( @@ -65,9 +67,9 @@ class Adjustment(BaseModel): """ Which line items and quantities are affected (optional). """ - amount: int | None = None + totals: list[total.Total] | None = None """ - Amount in minor units (cents) for refunds, credits, price adjustments (optional). + Adjustment totals breakdown. Signed values - negative for money returned to buyer (refunds, credits), positive for additional charges (exchanges). """ description: str | None = None """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/adjustment_create_request.py b/src/ucp_sdk/models/schemas/shopping/types/adjustment_create_request.py new file mode 100644 index 0000000..44881cd --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/adjustment_create_request.py @@ -0,0 +1,77 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from typing import Literal + +from pydantic import AwareDatetime, BaseModel, ConfigDict + +from . import total_create_request + + +class LineItem(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Line item ID reference. + """ + quantity: int + """ + Signed quantity affected by this adjustment. Negative values represent reductions (e.g. returns); positive values represent additions (e.g. exchanges). + """ + + +class AdjustmentCreateRequest(BaseModel): + """ + Post-order event that exists independently of fulfillment. Typically represents money movements but can be any post-order change. Polymorphic type that can optionally reference line items. + """ + + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Adjustment event identifier. + """ + type: str + """ + Type of adjustment (open string). Typically money-related like: refund, return, credit, price_adjustment, dispute, cancellation. Can be any value that makes sense for the merchant's business. + """ + occurred_at: AwareDatetime + """ + RFC 3339 timestamp when this adjustment occurred. + """ + status: Literal["pending", "completed", "failed"] + """ + Adjustment status. + """ + line_items: list[LineItem] | None = None + """ + Which line items and quantities are affected (optional). + """ + totals: list[total_create_request.TotalCreateRequest] | None = None + """ + Adjustment totals breakdown. Signed values - negative for money returned to buyer (refunds, credits), positive for additional charges (exchanges). + """ + description: str | None = None + """ + Human-readable reason or description (e.g., 'Defective item', 'Customer requested'). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/adjustment_update_request.py b/src/ucp_sdk/models/schemas/shopping/types/adjustment_update_request.py new file mode 100644 index 0000000..632c2e7 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/adjustment_update_request.py @@ -0,0 +1,77 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from typing import Literal + +from pydantic import AwareDatetime, BaseModel, ConfigDict + +from . import total_update_request + + +class LineItem(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Line item ID reference. + """ + quantity: int + """ + Signed quantity affected by this adjustment. Negative values represent reductions (e.g. returns); positive values represent additions (e.g. exchanges). + """ + + +class AdjustmentUpdateRequest(BaseModel): + """ + Post-order event that exists independently of fulfillment. Typically represents money movements but can be any post-order change. Polymorphic type that can optionally reference line items. + """ + + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Adjustment event identifier. + """ + type: str + """ + Type of adjustment (open string). Typically money-related like: refund, return, credit, price_adjustment, dispute, cancellation. Can be any value that makes sense for the merchant's business. + """ + occurred_at: AwareDatetime + """ + RFC 3339 timestamp when this adjustment occurred. + """ + status: Literal["pending", "completed", "failed"] + """ + Adjustment status. + """ + line_items: list[LineItem] | None = None + """ + Which line items and quantities are affected (optional). + """ + totals: list[total_update_request.TotalUpdateRequest] | None = None + """ + Adjustment totals breakdown. Signed values - negative for money returned to buyer (refunds, credits), positive for additional charges (exchanges). + """ + description: str | None = None + """ + Human-readable reason or description (e.g., 'Defective item', 'Customer requested'). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/amount.py b/src/ucp_sdk/models/schemas/shopping/types/amount.py new file mode 100644 index 0000000..ac8c453 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/amount.py @@ -0,0 +1,31 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import ConfigDict, Field, RootModel + + +class Amount(RootModel[int]): + model_config = ConfigDict( + frozen=True, + ) + root: int = Field(..., ge=0, title="Amount") + """ + Monetary amount in the currency's minor unit as defined by ISO 4217. Refer to the currency's exponent to determine minor-to-major ratio (e.g., 2 for USD, 0 for JPY, 3 for KWD). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/available_payment_instrument.py b/src/ucp_sdk/models/schemas/shopping/types/available_payment_instrument.py new file mode 100644 index 0000000..44ce88f --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/available_payment_instrument.py @@ -0,0 +1,41 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from typing import Any + +from pydantic import BaseModel, ConfigDict + + +class AvailablePaymentInstrument(BaseModel): + """ + An instrument type available from a payment handler with optional constraints. + """ + + model_config = ConfigDict( + extra="allow", + ) + type: str + """ + The instrument type identifier (e.g., 'card', 'gift_card'). References an instrument schema's type constant. + """ + constraints: dict[str, Any] | None = None + """ + Constraints on this instrument type. Structure depends on instrument type and active capabilities. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/card_payment_instrument.py b/src/ucp_sdk/models/schemas/shopping/types/card_payment_instrument.py index e5cffa1..2233292 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/card_payment_instrument.py +++ b/src/ucp_sdk/models/schemas/shopping/types/card_payment_instrument.py @@ -20,8 +20,9 @@ from typing import Literal -from pydantic import AnyUrl, BaseModel, ConfigDict +from pydantic import AnyUrl, BaseModel, ConfigDict, Field +from .available_payment_instrument import AvailablePaymentInstrument from .payment_instrument import PaymentInstrument @@ -59,6 +60,28 @@ class Display(BaseModel): """ +class Constraints(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + brands: list[str] | None = Field(None, min_length=1) + """ + Limit to specific card brands (e.g., ['visa', 'mastercard', 'amex']). + """ + + +class AvailableCardPaymentInstrument(AvailablePaymentInstrument): + """ + Declares card instrument availability with card-specific constraints. + """ + + model_config = ConfigDict( + extra="allow", + ) + type: Literal["card"] = "card" + constraints: Constraints | None = None + + class CardPaymentInstrument(PaymentInstrument): """ A basic card payment instrument with visible card details. Can be inherited by a handler's instrument schema to define handler-specific display details or more complex credential structures. diff --git a/src/ucp_sdk/models/schemas/shopping/types/category.py b/src/ucp_sdk/models/schemas/shopping/types/category.py new file mode 100644 index 0000000..128be48 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/category.py @@ -0,0 +1,39 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict + + +class Category(BaseModel): + """ + A product category with optional taxonomy identifier. + """ + + model_config = ConfigDict( + extra="allow", + ) + value: str + """ + Category value or path (e.g., 'Apparel > Shirts', '1604'). + """ + taxonomy: str | None = None + """ + Source taxonomy. Well-known values: `google_product_category`, `shopify`, `merchant`. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/context.py b/src/ucp_sdk/models/schemas/shopping/types/context.py index d501c00..0107938 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/context.py +++ b/src/ucp_sdk/models/schemas/shopping/types/context.py @@ -20,10 +20,12 @@ from pydantic import BaseModel, ConfigDict +from . import reverse_domain_name + class Context(BaseModel): """ - Provisional buyer signals for relevance and localization: product availability, pricing, currency, tax, shipping, payment methods, and eligibility (e.g., student or affiliation discounts). Businesses SHOULD use these values when authoritative data (e.g., address) is absent, and MAY ignore unsupported values without returning errors. Context can be disclosed progressively—coarse signals early, finer resolution as the session progresses. Higher-resolution data (shipping address, billing address) supersedes context. Platforms SHOULD progressively enhance context throughout the buyer journey. + Provisional buyer signals for relevance and localization—not authoritative data. Businesses SHOULD use these values when verified inputs (e.g., shipping address) are absent, and MAY ignore or down-rank them if inconsistent with higher-confidence signals (authenticated account, risk detection) or regulatory constraints (export controls). Eligibility and policy enforcement MUST occur at checkout time using binding transaction data. Context SHOULD be non-identifying and can be disclosed progressively—coarse signals early, finer resolution as the session progresses. Higher-resolution data (shipping address, billing address) supersedes context. """ model_config = ConfigDict( @@ -41,3 +43,19 @@ class Context(BaseModel): """ The postal code. For example, 94043. Optional hint for regional refinement—higher-resolution data (e.g., shipping address) supersedes this value. """ + intent: str | None = None + """ + Background context describing buyer's intent (e.g., 'looking for a gift under $50', 'need something durable for outdoor use'). Informs relevance, recommendations, and personalization. + """ + language: str | None = None + """ + Preferred language for content. Use IETF BCP 47 language tags (e.g., 'en', 'fr-CA', 'zh-Hans'). For REST, equivalent to Accept-Language header—platforms SHOULD fall back to Accept-Language when this field is absent; when provided, overrides Accept-Language. Businesses MAY return content in a different language if unavailable. + """ + currency: str | None = None + """ + Preferred currency (ISO 4217, e.g., 'EUR', 'USD'). Businesses determine presentment currency from context and authoritative signals; this hint MAY inform selection in multi-currency markets. Also serves as the denomination for price filter values — platforms SHOULD include this field when sending price filters. Response prices include explicit currency confirming the resolution. + """ + eligibility: list[reverse_domain_name.ReverseDomainName] | None = None + """ + Buyer claims about eligible benefits such as loyalty membership, payment instrument perks, and similar. Recognized claims MAY inform the Business response (e.g., member-only product availability, adjusted pricing in catalog, provisional discounts at cart or checkout). Businesses MUST ignore unrecognized values without error. Values MUST use reverse-domain naming (e.g., 'com.example.loyalty_gold', 'org.school.student') and MUST be non-identifying. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/context_create_request.py b/src/ucp_sdk/models/schemas/shopping/types/context_create_request.py index 64035b4..6c86b1a 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/context_create_request.py +++ b/src/ucp_sdk/models/schemas/shopping/types/context_create_request.py @@ -20,10 +20,12 @@ from pydantic import BaseModel, ConfigDict +from . import reverse_domain_name_create_request + class ContextCreateRequest(BaseModel): """ - Provisional buyer signals for relevance and localization: product availability, pricing, currency, tax, shipping, payment methods, and eligibility (e.g., student or affiliation discounts). Businesses SHOULD use these values when authoritative data (e.g., address) is absent, and MAY ignore unsupported values without returning errors. Context can be disclosed progressively—coarse signals early, finer resolution as the session progresses. Higher-resolution data (shipping address, billing address) supersedes context. Platforms SHOULD progressively enhance context throughout the buyer journey. + Provisional buyer signals for relevance and localization—not authoritative data. Businesses SHOULD use these values when verified inputs (e.g., shipping address) are absent, and MAY ignore or down-rank them if inconsistent with higher-confidence signals (authenticated account, risk detection) or regulatory constraints (export controls). Eligibility and policy enforcement MUST occur at checkout time using binding transaction data. Context SHOULD be non-identifying and can be disclosed progressively—coarse signals early, finer resolution as the session progresses. Higher-resolution data (shipping address, billing address) supersedes context. """ model_config = ConfigDict( @@ -41,3 +43,22 @@ class ContextCreateRequest(BaseModel): """ The postal code. For example, 94043. Optional hint for regional refinement—higher-resolution data (e.g., shipping address) supersedes this value. """ + intent: str | None = None + """ + Background context describing buyer's intent (e.g., 'looking for a gift under $50', 'need something durable for outdoor use'). Informs relevance, recommendations, and personalization. + """ + language: str | None = None + """ + Preferred language for content. Use IETF BCP 47 language tags (e.g., 'en', 'fr-CA', 'zh-Hans'). For REST, equivalent to Accept-Language header—platforms SHOULD fall back to Accept-Language when this field is absent; when provided, overrides Accept-Language. Businesses MAY return content in a different language if unavailable. + """ + currency: str | None = None + """ + Preferred currency (ISO 4217, e.g., 'EUR', 'USD'). Businesses determine presentment currency from context and authoritative signals; this hint MAY inform selection in multi-currency markets. Also serves as the denomination for price filter values — platforms SHOULD include this field when sending price filters. Response prices include explicit currency confirming the resolution. + """ + eligibility: ( + list[reverse_domain_name_create_request.ReverseDomainNameCreateRequest] + | None + ) = None + """ + Buyer claims about eligible benefits such as loyalty membership, payment instrument perks, and similar. Recognized claims MAY inform the Business response (e.g., member-only product availability, adjusted pricing in catalog, provisional discounts at cart or checkout). Businesses MUST ignore unrecognized values without error. Values MUST use reverse-domain naming (e.g., 'com.example.loyalty_gold', 'org.school.student') and MUST be non-identifying. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/context_update_request.py b/src/ucp_sdk/models/schemas/shopping/types/context_update_request.py index 77ee792..79610e9 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/context_update_request.py +++ b/src/ucp_sdk/models/schemas/shopping/types/context_update_request.py @@ -20,10 +20,12 @@ from pydantic import BaseModel, ConfigDict +from . import reverse_domain_name_update_request + class ContextUpdateRequest(BaseModel): """ - Provisional buyer signals for relevance and localization: product availability, pricing, currency, tax, shipping, payment methods, and eligibility (e.g., student or affiliation discounts). Businesses SHOULD use these values when authoritative data (e.g., address) is absent, and MAY ignore unsupported values without returning errors. Context can be disclosed progressively—coarse signals early, finer resolution as the session progresses. Higher-resolution data (shipping address, billing address) supersedes context. Platforms SHOULD progressively enhance context throughout the buyer journey. + Provisional buyer signals for relevance and localization—not authoritative data. Businesses SHOULD use these values when verified inputs (e.g., shipping address) are absent, and MAY ignore or down-rank them if inconsistent with higher-confidence signals (authenticated account, risk detection) or regulatory constraints (export controls). Eligibility and policy enforcement MUST occur at checkout time using binding transaction data. Context SHOULD be non-identifying and can be disclosed progressively—coarse signals early, finer resolution as the session progresses. Higher-resolution data (shipping address, billing address) supersedes context. """ model_config = ConfigDict( @@ -41,3 +43,22 @@ class ContextUpdateRequest(BaseModel): """ The postal code. For example, 94043. Optional hint for regional refinement—higher-resolution data (e.g., shipping address) supersedes this value. """ + intent: str | None = None + """ + Background context describing buyer's intent (e.g., 'looking for a gift under $50', 'need something durable for outdoor use'). Informs relevance, recommendations, and personalization. + """ + language: str | None = None + """ + Preferred language for content. Use IETF BCP 47 language tags (e.g., 'en', 'fr-CA', 'zh-Hans'). For REST, equivalent to Accept-Language header—platforms SHOULD fall back to Accept-Language when this field is absent; when provided, overrides Accept-Language. Businesses MAY return content in a different language if unavailable. + """ + currency: str | None = None + """ + Preferred currency (ISO 4217, e.g., 'EUR', 'USD'). Businesses determine presentment currency from context and authoritative signals; this hint MAY inform selection in multi-currency markets. Also serves as the denomination for price filter values — platforms SHOULD include this field when sending price filters. Response prices include explicit currency confirming the resolution. + """ + eligibility: ( + list[reverse_domain_name_update_request.ReverseDomainNameUpdateRequest] + | None + ) = None + """ + Buyer claims about eligible benefits such as loyalty membership, payment instrument perks, and similar. Recognized claims MAY inform the Business response (e.g., member-only product availability, adjusted pricing in catalog, provisional discounts at cart or checkout). Businesses MUST ignore unrecognized values without error. Values MUST use reverse-domain naming (e.g., 'com.example.loyalty_gold', 'org.school.student') and MUST be non-identifying. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/description.py b/src/ucp_sdk/models/schemas/shopping/types/description.py new file mode 100644 index 0000000..18f6582 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/description.py @@ -0,0 +1,43 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict + + +class Description(BaseModel): + """ + Description content in one or more formats. At least one format must be provided. + """ + + model_config = ConfigDict( + extra="allow", + ) + plain: str | None = None + """ + Plain text content. + """ + html: str | None = None + """ + HTML-formatted content. Security: Platforms MUST sanitize before rendering—strip scripts, event handlers, and untrusted elements. Treat all rich text as untrusted input. + """ + markdown: str | None = None + """ + Markdown-formatted content. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/detail_option_value.py b/src/ucp_sdk/models/schemas/shopping/types/detail_option_value.py new file mode 100644 index 0000000..05e4dd6 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/detail_option_value.py @@ -0,0 +1,41 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import ConfigDict + +from .option_value import OptionValue + + +class DetailOptionValue(OptionValue): + """ + An option value with availability signals relative to the current selections. Used in get_product responses where selected context exists. + """ + + model_config = ConfigDict( + extra="allow", + ) + available: bool | None = None + """ + Whether a variant matching this value and the current option selections is purchasable. + """ + exists: bool | None = None + """ + Whether a variant matching this value and the current option selections exists in the catalog. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/error_code.py b/src/ucp_sdk/models/schemas/shopping/types/error_code.py new file mode 100644 index 0000000..d7cb5d7 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/error_code.py @@ -0,0 +1,42 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import ConfigDict, Field, RootModel + + +class ErrorCode(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field( + ..., + examples=[ + "not_found", + "out_of_stock", + "item_unavailable", + "address_undeliverable", + "payment_failed", + "eligibility_invalid", + ], + title="Error Code", + ) + """ + Error code identifying the type of error. Standard errors are defined in specification (see examples), and have standardized semantics; freeform codes are permitted. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/error_response.py b/src/ucp_sdk/models/schemas/shopping/types/error_response.py new file mode 100644 index 0000000..3f02f0e --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/error_response.py @@ -0,0 +1,46 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import AnyUrl, BaseModel, ConfigDict, Field + +from ... import ucp as ucp_1 +from . import message + + +class ErrorResponse(BaseModel): + """ + Generic error response when business logic prevents resource creation or failed to retrieve resource. Used when no valid resource can be established. + """ + + model_config = ConfigDict( + extra="allow", + ) + ucp: ucp_1.UcpMetadata + """ + UCP protocol metadata. Status MUST be 'error' for error response. + """ + messages: list[message.Message] = Field(..., min_length=1) + """ + Array of messages describing why the operation failed. + """ + continue_url: AnyUrl | None = None + """ + URL for buyer handoff or session recovery. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/expectation_create_request.py b/src/ucp_sdk/models/schemas/shopping/types/expectation_create_request.py new file mode 100644 index 0000000..c1084bf --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/expectation_create_request.py @@ -0,0 +1,73 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field + +from . import postal_address_create_request + + +class LineItem(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Line item ID reference. + """ + quantity: int = Field(..., ge=1) + """ + Quantity of this item in this expectation. + """ + + +class ExpectationCreateRequest(BaseModel): + """ + Buyer-facing fulfillment expectation representing logical groupings of items (e.g., 'package'). Can be split, merged, or adjusted post-order to set buyer expectations for when/how items arrive. + """ + + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Expectation identifier. + """ + line_items: list[LineItem] + """ + Which line items and quantities are in this expectation. + """ + method_type: Literal["shipping", "pickup", "digital"] + """ + Delivery method type (shipping, pickup, digital). + """ + destination: postal_address_create_request.PostalAddressCreateRequest + """ + Delivery destination address. + """ + description: str | None = None + """ + Human-readable delivery description (e.g., 'Arrives in 5-8 business days'). + """ + fulfillable_on: str | None = None + """ + When this expectation can be fulfilled: 'now' or ISO 8601 timestamp for future date (backorder, pre-order). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/expectation_update_request.py b/src/ucp_sdk/models/schemas/shopping/types/expectation_update_request.py new file mode 100644 index 0000000..f0780a9 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/expectation_update_request.py @@ -0,0 +1,73 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field + +from . import postal_address_update_request + + +class LineItem(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Line item ID reference. + """ + quantity: int = Field(..., ge=1) + """ + Quantity of this item in this expectation. + """ + + +class ExpectationUpdateRequest(BaseModel): + """ + Buyer-facing fulfillment expectation representing logical groupings of items (e.g., 'package'). Can be split, merged, or adjusted post-order to set buyer expectations for when/how items arrive. + """ + + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Expectation identifier. + """ + line_items: list[LineItem] + """ + Which line items and quantities are in this expectation. + """ + method_type: Literal["shipping", "pickup", "digital"] + """ + Delivery method type (shipping, pickup, digital). + """ + destination: postal_address_update_request.PostalAddressUpdateRequest + """ + Delivery destination address. + """ + description: str | None = None + """ + Human-readable delivery description (e.g., 'Arrives in 5-8 business days'). + """ + fulfillable_on: str | None = None + """ + When this expectation can be fulfilled: 'now' or ISO 8601 timestamp for future date (backorder, pre-order). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/fulfillment_event_create_request.py b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_event_create_request.py new file mode 100644 index 0000000..a40632c --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_event_create_request.py @@ -0,0 +1,77 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field + + +class LineItem(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Line item ID reference. + """ + quantity: int = Field(..., ge=1) + """ + Quantity fulfilled in this event. + """ + + +class FulfillmentEventCreateRequest(BaseModel): + """ + Append-only fulfillment event representing an actual shipment. References line items by ID. + """ + + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Fulfillment event identifier. + """ + occurred_at: AwareDatetime + """ + RFC 3339 timestamp when this fulfillment event occurred. + """ + type: str + """ + Fulfillment event type. Common values include: processing (preparing to ship), shipped (handed to carrier), in_transit (in delivery network), delivered (received by buyer), failed_attempt (delivery attempt failed), canceled (fulfillment canceled), undeliverable (cannot be delivered), returned_to_sender (returned to merchant). + """ + line_items: list[LineItem] + """ + Which line items and quantities are fulfilled in this event. + """ + tracking_number: str | None = None + """ + Carrier tracking number (required if type != processing). + """ + tracking_url: AnyUrl | None = None + """ + URL to track this shipment (required if type != processing). + """ + carrier: str | None = None + """ + Carrier name (e.g., 'FedEx', 'USPS'). + """ + description: str | None = None + """ + Human-readable description of the shipment status or delivery information (e.g., 'Delivered to front door', 'Out for delivery'). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/fulfillment_event_update_request.py b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_event_update_request.py new file mode 100644 index 0000000..2462a71 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_event_update_request.py @@ -0,0 +1,77 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field + + +class LineItem(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Line item ID reference. + """ + quantity: int = Field(..., ge=1) + """ + Quantity fulfilled in this event. + """ + + +class FulfillmentEventUpdateRequest(BaseModel): + """ + Append-only fulfillment event representing an actual shipment. References line items by ID. + """ + + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Fulfillment event identifier. + """ + occurred_at: AwareDatetime + """ + RFC 3339 timestamp when this fulfillment event occurred. + """ + type: str + """ + Fulfillment event type. Common values include: processing (preparing to ship), shipped (handed to carrier), in_transit (in delivery network), delivered (received by buyer), failed_attempt (delivery attempt failed), canceled (fulfillment canceled), undeliverable (cannot be delivered), returned_to_sender (returned to merchant). + """ + line_items: list[LineItem] + """ + Which line items and quantities are fulfilled in this event. + """ + tracking_number: str | None = None + """ + Carrier tracking number (required if type != processing). + """ + tracking_url: AnyUrl | None = None + """ + URL to track this shipment (required if type != processing). + """ + carrier: str | None = None + """ + Carrier name (e.g., 'FedEx', 'USPS'). + """ + description: str | None = None + """ + Human-readable description of the shipment status or delivery information (e.g., 'Delivered to front door', 'Out for delivery'). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/fulfillment_method_update_request.py b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_method_update_request.py index e86694b..33b953f 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/fulfillment_method_update_request.py +++ b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_method_update_request.py @@ -18,6 +18,8 @@ from __future__ import annotations +from typing import Literal + from pydantic import BaseModel, ConfigDict from . import ( @@ -38,6 +40,10 @@ class FulfillmentMethodUpdateRequest(BaseModel): """ Unique fulfillment method identifier. """ + type: Literal["shipping", "pickup"] + """ + Fulfillment method type. + """ line_item_ids: list[str] """ Line item IDs fulfilled via this method. diff --git a/src/ucp_sdk/models/schemas/shopping/types/input_correlation.py b/src/ucp_sdk/models/schemas/shopping/types/input_correlation.py new file mode 100644 index 0000000..d4707ea --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/input_correlation.py @@ -0,0 +1,39 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field + + +class InputCorrelation(BaseModel): + """ + Maps a request identifier to the variant it resolved to, with match semantics. + """ + + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + The identifier from the lookup request that resolved to this variant. + """ + match: str | None = Field(None, examples=["exact", "featured"]) + """ + How the request identifier resolved to this variant. Well-known values: `exact` (input directly identifies this variant, e.g., variant ID, SKU), `featured` (server selected this variant as representative, e.g., product ID resolved to best match). Businesses MAY implement and provide additional resolution strategies. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/item.py b/src/ucp_sdk/models/schemas/shopping/types/item.py index e2b1954..cfcc71c 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/item.py +++ b/src/ucp_sdk/models/schemas/shopping/types/item.py @@ -18,7 +18,9 @@ from __future__ import annotations -from pydantic import AnyUrl, BaseModel, ConfigDict, Field +from pydantic import AnyUrl, BaseModel, ConfigDict + +from . import amount class Item(BaseModel): @@ -27,15 +29,15 @@ class Item(BaseModel): ) id: str """ - Should be recognized by both the Platform, and the Business. For Google it should match the id provided in the "id" field in the product feed. + The product identifier, often the SKU, required to resolve the product details associated with this line item. Should be recognized by both the Platform, and the Business. """ title: str """ Product title. """ - price: int = Field(..., ge=0) + price: amount.Amount """ - Unit price in minor (cents) currency units. + Unit price in ISO 4217 minor units. """ image_url: AnyUrl | None = None """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/item_create_request.py b/src/ucp_sdk/models/schemas/shopping/types/item_create_request.py index fd3f850..bbef8e8 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/item_create_request.py +++ b/src/ucp_sdk/models/schemas/shopping/types/item_create_request.py @@ -27,5 +27,5 @@ class ItemCreateRequest(BaseModel): ) id: str """ - Should be recognized by both the Platform, and the Business. For Google it should match the id provided in the "id" field in the product feed. + The product identifier, often the SKU, required to resolve the product details associated with this line item. Should be recognized by both the Platform, and the Business. """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/item_update_request.py b/src/ucp_sdk/models/schemas/shopping/types/item_update_request.py index 735b984..c957d16 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/item_update_request.py +++ b/src/ucp_sdk/models/schemas/shopping/types/item_update_request.py @@ -27,5 +27,5 @@ class ItemUpdateRequest(BaseModel): ) id: str """ - Should be recognized by both the Platform, and the Business. For Google it should match the id provided in the "id" field in the product feed. + The product identifier, often the SKU, required to resolve the product details associated with this line item. Should be recognized by both the Platform, and the Business. """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/media.py b/src/ucp_sdk/models/schemas/shopping/types/media.py new file mode 100644 index 0000000..dc8338a --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/media.py @@ -0,0 +1,51 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import AnyUrl, BaseModel, ConfigDict, Field + + +class Media(BaseModel): + """ + Product media item (image, video, etc.). + """ + + model_config = ConfigDict( + extra="allow", + ) + type: str + """ + Media type. Well-known values: `image`, `video`, `model_3d`. + """ + url: AnyUrl + """ + URL to the media resource. + """ + alt_text: str | None = None + """ + Accessibility text describing the media. + """ + width: int | None = Field(None, ge=1) + """ + Width in pixels (for images/video). + """ + height: int | None = Field(None, ge=1) + """ + Height in pixels (for images/video). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/message_create_request.py b/src/ucp_sdk/models/schemas/shopping/types/message_create_request.py new file mode 100644 index 0000000..531d331 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/message_create_request.py @@ -0,0 +1,43 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import ConfigDict, Field, RootModel + +from . import message_error, message_info, message_warning + + +class MessageCreateRequest( + RootModel[ + message_error.MessageError + | message_warning.MessageWarning + | message_info.MessageInfo + ] +): + model_config = ConfigDict( + frozen=True, + ) + root: ( + message_error.MessageError + | message_warning.MessageWarning + | message_info.MessageInfo + ) = Field(..., title="Message Create Request") + """ + Container for error, warning, or info messages. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/message_error.py b/src/ucp_sdk/models/schemas/shopping/types/message_error.py index d1e6adb..e74117c 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/message_error.py +++ b/src/ucp_sdk/models/schemas/shopping/types/message_error.py @@ -22,6 +22,8 @@ from pydantic import BaseModel, ConfigDict +from . import error_code + class MessageError(BaseModel): model_config = ConfigDict( @@ -31,10 +33,7 @@ class MessageError(BaseModel): """ Message type discriminator. """ - code: str - """ - Error code. Possible values include: missing, invalid, out_of_stock, payment_declined, requires_sign_in, requires_3ds, requires_identity_linking. Freeform codes also allowed. - """ + code: error_code.ErrorCode path: str | None = None """ RFC 9535 JSONPath to the component the message refers to (e.g., $.items[1]). @@ -48,8 +47,11 @@ class MessageError(BaseModel): Human-readable message. """ severity: Literal[ - "recoverable", "requires_buyer_input", "requires_buyer_review" + "recoverable", + "requires_buyer_input", + "requires_buyer_review", + "unrecoverable", ] """ - Declares who resolves this error. 'recoverable': agent can fix via API. 'requires_buyer_input': merchant requires information their API doesn't support collecting programmatically (checkout incomplete). 'requires_buyer_review': buyer must authorize before order placement due to policy, regulatory, or entitlement rules (checkout complete). Errors with 'requires_*' severity contribute to 'status: requires_escalation'. + Reflects the resource state and recommended action. 'recoverable': platform can resolve by modifying inputs and retrying via API. 'requires_buyer_input': merchant requires information their API doesn't support collecting programmatically (checkout incomplete). 'requires_buyer_review': buyer must authorize before order placement due to policy, regulatory, or entitlement rules. 'unrecoverable': no valid resource exists to act on, retry with new resource or inputs. Errors with 'requires_*' severity contribute to 'status: requires_escalation'. """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/message_update_request.py b/src/ucp_sdk/models/schemas/shopping/types/message_update_request.py new file mode 100644 index 0000000..66831d9 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/message_update_request.py @@ -0,0 +1,43 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import ConfigDict, Field, RootModel + +from . import message_error, message_info, message_warning + + +class MessageUpdateRequest( + RootModel[ + message_error.MessageError + | message_warning.MessageWarning + | message_info.MessageInfo + ] +): + model_config = ConfigDict( + frozen=True, + ) + root: ( + message_error.MessageError + | message_warning.MessageWarning + | message_info.MessageInfo + ) = Field(..., title="Message Update Request") + """ + Container for error, warning, or info messages. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/message_warning.py b/src/ucp_sdk/models/schemas/shopping/types/message_warning.py index f5eee46..7025558 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/message_warning.py +++ b/src/ucp_sdk/models/schemas/shopping/types/message_warning.py @@ -20,7 +20,7 @@ from typing import Literal -from pydantic import BaseModel, ConfigDict +from pydantic import AnyUrl, BaseModel, ConfigDict class MessageWarning(BaseModel): @@ -47,3 +47,15 @@ class MessageWarning(BaseModel): """ Content format, default = plain. """ + presentation: str | None = "notice" + """ + Rendering contract for this warning. 'notice' (default): platform MUST display, MAY dismiss. 'disclosure': platform MUST display in proximity to the path-referenced component, MUST NOT hide or auto-dismiss. See specification for full contract. + """ + image_url: AnyUrl | None = None + """ + URL to a required visual element (e.g., warning symbol, energy class label). + """ + url: AnyUrl | None = None + """ + Reference URL for more information (e.g., regulatory site, registry entry, policy page). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/option_value.py b/src/ucp_sdk/models/schemas/shopping/types/option_value.py new file mode 100644 index 0000000..63f0414 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/option_value.py @@ -0,0 +1,39 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict + + +class OptionValue(BaseModel): + """ + A selectable value for a product option. + """ + + model_config = ConfigDict( + extra="allow", + ) + id: str | None = None + """ + Optional server-assigned identifier for this option value. When present in a selected_option, the server SHOULD use it for matching instead of label. + """ + label: str + """ + Display text for this option value (e.g., 'Small', 'Blue'). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/order_confirmation.py b/src/ucp_sdk/models/schemas/shopping/types/order_confirmation.py index b17864a..fd0ed22 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/order_confirmation.py +++ b/src/ucp_sdk/models/schemas/shopping/types/order_confirmation.py @@ -33,6 +33,10 @@ class OrderConfirmation(BaseModel): """ Unique order identifier. """ + label: str | None = None + """ + Human-readable label for identifying the order. MUST only be provided by the business. + """ permalink_url: AnyUrl """ Permalink to access the order on merchant site. diff --git a/src/ucp_sdk/models/schemas/shopping/types/order_line_item.py b/src/ucp_sdk/models/schemas/shopping/types/order_line_item.py index 8f991bc..cd80f04 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/order_line_item.py +++ b/src/ucp_sdk/models/schemas/shopping/types/order_line_item.py @@ -28,19 +28,23 @@ class Quantity(BaseModel): """ - Quantity tracking. Both total and fulfilled are derived from events. + Quantity tracking for the line item. """ model_config = ConfigDict( extra="allow", ) + original: int | None = Field(None, ge=0) + """ + Quantity from the original checkout. + """ total: int = Field(..., ge=0) """ - Current total quantity. + Current total active quantity. May differ from original due to post-order modifications (e.g., returns or cancellations). """ fulfilled: int = Field(..., ge=0) """ - Quantity fulfilled (sum from fulfillment events). + Quantity fulfilled so far. """ @@ -58,15 +62,15 @@ class OrderLineItem(BaseModel): """ quantity: Quantity """ - Quantity tracking. Both total and fulfilled are derived from events. + Quantity tracking for the line item. """ totals: list[total_1.Total] """ Line item totals breakdown. """ - status: Literal["processing", "partial", "fulfilled"] + status: Literal["processing", "partial", "fulfilled", "removed"] """ - Derived status: fulfilled if quantity.fulfilled == quantity.total, partial if quantity.fulfilled > 0, otherwise processing. + Derived status: removed if quantity.total == 0, fulfilled if quantity.total > 0 and quantity.fulfilled == quantity.total, partial if quantity.total > 0 and quantity.fulfilled > 0, otherwise processing. """ parent_id: str | None = None """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/order_line_item_create_request.py b/src/ucp_sdk/models/schemas/shopping/types/order_line_item_create_request.py new file mode 100644 index 0000000..56693e0 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/order_line_item_create_request.py @@ -0,0 +1,77 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field + +from . import item_create_request, total_create_request + + +class Quantity(BaseModel): + """ + Quantity tracking for the line item. + """ + + model_config = ConfigDict( + extra="allow", + ) + original: int | None = Field(None, ge=0) + """ + Quantity from the original checkout. + """ + total: int = Field(..., ge=0) + """ + Current total active quantity. May differ from original due to post-order modifications (e.g., returns or cancellations). + """ + fulfilled: int = Field(..., ge=0) + """ + Quantity fulfilled so far. + """ + + +class OrderLineItemCreateRequest(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Line item identifier. + """ + item: item_create_request.ItemCreateRequest + """ + Product data (id, title, price, image_url). + """ + quantity: Quantity + """ + Quantity tracking for the line item. + """ + totals: list[total_create_request.TotalCreateRequest] + """ + Line item totals breakdown. + """ + status: Literal["processing", "partial", "fulfilled", "removed"] + """ + Derived status: removed if quantity.total == 0, fulfilled if quantity.total > 0 and quantity.fulfilled == quantity.total, partial if quantity.total > 0 and quantity.fulfilled > 0, otherwise processing. + """ + parent_id: str | None = None + """ + Parent line item identifier for any nested structures. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/order_line_item_update_request.py b/src/ucp_sdk/models/schemas/shopping/types/order_line_item_update_request.py new file mode 100644 index 0000000..178d09c --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/order_line_item_update_request.py @@ -0,0 +1,77 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from typing import Literal + +from pydantic import BaseModel, ConfigDict, Field + +from . import item_update_request, total_update_request + + +class Quantity(BaseModel): + """ + Quantity tracking for the line item. + """ + + model_config = ConfigDict( + extra="allow", + ) + original: int | None = Field(None, ge=0) + """ + Quantity from the original checkout. + """ + total: int = Field(..., ge=0) + """ + Current total active quantity. May differ from original due to post-order modifications (e.g., returns or cancellations). + """ + fulfilled: int = Field(..., ge=0) + """ + Quantity fulfilled so far. + """ + + +class OrderLineItemUpdateRequest(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Line item identifier. + """ + item: item_update_request.ItemUpdateRequest + """ + Product data (id, title, price, image_url). + """ + quantity: Quantity + """ + Quantity tracking for the line item. + """ + totals: list[total_update_request.TotalUpdateRequest] + """ + Line item totals breakdown. + """ + status: Literal["processing", "partial", "fulfilled", "removed"] + """ + Derived status: removed if quantity.total == 0, fulfilled if quantity.total > 0 and quantity.fulfilled == quantity.total, partial if quantity.total > 0 and quantity.fulfilled > 0, otherwise processing. + """ + parent_id: str | None = None + """ + Parent line item identifier for any nested structures. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/pagination.py b/src/ucp_sdk/models/schemas/shopping/types/pagination.py new file mode 100644 index 0000000..ca1b307 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/pagination.py @@ -0,0 +1,71 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field + + +class Pagination(BaseModel): + """ + Cursor-based pagination for list operations. + """ + + model_config = ConfigDict( + extra="allow", + ) + + +class Request(BaseModel): + """ + Pagination parameters for requests. + """ + + model_config = ConfigDict( + extra="allow", + ) + cursor: str | None = None + """ + Opaque cursor from previous response. + """ + limit: int | None = Field(10, ge=1) + """ + Requested page size. Implementations MAY clamp to a lower maximum. + """ + + +class Response(BaseModel): + """ + Pagination information in responses. + """ + + model_config = ConfigDict( + extra="allow", + ) + cursor: str | None = None + """ + Cursor to fetch the next page of results. MUST be present when has_next_page is true. + """ + has_next_page: bool + """ + Whether more results are available. + """ + total_count: int | None = Field(None, ge=0) + """ + Total number of matching items, if available. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/price.py b/src/ucp_sdk/models/schemas/shopping/types/price.py new file mode 100644 index 0000000..aff26a1 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/price.py @@ -0,0 +1,41 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field + +from . import amount as amount_1 + + +class Price(BaseModel): + """ + Price with explicit currency. + """ + + model_config = ConfigDict( + extra="allow", + ) + amount: amount_1.Amount + """ + Amount in ISO 4217 minor units. Use 0 for free items. + """ + currency: str = Field(..., pattern="^[A-Z]{3}$") + """ + ISO 4217 currency code (e.g., 'USD', 'EUR', 'GBP'). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/price_filter.py b/src/ucp_sdk/models/schemas/shopping/types/price_filter.py new file mode 100644 index 0000000..7772e95 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/price_filter.py @@ -0,0 +1,41 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict + +from . import amount + + +class PriceFilter(BaseModel): + """ + Price range filter denominated in context.currency. When context.currency matches the presentment currency, businesses apply the filter directly. When it differs, businesses SHOULD convert filter values to the presentment currency before applying; if conversion is not supported, businesses MAY ignore the filter and SHOULD indicate this via a message. When context.currency is absent, filter denomination is ambiguous and businesses MAY ignore it. + """ + + model_config = ConfigDict( + extra="allow", + ) + min: amount.Amount | None = None + """ + Minimum price in ISO 4217 minor units. + """ + max: amount.Amount | None = None + """ + Maximum price in ISO 4217 minor units. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/price_range.py b/src/ucp_sdk/models/schemas/shopping/types/price_range.py new file mode 100644 index 0000000..d3129db --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/price_range.py @@ -0,0 +1,41 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict + +from . import price + + +class PriceRange(BaseModel): + """ + A price range representing minimum and maximum values (e.g., across product variants). + """ + + model_config = ConfigDict( + extra="allow", + ) + min: price.Price + """ + Minimum price in the range. + """ + max: price.Price + """ + Maximum price in the range. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/product.py b/src/ucp_sdk/models/schemas/shopping/types/product.py new file mode 100644 index 0000000..6fa39d0 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/product.py @@ -0,0 +1,97 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from typing import Any + +from pydantic import AnyUrl, BaseModel, ConfigDict, Field + +from . import category +from . import description as description_1 +from . import media as media_1 +from . import price_range as price_range_1 +from . import product_option +from . import rating as rating_1 +from . import variant + + +class Product(BaseModel): + """ + A product in the catalog with variants and options. + """ + + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Global ID (GID) uniquely identifying this product. + """ + handle: str | None = None + """ + URL-safe slug for SEO-friendly URLs (e.g., 'blue-runner-pro'). Use id for stable API references. + """ + title: str + """ + Product title. + """ + description: description_1.Description + """ + Product description in one or more formats. + """ + url: AnyUrl | None = None + """ + Canonical product page URL. + """ + categories: list[category.Category] | None = None + """ + Product categories with optional taxonomy identifiers. + """ + price_range: price_range_1.PriceRange + """ + Price range across all variants. + """ + list_price_range: price_range_1.PriceRange | None = None + """ + List price range before discounts (for strikethrough display). + """ + media: list[media_1.Media] | None = None + """ + Product media (images, videos, 3D models). First item is the featured media for listings. + """ + options: list[product_option.ProductOption] | None = None + """ + Product options (Size, Color, etc.). + """ + variants: list[variant.Variant] = Field(..., min_length=1) + """ + Purchasable variants of this product. First item is the featured variant for listings. + """ + rating: rating_1.Rating | None = None + """ + Aggregate product rating. + """ + tags: list[str] | None = None + """ + Product tags for categorization and search. + """ + metadata: dict[str, Any] | None = None + """ + Business-defined custom data extending the standard product model. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/product_option.py b/src/ucp_sdk/models/schemas/shopping/types/product_option.py new file mode 100644 index 0000000..297717e --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/product_option.py @@ -0,0 +1,41 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field + +from . import option_value + + +class ProductOption(BaseModel): + """ + A product option such as size, color, or material. + """ + + model_config = ConfigDict( + extra="allow", + ) + name: str + """ + Option name (e.g., 'Size', 'Color'). + """ + values: list[option_value.OptionValue] = Field(..., min_length=1) + """ + Available values for this option. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/rating.py b/src/ucp_sdk/models/schemas/shopping/types/rating.py new file mode 100644 index 0000000..79388fd --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/rating.py @@ -0,0 +1,47 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field + + +class Rating(BaseModel): + """ + Product rating aggregate. + """ + + model_config = ConfigDict( + extra="allow", + ) + value: float = Field(..., ge=0.0) + """ + Average rating value. + """ + scale_min: float | None = Field(1, ge=0.0) + """ + Minimum value on the rating scale (e.g., 1 for 1-5 stars). + """ + scale_max: float = Field(..., ge=1.0) + """ + Maximum value on the rating scale (e.g., 5 for 5-star). + """ + count: int | None = Field(None, ge=0) + """ + Number of reviews contributing to the rating. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/reverse_domain_name.py b/src/ucp_sdk/models/schemas/shopping/types/reverse_domain_name.py new file mode 100644 index 0000000..7bb3f46 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/reverse_domain_name.py @@ -0,0 +1,35 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import ConfigDict, Field, RootModel + + +class ReverseDomainName(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field( + ..., + pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$", + title="Reverse Domain Name", + ) + """ + Reverse-domain identifier used for collision-safe namespacing of capabilities, services, handlers, eligibility claims, and extension-contributed keys. Must contain at least two dot-separated segments (e.g., 'dev.ucp.shopping.checkout', 'com.example.loyalty_gold'). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/reverse_domain_name_create_request.py b/src/ucp_sdk/models/schemas/shopping/types/reverse_domain_name_create_request.py new file mode 100644 index 0000000..d748408 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/reverse_domain_name_create_request.py @@ -0,0 +1,35 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import ConfigDict, Field, RootModel + + +class ReverseDomainNameCreateRequest(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field( + ..., + pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$", + title="Reverse Domain Name Create Request", + ) + """ + Reverse-domain identifier used for collision-safe namespacing of capabilities, services, handlers, eligibility claims, and extension-contributed keys. Must contain at least two dot-separated segments (e.g., 'dev.ucp.shopping.checkout', 'com.example.loyalty_gold'). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/reverse_domain_name_update_request.py b/src/ucp_sdk/models/schemas/shopping/types/reverse_domain_name_update_request.py new file mode 100644 index 0000000..8f95bf4 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/reverse_domain_name_update_request.py @@ -0,0 +1,35 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import ConfigDict, Field, RootModel + + +class ReverseDomainNameUpdateRequest(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field( + ..., + pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$", + title="Reverse Domain Name Update Request", + ) + """ + Reverse-domain identifier used for collision-safe namespacing of capabilities, services, handlers, eligibility claims, and extension-contributed keys. Must contain at least two dot-separated segments (e.g., 'dev.ucp.shopping.checkout', 'com.example.loyalty_gold'). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/search_filters.py b/src/ucp_sdk/models/schemas/shopping/types/search_filters.py new file mode 100644 index 0000000..6b35cd2 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/search_filters.py @@ -0,0 +1,38 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict + +from . import price_filter + + +class SearchFilters(BaseModel): + """ + Filter criteria to narrow search results. All specified filters combine with AND logic. + """ + + model_config = ConfigDict( + extra="allow", + ) + categories: list[str] | None = None + """ + Filter by product categories (OR logic — matches products in any listed categories). Values match against the value field in product category entries. Valid values can be discovered from the categories field in search results, merchant documentation, or standard taxonomies that businesses may align with. + """ + price: price_filter.PriceFilter | None = None diff --git a/src/ucp_sdk/models/schemas/shopping/types/selected_option.py b/src/ucp_sdk/models/schemas/shopping/types/selected_option.py new file mode 100644 index 0000000..0d6b9f4 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/selected_option.py @@ -0,0 +1,43 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict + + +class SelectedOption(BaseModel): + """ + A specific option selection on a variant (e.g., Size: Large). + """ + + model_config = ConfigDict( + extra="allow", + ) + name: str + """ + Option name (e.g., 'Size'). + """ + id: str | None = None + """ + Optional option value identifier from option_value.id. When present, the server SHOULD use it for matching; name and label remain required for display. + """ + label: str + """ + Selected option label (e.g., 'Large'). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/signals.py b/src/ucp_sdk/models/schemas/shopping/types/signals.py new file mode 100644 index 0000000..d80f329 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/signals.py @@ -0,0 +1,39 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field + + +class Signals(BaseModel): + """ + Environment data provided by the platform to support authorization and abuse prevention. Values MUST NOT be buyer-asserted claims — platforms provide signals based on direct observation or independently verifiable third-party attestations. All signal keys MUST use reverse-domain naming to ensure provenance and prevent collisions when multiple extensions contribute to the shared namespace. + """ + + model_config = ConfigDict( + extra="allow", + ) + dev_ucp_buyer_ip: str | None = Field(None, alias="dev.ucp.buyer_ip") + """ + Client's IP address (IPv4 or IPv6). + """ + dev_ucp_user_agent: str | None = Field(None, alias="dev.ucp.user_agent") + """ + Client's HTTP User-Agent header or equivalent. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/signals_complete_request.py b/src/ucp_sdk/models/schemas/shopping/types/signals_complete_request.py new file mode 100644 index 0000000..436b901 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/signals_complete_request.py @@ -0,0 +1,39 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field + + +class SignalsCompleteRequest(BaseModel): + """ + Environment data provided by the platform to support authorization and abuse prevention. Values MUST NOT be buyer-asserted claims — platforms provide signals based on direct observation or independently verifiable third-party attestations. All signal keys MUST use reverse-domain naming to ensure provenance and prevent collisions when multiple extensions contribute to the shared namespace. + """ + + model_config = ConfigDict( + extra="allow", + ) + dev_ucp_buyer_ip: str | None = Field(None, alias="dev.ucp.buyer_ip") + """ + Client's IP address (IPv4 or IPv6). + """ + dev_ucp_user_agent: str | None = Field(None, alias="dev.ucp.user_agent") + """ + Client's HTTP User-Agent header or equivalent. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/signals_create_request.py b/src/ucp_sdk/models/schemas/shopping/types/signals_create_request.py new file mode 100644 index 0000000..fd1630d --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/signals_create_request.py @@ -0,0 +1,39 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field + + +class SignalsCreateRequest(BaseModel): + """ + Environment data provided by the platform to support authorization and abuse prevention. Values MUST NOT be buyer-asserted claims — platforms provide signals based on direct observation or independently verifiable third-party attestations. All signal keys MUST use reverse-domain naming to ensure provenance and prevent collisions when multiple extensions contribute to the shared namespace. + """ + + model_config = ConfigDict( + extra="allow", + ) + dev_ucp_buyer_ip: str | None = Field(None, alias="dev.ucp.buyer_ip") + """ + Client's IP address (IPv4 or IPv6). + """ + dev_ucp_user_agent: str | None = Field(None, alias="dev.ucp.user_agent") + """ + Client's HTTP User-Agent header or equivalent. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/signals_update_request.py b/src/ucp_sdk/models/schemas/shopping/types/signals_update_request.py new file mode 100644 index 0000000..09ea148 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/signals_update_request.py @@ -0,0 +1,39 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field + + +class SignalsUpdateRequest(BaseModel): + """ + Environment data provided by the platform to support authorization and abuse prevention. Values MUST NOT be buyer-asserted claims — platforms provide signals based on direct observation or independently verifiable third-party attestations. All signal keys MUST use reverse-domain naming to ensure provenance and prevent collisions when multiple extensions contribute to the shared namespace. + """ + + model_config = ConfigDict( + extra="allow", + ) + dev_ucp_buyer_ip: str | None = Field(None, alias="dev.ucp.buyer_ip") + """ + Client's IP address (IPv4 or IPv6). + """ + dev_ucp_user_agent: str | None = Field(None, alias="dev.ucp.user_agent") + """ + Client's HTTP User-Agent header or equivalent. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/signed_amount.py b/src/ucp_sdk/models/schemas/shopping/types/signed_amount.py new file mode 100644 index 0000000..3ee19e1 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/signed_amount.py @@ -0,0 +1,31 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import ConfigDict, Field, RootModel + + +class SignedAmount(RootModel[int]): + model_config = ConfigDict( + frozen=True, + ) + root: int = Field(..., title="Signed Amount") + """ + Monetary amount in the currency's minor unit as defined by ISO 4217. Refer to the currency's exponent to determine minor-to-major ratio (e.g., 2 for USD, 0 for JPY, 3 for KWD). May be negative — the sign is intrinsic to the value (e.g., discounts are negative, charges are positive). + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/total.py b/src/ucp_sdk/models/schemas/shopping/types/total.py index 623c2dd..006d68b 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/total.py +++ b/src/ucp_sdk/models/schemas/shopping/types/total.py @@ -18,32 +18,25 @@ from __future__ import annotations -from typing import Literal +from pydantic import BaseModel, ConfigDict -from pydantic import BaseModel, ConfigDict, Field +from . import signed_amount class Total(BaseModel): + """ + A cost breakdown entry with a category, amount, and optional display text. + """ + model_config = ConfigDict( extra="allow", ) - type: Literal[ - "items_discount", - "subtotal", - "discount", - "fulfillment", - "tax", - "fee", - "total", - ] + type: str """ - Type of total categorization. + Cost category. Well-known values: subtotal, items_discount, discount, fulfillment, tax, fee, total. Businesses MAY use additional values. """ display_text: str | None = None """ Text to display against the amount. Should reflect appropriate method (e.g., 'Shipping', 'Delivery'). """ - amount: int = Field(..., ge=0) - """ - If type == total, sums subtotal - discount + fulfillment + tax + fee. Should be >= 0. Amount in minor (cents) currency units. - """ + amount: signed_amount.SignedAmount diff --git a/src/ucp_sdk/models/schemas/shopping/types/total_create_request.py b/src/ucp_sdk/models/schemas/shopping/types/total_create_request.py index 6318099..e3eb186 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/total_create_request.py +++ b/src/ucp_sdk/models/schemas/shopping/types/total_create_request.py @@ -22,6 +22,10 @@ class TotalCreateRequest(BaseModel): + """ + A cost breakdown entry with a category, amount, and optional display text. + """ + model_config = ConfigDict( extra="allow", ) diff --git a/src/ucp_sdk/models/schemas/shopping/types/total_update_request.py b/src/ucp_sdk/models/schemas/shopping/types/total_update_request.py index e8ca037..0f58f46 100644 --- a/src/ucp_sdk/models/schemas/shopping/types/total_update_request.py +++ b/src/ucp_sdk/models/schemas/shopping/types/total_update_request.py @@ -22,6 +22,10 @@ class TotalUpdateRequest(BaseModel): + """ + A cost breakdown entry with a category, amount, and optional display text. + """ + model_config = ConfigDict( extra="allow", ) diff --git a/src/ucp_sdk/models/schemas/shopping/types/totals.py b/src/ucp_sdk/models/schemas/shopping/types/totals.py new file mode 100644 index 0000000..0374f4b --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/totals.py @@ -0,0 +1,63 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field, RootModel + +from . import signed_amount +from .total import Total as Total_1 + + +class Line(BaseModel): + """ + Sub-line entry. Additional metadata MAY be included. + """ + + model_config = ConfigDict( + extra="allow", + ) + display_text: str + """ + Human-readable label for this sub-line. + """ + amount: signed_amount.SignedAmount + + +class Total(Total_1): + model_config = ConfigDict( + extra="allow", + ) + lines: list[Line] | None = None + """ + Optional itemized breakdown. The parent entry is always rendered; lines are supplementary. Sum of line amounts MUST equal the parent entry amount. + """ + + +class Totals(RootModel[list[Total]]): + """ + Pricing breakdown provided by the business. MUST contain exactly one subtotal and one total entry. Detail types (tax, fee, discount, fulfillment) may appear multiple times for itemization. Platforms MUST render all entries in order using display_text and amount. + """ + + model_config = ConfigDict( + frozen=True, + ) + root: list[Total] = Field(..., title="Totals") + """ + Pricing breakdown provided by the business. MUST contain exactly one subtotal and one total entry. Detail types (tax, fee, discount, fulfillment) may appear multiple times for itemization. Platforms MUST render all entries in order using display_text and amount. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/totals_create_request.py b/src/ucp_sdk/models/schemas/shopping/types/totals_create_request.py new file mode 100644 index 0000000..a4823e1 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/totals_create_request.py @@ -0,0 +1,77 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field, RootModel + +from . import signed_amount +from .total import Total + + +class Line(BaseModel): + """ + Sub-line entry. Additional metadata MAY be included. + """ + + model_config = ConfigDict( + extra="allow", + ) + display_text: str + """ + Human-readable label for this sub-line. + """ + amount: signed_amount.SignedAmount + + +class TotalsCreateRequestItem(Total): + model_config = ConfigDict( + extra="allow", + ) + lines: list[Line] | None = None + """ + Optional itemized breakdown. The parent entry is always rendered; lines are supplementary. Sum of line amounts MUST equal the parent entry amount. + """ + + +class TotalsCreateRequest1(BaseModel): + """ + Pricing breakdown provided by the business. MUST contain exactly one subtotal and one total entry. Detail types (tax, fee, discount, fulfillment) may appear multiple times for itemization. Platforms MUST render all entries in order using display_text and amount. + """ + + model_config = ConfigDict( + extra="allow", + ) + + +class TotalsCreateRequest( + RootModel[list[TotalsCreateRequestItem] | TotalsCreateRequest1] +): + """ + Pricing breakdown provided by the business. MUST contain exactly one subtotal and one total entry. Detail types (tax, fee, discount, fulfillment) may appear multiple times for itemization. Platforms MUST render all entries in order using display_text and amount. + """ + + model_config = ConfigDict( + frozen=True, + ) + root: list[TotalsCreateRequestItem] | TotalsCreateRequest1 = Field( + ..., title="Totals Create Request" + ) + """ + Pricing breakdown provided by the business. MUST contain exactly one subtotal and one total entry. Detail types (tax, fee, discount, fulfillment) may appear multiple times for itemization. Platforms MUST render all entries in order using display_text and amount. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/totals_update_request.py b/src/ucp_sdk/models/schemas/shopping/types/totals_update_request.py new file mode 100644 index 0000000..9617727 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/totals_update_request.py @@ -0,0 +1,77 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field, RootModel + +from . import signed_amount +from .total import Total + + +class Line(BaseModel): + """ + Sub-line entry. Additional metadata MAY be included. + """ + + model_config = ConfigDict( + extra="allow", + ) + display_text: str + """ + Human-readable label for this sub-line. + """ + amount: signed_amount.SignedAmount + + +class TotalsUpdateRequestItem(Total): + model_config = ConfigDict( + extra="allow", + ) + lines: list[Line] | None = None + """ + Optional itemized breakdown. The parent entry is always rendered; lines are supplementary. Sum of line amounts MUST equal the parent entry amount. + """ + + +class TotalsUpdateRequest1(BaseModel): + """ + Pricing breakdown provided by the business. MUST contain exactly one subtotal and one total entry. Detail types (tax, fee, discount, fulfillment) may appear multiple times for itemization. Platforms MUST render all entries in order using display_text and amount. + """ + + model_config = ConfigDict( + extra="allow", + ) + + +class TotalsUpdateRequest( + RootModel[list[TotalsUpdateRequestItem] | TotalsUpdateRequest1] +): + """ + Pricing breakdown provided by the business. MUST contain exactly one subtotal and one total entry. Detail types (tax, fee, discount, fulfillment) may appear multiple times for itemization. Platforms MUST render all entries in order using display_text and amount. + """ + + model_config = ConfigDict( + frozen=True, + ) + root: list[TotalsUpdateRequestItem] | TotalsUpdateRequest1 = Field( + ..., title="Totals Update Request" + ) + """ + Pricing breakdown provided by the business. MUST contain exactly one subtotal and one total entry. Detail types (tax, fee, discount, fulfillment) may appear multiple times for itemization. Platforms MUST render all entries in order using display_text and amount. + """ diff --git a/src/ucp_sdk/models/schemas/shopping/types/variant.py b/src/ucp_sdk/models/schemas/shopping/types/variant.py new file mode 100644 index 0000000..e45f245 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/variant.py @@ -0,0 +1,226 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from typing import Any + +from pydantic import AnyUrl, BaseModel, ConfigDict, Field + +from . import amount as amount_1 +from . import category +from . import description as description_1 +from . import link +from . import media as media_1 +from . import price as price_1 +from . import rating as rating_1 +from . import selected_option + + +class Barcode(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + type: str + """ + Barcode standard. Well-known values: UPC, EAN, ISBN, GTIN, JAN. + """ + value: str + """ + Barcode value. + """ + + +class Measure(BaseModel): + """ + Product quantity in packaging (e.g., 750ml bottle). + """ + + model_config = ConfigDict( + extra="allow", + ) + value: float + """ + Package quantity. + """ + unit: str + """ + Unit of measurement. + """ + + +class Reference(BaseModel): + """ + Denominator for unit price display (e.g., per 100ml, per 1kg). + """ + + model_config = ConfigDict( + extra="allow", + ) + value: int + """ + Reference quantity. + """ + unit: str + """ + Unit of measurement. + """ + + +class UnitPrice(BaseModel): + """ + Price per standard unit of measurement. MAY be omitted when unit pricing does not apply. + """ + + model_config = ConfigDict( + extra="allow", + ) + amount: amount_1.Amount + """ + Unit price in ISO 4217 minor units. Business MUST return precomputed unit price value: (variant.price / measure.value) * reference.value. + """ + currency: str = Field(..., pattern="^[A-Z]{3}$") + """ + ISO 4217 currency code. + """ + measure: Measure + """ + Product quantity in packaging (e.g., 750ml bottle). + """ + reference: Reference + """ + Denominator for unit price display (e.g., per 100ml, per 1kg). + """ + + +class Availability(BaseModel): + """ + Variant availability for purchase. + """ + + model_config = ConfigDict( + extra="allow", + ) + available: bool | None = None + """ + Whether this variant can be purchased. See status for fulfillment details. + """ + status: str | None = None + """ + Qualifies available with fulfillment state. Well-known values: `in_stock`, `backorder`, `preorder`, `out_of_stock`, `discontinued`. + """ + + +class Seller(BaseModel): + """ + Optional seller context for this variant. + """ + + model_config = ConfigDict( + extra="allow", + ) + name: str | None = None + """ + Seller display name. + """ + links: list[link.Link] | None = None + """ + Seller policy and information links. + """ + + +class Variant(BaseModel): + """ + A purchasable variant of a product with specific option selections. + """ + + model_config = ConfigDict( + extra="allow", + ) + id: str + """ + Global ID (GID) uniquely identifying this variant. Used as item.id in checkout. + """ + sku: str | None = None + """ + Business-assigned identifier for inventory and fulfillment. + """ + barcodes: list[Barcode] | None = None + """ + Industry-standard product identifiers for cross-reference and correlation. + """ + handle: str | None = None + """ + URL-safe variant handle/slug. + """ + title: str + """ + Variant display title (e.g., 'Blue / Large'). + """ + description: description_1.Description + """ + Variant description in one or more formats. + """ + url: AnyUrl | None = None + """ + Canonical variant page URL. + """ + categories: list[category.Category] | None = None + """ + Variant categories with optional taxonomy identifiers. + """ + price: price_1.Price + """ + Current selling price. + """ + list_price: price_1.Price | None = None + """ + List price before discounts (for strikethrough display). + """ + unit_price: UnitPrice | None = None + """ + Price per standard unit of measurement. MAY be omitted when unit pricing does not apply. + """ + availability: Availability | None = None + """ + Variant availability for purchase. + """ + options: list[selected_option.SelectedOption] | None = None + """ + Option values that define this variant (e.g., Color: Blue, Size: Large). + """ + media: list[media_1.Media] | None = None + """ + Variant media (images, videos, 3D models). First item is the featured media for listings. + """ + rating: rating_1.Rating | None = None + """ + Variant rating. + """ + tags: list[str] | None = None + """ + Variant tags for categorization and search. + """ + metadata: dict[str, Any] | None = None + """ + Business-defined custom data extending the standard variant model. + """ + seller: Seller | None = None + """ + Optional seller context for this variant. + """ diff --git a/src/ucp_sdk/models/schemas/transports/__init__.py b/src/ucp_sdk/models/schemas/transports/__init__.py index 1252d6b..421dc21 100644 --- a/src/ucp_sdk/models/schemas/transports/__init__.py +++ b/src/ucp_sdk/models/schemas/transports/__init__.py @@ -15,3 +15,4 @@ # generated by datamodel-codegen # pylint: disable=all # pyformat: disable + diff --git a/src/ucp_sdk/models/schemas/transports/embedded_config.py b/src/ucp_sdk/models/schemas/transports/embedded_config.py index 8f3cba7..c632826 100644 --- a/src/ucp_sdk/models/schemas/transports/embedded_config.py +++ b/src/ucp_sdk/models/schemas/transports/embedded_config.py @@ -18,12 +18,14 @@ from __future__ import annotations +from typing import Literal + from pydantic import BaseModel, ConfigDict class EmbeddedTransportConfig(BaseModel): """ - Per-checkout configuration for embedded transport binding. Allows businesses to vary ECP availability and delegations based on cart contents, agent authorization, or policy. + Per-session configuration for embedded transport binding. Allows businesses to vary EP availability and delegations based on cart contents, agent authorization, or policy. """ model_config = ConfigDict( @@ -31,5 +33,9 @@ class EmbeddedTransportConfig(BaseModel): ) delegate: list[str] | None = None """ - Delegations the business allows. At service-level, declares available delegations. In checkout responses, confirms accepted delegations for this session. + Delegations the business allows. At service-level, declares available delegations. In UCP responses, confirms accepted delegations for this session. + """ + color_scheme: list[Literal["light", "dark"]] | None = None + """ + Color schemes the business supports. Hosts use ec_color_scheme query parameter to request a scheme from this list. """ diff --git a/src/ucp_sdk/models/schemas/ucp.py b/src/ucp_sdk/models/schemas/ucp.py index 9f98a49..c0b2652 100644 --- a/src/ucp_sdk/models/schemas/ucp.py +++ b/src/ucp_sdk/models/schemas/ucp.py @@ -18,11 +18,12 @@ from __future__ import annotations -from typing import Any +from typing import Any, Literal from pydantic import AnyUrl, BaseModel, ConfigDict, Field, RootModel from . import capability, payment_handler, service +from .shopping.types import reverse_domain_name class Version(RootModel[str]): @@ -35,13 +36,41 @@ class Version(RootModel[str]): """ -class ReverseDomainName(RootModel[str]): +class VersionConstraint(BaseModel): + """ + Version range requirement with minimum and optional maximum. + """ + model_config = ConfigDict( - frozen=True, + extra="allow", ) - root: str = Field(..., pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$") + min: Version + """ + Minimum required version (inclusive). + """ + max: Version | None = None + """ + Maximum compatible version (inclusive). When absent, no upper bound. + """ + + +class Requires(BaseModel): + """ + Version requirements for extension schemas. Declares minimum (and optionally maximum) protocol and capability versions needed for correct operation. + """ + + model_config = ConfigDict( + extra="allow", + ) + protocol: VersionConstraint | None = None + """ + Required protocol version. + """ + capabilities: ( + dict[reverse_domain_name.ReverseDomainName, VersionConstraint] | None + ) = None """ - Reverse-domain identifier (e.g., com.google.pay, dev.ucp.shopping.checkout) + Required capability versions, keyed by capability name. Keys must be a subset of the extension's $defs keys. """ @@ -75,13 +104,6 @@ class Entity(BaseModel): """ -class ResponseCartSchema(RootModel[Any]): - model_config = ConfigDict( - frozen=True, - ) - root: Any - - class Base(BaseModel): """ Base UCP metadata with shared properties for all schema types. @@ -91,22 +113,60 @@ class Base(BaseModel): extra="allow", ) version: Version - services: dict[ReverseDomainName, list[service.Base]] | None = None + status: Literal["success", "error"] | None = "success" + """ + Application-level status of the UCP operation. + """ + services: ( + dict[reverse_domain_name.ReverseDomainName, list[service.Base]] | None + ) = None """ Service registry keyed by reverse-domain name. """ - capabilities: dict[ReverseDomainName, list[capability.Base]] | None = None + capabilities: ( + dict[reverse_domain_name.ReverseDomainName, list[capability.Base]] + | None + ) = None """ Capability registry keyed by reverse-domain name. """ payment_handlers: ( - dict[ReverseDomainName, list[payment_handler.Base]] | None + dict[reverse_domain_name.ReverseDomainName, list[payment_handler.Base]] + | None ) = None """ Payment handler registry keyed by reverse-domain name. """ +class Success(Base): + """ + UCP metadata with status 'success'. Use for response branches that carry the expected payload. + """ + + model_config = ConfigDict( + extra="allow", + ) + status: Literal["success"] + """ + Application-level status of the UCP operation. + """ + + +class Error(Base): + """ + UCP metadata with status 'error'. Use for response branches that carry error information. + """ + + model_config = ConfigDict( + extra="allow", + ) + status: Literal["error"] + """ + Application-level status of the UCP operation. + """ + + class PlatformSchema(Base): """ Full UCP metadata for platform-level configuration. Hosted at a URI advertised by the platform in request headers. @@ -115,18 +175,25 @@ class PlatformSchema(Base): model_config = ConfigDict( extra="allow", ) - services: dict[ReverseDomainName, list[service.PlatformSchema3]] + services: dict[ + reverse_domain_name.ReverseDomainName, list[service.PlatformSchema5] + ] """ Service registry keyed by reverse-domain name. """ capabilities: ( - dict[ReverseDomainName, list[capability.PlatformSchema]] | None + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.PlatformSchema], + ] + | None ) = None """ Capability registry keyed by reverse-domain name. """ payment_handlers: dict[ - ReverseDomainName, list[payment_handler.PlatformSchema] + reverse_domain_name.ReverseDomainName, + list[payment_handler.PlatformSchema], ] """ Payment handler registry keyed by reverse-domain name. @@ -141,18 +208,29 @@ class BusinessSchema(Base): model_config = ConfigDict( extra="allow", ) - services: dict[ReverseDomainName, list[service.BusinessSchema2]] + supported_versions: dict[Version, AnyUrl] | None = None + """ + Previous protocol versions this business supports, mapped to profile URIs. Businesses that support older protocol versions SHOULD advertise each version and link to its profile. Each URI points to a complete, self-contained profile for that version. When omitted, only `version` is supported. + """ + services: dict[ + reverse_domain_name.ReverseDomainName, list[service.BusinessSchema2] + ] """ Service registry keyed by reverse-domain name. """ capabilities: ( - dict[ReverseDomainName, list[capability.BusinessSchema]] | None + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.BusinessSchema], + ] + | None ) = None """ Capability registry keyed by reverse-domain name. """ payment_handlers: dict[ - ReverseDomainName, list[payment_handler.BusinessSchema] + reverse_domain_name.ReverseDomainName, + list[payment_handler.BusinessSchema], ] """ Payment handler registry keyed by reverse-domain name. @@ -167,20 +245,28 @@ class ResponseCheckoutSchema(Base): model_config = ConfigDict( extra="allow", ) - services: dict[ReverseDomainName, list[service.ResponseSchema2]] | None = ( - None - ) + services: ( + dict[ + reverse_domain_name.ReverseDomainName, list[service.ResponseSchema2] + ] + | None + ) = None """ Service registry keyed by reverse-domain name. """ capabilities: ( - dict[ReverseDomainName, list[capability.ResponseSchema]] | None + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.ResponseSchema], + ] + | None ) = None """ Capability registry keyed by reverse-domain name. """ payment_handlers: dict[ - ReverseDomainName, list[payment_handler.ResponseSchema] + reverse_domain_name.ReverseDomainName, + list[payment_handler.ResponseSchema], ] """ Payment handler registry keyed by reverse-domain name. @@ -196,7 +282,51 @@ class ResponseOrderSchema(Base): extra="allow", ) capabilities: ( - dict[ReverseDomainName, list[capability.ResponseSchema]] | None + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.ResponseSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + + +class ResponseCartSchema(Base): + """ + UCP metadata for cart responses. No payment handlers needed pre-checkout. + """ + + model_config = ConfigDict( + extra="allow", + ) + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.ResponseSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + + +class ResponseCatalogSchema(Base): + """ + UCP metadata for catalog responses. + """ + + model_config = ConfigDict( + extra="allow", + ) + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.ResponseSchema], + ] + | None ) = None """ Capability registry keyed by reverse-domain name. diff --git a/src/ucp_sdk/models/schemas/ucp_create_request.py b/src/ucp_sdk/models/schemas/ucp_create_request.py new file mode 100644 index 0000000..90bcba7 --- /dev/null +++ b/src/ucp_sdk/models/schemas/ucp_create_request.py @@ -0,0 +1,357 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from typing import Any, Literal + +from pydantic import AnyUrl, BaseModel, ConfigDict, Field, RootModel + +from . import capability, payment_handler, service +from .shopping.types import reverse_domain_name + + +class Version(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field(..., pattern="^\\d{4}-\\d{2}-\\d{2}$") + """ + UCP version in YYYY-MM-DD format. + """ + + +class VersionConstraint(BaseModel): + """ + Version range requirement with minimum and optional maximum. + """ + + model_config = ConfigDict( + extra="allow", + ) + min: Version + """ + Minimum required version (inclusive). + """ + max: Version | None = None + """ + Maximum compatible version (inclusive). When absent, no upper bound. + """ + + +class Requires(BaseModel): + """ + Version requirements for extension schemas. Declares minimum (and optionally maximum) protocol and capability versions needed for correct operation. + """ + + model_config = ConfigDict( + extra="allow", + ) + protocol: VersionConstraint | None = None + """ + Required protocol version. + """ + capabilities: ( + dict[reverse_domain_name.ReverseDomainName, VersionConstraint] | None + ) = None + """ + Required capability versions, keyed by capability name. Keys must be a subset of the extension's $defs keys. + """ + + +class Entity(BaseModel): + """ + Shared foundation for all UCP entities. + """ + + model_config = ConfigDict( + extra="allow", + ) + version: Version + """ + Entity version in YYYY-MM-DD format. + """ + spec: AnyUrl | None = None + """ + URL to human-readable specification document. + """ + schema_: AnyUrl | None = Field(None, alias="schema") + """ + URL to JSON Schema defining this entity's structure and payloads. + """ + id: str | None = None + """ + Unique identifier for this entity instance. Used to disambiguate when multiple instances exist. + """ + config: dict[str, Any] | None = None + """ + Entity-specific configuration. Structure defined by each entity's schema. + """ + + +class Base(BaseModel): + """ + Base UCP metadata with shared properties for all schema types. + """ + + model_config = ConfigDict( + extra="allow", + ) + version: Version + status: Literal["success", "error"] | None = "success" + """ + Application-level status of the UCP operation. + """ + services: ( + dict[reverse_domain_name.ReverseDomainName, list[service.Base]] | None + ) = None + """ + Service registry keyed by reverse-domain name. + """ + capabilities: ( + dict[reverse_domain_name.ReverseDomainName, list[capability.Base]] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + payment_handlers: ( + dict[reverse_domain_name.ReverseDomainName, list[payment_handler.Base]] + | None + ) = None + """ + Payment handler registry keyed by reverse-domain name. + """ + + +class Success(Base): + """ + UCP metadata with status 'success'. Use for response branches that carry the expected payload. + """ + + model_config = ConfigDict( + extra="allow", + ) + status: Literal["success"] + """ + Application-level status of the UCP operation. + """ + + +class Error(Base): + """ + UCP metadata with status 'error'. Use for response branches that carry error information. + """ + + model_config = ConfigDict( + extra="allow", + ) + status: Literal["error"] + """ + Application-level status of the UCP operation. + """ + + +class PlatformSchema(Base): + """ + Full UCP metadata for platform-level configuration. Hosted at a URI advertised by the platform in request headers. + """ + + model_config = ConfigDict( + extra="allow", + ) + services: dict[ + reverse_domain_name.ReverseDomainName, list[service.PlatformSchema5] + ] + """ + Service registry keyed by reverse-domain name. + """ + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.PlatformSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + payment_handlers: dict[ + reverse_domain_name.ReverseDomainName, + list[payment_handler.PlatformSchema], + ] + """ + Payment handler registry keyed by reverse-domain name. + """ + + +class BusinessSchema(Base): + """ + UCP metadata for business/merchant-level configuration. Subset of platform schema with business-specific settings. + """ + + model_config = ConfigDict( + extra="allow", + ) + supported_versions: dict[Version, AnyUrl] | None = None + """ + Previous protocol versions this business supports, mapped to profile URIs. Businesses that support older protocol versions SHOULD advertise each version and link to its profile. Each URI points to a complete, self-contained profile for that version. When omitted, only `version` is supported. + """ + services: dict[ + reverse_domain_name.ReverseDomainName, list[service.BusinessSchema2] + ] + """ + Service registry keyed by reverse-domain name. + """ + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.BusinessSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + payment_handlers: dict[ + reverse_domain_name.ReverseDomainName, + list[payment_handler.BusinessSchema], + ] + """ + Payment handler registry keyed by reverse-domain name. + """ + + +class ResponseCheckoutSchema(Base): + """ + UCP metadata for checkout responses. + """ + + model_config = ConfigDict( + extra="allow", + ) + services: ( + dict[ + reverse_domain_name.ReverseDomainName, list[service.ResponseSchema2] + ] + | None + ) = None + """ + Service registry keyed by reverse-domain name. + """ + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.ResponseSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + payment_handlers: dict[ + reverse_domain_name.ReverseDomainName, + list[payment_handler.ResponseSchema], + ] + """ + Payment handler registry keyed by reverse-domain name. + """ + + +class ResponseOrderSchema(Base): + """ + UCP metadata for order responses. No payment handlers needed post-purchase. + """ + + model_config = ConfigDict( + extra="allow", + ) + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.ResponseSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + + +class ResponseCartSchema(Base): + """ + UCP metadata for cart responses. No payment handlers needed pre-checkout. + """ + + model_config = ConfigDict( + extra="allow", + ) + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.ResponseSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + + +class ResponseCatalogSchema(Base): + """ + UCP metadata for catalog responses. + """ + + model_config = ConfigDict( + extra="allow", + ) + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.ResponseSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + + +class UcpMetadataCreateRequest( + RootModel[ + PlatformSchema + | BusinessSchema + | ResponseCheckoutSchema + | ResponseOrderSchema + | ResponseCartSchema + ] +): + model_config = ConfigDict( + frozen=True, + ) + root: ( + PlatformSchema + | BusinessSchema + | ResponseCheckoutSchema + | ResponseOrderSchema + | ResponseCartSchema + ) = Field(..., title="UCP Metadata Create Request") + """ + Protocol metadata for discovery profiles and responses. Uses slim schema pattern with context-specific required fields. + """ diff --git a/src/ucp_sdk/models/schemas/ucp_update_request.py b/src/ucp_sdk/models/schemas/ucp_update_request.py new file mode 100644 index 0000000..9279a1a --- /dev/null +++ b/src/ucp_sdk/models/schemas/ucp_update_request.py @@ -0,0 +1,357 @@ +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# generated by datamodel-codegen +# pylint: disable=all +# pyformat: disable + +from __future__ import annotations + +from typing import Any, Literal + +from pydantic import AnyUrl, BaseModel, ConfigDict, Field, RootModel + +from . import capability, payment_handler, service +from .shopping.types import reverse_domain_name + + +class Version(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field(..., pattern="^\\d{4}-\\d{2}-\\d{2}$") + """ + UCP version in YYYY-MM-DD format. + """ + + +class VersionConstraint(BaseModel): + """ + Version range requirement with minimum and optional maximum. + """ + + model_config = ConfigDict( + extra="allow", + ) + min: Version + """ + Minimum required version (inclusive). + """ + max: Version | None = None + """ + Maximum compatible version (inclusive). When absent, no upper bound. + """ + + +class Requires(BaseModel): + """ + Version requirements for extension schemas. Declares minimum (and optionally maximum) protocol and capability versions needed for correct operation. + """ + + model_config = ConfigDict( + extra="allow", + ) + protocol: VersionConstraint | None = None + """ + Required protocol version. + """ + capabilities: ( + dict[reverse_domain_name.ReverseDomainName, VersionConstraint] | None + ) = None + """ + Required capability versions, keyed by capability name. Keys must be a subset of the extension's $defs keys. + """ + + +class Entity(BaseModel): + """ + Shared foundation for all UCP entities. + """ + + model_config = ConfigDict( + extra="allow", + ) + version: Version + """ + Entity version in YYYY-MM-DD format. + """ + spec: AnyUrl | None = None + """ + URL to human-readable specification document. + """ + schema_: AnyUrl | None = Field(None, alias="schema") + """ + URL to JSON Schema defining this entity's structure and payloads. + """ + id: str | None = None + """ + Unique identifier for this entity instance. Used to disambiguate when multiple instances exist. + """ + config: dict[str, Any] | None = None + """ + Entity-specific configuration. Structure defined by each entity's schema. + """ + + +class Base(BaseModel): + """ + Base UCP metadata with shared properties for all schema types. + """ + + model_config = ConfigDict( + extra="allow", + ) + version: Version + status: Literal["success", "error"] | None = "success" + """ + Application-level status of the UCP operation. + """ + services: ( + dict[reverse_domain_name.ReverseDomainName, list[service.Base]] | None + ) = None + """ + Service registry keyed by reverse-domain name. + """ + capabilities: ( + dict[reverse_domain_name.ReverseDomainName, list[capability.Base]] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + payment_handlers: ( + dict[reverse_domain_name.ReverseDomainName, list[payment_handler.Base]] + | None + ) = None + """ + Payment handler registry keyed by reverse-domain name. + """ + + +class Success(Base): + """ + UCP metadata with status 'success'. Use for response branches that carry the expected payload. + """ + + model_config = ConfigDict( + extra="allow", + ) + status: Literal["success"] + """ + Application-level status of the UCP operation. + """ + + +class Error(Base): + """ + UCP metadata with status 'error'. Use for response branches that carry error information. + """ + + model_config = ConfigDict( + extra="allow", + ) + status: Literal["error"] + """ + Application-level status of the UCP operation. + """ + + +class PlatformSchema(Base): + """ + Full UCP metadata for platform-level configuration. Hosted at a URI advertised by the platform in request headers. + """ + + model_config = ConfigDict( + extra="allow", + ) + services: dict[ + reverse_domain_name.ReverseDomainName, list[service.PlatformSchema5] + ] + """ + Service registry keyed by reverse-domain name. + """ + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.PlatformSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + payment_handlers: dict[ + reverse_domain_name.ReverseDomainName, + list[payment_handler.PlatformSchema], + ] + """ + Payment handler registry keyed by reverse-domain name. + """ + + +class BusinessSchema(Base): + """ + UCP metadata for business/merchant-level configuration. Subset of platform schema with business-specific settings. + """ + + model_config = ConfigDict( + extra="allow", + ) + supported_versions: dict[Version, AnyUrl] | None = None + """ + Previous protocol versions this business supports, mapped to profile URIs. Businesses that support older protocol versions SHOULD advertise each version and link to its profile. Each URI points to a complete, self-contained profile for that version. When omitted, only `version` is supported. + """ + services: dict[ + reverse_domain_name.ReverseDomainName, list[service.BusinessSchema2] + ] + """ + Service registry keyed by reverse-domain name. + """ + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.BusinessSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + payment_handlers: dict[ + reverse_domain_name.ReverseDomainName, + list[payment_handler.BusinessSchema], + ] + """ + Payment handler registry keyed by reverse-domain name. + """ + + +class ResponseCheckoutSchema(Base): + """ + UCP metadata for checkout responses. + """ + + model_config = ConfigDict( + extra="allow", + ) + services: ( + dict[ + reverse_domain_name.ReverseDomainName, list[service.ResponseSchema2] + ] + | None + ) = None + """ + Service registry keyed by reverse-domain name. + """ + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.ResponseSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + payment_handlers: dict[ + reverse_domain_name.ReverseDomainName, + list[payment_handler.ResponseSchema], + ] + """ + Payment handler registry keyed by reverse-domain name. + """ + + +class ResponseOrderSchema(Base): + """ + UCP metadata for order responses. No payment handlers needed post-purchase. + """ + + model_config = ConfigDict( + extra="allow", + ) + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.ResponseSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + + +class ResponseCartSchema(Base): + """ + UCP metadata for cart responses. No payment handlers needed pre-checkout. + """ + + model_config = ConfigDict( + extra="allow", + ) + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.ResponseSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + + +class ResponseCatalogSchema(Base): + """ + UCP metadata for catalog responses. + """ + + model_config = ConfigDict( + extra="allow", + ) + capabilities: ( + dict[ + reverse_domain_name.ReverseDomainName, + list[capability.ResponseSchema], + ] + | None + ) = None + """ + Capability registry keyed by reverse-domain name. + """ + + +class UcpMetadataUpdateRequest( + RootModel[ + PlatformSchema + | BusinessSchema + | ResponseCheckoutSchema + | ResponseOrderSchema + | ResponseCartSchema + ] +): + model_config = ConfigDict( + frozen=True, + ) + root: ( + PlatformSchema + | BusinessSchema + | ResponseCheckoutSchema + | ResponseOrderSchema + | ResponseCartSchema + ) = Field(..., title="UCP Metadata Update Request") + """ + Protocol metadata for discovery profiles and responses. Uses slim schema pattern with context-specific required fields. + """