From 4af0c2d1c4ba5f5da1a1c354228568badea9fe0e Mon Sep 17 00:00:00 2001 From: Adrian McPhee Date: Tue, 3 Mar 2026 20:59:09 +0100 Subject: [PATCH 1/3] fix(schemas): define base entities before dependents and freeze key root models --- src/ucp_sdk/models/schemas/_internal.py | 92 +++++++++++++------------ 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/src/ucp_sdk/models/schemas/_internal.py b/src/ucp_sdk/models/schemas/_internal.py index e8a126b..3ef9655 100644 --- a/src/ucp_sdk/models/schemas/_internal.py +++ b/src/ucp_sdk/models/schemas/_internal.py @@ -32,6 +32,55 @@ class UcpCapability(RootModel[Any]): """ +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 ReverseDomainName(RootModel[str]): + model_config = ConfigDict( + frozen=True, + ) + root: str = Field(..., pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$") + """ + Reverse-domain identifier (e.g., com.google.pay, dev.ucp.shopping.checkout) + """ + + +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(Entity): model_config = ConfigDict( extra="allow", @@ -408,49 +457,6 @@ class UcpMetadata(RootModel[Any]): """ -class Version(RootModel[str]): - root: str = Field(..., pattern="^\\d{4}-\\d{2}-\\d{2}$") - """ - UCP version in YYYY-MM-DD format. - """ - - -class ReverseDomainName(RootModel[str]): - root: str = Field(..., pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$") - """ - Reverse-domain identifier (e.g., com.google.pay, dev.ucp.shopping.checkout) - """ - - -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_3(BaseModel): """Base UCP metadata with shared properties for all schema types. """ From 2297f7e8725e2440e67bd8168a18ca9d5fb86436 Mon Sep 17 00:00:00 2001 From: Adrian McPhee Date: Tue, 3 Mar 2026 20:59:15 +0100 Subject: [PATCH 2/3] fix(discovery): support legacy and keyed discovery payloads in pydantic v2 --- .../models/discovery/profile_schema.py | 214 ++++++++++++++++-- 1 file changed, 197 insertions(+), 17 deletions(-) diff --git a/src/ucp_sdk/models/discovery/profile_schema.py b/src/ucp_sdk/models/discovery/profile_schema.py index f9b18c3..2e61cec 100644 --- a/src/ucp_sdk/models/discovery/profile_schema.py +++ b/src/ucp_sdk/models/discovery/profile_schema.py @@ -18,11 +18,11 @@ from __future__ import annotations -from typing import Literal +from typing import Any, Literal -from pydantic import BaseModel, ConfigDict, Field, RootModel +from pydantic import AnyUrl, BaseModel, ConfigDict, Field, model_validator -from ..schemas._internal import Base_3, BusinessSchema_3, PlatformSchema_3 +from ..schemas._internal import ReverseDomainName, Version class SigningKey(BaseModel): @@ -70,44 +70,224 @@ class SigningKey(BaseModel): """ -class Base(BaseModel): - """Base discovery profile with shared properties for all profile types. +class ServiceBinding(BaseModel): + """Transport-specific binding information for a service. """ model_config = ConfigDict( extra="allow", ) - ucp: Base_3 - signing_keys: list[SigningKey] | None = None + endpoint: AnyUrl | str | None = None + schema_: AnyUrl | str | None = Field(None, alias="schema") """ - Public keys for signature verification (JWK format). Used to verify signed responses, webhooks, and other authenticated messages from this party. + URL to JSON schema for the service transport. """ -class PlatformProfile(Base): - """Full discovery profile for platforms. Exposes complete service, capability, and payment handler registries. +class ServiceDescriptor(BaseModel): + """Service metadata normalized across legacy and current schema layouts. """ model_config = ConfigDict( extra="allow", ) - ucp: PlatformSchema_3 | None = None + version: Version | None = None + spec: AnyUrl | str | None = None + rest: ServiceBinding | None = None + mcp: ServiceBinding | None = None + a2a: ServiceBinding | None = None + embedded: ServiceBinding | None = None -class BusinessProfile(Base): - """Discovery profile for businesses/merchants. Subset of platform profile with business-specific configuration. +def _normalize_service(value: Any) -> dict[str, Any]: + """Normalize service payloads from both legacy and generated schemas.""" + if isinstance(value, list): + normalized: dict[str, Any] = {} + for entry in value: + if not isinstance(entry, dict): + continue + transport = entry.get("transport") + if transport in {"rest", "mcp", "a2a", "embedded"}: + normalized[transport] = { + k: v + for k, v in entry.items() + if k in {"endpoint", "schema", "config"} and v is not None + } + # Carry shared metadata once from the first entry that has it. + if "version" in entry and "version" not in normalized: + normalized["version"] = entry["version"] + if "spec" in entry and "spec" not in normalized: + normalized["spec"] = entry["spec"] + return normalized + if isinstance(value, dict): + return value + return {} + + +class ServiceRegistry(BaseModel): + """Map of service names to service descriptors. """ model_config = ConfigDict( extra="allow", ) - ucp: BusinessSchema_3 | None = None + root: dict[str, ServiceDescriptor] = Field(default_factory=dict) + + @model_validator(mode="before") + @classmethod + def _normalize(cls, value: Any) -> dict[str, Any]: + if isinstance(value, dict) and "root" in value: + return value + if isinstance(value, dict): + return {"root": {str(k): _normalize_service(v) for k, v in value.items()}} + return {"root": {}} + + +class Capability(BaseModel): + """Capability entry for legacy discovery payloads.""" + + model_config = ConfigDict( + extra="allow", + ) + name: str | None = None + version: Version | None = None + spec: AnyUrl | str | None = None + schema_: AnyUrl | str | None = Field(None, alias="schema") + extends: str | None = Field( + None, pattern="^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$" + ) + + +def _normalize_capabilities(value: Any) -> list[dict[str, Any]]: + """Normalize capabilities from list or dict-keyed formats.""" + if isinstance(value, list): + return [item for item in value if isinstance(item, dict)] + if isinstance(value, dict): + normalized: list[dict[str, Any]] = [] + for capability_name, entries in value.items(): + if isinstance(entries, list): + iterable = entries + else: + iterable = [entries] + for entry in iterable: + if not isinstance(entry, dict): + continue + normalized_entry = dict(entry) + normalized_entry.setdefault("name", str(capability_name)) + normalized.append(normalized_entry) + return normalized + return [] + + +class PaymentHandlerResponse(BaseModel): + """Legacy-compatible payment handler declaration.""" + + model_config = ConfigDict( + extra="allow", + ) + id: str | None = None + name: str | None = None + version: Version | None = None + spec: AnyUrl | str | None = None + config_schema: AnyUrl | str | None = None + instrument_schemas: list[AnyUrl | str] | None = None + config: dict[str, Any] | None = None + + +class PaymentProfile(BaseModel): + """Legacy-compatible top-level payment discovery section.""" + + model_config = ConfigDict( + extra="allow", + ) + handlers: list[PaymentHandlerResponse] | None = None -class UcpDiscoveryProfile(RootModel[PlatformProfile | BusinessProfile]): - root: PlatformProfile | BusinessProfile = Field( - ..., title="UCP Discovery Profile" +class Base(BaseModel): + """Base discovery profile with shared properties for all profile types.""" + + model_config = ConfigDict( + extra="allow", ) + ucp: UcpMetadata + signing_keys: list[SigningKey] | None = None + """ + Public keys for signature verification (JWK format). Used to verify signed responses, webhooks, and other authenticated messages from this party. + """ + + +class UcpMetadata(BaseModel): + """Legacy-compatible UCP discovery payload.""" + + model_config = ConfigDict( + extra="allow", + ) + version: Version + services: ServiceRegistry + capabilities: list[Capability] = Field(default_factory=list) + payment_handlers: dict[ReverseDomainName, list[dict[str, Any]]] | None = None + + @model_validator(mode="before") + @classmethod + def _normalize(cls, value: Any) -> Any: + if not isinstance(value, dict): + return value + normalized = dict(value) + normalized["capabilities"] = _normalize_capabilities( + normalized.get("capabilities") + ) + return normalized + + +class PlatformProfile(Base): + """Full discovery profile for platforms.""" + + +class BusinessProfile(Base): + """Discovery profile for businesses/merchants.""" + + +def _flatten_payment_handlers( + keyed_handlers: dict[str, Any] | None, +) -> list[dict[str, Any]]: + if not isinstance(keyed_handlers, dict): + return [] + flattened: list[dict[str, Any]] = [] + for handler_name, entries in keyed_handlers.items(): + iterable = entries if isinstance(entries, list) else [entries] + for entry in iterable: + if not isinstance(entry, dict): + continue + normalized = dict(entry) + normalized.setdefault("name", str(handler_name)) + flattened.append(normalized) + return flattened + + +class UcpDiscoveryProfile(Base): + payment: PaymentProfile | None = None """ Schema for UCP discovery profiles. Business profiles are hosted at /.well-known/ucp; platform profiles are hosted at URIs advertised in request headers. """ + + @model_validator(mode="before") + @classmethod + def _normalize(cls, value: Any) -> Any: + if not isinstance(value, dict): + return value + + # Accept prior RootModel-style payloads. + if "root" in value and isinstance(value["root"], dict): + value = dict(value["root"]) + else: + value = dict(value) + + ucp = value.get("ucp") + if isinstance(ucp, dict): + payment = value.get("payment") + if payment is None: + flattened = _flatten_payment_handlers(ucp.get("payment_handlers")) + if flattened: + value["payment"] = {"handlers": flattened} + + return value From ab71626de5c00028cf7834ac55804787a70d3276 Mon Sep 17 00:00:00 2001 From: Adrian McPhee Date: Tue, 3 Mar 2026 20:59:20 +0100 Subject: [PATCH 3/3] fix(compat): restore legacy shopping request/response module aliases --- .../models/schemas/shopping/ap2_mandate.py | 16 +++++++++++++ .../schemas/shopping/buyer_consent_resp.py | 3 +++ .../schemas/shopping/checkout_create_req.py | 3 +++ .../schemas/shopping/checkout_update_req.py | 3 +++ .../models/schemas/shopping/discount_resp.py | 3 +++ .../schemas/shopping/discount_update_req.py | 13 +++++++++++ .../shopping/fulfillment_create_req.py | 17 ++++++++++++++ .../schemas/shopping/fulfillment_resp.py | 3 +++ .../shopping/fulfillment_update_req.py | 13 +++++++++++ .../schemas/shopping/payment_create_req.py | 3 +++ .../models/schemas/shopping/payment_resp.py | 3 +++ .../schemas/shopping/payment_update_req.py | 3 +++ .../types/fulfillment_destination_req.py | 5 ++++ .../types/fulfillment_group_create_req.py | 3 +++ .../types/fulfillment_method_create_req.py | 3 +++ .../schemas/shopping/types/fulfillment_req.py | 14 +++++++++++ .../schemas/shopping/types/item_create_req.py | 3 +++ .../schemas/shopping/types/item_update_req.py | 3 +++ .../shopping/types/line_item_create_req.py | 3 +++ .../shopping/types/line_item_update_req.py | 3 +++ .../shopping/types/payment_handler_resp.py | 23 +++++++++++++++++++ .../types/shipping_destination_req.py | 3 +++ .../shopping/types/token_credential_resp.py | 3 +++ 23 files changed, 149 insertions(+) create mode 100644 src/ucp_sdk/models/schemas/shopping/buyer_consent_resp.py create mode 100644 src/ucp_sdk/models/schemas/shopping/checkout_create_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/checkout_update_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/discount_resp.py create mode 100644 src/ucp_sdk/models/schemas/shopping/discount_update_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/fulfillment_create_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/fulfillment_resp.py create mode 100644 src/ucp_sdk/models/schemas/shopping/fulfillment_update_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/payment_create_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/payment_resp.py create mode 100644 src/ucp_sdk/models/schemas/shopping/payment_update_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/fulfillment_destination_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/fulfillment_group_create_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/fulfillment_method_create_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/fulfillment_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/item_create_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/item_update_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/line_item_create_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/line_item_update_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/payment_handler_resp.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/shipping_destination_req.py create mode 100644 src/ucp_sdk/models/schemas/shopping/types/token_credential_resp.py diff --git a/src/ucp_sdk/models/schemas/shopping/ap2_mandate.py b/src/ucp_sdk/models/schemas/shopping/ap2_mandate.py index 7504199..7103b06 100644 --- a/src/ucp_sdk/models/schemas/shopping/ap2_mandate.py +++ b/src/ucp_sdk/models/schemas/shopping/ap2_mandate.py @@ -121,3 +121,19 @@ class Checkout(Checkout_1): extra="allow", ) ap2: Ap2 | None = None + + +class Ap2CompleteRequest(Ap2WithCheckoutMandate): + """Legacy completion payload alias used by conformance and samples.""" + + model_config = ConfigDict( + extra="allow", + ) + + +class CheckoutResponseWithAp2(Checkout): + """Legacy response alias used by samples.""" + + model_config = ConfigDict( + extra="allow", + ) diff --git a/src/ucp_sdk/models/schemas/shopping/buyer_consent_resp.py b/src/ucp_sdk/models/schemas/shopping/buyer_consent_resp.py new file mode 100644 index 0000000..43d8762 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/buyer_consent_resp.py @@ -0,0 +1,3 @@ +from .buyer_consent import Buyer, Checkout, Consent + +__all__ = ["Buyer", "Checkout", "Consent"] diff --git a/src/ucp_sdk/models/schemas/shopping/checkout_create_req.py b/src/ucp_sdk/models/schemas/shopping/checkout_create_req.py new file mode 100644 index 0000000..3039a31 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/checkout_create_req.py @@ -0,0 +1,3 @@ +from .checkout_create_request import CheckoutCreateRequest + +__all__ = ["CheckoutCreateRequest"] diff --git a/src/ucp_sdk/models/schemas/shopping/checkout_update_req.py b/src/ucp_sdk/models/schemas/shopping/checkout_update_req.py new file mode 100644 index 0000000..b824ffb --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/checkout_update_req.py @@ -0,0 +1,3 @@ +from .checkout_update_request import CheckoutUpdateRequest + +__all__ = ["CheckoutUpdateRequest"] diff --git a/src/ucp_sdk/models/schemas/shopping/discount_resp.py b/src/ucp_sdk/models/schemas/shopping/discount_resp.py new file mode 100644 index 0000000..50b6263 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/discount_resp.py @@ -0,0 +1,3 @@ +from .discount import Allocation, AppliedDiscount, Checkout, DiscountsObject + +__all__ = ["Allocation", "AppliedDiscount", "Checkout", "DiscountsObject"] diff --git a/src/ucp_sdk/models/schemas/shopping/discount_update_req.py b/src/ucp_sdk/models/schemas/shopping/discount_update_req.py new file mode 100644 index 0000000..5e550e2 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/discount_update_req.py @@ -0,0 +1,13 @@ +from typing import Any + +from pydantic import BaseModel, ConfigDict + + +class Checkout(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + discounts: Any | None = None + + +__all__ = ["Checkout"] diff --git a/src/ucp_sdk/models/schemas/shopping/fulfillment_create_req.py b/src/ucp_sdk/models/schemas/shopping/fulfillment_create_req.py new file mode 100644 index 0000000..3a07f2a --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/fulfillment_create_req.py @@ -0,0 +1,17 @@ +from pydantic import BaseModel, ConfigDict, Field, RootModel + +from .types.fulfillment_req import FulfillmentRequest + + +class Fulfillment(RootModel[FulfillmentRequest]): + root: FulfillmentRequest = Field(..., title="Fulfillment") + + +class Checkout(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + fulfillment: Fulfillment | None = None + + +__all__ = ["Checkout", "Fulfillment"] diff --git a/src/ucp_sdk/models/schemas/shopping/fulfillment_resp.py b/src/ucp_sdk/models/schemas/shopping/fulfillment_resp.py new file mode 100644 index 0000000..1080b64 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/fulfillment_resp.py @@ -0,0 +1,3 @@ +from .checkout import Checkout + +__all__ = ["Checkout"] diff --git a/src/ucp_sdk/models/schemas/shopping/fulfillment_update_req.py b/src/ucp_sdk/models/schemas/shopping/fulfillment_update_req.py new file mode 100644 index 0000000..7e983da --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/fulfillment_update_req.py @@ -0,0 +1,13 @@ +from pydantic import BaseModel, ConfigDict + +from .fulfillment_create_req import Fulfillment + + +class Checkout(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + fulfillment: Fulfillment | None = None + + +__all__ = ["Checkout"] diff --git a/src/ucp_sdk/models/schemas/shopping/payment_create_req.py b/src/ucp_sdk/models/schemas/shopping/payment_create_req.py new file mode 100644 index 0000000..43c0643 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/payment_create_req.py @@ -0,0 +1,3 @@ +from .payment_create_request import PaymentCreateRequest + +__all__ = ["PaymentCreateRequest"] diff --git a/src/ucp_sdk/models/schemas/shopping/payment_resp.py b/src/ucp_sdk/models/schemas/shopping/payment_resp.py new file mode 100644 index 0000000..5c5ace1 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/payment_resp.py @@ -0,0 +1,3 @@ +from .payment import Payment as PaymentResponse + +__all__ = ["PaymentResponse"] diff --git a/src/ucp_sdk/models/schemas/shopping/payment_update_req.py b/src/ucp_sdk/models/schemas/shopping/payment_update_req.py new file mode 100644 index 0000000..8781e11 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/payment_update_req.py @@ -0,0 +1,3 @@ +from .payment_update_request import PaymentUpdateRequest + +__all__ = ["PaymentUpdateRequest"] diff --git a/src/ucp_sdk/models/schemas/shopping/types/fulfillment_destination_req.py b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_destination_req.py new file mode 100644 index 0000000..ec25c71 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_destination_req.py @@ -0,0 +1,5 @@ +from .fulfillment_destination import ( + FulfillmentDestination as FulfillmentDestinationRequest, +) + +__all__ = ["FulfillmentDestinationRequest"] diff --git a/src/ucp_sdk/models/schemas/shopping/types/fulfillment_group_create_req.py b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_group_create_req.py new file mode 100644 index 0000000..c5e9f62 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_group_create_req.py @@ -0,0 +1,3 @@ +from .fulfillment_group_create_request import FulfillmentGroupCreateRequest + +__all__ = ["FulfillmentGroupCreateRequest"] diff --git a/src/ucp_sdk/models/schemas/shopping/types/fulfillment_method_create_req.py b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_method_create_req.py new file mode 100644 index 0000000..9fd8cb6 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_method_create_req.py @@ -0,0 +1,3 @@ +from .fulfillment_method_create_request import FulfillmentMethodCreateRequest + +__all__ = ["FulfillmentMethodCreateRequest"] diff --git a/src/ucp_sdk/models/schemas/shopping/types/fulfillment_req.py b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_req.py new file mode 100644 index 0000000..31ad10f --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/fulfillment_req.py @@ -0,0 +1,14 @@ +from typing import Any + +from pydantic import BaseModel, ConfigDict + + +class FulfillmentRequest(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + methods: list[Any] | None = None + available_methods: list[Any] | None = None + + +__all__ = ["FulfillmentRequest"] diff --git a/src/ucp_sdk/models/schemas/shopping/types/item_create_req.py b/src/ucp_sdk/models/schemas/shopping/types/item_create_req.py new file mode 100644 index 0000000..fd7d453 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/item_create_req.py @@ -0,0 +1,3 @@ +from .item_create_request import ItemCreateRequest + +__all__ = ["ItemCreateRequest"] diff --git a/src/ucp_sdk/models/schemas/shopping/types/item_update_req.py b/src/ucp_sdk/models/schemas/shopping/types/item_update_req.py new file mode 100644 index 0000000..92fa275 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/item_update_req.py @@ -0,0 +1,3 @@ +from .item_update_request import ItemUpdateRequest + +__all__ = ["ItemUpdateRequest"] diff --git a/src/ucp_sdk/models/schemas/shopping/types/line_item_create_req.py b/src/ucp_sdk/models/schemas/shopping/types/line_item_create_req.py new file mode 100644 index 0000000..c78fd3e --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/line_item_create_req.py @@ -0,0 +1,3 @@ +from .line_item_create_request import LineItemCreateRequest + +__all__ = ["LineItemCreateRequest"] diff --git a/src/ucp_sdk/models/schemas/shopping/types/line_item_update_req.py b/src/ucp_sdk/models/schemas/shopping/types/line_item_update_req.py new file mode 100644 index 0000000..9419c8e --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/line_item_update_req.py @@ -0,0 +1,3 @@ +from .line_item_update_request import LineItemUpdateRequest + +__all__ = ["LineItemUpdateRequest"] diff --git a/src/ucp_sdk/models/schemas/shopping/types/payment_handler_resp.py b/src/ucp_sdk/models/schemas/shopping/types/payment_handler_resp.py new file mode 100644 index 0000000..530a8cc --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/payment_handler_resp.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from typing import Any + +from pydantic import AnyUrl, BaseModel, ConfigDict + +from ..._internal import Version + + +class PaymentHandlerResponse(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + id: str | None = None + name: str | None = None + version: Version | None = None + spec: AnyUrl | str | None = None + config_schema: AnyUrl | str | None = None + instrument_schemas: list[AnyUrl | str] | None = None + config: dict[str, Any] | None = None + + +__all__ = ["PaymentHandlerResponse"] diff --git a/src/ucp_sdk/models/schemas/shopping/types/shipping_destination_req.py b/src/ucp_sdk/models/schemas/shopping/types/shipping_destination_req.py new file mode 100644 index 0000000..d928181 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/shipping_destination_req.py @@ -0,0 +1,3 @@ +from .shipping_destination import ShippingDestination as ShippingDestinationRequest + +__all__ = ["ShippingDestinationRequest"] diff --git a/src/ucp_sdk/models/schemas/shopping/types/token_credential_resp.py b/src/ucp_sdk/models/schemas/shopping/types/token_credential_resp.py new file mode 100644 index 0000000..3fb26b6 --- /dev/null +++ b/src/ucp_sdk/models/schemas/shopping/types/token_credential_resp.py @@ -0,0 +1,3 @@ +from .token_credential import TokenCredential as TokenCredentialResponse + +__all__ = ["TokenCredentialResponse"]